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:
parent
638b0da970
commit
b726ead94e
10 changed files with 814 additions and 338 deletions
|
@ -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.
|
||||
|
|
|
@ -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
6
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
29
Src/subst.c
29
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) {
|
||||
|
|
145
Src/utils.c
145
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**/
|
||||
|
|
72
Src/zsh.h
72
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 */
|
||||
/********************************/
|
||||
|
|
Loading…
Reference in a new issue