summary refs log tree commit diff
path: root/Src/Builtins
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Builtins')
-rw-r--r--Src/Builtins/sched.c295
1 files changed, 206 insertions, 89 deletions
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index 558a00ba6..c32a5f219 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -34,61 +34,156 @@
 
 typedef struct schedcmd  *Schedcmd;
 
+/* Flags for each scheduled event */
+enum schedflags {
+    /* Trash zle if necessary when event is activated */
+    SCHEDFLAG_TRASH_ZLE = 1
+};
+
 struct schedcmd {
     struct schedcmd *next;
     char *cmd;			/* command to run */
     time_t time;		/* when to run it */
+    int flags;			/* flags as above */
 };
 
 /* the list of sched jobs pending */
  
 static struct schedcmd *schedcmds;
 
+/* Check scheduled commands; call this function from time to time. */
+
+/**/
+static void
+checksched(void)
+{
+    time_t t;
+    struct schedcmd *sch;
+
+    if(!schedcmds)
+	return;
+    t = time(NULL);
+    /*
+     * List is ordered, so we only need to consider the
+     * head element.
+     */
+    while (schedcmds && schedcmds->time <= t) {
+	/*
+	 * Remove the entry to be executed from the list
+	 * before execution:  this makes quite sure that
+	 * the entry hasn't been monkeyed with when we
+	 * free it.
+	 */
+	sch = schedcmds;
+	schedcmds = sch->next;
+	/*
+	 * Delete from the timed function list now in case
+	 * the called code reschedules.
+	 */
+	deltimedfn(checksched);
+
+	if ((sch->flags & SCHEDFLAG_TRASH_ZLE) && zleactive)
+	    trashzleptr();
+	execstring(sch->cmd, 0, 0);
+	zsfree(sch->cmd);
+	zfree(sch, sizeof(struct schedcmd));
+
+	/*
+	 * Fix time for future events.
+	 * I had this outside the loop, for a little extra efficiency.
+	 * However, it then occurred to me that having the list of
+	 * forthcoming entries up to date could be regarded as
+	 * a feature, and the inefficiency is negligible.
+	 */
+	if (schedcmds) {
+	    /*
+	     * We need to delete the function from the list again,
+	     * in case called code rescheduled.  This is almost
+	     * as cheap as checking if it's in the list already.
+	     */
+	    deltimedfn(checksched);
+	    DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (1)");	    addtimedfn(checksched, schedcmds->time);
+	}
+    }
+}
+
 /**/
 static int
-bin_sched(UNUSED(char *nam), char **argv, UNUSED(Options ops), UNUSED(int func))
+bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 {
-    char *s = *argv++;
+    char *s, **argptr;
     time_t t;
-    long h, m;
+    long h, m, sec;
     struct tm *tm;
     struct schedcmd *sch, *sch2, *schl;
-    int sn;
+    int sn, flags = 0;
 
     /* If the argument begins with a -, remove the specified item from the
     schedule. */
-    if (s && *s == '-') {
-	sn = atoi(s + 1);
+    for (argptr = argv; *argptr && **argptr == '-'; argptr++) {
+	char *arg = *argptr + 1;
+	if (idigit(*arg)) {
+	    sn = atoi(arg);
 
-	if (!sn) {
-	    zwarnnam("sched", "usage for delete: sched -<item#>.");
-	    return 1;
-	}
-	for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds, sn--;
-	     sch && sn; sch = (schl = sch)->next, sn--);
-	if (!sch) {
-	    zwarnnam("sched", "not that many entries");
+	    if (!sn) {
+		zwarnnam("sched", "usage for delete: sched -<item#>.");
+		return 1;
+	    }
+	    for (schl = NULL, sch = schedcmds, sn--;
+		 sch && sn; sch = (schl = sch)->next, sn--);
+	    if (!sch) {
+		zwarnnam("sched", "not that many entries");
+		return 1;
+	    }
+	    if (schl)
+		schl->next = sch->next;
+	    else {
+		deltimedfn(checksched);
+		schedcmds = sch->next;
+		if (schedcmds) {
+		    DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (2)");
+		    addtimedfn(checksched, schedcmds->time);
+		}
+	    }
+	    zsfree(sch->cmd);
+	    zfree(sch, sizeof(struct schedcmd));
+
+	    return 0;
+	} else if (*arg == '-') {
+	    /* end of options */
+	    argptr++;
+	    break;
+	} else if (!strcmp(arg, "o")) {
+	    flags |= SCHEDFLAG_TRASH_ZLE;
+	} else {
+	    if (*arg)
+		zwarnnam(nam, "bad option: -%c", *arg);
+	    else
+		zwarnnam(nam, "option expected");
 	    return 1;
 	}
-	schl->next = sch->next;
-	zsfree(sch->cmd);
-	zfree(sch, sizeof(struct schedcmd));
-
-	return 0;
     }
 
     /* given no arguments, display the schedule list */
-    if (!s) {
-	char tbuf[40];
+    if (!*argptr) {
+	char tbuf[40], *flagstr, *endstr;
 
 	for (sn = 1, sch = schedcmds; sch; sch = sch->next, sn++) {
 	    t = sch->time;
 	    tm = localtime(&t);
 	    ztrftime(tbuf, 20, "%a %b %e %k:%M:%S", tm);
-	    printf("%3d %s %s\n", sn, tbuf, sch->cmd);
+	    if (sch->flags & SCHEDFLAG_TRASH_ZLE)
+		flagstr = "-o ";
+	    else
+		flagstr = "";
+	    if (*sch->cmd == '-')
+		endstr = "-- ";
+	    else
+		endstr = "";
+	    printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr, sch->cmd);
 	}
 	return 0;
-    } else if (!*argv) {
+    } else if (!argptr[1]) {
 	/* other than the two cases above, sched *
 	 *requires at least two arguments        */
 	zwarnnam("sched", "not enough arguments");
@@ -97,86 +192,108 @@ bin_sched(UNUSED(char *nam), char **argv, UNUSED(Options ops), UNUSED(int func))
 
     /* The first argument specifies the time to schedule the command for.  The
     remaining arguments form the command. */
+    s = *argptr++;
     if (*s == '+') {
-	/* + introduces a relative time.  The rest of the argument is an
-	hour:minute offset from the current time.  Once the hour and minute
-	numbers have been extracted, and the format verified, the resulting
-	offset is simply added to the current time. */
-	h = zstrtol(s + 1, &s, 10);
-	if (*s != ':') {
+	/*
+	 * + introduces a relative time.  The rest of the argument may be an
+	 * hour:minute offset from the current time.  Once the hour and minute
+	 * numbers have been extracted, and the format verified, the resulting
+	 * offset is simply added to the current time.
+	 */
+	zlong zl = zstrtol(s + 1, &s, 10);
+	if (*s == ':') {
+	    m = (long)zstrtol(s + 1, &s, 10);
+	    if (*s == ':')
+		sec = (long)zstrtol(s + 1, &s, 10);
+	    else
+		sec = 0;
+	    if (*s) {
+		zwarnnam("sched", "bad time specifier");
+		return 1;
+	    }
+	    t = time(NULL) + (long)zl * 3600 + m * 60 + sec;
+	} else if (!*s) {
+	    /*
+	     * Alternatively, it may simply be a number of seconds.
+	     * This is here for consistency with absolute times.
+	     */
+	    t = time(NULL) + (time_t)zl;
+	} else {
 	    zwarnnam("sched", "bad time specifier");
 	    return 1;
 	}
-	m = zstrtol(s + 1, &s, 10);
-	if (*s) {
-	    zwarnnam("sched", "bad time specifier");
-	    return 1;
-	}
-	t = time(NULL) + h * 3600 + m * 60;
     } else {
-	/* If there is no +, an absolute time of day must have been given.
-	This is in hour:minute format, optionally followed by a string starting
-	with `a' or `p' (for a.m. or p.m.).  Characters after the `a' or `p'
-	are ignored. */
-	h = zstrtol(s, &s, 10);
-	if (*s != ':') {
-	    zwarnnam("sched", "bad time specifier");
-	    return 1;
-	}
-	m = zstrtol(s + 1, &s, 10);
-	if (*s && *s != 'a' && *s != 'A' && *s != 'p' && *s != 'P') {
+	/*
+	 * If there is no +, an absolute time must have been given.
+	 * This may be in hour:minute format, optionally followed by a string
+	 * starting with `a' or `p' (for a.m. or p.m.).  Characters after the
+	 * `a' or `p' are ignored.
+	 */
+	zlong zl = zstrtol(s, &s, 10);
+	if (*s == ':') {
+	    h = (long)zl;
+	    m = (long)zstrtol(s + 1, &s, 10);
+	    if (*s == ':')
+		sec = (long)zstrtol(s + 1, &s, 10);
+	    else
+		sec = 0;
+	    if (*s && *s != 'a' && *s != 'A' && *s != 'p' && *s != 'P') {
+		zwarnnam("sched", "bad time specifier");
+		return 1;
+	    }
+	    t = time(NULL);
+	    tm = localtime(&t);
+	    t -= tm->tm_sec + tm->tm_min * 60 + tm->tm_hour * 3600;
+	    if (*s == 'p' || *s == 'P')
+		h += 12;
+	    t += h * 3600 + m * 60 + sec;
+	    /*
+	     * If the specified time is before the current time, it must refer
+	     * to tomorrow.
+	     */
+	    if (t < time(NULL))
+		t += 3600 * 24;
+	} else if (!*s) {
+	    /*
+	     * Otherwise, it must be a raw time specifier.
+	     */
+	    t = (long)zl;
+	} else {
 	    zwarnnam("sched", "bad time specifier");
 	    return 1;
 	}
-	t = time(NULL);
-	tm = localtime(&t);
-	t -= tm->tm_sec + tm->tm_min * 60 + tm->tm_hour * 3600;
-	if (*s == 'p' || *s == 'P')
-	    h += 12;
-	t += h * 3600 + m * 60;
-	/* If the specified time is before the current time, it must refer to
-	tomorrow. */
-	if (t < time(NULL))
-	    t += 3600 * 24;
     }
     /* The time has been calculated; now add the new entry to the linked list
     of scheduled commands. */
-    sch = (struct schedcmd *) zshcalloc(sizeof *sch);
+    sch = (struct schedcmd *) zalloc(sizeof *sch);
     sch->time = t;
-    sch->cmd = zjoin(argv, ' ', 0);
-    sch->next = NULL;
-    for (sch2 = (struct schedcmd *)&schedcmds; sch2->next; sch2 = sch2->next);
-    sch2->next = sch;
-    return 0;
-}
-
-/* Check scheduled commands; call this function from time to time. */
-
-/**/
-static void
-checksched(void)
-{
-    time_t t;
-    struct schedcmd *sch, *schl;
-
-    if(!schedcmds)
-	return;
-    t = time(NULL);
-    for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds; sch;
-	 sch = (schl = sch)->next) {
-	if (sch->time <= t) {
-	    execstring(sch->cmd, 0, 0);
-	    schl->next = sch->next;
-	    zsfree(sch->cmd);
-	    zfree(sch, sizeof(struct schedcmd));
-	    sch = schl;
+    sch->cmd = zjoin(argptr, ' ', 0);
+    sch->flags = flags;
+    /* Insert into list in time order */
+    if (schedcmds) {
+	if (sch->time < schedcmds->time) {
+	    deltimedfn(checksched);
+	    sch->next = schedcmds;
+	    schedcmds = sch;
+	    DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (3)");
+	    addtimedfn(checksched, t);
+	} else {
+	    for (sch2 = schedcmds;
+		 sch2->next && sch2->next->time < sch->time;
+		 sch2 = sch2->next)
+		;
+	    sch->next = sch2->next;
+	    sch2->next = sch;
 	}
+    } else {
+	sch->next = NULL;
+	schedcmds = sch;
+	DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (4)");
+	addtimedfn(checksched, t);
     }
+    return 0;
 }
 
-static void (*p_checksched) _((void)) = checksched;
-static struct linknode n_checksched = { NULL, NULL, &p_checksched };
-
 static struct builtin bintab[] = {
     BUILTIN("sched", 0, bin_sched, 0, -1, 0, NULL, NULL),
 };
@@ -194,7 +311,7 @@ boot_(Module m)
 {
     if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
 	return 1;
-    uaddlinknode(prepromptfns, &n_checksched);
+    addprepromptfn(&checksched);
     return 0;
 }
 
@@ -209,7 +326,7 @@ cleanup_(Module m)
 	zsfree(sch->cmd);
 	zfree(sch, sizeof(*sch));
     }
-    uremnode(prepromptfns, &n_checksched);
+    delprepromptfn(&checksched);
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }