diff options
Diffstat (limited to 'Src/Modules')
-rw-r--r-- | Src/Modules/curses.c | 8 | ||||
-rw-r--r-- | Src/Modules/db_gdbm.c | 6 | ||||
-rw-r--r-- | Src/Modules/files.c | 4 | ||||
-rw-r--r-- | Src/Modules/hlgroup.c | 216 | ||||
-rw-r--r-- | Src/Modules/hlgroup.mdd | 7 | ||||
-rw-r--r-- | Src/Modules/ksh93.c | 287 | ||||
-rw-r--r-- | Src/Modules/ksh93.mdd | 10 | ||||
-rw-r--r-- | Src/Modules/mapfile.c | 10 | ||||
-rw-r--r-- | Src/Modules/param_private.c | 161 | ||||
-rw-r--r-- | Src/Modules/parameter.c | 138 | ||||
-rw-r--r-- | Src/Modules/pcre.c | 327 | ||||
-rw-r--r-- | Src/Modules/pcre.mdd | 2 | ||||
-rw-r--r-- | Src/Modules/stat.c | 14 | ||||
-rw-r--r-- | Src/Modules/system.c | 28 | ||||
-rw-r--r-- | Src/Modules/terminfo.c | 4 | ||||
-rw-r--r-- | Src/Modules/watch.c | 778 | ||||
-rw-r--r-- | Src/Modules/watch.mdd | 7 | ||||
-rw-r--r-- | Src/Modules/zftp.c | 17 | ||||
-rw-r--r-- | Src/Modules/zprof.c | 6 | ||||
-rw-r--r-- | Src/Modules/zpty.c | 2 | ||||
-rw-r--r-- | Src/Modules/zutil.c | 105 |
21 files changed, 1828 insertions, 309 deletions
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index e46903916..8950cc153 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -1302,7 +1302,7 @@ zccmd_mouse(const char *nam, char **args) zlong delay; if (!*++args || - ((delay = zstrtol(*args, &eptr, 10)), eptr != NULL)) { + ((delay = zstrtol(*args, &eptr, 10)), *eptr != '\0')) { zwarnnam(nam, "mouse delay requires an integer argument"); return 1; } @@ -1326,7 +1326,7 @@ zccmd_mouse(const char *nam, char **args) if (old_mask != zcurses_mouse_mask) zcurses_flags |= ZCF_MOUSE_MASK_CHANGED; } else { - zwarnnam(nam, "unrecognised mouse command: %s", *arg); + zwarnnam(nam, "unrecognised mouse command: %s", arg); return 1; } } @@ -1426,10 +1426,10 @@ zccmd_querychar(const char *nam, char **args) inc &= A_CHARTEXT; if (imeta(inc)) { instr[0] = Meta; - instr[1] = STOUC(inc ^ 32); + instr[1] = (unsigned char) (inc ^ 32); instr[2] = '\0'; } else { - instr[0] = STOUC(inc); + instr[0] = (unsigned char) inc; instr[1] = '\0'; } attrs = inc; diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index 7e11ec939..3fefd412b 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -34,8 +34,8 @@ #include "db_gdbm.mdh" #include "db_gdbm.pro" -#ifndef PM_UPTODATE -#define PM_UPTODATE (1<<19) /* Parameter has up-to-date data (e.g. loaded from DB) */ +#ifndef PM_UPTODATE /* Parameter has up-to-date data (e.g. loaded from DB) */ +#define PM_UPTODATE PM_DONTIMPORT_SUID /* Safe PM_ bit to re-use */ #endif static Param createhash( char *name, int flags ); @@ -111,7 +111,7 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) struct gsu_scalar_ext *dbf_carrier; char *resource_name, *pmname; GDBM_FILE dbf = NULL; - int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE; + int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE|PM_SINGLE; Param tied_param; if(!OPT_ISSET(ops,'d')) { diff --git a/Src/Modules/files.c b/Src/Modules/files.c index bf0e8f8a8..a3fec1daa 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -29,8 +29,8 @@ #include "files.mdh" -typedef int (*MoveFunc) _((char const *, char const *)); -typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *)); +typedef int (*MoveFunc) (char const *, char const *); +typedef int (*RecurseFunc) (char *, char *, struct stat const *, void *); struct recursivecmd; diff --git a/Src/Modules/hlgroup.c b/Src/Modules/hlgroup.c new file mode 100644 index 000000000..082762623 --- /dev/null +++ b/Src/Modules/hlgroup.c @@ -0,0 +1,216 @@ +/* + * hlgroup.c - Supporting parameters for highlight groups + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2024 Oliver Kiddle + * 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 Oliver Kiddle 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 Oliver Kiddle and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Oliver Kiddle 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 Oliver Kiddle and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "hlgroup.mdh" +#include "hlgroup.pro" + +#define GROUPVAR ".zle.hlgroups" + +static const struct gsu_scalar pmesc_gsu = +{ strgetfn, nullstrsetfn, nullunsetfn }; + +/**/ +static char * +convertattr(char *attrstr, int sgr) +{ + zattr atr; + char *r, *s; + int len; + + match_highlight(attrstr, &atr, NULL); + s = zattrescape(atr, sgr ? NULL : &len); + + if (sgr) { + char *c = s, *t = s - 1; + + while (c[0] == '\033' && c[1] == '[') { + c += 2; + while (isdigit(*c) || *c == ';') + *++t = *c++; + t++; + if (*c != 'm') + break; + *t = ';'; + c++; + } + if (t <= s) { /* always return at least "0" */ + *s = '0'; + t = s + 1; + } + *t = '\0'; + len = t - s; + } + + r = dupstring_wlen(s, len); + free(s); + return r; +} + +/**/ +static HashNode +getgroup(const char *name, int sgr) +{ + Param pm = NULL; + HashNode hn; + HashTable hlg; + Value v; + struct value vbuf; + char *var = GROUPVAR; + + pm = (Param) hcalloc(sizeof(struct param)); + pm->gsu.s = &pmesc_gsu; + pm->node.nam = dupstring(name); + pm->node.flags = PM_SCALAR|PM_SPECIAL; + + if (!(v = getvalue(&vbuf, &var, 0)) || + PM_TYPE(v->pm->node.flags) != PM_HASHED || + !(hlg = v->pm->gsu.h->getfn(v->pm)) || + !(hn = gethashnode2(hlg, name)) || + (((Param) hn)->node.flags & PM_UNSET)) + { + pm->u.str = dupstring(""); + pm->node.flags |= PM_UNSET; + } else { + pm->u.str = convertattr(((Param) hn)->u.str, sgr); + } + + return &pm->node; +} + +/**/ +static void +scangroup(ScanFunc func, int flags, int sgr) +{ + struct param pm; + int i; + HashNode hn; + HashTable hlg; + Value v; + struct value vbuf; + char *var = GROUPVAR; + + if (!(v = getvalue(&vbuf, &var, 0)) || + PM_TYPE(v->pm->node.flags) != PM_HASHED) + return; + hlg = v->pm->gsu.h->getfn(v->pm); + + memset((void *)&pm, 0, sizeof(struct param)); + pm.node.flags = PM_SCALAR; + pm.gsu.s = &pmesc_gsu; + + for (i = 0; i < hlg->hsize; i++) + for (hn = hlg->nodes[i]; hn; hn = hn->next) { + pm.u.str = convertattr(((Param) hn)->u.str, sgr); + pm.node.nam = hn->nam; + func(&pm.node, flags); + } +} +/**/ +static HashNode +getpmesc(UNUSED(HashTable ht), const char *name) +{ + return getgroup(name, 0); +} + +/**/ +static void +scanpmesc(UNUSED(HashTable ht), ScanFunc func, int flags) +{ + scangroup(func, flags, 0); +} + +/**/ +static HashNode +getpmsgr(UNUSED(HashTable ht), const char *name) +{ + return getgroup(name, 1); +} + +/**/ +static void +scanpmsgr(UNUSED(HashTable ht), ScanFunc func, int flags) +{ + scangroup(func, flags, 1); +} + +static struct paramdef partab[] = { + SPECIALPMDEF(".zle.esc", PM_READONLY_SPECIAL, 0, getpmesc, scanpmesc), + SPECIALPMDEF(".zle.sgr", PM_READONLY_SPECIAL, 0, getpmsgr, scanpmsgr) +}; + +static struct features module_features = { + NULL, 0, + NULL, 0, + NULL, 0, + partab, sizeof(partab)/sizeof(*partab), + 0 +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + 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)) +{ + return 0; +} + +/**/ +int +cleanup_(Module m) +{ + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} diff --git a/Src/Modules/hlgroup.mdd b/Src/Modules/hlgroup.mdd new file mode 100644 index 000000000..ee3ba7260 --- /dev/null +++ b/Src/Modules/hlgroup.mdd @@ -0,0 +1,7 @@ +name=zsh/hlgroup +link=either +load=yes + +autofeatures="p:.zle.esc p:.zle.sgr" + +objects="hlgroup.o" diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c new file mode 100644 index 000000000..3206c11f3 --- /dev/null +++ b/Src/Modules/ksh93.c @@ -0,0 +1,287 @@ +/* + * ksh93.c - support for more ksh93 features + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2022 Barton E. Schaefer + * 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 Barton E. Schaefer 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 Barton E. Schaefer and the Zsh + * Development Group have been advised of the possibility of such damage. + * + * Barton E. Schaefer 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 + * Barton E. Schaefer and the Zsh Development Group have no + * obligation to provide maintenance, support, updates, enhancements, or + * modifications. + * + */ + +#include "ksh93.mdh" +#include "ksh93.pro" + +/* Implementing "namespace" requires creating a new keword. Hrm. */ + +/* + * Standard module configuration/linkage + */ + +static struct builtin bintab[] = { + BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gur", "n") +}; + +#include "zsh.mdh" + +static void +edcharsetfn(Param pm, char *x) +{ + /* + * To make this work like ksh, we must intercept $KEYS before the widget + * is looked up, so that changing the key sequence causes a different + * widget to be substituted. Somewhat similar to "bindkey -s". + * + * Ksh93 adds SIGKEYBD to the trap list for this purpose. + */ + ; +} + +static char ** +matchgetfn(Param pm) +{ + char **zsh_match = getaparam("match"); + + /* For this to work accurately, ksh emulation should always imply + * that the (#m) and (#b) extendedglob operators are enabled. + * + * When we have a 0th element (ksharrays), it is $MATCH. Elements + * 1st and larger mirror the $match array. + */ + + if (pm->u.arr) + freearray(pm->u.arr); + if (zsh_match && *zsh_match) { + if (isset(KSHARRAYS)) { + char **ap = + (char **) zalloc(sizeof(char *) * (arrlen(zsh_match)+1)); + pm->u.arr = ap; + *ap++ = ztrdup(getsparam("MATCH")); + while (*zsh_match) + *ap = ztrdup(*zsh_match++); + } else + pm->u.arr = zarrdup(zsh_match); + } else if (isset(KSHARRAYS)) { + pm->u.arr = mkarray(ztrdup(getsparam("MATCH"))); + } else + pm->u.arr = NULL; + + return arrgetfn(pm); +} + +static const struct gsu_scalar constant_gsu = + { strgetfn, NULL, nullunsetfn }; + +static const struct gsu_scalar sh_edchar_gsu = + { strvargetfn, edcharsetfn, nullunsetfn }; +static const struct gsu_scalar sh_edmode_gsu = + { strgetfn, nullstrsetfn, nullunsetfn }; +static const struct gsu_array sh_match_gsu = + { matchgetfn, arrsetfn, stdunsetfn }; +static const struct gsu_scalar sh_name_gsu = + { strvargetfn, nullstrsetfn, nullunsetfn }; +static const struct gsu_scalar sh_subscript_gsu = + { strvargetfn, nullstrsetfn, nullunsetfn }; + +static char sh_unsetval[2]; /* Dummy to treat as NULL */ +static char *sh_name = sh_unsetval; +static char *sh_subscript = sh_unsetval; +static char *sh_edchar = sh_unsetval; +static char sh_edmode[2]; + +/* + * Some parameters listed here do not appear in ksh93.mdd autofeatures + * because they are only instantiated by ksh93_wrapper() below. This + * obviously includes those commented out here. + */ +static struct paramdef partab[] = { + PARAMDEF(".sh.edchar", PM_SCALAR|PM_SPECIAL, + &sh_edchar, &sh_edchar_gsu), + PARAMDEF(".sh.edmode", PM_SCALAR|PM_READONLY|PM_SPECIAL, + &sh_edmode, &sh_edmode_gsu), + PARAMDEF(".sh.file", PM_NAMEREF|PM_READONLY, "ZSH_SCRIPT", &constant_gsu), + PARAMDEF(".sh.lineno", PM_NAMEREF|PM_READONLY, "LINENO", &constant_gsu), + PARAMDEF(".sh.match", PM_ARRAY|PM_READONLY, NULL, &sh_match_gsu), + PARAMDEF(".sh.name", PM_SCALAR|PM_READONLY|PM_SPECIAL, + &sh_name, &sh_name_gsu), + PARAMDEF(".sh.subscript", PM_SCALAR|PM_READONLY|PM_SPECIAL, + &sh_subscript, &sh_subscript_gsu), + PARAMDEF(".sh.subshell", PM_NAMEREF|PM_READONLY, "ZSH_SUBSHELL", &constant_gsu), + /* SPECIALPMDEF(".sh.value", 0, NULL, NULL, NULL), */ + PARAMDEF(".sh.version", PM_NAMEREF|PM_READONLY, "ZSH_PATCHLEVEL", &constant_gsu) +}; + +static struct features module_features = { + bintab, sizeof(bintab)/sizeof(*bintab), + NULL, 0, + NULL, 0, + partab, sizeof(partab)/sizeof(*partab), + 0 +}; + +/**/ +static int +ksh93_wrapper(Eprog prog, FuncWrap w, char *name) +{ + Funcstack f; + Param pm; + zlong num = funcstack->prev ? getiparam(".sh.level") : 0; + + if (!EMULATION(EMULATE_KSH)) + return 1; + + if (num == 0) + for (f = funcstack; f; f = f->prev, num++); + else + num++; + + queue_signals(); + ++locallevel; /* Make these local */ +#define LOCAL_NAMEREF (PM_LOCAL|PM_UNSET|PM_NAMEREF) + if ((pm = createparam(".sh.command", LOCAL_NAMEREF))) { + pm->level = locallevel; /* Why is this necessary? */ + /* Force scoping by assignent hack */ + setloopvar(".sh.command", "ZSH_DEBUG_CMD"); + pm->node.flags |= PM_READONLY; + } + /* .sh.edchar is in partab and below */ + if (zleactive && (pm = createparam(".sh.edcol", LOCAL_NAMEREF))) { + pm->level = locallevel; + setloopvar(".sh.edcol", "CURSOR"); + pm->node.flags |= (PM_NAMEREF|PM_READONLY); + } + /* .sh.edmode is in partab and below */ + if (zleactive && (pm = createparam(".sh.edtext", LOCAL_NAMEREF))) { + pm->level = locallevel; + setloopvar(".sh.edtext", "BUFFER"); + pm->node.flags |= PM_READONLY; + } + + if ((pm = createparam(".sh.fun", PM_LOCAL|PM_UNSET))) { + pm->level = locallevel; + setsparam(".sh.fun", ztrdup(name)); + pm->node.flags |= PM_READONLY; + } + if ((pm = createparam(".sh.level", PM_LOCAL|PM_UNSET))) { + pm->level = locallevel; + setiparam(".sh.level", num); + } + if (zleactive) { + extern mod_import_variable char *curkeymapname; /* XXX */ + extern mod_import_variable char *varedarg; /* XXX */ + /* bindkey -v forces VIMODE so this test is as good as any */ + if (curkeymapname && isset(VIMODE) && + strcmp(curkeymapname, "main") == 0) + strcpy(sh_edmode, "\033"); + else + strcpy(sh_edmode, ""); + if (sh_edchar == sh_unsetval) + sh_edchar = dupstring(getsparam("KEYS")); + if (varedarg) { + char *ie = itype_end((sh_name = dupstring(varedarg)), INAMESPC, 0); + if (ie && *ie) { + *ie++ = '\0'; + /* Assume bin_vared has validated subscript */ + sh_subscript = dupstring(ie); + ie = sh_subscript + strlen(sh_subscript); + *--ie = '\0'; + } else + sh_subscript = sh_unsetval; + if ((pm = createparam(".sh.value", LOCAL_NAMEREF))) { + pm->level = locallevel; + setloopvar(".sh.value", "BUFFER"); /* Hack */ + pm->node.flags |= PM_READONLY; + } + } else + sh_name = sh_subscript = sh_unsetval; + } else { + sh_edchar = sh_name = sh_subscript = sh_unsetval; + strcpy(sh_edmode, ""); + /* TODO: + * - disciplines + * - special handling of .sh.value in math + */ + } + --locallevel; + unqueue_signals(); + + return 1; +} + +static struct funcwrap wrapper[] = { + WRAPDEF(ksh93_wrapper), +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + 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_(Module m) +{ + return addwrapper(m, wrapper); +} + +/**/ +int +cleanup_(Module m) +{ + struct paramdef *p; + + deletewrapper(m, wrapper); + + /* Clean up namerefs, otherwise deleteparamdef() is confused */ + for (p = partab; p < partab + sizeof(partab)/sizeof(*partab); ++p) { + if (p->flags & PM_NAMEREF) { + HashNode hn = gethashnode2(paramtab, p->name); + if (hn) + ((Param)hn)->node.flags &= ~PM_NAMEREF; + } + } + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} diff --git a/Src/Modules/ksh93.mdd b/Src/Modules/ksh93.mdd new file mode 100644 index 000000000..85e35e9cb --- /dev/null +++ b/Src/Modules/ksh93.mdd @@ -0,0 +1,10 @@ +name=zsh/ksh93 +link=either +load=yes + +autofeatures="b:nameref" +autofeatures_emu="b:nameref p:.sh.command p:.sh.edcol p:.sh.edtext p:.sh.file p:.sh.lineno p:.sh.match p:.sh.subshell p:.sh.version" + +moddeps="zsh/zle" + +objects="ksh93.o" diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c index dd86fb596..84cdfea18 100644 --- a/Src/Modules/mapfile.c +++ b/Src/Modules/mapfile.c @@ -170,6 +170,8 @@ get_contents(char *fname) #ifdef USE_MMAP caddr_t mmptr; struct stat sbuf; +#else + off_t size; #endif char *val; unmetafy(fname = ztrdup(fname), &fd); @@ -196,12 +198,8 @@ get_contents(char *fname) close(fd); #else /* don't USE_MMAP */ val = NULL; - if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) { - LinkList ll; - - if ((ll = readoutput(fd, 1, 0))) - val = peekfirst(ll); - } + if ((size = zstuff(&val, fname)) > 0) + val = metafy(val, size, META_HEAPDUP); #endif /* USE_MMAP */ free(fname); return val; diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 24545819d..044617190 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -87,12 +87,55 @@ makeprivate(HashNode hn, UNUSED(int flags)) ((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL && /* typeset_single() line 2300 discards PM_REMOVABLE -- why? */ !is_private(pm->old))))) { - zwarnnam("private", "can't change scope of existing param: %s", - pm->node.nam); - makeprivate_error = 1; + if (is_private(pm->old)) { + if (pm->old->node.flags & PM_READONLY) { + zerr("read-only variable: %s", pm->node.nam); + makeprivate_error = 1; + } else if ((pm->node.flags | pm->old->node.flags) == + pm->old->node.flags) { + /* private called twice on same parameter */ + Param tpm = pm; + pm = pm->old; + --locallevel; + /* why have a union if we need this switch anyway? */ + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + pm->gsu.s->setfn(pm, tpm->u.str); + tpm->u.str = NULL; + break; + case PM_INTEGER: + pm->gsu.i->setfn(pm, tpm->u.val); + break; + case PM_EFLOAT: + case PM_FFLOAT: + pm->gsu.f->setfn(pm, tpm->u.dval); + break; + case PM_ARRAY: + pm->gsu.a->setfn(pm, tpm->u.arr); + tpm->u.arr = NULL; + break; + case PM_HASHED: + pm->gsu.h->setfn(pm, tpm->u.hash); + tpm->u.hash = NULL; + break; + } + ++locallevel; + if (!(tpm->node.flags & PM_UNSET)) + pm->node.flags &= ~PM_UNSET; + } else { + zerrnam("private", + "can't change type of private param: %s", + pm->node.nam); + makeprivate_error = 1; + } + } else { + zerrnam("private", "can't change scope of existing param: %s", + pm->node.nam); + makeprivate_error = 1; + } return; } - struct gsu_closure *gsu = zhalloc(sizeof(struct gsu_closure)); + struct gsu_closure *gsu = zalloc(sizeof(struct gsu_closure)); switch (PM_TYPE(pm->node.flags)) { case PM_SCALAR: gsu->g = (void *)(pm->gsu.s); @@ -122,10 +165,11 @@ makeprivate(HashNode hn, UNUSED(int flags)) break; default: makeprivate_error = 1; + zfree(gsu, sizeof(struct gsu_closure)); 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,7 +215,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)); + int hasargs = /* *args != NULL || */ (assigns && firstnode(assigns)); makeprivate_error = 0; if (!OPT_ISSET(ops, 'P')) { @@ -190,8 +234,10 @@ 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, 'p') || (!hasargs && OPT_ISSET(ops, '+'))) { + 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); } @@ -227,7 +273,9 @@ setfn_error(Param pm) * calling the original unsetfn. This assures that if the old unsetfn * wants to use its getfn or setfn, they're unconditionally present. * The "explicit" flag indicates that "unset" was called, if zero the - * parameter is going out of scope (see params.c). + * parameter is going out of scope (see params.c). PM_DECLARED is + * asserted as if TYPESET_TO_UNSET were in use so that the private + * parameter is re-used rather than re-created when assigned again. * */ @@ -250,7 +298,7 @@ pps_setfn(Param pm, char *x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s); GsuScalar gsu = (GsuScalar)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -265,8 +313,11 @@ pps_unsetfn(Param pm, int explicit) pm->gsu.s = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.s = (GsuScalar)c; + } else + zfree(c, sizeof(struct gsu_closure)); } /**/ @@ -287,7 +338,7 @@ ppi_setfn(Param pm, zlong x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i); GsuInteger gsu = (GsuInteger)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -302,8 +353,11 @@ ppi_unsetfn(Param pm, int explicit) pm->gsu.i = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.i = (GsuInteger)c; + } else + zfree(c, sizeof(struct gsu_closure)); } /**/ @@ -324,7 +378,7 @@ ppf_setfn(Param pm, double x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f); GsuFloat gsu = (GsuFloat)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -339,8 +393,11 @@ ppf_unsetfn(Param pm, int explicit) pm->gsu.f = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.f = (GsuFloat)c; + } else + zfree(c, sizeof(struct gsu_closure)); } /**/ @@ -362,7 +419,7 @@ ppa_setfn(Param pm, char **x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a); GsuArray gsu = (GsuArray)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -377,8 +434,11 @@ ppa_unsetfn(Param pm, int explicit) pm->gsu.a = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.a = (GsuArray)c; + } else + zfree(c, sizeof(struct gsu_closure)); } static HashTable emptytable; @@ -401,7 +461,7 @@ pph_setfn(Param pm, HashTable x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h); GsuHash gsu = (GsuHash)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -416,8 +476,11 @@ pph_unsetfn(Param pm, int explicit) pm->gsu.h = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.h = (GsuHash)c; + } else + zfree(c, sizeof(struct gsu_closure)); } /* @@ -477,18 +540,19 @@ static struct funcwrap wrapper[] = { }; /**/ +static int private_wraplevel = 0; + +/**/ static int wrap_private(Eprog prog, FuncWrap w, char *name) { - static int wraplevel = 0; - - if (wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) { - int owl = wraplevel; - wraplevel = locallevel; + if (private_wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) { + int owl = private_wraplevel; + private_wraplevel = locallevel; scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET); runshfunc(prog, w, name); scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0); - wraplevel = owl; + private_wraplevel = owl; return 0; } return 1; @@ -500,27 +564,50 @@ static GetNodeFunc getparamnode; static HashNode getprivatenode(HashTable ht, const char *nam) { - HashNode hn = getparamnode(ht, nam); + /* getparamnode() would follow namerefs, we must not do that here */ + HashNode hn = gethashnode2(ht, nam); Param pm = (Param) hn; - while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) { + /* autoload has precedence over nameref, so getparamnode() */ + if (pm && (pm->node.flags & PM_AUTOLOAD)) { + hn = getparamnode(ht, nam); + pm = (Param) hn; + /* how would an autoloaded private behave? return here? */ + } + while (!fakelevel && pm && is_private(pm) && locallevel > pm->level) { + if (pm->level == private_wraplevel + 1) { + /* Variable is in the current function scope */ + break; + } +#if 0 if (!(pm->node.flags & PM_UNSET)) { /* * private parameters are always marked PM_UNSET before we - * increment locallevel, so the only way we get here is - * when createparam() wants a new parameter that is not at - * the current locallevel and it has therefore cleared the - * PM_UNSET flag. + * increment locallevel, so there are three possible ways + * to get here: + * 1) createparam() wants a new parameter that is not at + * the current locallevel and it has therefore cleared the + * PM_UNSET flag + * 2) locallevel has been incremented (startparamscope()) + * outside the usual function call stack (private_wraplevel) + * 3) dynamic scoping is fetching a value from a surrounding + * scope, we don't know if that's for assign or just expand + * The first of those is now caught in createparam() when + * testing PM_RO_BY_DESIGN and the second occurs only in + * nofork substitution or handling of ZLE specials. If the + * third is an assignment, the GSU setfn rejects it. */ DPUTS(pm->old, "BUG: PM_UNSET cleared in wrong scope"); - setfn_error(pm); - /* - * TODO: instead of throwing an error here, create a global - * parameter, insert as pm->old, handle WARN_CREATE_GLOBAL. - */ } +#endif pm = pm->old; } + + /* resolve nameref after skipping private parameters */ + if (pm && (pm->node.flags & PM_NAMEREF) && + (pm->u.str || (pm->node.flags & PM_UNSET))) + pm = (Param) resolve_nameref(pm, NULL); + return (HashNode)pm; } @@ -559,7 +646,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:%lnmrtux", "P") }; static struct features module_features = { diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index ef9148d7b..7441c30b8 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -49,13 +49,15 @@ paramtypestr(Param pm) if (pm->node.flags & PM_AUTOLOAD) return dupstring("undefined"); - switch (PM_TYPE(f)) { + /* For simplicity we treat PM_NAMEREF as PM_TYPE(PM_SCALAR) */ + switch (PM_TYPE(f)|(f & PM_NAMEREF)) { case PM_SCALAR: val = "scalar"; break; case PM_ARRAY: val = "array"; break; case PM_INTEGER: val = "integer"; break; case PM_EFLOAT: case PM_FFLOAT: val = "float"; break; case PM_HASHED: val = "association"; break; + case PM_NAMEREF: val = "nameref"; break; } DPUTS(!val, "BUG: type not handled in parameter"); val = dupstring(val); @@ -103,10 +105,15 @@ getpmparameter(UNUSED(HashTable ht), const char *name) pm->node.nam = dupstring(name); pm->node.flags = PM_SCALAR | PM_READONLY; pm->gsu.s = &nullsetscalar_gsu; - if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) && - !(rpm->node.flags & PM_UNSET)) + if ((rpm = (Param) realparamtab->getnode2(realparamtab, name)) && + !(rpm->node.flags & PM_UNSET)) { pm->u.str = paramtypestr(rpm); - else { + if ((rpm->node.flags & PM_NAMEREF) && rpm->u.str && *(rpm->u.str) && + (rpm = (Param) realparamtab->getnode(realparamtab, name)) && + !(rpm->node.flags & PM_UNSET)) { + pm->u.str = zhtricat(pm->u.str, "-", paramtypestr(rpm)); + } + } else { pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); } @@ -302,7 +309,7 @@ setfunction(char *name, char *val, int dis) shfunc_set_sticky(shf); if (!strncmp(name, "TRAP", 4) && - (sn = getsignum(name + 4)) != -1) { + (sn = getsigidx(name + 4)) != -1) { if (settrap(sn, NULL, ZSIG_FUNC)) { freeeprog(shf->funcdef); zfree(shf, sizeof(*shf)); @@ -592,7 +599,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 @@ -1226,9 +1233,16 @@ histwgetfn(UNUSED(Param pm)) pushnode(l, getdata(n)); while (he) { + char *hstr = he->node.nam; + int len = strlen(hstr); for (iw = he->nwords - 1; iw >= 0; iw--) { - h = he->node.nam + he->words[iw * 2]; - e = he->node.nam + he->words[iw * 2 + 1]; + int wbegin = he->words[iw * 2]; + int wend = he->words[iw * 2 + 1]; + + if (wbegin < 0 || wbegin >= len || wend < 0 || wend > len) + break; + h = hstr + wbegin; + e = hstr + wend; sav = *e; *e = '\0'; addlinknode(l, dupstring(h)); @@ -1244,19 +1258,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 +1283,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 +1314,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 +1343,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 +1355,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 +1391,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 +1421,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 +1450,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 +1463,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 +1493,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 +2043,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 +2098,7 @@ static Groupset get_all_groups(void) } return gs; +#endif /* DISABLE_DYNAMIC_NSS */ } /* Standard hash element lookup. */ @@ -2081,7 +2117,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 +2153,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/pcre.c b/Src/Modules/pcre.c index 6289e003e..67157cc01 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -34,11 +34,11 @@ #define CPCRE_PLAIN 0 /**/ -#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC) -#include <pcre.h> +#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H) +#define PCRE2_CODE_UNIT_WIDTH 8 +#include <pcre2.h> -static pcre *pcre_pattern; -static pcre_extra *pcre_hints; +static pcre2_code *pcre_pattern; /**/ static int @@ -47,8 +47,6 @@ zpcre_utf8_enabled(void) #if defined(MULTIBYTE_SUPPORT) && defined(HAVE_NL_LANGINFO) && defined(CODESET) static int have_utf8_pcre = -1; - /* value can toggle based on MULTIBYTE, so don't - * be too eager with caching */ if (have_utf8_pcre < -1) return 0; @@ -56,15 +54,11 @@ zpcre_utf8_enabled(void) return 0; if ((have_utf8_pcre == -1) && - (!strcmp(nl_langinfo(CODESET), "UTF-8"))) { - - if (pcre_config(PCRE_CONFIG_UTF8, &have_utf8_pcre)) - have_utf8_pcre = -2; /* erk, failed to ask */ + (pcre2_config(PCRE2_CONFIG_UNICODE, &have_utf8_pcre))) { + have_utf8_pcre = -2; /* erk, failed to ask */ } - if (have_utf8_pcre < 0) - return 0; - return have_utf8_pcre; + return (have_utf8_pcre == 1) && (!strcmp(nl_langinfo(CODESET), "UTF-8")); #else return 0; @@ -75,47 +69,38 @@ zpcre_utf8_enabled(void) static int bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func)) { - int pcre_opts = 0, pcre_errptr, target_len; - const char *pcre_error; + uint32_t pcre_opts = 0; + int target_len; + int pcre_error; + PCRE2_SIZE pcre_offset; char *target; - if(OPT_ISSET(ops,'a')) pcre_opts |= PCRE_ANCHORED; - if(OPT_ISSET(ops,'i')) pcre_opts |= PCRE_CASELESS; - if(OPT_ISSET(ops,'m')) pcre_opts |= PCRE_MULTILINE; - if(OPT_ISSET(ops,'x')) pcre_opts |= PCRE_EXTENDED; - if(OPT_ISSET(ops,'s')) pcre_opts |= PCRE_DOTALL; + if (OPT_ISSET(ops, 'a')) pcre_opts |= PCRE2_ANCHORED; + if (OPT_ISSET(ops, 'i')) pcre_opts |= PCRE2_CASELESS; + if (OPT_ISSET(ops, 'm')) pcre_opts |= PCRE2_MULTILINE; + if (OPT_ISSET(ops, 'x')) pcre_opts |= PCRE2_EXTENDED; + if (OPT_ISSET(ops, 's')) pcre_opts |= PCRE2_DOTALL; if (zpcre_utf8_enabled()) - pcre_opts |= PCRE_UTF8; - -#ifdef HAVE_PCRE_STUDY - if (pcre_hints) -#ifdef PCRE_CONFIG_JIT - pcre_free_study(pcre_hints); -#else - pcre_free(pcre_hints); -#endif - pcre_hints = NULL; -#endif + pcre_opts |= PCRE2_UTF; if (pcre_pattern) - pcre_free(pcre_pattern); + pcre2_code_free(pcre_pattern); pcre_pattern = NULL; target = ztrdup(*args); unmetafy(target, &target_len); - if ((int)strlen(target) != target_len) { - zwarnnam(nam, "embedded NULs in PCRE pattern terminate pattern"); - } - - pcre_pattern = pcre_compile(target, pcre_opts, &pcre_error, &pcre_errptr, NULL); + pcre_pattern = pcre2_compile((PCRE2_SPTR) target, (PCRE2_SIZE) target_len, + pcre_opts, &pcre_error, &pcre_offset, NULL); free(target); if (pcre_pattern == NULL) { - zwarnnam(nam, "error in regex: %s", pcre_error); + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(pcre_error, buffer, sizeof(buffer)); + zwarnnam(nam, "error in regex: %s", buffer); return 1; } @@ -123,67 +108,76 @@ bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func)) } /**/ -#ifdef HAVE_PCRE_STUDY - -/**/ static int bin_pcre_study(char *nam, UNUSED(char **args), UNUSED(Options ops), UNUSED(int func)) { - const char *pcre_error; - if (pcre_pattern == NULL) { zwarnnam(nam, "no pattern has been compiled for study"); return 1; } - - if (pcre_hints) -#ifdef PCRE_CONFIG_JIT - pcre_free_study(pcre_hints); -#else - pcre_free(pcre_hints); -#endif - pcre_hints = NULL; - pcre_hints = pcre_study(pcre_pattern, 0, &pcre_error); - if (pcre_error != NULL) - { - zwarnnam(nam, "error while studying regex: %s", pcre_error); - return 1; + int jit = 0; + if (!pcre2_config(PCRE2_CONFIG_JIT, &jit) && jit) { + if (pcre2_jit_compile(pcre_pattern, PCRE2_JIT_COMPLETE) < 0) { + zwarnnam(nam, "error while studying regex"); + return 1; + } } return 0; } -/**/ -#else /* !HAVE_PCRE_STUDY */ +static int +pcre_callout(pcre2_callout_block_8 *block, UNUSED(void *callout_data)) +{ + Eprog prog; + int ret=0; -# define bin_pcre_study bin_notavail + if (!block->callout_number && + ((prog = parse_string((char *) block->callout_string, 0)))) + { + int ef = errflag, lv = lastval; -/**/ -#endif /* !HAVE_PCRE_STUDY */ + setsparam(".pcre.subject", + metafy((char *) block->subject, block->subject_length, META_DUP)); + setiparam(".pcre.pos", block->current_position + 1); + execode(prog, 1, 0, "pcre"); + ret = lastval | errflag; + + /* Restore any user interrupt error status */ + errflag = ef | (errflag & ERRFLAG_INT); + lastval = lv; + } + + return ret; +} -/**/ static int -zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, - char *substravar, int want_offset_pair, int matchedinarr, - int want_begin_end) +zpcre_get_substrings(pcre2_code *pat, char *arg, pcre2_match_data *mdata, + int captured_count, char *matchvar, char *substravar, char *namedassoc, + int want_offset_pair, int matchedinarr, int want_begin_end) { - char **captures, *match_all, **matches; + PCRE2_SIZE *ovec; + char *match_all, **matches; char offset_all[50]; int capture_start = 1; + int vec_off; + PCRE2_SPTR ntable; /* table of named captures */ + uint32_t ncount, nsize; if (matchedinarr) { - /* bash-style captures[0] entire-matched string in the array */ + /* bash-style ovec[0] entire-matched string in the array */ capture_start = 0; } - /* captures[0] will be entire matched string, [1] first substring */ - if (!pcre_get_substring_list(arg, ovec, captured_count, (const char ***)&captures)) { - int nelem = arrlen(captures)-1; + /* ovec[0] will be entire matched string, [1] first substring */ + ovec = pcre2_get_ovector_pointer(mdata); + if (ovec) { + int nelem = captured_count - 1; /* Set to the offsets of the complete match */ if (want_offset_pair) { - sprintf(offset_all, "%d %d", ovec[0], ovec[1]); + sprintf(offset_all, "%ld %ld", ovec[0], ovec[1]); setsparam("ZPCRE_OP", ztrdup(offset_all)); } /* @@ -192,7 +186,7 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, * ovec is length 2*(1+capture_list_length) */ if (matchvar) { - match_all = metafy(captures[0], ovec[1] - ovec[0], META_DUP); + match_all = metafy(arg + ovec[0], ovec[1] - ovec[0], META_DUP); setsparam(matchvar, match_all); } /* @@ -207,21 +201,35 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, */ if (substravar && (!want_begin_end || nelem)) { - char **x, **y; - int vec_off, i; - y = &captures[capture_start]; + char **x; + int i; matches = x = (char **) zalloc(sizeof(char *) * (captured_count+1-capture_start)); - for (i = capture_start; i < captured_count; i++, y++) { + for (i = capture_start; i < captured_count; i++) { vec_off = 2*i; - if (*y) - *x++ = metafy(*y, ovec[vec_off+1]-ovec[vec_off], META_DUP); - else - *x++ = NULL; + *x++ = metafy(arg + ovec[vec_off], ovec[vec_off+1]-ovec[vec_off], META_DUP); } *x = NULL; setaparam(substravar, matches); } + if (namedassoc + && !pcre2_pattern_info(pat, PCRE2_INFO_NAMECOUNT, &ncount) && ncount + && !pcre2_pattern_info(pat, PCRE2_INFO_NAMEENTRYSIZE, &nsize) + && !pcre2_pattern_info(pat, PCRE2_INFO_NAMETABLE, &ntable)) + { + char **hash, **hashptr; + uint32_t nidx; + hashptr = hash = (char **)zshcalloc((ncount+1)*2*sizeof(char *)); + for (nidx = 0; nidx < ncount; nidx++) { + vec_off = (ntable[nsize * nidx] << 9) + 2 * ntable[nsize * nidx + 1]; + /* would metafy the key but pcre limits characters in the name */ + *hashptr++ = ztrdup((char *) ntable + nsize * nidx + 2); + *hashptr++ = metafy(arg + ovec[vec_off], + ovec[vec_off+1]-ovec[vec_off], META_DUP); + } + sethparam(namedassoc, hash); + } + if (want_begin_end) { /* * cond-infix rather than builtin; also not bash; so we set a bunch @@ -253,7 +261,8 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, setiparam("MEND", offs + !isset(KSHARRAYS) - 1); if (nelem) { char **mbegin, **mend, **bptr, **eptr; - int i, *ipair; + int i; + size_t *ipair; bptr = mbegin = zalloc(sizeof(char*)*(nelem+1)); eptr = mend = zalloc(sizeof(char*)*(nelem+1)); @@ -293,8 +302,6 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, setaparam("mend", mend); } } - - pcre_free_substring_list((const char **)captures); } return 0; @@ -320,29 +327,33 @@ getposint(char *instr, char *nam) static int bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) { - int ret, capcount, *ovec, ovecsize, c; + int ret, c; + pcre2_match_data *pcre_mdata = NULL; char *matched_portion = NULL; char *plaintext = NULL; - char *receptacle = NULL; + char *receptacle; + char *named = NULL; int return_value = 1; /* The subject length and offset start are both int values in pcre_exec */ int subject_len; int offset_start = 0; int want_offset_pair = 0; + int use_dfa = 0; if (pcre_pattern == NULL) { zwarnnam(nam, "no pattern has been compiled"); return 1; } - matched_portion = "MATCH"; - receptacle = "match"; - if(OPT_HASARG(ops,c='a')) { - receptacle = OPT_ARG(ops,c); - } - if(OPT_HASARG(ops,c='v')) { - matched_portion = OPT_ARG(ops,c); + if (!(use_dfa = OPT_ISSET(ops, 'd'))) { + matched_portion = OPT_HASARG(ops, c='v') ? OPT_ARG(ops, c) : "MATCH"; + named = OPT_HASARG(ops, c='A') ? OPT_ARG(ops, c) : ".pcre.match"; + } else if (OPT_HASARG(ops, c='v') || OPT_HASARG(ops, c='A')) { + zwarnnam(nam, "-d cannot be combined with -%c", c); + return 1; } + receptacle = OPT_HASARG(ops, 'a') ? OPT_ARG(ops, 'a') : "match"; + if(OPT_HASARG(ops,c='n')) { /* The offset position to start the search, in bytes. */ if ((offset_start = getposint(OPT_ARG(ops,c), nam)) < 0) return 1; @@ -350,36 +361,57 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) /* For the entire match, 'Return' the offset byte positions instead of the matched string */ if(OPT_ISSET(ops,'b')) want_offset_pair = 1; - if ((ret = pcre_fullinfo(pcre_pattern, pcre_hints, PCRE_INFO_CAPTURECOUNT, &capcount))) - { - zwarnnam(nam, "error %d in fullinfo", ret); - return 1; - } - - ovecsize = (capcount+1)*3; - ovec = zalloc(ovecsize*sizeof(int)); - plaintext = ztrdup(*args); unmetafy(plaintext, &subject_len); + pcre2_match_context_8 *mcontext = pcre2_match_context_create(NULL); + pcre2_set_callout(mcontext, &pcre_callout, 0); + if (offset_start > 0 && offset_start >= subject_len) - ret = PCRE_ERROR_NOMATCH; - else - ret = pcre_exec(pcre_pattern, pcre_hints, plaintext, subject_len, offset_start, 0, ovec, ovecsize); + ret = PCRE2_ERROR_NOMATCH; + else if (use_dfa) { + PCRE2_SIZE old, wscount = 128, capcount = 128; + void *workspace = zhalloc(sizeof(int) * wscount); + pcre_mdata = pcre2_match_data_create(capcount, NULL); + do { + ret = pcre2_dfa_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len, + offset_start, 0, pcre_mdata, mcontext, (int *) workspace, wscount); + if (ret == PCRE2_ERROR_DFA_WSSIZE) { + old = wscount; + wscount += wscount / 2; + workspace = hrealloc(workspace, sizeof(int) * old, sizeof(int) * wscount); + } else if (ret == 0) { + capcount += capcount / 2; + pcre2_match_data_free(pcre_mdata); + pcre_mdata = pcre2_match_data_create(capcount, NULL); + } else + break; + } while(1); + } else { + pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pattern, NULL); + ret = pcre2_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len, + offset_start, 0, pcre_mdata, mcontext); + if (ret > 0) + ret = pcre2_get_ovector_count(pcre_mdata); + } if (ret==0) return_value = 0; - else if (ret==PCRE_ERROR_NOMATCH) /* no match */; + else if (ret == PCRE2_ERROR_NOMATCH) /* no match */; else if (ret>0) { - zpcre_get_substrings(plaintext, ovec, ret, matched_portion, receptacle, - want_offset_pair, 0, 0); + zpcre_get_substrings(pcre_pattern, plaintext, pcre_mdata, ret, + matched_portion, receptacle, named, want_offset_pair, use_dfa, 0); return_value = 0; } else { - zwarnnam(nam, "error in pcre_exec [%d]", ret); + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(ret, buffer, sizeof(buffer)); + zwarnnam(nam, "error in pcre matching for %s: %s", *args, buffer); } - if (ovec) - zfree(ovec, ovecsize*sizeof(int)); + if (pcre_mdata) + pcre2_match_data_free(pcre_mdata); + if (mcontext) + pcre2_match_context_free(mcontext); zsfree(plaintext); return return_value; @@ -389,17 +421,19 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) static int cond_pcre_match(char **a, int id) { - pcre *pcre_pat; - const char *pcre_err; + pcre2_code *pcre_pat = NULL; + int pcre_err; + PCRE2_SIZE pcre_erroff; char *lhstr, *rhre, *lhstr_plain, *rhre_plain, *avar, *svar; - int r = 0, pcre_opts = 0, pcre_errptr, capcnt, *ov, ovsize; + int r = 0, pcre_opts = 0; + pcre2_match_data *pcre_mdata = NULL; int lhstr_plain_len, rhre_plain_len; int return_value = 0; if (zpcre_utf8_enabled()) - pcre_opts |= PCRE_UTF8; + pcre_opts |= PCRE2_UTF; if (isset(REMATCHPCRE) && !isset(CASEMATCH)) - pcre_opts |= PCRE_CASELESS; + pcre_opts |= PCRE2_CASELESS; lhstr = cond_str(a,0,0); rhre = cond_str(a,1,0); @@ -407,9 +441,6 @@ cond_pcre_match(char **a, int id) rhre_plain = ztrdup(rhre); unmetafy(lhstr_plain, &lhstr_plain_len); unmetafy(rhre_plain, &rhre_plain_len); - pcre_pat = NULL; - ov = NULL; - ovsize = 0; if (isset(BASHREMATCH)) { svar = NULL; @@ -421,27 +452,27 @@ cond_pcre_match(char **a, int id) switch(id) { case CPCRE_PLAIN: - if ((int)strlen(rhre_plain) != rhre_plain_len) { - zwarn("embedded NULs in PCRE pattern terminate pattern"); - } - pcre_pat = pcre_compile(rhre_plain, pcre_opts, &pcre_err, &pcre_errptr, NULL); - if (pcre_pat == NULL) { - zwarn("failed to compile regexp /%s/: %s", rhre, pcre_err); + if (!(pcre_pat = pcre2_compile((PCRE2_SPTR) rhre_plain, + (PCRE2_SIZE) rhre_plain_len, pcre_opts, + &pcre_err, &pcre_erroff, NULL))) + { + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(pcre_err, buffer, sizeof(buffer)); + zwarn("failed to compile regexp /%s/: %s", rhre, buffer); break; } - pcre_fullinfo(pcre_pat, NULL, PCRE_INFO_CAPTURECOUNT, &capcnt); - ovsize = (capcnt+1)*3; - ov = zalloc(ovsize*sizeof(int)); - r = pcre_exec(pcre_pat, NULL, lhstr_plain, lhstr_plain_len, 0, 0, ov, ovsize); - /* r < 0 => error; r==0 match but not enough size in ov + pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pat, NULL); + r = pcre2_match(pcre_pat, (PCRE2_SPTR8) lhstr_plain, lhstr_plain_len, + 0, 0, pcre_mdata, NULL); + /* r < 0 => error; r==0 match but not enough size in match data * r > 0 => (r-1) substrings found; r==1 => no substrings */ if (r==0) { - zwarn("reportable zsh problem: pcre_exec() returned 0"); + zwarn("reportable zsh problem: pcre2_match() returned 0"); return_value = 1; break; } - else if (r==PCRE_ERROR_NOMATCH) { + else if (r == PCRE2_ERROR_NOMATCH) { return_value = 0; /* no match */ break; } @@ -450,9 +481,9 @@ cond_pcre_match(char **a, int id) break; } else if (r>0) { - zpcre_get_substrings(lhstr_plain, ov, r, svar, avar, 0, - isset(BASHREMATCH), - !isset(BASHREMATCH)); + uint32_t ovec_count = pcre2_get_ovector_count(pcre_mdata); + zpcre_get_substrings(pcre_pat, lhstr_plain, pcre_mdata, ovec_count, svar, avar, + ".pcre.match", 0, isset(BASHREMATCH), !isset(BASHREMATCH)); return_value = 1; break; } @@ -463,10 +494,10 @@ cond_pcre_match(char **a, int id) free(lhstr_plain); if(rhre_plain) free(rhre_plain); + if (pcre_mdata) + pcre2_match_data_free(pcre_mdata); if (pcre_pat) - pcre_free(pcre_pat); - if (ov) - zfree(ov, ovsize*sizeof(int)); + pcre2_code_free(pcre_pat); return return_value; } @@ -488,18 +519,18 @@ static struct conddef cotab[] = { static struct builtin bintab[] = { BUILTIN("pcre_compile", 0, bin_pcre_compile, 1, 1, 0, "aimxs", NULL), - BUILTIN("pcre_match", 0, bin_pcre_match, 1, 1, 0, "a:v:n:b", NULL), + BUILTIN("pcre_match", 0, bin_pcre_match, 1, 1, 0, "A:a:v:n:bd", NULL), BUILTIN("pcre_study", 0, bin_pcre_study, 0, 0, 0, NULL, NULL) }; static struct features module_features = { bintab, sizeof(bintab)/sizeof(*bintab), -#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC) +#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H) cotab, sizeof(cotab)/sizeof(*cotab), -#else /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */ +#else /* !(HAVE_PCRE2_COMPILE_8 && HAVE_PCRE2_H) */ NULL, 0, -#endif /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */ +#endif /* !(HAVE_PCRE2_COMPILE_8 && HAVE_PCRE2_H) */ NULL, 0, NULL, 0, 0 @@ -546,19 +577,9 @@ cleanup_(Module m) int finish_(UNUSED(Module m)) { -#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC) -#ifdef HAVE_PCRE_STUDY - if (pcre_hints) -#ifdef PCRE_CONFIG_JIT - pcre_free_study(pcre_hints); -#else - pcre_free(pcre_hints); -#endif - pcre_hints = NULL; -#endif - +#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H) if (pcre_pattern) - pcre_free(pcre_pattern); + pcre2_code_free(pcre_pattern); pcre_pattern = NULL; #endif diff --git a/Src/Modules/pcre.mdd b/Src/Modules/pcre.mdd index 6eb3c691b..3e1579117 100644 --- a/Src/Modules/pcre.mdd +++ b/Src/Modules/pcre.mdd @@ -1,5 +1,5 @@ name=zsh/pcre -link=`if test x$enable_pcre = xyes && (pcre-config --version >/dev/null 2>/dev/null); then echo dynamic; else echo no; fi` +link=`if test x$enable_pcre = xyes; then echo dynamic; else echo no; fi` load=no autofeatures="b:pcre_compile b:pcre_study b:pcre_match" diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index 7c736072b..c9f851974 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -406,7 +406,7 @@ bin_stat(char *name, char **args, Options ops, UNUSED(int func)) } else { for (; *arg; arg++) { if (strchr("glLnNorstT", *arg)) - ops->ind[STOUC(*arg)] = 1; + ops->ind[(unsigned char) *arg] = 1; else if (*arg == 'A') { if (arg[1]) { arrnam = arg+1; @@ -503,8 +503,10 @@ bin_stat(char *name, char **args, Options ops, UNUSED(int func)) if (OPT_ISSET(ops,'f')) nargs = 1; else - for (aptr = args; *aptr; aptr++) + for (aptr = args; *aptr; aptr++) { + unmetafy(*aptr, NULL); nargs++; + } if (OPT_ISSET(ops,'g')) { flags |= STF_GMT; @@ -555,8 +557,8 @@ bin_stat(char *name, char **args, Options ops, UNUSED(int func)) for (; OPT_ISSET(ops,'f') || *args; args++) { char outbuf[PATH_MAX + 9]; /* "link " + link name + NULL */ int rval = OPT_ISSET(ops,'f') ? fstat(fd, &statbuf) : - OPT_ISSET(ops,'L') ? lstat(unmeta(*args), &statbuf) : - stat(unmeta(*args), &statbuf); + OPT_ISSET(ops,'L') ? lstat(*args, &statbuf) : + stat(*args, &statbuf); if (rval) { if (OPT_ISSET(ops,'f')) sprintf(outbuf, "%d", fd); @@ -571,10 +573,10 @@ bin_stat(char *name, char **args, Options ops, UNUSED(int func)) if (flags & STF_FILE) { if (arrnam) - *arrptr++ = ztrdup(*args); + *arrptr++ = ztrdup_metafy(*args); else if (hashnam) { *hashptr++ = ztrdup(HNAMEKEY); - *hashptr++ = ztrdup(*args); + *hashptr++ = ztrdup_metafy(*args); } else printf("%s%s", *args, (flags & STF_PICK) ? " " : ":\n"); } diff --git a/Src/Modules/system.c b/Src/Modules/system.c index ecd4e2546..929a8b002 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -74,6 +74,8 @@ bin_sysread(char *nam, char **args, Options ops, UNUSED(int func)) int infd = 0, outfd = -1, bufsize = SYSREAD_BUFSIZE, count; char *outvar = NULL, *countvar = NULL, *inbuf; + errno = 0; /* Distinguish non-system errors */ + /* -i: input file descriptor if not stdin */ if (OPT_ISSET(ops, 'i')) { infd = getposint(OPT_ARG(ops, 'i'), nam); @@ -83,10 +85,6 @@ bin_sysread(char *nam, char **args, Options ops, UNUSED(int func)) /* -o: output file descriptor, else store in REPLY */ if (OPT_ISSET(ops, 'o')) { - if (*args) { - zwarnnam(nam, "no argument allowed with -o"); - return 1; - } outfd = getposint(OPT_ARG(ops, 'o'), nam); if (outfd < 0) return 1; @@ -242,6 +240,8 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func)) int outfd = 1, len, count, totcount; char *countvar = NULL; + errno = 0; /* Distinguish non-system errors */ + /* -o: output file descriptor if not stdout */ if (OPT_ISSET(ops, 'o')) { outfd = getposint(OPT_ARG(ops, 'o'), nam); @@ -280,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 @@ -297,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 }, @@ -304,6 +307,13 @@ static struct { char *name; int oflag; } openopts[] = { { "trunc", O_TRUNC } }; +/* + * Return values of bin_sysopen: + * 0 Success + * 1 Error in parameters to command + * 2 Error on open, ERRNO set by system + */ + /**/ static int bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) @@ -320,6 +330,8 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) int fdflags = 0; #endif + errno = 0; /* Distinguish non-system errors */ + if (!OPT_ISSET(ops, 'u')) { zwarnnam(nam, "file descriptor not specified"); return 1; @@ -375,12 +387,12 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) if (fd == -1) { zwarnnam(nam, "can't open file %s: %e", *args, errno); - return 1; + return 2; } moved_fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd); if (moved_fd == -1) { zwarnnam(nam, "can't open file %s", *args); - return 1; + return 2; } #ifdef FD_CLOEXEC @@ -424,6 +436,8 @@ bin_sysseek(char *nam, char **args, Options ops, UNUSED(int func)) char *whence; off_t pos; + errno = 0; /* Distinguish non-system errors */ + /* -u: file descriptor if not stdin */ if (OPT_ISSET(ops, 'u')) { fd = getposint(OPT_ARG(ops, 'u'), nam); diff --git a/Src/Modules/terminfo.c b/Src/Modules/terminfo.c index 4596b41d2..f9ab64fb3 100644 --- a/Src/Modules/terminfo.c +++ b/Src/Modules/terminfo.c @@ -160,7 +160,7 @@ getterminfo(UNUSED(HashTable ht), const char *name) pm->node.flags |= PM_SCALAR; pm->gsu.s = &nullsetscalar_gsu; } else if ((tistr = (char *)tigetstr(nameu)) != NULL && tistr != (char *)-1) { - pm->u.str = dupstring(tistr); + pm->u.str = metafy(tistr, -1, META_HEAPDUP); pm->node.flags |= PM_SCALAR; pm->gsu.s = &nullsetscalar_gsu; } else { @@ -280,7 +280,7 @@ scanterminfo(UNUSED(HashTable ht), ScanFunc func, int flags) for (capname = (char **)strnames; *capname; capname++) { if ((tistr = (char *)tigetstr(*capname)) != NULL && tistr != (char *)-1) { - pm->u.str = dupstring(tistr); + pm->u.str = metafy(tistr, -1, META_HEAPDUP); pm->node.nam = dupstring(*capname); func(&pm->node, flags); } diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c new file mode 100644 index 000000000..acc499518 --- /dev/null +++ b/Src/Modules/watch.c @@ -0,0 +1,778 @@ +/* + * 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) { + applytextattributes(TSC_RAW); + putchar(*fmt); + } + ++fmt; + } else if (fini) + return fmt; + else + break; + } + else if (*fmt == fini) + return ++fmt; + else if (*fmt != '%') { + if (prnt) { + applytextattributes(TSC_RAW); + putchar(*fmt); + } + ++fmt; + } else { + if (*++fmt == BEGIN3) + fmt = watch3ary(inout, u, ++fmt, prnt); + else if (!prnt) + ++fmt; + else + switch (*(fm2 = fmt++)) { + case 'n': + applytextattributes(TSC_RAW); + printf("%.*s", (int)sizeof(u->ut_name), u->ut_name); + break; + case 'a': + applytextattributes(TSC_RAW); + printf("%s", (!inout) ? "logged off" : "logged on"); + break; + case 'l': + applytextattributes(TSC_RAW); + 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': + applytextattributes(TSC_RAW); + 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': + applytextattributes(TSC_RAW); + 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); + applytextattributes(TSC_RAW); + printf("%s", (*buf == ' ') ? buf + 1 : buf); + break; + case '%': + applytextattributes(TSC_RAW); + putchar('%'); + break; + case 'F': + if (*fmt == '{') { + fmt++; + atr = match_colour((const char**)&fmt, 1, 0); + if (*fmt == '}') + fmt++; + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + break; + } + } /* fall-through */ + case 'f': + tunsetattrs(TXTFGCOLOUR); + break; + case 'H': + if (*fmt == '{') { + fmt = parsehighlight(fmt + 1, '}', &atr); + if (atr && atr != TXT_ERROR) + treplaceattrs(atr); + } + break; + case 'K': + if (*fmt == '{') { + fmt++; + atr = match_colour((const char**)&fmt, 0, 0); + if (*fmt == '}') + fmt++; + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + break; + } + } /* fall-through */ + case 'k': + tunsetattrs(TXTBGCOLOUR); + break; + case 'S': + tsetattrs(TXTSTANDOUT); + break; + case 's': + tunsetattrs(TXTSTANDOUT); + break; + case 'B': + tsetattrs(TXTBOLDFACE); + break; + case 'b': + tunsetattrs(TXTBOLDFACE); + break; + case 'U': + tsetattrs(TXTUNDERLINE); + break; + case 'u': + tunsetattrs(TXTUNDERLINE); + break; + default: + applytextattributes(TSC_RAW); + putchar('%'); + putchar(*fm2); + break; + } + } + if (prnt) { + applytextattributes(TSC_RAW); + putchar('\n'); + } + + return fmt; +} + +/* See if the watch entry matches */ + +static int +watchlog_match(char *teststr, char *actual, size_t buflen) +{ + int ret = 0; + Patprog pprog; + char *str = dupstring(teststr); + size_t len = strnlen(actual, buflen); + char *user = metafy(actual, len, + len == buflen ? META_HEAPDUP : META_USEHEAP); + + tokenize(str); + + if ((pprog = patcompile(str, PAT_STATIC, 0))) { + queue_signals(); + if (pattry(pprog, user)) + ret = 1; + unqueue_signals(); + } else if (!strcmp(user, teststr)) + 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")) { + int len = strnlen(u->ut_name, sizeof(u->ut_name)); + char *username = metafy(u->ut_name, len, + (len == sizeof(u->ut_name) ? + META_HEAPDUP /* allow for nul terminator */ : + META_USEHEAP)); + if (strcmp(username, get_username())) { + (void)watchlog2(inout, u, fmt, 1, 0); + return; + } + w++; + } + 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, sizeof(u->ut_host))) + 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/zftp.c b/Src/Modules/zftp.c index e8e239e76..b60e5bf31 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -127,7 +127,7 @@ typedef int (*readwrite_t)(int, char *, off_t, int); struct zftpcmd { const char *nam; - int (*fun) _((char *, char **, int)); + int (*fun) (char *, char **, int); int min, max, flags; }; @@ -944,9 +944,9 @@ zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep) return 1; } for (i = 0; i < 4; i++) - iaddr[i] = STOUC(nums[i]); - iport[0] = STOUC(nums[4]); - iport[1] = STOUC(nums[5]); + iaddr[i] = (unsigned char) nums[i]; + iport[0] = (unsigned char) nums[4]; + iport[1] = (unsigned char) nums[5]; memcpy(&zdsockp->in.sin_addr, iaddr, sizeof(iaddr)); memcpy(&zdsockp->in.sin_port, iport, sizeof(iport)); @@ -2438,7 +2438,7 @@ zftp_type(char *name, char **args, int flags) fflush(stdout); return 0; } else { - nt = toupper(STOUC(*str)); + nt = toupper((unsigned char) *str); /* * RFC959 specifies other types, but these are the only * ones we know what to do with. @@ -2472,7 +2472,7 @@ zftp_mode(char *name, char **args, UNUSED(int flags)) fflush(stdout); return 0; } - nt = str[0] = toupper(STOUC(*str)); + nt = str[0] = toupper((unsigned char) *str); if (str[1] || (nt != 'S' && nt != 'B')) { zwarnnam(name, "transfer mode %s not recognised", str); return 1; @@ -3075,7 +3075,7 @@ bin_zftp(char *name, char **args, UNUSED(Options ops), UNUSED(int func)) if ((prefs = getsparam_u("ZFTP_PREFS"))) { zfprefs = 0; for (ptr = prefs; *ptr; ptr++) { - switch (toupper(STOUC(*ptr))) { + switch (toupper((unsigned char) *ptr)) { case 'S': /* sendport */ zfprefs |= ZFPF_SNDP; @@ -3147,6 +3147,7 @@ zftp_cleanup(void) lastmsg = NULL; zfunsetparam("ZFTP_SESSION"); freelinklist(zfsessions, (FreeFunc) freesession); + zfsessions = NULL; zfree(zfstatusp, sizeof(int)*zfsesscnt); zfstatusp = NULL; } @@ -3172,7 +3173,7 @@ static struct features module_features = { int setup_(UNUSED(Module m)) { - return (require_module("zsh/net/tcp", NULL, 0) == 1); + return 0; } /**/ diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c index 56cdab888..171a15b90 100644 --- a/Src/Modules/zprof.c +++ b/Src/Modules/zprof.c @@ -163,9 +163,9 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func)) *ap = NULL; qsort(fs, ncalls, sizeof(f), - (int (*) _((const void *, const void *))) cmpsfuncs); + (int (*) (const void *, const void *)) cmpsfuncs); qsort(as, narcs, sizeof(a), - (int (*) _((const void *, const void *))) cmpparcs); + (int (*) (const void *, const void *)) cmpparcs); printf("num calls time self name\n-----------------------------------------------------------------------------------\n"); for (fp = fs, i = 1; *fp; fp++, i++) { @@ -179,7 +179,7 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func)) (*fp)->name); } qsort(fs, ncalls, sizeof(f), - (int (*) _((const void *, const void *))) cmptfuncs); + (int (*) (const void *, const void *)) cmptfuncs); for (fp = fs; *fp; fp++) { printf("\n-----------------------------------------------------------------------------------\n\n"); diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index dfd2a2a7a..c2656698c 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -638,7 +638,7 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch) readchar = cmd->read; cmd->read = -1; } else - readchar = STOUC(buf[used]); + readchar = (unsigned char) buf[used]; if (imeta(readchar)) { buf[used++] = Meta; buf[used++] = (char) (readchar ^ 32); diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 691ba6c2f..5eccea7a9 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -462,6 +462,28 @@ lookupstyle(char *ctxt, char *style) } static int +testforstyle(char *ctxt, char *style) +{ + Style s; + Stypat p; + int found = 0; + + s = (Style)zstyletab->getnode2(zstyletab, style); + if (s) { + MatchData match; + savematch(&match); + for (p = s->pats; p; p = p->next) + if (pattry(p->prog, ctxt)) { + found = 1; + break; + } + restorematch(&match); + } + + return !found; /* 0 == success */ +} + +static int bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { int min, max, n, add = 0, list = ZSLIST_NONE, eval = 0; @@ -570,6 +592,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) case 't': min = 2; max = -1; break; case 'T': min = 2; max = -1; break; case 'm': min = 3; max = 3; break; + case 'q': min = 2; max = 2; break; case 'g': min = 1; max = 3; break; default: zwarnnam(nam, "invalid option: %s", args[0]); @@ -723,6 +746,15 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) return 1; } break; + case 'q': + { + int success; + queue_signals(); /* Protect PAT_STATIC */ + success = testforstyle(args[1], args[2]); + unqueue_signals(); + return success; + } + break; case 'g': { int ret = 1; @@ -776,10 +808,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; @@ -793,11 +827,11 @@ static char *zformat_substring(char* instr, char **specs, char **outp, if (idigit(*s)) { for (min = 0; idigit(*s); s++) - min = (min * 10) + (int) STOUC(*s) - '0'; + min = (min * 10) + (int) (unsigned char) *s - '0'; } /* Ternary expressions */ - testit = (STOUC(*s) == '('); + testit = ((unsigned char) *s == '('); if (testit && s[1] == '-') { /* Allow %(-1... etc. */ @@ -806,27 +840,36 @@ 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'; + max = (max * 10) + (int) (unsigned char) *s - '0'; } else if (*s == '.' || testit) s++; - if (testit && STOUC(*s)) { + if (testit && (unsigned char) *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[(unsigned char) *s] && *specs[(unsigned char) *s]) { + if (presence) { + if (testval) +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) + actval = MB_METASTRWIDTH(specs[(unsigned char) *s]); + else +#endif + actval = strlen(specs[(unsigned char) *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[(unsigned char) *s]) - testval; + } + } else + actval = presence ? !right : testval; /* careful about premature end of string */ if (!(endcharl = *++s)) @@ -837,14 +880,14 @@ 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; - } else if ((spec = specs[STOUC(*s)])) { + } else if ((spec = specs[(unsigned char) *s])) { int len; if ((len = strlen(spec)) > max && max >= 0) @@ -912,6 +955,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]); @@ -920,6 +964,9 @@ 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] = {0}, *out; @@ -935,11 +982,12 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) zwarnnam(nam, "invalid argument: %s", *ap); return 1; } - specs[STOUC(ap[0][0])] = ap[0] + 2; + specs[(unsigned char) ap[0][0]] = ap[0] + 2; } 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)); @@ -949,7 +997,7 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) case 'a': { char **ap, *cp; - int nbc = 0, colon = 0, pre = 0, suf = 0; + int nbc = 0, pre = 0, suf = 0; #ifdef MULTIBYTE_SUPPORT int prechars = 0; #endif /* MULTIBYTE_SUPPORT */ @@ -964,7 +1012,6 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) int dchars = 0; #endif /* MULTIBYTE_SUPPORT */ - colon++; if ((d = cp - *ap - nbc) > pre) pre = d; #ifdef MULTIBYTE_SUPPORT @@ -1362,11 +1409,11 @@ rmatch(RParseResult *sm, char *subj, char *var1, char *var2, int comp) "zregexparse-guard"), !lastval))) { LinkNode aln; char **mend; - int len; + int len = 0; queue_signals(); - mend = getaparam("mend"); - len = atoi(mend[0]); + if ((mend = getaparam("mend"))) + len = atoi(mend[0]); unqueue_signals(); for (i = len; i; i--) @@ -1848,7 +1895,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) d->vals = d->last = NULL; opt_descs = d; if (!o[1]) - sopts[STOUC(*o)] = d; + sopts[(unsigned char) *o] = d; if ((flags & ZOF_MAP) && !map_opt_desc(d)) { zwarnnam(nam, "cyclic option mapping: %s", args[-1]); return 1; @@ -1872,7 +1919,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } if (!(d = lookup_opt(o + 1))) { while (*++o) { - if (!(d = sopts[STOUC(*o)])) { + if (!(d = sopts[(unsigned char) *o])) { if (fail) { if (*o != '-') zwarnnam(nam, "bad option: -%c", *o); |