1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-01-01 05:16:05 +01: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

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

View file

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

6
README
View file

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

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