1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-10-23 04:30:24 +02:00

22676, 22678: extend sched and make it able to run events when waiting for

input
This commit is contained in:
Peter Stephenson 2006-09-10 15:24:26 +00:00
parent 638b0da970
commit b726ead94e
10 changed files with 814 additions and 338 deletions

View file

@ -34,122 +34,23 @@
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;
/**/
static int
bin_sched(UNUSED(char *nam), char **argv, UNUSED(Options ops), UNUSED(int func))
{
char *s = *argv++;
time_t t;
long h, m;
struct tm *tm;
struct schedcmd *sch, *sch2, *schl;
int sn;
/* If the argument begins with a -, remove the specified item from the
schedule. */
if (s && *s == '-') {
sn = atoi(s + 1);
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");
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];
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);
}
return 0;
} else if (!*argv) {
/* other than the two cases above, sched *
*requires at least two arguments */
zwarnnam("sched", "not enough arguments");
return 1;
}
/* The first argument specifies the time to schedule the command for. The
remaining arguments form the command. */
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 != ':') {
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') {
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->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. */
/**/
@ -157,25 +58,241 @@ static void
checksched(void)
{
time_t t;
struct schedcmd *sch, *schl;
struct schedcmd *sch;
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;
/*
* 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 void (*p_checksched) _((void)) = checksched;
static struct linknode n_checksched = { NULL, NULL, &p_checksched };
/**/
static int
bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
{
char *s, **argptr;
time_t t;
long h, m, sec;
struct tm *tm;
struct schedcmd *sch, *sch2, *schl;
int sn, flags = 0;
/* If the argument begins with a -, remove the specified item from the
schedule. */
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 = 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;
}
}
/* given no arguments, display the schedule list */
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);
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 (!argptr[1]) {
/* other than the two cases above, sched *
*requires at least two arguments */
zwarnnam("sched", "not enough arguments");
return 1;
}
/* 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 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;
}
} else {
/*
* 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;
}
}
/* The time has been calculated; now add the new entry to the linked list
of scheduled commands. */
sch = (struct schedcmd *) zalloc(sizeof *sch);
sch->time = t;
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 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;
}

View file

@ -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
static int
raw_getbyte(int keytmout, char *cptr)
/*
* 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 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;
/*
* 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) {
/*
* Make sure a user interrupt gets passed on straight away.
* Nothing ready and no error, so we timed out.
*/
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 && !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) {
/*
* 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)
# else
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));
}
# ifdef HAVE_POLL
# ifdef POLLERR
if (fds[i+1].revents & POLLERR)
zaddlinknode(funcargs, ztrdup("err"));
# endif
# ifdef POLLHUP
if (fds[i+1].revents & POLLHUP)
zaddlinknode(funcargs, ztrdup("hup"));
# endif
# ifdef POLLNVAL
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);
}
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 invalidated the display. */
/* Function may have messed up the display */
if (resetneeded)
zrefresh();
zfree(lwatch_fds, lnwatch*sizeof(int));
freearray(lwatch_funcs);
/* We need to recalculate the timeout */
/*FALLTHROUGH*/
case ZTM_MAX:
/*
* 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.
*/
calc_timeout(&tmout, do_keytmout);
break;
}
} while (!
# ifdef HAVE_POLL
(fds[0].revents & POLLIN)
# else
FD_ISSET(SHTTY, &foofd)
# endif
);
# ifdef HAVE_POLL
zfree(fds, sizeof(struct pollfd) * nfds);
# endif
/*
* If we handled the timeout successfully,
* carry on.
*/
if (selret == 0)
continue;
}
/* If error or unhandled timeout, give up. */
if (selret < 0)
return selret;
}
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)
# else
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));
}
# ifdef HAVE_POLL
# ifdef POLLERR
if (fds[i+1].revents & POLLERR)
zaddlinknode(funcargs, ztrdup("err"));
# endif
# ifdef POLLHUP
if (fds[i+1].revents & POLLHUP)
zaddlinknode(funcargs, ztrdup("hup"));
# endif
# ifdef POLLNVAL
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);
}
}
/* 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)
# else
FD_ISSET(SHTTY, &foofd)
# endif
);
# ifdef HAVE_POLL
zfree(fds, sizeof(struct pollfd) * nfds);
# endif
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 *

View file

@ -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;

View file

@ -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;

View file

@ -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) {

View file

@ -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();
}
}
}
/**/

View file

@ -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 */
/********************************/