mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-11 13:01:28 +02: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>
|
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
|
* 22681: configure.ac, Src/Modules/system.mdd: pass all
|
||||||
files defining errnos to awk so that it finds them all on
|
files defining errnos to awk so that it finds them all on
|
||||||
systems where the list of definitions is fragmented.
|
systems where the list of definitions is fragmented.
|
||||||
|
|
|
@ -1,17 +1,42 @@
|
||||||
texinode(The sched Module)(The stat Module)(The files Module)(Zsh Modules)
|
COMMENT(!MOD!zsh/sched
|
||||||
sect(The sched Module)
|
A builtin that provides a timed execution facility within the shell.
|
||||||
The tt(sched) module makes available one builtin command:
|
!MOD!)
|
||||||
|
The tt(zsh/sched) module makes available one builtin command:
|
||||||
|
|
||||||
startitem()
|
startitem()
|
||||||
findex(sched)
|
findex(sched)
|
||||||
cindex(timed execution)
|
cindex(timed execution)
|
||||||
cindex(execution, timed)
|
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) ])(
|
item(tt(sched) [ tt(-)var(item) ])(
|
||||||
Make an entry in the scheduled list of commands to execute.
|
Make an entry in the scheduled list of commands to execute.
|
||||||
The time may be specified in either absolute or relative time.
|
The time may be specified in either absolute or relative time,
|
||||||
With no arguments, prints the list of scheduled commands.
|
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
|
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()
|
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
|
are allowed in identifiers even though the shell will recognise
|
||||||
alphanumeric multibyte characters.
|
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
|
The completion style pine-directory must now be set to use completion
|
||||||
for PINE mailbox folders; previously it had the default ~/mail. This
|
for PINE mailbox folders; previously it had the default ~/mail. This
|
||||||
change was necessary because otherwise recursive directories under
|
change was necessary because otherwise recursive directories under
|
||||||
|
|
|
@ -34,61 +34,156 @@
|
||||||
|
|
||||||
typedef struct schedcmd *Schedcmd;
|
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 {
|
||||||
struct schedcmd *next;
|
struct schedcmd *next;
|
||||||
char *cmd; /* command to run */
|
char *cmd; /* command to run */
|
||||||
time_t time; /* when to run it */
|
time_t time; /* when to run it */
|
||||||
|
int flags; /* flags as above */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* the list of sched jobs pending */
|
/* the list of sched jobs pending */
|
||||||
|
|
||||||
static struct schedcmd *schedcmds;
|
static struct schedcmd *schedcmds;
|
||||||
|
|
||||||
|
/* Check scheduled commands; call this function from time to time. */
|
||||||
|
|
||||||
|
/**/
|
||||||
|
static void
|
||||||
|
checksched(void)
|
||||||
|
{
|
||||||
|
time_t t;
|
||||||
|
struct schedcmd *sch;
|
||||||
|
|
||||||
|
if(!schedcmds)
|
||||||
|
return;
|
||||||
|
t = time(NULL);
|
||||||
|
/*
|
||||||
|
* List is ordered, so we only need to consider the
|
||||||
|
* head element.
|
||||||
|
*/
|
||||||
|
while (schedcmds && schedcmds->time <= t) {
|
||||||
|
/*
|
||||||
|
* Remove the entry to be executed from the list
|
||||||
|
* before execution: this makes quite sure that
|
||||||
|
* the entry hasn't been monkeyed with when we
|
||||||
|
* free it.
|
||||||
|
*/
|
||||||
|
sch = schedcmds;
|
||||||
|
schedcmds = sch->next;
|
||||||
|
/*
|
||||||
|
* Delete from the timed function list now in case
|
||||||
|
* the called code reschedules.
|
||||||
|
*/
|
||||||
|
deltimedfn(checksched);
|
||||||
|
|
||||||
|
if ((sch->flags & SCHEDFLAG_TRASH_ZLE) && zleactive)
|
||||||
|
trashzleptr();
|
||||||
|
execstring(sch->cmd, 0, 0);
|
||||||
|
zsfree(sch->cmd);
|
||||||
|
zfree(sch, sizeof(struct schedcmd));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fix time for future events.
|
||||||
|
* I had this outside the loop, for a little extra efficiency.
|
||||||
|
* However, it then occurred to me that having the list of
|
||||||
|
* forthcoming entries up to date could be regarded as
|
||||||
|
* a feature, and the inefficiency is negligible.
|
||||||
|
*/
|
||||||
|
if (schedcmds) {
|
||||||
|
/*
|
||||||
|
* We need to delete the function from the list again,
|
||||||
|
* in case called code rescheduled. This is almost
|
||||||
|
* as cheap as checking if it's in the list already.
|
||||||
|
*/
|
||||||
|
deltimedfn(checksched);
|
||||||
|
DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (1)"); addtimedfn(checksched, schedcmds->time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
static int
|
static int
|
||||||
bin_sched(UNUSED(char *nam), char **argv, UNUSED(Options ops), UNUSED(int func))
|
bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
|
||||||
{
|
{
|
||||||
char *s = *argv++;
|
char *s, **argptr;
|
||||||
time_t t;
|
time_t t;
|
||||||
long h, m;
|
long h, m, sec;
|
||||||
struct tm *tm;
|
struct tm *tm;
|
||||||
struct schedcmd *sch, *sch2, *schl;
|
struct schedcmd *sch, *sch2, *schl;
|
||||||
int sn;
|
int sn, flags = 0;
|
||||||
|
|
||||||
/* If the argument begins with a -, remove the specified item from the
|
/* If the argument begins with a -, remove the specified item from the
|
||||||
schedule. */
|
schedule. */
|
||||||
if (s && *s == '-') {
|
for (argptr = argv; *argptr && **argptr == '-'; argptr++) {
|
||||||
sn = atoi(s + 1);
|
char *arg = *argptr + 1;
|
||||||
|
if (idigit(*arg)) {
|
||||||
|
sn = atoi(arg);
|
||||||
|
|
||||||
if (!sn) {
|
if (!sn) {
|
||||||
zwarnnam("sched", "usage for delete: sched -<item#>.");
|
zwarnnam("sched", "usage for delete: sched -<item#>.");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds, sn--;
|
for (schl = NULL, sch = schedcmds, sn--;
|
||||||
sch && sn; sch = (schl = sch)->next, sn--);
|
sch && sn; sch = (schl = sch)->next, sn--);
|
||||||
if (!sch) {
|
if (!sch) {
|
||||||
zwarnnam("sched", "not that many entries");
|
zwarnnam("sched", "not that many entries");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (schl)
|
||||||
schl->next = sch->next;
|
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);
|
zsfree(sch->cmd);
|
||||||
zfree(sch, sizeof(struct schedcmd));
|
zfree(sch, sizeof(struct schedcmd));
|
||||||
|
|
||||||
return 0;
|
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 */
|
/* given no arguments, display the schedule list */
|
||||||
if (!s) {
|
if (!*argptr) {
|
||||||
char tbuf[40];
|
char tbuf[40], *flagstr, *endstr;
|
||||||
|
|
||||||
for (sn = 1, sch = schedcmds; sch; sch = sch->next, sn++) {
|
for (sn = 1, sch = schedcmds; sch; sch = sch->next, sn++) {
|
||||||
t = sch->time;
|
t = sch->time;
|
||||||
tm = localtime(&t);
|
tm = localtime(&t);
|
||||||
ztrftime(tbuf, 20, "%a %b %e %k:%M:%S", tm);
|
ztrftime(tbuf, 20, "%a %b %e %k:%M:%S", tm);
|
||||||
printf("%3d %s %s\n", sn, tbuf, sch->cmd);
|
if (sch->flags & SCHEDFLAG_TRASH_ZLE)
|
||||||
|
flagstr = "-o ";
|
||||||
|
else
|
||||||
|
flagstr = "";
|
||||||
|
if (*sch->cmd == '-')
|
||||||
|
endstr = "-- ";
|
||||||
|
else
|
||||||
|
endstr = "";
|
||||||
|
printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr, sch->cmd);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} else if (!*argv) {
|
} else if (!argptr[1]) {
|
||||||
/* other than the two cases above, sched *
|
/* other than the two cases above, sched *
|
||||||
*requires at least two arguments */
|
*requires at least two arguments */
|
||||||
zwarnnam("sched", "not enough arguments");
|
zwarnnam("sched", "not enough arguments");
|
||||||
|
@ -97,33 +192,51 @@ bin_sched(UNUSED(char *nam), char **argv, UNUSED(Options ops), UNUSED(int func))
|
||||||
|
|
||||||
/* The first argument specifies the time to schedule the command for. The
|
/* The first argument specifies the time to schedule the command for. The
|
||||||
remaining arguments form the command. */
|
remaining arguments form the command. */
|
||||||
|
s = *argptr++;
|
||||||
if (*s == '+') {
|
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
|
* + introduces a relative time. The rest of the argument may be an
|
||||||
numbers have been extracted, and the format verified, the resulting
|
* hour:minute offset from the current time. Once the hour and minute
|
||||||
offset is simply added to the current time. */
|
* numbers have been extracted, and the format verified, the resulting
|
||||||
h = zstrtol(s + 1, &s, 10);
|
* offset is simply added to the current time.
|
||||||
if (*s != ':') {
|
*/
|
||||||
zwarnnam("sched", "bad time specifier");
|
zlong zl = zstrtol(s + 1, &s, 10);
|
||||||
return 1;
|
if (*s == ':') {
|
||||||
}
|
m = (long)zstrtol(s + 1, &s, 10);
|
||||||
m = zstrtol(s + 1, &s, 10);
|
if (*s == ':')
|
||||||
|
sec = (long)zstrtol(s + 1, &s, 10);
|
||||||
|
else
|
||||||
|
sec = 0;
|
||||||
if (*s) {
|
if (*s) {
|
||||||
zwarnnam("sched", "bad time specifier");
|
zwarnnam("sched", "bad time specifier");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
t = time(NULL) + h * 3600 + m * 60;
|
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 {
|
} 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");
|
zwarnnam("sched", "bad time specifier");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
m = zstrtol(s + 1, &s, 10);
|
} 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') {
|
if (*s && *s != 'a' && *s != 'A' && *s != 'p' && *s != 'P') {
|
||||||
zwarnnam("sched", "bad time specifier");
|
zwarnnam("sched", "bad time specifier");
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -133,50 +246,54 @@ bin_sched(UNUSED(char *nam), char **argv, UNUSED(Options ops), UNUSED(int func))
|
||||||
t -= tm->tm_sec + tm->tm_min * 60 + tm->tm_hour * 3600;
|
t -= tm->tm_sec + tm->tm_min * 60 + tm->tm_hour * 3600;
|
||||||
if (*s == 'p' || *s == 'P')
|
if (*s == 'p' || *s == 'P')
|
||||||
h += 12;
|
h += 12;
|
||||||
t += h * 3600 + m * 60;
|
t += h * 3600 + m * 60 + sec;
|
||||||
/* If the specified time is before the current time, it must refer to
|
/*
|
||||||
tomorrow. */
|
* If the specified time is before the current time, it must refer
|
||||||
|
* to tomorrow.
|
||||||
|
*/
|
||||||
if (t < time(NULL))
|
if (t < time(NULL))
|
||||||
t += 3600 * 24;
|
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
|
/* The time has been calculated; now add the new entry to the linked list
|
||||||
of scheduled commands. */
|
of scheduled commands. */
|
||||||
sch = (struct schedcmd *) zshcalloc(sizeof *sch);
|
sch = (struct schedcmd *) zalloc(sizeof *sch);
|
||||||
sch->time = t;
|
sch->time = t;
|
||||||
sch->cmd = zjoin(argv, ' ', 0);
|
sch->cmd = zjoin(argptr, ' ', 0);
|
||||||
sch->next = NULL;
|
sch->flags = flags;
|
||||||
for (sch2 = (struct schedcmd *)&schedcmds; sch2->next; sch2 = sch2->next);
|
/* 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;
|
sch2->next = sch;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sch->next = NULL;
|
||||||
|
schedcmds = sch;
|
||||||
|
DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (4)");
|
||||||
|
addtimedfn(checksched, t);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check scheduled commands; call this function from time to time. */
|
|
||||||
|
|
||||||
/**/
|
|
||||||
static void
|
|
||||||
checksched(void)
|
|
||||||
{
|
|
||||||
time_t t;
|
|
||||||
struct schedcmd *sch, *schl;
|
|
||||||
|
|
||||||
if(!schedcmds)
|
|
||||||
return;
|
|
||||||
t = time(NULL);
|
|
||||||
for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds; sch;
|
|
||||||
sch = (schl = sch)->next) {
|
|
||||||
if (sch->time <= t) {
|
|
||||||
execstring(sch->cmd, 0, 0);
|
|
||||||
schl->next = sch->next;
|
|
||||||
zsfree(sch->cmd);
|
|
||||||
zfree(sch, sizeof(struct schedcmd));
|
|
||||||
sch = schl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void (*p_checksched) _((void)) = checksched;
|
|
||||||
static struct linknode n_checksched = { NULL, NULL, &p_checksched };
|
|
||||||
|
|
||||||
static struct builtin bintab[] = {
|
static struct builtin bintab[] = {
|
||||||
BUILTIN("sched", 0, bin_sched, 0, -1, 0, NULL, NULL),
|
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)))
|
if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
|
||||||
return 1;
|
return 1;
|
||||||
uaddlinknode(prepromptfns, &n_checksched);
|
addprepromptfn(&checksched);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +326,7 @@ cleanup_(Module m)
|
||||||
zsfree(sch->cmd);
|
zsfree(sch->cmd);
|
||||||
zfree(sch, sizeof(*sch));
|
zfree(sch, sizeof(*sch));
|
||||||
}
|
}
|
||||||
uremnode(prepromptfns, &n_checksched);
|
delprepromptfn(&checksched);
|
||||||
deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
|
deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,11 @@ int insmode;
|
||||||
mod_export int eofchar;
|
mod_export int eofchar;
|
||||||
|
|
||||||
static int eofsent;
|
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)
|
#if defined(HAVE_SELECT) || defined(HAVE_POLL)
|
||||||
/* Terminal baud rate */
|
/* Terminal baud rate */
|
||||||
|
@ -387,11 +391,110 @@ breakread(int fd, char *buf, int n)
|
||||||
# define read breakread
|
# define read breakread
|
||||||
#endif
|
#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;
|
int ret;
|
||||||
|
struct ztmout tmout;
|
||||||
#if defined(HAS_TIO) && \
|
#if defined(HAS_TIO) && \
|
||||||
(defined(sun) || (!defined(HAVE_POLL) && !defined(HAVE_SELECT)))
|
(defined(sun) || (!defined(HAVE_POLL) && !defined(HAVE_SELECT)))
|
||||||
struct ttyinfo ti;
|
struct ttyinfo ti;
|
||||||
|
@ -402,33 +505,27 @@ raw_getbyte(int keytmout, char *cptr)
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
calc_timeout(&tmout, do_keytmout);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle timeouts and watched fd's. We only do one at once;
|
* Handle timeouts and watched fd's. If a watched fd or a function
|
||||||
* key timeouts take precedence. This saves tricky timing
|
* timeout triggers we restart any key timeout. This is likely to
|
||||||
* problems with the key timeout.
|
* 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
|
#ifdef FIONREAD
|
||||||
&& ! delayzsetterm
|
&& ! delayzsetterm
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
if (!keytmout || keytimeout <= 0)
|
|
||||||
exp100ths = 0;
|
|
||||||
else if (keytimeout > 500)
|
|
||||||
exp100ths = 500;
|
|
||||||
else
|
|
||||||
exp100ths = keytimeout;
|
|
||||||
#if defined(HAVE_SELECT) || defined(HAVE_POLL)
|
#if defined(HAVE_SELECT) || defined(HAVE_POLL)
|
||||||
if (!keytmout || exp100ths) {
|
|
||||||
int i, errtry = 0, selret;
|
int i, errtry = 0, selret;
|
||||||
# ifdef HAVE_POLL
|
# ifdef HAVE_POLL
|
||||||
int poll_timeout;
|
|
||||||
int nfds;
|
int nfds;
|
||||||
struct pollfd *fds;
|
struct pollfd *fds;
|
||||||
# else
|
|
||||||
int fdmax;
|
|
||||||
struct timeval *tvptr;
|
|
||||||
struct timeval expire_tv;
|
|
||||||
# endif
|
# endif
|
||||||
# if defined(HAS_TIO) && defined(sun)
|
# if defined(HAS_TIO) && defined(sun)
|
||||||
/*
|
/*
|
||||||
|
@ -454,42 +551,38 @@ raw_getbyte(int keytmout, char *cptr)
|
||||||
return 1;
|
return 1;
|
||||||
# endif
|
# endif
|
||||||
# ifdef HAVE_POLL
|
# ifdef HAVE_POLL
|
||||||
nfds = keytmout ? 1 : 1 + nwatch;
|
nfds = 1 + nwatch;
|
||||||
/* First pollfd is SHTTY, following are the nwatch fds */
|
/* First pollfd is SHTTY, following are the nwatch fds */
|
||||||
fds = zalloc(sizeof(struct pollfd) * nfds);
|
fds = zalloc(sizeof(struct pollfd) * nfds);
|
||||||
if (exp100ths)
|
|
||||||
poll_timeout = exp100ths * 10;
|
|
||||||
else
|
|
||||||
poll_timeout = -1;
|
|
||||||
|
|
||||||
fds[0].fd = SHTTY;
|
fds[0].fd = SHTTY;
|
||||||
/*
|
/*
|
||||||
* POLLIN, POLLIN, POLLIN,
|
* POLLIN, POLLIN, POLLIN,
|
||||||
* Keep those fd's POLLIN...
|
* Keep those fd's POLLIN...
|
||||||
*/
|
*/
|
||||||
fds[0].events = POLLIN;
|
fds[0].events = POLLIN;
|
||||||
if (!keytmout) {
|
|
||||||
for (i = 0; i < nwatch; i++) {
|
for (i = 0; i < nwatch; i++) {
|
||||||
fds[i+1].fd = watch_fds[i];
|
fds[i+1].fd = watch_fds[i];
|
||||||
fds[i+1].events = POLLIN;
|
fds[i+1].events = POLLIN;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
# else
|
|
||||||
fdmax = SHTTY;
|
|
||||||
tvptr = NULL;
|
|
||||||
if (exp100ths) {
|
|
||||||
expire_tv.tv_sec = exp100ths / 100;
|
|
||||||
expire_tv.tv_usec = (exp100ths % 100) * 10000L;
|
|
||||||
tvptr = &expire_tv;
|
|
||||||
}
|
|
||||||
# endif
|
# endif
|
||||||
do {
|
do {
|
||||||
# ifdef HAVE_POLL
|
# ifdef HAVE_POLL
|
||||||
|
int poll_timeout;
|
||||||
|
|
||||||
|
if (tmout.tp != ZTM_NONE)
|
||||||
|
poll_timeout = tmout.exp100ths * 10;
|
||||||
|
else
|
||||||
|
poll_timeout = -1;
|
||||||
|
|
||||||
selret = poll(fds, errtry ? 1 : nfds, poll_timeout);
|
selret = poll(fds, errtry ? 1 : nfds, poll_timeout);
|
||||||
# else
|
# else
|
||||||
|
int fdmax = SHTTY;
|
||||||
|
struct timeval *tvptr;
|
||||||
|
struct timeval expire_tv;
|
||||||
|
|
||||||
FD_ZERO(&foofd);
|
FD_ZERO(&foofd);
|
||||||
FD_SET(SHTTY, &foofd);
|
FD_SET(SHTTY, &foofd);
|
||||||
if (!keytmout && !errtry) {
|
if (!errtry) {
|
||||||
for (i = 0; i < nwatch; i++) {
|
for (i = 0; i < nwatch; i++) {
|
||||||
int fd = watch_fds[i];
|
int fd = watch_fds[i];
|
||||||
FD_SET(fd, &foofd);
|
FD_SET(fd, &foofd);
|
||||||
|
@ -497,6 +590,15 @@ raw_getbyte(int keytmout, char *cptr)
|
||||||
fdmax = fd;
|
fdmax = fd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
selret = select(fdmax+1, (SELECT_ARG_2_T) & foofd,
|
||||||
NULL, NULL, tvptr);
|
NULL, NULL, tvptr);
|
||||||
# endif
|
# endif
|
||||||
|
@ -510,17 +612,69 @@ raw_getbyte(int keytmout, char *cptr)
|
||||||
* messing up reads from the terminal. Try first
|
* messing up reads from the terminal. Try first
|
||||||
* with all fds, then try unsetting the special ones.
|
* with all fds, then try unsetting the special ones.
|
||||||
*/
|
*/
|
||||||
if (selret < 0 && !keytmout && !errtry) {
|
if (selret < 0 && !errtry) {
|
||||||
errtry = 1;
|
errtry = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (selret == 0) {
|
if (selret == 0) {
|
||||||
|
/*
|
||||||
|
* Nothing ready and no error, so we timed out.
|
||||||
|
*/
|
||||||
|
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 */
|
/* Special value -2 signals nothing ready */
|
||||||
selret = -2;
|
selret = -2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ZTM_FUNC:
|
||||||
|
while (firstnode(timedfns)) {
|
||||||
|
Timedfn tfdat = (Timedfn)getdata(firstnode(timedfns));
|
||||||
|
/*
|
||||||
|
* It's possible a previous function took
|
||||||
|
* a long time to run (though it can't
|
||||||
|
* call zle recursively), so recalculate
|
||||||
|
* the time on each iteration.
|
||||||
|
*/
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if (tfdat->when > now)
|
||||||
|
break;
|
||||||
|
tfdat->func();
|
||||||
}
|
}
|
||||||
|
/* Function may have messed up the display */
|
||||||
|
if (resetneeded)
|
||||||
|
zrefresh();
|
||||||
|
/* We need to recalculate the timeout */
|
||||||
|
/*FALLTHROUGH*/
|
||||||
|
case ZTM_MAX:
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If we handled the timeout successfully,
|
||||||
|
* carry on.
|
||||||
|
*/
|
||||||
|
if (selret == 0)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* If error or unhandled timeout, give up. */
|
||||||
if (selret < 0)
|
if (selret < 0)
|
||||||
break;
|
break;
|
||||||
if (!keytmout && nwatch) {
|
if (nwatch && !errtry) {
|
||||||
/*
|
/*
|
||||||
* Copy the details of the watch fds in case the
|
* Copy the details of the watch fds in case the
|
||||||
* user decides to delete one from inside the
|
* user decides to delete one from inside the
|
||||||
|
@ -593,13 +747,12 @@ raw_getbyte(int keytmout, char *cptr)
|
||||||
# endif
|
# endif
|
||||||
if (selret < 0)
|
if (selret < 0)
|
||||||
return selret;
|
return selret;
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
# ifdef HAS_TIO
|
# ifdef HAS_TIO
|
||||||
ti = shttyinfo;
|
ti = shttyinfo;
|
||||||
ti.tio.c_lflag &= ~ICANON;
|
ti.tio.c_lflag &= ~ICANON;
|
||||||
ti.tio.c_cc[VMIN] = 0;
|
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
|
# ifdef HAVE_TERMIOS_H
|
||||||
tcsetattr(SHTTY, TCSANOW, &ti.tio);
|
tcsetattr(SHTTY, TCSANOW, &ti.tio);
|
||||||
# else
|
# else
|
||||||
|
@ -623,7 +776,7 @@ raw_getbyte(int keytmout, char *cptr)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
mod_export int
|
mod_export int
|
||||||
getbyte(int keytmout, int *timeout)
|
getbyte(int do_keytmout, int *timeout)
|
||||||
{
|
{
|
||||||
char cc;
|
char cc;
|
||||||
unsigned int ret;
|
unsigned int ret;
|
||||||
|
@ -656,7 +809,7 @@ getbyte(int keytmout, int *timeout)
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int q = queue_signal_level();
|
int q = queue_signal_level();
|
||||||
dont_queue_signals();
|
dont_queue_signals();
|
||||||
r = raw_getbyte(keytmout, &cc);
|
r = raw_getbyte(do_keytmout, &cc);
|
||||||
restore_queue_signals(q);
|
restore_queue_signals(q);
|
||||||
if (r == -2) {
|
if (r == -2) {
|
||||||
/* timeout */
|
/* timeout */
|
||||||
|
@ -733,9 +886,9 @@ getbyte(int keytmout, int *timeout)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
mod_export ZLE_INT_T
|
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
|
#ifdef MULTIBYTE_SUPPORT
|
||||||
return getrestchar(inchar);
|
return getrestchar(inchar);
|
||||||
|
@ -938,7 +1091,7 @@ zleread(char **lp, char **rp, int flags, int context)
|
||||||
return shingetline();
|
return shingetline();
|
||||||
}
|
}
|
||||||
|
|
||||||
keytimeout = getiparam("KEYTIMEOUT");
|
keytimeout = (time_t)getiparam("KEYTIMEOUT");
|
||||||
if (!shout) {
|
if (!shout) {
|
||||||
if (SHTTY != -1)
|
if (SHTTY != -1)
|
||||||
init_shout();
|
init_shout();
|
||||||
|
@ -1551,7 +1704,7 @@ zle_resetprompt(void)
|
||||||
mod_export void
|
mod_export void
|
||||||
trashzle(void)
|
trashzle(void)
|
||||||
{
|
{
|
||||||
if (zleactive) {
|
if (zleactive && !trashedzle) {
|
||||||
/* This zrefresh() is just to get the main editor display right and *
|
/* This zrefresh() is just to get the main editor display right and *
|
||||||
* get the cursor in the right place. For that reason, we disable *
|
* get the cursor in the right place. For that reason, we disable *
|
||||||
* list display (which would otherwise result in infinite *
|
* list display (which would otherwise result in infinite *
|
||||||
|
|
|
@ -721,7 +721,6 @@ bin_zle_invalidate(UNUSED(char *name), UNUSED(char **args), UNUSED(Options ops),
|
||||||
* true if a completion widget is active.
|
* true if a completion widget is active.
|
||||||
*/
|
*/
|
||||||
if (zleactive) {
|
if (zleactive) {
|
||||||
if (!trashedzle)
|
|
||||||
trashzle();
|
trashzle();
|
||||||
return 0;
|
return 0;
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -869,7 +869,6 @@ setupvals(void)
|
||||||
nohistsave = 1;
|
nohistsave = 1;
|
||||||
dirstack = znewlinklist();
|
dirstack = znewlinklist();
|
||||||
bufstack = znewlinklist();
|
bufstack = znewlinklist();
|
||||||
prepromptfns = znewlinklist();
|
|
||||||
hsubl = hsubr = NULL;
|
hsubl = hsubr = NULL;
|
||||||
lastpid = 0;
|
lastpid = 0;
|
||||||
bshin = SHIN ? fdopen(SHIN, "r") : stdin;
|
bshin = SHIN ? fdopen(SHIN, "r") : stdin;
|
||||||
|
|
29
Src/subst.c
29
Src/subst.c
|
@ -70,9 +70,24 @@ prefork(LinkList list, int flags)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isset(SHFILEEXPANSION))
|
if (isset(SHFILEEXPANSION)) {
|
||||||
filesub((char **)getaddrdata(node),
|
/*
|
||||||
flags & (PF_TYPESET|PF_ASSIGN));
|
* 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))) {
|
if (!(node = stringsubst(list, node, flags & PF_SINGLE, asssub))) {
|
||||||
unqueue_signals();
|
unqueue_signals();
|
||||||
return;
|
return;
|
||||||
|
@ -92,9 +107,11 @@ prefork(LinkList list, int flags)
|
||||||
xpandbraces(list, &node);
|
xpandbraces(list, &node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (unset(SHFILEEXPANSION))
|
if (unset(SHFILEEXPANSION)) {
|
||||||
filesub((char **)getaddrdata(node),
|
char *cptr = (char *)getdata(node);
|
||||||
flags & (PF_TYPESET|PF_ASSIGN));
|
filesub(&cptr, flags & (PF_TYPESET|PF_ASSIGN));
|
||||||
|
setdata(node, cptr);
|
||||||
|
}
|
||||||
} else if (!(flags & PF_SINGLE) && !keep)
|
} else if (!(flags & PF_SINGLE) && !keep)
|
||||||
uremnode(list, node);
|
uremnode(list, node);
|
||||||
if (errflag) {
|
if (errflag) {
|
||||||
|
|
145
Src/utils.c
145
Src/utils.c
|
@ -911,10 +911,141 @@ dircmp(char *s, char *t)
|
||||||
return 1;
|
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 */
|
/* the last time we checked mail */
|
||||||
|
|
||||||
|
@ -1027,10 +1158,12 @@ preprompt(void)
|
||||||
lastmailcheck = time(NULL);
|
lastmailcheck = time(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Some people have claimed that C performs type *
|
if (prepromptfns) {
|
||||||
* checking, but they were later found to be lying. */
|
for(ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) {
|
||||||
for(ln = firstnode(prepromptfns); ln; ln = nextnode(ln))
|
Prepromptfn ppnode = (Prepromptfn)getdata(ln);
|
||||||
(**(void (**) _((void)))getdata(ln))();
|
ppnode->func();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
|
|
72
Src/zsh.h
72
Src/zsh.h
|
@ -333,40 +333,38 @@ enum {
|
||||||
/* Abstract types for zsh */
|
/* 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 alias *Alias;
|
||||||
typedef struct param *Param;
|
typedef struct asgment *Asgment;
|
||||||
typedef struct paramdef *Paramdef;
|
typedef struct builtin *Builtin;
|
||||||
typedef struct cmdnam *Cmdnam;
|
typedef struct cmdnam *Cmdnam;
|
||||||
typedef struct shfunc *Shfunc;
|
typedef struct complist *Complist;
|
||||||
|
typedef struct conddef *Conddef;
|
||||||
typedef struct funcstack *Funcstack;
|
typedef struct funcstack *Funcstack;
|
||||||
typedef struct funcwrap *FuncWrap;
|
typedef struct funcwrap *FuncWrap;
|
||||||
typedef struct options *Options;
|
typedef struct hashnode *HashNode;
|
||||||
typedef struct builtin *Builtin;
|
typedef struct hashtable *HashTable;
|
||||||
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 heap *Heap;
|
typedef struct heap *Heap;
|
||||||
typedef struct heapstack *Heapstack;
|
typedef struct heapstack *Heapstack;
|
||||||
typedef struct histent *Histent;
|
typedef struct histent *Histent;
|
||||||
typedef struct hookdef *Hookdef;
|
typedef struct hookdef *Hookdef;
|
||||||
|
typedef struct job *Job;
|
||||||
typedef struct asgment *Asgment;
|
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 */
|
/* Definitions for linked lists */
|
||||||
|
@ -432,6 +430,28 @@ union linkroot {
|
||||||
__n0.dat = (void *) (V0); \
|
__n0.dat = (void *) (V0); \
|
||||||
} while (0)
|
} 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 */
|
/* Definitions for syntax trees */
|
||||||
/********************************/
|
/********************************/
|
||||||
|
|
Loading…
Reference in a new issue