diff options
Diffstat (limited to 'Src/Modules')
-rw-r--r-- | Src/Modules/curses.c | 4 | ||||
-rw-r--r-- | Src/Modules/curses_keys.awk | 7 | ||||
-rw-r--r-- | Src/Modules/datetime.c | 5 | ||||
-rw-r--r-- | Src/Modules/db_gdbm.c | 4 | ||||
-rw-r--r-- | Src/Modules/files.c | 44 | ||||
-rw-r--r-- | Src/Modules/nearcolor.c | 2 | ||||
-rw-r--r-- | Src/Modules/param_private.c | 12 | ||||
-rw-r--r-- | Src/Modules/parameter.c | 110 | ||||
-rw-r--r-- | Src/Modules/system.c | 84 | ||||
-rw-r--r-- | Src/Modules/watch.c | 759 | ||||
-rw-r--r-- | Src/Modules/watch.mdd | 7 | ||||
-rw-r--r-- | Src/Modules/zprof.c | 31 | ||||
-rw-r--r-- | Src/Modules/zpty.c | 11 | ||||
-rw-r--r-- | Src/Modules/zutil.c | 82 |
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; } |