summaryrefslogtreecommitdiff
path: root/Src/Modules
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Modules')
-rw-r--r--Src/Modules/curses.c4
-rw-r--r--Src/Modules/curses_keys.awk7
-rw-r--r--Src/Modules/datetime.c5
-rw-r--r--Src/Modules/db_gdbm.c4
-rw-r--r--Src/Modules/files.c44
-rw-r--r--Src/Modules/nearcolor.c2
-rw-r--r--Src/Modules/param_private.c12
-rw-r--r--Src/Modules/parameter.c110
-rw-r--r--Src/Modules/system.c84
-rw-r--r--Src/Modules/watch.c759
-rw-r--r--Src/Modules/watch.mdd7
-rw-r--r--Src/Modules/zprof.c31
-rw-r--r--Src/Modules/zpty.c11
-rw-r--r--Src/Modules/zutil.c82
14 files changed, 1059 insertions, 103 deletions
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c
index 19f285e34..e46903916 100644
--- a/Src/Modules/curses.c
+++ b/Src/Modules/curses.c
@@ -1212,7 +1212,7 @@ zccmd_input(const char *nam, char **args)
addlinknode(margs, "CTRL");
if (mevent.bstate & BUTTON_ALT)
addlinknode(margs, "ALT");
- if (!setaparam(args[3], zlinklist2array(margs)))
+ if (!setaparam(args[3], zlinklist2array(margs, 1)))
return 1;
} else {
#endif
@@ -1464,7 +1464,7 @@ zccmd_querychar(const char *nam, char **args)
}
/* Turn this into an array and store it. */
- return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist));
+ return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist, 1));
}
diff --git a/Src/Modules/curses_keys.awk b/Src/Modules/curses_keys.awk
index ffb182c35..25bd63c7e 100644
--- a/Src/Modules/curses_keys.awk
+++ b/Src/Modules/curses_keys.awk
@@ -12,8 +12,13 @@ BEGIN {nkeydefs = 0}
END {
printf("static const struct zcurses_namenumberpair keypad_names[] = {\n")
- for (i = 0; i < 0 + nkeydefs; i++)
+ for (i = 0; i < 0 + nkeydefs; i++) {
+ if (name[i] == "EVENT")
+ printf("#ifdef KEY_EVENT\n")
printf(" {\"%s\", KEY_%s},\n", name[i], name[i])
+ if (name[i] == "EVENT")
+ printf("#endif\n")
+ }
printf(" {NULL, 0}\n")
printf("};\n")
}
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index 521c15a5b..085e4cc26 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -175,7 +175,8 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
setsparam(scalar, metafy(buffer, len, META_DUP));
} else {
fwrite(buffer, 1, len, stdout);
- putchar('\n');
+ if (!OPT_ISSET(ops,'n'))
+ putchar('\n');
}
zfree(buffer, bufsize);
@@ -235,7 +236,7 @@ getcurrenttime(UNUSED(Param pm))
}
static struct builtin bintab[] = {
- BUILTIN("strftime", 0, bin_strftime, 1, 3, 0, "qrs:", NULL),
+ BUILTIN("strftime", 0, bin_strftime, 1, 3, 0, "nqrs:", NULL),
};
static const struct gsu_integer epochseconds_gsu =
diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index b8e7c76c6..7e11ec939 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -233,7 +233,7 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
/**/
static int
-bin_zgdbmpath(char *nam, char **args, Options ops, UNUSED(int func))
+bin_zgdbmpath(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
Param pm;
char *pmname;
@@ -332,6 +332,8 @@ gdbmgetfn(Param pm)
/* Can return pointer, correctly saved inside hash */
return pm->u.str;
+ } else {
+ pm->node.flags |= PM_DEFAULTED;
}
/* Free key */
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index 6d20e38a8..bf0e8f8a8 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -32,12 +32,6 @@
typedef int (*MoveFunc) _((char const *, char const *));
typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *));
-#ifndef STDC_HEADERS
-extern int link _((const char *, const char *));
-extern int symlink _((const char *, const char *));
-extern int rename _((const char *, const char *));
-#endif
-
struct recursivecmd;
#include "files.pro"
@@ -122,19 +116,29 @@ domkdir(char *nam, char *path, mode_t mode, int p)
{
int err;
mode_t oumask;
+ struct stat st;
+ int n = 8;
char const *rpath = unmeta(path);
- if(p) {
- struct stat st;
-
- if(!stat(rpath, &st) && S_ISDIR(st.st_mode))
+ while(n-- > 0) {
+ oumask = umask(0);
+ err = mkdir(rpath, mode) ? errno : 0;
+ umask(oumask);
+ if (!err)
return 0;
+ if(!p || err != EEXIST)
+ break;
+ if(stat(rpath, &st)) {
+ if(errno == ENOENT)
+ continue;
+ err = errno;
+ break;
+ }
+ if(S_ISDIR(st.st_mode))
+ return 0;
+ break;
}
- oumask = umask(0);
- err = mkdir(rpath, mode) ? errno : 0;
- umask(oumask);
- if(!err)
- return 0;
+
zwarnnam(nam, "cannot make directory `%s': %e", path, err);
return 1;
}
@@ -342,7 +346,13 @@ domove(char *nam, MoveFunc movefn, char *p, char *q, int flags)
unlink(qbuf);
}
if(movefn(pbuf, qbuf)) {
- zwarnnam(nam, "%s: %e", p, errno);
+ int ferrno = errno;
+ char *errfile = p;
+ if (ferrno == ENOENT && !lstat(pbuf, &st)) {
+ /* p *does* exist, so error is in q */
+ errfile = q;
+ }
+ zwarnnam(nam, "`%s': %e", errfile, ferrno);
zsfree(pbuf);
return 1;
}
@@ -642,7 +652,7 @@ chmod_dochmod(char *arg, char *rp, UNUSED(struct stat const *sp), void *magic)
/**/
static int
-bin_chmod(char *nam, char **args, Options ops, int func)
+bin_chmod(char *nam, char **args, Options ops, UNUSED(int func))
{
struct chmodmagic chm;
char *str = args[0], *ptr;
diff --git a/Src/Modules/nearcolor.c b/Src/Modules/nearcolor.c
index b49ee9afb..d50a6bb44 100644
--- a/Src/Modules/nearcolor.c
+++ b/Src/Modules/nearcolor.c
@@ -188,7 +188,7 @@ enables_(Module m, int **enables)
/**/
int
-boot_(Module m)
+boot_(UNUSED(Module m))
{
addhookfunc("get_color_attr", (Hookfn) getnearestcolor);
return 0;
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
index 86416c5c5..c53839152 100644
--- a/Src/Modules/param_private.c
+++ b/Src/Modules/param_private.c
@@ -125,7 +125,7 @@ makeprivate(HashNode hn, UNUSED(int flags))
break;
}
/* PM_HIDE so new parameters in deeper scopes do not shadow */
- pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE);
+ pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE|PM_RO_BY_DESIGN);
pm->level -= 1;
}
}
@@ -171,6 +171,7 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func)
{
int from_typeset = 1;
int ofake = fakelevel; /* paranoia in case of recursive call */
+ int hasargs = /* *args != NULL || */ (assigns && firstnode(assigns));
makeprivate_error = 0;
if (!OPT_ISSET(ops, 'P')) {
@@ -189,7 +190,12 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func)
return bin_typeset("private", args, assigns, ops, func);
}
- ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */
+ if (!(OPT_ISSET(ops, 'm') || OPT_ISSET(ops, '+')))
+ ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */
+ if (OPT_ISSET(ops, 'p') || OPT_ISSET(ops, 'm') ||
+ (!hasargs && OPT_ISSET(ops, '+'))) {
+ return bin_typeset("private", args, assigns, ops, func);
+ }
queue_signals();
fakelevel = locallevel;
@@ -555,7 +561,7 @@ printprivatenode(HashNode hn, int printflags)
static struct builtin bintab[] = {
/* Copied from BUILTIN("local"), "P" added */
- BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lprtux", "P")
+ BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lmprtux", "P")
};
static struct features module_features = {
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index ef9148d7b..dbb61e474 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -592,7 +592,7 @@ getpmfunction_source(HashTable ht, const char *name)
return getfunction_source(ht, name, 0);
}
-/* Param table entry for retrieving ds_functions_source element */
+/* Param table entry for retrieving dis_functions_source element */
/**/
static HashNode
@@ -1244,19 +1244,19 @@ histwgetfn(UNUSED(Param pm))
/**/
static char *
-pmjobtext(int job)
+pmjobtext(Job jtab, int job)
{
Process pn;
int len = 1;
char *ret;
- for (pn = jobtab[job].procs; pn; pn = pn->next)
+ for (pn = jtab[job].procs; pn; pn = pn->next)
len += strlen(pn->text) + 3;
ret = (char *) zhalloc(len);
ret[0] = '\0';
- for (pn = jobtab[job].procs; pn; pn = pn->next) {
+ for (pn = jtab[job].procs; pn; pn = pn->next) {
strcat(ret, pn->text);
if (pn->next)
strcat(ret, " | ");
@@ -1269,22 +1269,25 @@ static HashNode
getpmjobtext(UNUSED(HashTable ht), const char *name)
{
Param pm = NULL;
- int job;
+ int job, jmax;
char *pend;
+ Job jtab;
pm = (Param) hcalloc(sizeof(struct param));
pm->node.nam = dupstring(name);
pm->node.flags = PM_SCALAR | PM_READONLY;
pm->gsu.s = &nullsetscalar_gsu;
+ selectjobtab(&jtab, &jmax);
+
job = strtod(name, &pend);
/* Non-numeric keys are looked up by job name */
if (*pend)
job = getjob(name, NULL);
- if (job >= 1 && job <= maxjob &&
- jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT))
- pm->u.str = pmjobtext(job);
+ if (job >= 1 && job <= jmax &&
+ jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT))
+ pm->u.str = pmjobtext(jtab, job);
else {
pm->u.str = dupstring("");
pm->node.flags |= (PM_UNSET|PM_SPECIAL);
@@ -1297,22 +1300,25 @@ static void
scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags)
{
struct param pm;
- int job;
+ int job, jmax;
char buf[40];
+ Job jtab;
memset((void *)&pm, 0, sizeof(struct param));
pm.node.flags = PM_SCALAR | PM_READONLY;
pm.gsu.s = &nullsetscalar_gsu;
- for (job = 1; job <= maxjob; job++) {
- if (jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT)) {
+ selectjobtab(&jtab, &jmax);
+
+ for (job = 1; job <= jmax; job++) {
+ if (jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT)) {
if (func != scancountparams) {
sprintf(buf, "%d", job);
pm.node.nam = dupstring(buf);
if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
!(flags & SCANPM_WANTKEYS))
- pm.u.str = pmjobtext(job);
+ pm.u.str = pmjobtext(jtab, job);
}
func(&pm.node, flags);
}
@@ -1323,7 +1329,7 @@ scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags)
/**/
static char *
-pmjobstate(int job)
+pmjobstate(Job jtab, int job)
{
Process pn;
char buf[256], buf2[128], *ret, *state, *cp;
@@ -1335,14 +1341,14 @@ pmjobstate(int job)
else
cp = ":";
- if (jobtab[job].stat & STAT_DONE)
+ if (jtab[job].stat & STAT_DONE)
ret = dyncat("done", cp);
- else if (jobtab[job].stat & STAT_STOPPED)
+ else if (jtab[job].stat & STAT_STOPPED)
ret = dyncat("suspended", cp);
else
ret = dyncat("running", cp);
- for (pn = jobtab[job].procs; pn; pn = pn->next) {
+ for (pn = jtab[job].procs; pn; pn = pn->next) {
if (pn->status == SP_RUNNING)
state = "running";
@@ -1371,21 +1377,24 @@ static HashNode
getpmjobstate(UNUSED(HashTable ht), const char *name)
{
Param pm = NULL;
- int job;
+ int job, jmax;
char *pend;
+ Job jtab;
pm = (Param) hcalloc(sizeof(struct param));
pm->node.nam = dupstring(name);
pm->node.flags = PM_SCALAR | PM_READONLY;
pm->gsu.s = &nullsetscalar_gsu;
+ selectjobtab(&jtab, &jmax);
+
job = strtod(name, &pend);
if (*pend)
job = getjob(name, NULL);
- if (job >= 1 && job <= maxjob &&
- jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT))
- pm->u.str = pmjobstate(job);
+ if (job >= 1 && job <= jmax &&
+ jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT))
+ pm->u.str = pmjobstate(jtab, job);
else {
pm->u.str = dupstring("");
pm->node.flags |= (PM_UNSET|PM_SPECIAL);
@@ -1398,22 +1407,25 @@ static void
scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags)
{
struct param pm;
- int job;
+ int job, jmax;
+ Job jtab;
char buf[40];
+ selectjobtab(&jtab, &jmax);
+
memset((void *)&pm, 0, sizeof(struct param));
pm.node.flags = PM_SCALAR | PM_READONLY;
pm.gsu.s = &nullsetscalar_gsu;
- for (job = 1; job <= maxjob; job++) {
- if (jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT)) {
+ for (job = 1; job <= jmax; job++) {
+ if (jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT)) {
if (func != scancountparams) {
sprintf(buf, "%d", job);
pm.node.nam = dupstring(buf);
if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
!(flags & SCANPM_WANTKEYS))
- pm.u.str = pmjobstate(job);
+ pm.u.str = pmjobstate(jtab, job);
}
func(&pm.node, flags);
}
@@ -1424,11 +1436,11 @@ scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags)
/**/
static char *
-pmjobdir(int job)
+pmjobdir(Job jtab, int job)
{
char *ret;
- ret = dupstring(jobtab[job].pwd ? jobtab[job].pwd : pwd);
+ ret = dupstring(jtab[job].pwd ? jtab[job].pwd : pwd);
return ret;
}
@@ -1437,21 +1449,24 @@ static HashNode
getpmjobdir(UNUSED(HashTable ht), const char *name)
{
Param pm = NULL;
- int job;
+ int job, jmax;
char *pend;
+ Job jtab;
pm = (Param) hcalloc(sizeof(struct param));
pm->node.nam = dupstring(name);
pm->node.flags = PM_SCALAR | PM_READONLY;
pm->gsu.s = &nullsetscalar_gsu;
+ selectjobtab(&jtab, &jmax);
+
job = strtod(name, &pend);
if (*pend)
job = getjob(name, NULL);
- if (job >= 1 && job <= maxjob &&
- jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT))
- pm->u.str = pmjobdir(job);
+ if (job >= 1 && job <= jmax &&
+ jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT))
+ pm->u.str = pmjobdir(jtab, job);
else {
pm->u.str = dupstring("");
pm->node.flags |= (PM_UNSET|PM_SPECIAL);
@@ -1464,22 +1479,25 @@ static void
scanpmjobdirs(UNUSED(HashTable ht), ScanFunc func, int flags)
{
struct param pm;
- int job;
+ int job, jmax;
char buf[40];
+ Job jtab;
memset((void *)&pm, 0, sizeof(struct param));
pm.node.flags = PM_SCALAR | PM_READONLY;
pm.gsu.s = &nullsetscalar_gsu;
- for (job = 1; job <= maxjob; job++) {
- if (jobtab[job].stat && jobtab[job].procs &&
- !(jobtab[job].stat & STAT_NOPRINT)) {
+ selectjobtab(&jtab, &jmax);
+
+ for (job = 1; job <= jmax; job++) {
+ if (jtab[job].stat && jtab[job].procs &&
+ !(jtab[job].stat & STAT_NOPRINT)) {
if (func != scancountparams) {
sprintf(buf, "%d", job);
pm.node.nam = dupstring(buf);
if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
!(flags & SCANPM_WANTKEYS))
- pm.u.str = pmjobdir(job);
+ pm.u.str = pmjobdir(jtab, job);
}
func(&pm.node, flags);
}
@@ -2011,6 +2029,9 @@ scanpmdissaliases(HashTable ht, ScanFunc func, int flags)
/**/
static Groupset get_all_groups(void)
{
+#ifdef DISABLE_DYNAMIC_NSS
+ return NULL;
+#else
Groupset gs = zhalloc(sizeof(*gs));
Groupmap gaptr;
gid_t *list, *lptr, egid;
@@ -2063,6 +2084,7 @@ static Groupset get_all_groups(void)
}
return gs;
+#endif /* DISABLE_DYNAMIC_NSS */
}
/* Standard hash element lookup. */
@@ -2081,7 +2103,11 @@ getpmusergroups(UNUSED(HashTable ht), const char *name)
pm->gsu.s = &nullsetscalar_gsu;
if (!gs) {
+#ifdef DISABLE_DYNAMIC_NSS
+ zerr("parameter 'usergroups' not available: NSS is disabled");
+#else
zerr("failed to retrieve groups for user: %e", errno);
+#endif
pm->u.str = dupstring("");
pm->node.flags |= (PM_UNSET|PM_SPECIAL);
return &pm->node;
@@ -2113,7 +2139,11 @@ scanpmusergroups(UNUSED(HashTable ht), ScanFunc func, int flags)
Groupmap gaptr;
if (!gs) {
+#ifdef DISABLE_DYNAMIC_NSS
+ zerr("parameter 'usergroups' not available: NSS is disabled");
+#else
zerr("failed to retrieve groups for user: %e", errno);
+#endif
return;
}
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
index fb3d80773..71745548f 100644
--- a/Src/Modules/system.c
+++ b/Src/Modules/system.c
@@ -29,6 +29,7 @@
#include "system.mdh"
#include "system.pro"
+#include <math.h>
#ifdef HAVE_POLL_H
# include <poll.h>
@@ -279,7 +280,7 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func))
}
-static struct { char *name; int oflag; } openopts[] = {
+static struct { const char *name; int oflag; } openopts[] = {
#ifdef O_CLOEXEC
{ "cloexec", O_CLOEXEC },
#else
@@ -296,6 +297,9 @@ static struct { char *name; int oflag; } openopts[] = {
#ifdef O_NOATIME
{ "noatime", O_NOATIME },
#endif
+#ifdef O_NONBLOCK
+ { "nonblock", O_NONBLOCK},
+#endif
{ "excl", O_EXCL | O_CREAT },
{ "creat", O_CREAT },
{ "create", O_CREAT },
@@ -531,7 +535,9 @@ static int
bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
int cloexec = 1, unlock = 0, readlock = 0;
- zlong timeout = -1;
+ double timeout = -1;
+ long timeout_interval = 1e6;
+ mnumber timeout_param;
char *fdvar = NULL;
#ifdef HAVE_FCNTL_H
struct flock lck;
@@ -583,7 +589,51 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
} else {
optarg = *args++;
}
- timeout = mathevali(optarg);
+ timeout_param = matheval(optarg);
+ timeout = (timeout_param.type & MN_FLOAT) ?
+ timeout_param.u.d : (double)timeout_param.u.l;
+
+ /*
+ * timeout must not overflow time_t, but little is known
+ * about this type's limits. Conservatively limit to 2^30-1
+ * (34 years). Then it can only overflow if time_t is only
+ * a 32-bit int and CLOCK_MONOTONIC is not supported, in which
+ * case there is a Y2038 problem anyway.
+ */
+ if (timeout > 1073741823.) {
+ zwarnnam(nam, "flock: invalid timeout value: '%s'",
+ optarg);
+ return 1;
+ }
+ break;
+
+ case 'i':
+ /* retry interval in seconds */
+ if (optptr[1]) {
+ optarg = optptr + 1;
+ optptr += strlen(optarg) - 1;
+ } else if (!*args) {
+ zwarnnam(nam,
+ "flock: option %c requires "
+ "a numeric retry interval",
+ opt);
+ return 1;
+ } else {
+ optarg = *args++;
+ }
+ timeout_param = matheval(optarg);
+ if (!(timeout_param.type & MN_FLOAT)) {
+ timeout_param.type = MN_FLOAT;
+ timeout_param.u.d = (double)timeout_param.u.l;
+ }
+ timeout_param.u.d = ceil(timeout_param.u.d * 1e6);
+ if (timeout_param.u.d < 1
+ || timeout_param.u.d > 0.999 * LONG_MAX) {
+ zwarnnam(nam, "flock: invalid interval value: '%s'",
+ optarg);
+ return 1;
+ }
+ timeout_interval = (long)timeout_param.u.d;
break;
case 'u':
@@ -647,7 +697,24 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
lck.l_len = 0; /* lock the whole file */
if (timeout > 0) {
- time_t end = time(NULL) + (time_t)timeout;
+ /*
+ * Get current time, calculate timeout time.
+ * No need to check for overflow, already checked above.
+ */
+ struct timespec now, end;
+ double timeout_s;
+ long remaining_us;
+ zgettime_monotonic_if_available(&now);
+ end.tv_sec = now.tv_sec;
+ end.tv_nsec = now.tv_nsec;
+ end.tv_nsec += ceil(modf(timeout, &timeout_s) * 1000000000L);
+ end.tv_sec += timeout_s;
+ if (end.tv_nsec >= 1000000000L) {
+ end.tv_nsec -= 1000000000L;
+ end.tv_sec += 1;
+ }
+
+ /* Try acquiring lock, loop until timeout. */
while (fcntl(flock_fd, F_SETLK, &lck) < 0) {
if (errflag) {
zclose(flock_fd);
@@ -658,11 +725,16 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
zwarnnam(nam, "failed to lock file %s: %e", args[0], errno);
return 1;
}
- if (time(NULL) >= end) {
+ zgettime_monotonic_if_available(&now);
+ remaining_us = timespec_diff_us(&now, &end);
+ if (remaining_us <= 0) {
zclose(flock_fd);
return 2;
}
- sleep(1);
+ if (remaining_us <= timeout_interval) {
+ timeout_interval = remaining_us;
+ }
+ zsleep(timeout_interval);
}
} else {
while (fcntl(flock_fd, timeout == 0 ? F_SETLK : F_SETLKW, &lck) < 0) {
diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c
new file mode 100644
index 000000000..d45c3cf3d
--- /dev/null
+++ b/Src/Modules/watch.c
@@ -0,0 +1,759 @@
+/*
+ * watch.c - login/logout watching
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose. The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "watch.mdh"
+
+/* Headers for utmp/utmpx structures */
+#ifdef HAVE_UTMP_H
+# include <utmp.h>
+#endif
+#ifdef HAVE_UTMPX_H
+# include <utmpx.h>
+#endif
+
+/* Find utmp file */
+#if !defined(REAL_UTMP_FILE) && defined(UTMP_FILE)
+# define REAL_UTMP_FILE UTMP_FILE
+#endif
+#if !defined(REAL_UTMP_FILE) && defined(_PATH_UTMP)
+# define REAL_UTMP_FILE _PATH_UTMP
+#endif
+#if !defined(REAL_UTMP_FILE) && defined(PATH_UTMP_FILE)
+# define REAL_UTMP_FILE PATH_UTMP_FILE
+#endif
+
+/* Find wtmp file */
+#if !defined(REAL_WTMP_FILE) && defined(WTMP_FILE)
+# define REAL_WTMP_FILE WTMP_FILE
+#endif
+#if !defined(REAL_WTMP_FILE) && defined(_PATH_WTMP)
+# define REAL_WTMP_FILE _PATH_WTMP
+#endif
+#if !defined(REAL_WTMP_FILE) && defined(PATH_WTMP_FILE)
+# define REAL_WTMP_FILE PATH_WTMP_FILE
+#endif
+
+/* Find utmpx file */
+#if !defined(REAL_UTMPX_FILE) && defined(UTMPX_FILE)
+# define REAL_UTMPX_FILE UTMPX_FILE
+#endif
+#if !defined(REAL_UTMPX_FILE) && defined(_PATH_UTMPX)
+# define REAL_UTMPX_FILE _PATH_UTMPX
+#endif
+#if !defined(REAL_UTMPX_FILE) && defined(PATH_UTMPX_FILE)
+# define REAL_UTMPX_FILE PATH_UTMPX_FILE
+#endif
+
+/* Find wtmpx file */
+#if !defined(REAL_WTMPX_FILE) && defined(WTMPX_FILE)
+# define REAL_WTMPX_FILE WTMPX_FILE
+#endif
+#if !defined(REAL_WTMPX_FILE) && defined(_PATH_WTMPX)
+# define REAL_WTMPX_FILE _PATH_WTMPX
+#endif
+#if !defined(REAL_WTMPX_FILE) && defined(PATH_WTMPX_FILE)
+# define REAL_WTMPX_FILE PATH_WTMPX_FILE
+#endif
+
+/* Decide which structure to use. We use a structure that exists in *
+ * the headers, and require that its corresponding utmp file exist. *
+ * (wtmp is less important.) */
+
+#if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMPX) && defined(REAL_UTMPX_FILE)
+# define WATCH_STRUCT_UTMP struct utmpx
+# if defined(HAVE_SETUTXENT) && defined(HAVE_GETUTXENT) && defined(HAVE_ENDUTXENT)
+# define setutent setutxent
+# define getutent getutxent
+# define endutent endutxent
+# ifndef HAVE_GETUTENT
+# define HAVE_GETUTENT 1
+# endif
+# endif
+
+/*
+ * In utmpx, the ut_name field is replaced by ut_user.
+ * However, on some systems ut_name may already be defined this
+ * way for the purposes of utmp.
+ */
+# ifndef ut_name
+# define ut_name ut_user
+# endif
+# ifdef HAVE_STRUCT_UTMPX_UT_XTIME
+# undef ut_time
+# define ut_time ut_xtime
+# else /* !HAVE_STRUCT_UTMPX_UT_XTIME */
+# ifdef HAVE_STRUCT_UTMPX_UT_TV
+# undef ut_time
+# define ut_time ut_tv.tv_sec
+# endif /* HAVE_STRUCT_UTMPX_UT_TV */
+# endif /* !HAVE_STRUCT_UTMPX_UT_XTIME */
+# define WATCH_UTMP_FILE REAL_UTMPX_FILE
+# ifdef REAL_WTMPX_FILE
+# define WATCH_WTMP_FILE REAL_WTMPX_FILE
+# endif
+# ifdef HAVE_STRUCT_UTMPX_UT_HOST
+# define WATCH_UTMP_UT_HOST 1
+# endif
+#endif
+
+#if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMP) && defined(REAL_UTMP_FILE)
+# define WATCH_STRUCT_UTMP struct utmp
+# define WATCH_UTMP_FILE REAL_UTMP_FILE
+# ifdef REAL_WTMP_FILE
+# define WATCH_WTMP_FILE REAL_WTMP_FILE
+# endif
+# ifdef HAVE_STRUCT_UTMP_UT_HOST
+# define WATCH_UTMP_UT_HOST 1
+# endif
+#endif
+
+#ifdef WATCH_UTMP_UT_HOST
+# define DEFAULT_WATCHFMT "%n has %a %l from %m."
+#else /* !WATCH_UTMP_UT_HOST */
+# define DEFAULT_WATCHFMT "%n has %a %l."
+#endif /* !WATCH_UTMP_UT_HOST */
+
+#ifdef WATCH_STRUCT_UTMP
+
+# include "watch.pro"
+
+# ifndef WATCH_WTMP_FILE
+# define WATCH_WTMP_FILE "/dev/null"
+# endif
+
+static int wtabsz = 0;
+static WATCH_STRUCT_UTMP *wtab = NULL;
+
+/* the last time we checked the people in the WATCH variable */
+static time_t lastwatch;
+
+static time_t lastutmpcheck = 0;
+
+/* get the time of login/logout for WATCH */
+
+static time_t
+getlogtime(WATCH_STRUCT_UTMP *u, int inout)
+{
+ FILE *in;
+ WATCH_STRUCT_UTMP uu;
+ int first = 1;
+ int srchlimit = 50; /* max number of wtmp records to search */
+
+ if (inout)
+ return u->ut_time;
+ if (!(in = fopen(WATCH_WTMP_FILE, "r")))
+ return time(NULL);
+ fseek(in, 0, SEEK_END);
+ do {
+ if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), SEEK_CUR)) {
+ fclose(in);
+ return time(NULL);
+ }
+ first = 0;
+ if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) {
+ fclose(in);
+ return time(NULL);
+ }
+ if (uu.ut_time < lastwatch || !srchlimit--) {
+ fclose(in);
+ return time(NULL);
+ }
+ }
+ while (memcmp(&uu, u, sizeof(uu)));
+
+ do
+ if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) {
+ fclose(in);
+ return time(NULL);
+ }
+ while (strncmp(uu.ut_line, u->ut_line, sizeof(u->ut_line)));
+ fclose(in);
+ return uu.ut_time;
+}
+
+/* Mutually recursive call to handle ternaries in $WATCHFMT */
+
+# define BEGIN3 '('
+# define END3 ')'
+
+static char *
+watch3ary(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt)
+{
+ int truth = 1, sep;
+
+ switch (*fmt++) {
+ case 'n':
+ truth = (u->ut_name[0] != 0);
+ break;
+ case 'a':
+ truth = inout;
+ break;
+ case 'l':
+ if (!strncmp(u->ut_line, "tty", 3))
+ truth = (u->ut_line[3] != 0);
+ else
+ truth = (u->ut_line[0] != 0);
+ break;
+# ifdef WATCH_UTMP_UT_HOST
+ case 'm':
+ case 'M':
+ truth = (u->ut_host[0] != 0);
+ break;
+# endif /* WATCH_UTMP_UT_HOST */
+ default:
+ prnt = 0; /* Skip unknown conditionals entirely */
+ break;
+ }
+ sep = *fmt++;
+ fmt = watchlog2(inout, u, fmt, (truth && prnt), sep);
+ return watchlog2(inout, u, fmt, (!truth && prnt), END3);
+}
+
+/* print a login/logout event */
+
+/**/
+static char *
+watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini)
+{
+ char buf[40], buf2[80];
+ time_t timet;
+ struct tm *tm;
+ char *fm2;
+ int len;
+ zattr atr;
+# ifdef WATCH_UTMP_UT_HOST
+ char *p;
+ int i;
+# endif /* WATCH_UTMP_UT_HOST */
+
+ while (*fmt)
+ if (*fmt == '\\') {
+ if (*++fmt) {
+ if (prnt)
+ putchar(*fmt);
+ ++fmt;
+ } else if (fini)
+ return fmt;
+ else
+ break;
+ }
+ else if (*fmt == fini)
+ return ++fmt;
+ else if (*fmt != '%') {
+ if (prnt)
+ putchar(*fmt);
+ ++fmt;
+ } else {
+ if (*++fmt == BEGIN3)
+ fmt = watch3ary(inout, u, ++fmt, prnt);
+ else if (!prnt)
+ ++fmt;
+ else
+ switch (*(fm2 = fmt++)) {
+ case 'n':
+ printf("%.*s", (int)sizeof(u->ut_name), u->ut_name);
+ break;
+ case 'a':
+ printf("%s", (!inout) ? "logged off" : "logged on");
+ break;
+ case 'l':
+ if (!strncmp(u->ut_line, "tty", 3))
+ printf("%.*s", (int)sizeof(u->ut_line) - 3, u->ut_line + 3);
+ else
+ printf("%.*s", (int)sizeof(u->ut_line), u->ut_line);
+ break;
+# ifdef WATCH_UTMP_UT_HOST
+ case 'm':
+ for (p = u->ut_host, i = sizeof(u->ut_host); i && *p; i--, p++) {
+ if (*p == '.' && !idigit(p[1]))
+ break;
+ putchar(*p);
+ }
+ break;
+ case 'M':
+ printf("%.*s", (int)sizeof(u->ut_host), u->ut_host);
+ break;
+# endif /* WATCH_UTMP_UT_HOST */
+ case 'T':
+ case 't':
+ case '@':
+ case 'W':
+ case 'w':
+ case 'D':
+ switch (*fm2) {
+ case '@':
+ case 't':
+ fm2 = "%l:%M%p";
+ break;
+ case 'T':
+ fm2 = "%K:%M";
+ break;
+ case 'w':
+ fm2 = "%a %f";
+ break;
+ case 'W':
+ fm2 = "%m/%d/%y";
+ break;
+ case 'D':
+ if (fm2[1] == '{') {
+ char *dd, *ss;
+ int n = 79;
+
+ for (ss = fm2 + 2, dd = buf2;
+ n-- && *ss && *ss != '}'; ++ss, ++dd)
+ *dd = *((*ss == '\\' && ss[1]) ? ++ss : ss);
+ if (*ss == '}') {
+ *dd = '\0';
+ fmt = ss + 1;
+ fm2 = buf2;
+ }
+ else fm2 = "%y-%m-%d";
+ }
+ else fm2 = "%y-%m-%d";
+ break;
+ }
+ timet = getlogtime(u, inout);
+ tm = localtime(&timet);
+ len = ztrftime(buf, 40, fm2, tm, 0L);
+ if (len > 0)
+ metafy(buf, len, META_NOALLOC);
+ printf("%s", (*buf == ' ') ? buf + 1 : buf);
+ break;
+ case '%':
+ putchar('%');
+ break;
+ case 'F':
+ if (*fmt == '{') {
+ fmt++;
+ atr = match_colour((const char**)&fmt, 1, 0);
+ if (*fmt == '}')
+ fmt++;
+ if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) {
+ txtunset(TXT_ATTR_FG_COL_MASK);
+ txtset(atr & TXT_ATTR_FG_ON_MASK);
+ set_colour_attribute(atr, COL_SEQ_FG, TSC_RAW);
+ }
+ }
+ break;
+ case 'f':
+ txtunset(TXT_ATTR_FG_ON_MASK);
+ set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_RAW);
+ break;
+ case 'K':
+ if (*fmt == '{') {
+ fmt++;
+ atr = match_colour((const char**)&fmt, 0, 0);
+ if (*fmt == '}')
+ fmt++;
+ if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) {
+ txtunset(TXT_ATTR_BG_COL_MASK);
+ txtset(atr & TXT_ATTR_BG_ON_MASK);
+ set_colour_attribute(atr, COL_SEQ_BG, TSC_RAW);
+ }
+ }
+ break;
+ case 'k':
+ txtunset(TXT_ATTR_BG_ON_MASK);
+ set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_RAW);
+ break;
+ case 'S':
+ txtset(TXTSTANDOUT);
+ tsetcap(TCSTANDOUTBEG, TSC_RAW);
+ break;
+ case 's':
+ txtunset(TXTSTANDOUT);
+ tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY);
+ break;
+ case 'B':
+ txtset(TXTBOLDFACE);
+ tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY);
+ break;
+ case 'b':
+ txtunset(TXTBOLDFACE);
+ tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY);
+ break;
+ case 'U':
+ txtset(TXTUNDERLINE);
+ tsetcap(TCUNDERLINEBEG, TSC_RAW);
+ break;
+ case 'u':
+ txtunset(TXTUNDERLINE);
+ tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY);
+ break;
+ default:
+ putchar('%');
+ putchar(*fm2);
+ break;
+ }
+ }
+ if (prnt)
+ putchar('\n');
+
+ return fmt;
+}
+
+/* See if the watch entry matches */
+
+static int
+watchlog_match(char *teststr, char *actual, int len)
+{
+ int ret = 0;
+ Patprog pprog;
+ char *str = dupstring(teststr);
+
+ tokenize(str);
+
+ if ((pprog = patcompile(str, PAT_STATIC, 0))) {
+ queue_signals();
+ if (pattry(pprog, actual))
+ ret = 1;
+ unqueue_signals();
+ } else if (!strncmp(actual, teststr, len))
+ ret = 1;
+ return ret;
+}
+
+/* check the List for login/logouts */
+
+static void
+watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt)
+{
+ char *v, *vv, sav;
+ int bad;
+
+ if (!*u->ut_name)
+ return;
+
+ if (*w && !strcmp(*w, "all")) {
+ (void)watchlog2(inout, u, fmt, 1, 0);
+ return;
+ }
+ if (*w && !strcmp(*w, "notme") &&
+ strncmp(u->ut_name, get_username(), sizeof(u->ut_name))) {
+ (void)watchlog2(inout, u, fmt, 1, 0);
+ return;
+ }
+ for (; *w; w++) {
+ bad = 0;
+ v = *w;
+ if (*v != '@' && *v != '%') {
+ for (vv = v; *vv && *vv != '@' && *vv != '%'; vv++);
+ sav = *vv;
+ *vv = '\0';
+ if (!watchlog_match(v, u->ut_name, sizeof(u->ut_name)))
+ bad = 1;
+ *vv = sav;
+ v = vv;
+ }
+ for (;;)
+ if (*v == '%') {
+ for (vv = ++v; *vv && *vv != '@'; vv++);
+ sav = *vv;
+ *vv = '\0';
+ if (!watchlog_match(v, u->ut_line, sizeof(u->ut_line)))
+ bad = 1;
+ *vv = sav;
+ v = vv;
+ }
+# ifdef WATCH_UTMP_UT_HOST
+ else if (*v == '@') {
+ for (vv = ++v; *vv && *vv != '%'; vv++);
+ sav = *vv;
+ *vv = '\0';
+ if (!watchlog_match(v, u->ut_host, strlen(v)))
+ bad = 1;
+ *vv = sav;
+ v = vv;
+ }
+# endif /* WATCH_UTMP_UT_HOST */
+ else
+ break;
+ if (!bad) {
+ (void)watchlog2(inout, u, fmt, 1, 0);
+ return;
+ }
+ }
+}
+
+/* compare 2 utmp entries */
+
+static int
+ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v)
+{
+ if (u->ut_time == v->ut_time)
+ return strncmp(u->ut_line, v->ut_line, sizeof(u->ut_line));
+ return u->ut_time - v->ut_time;
+}
+
+/* initialize the user List */
+
+static int
+readwtab(WATCH_STRUCT_UTMP **head, int initial_sz)
+{
+ WATCH_STRUCT_UTMP *uptr;
+ int wtabmax = initial_sz < 2 ? 32 : initial_sz;
+ int sz = 0;
+# ifdef HAVE_GETUTENT
+ WATCH_STRUCT_UTMP *tmp;
+# else
+ FILE *in;
+# endif
+
+ uptr = *head = (WATCH_STRUCT_UTMP *)
+ zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP));
+# ifdef HAVE_GETUTENT
+ setutent();
+ while ((tmp = getutent()) != NULL) {
+ memcpy(uptr, tmp, sizeof (WATCH_STRUCT_UTMP));
+# else
+ if (!(in = fopen(WATCH_UTMP_FILE, "r")))
+ return 0;
+ while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in)) {
+# endif
+# ifdef USER_PROCESS
+ if (uptr->ut_type == USER_PROCESS)
+# else /* !USER_PROCESS */
+ if (uptr->ut_name[0])
+# endif /* !USER_PROCESS */
+ {
+ uptr++;
+ if (++sz == wtabmax) {
+ uptr = (WATCH_STRUCT_UTMP *)
+ realloc(*head, (wtabmax *= 2) * sizeof(WATCH_STRUCT_UTMP));
+ if (uptr == NULL) {
+ /* memory pressure - so stop consuming and use, what we have
+ * Other option is to exit() here, as zmalloc does on error */
+ sz--;
+ break;
+ }
+ *head = uptr;
+ uptr += sz;
+ }
+ }
+ }
+# ifdef HAVE_GETUTENT
+ endutent();
+# else
+ fclose(in);
+# endif
+
+ if (sz)
+ qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP),
+ (int (*) _((const void *, const void *)))ucmp);
+ return sz;
+}
+
+/* Check for login/logout events; executed before *
+ * each prompt if WATCH is set */
+
+/**/
+void
+dowatch(void)
+{
+ WATCH_STRUCT_UTMP *utab, *uptr, *wptr;
+ struct stat st;
+ char **s;
+ char *fmt;
+ int utabsz, uct, wct;
+
+ s = watch;
+
+ holdintr();
+ if (!wtab)
+ wtabsz = readwtab(&wtab, 32);
+ if ((stat(WATCH_UTMP_FILE, &st) == -1) || (st.st_mtime <= lastutmpcheck)) {
+ noholdintr();
+ return;
+ }
+ lastutmpcheck = st.st_mtime;
+ utabsz = readwtab(&utab, wtabsz + 4);
+ noholdintr();
+ if (errflag) {
+ free(utab);
+ return;
+ }
+
+ wct = wtabsz;
+ uct = utabsz;
+ uptr = utab;
+ wptr = wtab;
+ if (errflag) {
+ free(utab);
+ return;
+ }
+ queue_signals();
+ if (!(fmt = getsparam_u("WATCHFMT")))
+ fmt = DEFAULT_WATCHFMT;
+ while ((uct || wct) && !errflag) {
+ if (!uct || (wct && ucmp(uptr, wptr) > 0))
+ wct--, watchlog(0, wptr++, s, fmt);
+ else if (!wct || (uct && ucmp(uptr, wptr) < 0))
+ uct--, watchlog(1, uptr++, s, fmt);
+ else
+ uptr++, wptr++, wct--, uct--;
+ }
+ unqueue_signals();
+ free(wtab);
+ wtab = utab;
+ wtabsz = utabsz;
+ fflush(stdout);
+ lastwatch = time(NULL);
+}
+
+static void
+checksched(void)
+{
+ /* Do nothing if WATCH is not set, or LOGCHECK has not elapsed */
+ if (watch && (int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK"))
+ dowatch();
+}
+
+/**/
+static int
+bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
+{
+ if (!watch)
+ return 1;
+ if (wtab)
+ free(wtab);
+ wtab = (WATCH_STRUCT_UTMP *)zalloc(1);
+ wtabsz = 0;
+ lastutmpcheck = 0;
+ dowatch();
+ return 0;
+}
+
+#else /* !WATCH_STRUCT_UTMP */
+
+static void
+checksched(void)
+{
+}
+
+/**/
+static int
+bin_log(char *nam, char **argv, Options ops, int func)
+{
+ return bin_notavail(nam, argv, ops, func);
+}
+
+#endif /* !WATCH_STRUCT_UTMP */
+
+/**/
+static char **watch; /* $watch */
+
+/* module setup */
+
+static struct builtin bintab[] = {
+ BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
+};
+
+static struct paramdef partab[] = {
+ PARAMDEF("WATCH", PM_SCALAR|PM_SPECIAL, &watch, NULL),
+ PARAMDEF("watch", PM_ARRAY|PM_SPECIAL, &watch, NULL),
+};
+
+static struct features module_features = {
+ bintab, sizeof(bintab)/sizeof(*bintab),
+ NULL, 0,
+ NULL, 0,
+ partab, sizeof(partab)/sizeof(*partab),
+ 0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+ /* On Cygwin, colonarr_gsu exists in libzsh.dll and we can't
+ * use &colonarr_gsu in the initialization of partab[] above */
+ partab[0].gsu = (void *)&colonarr_gsu;
+ partab[1].gsu = (void *)&vararray_gsu;
+ return 0;
+}
+
+/**/
+int
+features_(Module m, char ***features)
+{
+ *features = featuresarray(m, &module_features);
+ return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+ return handlefeatures(m, &module_features, enables);
+}
+
+/**/
+int
+boot_(UNUSED(Module m))
+{
+ static char const * const default_watchfmt = DEFAULT_WATCHFMT;
+
+ Param pma = (Param) paramtab->getnode(paramtab, "watch");
+ Param pms = (Param) paramtab->getnode(paramtab, "WATCH");
+ if (pma && pms && pma->u.arr == watch && pms->u.arr == watch) {
+ /* only tie the two parameters if both were added */
+ pma->ename = "WATCH";
+ pms->ename = "watch";
+ pma->node.flags |= PM_TIED;
+ pms->node.flags |= PM_TIED;
+ }
+ watch = mkarray(NULL);
+
+ /* These two parameters are only set to defaults if not set.
+ * So setting them in .zshrc will not be enough to load the
+ * module. It's useless until the watch array is set anyway. */
+ if (!paramtab->getnode(paramtab, "WATCHFMT"))
+ setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt));
+ if (!paramtab->getnode(paramtab, "LOGCHECK"))
+ setiparam("LOGCHECK", 60);
+
+ addprepromptfn(&checksched);
+
+ return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+ delprepromptfn(&checksched);
+ return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+ return 0;
+}
diff --git a/Src/Modules/watch.mdd b/Src/Modules/watch.mdd
new file mode 100644
index 000000000..7e8454ede
--- /dev/null
+++ b/Src/Modules/watch.mdd
@@ -0,0 +1,7 @@
+name=zsh/watch
+link=dynamic
+load=yes
+
+autofeatures="b:log p:WATCH p:watch"
+
+objects="watch.o"
diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c
index bc97771c0..56cdab888 100644
--- a/Src/Modules/zprof.c
+++ b/Src/Modules/zprof.c
@@ -213,7 +213,25 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func))
return 0;
}
-/**/
+static char *
+name_for_anonymous_function(char *name)
+{
+ char lineno[DIGBUFSIZE];
+ char *parts[7];
+
+ convbase(lineno, funcstack[0].flineno, 10);
+
+ parts[0] = name;
+ parts[1] = " [";
+ parts[2] = funcstack[0].filename ? funcstack[0].filename : "";
+ parts[3] = ":";
+ parts[4] = lineno;
+ parts[5] = "]";
+ parts[6] = NULL;
+
+ return sepjoin(parts, "", 1);
+}
+
static int
zprof_wrapper(Eprog prog, FuncWrap w, char *name)
{
@@ -224,12 +242,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name)
struct timeval tv;
struct timezone dummy;
double prev = 0, now;
+ char *name_for_lookups;
+
+ if (is_anonymous_function_name(name)) {
+ name_for_lookups = name_for_anonymous_function(name);
+ } else {
+ name_for_lookups = name;
+ }
if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) {
active = 1;
- if (!(f = findpfunc(name))) {
+ if (!(f = findpfunc(name_for_lookups))) {
f = (Pfunc) zalloc(sizeof(*f));
- f->name = ztrdup(name);
+ f->name = ztrdup(name_for_lookups);
f->calls = 0;
f->time = f->self = 0.0;
f->next = calls;
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 45fd15ee0..dfd2a2a7a 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -30,6 +30,13 @@
#include "zpty.mdh"
#include "zpty.pro"
+#ifdef __CYGWIN__
+#include <cygwin/version.h>
+#if defined(CYGWIN_VERSION_DLL_MAJOR) && CYGWIN_VERSION_DLL_MAJOR<3002
+#define USE_CYGWIN_FIX 1
+#endif
+#endif
+
/* The number of bytes we normally read when given no pattern and the
* upper bound on the number of bytes we read (even if we are give a
* pattern). */
@@ -428,6 +435,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
mypid = 0; /* trick to ensure we _exit() */
zexit(lastval, ZEXIT_NORMAL);
}
+#ifndef USE_CYGWIN_FIX
master = movefd(master);
if (master == -1) {
zerrnam(nam, "cannot duplicate fd %d: %e", master, errno);
@@ -435,6 +443,9 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
ineval = oineval;
return 1;
}
+#else
+ addmodulefd(master, FDT_INTERNAL);
+#endif
p = (Ptycmd) zalloc(sizeof(*p));
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 24659cb16..2f17c03f1 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -96,7 +96,7 @@ struct stypat {
Stypat next;
char *pat; /* pattern string */
Patprog prog; /* compiled pattern */
- int weight; /* how specific is the pattern? */
+ zulong weight; /* how specific is the pattern? */
Eprog eval; /* eval-on-retrieve? */
char **vals;
};
@@ -200,7 +200,8 @@ printstylenode(HashNode hn, int printflags)
else {
printf("zstyle %s", (p->eval ? "-e " : ""));
quotedzputs(p->pat, stdout);
- printf(" %s", s->node.nam);
+ putchar(' ');
+ quotedzputs(s->node.nam, stdout);
}
for (v = p->vals; *v; v++) {
putchar(' ');
@@ -293,7 +294,9 @@ newzstyletable(int size, char const *name)
static int
setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
{
- int weight, tmp, first;
+ zulong weight;
+ int tmp;
+ int first;
char *str;
Stypat p, q, qq;
Eprog eprog = NULL;
@@ -348,6 +351,12 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
* - A component that's a literal string scores 2 points.
* - The score of a pattern is the sum of the score of its components.
*
+ * The result of this calculation is stored in the low bits of 'weight'.
+ * The high bits of 'weight' are used to store the number of ':'-separated
+ * components. This provides a lexicographic comparison: first compare
+ * the number of components, and if that's equal, compare the specificity
+ * of the components.
+ *
* This corresponds to the notion of 'more specific' in the zshmodules(1)
* documentation of zstyle.
*/
@@ -367,6 +376,7 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
if (*str == ':') {
/* Yet another component. */
+ weight += ZLONG_CONST(1) << (CHAR_BIT * sizeof(weight) / 2);
first = 1;
weight += tmp;
@@ -766,10 +776,12 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
* ousedp (*outp)[*ousedp] is where to write next
* olenp *olenp is the size allocated for *outp
* endchar Terminator character in addition to `\0' (may be '\0')
+ * presence -F: Ternary expressions test emptyness instead
* skip If 1, don't output, just parse.
*/
static char *zformat_substring(char* instr, char **specs, char **outp,
- int *ousedp, int *olenp, int endchar, int skip)
+ int *ousedp, int *olenp, int endchar,
+ int presence, int skip)
{
char *s;
@@ -797,27 +809,35 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
if ((*s == '.' || testit) && idigit(s[1])) {
for (max = 0, s++; idigit(*s); s++)
max = (max * 10) + (int) STOUC(*s) - '0';
- }
- else if (testit)
+ } else if (*s == '.' || testit)
s++;
if (testit && STOUC(*s)) {
int actval, testval, endcharl;
- /*
- * One one number is useful for ternary expressions.
- * Remember to put the sign back.
- */
+ /* Only one number is useful for ternary expressions. */
testval = (min >= 0) ? min : (max >= 0) ? max : 0;
- if (right)
- testval *= -1;
- if (specs[STOUC(*s)])
- actval = (int)mathevali(specs[STOUC(*s)]);
- else
- actval = 0;
- /* zero means values are equal, i.e. true */
- actval -= testval;
+ if (specs[STOUC(*s)] && *specs[STOUC(*s)]) {
+ if (presence) {
+ if (testval)
+#ifdef MULTIBYTE_SUPPORT
+ if (isset(MULTIBYTE))
+ actval = MB_METASTRWIDTH(specs[STOUC(*s)]);
+ else
+#endif
+ actval = strlen(specs[STOUC(*s)]);
+ else
+ actval = 1;
+ actval = right ? (testval < actval) : (testval >= actval);
+ } else {
+ if (right) /* put the sign back */
+ testval *= -1;
+ /* zero means values are equal, i.e. true */
+ actval = (int)mathevali(specs[STOUC(*s)]) - testval;
+ }
+ } else
+ actval = presence ? !right : testval;
/* careful about premature end of string */
if (!(endcharl = *++s))
@@ -828,10 +848,10 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
* vice versa... unless we are already skipping.
*/
if (!(s = zformat_substring(s+1, specs, outp, ousedp,
- olenp, endcharl, skip || actval)))
+ olenp, endcharl, presence, skip || actval)))
return NULL;
if (!(s = zformat_substring(s+1, specs, outp, ousedp,
- olenp, ')', skip || !actval)))
+ olenp, ')', presence, skip || !actval)))
return NULL;
} else if (skip) {
continue;
@@ -903,6 +923,7 @@ static int
bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
{
char opt;
+ int presence = 0;
if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) {
zwarnnam(nam, "invalid argument: %s", args[0]);
@@ -911,15 +932,18 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
args++;
switch (opt) {
+ case 'F':
+ presence = 1;
+ /* fall-through */
case 'f':
{
- char **ap, *specs[256], *out;
+ char **ap, *specs[256] = {0}, *out;
int olen, oused = 0;
- memset(specs, 0, 256 * sizeof(char *));
-
specs['%'] = "%";
specs[')'] = ")";
+
+ /* Parse the specs in argv. */
for (ap = args + 2; *ap; ap++) {
if (!ap[0][0] || ap[0][0] == '-' || ap[0][0] == '.' ||
idigit(ap[0][0]) || ap[0][1] != ':') {
@@ -930,7 +954,8 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
}
out = (char *) zhalloc(olen = 128);
- zformat_substring(args[1], specs, &out, &oused, &olen, '\0', 0);
+ zformat_substring(args[1], specs, &out, &oused, &olen, '\0',
+ presence, 0);
out[oused] = '\0';
setsparam(args[0], ztrdup(out));
@@ -1865,7 +1890,10 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
while (*++o) {
if (!(d = sopts[STOUC(*o)])) {
if (fail) {
- zwarnnam(nam, "bad option: %c", *o);
+ if (*o != '-')
+ zwarnnam(nam, "bad option: -%c", *o);
+ else
+ zwarnnam(nam, "bad option: -%s", o);
return 1;
}
o = NULL;
@@ -1878,7 +1906,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
} else if (!(d->flags & ZOF_OPT) ||
(pp[1] && pp[1][0] != '-')) {
if (!pp[1]) {
- zwarnnam(nam, "missing argument for option: %s",
+ zwarnnam(nam, "missing argument for option: -%s",
d->name);
return 1;
}
@@ -1905,7 +1933,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
else if (!(d->flags & ZOF_OPT) ||
(pp[1] && pp[1][0] != '-')) {
if (!pp[1]) {
- zwarnnam(nam, "missing argument for option: %s",
+ zwarnnam(nam, "missing argument for option: -%s",
d->name);
return 1;
}