summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2006-09-10 15:24:26 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2006-09-10 15:24:26 +0000
commitb726ead94e911e1ce3e8b582c315b3d6f83a6eb7 (patch)
tree6298d1215ba8a55aa8cac4fd536de3af91bbb4b2
parent638b0da9704add12fff91868efdfbb2dd35f0b54 (diff)
downloadzsh-b726ead94e911e1ce3e8b582c315b3d6f83a6eb7.tar.gz
zsh-b726ead94e911e1ce3e8b582c315b3d6f83a6eb7.zip
22676, 22678: extend sched and make it able to run events when waiting for
input
-rw-r--r--ChangeLog7
-rw-r--r--Doc/Zsh/mod_sched.yo39
-rw-r--r--README6
-rw-r--r--Src/Builtins/sched.c295
-rw-r--r--Src/Zle/zle_main.c459
-rw-r--r--Src/Zle/zle_thingy.c3
-rw-r--r--Src/init.c1
-rw-r--r--Src/subst.c29
-rw-r--r--Src/utils.c145
-rw-r--r--Src/zsh.h72
10 files changed, 766 insertions, 290 deletions
diff --git a/ChangeLog b/ChangeLog
index 12f1a78bd..22d4b1d34 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
2006-09-10 Peter Stephenson <p.w.stephenson@ntlworld.com>
+ * 22676, adapted as in 22678: README, Doc/Zsh/mod_sched.yo,
+ Src/init.c, Src/subst.c, Src/utils.c, Src/zsh.h,
+ Src/Builtins/sched.c, Src/Zle/zle_main.c, Src/Zle/zle_thingy.c:
+ make sched events work when waiting for input in zle; allow sched
+ to handle the trashzle(); improve the interface to sched both
+ internally and externally.
+
* 22681: configure.ac, Src/Modules/system.mdd: pass all
files defining errnos to awk so that it finds them all on
systems where the list of definitions is fragmented.
diff --git a/Doc/Zsh/mod_sched.yo b/Doc/Zsh/mod_sched.yo
index 1be550a79..82ba8550f 100644
--- a/Doc/Zsh/mod_sched.yo
+++ b/Doc/Zsh/mod_sched.yo
@@ -1,17 +1,42 @@
-texinode(The sched Module)(The stat Module)(The files Module)(Zsh Modules)
-sect(The sched Module)
-The tt(sched) module makes available one builtin command:
+COMMENT(!MOD!zsh/sched
+A builtin that provides a timed execution facility within the shell.
+!MOD!)
+The tt(zsh/sched) module makes available one builtin command:
startitem()
findex(sched)
cindex(timed execution)
cindex(execution, timed)
-xitem(tt(sched) [tt(PLUS())]var(hh)tt(:)var(mm) var(command) ...)
+xitem(tt(sched) [tt(-o)] [tt(PLUS())]var(hh)tt(:)var(mm)[:var(ss)] var(command) ...)
+xitem(tt(sched) [tt(-o)] [tt(PLUS())]var(seconds) var(command) ...)
item(tt(sched) [ tt(-)var(item) ])(
Make an entry in the scheduled list of commands to execute.
-The time may be specified in either absolute or relative time.
-With no arguments, prints the list of scheduled commands.
+The time may be specified in either absolute or relative time,
+and either as hours, minutes and (optionally) seconds separated by a
+colon, or seconds alone.
+An absolute number of seconds indicates the time since the epoch
+(1970/01/01 00:00); this is useful in combination with the features in
+the tt(zsh/datetime) module, see
+ifzman(the zsh/datetime module entry in zmanref(zshmodules))\
+ifnzman(noderef(The zsh/datetime Module))\
+.
+
+With no arguments, prints the list of scheduled commands. If the
+scheduled command has the tt(-o) flag set, this is shown at the
+start of the command.
+
With the argument `tt(-)var(item)', removes the given item
-from the list.
+from the list. The numbering of the list is continguous and entries are
+in time order, so the numbering can change when entries are added or
+deleted.
+
+Commands are executed either immediately before a prompt, or while
+the shell's line editor is waiting for input. In the latter case
+it is useful to be able to produce output that does not interfere
+with the line being edited. Providing the option tt(-o) causes
+the shell to clear the command line before the event and redraw it
+afterwards. This should be used with any scheduled event that produces
+visible output to the terminal; it is not needed, for example, with
+output that updates a terminal emulatorʼs title bar.
)
enditem()
diff --git a/README b/README
index 68ddfd61a..88b6be3a9 100644
--- a/README
+++ b/README
@@ -69,6 +69,12 @@ it is supported) there are three possible cases:
are allowed in identifiers even though the shell will recognise
alphanumeric multibyte characters.
+The sched builtin now keeps entries in time order. This means that
+after adding an entry the index of an existing entry used for deletion
+may change, if that entry had a later time than the new entry. However,
+deleting a entry with a later time will now never change the index of an
+entry with an earlier time, which could happen with the previous method.
+
The completion style pine-directory must now be set to use completion
for PINE mailbox folders; previously it had the default ~/mail. This
change was necessary because otherwise recursive directories under
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;
}
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 1d4636937..0e34a3fc3 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -122,7 +122,11 @@ int insmode;
mod_export int eofchar;
static int eofsent;
-static long keytimeout;
+/*
+ * Key timeout in hundredths of a second: we use time_t so
+ * that we only have the limits on one integer type to worry about.
+ */
+static time_t keytimeout;
#if defined(HAVE_SELECT) || defined(HAVE_POLL)
/* Terminal baud rate */
@@ -387,11 +391,110 @@ breakread(int fd, char *buf, int n)
# define read breakread
#endif
+/*
+ * Possible forms of timeout.
+ */
+enum ztmouttp {
+ /* No timeout in use. */
+ ZTM_NONE,
+ /*
+ * Key timeout in use (do_keytmout flag set). If this goes off
+ * we return without anything being read.
+ */
+ ZTM_KEY,
+ /*
+ * Function timeout in use (from timedfns list).
+ * If this goes off we call any functions which have reached
+ * the time and then continue processing.
+ */
+ ZTM_FUNC,
+ /*
+ * Timeout hit the maximum allowed; if it fires we
+ * need to recalculate. As we may use poll() for the timeout,
+ * which takes an int value in milliseconds, we might need this
+ * for times long in the future. (We make no attempt to extend
+ * the range of time beyond that of time_t, however; that seems
+ * like a losing battle.)
+ *
+ * For key timeouts we just limit the value to
+ * ZMAXTIMEOUT; that's already absurdly large.
+ *
+ * The following is the maximum signed range over 1024 (2^10), which
+ * is a little more convenient than 1000, but done differently
+ * to avoid problems with unsigned integers. We assume 8-bit bytes;
+ * there's no general way to fix up if that's wrong.
+ */
+ ZTM_MAX
+#define ZMAXTIMEOUT ((time_t)(1 << (sizeof(time_t)*8-11)))
+};
+
+struct ztmout {
+ /* Type of timeout setting, see enum above */
+ enum ztmouttp tp;
+ /*
+ * Value for timeout in 100ths of a second if type is not ZTM_NONE.
+ */
+ time_t exp100ths;
+};
+
+/*
+ * See if we need a timeout either for a key press or for a
+ * timed function.
+ */
+
+static void
+calc_timeout(struct ztmout *tmoutp, int do_keytmout)
+{
+ if (do_keytmout && keytimeout > 0) {
+ if (keytimeout > ZMAXTIMEOUT * 100 /* 24 days for a keypress???? */)
+ tmoutp->exp100ths = ZMAXTIMEOUT * 100;
+ else
+ tmoutp->exp100ths = keytimeout;
+ tmoutp->tp = ZTM_KEY;
+ } else
+ tmoutp->tp = ZTM_NONE;
+
+ if (timedfns) {
+ for (;;) {
+ LinkNode tfnode = firstnode(timedfns);
+ Timedfn tfdat;
+ time_t diff, exp100ths;
+
+ if (!tfnode)
+ break;
+
+ tfdat = (Timedfn)getdata(tfnode);
+ diff = tfdat->when - time(NULL);
+ if (diff < 0) {
+ /* Already due; call it and rescan. */
+ tfdat->func();
+ continue;
+ }
+
+ if (diff > ZMAXTIMEOUT) {
+ tmoutp->exp100ths = ZMAXTIMEOUT * 100;
+ tmoutp->tp = ZTM_MAX;
+ } else if (diff > 0) {
+ exp100ths = diff * 100;
+ if (tmoutp->tp != ZTM_KEY ||
+ exp100ths < tmoutp->exp100ths) {
+ tmoutp->exp100ths = exp100ths;
+ tmoutp->tp = ZTM_FUNC;
+ }
+ }
+ break;
+ }
+ /* In case we called a function which messed up the display... */
+ if (resetneeded)
+ zrefresh();
+ }
+}
+
static int
-raw_getbyte(int keytmout, char *cptr)
+raw_getbyte(int do_keytmout, char *cptr)
{
- long exp100ths;
int ret;
+ struct ztmout tmout;
#if defined(HAS_TIO) && \
(defined(sun) || (!defined(HAVE_POLL) && !defined(HAVE_SELECT)))
struct ttyinfo ti;
@@ -402,204 +505,254 @@ raw_getbyte(int keytmout, char *cptr)
# endif
#endif
+ calc_timeout(&tmout, do_keytmout);
+
/*
- * Handle timeouts and watched fd's. We only do one at once;
- * key timeouts take precedence. This saves tricky timing
- * problems with the key timeout.
+ * Handle timeouts and watched fd's. If a watched fd or a function
+ * timeout triggers we restart any key timeout. This is likely to
+ * be harmless: the combination is extremely rare and a function
+ * is likely to occupy the user for a little while anyway. We used
+ * to make timeouts take precedence, but we can't now that the
+ * timeouts may be external, so we may have both a permanent watched
+ * fd and a long-term timeout.
*/
- if ((nwatch || keytmout)
+ if ((nwatch || tmout.tp != ZTM_NONE)
#ifdef FIONREAD
&& ! delayzsetterm
#endif
) {
- if (!keytmout || keytimeout <= 0)
- exp100ths = 0;
- else if (keytimeout > 500)
- exp100ths = 500;
- else
- exp100ths = keytimeout;
#if defined(HAVE_SELECT) || defined(HAVE_POLL)
- if (!keytmout || exp100ths) {
- int i, errtry = 0, selret;
+ int i, errtry = 0, selret;
# ifdef HAVE_POLL
- int poll_timeout;
- int nfds;
- struct pollfd *fds;
-# else
- int fdmax;
- struct timeval *tvptr;
- struct timeval expire_tv;
+ int nfds;
+ struct pollfd *fds;
# endif
# if defined(HAS_TIO) && defined(sun)
- /*
- * Yes, I know this is complicated. Yes, I know we
- * already have three bits of code to poll the terminal
- * down below. No, I don't want to do this either.
- * However, it turns out on certain OSes, specifically
- * Solaris, that you can't poll typeahead for love nor
- * money without actually trying to read it. But
- * if we are trying to select (and we need to if we
- * are watching other fd's) we won't pick that up.
- * So we just try and read it without blocking in
- * the time-honoured (i.e. absurdly baroque) termios
- * fashion.
- */
- gettyinfo(&ti);
- ti.tio.c_cc[VMIN] = 0;
- settyinfo(&ti);
- ret = read(SHTTY, cptr, 1);
- ti.tio.c_cc[VMIN] = 1;
- settyinfo(&ti);
- if (ret > 0)
- return 1;
+ /*
+ * Yes, I know this is complicated. Yes, I know we
+ * already have three bits of code to poll the terminal
+ * down below. No, I don't want to do this either.
+ * However, it turns out on certain OSes, specifically
+ * Solaris, that you can't poll typeahead for love nor
+ * money without actually trying to read it. But
+ * if we are trying to select (and we need to if we
+ * are watching other fd's) we won't pick that up.
+ * So we just try and read it without blocking in
+ * the time-honoured (i.e. absurdly baroque) termios
+ * fashion.
+ */
+ gettyinfo(&ti);
+ ti.tio.c_cc[VMIN] = 0;
+ settyinfo(&ti);
+ ret = read(SHTTY, cptr, 1);
+ ti.tio.c_cc[VMIN] = 1;
+ settyinfo(&ti);
+ if (ret > 0)
+ return 1;
# endif
# ifdef HAVE_POLL
- nfds = keytmout ? 1 : 1 + nwatch;
- /* First pollfd is SHTTY, following are the nwatch fds */
- fds = zalloc(sizeof(struct pollfd) * nfds);
- if (exp100ths)
- poll_timeout = exp100ths * 10;
+ nfds = 1 + nwatch;
+ /* First pollfd is SHTTY, following are the nwatch fds */
+ fds = zalloc(sizeof(struct pollfd) * nfds);
+ fds[0].fd = SHTTY;
+ /*
+ * POLLIN, POLLIN, POLLIN,
+ * Keep those fd's POLLIN...
+ */
+ fds[0].events = POLLIN;
+ for (i = 0; i < nwatch; i++) {
+ fds[i+1].fd = watch_fds[i];
+ fds[i+1].events = POLLIN;
+ }
+# endif
+ do {
+# ifdef HAVE_POLL
+ int poll_timeout;
+
+ if (tmout.tp != ZTM_NONE)
+ poll_timeout = tmout.exp100ths * 10;
else
poll_timeout = -1;
- fds[0].fd = SHTTY;
- /*
- * POLLIN, POLLIN, POLLIN,
- * Keep those fd's POLLIN...
- */
- fds[0].events = POLLIN;
- if (!keytmout) {
+ selret = poll(fds, errtry ? 1 : nfds, poll_timeout);
+# else
+ int fdmax = SHTTY;
+ struct timeval *tvptr;
+ struct timeval expire_tv;
+
+ FD_ZERO(&foofd);
+ FD_SET(SHTTY, &foofd);
+ if (!errtry) {
for (i = 0; i < nwatch; i++) {
- fds[i+1].fd = watch_fds[i];
- fds[i+1].events = POLLIN;
+ int fd = watch_fds[i];
+ FD_SET(fd, &foofd);
+ if (fd > fdmax)
+ fdmax = fd;
}
}
-# else
- fdmax = SHTTY;
- tvptr = NULL;
- if (exp100ths) {
- expire_tv.tv_sec = exp100ths / 100;
- expire_tv.tv_usec = (exp100ths % 100) * 10000L;
+
+ if (tmout.tp != ZTM_NONE) {
+ expire_tv.tv_sec = tmout.exp100ths / 100;
+ expire_tv.tv_usec = (tmout.exp100ths % 100) * 10000L;
tvptr = &expire_tv;
}
+ else
+ tvptr = NULL;
+
+ selret = select(fdmax+1, (SELECT_ARG_2_T) & foofd,
+ NULL, NULL, tvptr);
# endif
- do {
-# ifdef HAVE_POLL
- selret = poll(fds, errtry ? 1 : nfds, poll_timeout);
-# else
- FD_ZERO(&foofd);
- FD_SET(SHTTY, &foofd);
- if (!keytmout && !errtry) {
- for (i = 0; i < nwatch; i++) {
- int fd = watch_fds[i];
- FD_SET(fd, &foofd);
- if (fd > fdmax)
- fdmax = fd;
- }
- }
- selret = select(fdmax+1, (SELECT_ARG_2_T) & foofd,
- NULL, NULL, tvptr);
-# endif
- /*
- * Make sure a user interrupt gets passed on straight away.
- */
- if (selret < 0 && errflag)
- break;
+ /*
+ * Make sure a user interrupt gets passed on straight away.
+ */
+ if (selret < 0 && errflag)
+ break;
+ /*
+ * Try to avoid errors on our special fd's from
+ * messing up reads from the terminal. Try first
+ * with all fds, then try unsetting the special ones.
+ */
+ if (selret < 0 && !errtry) {
+ errtry = 1;
+ continue;
+ }
+ if (selret == 0) {
/*
- * Try to avoid errors on our special fd's from
- * messing up reads from the terminal. Try first
- * with all fds, then try unsetting the special ones.
+ * Nothing ready and no error, so we timed out.
*/
- if (selret < 0 && !keytmout && !errtry) {
- errtry = 1;
- continue;
- }
- if (selret == 0) {
+ switch (tmout.tp) {
+ case ZTM_NONE:
+ /* keeps compiler happy if not debugging */
+#ifdef DEBUG
+ dputs("BUG: timeout fired with no timeout set.");
+#endif
+ /* treat as if a key timeout triggered */
+ /*FALLTHROUGH*/
+ case ZTM_KEY:
/* Special value -2 signals nothing ready */
selret = -2;
- }
- if (selret < 0)
break;
- if (!keytmout && nwatch) {
+
+ case ZTM_FUNC:
+ while (firstnode(timedfns)) {
+ Timedfn tfdat = (Timedfn)getdata(firstnode(timedfns));
+ /*
+ * It's possible a previous function took
+ * a long time to run (though it can't
+ * call zle recursively), so recalculate
+ * the time on each iteration.
+ */
+ time_t now = time(NULL);
+ if (tfdat->when > now)
+ break;
+ tfdat->func();
+ }
+ /* Function may have messed up the display */
+ if (resetneeded)
+ zrefresh();
+ /* We need to recalculate the timeout */
+ /*FALLTHROUGH*/
+ case ZTM_MAX:
/*
- * Copy the details of the watch fds in case the
- * user decides to delete one from inside the
- * handler function.
+ * Reached the limit of our range, but not the
+ * actual timeout; recalculate the timeout.
+ * We're cheating with the key timeout here:
+ * if one clashed with a function timeout we
+ * reconsider the key timeout from scratch.
+ * The effect of this is microscopic.
*/
- int lnwatch = nwatch;
- int *lwatch_fds = zalloc(lnwatch*sizeof(int));
- char **lwatch_funcs = zarrdup(watch_funcs);
- memcpy(lwatch_fds, watch_fds, lnwatch*sizeof(int));
- for (i = 0; i < lnwatch; i++) {
- if (
+ calc_timeout(&tmout, do_keytmout);
+ break;
+ }
+ /*
+ * If we handled the timeout successfully,
+ * carry on.
+ */
+ if (selret == 0)
+ continue;
+ }
+ /* If error or unhandled timeout, give up. */
+ if (selret < 0)
+ break;
+ if (nwatch && !errtry) {
+ /*
+ * Copy the details of the watch fds in case the
+ * user decides to delete one from inside the
+ * handler function.
+ */
+ int lnwatch = nwatch;
+ int *lwatch_fds = zalloc(lnwatch*sizeof(int));
+ char **lwatch_funcs = zarrdup(watch_funcs);
+ memcpy(lwatch_fds, watch_fds, lnwatch*sizeof(int));
+ for (i = 0; i < lnwatch; i++) {
+ if (
# ifdef HAVE_POLL
- (fds[i+1].revents & POLLIN)
+ (fds[i+1].revents & POLLIN)
# else
- FD_ISSET(lwatch_fds[i], &foofd)
+ FD_ISSET(lwatch_fds[i], &foofd)
# endif
- ) {
- /* Handle the fd. */
- LinkList funcargs = znewlinklist();
- zaddlinknode(funcargs, ztrdup(lwatch_funcs[i]));
- {
- char buf[BDIGBUFSIZE];
- convbase(buf, lwatch_fds[i], 10);
- zaddlinknode(funcargs, ztrdup(buf));
- }
+ ) {
+ /* Handle the fd. */
+ LinkList funcargs = znewlinklist();
+ zaddlinknode(funcargs, ztrdup(lwatch_funcs[i]));
+ {
+ char buf[BDIGBUFSIZE];
+ convbase(buf, lwatch_fds[i], 10);
+ zaddlinknode(funcargs, ztrdup(buf));
+ }
# ifdef HAVE_POLL
# ifdef POLLERR
- if (fds[i+1].revents & POLLERR)
- zaddlinknode(funcargs, ztrdup("err"));
+ if (fds[i+1].revents & POLLERR)
+ zaddlinknode(funcargs, ztrdup("err"));
# endif
# ifdef POLLHUP
- if (fds[i+1].revents & POLLHUP)
- zaddlinknode(funcargs, ztrdup("hup"));
+ if (fds[i+1].revents & POLLHUP)
+ zaddlinknode(funcargs, ztrdup("hup"));
# endif
# ifdef POLLNVAL
- if (fds[i+1].revents & POLLNVAL)
- zaddlinknode(funcargs, ztrdup("nval"));
+ if (fds[i+1].revents & POLLNVAL)
+ zaddlinknode(funcargs, ztrdup("nval"));
# endif
# endif
- callhookfunc(lwatch_funcs[i], funcargs);
- if (errflag) {
- /* No sensible way of handling errors here */
- errflag = 0;
- /*
- * Paranoia: don't run the hooks again this
- * time.
- */
- errtry = 1;
- }
- freelinklist(funcargs, freestr);
+ callhookfunc(lwatch_funcs[i], funcargs);
+ if (errflag) {
+ /* No sensible way of handling errors here */
+ errflag = 0;
+ /*
+ * Paranoia: don't run the hooks again this
+ * time.
+ */
+ errtry = 1;
}
+ freelinklist(funcargs, freestr);
}
- /* Function may have invalidated the display. */
- if (resetneeded)
- zrefresh();
- zfree(lwatch_fds, lnwatch*sizeof(int));
- freearray(lwatch_funcs);
}
- } while (!
+ /* Function may have invalidated the display. */
+ if (resetneeded)
+ zrefresh();
+ zfree(lwatch_fds, lnwatch*sizeof(int));
+ freearray(lwatch_funcs);
+ }
+ } while (!
# ifdef HAVE_POLL
- (fds[0].revents & POLLIN)
+ (fds[0].revents & POLLIN)
# else
- FD_ISSET(SHTTY, &foofd)
+ FD_ISSET(SHTTY, &foofd)
# endif
- );
+ );
# ifdef HAVE_POLL
- zfree(fds, sizeof(struct pollfd) * nfds);
+ zfree(fds, sizeof(struct pollfd) * nfds);
# endif
- if (selret < 0)
- return selret;
- }
+ if (selret < 0)
+ return selret;
#else
# ifdef HAS_TIO
ti = shttyinfo;
ti.tio.c_lflag &= ~ICANON;
ti.tio.c_cc[VMIN] = 0;
- ti.tio.c_cc[VTIME] = exp100ths / 10;
+ ti.tio.c_cc[VTIME] = tmout.exp100ths / 10;
# ifdef HAVE_TERMIOS_H
tcsetattr(SHTTY, TCSANOW, &ti.tio);
# else
@@ -623,7 +776,7 @@ raw_getbyte(int keytmout, char *cptr)
/**/
mod_export int
-getbyte(int keytmout, int *timeout)
+getbyte(int do_keytmout, int *timeout)
{
char cc;
unsigned int ret;
@@ -656,7 +809,7 @@ getbyte(int keytmout, int *timeout)
for (;;) {
int q = queue_signal_level();
dont_queue_signals();
- r = raw_getbyte(keytmout, &cc);
+ r = raw_getbyte(do_keytmout, &cc);
restore_queue_signals(q);
if (r == -2) {
/* timeout */
@@ -733,9 +886,9 @@ getbyte(int keytmout, int *timeout)
/**/
mod_export ZLE_INT_T
-getfullchar(int keytmout)
+getfullchar(int do_keytmout)
{
- int inchar = getbyte(keytmout, NULL);
+ int inchar = getbyte(do_keytmout, NULL);
#ifdef MULTIBYTE_SUPPORT
return getrestchar(inchar);
@@ -938,7 +1091,7 @@ zleread(char **lp, char **rp, int flags, int context)
return shingetline();
}
- keytimeout = getiparam("KEYTIMEOUT");
+ keytimeout = (time_t)getiparam("KEYTIMEOUT");
if (!shout) {
if (SHTTY != -1)
init_shout();
@@ -1551,7 +1704,7 @@ zle_resetprompt(void)
mod_export void
trashzle(void)
{
- if (zleactive) {
+ if (zleactive && !trashedzle) {
/* This zrefresh() is just to get the main editor display right and *
* get the cursor in the right place. For that reason, we disable *
* list display (which would otherwise result in infinite *
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 72d3314ff..debd31a28 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -721,8 +721,7 @@ bin_zle_invalidate(UNUSED(char *name), UNUSED(char **args), UNUSED(Options ops),
* true if a completion widget is active.
*/
if (zleactive) {
- if (!trashedzle)
- trashzle();
+ trashzle();
return 0;
} else
return 1;
diff --git a/Src/init.c b/Src/init.c
index 7447842ab..9d6a0514e 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -869,7 +869,6 @@ setupvals(void)
nohistsave = 1;
dirstack = znewlinklist();
bufstack = znewlinklist();
- prepromptfns = znewlinklist();
hsubl = hsubr = NULL;
lastpid = 0;
bshin = SHIN ? fdopen(SHIN, "r") : stdin;
diff --git a/Src/subst.c b/Src/subst.c
index 9f2703326..67afd0f03 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -70,9 +70,24 @@ prefork(LinkList list, int flags)
return;
}
} else {
- if (isset(SHFILEEXPANSION))
- filesub((char **)getaddrdata(node),
- flags & (PF_TYPESET|PF_ASSIGN));
+ if (isset(SHFILEEXPANSION)) {
+ /*
+ * Here and below we avoid taking the address
+ * of a void * and then pretending it's a char **
+ * instead of a void ** by a little inefficiency.
+ * This could be avoided with some extra linked list
+ * machinery, but that would need quite a lot of work
+ * to ensure consistency. What we really need is
+ * templates...
+ */
+ char *cptr = (char *)getdata(node);
+ filesub(&cptr, flags & (PF_TYPESET|PF_ASSIGN));
+ /*
+ * The assignment is so simple it's not worth
+ * testing if cptr changed...
+ */
+ setdata(node, cptr);
+ }
if (!(node = stringsubst(list, node, flags & PF_SINGLE, asssub))) {
unqueue_signals();
return;
@@ -92,9 +107,11 @@ prefork(LinkList list, int flags)
xpandbraces(list, &node);
}
}
- if (unset(SHFILEEXPANSION))
- filesub((char **)getaddrdata(node),
- flags & (PF_TYPESET|PF_ASSIGN));
+ if (unset(SHFILEEXPANSION)) {
+ char *cptr = (char *)getdata(node);
+ filesub(&cptr, flags & (PF_TYPESET|PF_ASSIGN));
+ setdata(node, cptr);
+ }
} else if (!(flags & PF_SINGLE) && !keep)
uremnode(list, node);
if (errflag) {
diff --git a/Src/utils.c b/Src/utils.c
index 2bfae667c..3a5246bd1 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -911,10 +911,141 @@ dircmp(char *s, char *t)
return 1;
}
-/* extra functions to call before displaying the prompt */
+/*
+ * Extra functions to call before displaying the prompt.
+ * The data is a Prepromptfn.
+ */
+
+static LinkList prepromptfns;
+
+/* Add a function to the list of pre-prompt functions. */
/**/
-mod_export LinkList prepromptfns;
+mod_export void
+addprepromptfn(voidvoidfnptr_t func)
+{
+ Prepromptfn ppdat = (Prepromptfn)zalloc(sizeof(struct prepromptfn));
+ ppdat->func = func;
+ if (!prepromptfns)
+ prepromptfns = znewlinklist();
+ zaddlinknode(prepromptfns, ppdat);
+}
+
+/* Remove a function from the list of pre-prompt functions. */
+
+/**/
+mod_export void
+delprepromptfn(voidvoidfnptr_t func)
+{
+ LinkNode ln;
+
+ for (ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) {
+ Prepromptfn ppdat = (Prepromptfn)getdata(ln);
+ if (ppdat->func == func) {
+ (void)remnode(prepromptfns, ln);
+ zfree(ppdat, sizeof(struct prepromptfn));
+ return;
+ }
+ }
+#ifdef DEBUG
+ dputs("BUG: failed to delete node from prepromptfns");
+#endif
+}
+
+/*
+ * Functions to call at a particular time even if not at
+ * the prompt. This is handled by zle. The data is a
+ * Timedfn. The functions must be in time order, but this
+ * is enforced by addtimedfn().
+ *
+ * Note on debugging: the code in sched.c currently assumes it's
+ * the only user of timedfns for the purposes of checking whether
+ * there's a function on the list. If this becomes no longer the case,
+ * the DPUTS() tests in sched.c need rewriting.
+ */
+
+/**/
+mod_export LinkList timedfns;
+
+/* Add a function to the list of timed functions. */
+
+/**/
+mod_export void
+addtimedfn(voidvoidfnptr_t func, time_t when)
+{
+ Timedfn tfdat = (Timedfn)zalloc(sizeof(struct timedfn));
+ tfdat->func = func;
+ tfdat->when = when;
+
+ if (!timedfns) {
+ timedfns = znewlinklist();
+ zaddlinknode(timedfns, tfdat);
+ } else {
+ LinkNode ln = firstnode(timedfns);
+
+ /*
+ * Insert the new element in the linked list. We do
+ * rather too much work here since the standard
+ * functions insert after a given node, whereas we
+ * want to insert the new data before the first element
+ * with a greater time.
+ *
+ * In practice, the only use of timed functions is
+ * sched, which only adds the one function; so this
+ * whole branch isn't used beyond the following block.
+ */
+ if (!ln) {
+ zaddlinknode(timedfns, tfdat);
+ return;
+ }
+ for (;;) {
+ Timedfn tfdat2;
+ LinkNode next = nextnode(ln);
+ if (!next) {
+ zaddlinknode(timedfns, tfdat);
+ return;
+ }
+ tfdat2 = (Timedfn)getdata(next);
+ if (when < tfdat2->when) {
+ zinsertlinknode(timedfns, ln, tfdat);
+ return;
+ }
+ ln = next;
+ }
+ }
+}
+
+/*
+ * Delete a function from the list of timed functions.
+ * Note that if the function apperas multiple times only
+ * the first occurrence will be removed.
+ *
+ * Note also that when zle calls the function it does *not*
+ * automatically delete the entry from the list. That must
+ * be done by the function called. This is recommended as otherwise
+ * the function will keep being called immediately. (It just so
+ * happens this "feature" fits in well with the only current use
+ * of timed functions.)
+ */
+
+/**/
+mod_export void
+deltimedfn(voidvoidfnptr_t func)
+{
+ LinkNode ln;
+
+ for (ln = firstnode(timedfns); ln; ln = nextnode(ln)) {
+ Timedfn ppdat = (Timedfn)getdata(ln);
+ if (ppdat->func == func) {
+ (void)remnode(timedfns, ln);
+ zfree(ppdat, sizeof(struct timedfn));
+ return;
+ }
+ }
+#ifdef DEBUG
+ dputs("BUG: failed to delete node from timedfns");
+#endif
+}
/* the last time we checked mail */
@@ -1027,10 +1158,12 @@ preprompt(void)
lastmailcheck = time(NULL);
}
- /* Some people have claimed that C performs type *
- * checking, but they were later found to be lying. */
- for(ln = firstnode(prepromptfns); ln; ln = nextnode(ln))
- (**(void (**) _((void)))getdata(ln))();
+ if (prepromptfns) {
+ for(ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) {
+ Prepromptfn ppnode = (Prepromptfn)getdata(ln);
+ ppnode->func();
+ }
+ }
}
/**/
diff --git a/Src/zsh.h b/Src/zsh.h
index 3c455b939..19d8a368f 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -333,40 +333,38 @@ enum {
/* Abstract types for zsh */
/**************************/
-typedef struct linknode *LinkNode;
-typedef union linkroot *LinkList;
-typedef struct hashnode *HashNode;
-typedef struct hashtable *HashTable;
-
-typedef struct optname *Optname;
-typedef struct reswd *Reswd;
typedef struct alias *Alias;
-typedef struct param *Param;
-typedef struct paramdef *Paramdef;
+typedef struct asgment *Asgment;
+typedef struct builtin *Builtin;
typedef struct cmdnam *Cmdnam;
-typedef struct shfunc *Shfunc;
+typedef struct complist *Complist;
+typedef struct conddef *Conddef;
typedef struct funcstack *Funcstack;
typedef struct funcwrap *FuncWrap;
-typedef struct options *Options;
-typedef struct builtin *Builtin;
-typedef struct nameddir *Nameddir;
-typedef struct module *Module;
-typedef struct linkedmod *Linkedmod;
-
-typedef struct patprog *Patprog;
-typedef struct process *Process;
-typedef struct job *Job;
-typedef struct value *Value;
-typedef struct conddef *Conddef;
-typedef struct redir *Redir;
-typedef struct complist *Complist;
+typedef struct hashnode *HashNode;
+typedef struct hashtable *HashTable;
typedef struct heap *Heap;
typedef struct heapstack *Heapstack;
typedef struct histent *Histent;
typedef struct hookdef *Hookdef;
-
-typedef struct asgment *Asgment;
-
+typedef struct job *Job;
+typedef struct linkedmod *Linkedmod;
+typedef struct linknode *LinkNode;
+typedef union linkroot *LinkList;
+typedef struct module *Module;
+typedef struct nameddir *Nameddir;
+typedef struct options *Options;
+typedef struct optname *Optname;
+typedef struct param *Param;
+typedef struct paramdef *Paramdef;
+typedef struct patprog *Patprog;
+typedef struct prepromptfn *Prepromptfn;
+typedef struct process *Process;
+typedef struct redir *Redir;
+typedef struct reswd *Reswd;
+typedef struct shfunc *Shfunc;
+typedef struct timedfn *Timedfn;
+typedef struct value *Value;
/********************************/
/* Definitions for linked lists */
@@ -432,6 +430,28 @@ union linkroot {
__n0.dat = (void *) (V0); \
} while (0)
+/*************************************/
+/* Specific elements of linked lists */
+/*************************************/
+
+typedef void (*voidvoidfnptr_t) _((void));
+
+/*
+ * Element of the prepromptfns list.
+ */
+struct prepromptfn {
+ voidvoidfnptr_t func;
+};
+
+
+/*
+ * Element of the timedfns list.
+ */
+struct timedfn {
+ voidvoidfnptr_t func;
+ time_t when;
+};
+
/********************************/
/* Definitions for syntax trees */
/********************************/