diff options
Diffstat (limited to 'Src')
68 files changed, 4202 insertions, 1809 deletions
diff --git a/Src/Makemod.in.in b/Src/Makemod.in.in index ea0cdc3a4..3343ae1d0 100644 --- a/Src/Makemod.in.in +++ b/Src/Makemod.in.in @@ -52,32 +52,17 @@ DLCOMPILE = $(CC) -c -I. -I$(dir_top)/Src -I$(sdir_top)/Src -I$(sdir_top)/Src/ LINK = $(CC) $(LDFLAGS) $(EXELDFLAGS) $(EXTRA_LDFLAGS) -o $@ DLLINK = $(DLLD) $(LDFLAGS) $(LIBLDFLAGS) $(DLLDFLAGS) -o $@ -KNR_OBJ=.o -KNROBJ=._foo_ +OBJ=.o -ANSIOBJ=.o -ANSI_OBJ=._foo_ +.SUFFIXES: .c .$(DL_EXT) ..o .o .syms .pro .epro -.SUFFIXES: .c .$(DL_EXT) ..o .._foo_ .o ._foo_ .syms .pro .epro - -.c$(ANSI@U@OBJ): +.c$(OBJ): $(COMPILE) -o $@ $< @rm -f $(dir_src)/stamp-modobjs -.c$(KNR@U@OBJ): - @ANSI2KNR@ $< > $@.c - $(COMPILE) -o $@ $@.c - rm -f $@.c - @rm -f $(dir_src)/stamp-modobjs - -.c.$(ANSI@U@OBJ): +.c.$(OBJ): $(DLCOMPILE) -o $@ $< -.c.$(KNR@U@OBJ): - @ANSI2KNR@ $< > $@.c - $(DLCOMPILE) -o $@ $@.c - rm -f $@.c - .c.syms: $(AWK) -f $(sdir_src)/makepro.awk $< $(subdir) > $@ 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/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 065fa63d2..044617190 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -87,9 +87,52 @@ 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 = zalloc(sizeof(struct gsu_closure)); @@ -122,6 +165,7 @@ 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 */ @@ -229,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. * */ @@ -252,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); @@ -267,9 +313,10 @@ 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 + } else zfree(c, sizeof(struct gsu_closure)); } @@ -291,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); @@ -306,9 +353,10 @@ 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 + } else zfree(c, sizeof(struct gsu_closure)); } @@ -330,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); @@ -345,9 +393,10 @@ 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 + } else zfree(c, sizeof(struct gsu_closure)); } @@ -370,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); @@ -385,9 +434,10 @@ 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 + } else zfree(c, sizeof(struct gsu_closure)); } @@ -411,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); @@ -426,9 +476,10 @@ 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 + } else zfree(c, sizeof(struct gsu_closure)); } @@ -489,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; @@ -512,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; } @@ -571,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:%lmprtux", "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 dbb61e474..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)); @@ -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)); 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 0df9b35b7..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; 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 index d45c3cf3d..acc499518 100644 --- a/Src/Modules/watch.c +++ b/Src/Modules/watch.c @@ -255,8 +255,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) while (*fmt) if (*fmt == '\\') { if (*++fmt) { - if (prnt) + if (prnt) { + applytextattributes(TSC_RAW); putchar(*fmt); + } ++fmt; } else if (fini) return fmt; @@ -266,8 +268,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) else if (*fmt == fini) return ++fmt; else if (*fmt != '%') { - if (prnt) + if (prnt) { + applytextattributes(TSC_RAW); putchar(*fmt); + } ++fmt; } else { if (*++fmt == BEGIN3) @@ -277,12 +281,15 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) 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 @@ -290,6 +297,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) 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; @@ -297,6 +305,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) } break; case 'M': + applytextattributes(TSC_RAW); printf("%.*s", (int)sizeof(u->ut_host), u->ut_host); break; # endif /* WATCH_UTMP_UT_HOST */ @@ -343,9 +352,11 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) 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': @@ -354,16 +365,20 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) 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); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + break; } - } - break; + } /* fall-through */ case 'f': - txtunset(TXT_ATTR_FG_ON_MASK); - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_RAW); + tunsetattrs(TXTFGCOLOUR); + break; + case 'H': + if (*fmt == '{') { + fmt = parsehighlight(fmt + 1, '}', &atr); + if (atr && atr != TXT_ERROR) + treplaceattrs(atr); + } break; case 'K': if (*fmt == '{') { @@ -371,49 +386,43 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) 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); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + break; } - } - break; + } /* fall-through */ case 'k': - txtunset(TXT_ATTR_BG_ON_MASK); - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_RAW); + tunsetattrs(TXTBGCOLOUR); break; case 'S': - txtset(TXTSTANDOUT); - tsetcap(TCSTANDOUTBEG, TSC_RAW); + tsetattrs(TXTSTANDOUT); break; case 's': - txtunset(TXTSTANDOUT); - tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY); + tunsetattrs(TXTSTANDOUT); break; case 'B': - txtset(TXTBOLDFACE); - tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY); + tsetattrs(TXTBOLDFACE); break; case 'b': - txtunset(TXTBOLDFACE); - tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY); + tunsetattrs(TXTBOLDFACE); break; case 'U': - txtset(TXTUNDERLINE); - tsetcap(TCUNDERLINEBEG, TSC_RAW); + tsetattrs(TXTUNDERLINE); break; case 'u': - txtunset(TXTUNDERLINE); - tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY); + tunsetattrs(TXTUNDERLINE); break; default: + applytextattributes(TSC_RAW); putchar('%'); putchar(*fm2); break; } } - if (prnt) + if (prnt) { + applytextattributes(TSC_RAW); putchar('\n'); + } return fmt; } @@ -421,20 +430,23 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) /* See if the watch entry matches */ static int -watchlog_match(char *teststr, char *actual, int len) +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, actual)) + if (pattry(pprog, user)) ret = 1; unqueue_signals(); - } else if (!strncmp(actual, teststr, len)) + } else if (!strcmp(user, teststr)) ret = 1; return ret; } @@ -454,10 +466,17 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt) (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; + 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; @@ -486,7 +505,7 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt) for (vv = ++v; *vv && *vv != '%'; vv++); sav = *vv; *vv = '\0'; - if (!watchlog_match(v, u->ut_host, strlen(v))) + if (!watchlog_match(v, u->ut_host, sizeof(u->ut_host))) bad = 1; *vv = sav; v = vv; @@ -565,7 +584,7 @@ readwtab(WATCH_STRUCT_UTMP **head, int initial_sz) if (sz) qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP), - (int (*) _((const void *, const void *)))ucmp); + (int (*) (const void *, const void *))ucmp); return sz; } diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index e8e239e76..0c26828fd 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; @@ -3172,7 +3172,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 2f17c03f1..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; @@ -795,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. */ @@ -808,25 +840,25 @@ 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; /* Only one number is useful for ternary expressions. */ testval = (min >= 0) ? min : (max >= 0) ? max : 0; - if (specs[STOUC(*s)] && *specs[STOUC(*s)]) { + if (specs[(unsigned char) *s] && *specs[(unsigned char) *s]) { if (presence) { if (testval) #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE)) - actval = MB_METASTRWIDTH(specs[STOUC(*s)]); + actval = MB_METASTRWIDTH(specs[(unsigned char) *s]); else #endif - actval = strlen(specs[STOUC(*s)]); + actval = strlen(specs[(unsigned char) *s]); else actval = 1; actval = right ? (testval < actval) : (testval >= actval); @@ -834,7 +866,7 @@ static char *zformat_substring(char* instr, char **specs, char **outp, if (right) /* put the sign back */ testval *= -1; /* zero means values are equal, i.e. true */ - actval = (int)mathevali(specs[STOUC(*s)]) - testval; + actval = (int) mathevali(specs[(unsigned char) *s]) - testval; } } else actval = presence ? !right : testval; @@ -855,7 +887,7 @@ static char *zformat_substring(char* instr, char **specs, char **outp, 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) @@ -950,7 +982,7 @@ 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); @@ -965,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 */ @@ -980,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 @@ -1378,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--) @@ -1864,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; @@ -1888,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); diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 4ac5d089f..09282d42d 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -1230,14 +1230,14 @@ check_param(char *s, int set, int test) else if (idigit(*e)) while (idigit(*e)) e++; - else if ((ie = itype_end(e, IIDENT, 0)) != e) { + else if ((ie = itype_end(e, INAMESPC, 0)) != e) { do { e = ie; if (comppatmatch && *comppatmatch && (*e == Star || *e == Quest)) ie = e + 1; else - ie = itype_end(e, IIDENT, 0); + ie = itype_end(e, INAMESPC, 0); } while (ie != e); } @@ -2249,8 +2249,9 @@ addmatches(Cadata dat, char **argv) llpl = strlen(lpre); llsl = strlen(lsuf); - if (llpl + (int)strlen(compqiprefix) + (int)strlen(lipre) != origlpre - || llsl + (int)strlen(compqisuffix) + (int)strlen(lisuf) != origlsuf) + /* This used to reference compqiprefix and compqisuffix, why? */ + if (llpl + (int)strlen(qipre) + (int)strlen(lipre) != origlpre + || llsl + (int)strlen(qisuf) + (int)strlen(lisuf) != origlsuf) lenchanged = 1; /* Test if there is an existing -P prefix. */ @@ -2898,9 +2899,9 @@ add_match_data(int alt, char *str, char *orig, Cline line, *t++ = '$'; *t++ = '\''; *t++ = '\\'; - *t++ = '0' + ((STOUC(curchar) >> 6) & 7); - *t++ = '0' + ((STOUC(curchar) >> 3) & 7); - *t++ = '0' + (STOUC(curchar) & 7); + *t++ = '0' + (((unsigned char) curchar >> 6) & 7); + *t++ = '0' + (((unsigned char) curchar >> 3) & 7); + *t++ = '0' + ((unsigned char) curchar & 7); *t++ = '\''; } while (cnt == MB_INCOMPLETE && fs < fe); /* Scanning restarts from the spot after the char we skipped. */ @@ -3252,7 +3253,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) /* Now sort the array (it contains matches). */ matchorder = flags; qsort((void *) rp, n, sizeof(Cmatch), - (int (*) _((const void *, const void *)))matchcmp); + (int (*) (const void *, const void *))matchcmp); /* since the matches are sorted and the default is to remove * all duplicates, -1 (remove only consecutive dupes) is a no-op, @@ -3294,7 +3295,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) sp = (Cmatch *) zhalloc((n + 1) * sizeof(Cmatch)); memcpy(sp, rp, (n + 1) * sizeof(Cmatch)); qsort((void *) sp, n, sizeof(Cmatch), - (int (*) _((const void *, const void *)))matchcmp); + (int (*) (const void *, const void *))matchcmp); for (asp = sp + 1; *asp; asp++) { Cmatch *ap = asp - 1, *bp = asp; if (matcheq(*ap, *bp)) { diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 67a60963e..342611f1f 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -518,7 +518,7 @@ parse_class(Cpattern p, char *iptr) ch = range_type((char *)iptr, nptr-iptr); iptr = nptr + 2; if (ch != PP_UNKWN) - *optr++ = STOUC(Meta) + ch; + *optr++ = (unsigned char) Meta + ch; } else { /* characters stay metafied */ char *ptr1 = iptr; @@ -829,7 +829,9 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) ca_args: - if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) { + if (mstr && (dat.aflags & CAF_MATCH) && + (match = parse_cmatcher(name, mstr)) == pcm_err) + { zsfree(mstr); zfree(dat.dpar, dparsize); return 1; diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 0dc64db6a..5619160a9 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -291,12 +291,12 @@ getcolval(char *s, int multi) case '?': *p = '\177'; break; default: if (*s >= '0' && *s <= '7') { - int i = STOUC(*s); + int i = (unsigned char) *s; if (*++s >= '0' && *s <= '7') { - i = (i * 8) + STOUC(*s); + i = (i * 8) + (unsigned char) *s; if (*++s >= '0' && *s <= '7') - i = (i * 8) + STOUC(*s); + i = (i * 8) + (unsigned char) *s; } *p = (char) i; } else @@ -305,7 +305,7 @@ getcolval(char *s, int multi) } else if (*s == '^') { if ((s[1] >= '@' && s[1] <= '_') || (s[1] >= 'a' && s[1] <= 'z')) - *p = (char) (STOUC(*s) & ~0x60); + *p = (char) ((unsigned char) *s & ~0x60); else if (s[1] == '?') *p = '\177'; else { @@ -794,7 +794,7 @@ clnicezputs(int do_colors, char *s, int ml) */ for (t = sptr; *t; t++) { /* Input is metafied... */ - int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t); + int nc = (*t == Meta) ? (unsigned char) (*++t ^ 32) : (unsigned char) *t; /* Is the screen full? */ if (ml == mlend - 1 && col == zterm_columns - 1) { mlprinted = ml - oml; @@ -852,7 +852,7 @@ clnicezputs(int do_colors, char *s, int ml) cc = *s++ ^ 32; for (t = nicechar(cc); *t; t++) { - int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t); + int nc = (*t == Meta) ? (unsigned char) (*++t ^ 32) : (unsigned char) *t; if (ml == mlend - 1 && col == zterm_columns - 1) { mlprinted = ml - oml; return 0; @@ -1072,7 +1072,7 @@ static int compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) { char *p, nc[2*DIGBUFSIZE + 12], nbuf[2*DIGBUFSIZE + 12]; - int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg, stat; + int l = 0, cc = 0, m, ask, beg, stat; if ((stat = !fmt)) { if (mlbeg >= 0) { @@ -1118,48 +1118,46 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) m = 0; switch (cchar) { case ZWC('%'): - if (dopr == 1) + if (dopr == 1) { + applytextattributes(0); putc('%', shout); + } cc++; break; case ZWC('n'): if (!stat) { sprintf(nc, "%d", n); - if (dopr == 1) + if (dopr == 1) { + applytextattributes(0); fputs(nc, shout); + } /* everything here is ASCII... */ cc += strlen(nc); } break; case ZWC('B'): - b = 1; if (dopr) - tcout(TCBOLDFACEBEG); + tsetattrs(TXTBOLDFACE); break; case ZWC('b'): - b = 0; m = 1; if (dopr) - tcout(TCALLATTRSOFF); + tunsetattrs(TXTBOLDFACE); break; case ZWC('S'): - s = 1; if (dopr) - tcout(TCSTANDOUTBEG); + tsetattrs(TXTSTANDOUT); break; case ZWC('s'): - s = 0; m = 1; if (dopr) - tcout(TCSTANDOUTEND); + tunsetattrs(TXTSTANDOUT); break; case ZWC('U'): - u = 1; if (dopr) - tcout(TCUNDERLINEBEG); + tsetattrs(TXTUNDERLINE); break; case ZWC('u'): - u = 0; m = 1; if (dopr) - tcout(TCUNDERLINEEND); + tunsetattrs(TXTUNDERLINE); break; case ZWC('F'): case ZWC('K'): @@ -1173,20 +1171,28 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) } else atr = match_colour(NULL, is_fg, arg); if (atr != TXT_ERROR && dopr) - set_colour_attribute(atr, is_fg ? COL_SEQ_FG : - COL_SEQ_BG, 0); + tsetattrs(atr); break; case ZWC('f'): if (dopr) - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0); + tunsetattrs(TXTFGCOLOUR); break; case ZWC('k'): if (dopr) - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0); + tunsetattrs(TXTBGCOLOUR); + break; + case ZWC('H'): + if (*p == '{') { + p = parsehighlight(p + 1, '}', &atr); + if (atr != TXT_ERROR && dopr) + treplaceattrs(atr); + } break; case ZWC('{'): if (arg) cc += arg; + if (dopr) + applytextattributes(0); for (; *p && (*p != '%' || p[1] != '}'); p++) if (dopr) putc(*p == Meta ? *++p ^ 32 : *p, shout); @@ -1197,7 +1203,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) if (stat) { sprintf(nc, "%d/%d", (n ? mlastm : mselect), listdat.nlist); - m = 2; + m = 1; } break; case ZWC('M'): @@ -1205,20 +1211,20 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) sprintf(nbuf, "%d/%d", (n ? mlastm : mselect), listdat.nlist); sprintf(nc, "%-9s", nbuf); - m = 2; + m = 1; } break; case ZWC('l'): if (stat) { sprintf(nc, "%d/%d", ml + 1, listdat.nlines); - m = 2; + m = 1; } break; case ZWC('L'): if (stat) { sprintf(nbuf, "%d/%d", ml + 1, listdat.nlines); sprintf(nc, "%-9s", nbuf); - m = 2; + m = 1; } break; case ZWC('p'): @@ -1230,7 +1236,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) ((ml + 1) * 100) / listdat.nlines); else strcpy(nc, "Top"); - m = 2; + m = 1; } break; case ZWC('P'): @@ -1242,25 +1248,19 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) ((ml + 1) * 100) / listdat.nlines); else strcpy(nc, "Top "); - m = 2; + m = 1; } break; } - if (m == 2 && dopr == 1) { + if (m && dopr) { /* nc only contains ASCII text */ int l = strlen(nc); if (l + cc > zterm_columns - 2) nc[l -= l + cc - (zterm_columns - 2)] = '\0'; + applytextattributes(0); fputs(nc, shout); cc += l; - } else if (dopr && m == 1) { - if (b) - tcout(TCBOLDFACEBEG); - if (s) - tcout(TCSTANDOUTBEG); - if (u) - tcout(TCUNDERLINEBEG); } } else break; @@ -1276,6 +1276,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) cc = 0; } if (dopr == 1) { + applytextattributes(0); if (ml == mlend - 1 && (cc % zterm_columns) == zterm_columns - 1) { dopr = 0; @@ -1311,6 +1312,8 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) } } if (dopr) { + treplaceattrs(0); + applytextattributes(0); if (!(cc % zterm_columns)) fputs(" \010", shout); cleareol(); diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index 56e5509a4..ddcecd589 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -2045,12 +2045,12 @@ join_strs(int la, char *sa, int lb, char *sb) zlelineasstring(line, mp->llen, 0, &convlen, NULL, 0); if (rr <= convlen) { - char *or = rs; + ptrdiff_t diff = rp - rs; int alloclen = (convlen > 20) ? convlen : 20; rs = realloc(rs, (rl += alloclen)); rr += alloclen; - rp += rs - or; + rp = rs + diff; } memcpy(rp, convstr, convlen); rp += convlen; @@ -2073,11 +2073,11 @@ join_strs(int la, char *sa, int lb, char *sb) } else { /* Same character, just take it. */ if (rr <= 1 /* HERE charlen */) { - char *or = rs; + ptrdiff_t diff = rp - rs; rs = realloc(rs, (rl += 20)); rr += 20; - rp += rs - or; + rp = rs + diff; } /* HERE: multibyte char */ *rp++ = *sa; diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 57789c0f3..cd8c7dd64 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -897,7 +897,7 @@ void do_allmatches(UNUSED(int end)) { int first = 1, nm = nmatches - 1, omc = menucmp, oma = menuacc, e; - Cmatch *mc; + Cmatch *mc = 0; struct menuinfo mi; char *p = (brbeg ? ztrdup(lastbrbeg->str) : NULL); @@ -915,10 +915,10 @@ do_allmatches(UNUSED(int end)) #endif } + if (minfo.group) + mc = (minfo.group)->matches; - mc = (minfo.group)->matches; - - while (1) { + while (mc) { if (!((*mc)->flags & CMF_ALL)) { if (!first) accept_last(); @@ -1731,8 +1731,6 @@ calclist(int showall) width < zterm_columns && nth < g->dcount; nth++, tcol++) { - m = *p; - if (tcol == tcols) { tcol = 0; tlines++; @@ -1994,7 +1992,6 @@ printlist(int over, CLPrintFunc printm, int showall) (listdat.onlyexpl & ((*e)->always > 0 ? 2 : 1)))) { if (pnl) { putc('\n', shout); - pnl = 0; ml++; if (cl >= 0 && --cl <= 1) { cl = -1; @@ -2087,7 +2084,6 @@ printlist(int over, CLPrintFunc printm, int showall) (showall || !(m->flags & (CMF_HIDE|CMF_NOLIST)))) { if (pnl) { putc('\n', shout); - pnl = 0; ml++; if (cl >= 0 && --cl <= 1) { cl = -1; diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 77ccdebf7..360667884 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -911,7 +911,7 @@ struct cadef { Caarg rest; /* the rest-argument */ char **defs; /* the original strings */ int ndefs; /* number of ... */ - int lastt; /* last time this was used */ + time_t lastt; /* last time this was used */ Caopt *single; /* array of single-letter options */ char *match; /* -M spec to use */ int argsactive; /* if normal arguments are still allowed */ @@ -2935,7 +2935,7 @@ struct cvdef { Cvval vals; /* value definitions */ char **defs; /* original strings */ int ndefs; /* number of ... */ - int lastt; /* last time used */ + time_t lastt; /* last time used */ int words; /* if to look at other words */ }; diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c index c93777b65..a68c5296e 100644 --- a/Src/Zle/textobjects.c +++ b/Src/Zle/textobjects.c @@ -283,9 +283,9 @@ selectargument(UNUSED(char **args)) free(linein); if (IS_THINGY(bindk, selectinshellword)) { - ZLE_CHAR_T *match = ZWS("`\'\""); - ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}"); - ZLE_CHAR_T *ematch = match, *found; + const ZLE_CHAR_T *match = ZWS("`\'\""); + const ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}"); + const ZLE_CHAR_T *ematch = match, *found; int start, end = zlecs; /* for 'in' widget, don't include initial blanks ... */ while (mark < zlecs && ZC_iblank(zleline[mark])) diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 391586c4a..5bb9e7a5e 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -186,7 +186,7 @@ typedef struct thingy *Thingy; /* widgets (ZLE functions) */ -typedef int (*ZleIntFunc) _((char **)); +typedef int (*ZleIntFunc) (char **); struct widget { int flags; /* flags (see below) */ @@ -258,6 +258,9 @@ struct modifier { #define MOD_NULL (1<<5) /* throw away text for the vi cut buffer */ #define MOD_CHAR (1<<6) /* force character-wise movement */ #define MOD_LINE (1<<7) /* force line-wise movement */ +#define MOD_PRI (1<<8) /* OS primary selection for the vi cut buffer */ +#define MOD_CLIP (1<<9) /* OS clipboard for the vi cut buffer */ +#define MOD_OSSEL (MOD_PRI | MOD_CLIP) /* either system selection */ /* current modifier status */ @@ -316,7 +319,7 @@ struct vichange { typedef struct keymap *Keymap; -typedef void (*KeyScanFunc) _((char *, Thingy, char *, void *)); +typedef void (*KeyScanFunc) (char *, Thingy, char *, void *); #define invicmdmode() (!strcmp(curkeymapname, "vicmd")) @@ -432,6 +435,8 @@ enum { struct region_highlight { /* Attributes turned on in the region */ zattr atr; + /* Priority for this region relative to others that overlap */ + int layer; /* Start of the region */ int start; /* Start of the region in metafied ZLE line */ @@ -487,11 +492,7 @@ typedef struct { */ REFRESH_CHAR chr; /* - * Its attributes. 'On' attributes (TXT_ATTR_ON_MASK) are - * applied before the character, 'off' attributes (TXT_ATTR_OFF_MASK) - * after it. 'On' attributes are present for all characters that - * need the effect; 'off' attributes are only present for the - * last character in the sequence. + * Its attributes. */ zattr atr; } REFRESH_ELEMENT; @@ -523,7 +524,7 @@ typedef REFRESH_ELEMENT *REFRESH_STRING; ((int)((unsigned)(x) - ZSH_INVALID_WCHAR_BASE)) /* Turn a single byte character into a private wide character */ #define ZSH_CHAR_TO_INVALID_WCHAR(x) \ - ((wchar_t)(STOUC(x) + ZSH_INVALID_WCHAR_BASE)) + ((wchar_t)((unsigned char) x + ZSH_INVALID_WCHAR_BASE)) #endif diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index cfaa70dae..0fdad70d9 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -68,6 +68,13 @@ Keymap isearch_keymap; */ #define GETZLETEXT(ent) ((ent)->zle_text ? (ent)->zle_text : (ent)->node.nam) +/* + * Flag that edits have been made to a zle line. + * If not set, nothing to forget. + */ +/**/ +int have_edits = 0; + /**/ void remember_edits(void) @@ -81,6 +88,7 @@ remember_edits(void) if (ent->zle_text) free(ent->zle_text); ent->zle_text = zlemetaline ? ztrdup(line) : line; + have_edits = 1; } else if (!zlemetaline) free(line); } @@ -90,6 +98,10 @@ remember_edits(void) void forget_edits(void) { + if (!have_edits) { + return; + } + have_edits = 0; Histent he; for (he = hist_ring; he; he = up_histent(he)) { diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index d90838f03..5012917f5 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -404,7 +404,7 @@ static void scankeys(HashNode hn, UNUSED(int flags)) { Key k = (Key) hn; - int f = k->nam[0] == Meta ? STOUC(k->nam[1])^32 : STOUC(k->nam[0]); + int f = k->nam[0] == Meta ? (unsigned char) k->nam[1]^32 : (unsigned char) k->nam[0]; char m[3]; while(skm_last < f) { @@ -566,7 +566,7 @@ mod_export int bindkey(Keymap km, const char *seq, Thingy bind, char *str) { Key k; - int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]); + int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0]; char *buf, *ptr; if(km->flags & KM_IMMUTABLE) @@ -661,7 +661,7 @@ keybind(Keymap km, char *seq, char **strp) Key k; if(ztrlen(seq) == 1) { - int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]); + int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0]; Thingy bind = km->first[f]; if(bind) @@ -687,7 +687,7 @@ keyisprefix(Keymap km, char *seq) if(!*seq) return 1; if(ztrlen(seq) == 1) { - int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]); + int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0]; if(km->first[f]) return 0; @@ -745,7 +745,7 @@ bin_bindkey(char *name, char **argv, Options ops, UNUSED(int func)) static struct opn { char o; char selp; - int (*func) _((char *, char *, Keymap, char **, Options, char)); + int (*func) (char *, char *, Keymap, char **, Options, char); int min, max; } const opns[] = { { 'l', 0, bin_bindkey_lsmaps, 0, -1 }, @@ -764,10 +764,10 @@ bin_bindkey(char *name, char **argv, Options ops, UNUSED(int func)) int n; /* select operation and ensure no clashing arguments */ - for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ; + for(op = opns; op->o && !OPT_ISSET(ops,(unsigned char) op->o); op++) ; if(op->o) for(opp = op; (++opp)->o; ) - if(OPT_ISSET(ops,STOUC(opp->o))) { + if(OPT_ISSET(ops,(unsigned char) opp->o)) { zwarnnam(name, "incompatible operation selection options"); return 1; } @@ -1049,7 +1049,7 @@ bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, Options ops, char m[3]; if(len < 2 || len > 2 + (bseq[1] == '-') || - (first = STOUC(bseq[0])) > (last = STOUC(bseq[len - 1]))) { + (first = (unsigned char) bseq[0]) > (last = (unsigned char) bseq[len - 1])) { zwarnnam(name, "malformed key range `%s'", useq); ret = 1; } else { @@ -1149,8 +1149,8 @@ scanbindlist(char *seq, Thingy bind, char *str, void *magic) if(bind == bs->bind && (bind || !strcmp(str, bs->str)) && ztrlen(seq) == 1 && ztrlen(bs->lastseq) == 1) { int l = bs->lastseq[1] ? - STOUC(bs->lastseq[1]) ^ 32 : STOUC(bs->lastseq[0]); - int t = seq[1] ? STOUC(seq[1]) ^ 32 : STOUC(seq[0]); + (unsigned char) bs->lastseq[1] ^ 32 : (unsigned char) bs->lastseq[0]; + int t = seq[1] ? (unsigned char) seq[1] ^ 32 : (unsigned char) seq[0]; if(t == l + 1) { zsfree(bs->lastseq); @@ -1315,7 +1315,7 @@ default_bindings(void) Keymap vismap = newkeymap(NULL, "visual"); Keymap smap = newkeymap(NULL, ".safe"); Keymap vimaps[2], vilmaps[2], kptr; - char buf[3], *ed; + char buf[3]; int i; /* vi insert mode and emacs mode: * @@ -1445,20 +1445,14 @@ default_bindings(void) } /* Put the keymaps in the right namespace. The "main" keymap * - * will be linked to the "emacs" keymap, except that if VISUAL * - * or EDITOR contain the string "vi" then it will be linked to * - * the "viins" keymap. */ + * will be linked to the "emacs" keymap. */ linkkeymap(vmap, "viins", 0); linkkeymap(emap, "emacs", 0); linkkeymap(amap, "vicmd", 0); linkkeymap(oppmap, "viopp", 0); linkkeymap(vismap, "visual", 0); linkkeymap(smap, ".safe", 1); - if (((ed = zgetenv("VISUAL")) && strstr(ed, "vi")) || - ((ed = zgetenv("EDITOR")) && strstr(ed, "vi"))) - linkkeymap(vmap, "main", 0); - else - linkkeymap(emap, "main", 0); + linkkeymap(emap, "main", 0); /* the .safe map cannot be modified or deleted */ smap->flags |= KM_IMMUTABLE; @@ -1526,10 +1520,10 @@ getrestchar_keybuf(void) */ while (1) { if (bufind < buflen) { - c = STOUC(keybuf[bufind++]); + c = (unsigned char) keybuf[bufind++]; if (c == Meta) { DPUTS(bufind == buflen, "Meta at end of keybuf"); - c = STOUC(keybuf[bufind++]) ^ 32; + c = (unsigned char) keybuf[bufind++] ^ 32; } } else { /* @@ -1586,7 +1580,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) Thingy func = t_undefinedkey; char *str = NULL; int lastlen = 0, lastc = lastchar; - int timeout = 0; + int timeout = 0, csi = 0; keybuflen = 0; keybuf[0] = 0; @@ -1636,7 +1630,31 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) } #endif } - if (!ispfx) + + /* CSI key sequences have a well defined structure so if we currently + * have an incomplete one, loop so the rest of it will be included in + * the key sequence if that arrives within the timeout. */ + if (!csi && keybuflen >= 3 && keybuf[keybuflen - 3] == '\033' && + keybuf[keybuflen - 2] == '[') + csi = keybuflen - 1; + if (csi) { + if (keybuf[keybuflen - 2] == Meta || keybuf[keybuflen - 1] < 0x20 + || keybuf[keybuflen - 1] > 0x3f) { + /* If we reach the end of a valid CSI sequence and the matched key + * binding is for part of the CSI introduction, select instead the + * undefined-key widget and consume the full sequence from the + * input buffer. */ + if (keybuf[keybuflen - 1] >= 0x40 && + keybuf[keybuflen - 1] <= 0x7e && lastlen > csi - 2 && + lastlen <= csi) { + func = t_undefinedkey; + lastlen = keybuflen; + } + csi = 0; + } + } + + if (!ispfx && !csi) break; } if(!lastlen && keybuflen) diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 9edf30e01..1afb1bf58 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -737,6 +737,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full) ) { /* Handle the fd. */ char *fdbuf; + Thingy save_lbindk = refthingy(lbindk); { char buf[BDIGBUFSIZE]; convbase(buf, lwatch_fd->fd, 10); @@ -779,6 +780,8 @@ raw_getbyte(long do_keytmout, char *cptr, int full) */ errtry = 1; } + unrefthingy(lbindk); + lbindk = save_lbindk; } } /* Function may have invalidated the display. */ @@ -876,7 +879,7 @@ getbyte(long do_keytmout, int *timeout, int full) #endif if (kungetct) - ret = STOUC(kungetbuf[--kungetct]); + ret = (unsigned char) kungetbuf[--kungetct]; else { for (;;) { int q = queue_signal_level(); @@ -940,7 +943,7 @@ getbyte(long do_keytmout, int *timeout, int full) else if (cc == '\n') cc = '\r'; - ret = STOUC(cc); + ret = (unsigned char) cc; } /* * curvichg.buf is raw bytes, not wide characters, so is dealt @@ -1230,9 +1233,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) char *pptbuf; int pptlen; - pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL, - &pmpt_attr), + pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL), &pptlen); + pmpt_attr = txtcurrentattrs; write_loop(2, pptbuf, pptlen); free(pptbuf); return shingetline(); @@ -1267,10 +1270,13 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) fetchttyinfo = 0; trashedzle = 0; raw_lp = lp; - lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL, &pmpt_attr); + txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0; + lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL); + pmpt_attr = txtcurrentattrs; raw_rp = rp; - rpmpt_attr = pmpt_attr; - rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL, &rpmpt_attr); + rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL); + rpmpt_attr = txtcurrentattrs; + prompt_attr = mixattrs(pmpt_attr, rpmpt_attr); free_prepostdisplay(); zlereadflags = flags; @@ -2009,17 +2015,18 @@ reexpandprompt(void) char *new_lprompt, *new_rprompt; looping = reexpanding; - new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL, - &pmpt_attr); + txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0; + new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL); + pmpt_attr = txtcurrentattrs; free(lpromptbuf); lpromptbuf = new_lprompt; if (looping != reexpanding) continue; - rpmpt_attr = pmpt_attr; - new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL, - &rpmpt_attr); + new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL); + rpmpt_attr = txtcurrentattrs; + prompt_attr = mixattrs(pmpt_attr, rpmpt_attr); free(rpromptbuf); rpromptbuf = new_rprompt; } while (looping != reexpanding); @@ -2065,6 +2072,8 @@ trashzle(void) trashedzle = 1; zrefresh(); showinglist = sl; + treplaceattrs(prompt_attr); + applytextattributes(0); moveto(nlnct, 0); if (clearflag && tccan(TCCLEAREOD)) { tcout(TCCLEAREOD); diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 30b5d4447..f076bdd61 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -36,8 +36,8 @@ * non-zero width followed by an arbitrary (but typically small) * number of characters that have zero width (combining characters). * - * The allocated size for each array is given by ?mw_size; nmw_ind - * is the next free element, i.e. nmwbuf[nmw_ind] will be the next + * The allocated size for each array is given by omw_size and nmw_size; + * nmw_ind is the next free element, i.e. nmwbuf[nmw_ind] will be the next * element to be written (we never insert into omwbuf). We initialise * nmw_ind to 1 to avoid the index stored in the character looking like a * NULL. This wastees a word but it's safer than messing with pointers. @@ -149,7 +149,7 @@ char *lpromptbuf, *rpromptbuf; /* Text attributes after displaying prompts */ /**/ -zattr pmpt_attr, rpmpt_attr; +zattr pmpt_attr, rpmpt_attr, prompt_attr; /* number of lines displayed */ @@ -203,12 +203,18 @@ int predisplaylen, postdisplaylen; /* - * Attributes used by default on the command line, and - * attributes for highlighting special (unprintable) characters - * displayed on screen. + * Attributes used by default on the command line, + * for highlighting special (unprintable) characters displayed on screen, + * and for ellipsis continuation markers. */ -static zattr default_atr_on, special_atr_on; +static zattr default_attr, special_attr, ellipsis_attr; + +/* + * Layer applied to highlighting for special characters + */ + +static int special_layer; /* * Array of region highlights, no special termination. @@ -245,20 +251,20 @@ char *tcout_func_name; int cost; # define SELECT_ADD_COST(X) (cost += X) -# define zputc(a) (zwcputc(a, NULL), cost++) +# define zputc(a) (zwcputc(a), cost++) # define zwrite(a, b) (zwcwrite((a), (b)), \ cost += ((b) * ZLE_CHAR_SIZE)) #else # define SELECT_ADD_COST(X) -# define zputc(a) zwcputc(a, NULL) +# define zputc(a) zwcputc(a) # define zwrite(a, b) zwcwrite((a), (b)) #endif -static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), 0 }; +static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), TXT_ERROR }; #ifdef MULTIBYTE_SUPPORT -static const REFRESH_ELEMENT zr_dt = { ZWC('.'), 0 }; +static const REFRESH_ELEMENT zr_dt = { ZWC('.'), TXT_ERROR }; #endif -static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), 0 }; +static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), TXT_ERROR }; static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 }; static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 }; @@ -269,10 +275,10 @@ static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 }; static const REFRESH_ELEMENT zr_end_ellipsis[] = { { ZWC(' '), 0 }, { ZWC('<'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, { ZWC(' '), 0 }, }; #define ZR_END_ELLIPSIS_SIZE \ @@ -281,16 +287,16 @@ static const REFRESH_ELEMENT zr_end_ellipsis[] = { static const REFRESH_ELEMENT zr_mid_ellipsis1[] = { { ZWC(' '), 0 }, { ZWC('<'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, }; #define ZR_MID_ELLIPSIS1_SIZE \ ((int)(sizeof(zr_mid_ellipsis1)/sizeof(zr_mid_ellipsis1[0]))) static const REFRESH_ELEMENT zr_mid_ellipsis2[] = { - { ZWC('>'), 0 }, + { ZWC('>'), TXT_ERROR }, { ZWC(' '), 0 }, }; #define ZR_MID_ELLIPSIS2_SIZE \ @@ -298,10 +304,10 @@ static const REFRESH_ELEMENT zr_mid_ellipsis2[] = { static const REFRESH_ELEMENT zr_start_ellipsis[] = { { ZWC('>'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, }; #define ZR_START_ELLIPSIS_SIZE \ ((int)(sizeof(zr_start_ellipsis)/sizeof(zr_start_ellipsis[0]))) @@ -316,14 +322,15 @@ static void zle_set_highlight(void) { char **atrs = getaparam("zle_highlight"); - int special_atr_on_set = 0; - int region_atr_on_set = 0; - int isearch_atr_on_set = 0; - int suffix_atr_on_set = 0; - int paste_atr_on_set = 0; + int special_attr_set = 0; + int region_attr_set = 0; + int isearch_attr_set = 0; + int suffix_attr_set = 0; + int paste_attr_set = 0; + int ellipsis_attr_set = 0; struct region_highlight *rhp; - special_atr_on = default_atr_on = 0; + special_attr = default_attr = 0; if (!region_highlights) { region_highlights = (struct region_highlight *) zshcalloc(N_SPECIAL_HIGHLIGHTS*sizeof(struct region_highlight)); @@ -336,46 +343,63 @@ zle_set_highlight(void) } } + /* Default layers */ + region_highlights[0].layer = 20; /* region */ + region_highlights[1].layer = 20; /* isearch */ + region_highlights[2].layer = 10; /* suffix */ + region_highlights[3].layer = 15; /* paste */ + special_layer = 30; + if (atrs) { for (; *atrs; atrs++) { if (!strcmp(*atrs, "none")) { /* reset attributes for consistency... usually unnecessary */ - special_atr_on = default_atr_on = 0; - special_atr_on_set = 1; - paste_atr_on_set = region_atr_on_set = - isearch_atr_on_set = suffix_atr_on_set = 1; + special_attr = default_attr = 0; + special_attr_set = 1; + paste_attr_set = region_attr_set = + isearch_attr_set = suffix_attr_set = 1; } else if (strpfx("default:", *atrs)) { - match_highlight(*atrs + 8, &default_atr_on); + match_highlight(*atrs + 8, &default_attr, NULL); } else if (strpfx("special:", *atrs)) { - match_highlight(*atrs + 8, &special_atr_on); - special_atr_on_set = 1; + match_highlight(*atrs + 8, &special_attr, &special_layer); + special_attr_set = 1; } else if (strpfx("region:", *atrs)) { - match_highlight(*atrs + 7, ®ion_highlights[0].atr); - region_atr_on_set = 1; + match_highlight(*atrs + 7, &(region_highlights[0].atr), + &(region_highlights[0].layer)); + region_attr_set = 1; } else if (strpfx("isearch:", *atrs)) { - match_highlight(*atrs + 8, &(region_highlights[1].atr)); - isearch_atr_on_set = 1; + match_highlight(*atrs + 8, &(region_highlights[1].atr), + &(region_highlights[1].layer)); + isearch_attr_set = 1; } else if (strpfx("suffix:", *atrs)) { - match_highlight(*atrs + 7, &(region_highlights[2].atr)); - suffix_atr_on_set = 1; + match_highlight(*atrs + 7, &(region_highlights[2].atr), + &(region_highlights[2].layer)); + suffix_attr_set = 1; } else if (strpfx("paste:", *atrs)) { - match_highlight(*atrs + 6, &(region_highlights[3].atr)); - paste_atr_on_set = 1; + match_highlight(*atrs + 6, &(region_highlights[3].atr), + &(region_highlights[3].layer)); + paste_attr_set = 1; + } else if (strpfx("ellipsis:", *atrs)) { + match_highlight(*atrs + 9, &ellipsis_attr, NULL); + ellipsis_attr_set = 1; } } } - /* Defaults */ - if (!special_atr_on_set) - special_atr_on = TXTSTANDOUT; - if (!region_atr_on_set) + /* Default attributes */ + if (!special_attr_set) + special_attr = TXTSTANDOUT; + if (!region_attr_set) region_highlights[0].atr = TXTSTANDOUT; - if (!isearch_atr_on_set) + if (!isearch_attr_set) region_highlights[1].atr = TXTUNDERLINE; - if (!suffix_atr_on_set) + if (!suffix_attr_set) region_highlights[2].atr = TXTBOLDFACE; - if (!paste_atr_on_set) + if (!paste_attr_set) region_highlights[3].atr = TXTSTANDOUT; + if (!ellipsis_attr_set) + ellipsis_attr = TXTBGCOLOUR | ((zattr) 3 << TXT_ATTR_BG_COL_SHIFT) | + TXTFGCOLOUR | ((zattr) 4 << TXT_ATTR_FG_COL_SHIFT); allocate_colour_buffer(); } @@ -400,14 +424,13 @@ zle_free_highlight(void) char ** get_region_highlight(UNUSED(Param pm)) { - int arrsize = n_region_highlights; + int arrsize = n_region_highlights - N_SPECIAL_HIGHLIGHTS; char **retarr, **arrp; struct region_highlight *rhp; /* region_highlights may not have been set yet */ - if (!arrsize) + if (!n_region_highlights) return hmkarray(NULL); - arrsize -= N_SPECIAL_HIGHLIGHTS; DPUTS(arrsize < 0, "arrsize is negative from n_region_highlights"); arrp = retarr = (char **)zhalloc((arrsize+1)*sizeof(char *)); @@ -415,20 +438,18 @@ get_region_highlight(UNUSED(Param pm)) for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS; arrsize--; rhp++, arrp++) { - char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE]; - int atrlen, alloclen; - const char memo_equals[] = "memo="; - - sprintf(digbuf1, "%d", rhp->start); - sprintf(digbuf2, "%d", rhp->end); - - atrlen = output_highlight(rhp->atr, NULL); - alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) + - 3; /* 2 spaces, 1 terminating NUL */ + char digbuf[2 * DIGBUFSIZE], layerbuf[7 + DIGBUFSIZE]; + int offset; + const char memo_equals[] = " memo="; + int alloclen = sprintf(digbuf, "%d %d", rhp->start, rhp->end) + + output_highlight(rhp->atr, NULL) + + 2; /* space and terminating NUL */ if (rhp->flags & ZRH_PREDISPLAY) - alloclen += 2; /* "P " */ + alloclen++; /* "P" */ + if (rhp->layer != 10) + alloclen += sprintf(layerbuf, ",layer=%d", rhp->layer); if (rhp->memo) - alloclen += 1 /* space */ + strlen(memo_equals) + strlen(rhp->memo); + alloclen += sizeof(memo_equals) - 1 + strlen(rhp->memo); *arrp = (char *)zhalloc(alloclen * sizeof(char)); /* * On input we allow a space after the flags. @@ -437,13 +458,14 @@ get_region_highlight(UNUSED(Param pm)) * into three words, and then check the first to * see if there are flags. However, it's arguable. */ - sprintf(*arrp, "%s%s %s ", + offset = sprintf(*arrp, "%s%s ", (rhp->flags & ZRH_PREDISPLAY) ? "P" : "", - digbuf1, digbuf2); - (void)output_highlight(rhp->atr, *arrp + strlen(*arrp)); + digbuf); + (void)output_highlight(rhp->atr, *arrp + offset); + if (rhp->layer != 10) + strcat(*arrp, layerbuf); if (rhp->memo) { - strcat(*arrp, " "); strcat(*arrp, memo_equals); strcat(*arrp, rhp->memo); } @@ -452,12 +474,10 @@ get_region_highlight(UNUSED(Param pm)) return retarr; } - /* * The parameter system requires the pm argument, but this * may be NULL if called directly. */ - /**/ void set_region_highlight(UNUSED(Param pm), char **aval) @@ -516,7 +536,8 @@ set_region_highlight(UNUSED(Param pm), char **aval) while (inblank(*strp)) strp++; - strp = (char*) match_highlight(strp, &rhp->atr); + rhp->layer = 10; /* default */ + strp = (char*) match_highlight(strp, &rhp->atr, &rhp->layer); while (inblank(*strp)) strp++; @@ -571,22 +592,6 @@ unset_region_highlight(Param pm, int exp) } -/* The last attributes that were on. */ -static zattr lastatr; - -/* - * Clear the last attributes that we set: used when we're going - * to be outputting stuff that shouldn't show up as text. - */ -static void -clearattributes(void) -{ - if (lastatr) { - settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr)); - lastatr = 0; - } -} - /* * Output a termcap capability, clearing any text attributes so * as not to mess up the display. @@ -595,7 +600,7 @@ clearattributes(void) static void tcoutclear(int cap) { - clearattributes(); + cleartextattributes(0); tcout(cap); } @@ -603,47 +608,20 @@ tcoutclear(int cap) * Output the character. This must come from the new video * buffer, nbuf, since we access the multiword buffer nmwbuf * directly. - * - * curatrp may be NULL, otherwise points to an integer specifying - * what attributes were turned on for a character output immediately - * before, in order to optimise output of attribute changes. */ /**/ void -zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp) +zwcputc(const REFRESH_ELEMENT *c) { - /* - * Safety: turn attributes off if last heard of turned on. - * This differs from *curatrp, which is an optimisation for - * writing lots of stuff at once. - */ #ifdef MULTIBYTE_SUPPORT mbstate_t mbstate; int i; VARARR(char, mbtmp, MB_CUR_MAX + 1); #endif - if (lastatr & ~c->atr) { - /* Stuff on we don't want, turn it off */ - settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr & ~c->atr)); - lastatr = 0; - } - - /* - * Don't output "on" attributes in a string of characters with - * the same attributes. Be careful in case a different colour - * needs setting. - */ - if ((c->atr & TXT_ATTR_ON_MASK) && - (!curatrp || - ((*curatrp & TXT_ATTR_ON_VALUES_MASK) != - (c->atr & TXT_ATTR_ON_VALUES_MASK)))) { - /* Record just the control flags we might need to turn off... */ - lastatr = c->atr & TXT_ATTR_ON_MASK; - /* ...but set including the values for colour attributes */ - settextattributes(c->atr & TXT_ATTR_ON_VALUES_MASK); - } + treplaceattrs(c->atr); + applytextattributes(0); #ifdef MULTIBYTE_SUPPORT if (c->atr & TXT_MULTIWORD_MASK) { @@ -664,35 +642,15 @@ zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp) #else fputc(c->chr, shout); #endif - - /* - * Always output "off" attributes since we only turn off at - * the end of a chunk of highlighted text. - */ - if (c->atr & TXT_ATTR_OFF_MASK) { - settextattributes(c->atr & TXT_ATTR_OFF_MASK); - lastatr &= ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT); - } - if (curatrp) { - /* - * Remember the current attributes: those that are turned - * on, less those that are turned off again. Include - * colour attributes here in case the colour changes to - * another non-default one. - */ - *curatrp = (c->atr & TXT_ATTR_ON_VALUES_MASK) & - ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT); - } } static int zwcwrite(const REFRESH_STRING s, size_t i) { size_t j; - zattr curatr = 0; for (j = 0; j < i; j++) - zwcputc(s + j, &curatr); + zwcputc(s + j); return i; /* TODO something better for error indication */ } @@ -939,29 +897,6 @@ snextline(Rparams rpms) rpms->sen = rpms->s + winw; } - -/**/ -static void -settextattributes(zattr atr) -{ - if (txtchangeisset(atr, TXTNOBOLDFACE)) - tsetcap(TCALLATTRSOFF, 0); - if (txtchangeisset(atr, TXTNOSTANDOUT)) - tsetcap(TCSTANDOUTEND, 0); - if (txtchangeisset(atr, TXTNOUNDERLINE)) - tsetcap(TCUNDERLINEEND, 0); - if (txtchangeisset(atr, TXTBOLDFACE)) - tsetcap(TCBOLDFACEBEG, 0); - if (txtchangeisset(atr, TXTSTANDOUT)) - tsetcap(TCSTANDOUTBEG, 0); - if (txtchangeisset(atr, TXTUNDERLINE)) - tsetcap(TCUNDERLINEBEG, 0); - if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR)) - set_colour_attribute(atr, COL_SEQ_FG, 0); - if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR)) - set_colour_attribute(atr, COL_SEQ_BG, 0); -} - #ifdef MULTIBYTE_SUPPORT /* * Add a multiword glyph at the screen location base. @@ -1043,7 +978,6 @@ zrefresh(void) int tmppos; /* t - tmpline */ int tmpalloced; /* flag to free tmpline when finished */ int remetafy; /* flag that zle line is metafied */ - zattr txtchange; /* attributes set after prompts */ int rprompt_off = 1; /* Offset of rprompt from right of screen */ struct rparams rpms; #ifdef MULTIBYTE_SUPPORT @@ -1192,9 +1126,7 @@ zrefresh(void) #endif /* we probably should only have explicitly set attributes */ tsetcap(TCALLATTRSOFF, 0); - tsetcap(TCSTANDOUTEND, 0); - tsetcap(TCUNDERLINEEND, 0); - txtattrmask = 0; + txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0; if (trashedzle && !clearflag) reexpandprompt(); @@ -1218,9 +1150,8 @@ zrefresh(void) zputs(lpromptbuf, shout); if (lpromptwof == winw) zputs("\n", shout); /* works with both hasam and !hasam */ - } else { - txtchange = pmpt_attr; - settextattributes(txtchange); + /* lpromptbuf includes literal escapes so we need to update for it */ + txtcurrentattrs = txtpendingattrs = pmpt_attr; } if (clearflag) { zputc(&zr_cr); @@ -1263,45 +1194,40 @@ zrefresh(void) rpms.s = nbuf[rpms.ln = 0] + lpromptw; rpms.sen = *nbuf + winw; for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) { - unsigned ireg; - zattr base_atr_on = default_atr_on, base_atr_off = 0; - zattr all_atr_on, all_atr_off; + zattr base_attr = mixattrs(default_attr, prompt_attr); + zattr all_attr; struct region_highlight *rhp; + int layer, nextlayer = 0; /* * Calculate attribute based on region. */ - for (ireg = 0, rhp = region_highlights; - ireg < n_region_highlights; - ireg++, rhp++) { - int offset; - if (rhp->flags & ZRH_PREDISPLAY) - offset = 0; /* include predisplay in start end */ - else - offset = predisplaylen; /* increment over it */ - if (rhp->start + offset <= tmppos && - tmppos < rhp->end + offset) { - if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) { - /* override colour with later entry */ - base_atr_on = (base_atr_on & ~TXT_ATTR_ON_VALUES_MASK) | - rhp->atr; - } else { - /* no colour set yet */ - base_atr_on |= rhp->atr; + do { + unsigned ireg; + layer = nextlayer; + nextlayer = special_layer; + for (ireg = 0, rhp = region_highlights; + ireg < n_region_highlights; + ireg++, rhp++) { + if (rhp->layer == layer) { + int offset; + if (rhp->flags & ZRH_PREDISPLAY) + offset = 0; /* include predisplay in start end */ + else + offset = predisplaylen; /* increment over it */ + if (rhp->start + offset <= tmppos && + tmppos < rhp->end + offset) { + base_attr = mixattrs(rhp->atr, base_attr); + if (layer > special_layer) + all_attr = mixattrs(rhp->atr, all_attr); + } + } else if (rhp->layer > layer && rhp->layer < nextlayer) { + nextlayer = rhp->layer; } - if (tmppos == rhp->end + offset - 1 || - tmppos == tmpll - 1) - base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr); } - } - if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) { - /* keep colours from special attributes */ - all_atr_on = special_atr_on | - (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK); - } else { - /* keep colours from standard attributes */ - all_atr_on = special_atr_on | base_atr_on; - } - all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on); + if (special_layer == layer) { + all_attr = mixattrs(special_attr, base_attr); + } + } while (nextlayer > layer); if (t == scs) /* if cursor is here, remember it */ rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln]; @@ -1319,10 +1245,9 @@ zrefresh(void) } else { do { rpms.s->chr = ZWC(' '); - rpms.s->atr = base_atr_on; + rpms.s->atr = base_attr; rpms.s++; } while ((++t0) & 7); - rpms.s[-1].atr |= base_atr_off; } } #ifdef MULTIBYTE_SUPPORT @@ -1341,11 +1266,9 @@ zrefresh(void) rpms.s->chr = ZWC(' '); if (!started) started = 1; - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; } while (rpms.s < rpms.sen); - if (started) - rpms.s[-1].atr |= all_atr_off; if (nextline(&rpms, 1)) break; if (t == scs) { @@ -1369,15 +1292,11 @@ zrefresh(void) * occurrence. */ rpms.s->chr = ZWC('?'); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } else { /* We can fit it without reaching the end of the line. */ - /* - * As we don't actually output the WEOF, we attach - * any off attributes to the character itself. - */ - rpms.s->atr = base_atr_on | base_atr_off; + rpms.s->atr = base_attr; if (ichars > 1) { /* * Glyph includes combining characters. @@ -1393,7 +1312,7 @@ zrefresh(void) while (--width > 0) { rpms.s->chr = WEOF; /* Not used, but be consistent... */ - rpms.s->atr = base_atr_on | base_atr_off; + rpms.s->atr = base_attr; rpms.s++; } } @@ -1410,17 +1329,16 @@ zrefresh(void) #endif ) { /* other control character */ rpms.s->chr = ZWC('^'); - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; if (rpms.s == rpms.sen) { /* text wrapped */ - rpms.s[-1].atr |= all_atr_off; if (nextline(&rpms, 1)) break; } rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ? ZWC('?') : (*t | ZWC('@')); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } #ifdef MULTIBYTE_SUPPORT @@ -1432,7 +1350,6 @@ zrefresh(void) char dispchars[11]; char *dispptr = dispchars; wchar_t wc; - int started = 0; #ifdef __STDC_ISO_10646__ if (ZSH_INVALID_WCHAR_TEST(*t)) { @@ -1449,31 +1366,23 @@ zrefresh(void) if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) { rpms.s->chr = wc; - if (!started) - started = 1; - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; if (rpms.s == rpms.sen) { /* text wrapped */ - if (started) { - rpms.s[-1].atr |= all_atr_off; - started = 0; - } if (nextline(&rpms, 1)) break; } } dispptr++; } - if (started) - rpms.s[-1].atr |= all_atr_off; if (*dispptr) /* nextline said stop processing */ break; } #else else { /* normal character */ rpms.s->chr = *t; - rpms.s->atr = base_atr_on | base_atr_off; + rpms.s->atr = base_attr; rpms.s++; } #endif @@ -1499,13 +1408,12 @@ zrefresh(void) if (statusline) { int outll, outsz; - zattr all_atr_on, all_atr_off; + zattr all_attr; char *statusdup = ztrdup(statusline); ZLE_STRING_T outputline = stringaszleline(statusdup, 0, &outll, &outsz, NULL); - all_atr_on = special_atr_on; - all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on); + all_attr = special_attr; rpms.tosln = rpms.ln + 1; nbuf[rpms.ln][winw + 1] = zr_zr; /* text not wrapped */ @@ -1525,7 +1433,7 @@ zrefresh(void) } if (width > rpms.sen - rpms.s) { rpms.s->chr = ZWC('?'); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } else { rpms.s->chr = *u; @@ -1542,7 +1450,7 @@ zrefresh(void) #endif if (ZC_icntrl(*u)) { /* simplified processing in the status line */ rpms.s->chr = ZWC('^'); - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; if (rpms.s == rpms.sen) { nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */ @@ -1550,7 +1458,7 @@ zrefresh(void) } rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31) ? ZWC('?') : (*u | ZWC('@')); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } else { rpms.s->chr = *u; @@ -1603,6 +1511,7 @@ zrefresh(void) } #endif ZR_memcpy(rpms.sen, zr_end_ellipsis, ZR_END_ELLIPSIS_SIZE); + rpms.sen[1].atr = ellipsis_attr; #ifdef MULTIBYTE_SUPPORT /* Extend to the end if we backed off for a wide character */ if (extra_ellipsis) { @@ -1638,6 +1547,7 @@ zrefresh(void) } #endif ZR_memcpy(rpms.sen, zr_mid_ellipsis1, ZR_MID_ELLIPSIS1_SIZE); + rpms.sen[1].atr = ellipsis_attr; rpms.sen += ZR_MID_ELLIPSIS1_SIZE; #ifdef MULTIBYTE_SUPPORT /* Extend if we backed off for a wide character */ @@ -1647,6 +1557,7 @@ zrefresh(void) } #endif ZR_memcpy(rpms.sen, zr_mid_ellipsis2, ZR_MID_ELLIPSIS2_SIZE); + rpms.sen[1].atr = prompt_attr; nbuf[rpms.tosln][winw] = nbuf[rpms.tosln][winw + 1] = zr_zr; } @@ -1679,7 +1590,9 @@ zrefresh(void) t0 = winw - lpromptw; t0 = t0 > ZR_START_ELLIPSIS_SIZE ? ZR_START_ELLIPSIS_SIZE : t0; ZR_memcpy(nbuf[0] + lpromptw, zr_start_ellipsis, t0); + (*nbuf + lpromptw)->atr = ellipsis_attr; ZR_memset(nbuf[0] + lpromptw + t0, zr_sp, winw - t0 - lpromptw); + (*nbuf + lpromptw + t0)->atr = prompt_attr; nbuf[0][winw] = nbuf[0][winw + 1] = zr_zr; } @@ -1725,9 +1638,9 @@ zrefresh(void) /* output the right-prompt if appropriate */ if (put_rpmpt && !iln && !oput_rpmpt) { - zattr attrchange; - moveto(0, winw - rprompt_off - rpromptw); + treplaceattrs(pmpt_attr); + applytextattributes(0); zputs(rpromptbuf, shout); if (rprompt_off) { vcs = winw - rprompt_off; @@ -1735,39 +1648,7 @@ zrefresh(void) zputc(&zr_cr); vcs = 0; } - /* reset character attributes to that set by the main prompt */ - txtchange = pmpt_attr; - /* - * Keep attributes that have actually changed, - * which are ones off in rpmpt_attr and on in - * pmpt_attr, and vice versa. - */ - attrchange = txtchange & - (TXT_ATTR_OFF_FROM_ON(rpmpt_attr) | - TXT_ATTR_ON_FROM_OFF(rpmpt_attr)); - /* - * Careful in case the colour changed. - */ - if (txtchangeisset(txtchange, TXTFGCOLOUR) && - (!txtchangeisset(rpmpt_attr, TXTFGCOLOUR) || - ((txtchange ^ rpmpt_attr) & TXT_ATTR_FG_COL_MASK))) - { - attrchange |= - txtchange & (TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK); - } - if (txtchangeisset(txtchange, TXTBGCOLOUR) && - (!txtchangeisset(rpmpt_attr, TXTBGCOLOUR) || - ((txtchange ^ rpmpt_attr) & TXT_ATTR_BG_COL_MASK))) - { - attrchange |= - txtchange & (TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK); - } - /* - * Now feed these changes into the usual function, - * if necessary. - */ - if (attrchange) - settextattributes(attrchange); + txtcurrentattrs = txtpendingattrs = rpmpt_attr; } } @@ -1780,11 +1661,6 @@ individually */ refreshline(iln); } -/* reset character attributes */ - if (clearf && postedit) { - if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr)) - settextattributes(txtchange); - } clearf = 0; oput_rpmpt = put_rpmpt; @@ -1984,8 +1860,6 @@ refreshline(int ln) /* 3: main display loop - write out the buffer using whatever tricks we can */ for (;;) { - zattr now_off; - #ifdef MULTIBYTE_SUPPORT if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) { #endif @@ -2087,7 +1961,7 @@ refreshline(int ln) * deletions, so turn off text attributes. */ if (first) { - clearattributes(); + cleartextattributes(0); first = 0; } tc_delchars(i); @@ -2176,13 +2050,8 @@ refreshline(int ln) break; do { #endif - /* - * If an attribute was on here but isn't any more, - * output the sequence to turn it off. - */ - now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK; - if (now_off) - settextattributes(TXT_ATTR_OFF_FROM_ON(now_off)); + treplaceattrs(nl->atr); + applytextattributes(0); /* * This is deliberately called if nl->chr is WEOF @@ -2560,8 +2429,8 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) for (t0 = 0; t0 < tmpll; t0++) { unsigned ireg; - zattr base_atr_on = 0, base_atr_off = 0; - zattr all_atr_on, all_atr_off; + zattr base_attr = 0; + zattr all_attr; struct region_highlight *rhp; /* * Calculate attribute based on region. @@ -2576,38 +2445,28 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) offset = predisplaylen; /* increment over it */ if (rhp->start + offset <= t0 && t0 < rhp->end + offset) { - if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) { + if (base_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) { /* keep colour already set */ - base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK; + base_attr |= rhp->atr & ~TXT_ATTR_COLOUR_MASK; } else { /* no colour set yet */ - base_atr_on |= rhp->atr; + base_attr |= rhp->atr; } - if (t0 == rhp->end + offset - 1 || - t0 == tmpll - 1) - base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr); } } - if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) { - /* keep colours from special attributes */ - all_atr_on = special_atr_on | - (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK); - } else { - /* keep colours from standard attributes */ - all_atr_on = special_atr_on | base_atr_on; - } - all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on); + all_attr = mixattrs(special_attr, base_attr); + if (t0 == tmpcs) + nvcs = vp - vbuf; if (tmpline[t0] == ZWC('\t')) { for (*vp++ = zr_sp; (vp - vbuf) & 7; ) *vp++ = zr_sp; - vp[-1].atr |= base_atr_off; } else if (tmpline[t0] == ZWC('\n')) { vp->chr = ZWC('\\'); - vp->atr = all_atr_on; + vp->atr = all_attr; vp++; vp->chr = ZWC('n'); - vp->atr = all_atr_on | all_atr_off; + vp->atr = all_attr; vp++; #ifdef MULTIBYTE_SUPPORT } else if (WC_ISPRINT(tmpline[t0]) && @@ -2623,7 +2482,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) } } else ichars = 1; - vp->atr = base_atr_on | base_atr_off; + vp->atr = base_attr; if (ichars > 1) addmultiword(vp, tmpline+t0, ichars); else @@ -2631,7 +2490,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) vp++; while (--width > 0) { vp->chr = WEOF; - vp->atr = base_atr_on | base_atr_off; + vp->atr = base_attr; vp++; } t0 += ichars - 1; @@ -2641,14 +2500,14 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) && (unsigned)tmpline[t0] <= 0xffU #endif ) { - ZLE_INT_T t = tmpline[++t0]; + ZLE_INT_T t = tmpline[t0]; vp->chr = ZWC('^'); - vp->atr = all_atr_on; + vp->atr = all_attr; vp++; vp->chr = (((unsigned int)t & ~0x80u) > 31) ? ZWC('?') : (t | ZWC('@')); - vp->atr = all_atr_on | all_atr_off; + vp->atr = all_attr; vp++; } #ifdef MULTIBYTE_SUPPORT @@ -2656,7 +2515,6 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) char dispchars[11]; char *dispptr = dispchars; wchar_t wc; - int started = 0; if ((unsigned)tmpline[t0] > 0xffffU) { sprintf(dispchars, "<%.08x>", (unsigned)tmpline[t0]); @@ -2666,25 +2524,19 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) while (*dispptr) { if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) { vp->chr = wc; - if (!started) - started = 1; - vp->atr = all_atr_on; + vp->atr = all_attr; vp++; } dispptr++; } - if (started) - vp[-1].atr |= all_atr_off; } #else else { vp->chr = tmpline[t0]; - vp->atr = base_atr_on | base_atr_off; + vp->atr = base_attr; vp++; } #endif - if (t0 == tmpcs) - nvcs = vp - vbuf - 1; } if (t0 == tmpcs) nvcs = vp - vbuf; @@ -2699,11 +2551,11 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) } if (winpos) { vbuf[winpos].chr = ZWC('<'); /* line continues to the left */ - vbuf[winpos].atr = 0; + vbuf[winpos].atr = ellipsis_attr; } if ((int)ZR_strlen(vbuf + winpos) > (winw - hasam)) { vbuf[winpos + winw - hasam - 1].chr = ZWC('>'); /* line continues to right */ - vbuf[winpos + winw - hasam - 1].atr = 0; + vbuf[winpos + winw - hasam - 1].atr = ellipsis_attr; vbuf[winpos + winw - hasam] = zr_zr; } ZR_strcpy(nbuf[0], vbuf + winpos); diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index cd3f2c356..f71435b01 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -344,7 +344,7 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func)) { static struct opn { char o; - int (*func) _((char *, char **, Options, char)); + int (*func) (char *, char **, Options, char); int min, max; } const opns[] = { { 'l', bin_zle_list, 0, -1 }, @@ -366,10 +366,10 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func)) int n; /* select operation and ensure no clashing arguments */ - for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ; + for(op = opns; op->o && !OPT_ISSET(ops, (unsigned char) op->o); op++) ; if(op->o) for(opp = op; (++opp)->o; ) - if(OPT_ISSET(ops,STOUC(opp->o))) { + if(OPT_ISSET(ops, (unsigned char) opp->o)) { zwarnnam(name, "incompatible operation selection options"); return 1; } diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index fdd168763..aa3c71bc2 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -576,7 +576,7 @@ parambeg(char *s) while (idigit(*e)) e++; else - e = itype_end(e, IIDENT, 0); + e = itype_end(e, INAMESPC, 0); /* Now make sure that the cursor is inside the name. */ if (offs <= e - s && offs >= b - s && n <= 0) { @@ -765,7 +765,7 @@ docomplete(int lst) else if (idigit(*q)) do q++; while (idigit(*q)); else - q = itype_end(q, IIDENT, 0); + q = itype_end(q, INAMESPC, 0); sav = *q; *q = '\0'; if (zlemetacs - wb == q - s && @@ -1315,6 +1315,8 @@ get_comp_string(void) ins = (tok == REPEAT ? 2 : (tok != STRING && tok != TYPESET)); zsfree(cmdstr); cmdstr = ztrdup(tokstr); + untokenize(cmdstr); + remnulargs(cmdstr); cmdtok = tok; /* * If everything before is a redirection, or anything @@ -1497,7 +1499,8 @@ get_comp_string(void) if (varq) tt = clwords[clwpos]; - s = itype_end(tt, IIDENT, 0); + /* The only place we complete namespaces, see IIDENT below */ + s = itype_end(tt, INAMESPC, 0); sav = *s; *s = '\0'; zsfree(varname); @@ -1568,6 +1571,8 @@ get_comp_string(void) i = 0; MB_METACHARINIT(); + /* All further uses of IIDENT in this file should change to * + * INAMESPC if this case is changed. Too ugly to risk now. */ if (itype_end(s, IIDENT, 1) == s) nnb = s + MB_METACHARLEN(s); else @@ -1641,7 +1646,7 @@ get_comp_string(void) } else { /* In mathematical expression, we complete parameter names * * (even if they don't have a `$' in front of them). So we * - * have to find that name. */ + * have to find that name. See above regarding INAMESPC */ char *cspos = zlemetaline + zlemetacs, *wptr, *cptr; we = itype_end(cspos, IIDENT, 0) - zlemetaline; @@ -2426,7 +2431,7 @@ mod_export int printfmt(char *fmt, int n, int dopr, int doesc) { char *p = fmt, nc[DIGBUFSIZE]; - int l = 0, cc = 0, b = 0, s = 0, u = 0, m; + int l = 0, cc = 0; MB_METACHARINIT(); for (; *p; ) { @@ -2437,48 +2442,45 @@ printfmt(char *fmt, int n, int dopr, int doesc) if (idigit(*++p)) arg = zstrtol(p, &p, 10); if (*p) { - m = 0; switch (*p) { case '%': - if (dopr) + if (dopr) { + applytextattributes(0); putc('%', shout); + } cc++; break; case 'n': sprintf(nc, "%d", n); - if (dopr) + if (dopr) { + applytextattributes(0); fputs(nc, shout); + } cc += MB_METASTRWIDTH(nc); break; case 'B': - b = 1; if (dopr) - tcout(TCBOLDFACEBEG); + tsetattrs(TXTBOLDFACE); break; case 'b': - b = 0; m = 1; if (dopr) - tcout(TCALLATTRSOFF); + tunsetattrs(TXTBOLDFACE); break; case 'S': - s = 1; if (dopr) - tcout(TCSTANDOUTBEG); + tsetattrs(TXTSTANDOUT); break; case 's': - s = 0; m = 1; if (dopr) - tcout(TCSTANDOUTEND); + tunsetattrs(TXTSTANDOUT); break; case 'U': - u = 1; if (dopr) - tcout(TCUNDERLINEBEG); + tsetattrs(TXTUNDERLINE); break; case 'u': - u = 0; m = 1; if (dopr) - tcout(TCUNDERLINEEND); + tunsetattrs(TXTUNDERLINE); break; case 'F': case 'K': @@ -2491,18 +2493,27 @@ printfmt(char *fmt, int n, int dopr, int doesc) } else atr = match_colour(NULL, is_fg, arg); if (atr != TXT_ERROR) - set_colour_attribute(atr, is_fg ? COL_SEQ_FG : - COL_SEQ_BG, 0); + tsetattrs(atr); break; case 'f': - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0); + tunsetattrs(TXTFGCOLOUR); break; case 'k': - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0); + tunsetattrs(TXTBGCOLOUR); + break; + case 'H': + if (p[1] == '{') { + p = parsehighlight(p + 2, '}', &atr); + --p; + if (atr != TXT_ERROR) + treplaceattrs(atr); + } break; case '{': if (arg) cc += arg; + if (dopr) + applytextattributes(0); for (p++; *p && (*p != '%' || p[1] != '}'); p++) { if (*p == Meta) { p++; @@ -2518,14 +2529,6 @@ printfmt(char *fmt, int n, int dopr, int doesc) p--; break; } - if (dopr && m) { - if (b) - tcout(TCBOLDFACEBEG); - if (s) - tcout(TCSTANDOUTBEG); - if (u) - tcout(TCUNDERLINEBEG); - } } else break; p++; @@ -2533,6 +2536,7 @@ printfmt(char *fmt, int n, int dopr, int doesc) if (*p == '\n') { cc++; if (dopr) { + applytextattributes(0); if (tccan(TCCLEAREOL)) tcout(TCCLEAREOL); else { @@ -2551,6 +2555,7 @@ printfmt(char *fmt, int n, int dopr, int doesc) convchar_t cchar; int clen = MB_METACHARLENCONV(p, &cchar); if (dopr) { + applytextattributes(0); while (clen--) { if (*p == Meta) { p++; @@ -2568,6 +2573,8 @@ printfmt(char *fmt, int n, int dopr, int doesc) } } if (dopr) { + treplaceattrs(0); + applytextattributes(0); if (!(cc % zterm_columns)) fputs(" \010", shout); if (tccan(TCCLEAREOL)) diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 526216fa7..e2b86e863 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -512,12 +512,13 @@ stringaszleline(char *instr, int incs, int *outll, int *outsz, int *outcs) *outcs = outptr - outstr; *outll = outptr - outstr; } else { + *outstr = ZWC('\0'); *outll = 0; if (outcs) *outcs = 0; } #else - memcpy(outstr, instr, ll); + strcpy(outstr, instr); *outll = ll; if (outcs) *outcs = incs; @@ -580,7 +581,7 @@ struct zle_region; struct zle_region { struct zle_region *next; /* Entries of region_highlight, as needed */ - int atr; + zattr atr; int start; int end; int flags; @@ -799,7 +800,7 @@ spaceinline(int ct) if (rhp->start_meta - sub >= zlemetacs) { rhp->start_meta += ct; } - if (rhp->end_meta - sub >= zlemetacs) { + if (rhp->end_meta - sub >= zlemetacs && (!predisplaylen || zlemetacs)) { rhp->end_meta += ct; } } @@ -827,7 +828,7 @@ spaceinline(int ct) if (rhp->start - sub >= zlecs) { rhp->start += ct; } - if (rhp->end - sub >= zlecs) { + if (rhp->end - sub >= zlecs && (!predisplaylen || zlecs)) { rhp->end += ct; } } @@ -866,13 +867,13 @@ shiftchars(int to, int cnt) if (rhp->start_meta - sub > to + cnt) rhp->start_meta -= cnt; else - rhp->start_meta = to; + rhp->start_meta = to + sub; } if (rhp->end_meta - sub > to) { if (rhp->end_meta - sub > to + cnt) rhp->end_meta -= cnt; else - rhp->end_meta = to; + rhp->end_meta = to + sub; } } } @@ -896,13 +897,13 @@ shiftchars(int to, int cnt) if (rhp->start - sub > to + cnt) rhp->start -= cnt; else - rhp->start = to; + rhp->start = to + sub; } if (rhp->end - sub > to) { if (rhp->end - sub > to + cnt) rhp->end -= cnt; else - rhp->end = to; + rhp->end = to + sub; } } } @@ -936,6 +937,28 @@ cut(int i, int ct, int flags) cuttext(zleline + i, ct, flags); } +static char* +base64_encode(const char *src, size_t len) { + static const char* base64_table = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + const unsigned char *end = (unsigned char *)src + len; + const unsigned char *in = (unsigned char *)src; + char *ret = zhalloc(1 + 4 * ((len + 2) / 3)); /* 4 bytes out for 3 in */ + char *cur = ret; + + for (; end - in > 0; in += 3, cur += 4) { + unsigned int n = *in << 16; + cur[3] = end - in > 2 ? base64_table[(n |= in[2]) & 0x3f] : '='; + cur[2] = end - in > 1 ? base64_table[((n |= in[1]<<8) >> 6) & 0x3f] : '='; + cur[1] = base64_table[(n >> 12) & 0x3f]; + cur[0] = base64_table[n >> 18]; + } + *cur = '\0'; + + return ret; +} + /* * As cut, but explicitly supply the text together with its length. */ @@ -948,7 +971,15 @@ cuttext(ZLE_STRING_T line, int ct, int flags) return; UNMETACHECK(); - if (zmod.flags & MOD_VIBUF) { + if (zmod.flags & MOD_OSSEL) { + int cutll; + char *mbcut = zlelineasstring(line, ct, 0, &cutll, NULL, 1); + unmetafy(mbcut, &cutll); + mbcut = base64_encode(mbcut, cutll); + + fprintf(shout, "\033]52;%c;%s\a", zmod.flags & MOD_CLIP ? 'c' : 'p', + mbcut); + } else if (zmod.flags & MOD_VIBUF) { struct cutbuffer *b = &vibuf[zmod.vibuf]; if (!(zmod.flags & MOD_VIAPP) || !b->buf) { @@ -1220,7 +1251,7 @@ getzlequery(void) REFRESH_ELEMENT re; re.chr = c; re.atr = 0; - zwcputc(&re, NULL); + zwcputc(&re); } return c == ZWC('y'); } @@ -1235,7 +1266,7 @@ bindztrdup(char *str) char *buf, *ptr, *ret; for(ptr = str; *ptr; ptr++) { - c = *ptr == Meta ? STOUC(*++ptr) ^ 32 : STOUC(*ptr); + c = *ptr == Meta ? (unsigned char) *++ptr ^ 32 : (unsigned char) *ptr; if(c & 0x80) { len += 3; c &= 0x7f; @@ -1249,7 +1280,7 @@ bindztrdup(char *str) } ptr = buf = zalloc(len); for(; *str; str++) { - c = *str == Meta ? STOUC(*++str) ^ 32 : STOUC(*str); + c = *str == Meta ? (unsigned char) *++str ^ 32 : (unsigned char) *str; if(c & 0x80) { *ptr++ = '\\'; *ptr++ = 'M'; diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c index 0f198d0e8..6692df830 100644 --- a/Src/Zle/zle_vi.c +++ b/Src/Zle/zle_vi.c @@ -1014,6 +1014,9 @@ int visetbuffer(char **args) { ZLE_INT_T ch; + const ZLE_CHAR_T *match = ZWS("_*+"); + int registermod[] = { MOD_NULL, MOD_PRI, MOD_CLIP }; + ZLE_CHAR_T *found; if (*args) { ch = **args; @@ -1022,12 +1025,12 @@ visetbuffer(char **args) } else { ch = getfullchar(0); } - if (ch == ZWC('_')) { - zmod.flags |= MOD_NULL; + if ((found = ZS_strchr(match, ch))) { + zmod.flags |= registermod[found - match]; prefixflag = 1; return 0; } else - zmod.flags &= ~MOD_NULL; + zmod.flags &= ~(MOD_NULL | MOD_OSSEL); if ((ch < ZWC('0') || ch > ZWC('9')) && (ch < ZWC('a') || ch > ZWC('z')) && (ch < ZWC('A') || ch > ZWC('Z'))) diff --git a/Src/builtin.c b/Src/builtin.c index a7b7755a7..7bfb1ce1d 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -55,7 +55,7 @@ static struct builtin builtins[] = BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL), - BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmp:%rtuxz", NULL), + BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmnp:%rtuxz", NULL), BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL), BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), @@ -88,7 +88,7 @@ static struct builtin builtins[] = BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL), BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL), BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL), - BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL), + BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lnp:%rtux", NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), #if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG) @@ -121,12 +121,12 @@ static struct builtin builtins[] = BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL), BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL), BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"), - BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmz", NULL), + BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmnz", NULL), BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL), BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL), BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"), BUILTIN("unhash", 0, bin_unhash, 1, -1, BIN_UNHASH, "adfms", NULL), - BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmv", NULL), + BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmvn", NULL), BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL), BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL), BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL), @@ -2030,6 +2030,25 @@ typeset_single(char *cname, char *pname, Param pm, int func, int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0; char *subscript; + if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF) && + (pm->level == locallevel || !(on & PM_LOCAL))) { + if ((pm = (Param)resolve_nameref(pm, NULL))) + pname = pm->node.nam; + if (pm && (pm->node.flags & PM_NAMEREF) && + (on & ~(PM_NAMEREF|PM_LOCAL|PM_READONLY))) { + /* Changing type of PM_SPECIAL|PM_AUTOLOAD is a fatal error. * + * Should this be a fatal error as well, rather than warning? */ + if (pm->width) + zwarnnam(cname, + "%s: can't change type via subscript reference", + pm->u.str); + else + zwarnnam(cname, "%s: can't change type of a named reference", + pname); + return NULL; + } + } + /* * Do we use the existing pm? Note that this isn't the end of the * story, because if we try and create a new pm at the same @@ -2040,7 +2059,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export' * flags of an unset parameter. */ - usepm = pm && (!(pm->node.flags & PM_UNSET) || + usepm = pm && (!(pm->node.flags & PM_UNSET) || OPT_ISSET(ops, 'p') || (isset(POSIXBUILTINS) && (pm->node.flags & (PM_READONLY|PM_EXPORTED)))); @@ -2088,15 +2107,27 @@ typeset_single(char *cname, char *pname, Param pm, int func, return NULL; } tc = 1; - usepm = 0; + if (OPT_MINUS(ops,'p')) + usepm = (on & pm->node.flags); + else if (OPT_PLUS(ops,'p')) + usepm = (off & pm->node.flags); + else + usepm = 0; } else if (usepm || newspecial != NS_NONE) { int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) & (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED| PM_ARRAY|PM_TIED|PM_AUTOLOAD); /* keep the parameter if just switching between floating types */ - if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT))) - usepm = 0; + if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT))) { + if (OPT_MINUS(ops,'p')) + usepm = (on & pm->node.flags); + else if (OPT_PLUS(ops,'p')) + usepm = (off & pm->node.flags); + else + usepm = 0; + } + } /* @@ -2148,20 +2179,23 @@ typeset_single(char *cname, char *pname, Param pm, int func, } if (err) { - zerrnam(cname, "%s: can't change type of a special parameter", - pname); + if (!OPT_ISSET(ops,'p')) + zerrnam(cname, + "%s: can't change type of a special parameter", + pname); return NULL; } } else if (pm->node.flags & PM_AUTOLOAD) { - zerrnam(cname, "%s: can't change type of autoloaded parameter", - pname); + if (!OPT_ISSET(ops,'p')) + zerrnam(cname, "%s: can't change type of autoloaded parameter", + pname); return NULL; } } else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0) newspecial = NS_SECONDS; - if (isset(POSIXBUILTINS)) { + if (isset(POSIXBUILTINS) && !OPT_ISSET(ops,'p')) { /* * Stricter rules about retaining readonly attribute in this case. */ @@ -2190,6 +2224,11 @@ typeset_single(char *cname, char *pname, Param pm, int func, * ii. we are creating a new local parameter */ if (usepm) { + if (OPT_MINUS(ops,'p') && on && + !((on & pm->node.flags) || ((on & PM_LOCAL) && pm->level))) + return NULL; + else if (OPT_PLUS(ops,'p') && off && !(off & pm->node.flags)) + return NULL; if ((asg->flags & ASG_ARRAY) ? !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) : (asg->value.scalar && (PM_TYPE(pm->node.flags & @@ -2199,17 +2238,27 @@ typeset_single(char *cname, char *pname, Param pm, int func, } on &= ~PM_LOCAL; if (!on && !roff && !ASG_VALUEP(asg)) { + int with_ns = OPT_ISSET(ops,'m') ? PRINT_WITH_NAMESPACE : 0; if (OPT_ISSET(ops,'p')) - paramtab->printnode(&pm->node, PRINT_TYPESET); + paramtab->printnode(&pm->node, PRINT_TYPESET|with_ns); else if (!OPT_ISSET(ops,'g') && (unset(TYPESETSILENT) || OPT_ISSET(ops,'m'))) - paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE); + paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE|with_ns); return pm; } if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { zerrnam(cname, "%s: restricted", pname); return pm; } + if ((pm->node.flags & PM_READONLY) && !(off & PM_READONLY) && + /* It seems as though these checks should not be specific to + * PM_NAMEREF, but changing that changes historic behavior */ + ((on & PM_NAMEREF) != (pm->node.flags & PM_NAMEREF) || + (asg && (pm->node.flags & PM_NAMEREF)))) { + zerrnam(cname, "%s: read-only %s", pname, + (pm->node.flags & PM_NAMEREF) ? "reference" : "variable"); + return NULL; + } if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) { Param apm; char **x; @@ -2231,6 +2280,10 @@ typeset_single(char *cname, char *pname, Param pm, int func, arrfixenv(pm->node.nam, x); } } + if (OPT_ISSET(ops,'p')) { + paramtab->printnode(&pm->node, PRINT_TYPESET|PRINT_WITH_NAMESPACE); + return pm; + } if (usepm == 2) /* do not change the PM_UNSET flag */ pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off; else { @@ -2286,10 +2339,9 @@ typeset_single(char *cname, char *pname, Param pm, int func, if (errflag) return NULL; pm->node.flags |= (on & PM_READONLY); - if (OPT_ISSET(ops,'p')) - paramtab->printnode(&pm->node, PRINT_TYPESET); return pm; - } + } else if (OPT_ISSET(ops,'p')) + return NULL; /* Nothing to print */ if ((asg->flags & ASG_ARRAY) ? !(on & (PM_ARRAY|PM_HASHED)) : @@ -2304,7 +2356,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, * or we're converting the type of a parameter. In the * last case only, we need to delete the old parameter. */ - if (tc) { + if (tc && !OPT_ISSET(ops,'p')) { /* Maintain existing readonly/exported status... */ on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->node.flags; /* ...but turn off existing readonly so we can delete it */ @@ -2406,6 +2458,11 @@ typeset_single(char *cname, char *pname, Param pm, int func, return NULL; } } else if ((subscript = strchr(pname, '['))) { + if (on & PM_NAMEREF) { + zerrnam(cname, + "%s: reference variable cannot be an array", pname); + return NULL; + } if (on & PM_READONLY) { zerrnam(cname, "%s: can't create readonly array elements", pname); @@ -2452,6 +2509,8 @@ typeset_single(char *cname, char *pname, Param pm, int func, "%s: inconsistent array element or slice assignment", pname); return NULL; } + } else if (!pm && OPT_ISSET(ops,'p')) { + return NULL; } /* * As we can hide existing parameters, we allow a name if @@ -2613,7 +2672,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) char *optstr = TYPESET_OPTSTR; int on = 0, off = 0, roff, bit = PM_ARRAY; int i; - int returnval = 0, printflags = 0; + int returnval = 0, printflags = PRINT_WITH_NAMESPACE; int hasargs = *argv != NULL || (assigns && firstnode(assigns)); /* POSIXBUILTINS is set for bash/ksh and both ignore -p with args */ @@ -2629,17 +2688,34 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) if (func == BIN_READONLY && isset(POSIXBUILTINS) && !OPT_PLUS(ops, 'g')) ops->ind['g'] = 1; +#if 0 + /* "local" rejects -m, this should too ... what about +m ? */ + if (locallevel && OPT_MINUS(ops, 'm') && + !(OPT_MINUS(ops, 'g') || OPT_ISSET(ops, 'p'))) { + zerrnam(name, "bad option: -m"); + return 1; + } +#endif + /* Translate the options into PM_* flags. * * Unfortunately, this depends on the order * * these flags are defined in zsh.h */ for (; *optstr; optstr++, bit <<= 1) { - int optval = STOUC(*optstr); + int optval = (unsigned char) *optstr; if (OPT_MINUS(ops,optval)) on |= bit; else if (OPT_PLUS(ops,optval)) off |= bit; } + if (OPT_MINUS(ops,'n')) { + if ((on|off) & ~(PM_READONLY|PM_UPPER)) { + zwarnnam(name, "no other attributes allowed with -n"); + return 1; + } + on |= PM_NAMEREF; + } else if (OPT_PLUS(ops,'n')) + off |= PM_NAMEREF; roff = off; /* Sanity checks on the options. Remove conflicting options. */ @@ -2673,7 +2749,6 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) queue_signals(); - /* Given no arguments, list whatever the options specify. */ if (OPT_ISSET(ops,'p')) { if (isset(POSIXBUILTINS) && SHELL_EMULATION() != EMULATE_KSH) { @@ -2699,8 +2774,14 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* -p0 treated as -p for consistency */ } } + + /* Given no arguments, list whatever the options specify. */ if (!hasargs) { int exclude = 0; + + if (!OPT_ISSET(ops,'m')) + printflags &= ~PRINT_WITH_NAMESPACE; + if (!OPT_ISSET(ops,'p')) { if (!(on|roff)) printflags |= PRINT_TYPE; @@ -2718,10 +2799,18 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) return 0; } - if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) || - OPT_PLUS(ops,'g') || *name == 'l' || - (!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g'))) - on |= PM_LOCAL; + /* Using *name here is cheating, "local" allows no -g option */ + if ((*name == 'l' || OPT_PLUS(ops,'g'))) + on |= PM_LOCAL; + else if (!OPT_ISSET(ops,'g')) { + if (OPT_MINUS(ops, 'x')) { + if (isset(GLOBALEXPORT)) + ops->ind['g'] = 1; + else if (locallevel) + on |= PM_LOCAL; + } else if (!(OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m'))) + on |= PM_LOCAL; + } if ((on & PM_TIED) && !OPT_ISSET(ops, 'p')) { Param apm; @@ -2956,6 +3045,13 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* With the -m option, treat arguments as glob patterns */ if (OPT_ISSET(ops,'m')) { if (!OPT_ISSET(ops,'p')) { + if (on & PM_NAMEREF) { + /* It's generally unwise to mass-change the types of + * parameters, but for namerefs it would be fatal */ + unqueue_signals(); + zerrnam(name, "invalid reference"); + return 1; + } if (!(on|roff)) printflags |= PRINT_TYPE; if (!on) @@ -3022,6 +3118,47 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) } continue; } + + if (on & PM_NAMEREF) { + if (asg->value.scalar && + ((pm = (Param)resolve_nameref((Param)hn, asg)) && + (pm->node.flags & PM_NAMEREF))) { + if (pm->node.flags & PM_SPECIAL) { + zwarnnam(name, "%s: invalid reference", pm->node.nam); + returnval = 1; + continue; + } else if (pm->u.str && strcmp(pm->u.str, asg->name) == 0) { + zwarnnam(name, "%s: invalid self reference", asg->name); + returnval = 1; + continue; + } + } + if (hn) { + /* namerefs always start over fresh */ + if (((Param)hn)->level >= locallevel || + (!(on & PM_LOCAL) && ((Param)hn)->level < locallevel)) { + Param oldpm = (Param)hn; + if (!asg->value.scalar && + PM_TYPE(oldpm->node.flags) == PM_SCALAR && + oldpm->u.str) + asg->value.scalar = dupstring(oldpm->u.str); + /* Defer read-only error to typeset_single() */ + if (!(hn->flags & PM_READONLY)) { + unsetparam_pm(oldpm, 0, 1); + hn = NULL; + } + } + /* Passing a NULL pm to typeset_single() makes the + * nameref read-only before assignment, which breaks + * typeset -rn ref=var + * so this is special-cased to permit that action + * like assign-at-create for other parameter types. + */ + if (hn && !(hn->flags & PM_READONLY)) + hn = NULL; + } + } + if (!typeset_single(name, asg->name, (Param)hn, func, on, off, roff, asg, NULL, ops, 0)) @@ -3308,16 +3445,16 @@ bin_functions(char *name, char **argv, Options ops, int func) newsh->sticky = sticky_emulation_dup(shf->sticky, 0); /* is newsh a signal trap? (adapted from exec.c) */ if (!strncmp(s, "TRAP", 4)) { - int signum = getsignum(s + 4); - if (signum != -1) { - if (settrap(signum, NULL, ZSIG_FUNC)) { + int sigidx = getsigidx(s + 4); + if (sigidx != -1) { + if (settrap(sigidx, NULL, ZSIG_FUNC)) { freeeprog(newsh->funcdef); dircache_set(&newsh->filename, NULL); zfree(newsh, sizeof(*newsh)); return 1; } /* Remove any old node explicitly */ - removetrapnode(signum); + removetrapnode(sigidx); } } shfunctab->addnode(shfunctab, ztrdup(s), &newsh->node); @@ -3596,15 +3733,15 @@ bin_functions(char *name, char **argv, Options ops, int func) /* no flags, so just print */ printshfuncexpand(&shf->node, pflags, expand); } else if (on & PM_UNDEFINED) { - int signum = -1, ok = 1; + int sigidx = -1, ok = 1; if (!strncmp(*argv, "TRAP", 4) && - (signum = getsignum(*argv + 4)) != -1) { + (sigidx = getsigidx(*argv + 4)) != -1) { /* * Because of the possibility of alternative names, * we must remove the trap explicitly. */ - removetrapnode(signum); + removetrapnode(sigidx); } if (**argv == '/') { @@ -3640,8 +3777,8 @@ bin_functions(char *name, char **argv, Options ops, int func) shfunc_set_sticky(shf); add_autoload_function(shf, *argv); - if (signum != -1) { - if (settrap(signum, NULL, ZSIG_FUNC)) { + if (sigidx != -1) { + if (settrap(sigidx, NULL, ZSIG_FUNC)) { shfunctab->removenode(shfunctab, *argv); shfunctab->freenode(&shf->node); returnval = 1; @@ -3715,7 +3852,11 @@ bin_unset(char *name, char **argv, Options ops, int func) if ((!(pm->node.flags & PM_RESTRICTED) || unset(RESTRICTED)) && pattry(pprog, pm->node.nam)) { - unsetparam_pm(pm, 0, 1); + if (!OPT_ISSET(ops,'n') && + (pm->node.flags & PM_NAMEREF) && pm->u.str) + unsetparam(pm->u.str); + else + unsetparam_pm(pm, 0, 1); match++; } } @@ -3767,6 +3908,11 @@ bin_unset(char *name, char **argv, Options ops, int func) zerrnam(name, "%s: restricted", pm->node.nam); returnval = 1; } else if (ss) { + if ((pm->node.flags & PM_NAMEREF) && + (!(pm = (Param)resolve_nameref(pm, NULL)) || pm->width)) { + /* warning? */ + continue; + } if (PM_TYPE(pm->node.flags) == PM_HASHED) { HashTable tht = paramtab; if ((paramtab = pm->gsu.h->getfn(pm))) @@ -3805,6 +3951,18 @@ bin_unset(char *name, char **argv, Options ops, int func) returnval = 1; } } else { + if (!OPT_ISSET(ops,'n')) { + int ref = (pm->node.flags & PM_NAMEREF); + if (!(pm = (Param)resolve_nameref(pm, NULL))) + continue; + if (ref && pm->level < locallevel && + !(pm->node.flags & PM_READONLY)) { + /* Just mark unset, do not remove from table */ + stdunsetfn(pm, 0); + pm->node.flags |= PM_DECLARED; + continue; + } + } if (unsetparam_pm(pm, 0, 1)) returnval = 1; } @@ -3941,6 +4099,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) /* Take arguments literally -- do not glob */ queue_signals(); for (; *argv; argv++) { + informed = 0; if (!OPT_ISSET(ops,'p') && !allmatched) { char *suf; @@ -4603,6 +4762,8 @@ bin_print(char *name, char **args, Options ops, int func) /* compute lengths, and interpret according to -P, -D, -e, etc. */ argc = arrlen(args); len = (int *) hcalloc(argc * sizeof(int)); + if (OPT_ISSET(ops, 'P')) + txtunknownattrs = TXT_ATTR_ALL; for (n = 0; n < argc; n++) { /* first \ sequences */ if (fmt || @@ -4633,7 +4794,7 @@ bin_print(char *name, char **args, Options ops, int func) */ char *str = unmetafy( promptexpand(metafy(args[n], len[n], META_NOALLOC), - 0, NULL, NULL, NULL), + 0, NULL, NULL), &len[n]); args[n] = dupstrpfx(str, len[n]); free(str); @@ -4752,7 +4913,7 @@ bin_print(char *name, char **args, Options ops, int func) */ if (*aptr == '\033' || *aptr == '\233') { for (aptr++, l--; - l && !isalpha(STOUC(*aptr)); + l && !isalpha((unsigned char) (*aptr)); aptr++, l--) ; aptr++; @@ -5114,7 +5275,8 @@ bin_print(char *name, char **args, Options ops, int func) } } if (*argp) { - width = (int)mathevali(*argp++); + width = (int)mathevali(metafy(*argp, len[argp - args], META_NOALLOC)); + argp++; if (errflag) { errflag &= ~ERRFLAG_ERROR; ret = 1; @@ -5148,7 +5310,8 @@ bin_print(char *name, char **args, Options ops, int func) } if (*argp) { - prec = (int)mathevali(*argp++); + prec = (int)mathevali(metafy(*argp, len[argp - args], META_NOALLOC)); + argp++; if (errflag) { errflag &= ~ERRFLAG_ERROR; ret = 1; @@ -5222,20 +5385,21 @@ bin_print(char *name, char **args, Options ops, int func) #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE)) { chars = mbrlen(ptr, lleft, &mbs); - if (chars < 0) { - /* - * Invalid/incomplete character at this - * point. Assume all the rest are a - * single byte. That's about the best we - * can do. - */ - lchars += lleft; - lbytes = (ptr - b) + lleft; - break; - } else if (chars == 0) { - /* NUL, handle as real character */ + /* + * chars <= 0 means one of + * + * 0: NUL, handle as real character + * + * -1: MB_INVALID: Assume this is + * a single character as we do + * elsewhere in the code. + * + * -2: MB_INCOMPLETE: We're not waiting + * for input on this occasion, so + * just treat this as invalid. + */ + if (chars <= 0) chars = 1; - } } else /* use the non-multibyte code below */ #endif @@ -5259,7 +5423,7 @@ bin_print(char *name, char **args, Options ops, int func) break; case 'q': stringval = curarg ? - quotestring(metafy(curarg, curlen, META_USEHEAP), + quotestring(metafy(curarg, curlen, META_NOALLOC), QT_BACKSLASH_SHOWNULL) : &nullstr; *d = 's'; print_val(unmetafy(stringval, &curlen)); @@ -5313,9 +5477,9 @@ bin_print(char *name, char **args, Options ops, int func) else cc = WEOF; if (cc == WEOF) - cc = (curlen > 1) ? STOUC(curarg[1]) : 0; + cc = (curlen > 1) ? (unsigned char) (curarg[1]) : 0; #else - cc = (curlen > 1) ? STOUC(curarg[1]) : 0; + cc = (curlen > 1) ? (unsigned char) (curarg[1]) : 0; #endif if (type == 2) { doubleval = cc; @@ -5331,7 +5495,7 @@ bin_print(char *name, char **args, Options ops, int func) *d++ = 'l'; #endif *d++ = 'l', *d++ = *c, *d = '\0'; - zlongval = (curarg) ? mathevali(curarg) : 0; + zlongval = (curarg) ? mathevali(metafy(curarg, curlen, META_NOALLOC)) : 0; if (errflag) { zlongval = 0; errflag &= ~ERRFLAG_ERROR; @@ -5382,7 +5546,7 @@ bin_print(char *name, char **args, Options ops, int func) if (!curarg) zulongval = (zulong)0; else if (!zstrtoul_underscore(curarg, &zulongval)) - zulongval = mathevali(curarg); + zulongval = mathevali(metafy(curarg, curlen, META_NOALLOC)); if (errflag) { zulongval = 0; errflag &= ~ERRFLAG_ERROR; @@ -6282,11 +6446,12 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) long izle_timeout = 0; #ifdef MULTIBYTE_SUPPORT wchar_t delim = L'\n', wc; + int rawbyte = 0; mbstate_t mbs; char *laststart; size_t ret; #else - char delim = '\n'; + int delim = '\n'; #endif if (OPT_HASARG(ops,c='k')) { @@ -6373,9 +6538,10 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) } else readfd = izle = 0; - if (OPT_ISSET(ops,'s') && SHTTY != -1) { + if (OPT_ISSET(ops,'s') && isatty(readfd)) { struct ttyinfo ti; - gettyinfo(&ti); + memset(&ti, 0, sizeof(struct ttyinfo)); + fdgettyinfo(readfd, &ti); saveti = ti; resettty = 1; #ifdef HAS_TIO @@ -6383,7 +6549,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) #else ti.sgttyb.sg_flags &= ~ECHO; #endif - settyinfo(&ti); + fdsettyinfo(readfd, &ti); } /* handle prompt */ @@ -6412,15 +6578,18 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) wi = WEOF; if (wi != WEOF) delim = (wchar_t)wi; - else - delim = (wchar_t)((delimstr[0] == Meta) ? + else { + delim = (wchar_t) (unsigned char) ((delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0]); + rawbyte = 1; + } #else - delim = (delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0]; + delim = (unsigned char) ((delimstr[0] == Meta) ? + delimstr[1] ^ 32 : delimstr[0]); #endif - if (SHTTY != -1) { + if (isatty(readfd)) { struct ttyinfo ti; - gettyinfo(&ti); + fdgettyinfo(readfd, &ti); if (! resettty) { saveti = ti; resettty = 1; @@ -6432,7 +6601,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) #else ti.sgttyb.sg_flags |= CBREAK; #endif - settyinfo(&ti); + fdsettyinfo(readfd, &ti); } } if (OPT_ISSET(ops,'t')) { @@ -6467,10 +6636,11 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) timeout)) { if (keys && !zleactive && !isem) settyinfo(&shttyinfo); - else if (resettty && SHTTY != -1) - settyinfo(&saveti); + else if (resettty) + fdsettyinfo(readfd, &saveti); if (haso) { - fclose(shout); + if (shout) + fclose(shout); shout = oshout; SHTTY = -1; } @@ -6578,8 +6748,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) /* dispose of result appropriately, etc. */ if (isem) while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n'); - else { - settyinfo(&shttyinfo); + else if (resettty) { + fdsettyinfo(readfd, &saveti); resettty = 0; } if (haso) { @@ -6608,8 +6778,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) setsparam(reply, metafy(buf, bptr - buf, META_REALLOC)); else zfree(buf, bptr - buf + 1); - if (resettty && SHTTY != -1) - settyinfo(&saveti); + if (resettty) + fdsettyinfo(readfd, &saveti); return eof; } @@ -6685,7 +6855,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) continue; first = 0; } - if (imeta(STOUC(*bptr))) { + if (imeta((unsigned char) *bptr)) { bptr[1] = bptr[0] ^ 32; bptr[0] = Meta; bptr += 2; @@ -6819,8 +6989,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) *pp++ = NULL; setaparam(reply, p); } - if (resettty && SHTTY != -1) - settyinfo(&saveti); + if (resettty) + fdsettyinfo(readfd, &saveti); return c == EOF; } buf = bptr = (char *)zalloc(bsiz = 64); @@ -6841,7 +7011,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) break; } *bptr = (char)c; - if (isset(MULTIBYTE)) { + if (isset(MULTIBYTE) && !rawbyte) { ret = mbrtowc(&wc, bptr, 1, &mbs); if (!ret) /* NULL */ ret = 1; @@ -6878,7 +7048,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) if (bslash) continue; } - if (imeta(STOUC(*bptr))) { + if (imeta((unsigned char) *bptr)) { bptr[1] = bptr[0] ^ 32; bptr[0] = Meta; bptr += 2; @@ -6948,8 +7118,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) break; } *bptr = '\0'; - if (resettty && SHTTY != -1) - settyinfo(&saveti); + if (resettty) + fdsettyinfo(readfd, &saveti); /* final assignment of reply, etc. */ if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) { zputs(buf, stdout); @@ -7000,14 +7170,14 @@ zread(int izle, int *readchar, long izle_timeout) buffer. This may be a null byte to indicate EOF. If reading from the buffer, move on the buffer pointer. */ if (*zbuf == Meta) - return zbuf++, STOUC(*zbuf++ ^ 32); + return zbuf++, (unsigned char) (*zbuf++ ^ 32); else - return (*zbuf) ? STOUC(*zbuf++) : EOF; + return (*zbuf) ? (unsigned char) *zbuf++ : EOF; } if (*readchar >= 0) { cc = *readchar; *readchar = -1; - return STOUC(cc); + return (unsigned char) cc; } for (;;) { /* read a character from readfd */ @@ -7015,7 +7185,7 @@ zread(int izle, int *readchar, long izle_timeout) switch (ret) { case 1: /* return the character read */ - return STOUC(cc); + return (unsigned char) cc; case -1: #if defined(EAGAIN) || defined(EWOULDBLOCK) if (!retry && readfd == 0 && ( @@ -7204,7 +7374,7 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) /* If given no arguments, list all currently-set traps */ if (!*argv) { queue_signals(); - for (sig = 0; sig < VSIGCOUNT; sig++) { + for (sig = 0; sig < TRAPCOUNT; sig++) { if (sigtrapped[sig] & ZSIG_FUNC) { HashNode hn; @@ -7230,13 +7400,13 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) /* If we have a signal number, unset the specified * * signals. With only -, remove all traps. */ - if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) { + if ((getsigidx(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) { if (!*argv) { - for (sig = 0; sig < VSIGCOUNT; sig++) + for (sig = 0; sig < TRAPCOUNT; sig++) unsettrap(sig); } else { for (; *argv; argv++) { - sig = getsignum(*argv); + sig = getsigidx(*argv); if (sig == -1) { zwarnnam(name, "undefined signal: %s", *argv); break; @@ -7261,12 +7431,12 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) Eprog t; int flags; - sig = getsignum(*argv); + sig = getsigidx(*argv); if (sig == -1) { zwarnnam(name, "undefined signal: %s", *argv); break; } - if (idigit(**argv) || + if (idigit(**argv) || (sig >= VSIGCOUNT) || !strcmp(sigs[sig], *argv) || (!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) { /* The signal was specified by number or by canonical name (with diff --git a/Src/compat.c b/Src/compat.c index 817bb4aaf..8b31ad9f4 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -342,36 +342,69 @@ zopenmax(void) mod_export char * zgetdir(struct dirsav *d) { - char nbuf[PATH_MAX+3]; char *buf; +#if defined(HAVE_GETCWD) || defined(__CYGWIN__) + char *cwdbuf; +#endif +#if defined(USE_GETCWD) || defined(__CYGWIN__) +#ifdef GETCWD_CALLS_MALLOC + if ((cwdbuf = getcwd(NULL, 0))) { + buf = dupstring(cwdbuf); + free(cwdbuf); + } else + buf = NULL; +#else + cwdbuf = zalloc(PATH_MAX+1); + if ((buf = getcwd(cwdbuf, PATH_MAX))) + buf = dupstring(buf); + zfree(cwdbuf, PATH_MAX+1); +#endif /* GETCWD_CALLS_MALLOC */ + if (d && buf) + return d->dirname = ztrdup(buf); + else + return buf; +#else /* !USE_GETCWD && !__CYGWIN__ */ int bufsiz, pos; - struct stat sbuf; - ino_t pino; - dev_t pdev; -#if !defined(__CYGWIN__) && !defined(USE_GETCWD) + char nbuf[PATH_MAX+3]; struct dirent *de; DIR *dir; - dev_t dev; - ino_t ino; + struct stat sbuf; + ino_t pino, ino; + dev_t pdev, dev; int len; -#endif + strcpy(nbuf, "../"); + if (stat(".", &sbuf) == 0) { + /* Record the initial inode and device */ + pino = sbuf.st_ino; + pdev = sbuf.st_dev; + if (d) + d->ino = pino, d->dev = pdev; + } +#ifdef HAVE_GETCWD + else { +#ifdef GETCWD_CALLS_MALLOC + if ((cwdbuf = getcwd(NULL, 0))) { + buf = dupstring(cwdbuf); + free(cwdbuf); + } else + buf = NULL; +#else + cwdbuf = zalloc(PATH_MAX+1); + if ((buf = getcwd(cwdbuf, PATH_MAX))) + buf = dupstring(buf); + zfree(cwdbuf, PATH_MAX+1); +#endif /* GETCWD_CALLS_MALLOC */ + return buf; /* NULL when stat() and getcwd() both failed */ + } +#endif + /* stat() succeeded */ buf = zhalloc(bufsiz = PATH_MAX+1); pos = bufsiz - 1; buf[pos] = '\0'; - strcpy(nbuf, "../"); - if (stat(".", &sbuf) < 0) { - return NULL; - } - /* Record the initial inode and device */ - pino = sbuf.st_ino; - pdev = sbuf.st_dev; - if (d) - d->ino = pino, d->dev = pdev; -#if !defined(__CYGWIN__) && !defined(USE_GETCWD) #ifdef HAVE_FCHDIR - else + if (!d) #endif holdintr(); @@ -487,18 +520,6 @@ zgetdir(struct dirsav *d) zchdir(buf + pos + 1); noholdintr(); -#else /* __CYGWIN__, USE_GETCWD cases */ - - if (!getcwd(buf, bufsiz)) { - if (d) { - return NULL; - } - } else { - if (d) { - return d->dirname = ztrdup(buf); - } - return buf; - } #endif /* @@ -526,23 +547,6 @@ mod_export char * zgetcwd(void) { char *ret = zgetdir(NULL); -#ifdef HAVE_GETCWD - if (!ret) { -#ifdef GETCWD_CALLS_MALLOC - char *cwd = getcwd(NULL, 0); - if (cwd) { - ret = dupstring(cwd); - free(cwd); - } -#else - char *cwdbuf = zalloc(PATH_MAX+1); - ret = getcwd(cwdbuf, PATH_MAX); - if (ret) - ret = dupstring(ret); - zfree(cwdbuf, PATH_MAX+1); -#endif /* GETCWD_CALLS_MALLOC */ - } -#endif /* HAVE_GETCWD */ if (!ret) ret = unmeta(pwd); if (!ret || *ret == '\0') diff --git a/Src/exec.c b/Src/exec.c index f2911807c..e955e85df 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -56,14 +56,56 @@ struct funcsave { typedef struct funcsave *Funcsave; /* - * used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT. + * Used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT in the + * evaluation of sub-commands of the command under evaluation. The + * variable must be updated before the evaluation of the sub-commands + * starts and restored to its previous state right after that + * evaluation ends. The variable is read and acted upon in execlist. + * + * A good usage example can be found in execwhile in loop.c, which + * evaluates while statements. The variable is updated to disable + * ERREXIT just before evaluating the while's condition and restored + * to its previous state right after the evaluation of the condition. + * * Bits from noerrexit_bits. */ /**/ int noerrexit; -/* used to suppress ERREXIT or ERRRETURN for one occurrence: 0 or 1 */ +/* + * Used to suppress ERREXIT and ERRRETURN for the command under + * evaluation. The variable must be enabled (set to 1) at the very + * end of the evaluation of the command. It must come after the + * evaluation of any sub-commands of the command under evaluation. The + * variable is read and acted upon in execlist, which also takes care + * of initialising and resetting it to 0. + * + * Unlike the variable noerrexit, whose state applies to the + * evaluation of whole sub-commands (and their direct and indirect + * sub-commands), the scope of the variable this_noerrexit is much + * more localized. ERREXIT and ERRRETURN are triggered at the end of + * the function execlist after the evaluation of some or all of the + * list's sub-commands. The role of the variable this_noerrexit is to + * give to the functions evaluating the list's sub-commands the + * possibility to tell the calling execlist not to trigger ERREXIT and + * ERRRETURN. In other words, the variable acts as an additional + * return value between the called evaluation functions and the + * calling execlist. For that reason the variable must always be set + * as late as possible and in particular after any sub-command + * evaluation. If the variable is set before the evaluation of a + * sub-command, if may affect the wrong execlist, if the sub-command + * evaluation involves another execlist call, and/or the variable may + * get modified by the sub-command evaluation and thus wouldn't return + * the desired value to the calling execlist. + * + * Good usage examples can be found in the exec functions in loop.c, + * which evaluate compound commands. The variable is enabled right + * before returning from the functions, after all the sub-commands of + * the compound commands have already been evaluated. + * + * 0 or 1 + */ /**/ int this_noerrexit; @@ -225,7 +267,7 @@ static char *blank_env[] = { NULL }; /* Execution functions. */ -static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = { +static int (*execfuncs[WC_COUNT-WC_CURSH]) (Estate, int) = { execcursh, exectime, NULL /* execfuncdef handled specially */, execfor, execselect, execwhile, execrepeat, execcase, execif, execcond, @@ -477,7 +519,7 @@ zexecve(char *pth, char **argv, char **newenvp) if (*pth == '/') strcpy(buf + 2, pth); else - sprintf(buf + 2, "%s/%s", pwd, pth); + sprintf(buf + 2, "%s/%s", unmeta(pwd), pth); zputenv(buf); #ifndef FD_CLOEXEC closedumps(); @@ -509,7 +551,7 @@ zexecve(char *pth, char **argv, char **newenvp) break; if (t0 == ct) zerr("%s: bad interpreter: %s: %e", pth, - execvebuf + 2, eno); + metafy(execvebuf + 2, -1, META_STATIC), eno); else { while (inblank(execvebuf[t0])) execvebuf[t0--] = '\0'; @@ -532,8 +574,8 @@ zexecve(char *pth, char **argv, char **newenvp) execve(pprog, argv - 2, newenvp); } } - zerr("%s: bad interpreter: %s: %e", pth, ptr2, - eno); + zerr("%s: bad interpreter: %s: %e", pth, + metafy(ptr2, -1, META_STATIC), eno); } else if (*ptr) { *ptr = '\0'; argv[-2] = ptr2; @@ -561,7 +603,7 @@ zexecve(char *pth, char **argv, char **newenvp) isbinary = 1; hasletter = 0; for (ptr = execvebuf; ptr < ptr2; ptr++) { - if (islower(STOUC(*ptr)) || *ptr == '$' || *ptr == '`') + if (islower((unsigned char) *ptr) || *ptr == '$' || *ptr == '`') hasletter = 1; if (hasletter && *ptr == '\n') { isbinary = 0; @@ -570,9 +612,22 @@ zexecve(char *pth, char **argv, char **newenvp) } } if (!isbinary) { - argv[-1] = "sh"; + char** args = argv; + if (argv[0][0] == '-' || argv[0][0] == '+') { + /* + * guard against +foo or -bar script paths being + * taken as options. POSIX says the script path + * must be passed as an *operand*. "--" would also + * make sure the next argument is treated as an + * operand with POSIX compliant sh implementations + * but "-" is more portable (to the Bourne shell in + * particular) and shorter. + */ + *--args = "-"; + } + *--args = "sh"; winch_unblock(); - execve("/bin/sh", argv - 1, newenvp); + execve("/bin/sh", args, newenvp); } } } else @@ -1373,7 +1428,7 @@ execlist(Estate state, int dont_change_job, int exiting) int oerrexit_opt = opts[ERREXIT]; Param pm; opts[ERREXIT] = 0; - noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN; if (ltype & Z_SIMPLE) /* skip the line number */ pc2++; pm = assignsparam("ZSH_DEBUG_CMD", @@ -1424,17 +1479,13 @@ execlist(Estate state, int dont_change_job, int exiting) goto sublist_done; } while (wc_code(code) == WC_SUBLIST) { - int isend = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END); + this_noerrexit = 0; + int isandor = WC_SUBLIST_TYPE(code) != WC_SUBLIST_END; + int isnot = WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT; next = state->pc + WC_SUBLIST_SKIP(code); - if (!oldnoerrexit) - noerrexit = isend ? 0 : NOERREXIT_EXIT | NOERREXIT_RETURN; - if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) { - /* suppress errexit for "! this_command" */ - if (isend) - this_noerrexit = 1; - /* suppress errexit for ! <list-of-shell-commands> */ - noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; - } + /* suppress errexit for commands before && and || and after ! */ + if (isandor || isnot) + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN; switch (WC_SUBLIST_TYPE(code)) { case WC_SUBLIST_END: /* End of sublist; just execute, ignoring status. */ @@ -1443,6 +1494,9 @@ execlist(Estate state, int dont_change_job, int exiting) else execpline(state, code, ltype, (ltype & Z_END) && exiting); state->pc = next; + /* suppress errexit for the command "! ..." */ + if (isnot) + this_noerrexit = 1; goto sublist_done; break; case WC_SUBLIST_AND: @@ -1450,7 +1504,7 @@ execlist(Estate state, int dont_change_job, int exiting) * we find a sublist followed by ORNEXT. */ if ((ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ? execsimple(state) : - execpline(state, code, Z_SYNC, 0)))) { + execpline(state, code, Z_SYNC, 0))) || breaks) { state->pc = next; code = *state->pc++; next = state->pc + WC_SUBLIST_SKIP(code); @@ -1483,7 +1537,7 @@ execlist(Estate state, int dont_change_job, int exiting) * we find a sublist followed by ANDNEXT. */ if (!(ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ? execsimple(state) : - execpline(state, code, Z_SYNC, 0)))) { + execpline(state, code, Z_SYNC, 0))) || breaks) { state->pc = next; code = *state->pc++; next = state->pc + WC_SUBLIST_SKIP(code); @@ -1518,14 +1572,7 @@ execlist(Estate state, int dont_change_job, int exiting) state->pc--; sublist_done: - /* - * See hairy code near the end of execif() for the - * following. "noerrexit " only applies until - * we hit execcmd on the way down. We're now - * on the way back up, so don't restore it. - */ - if (!(oldnoerrexit & NOERREXIT_UNTIL_EXEC)) - noerrexit = oldnoerrexit; + noerrexit = oldnoerrexit; if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) { /* @@ -1534,7 +1581,7 @@ sublist_done: */ int oerrexit_opt = opts[ERREXIT]; opts[ERREXIT] = 0; - noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN; exiting = donetrap; ret = lastval; dotrap(SIGDEBUG); @@ -1564,6 +1611,7 @@ sublist_done: (isset(ERRRETURN) && !errreturn)) && !(noerrexit & NOERREXIT_EXIT); if (errexit) { + errflag = 0; if (sigtrapped[SIGEXIT]) dotrap(SIGEXIT); if (mypid != getpid()) @@ -1581,6 +1629,7 @@ sublist_done: break; code = *state->pc++; } + this_noerrexit = 0; pline_level = old_pline_level; list_pipe = old_list_pipe; list_pipe_job = old_list_pipe_job; @@ -1595,9 +1644,12 @@ sublist_done: thisjob = cj; if (exiting && sigtrapped[SIGEXIT]) { + int eflag = errflag; + errflag = 0; /* Clear the context for trap */ dotrap(SIGEXIT); /* Make sure this doesn't get executed again. */ sigtrapped[SIGEXIT] = 0; + errflag = eflag; } unqueue_signals(); @@ -1899,8 +1951,12 @@ execpline(Estate state, wordcode slcode, int how, int last1) break; } } - else if (subsh && jn->stat & STAT_STOPPED) - thisjob = newjob; + else if (subsh && jn->stat & STAT_STOPPED) { + if (thisjob == newjob) + makerunning(jn); + else + thisjob = newjob; + } else break; } @@ -1922,7 +1978,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) } else unqueue_signals(); - if ((slflags & WC_SUBLIST_NOT) && !errflag) + if ((slflags & WC_SUBLIST_NOT) && !errflag && !retflag) lastval = !lastval; } if (!pline_level) @@ -2247,6 +2303,8 @@ closemn(struct multio **mfds, int fd, int type) return; } /* pid == 0 */ + opts[INTERACTIVE] = 0; + dont_queue_signals(); child_unblock(); closeallelse(mn); if (mn->rflag) { @@ -2259,19 +2317,21 @@ closemn(struct multio **mfds, int fd, int type) break; } for (i = 0; i < mn->ct; i++) - write_loop(mn->fds[i], buf, len); + if (write_loop(mn->fds[i], buf, len) < 0) + break; } } else { /* cat process */ for (i = 0; i < mn->ct; i++) while ((len = read(mn->fds[i], buf, TCBUFSIZE)) != 0) { if (len < 0) { - if (errno == EINTR) + if (errno == EINTR && !isatty(mn->fds[i])) continue; else break; } - write_loop(mn->pipe, buf, len); + if (write_loop(mn->pipe, buf, len) < 0) + break; } } _exit(0); @@ -2345,7 +2405,7 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, /* fd will be over 10, don't touch mfds */ fd1 = movefd(fd2); if (fd1 == -1) { - zerr("cannot moved fd %d: %e", fd2, errno); + zerr("cannot move fd %d: %e", fd2, errno); return; } else { fdtable[fd1] = FDT_EXTERNAL; @@ -2544,6 +2604,17 @@ addvars(Estate state, Wordcode pc, int addflags) opts[ALLEXPORT] = allexp; } else pm = assignsparam(name, val, myflags); + if (!pm) { + lastval = 1; + /* + * This is cheating but some exec functions propagate + * assignment status only from command substitution + * + * zerr("%s: assignment failed", name); + */ + if (!cmdoutval) + cmdoutval = 1; + } if (errflag) { state->pc = opc; return; @@ -2568,7 +2639,16 @@ addvars(Estate state, Wordcode pc, int addflags) } fprintf(xtrerr, ") "); } - assignaparam(name, arr, myflags); + if (!assignaparam(name, arr, myflags)) { + lastval = 1; + /* + * See above RE "cheating" + * + * zerr("%s: array assignment failed", name); + */ + if (!cmdoutval) + cmdoutval = 1; + } if (errflag) { state->pc = opc; return; @@ -3200,10 +3280,6 @@ execcmd_exec(Estate state, Execcmd_params eparams, } else preargs = NULL; - /* if we get this far, it is OK to pay attention to lastval again */ - if (noerrexit & NOERREXIT_UNTIL_EXEC) - noerrexit = 0; - /* Do prefork substitutions. * * Decide if we need "magic" handling of ~'s etc. in @@ -3712,7 +3788,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid); /* If this is 'exec < file', read from stdin, * * not terminal, unless `file' is a terminal. */ - if (nullexec == 1 && fn->fd1 == 0 && + if (nullexec == 1 && fn->fd1 == 0 && !fn->varid && isset(SHINSTDIN) && interact && !zleactive) init_io(NULL); break; @@ -4301,10 +4377,13 @@ execcmd_exec(Estate state, Execcmd_params eparams, } } if (newxtrerr) { + int eno = errno; fil = fileno(newxtrerr); fclose(newxtrerr); xtrerr = oxtrerr; + /* Call zclose() to clean up internal tables, ignore EBADF */ zclose(fil); + errno = eno; } zsfree(STTYval); @@ -4636,6 +4715,9 @@ getoutput(char *cmd, int qt) if (!prog) return NULL; + if (!isset(EXECOPT)) + return newlinklist(); + if ((s = simple_redir_name(prog, REDIR_READ))) { /* $(< word) */ int stream; @@ -4852,7 +4934,6 @@ getoutputfile(char *cmd, char **eptr) if ((fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600)) < 0) { zerr("process substitution failed: %e", errno); - free(nam); if (!s) child_unblock(); return NULL; @@ -5052,7 +5133,7 @@ getpipe(char *cmd, int nullexec) procsubstpid = pid; return pipes[!out]; } - entersubsh(ESUB_ASYNC|ESUB_PGRP, NULL); + entersubsh(ESUB_ASYNC|ESUB_PGRP|ESUB_NOMONITOR, NULL); redup(pipes[out], out); closem(FDT_UNUSED, 0); /* this closes pipes[!out] as well */ cmdpush(CS_CMDSUBST); @@ -5366,7 +5447,7 @@ execfuncdef(Estate state, Eprog redir_prog) } else { /* is this shell function a signal trap? */ if (!strncmp(s, "TRAP", 4) && - (signum = getsignum(s + 4)) != -1) { + (signum = getsigidx(s + 4)) != -1) { if (settrap(signum, NULL, ZSIG_FUNC)) { freeeprog(shf->funcdef); dircache_set(&shf->filename, NULL); @@ -5734,12 +5815,25 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) char *name = shfunc->node.nam; int flags = shfunc->node.flags; char *fname = dupstring(name); - Eprog prog; + Eprog prog, marked_prog; static int oflags; static int funcdepth; Heap funcheap; queue_signals(); /* Lots of memory and global state changes coming */ + /* + * In case this is a special function such as a trap, mark it + * as in use right now, so it doesn't get freed early. The + * worst that can happen is this hangs around in memory a little + * longer than strictly needed. + * + * Classic example of this happening is running TRAPEXIT directly. + * + * Because the shell function's contents may change, we'll ensure + * we use a consistent structure for use / free. + */ + marked_prog = shfunc->funcdef; + useeprog(marked_prog); NEWHEAPS(funcheap) { /* @@ -5773,6 +5867,22 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) memcpy(funcsave->pipestats, pipestats, bytes); } + if (!strcmp(fname, "TRAPEXIT")) { + /* + * If we are executing TRAPEXIT directly, starttrapscope() + * will pull the rug out from under us to ensure the + * exit trap isn't run inside the function. We just need + * the information locally here, so copy it on the heap. + * + * The funcdef is separately handled by reference counting. + */ + Shfunc shcopy = (Shfunc)zhalloc(sizeof(struct shfunc)); + memcpy(shcopy, shfunc, sizeof(struct shfunc)); + shcopy->node.nam = dupstring(shfunc->node.nam); + shfunc = shcopy; + name = shfunc->node.nam; + } + starttrapscope(); startpatternscope(); @@ -5897,6 +6007,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) funcsave->fstack.filename = getshfuncfile(shfunc); prog = shfunc->funcdef; + DPUTS1(!prog->nref, "function definition %s has zero reference count", + (fname && *fname) ? fname : "<anon>"); if (prog->flags & EF_RUN) { Shfunc shf; @@ -5925,15 +6037,6 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) * This function is forced to return. */ retflag = 0; - /* - * The calling function isn't necessarily forced to return, - * but it should be made sensitive to ERR_EXIT and - * ERR_RETURN as the assumptions we made at the end of - * constructs within this function no longer apply. If - * there are cases where this is not true, they need adding - * to C03traps.ztst. - */ - this_noerrexit = 0; breaks = funcsave->breaks; } freearray(pparams); @@ -5961,11 +6064,23 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) emulation = funcsave->emulation; sticky = funcsave->sticky; } else if (isset(LOCALOPTIONS)) { + /* we need to call inittyptab() if these options change */ + int init_typtab = +#ifdef MULTIBYTE_SUPPORT + funcsave->opts[MULTIBYTE] != opts[MULTIBYTE] || +#endif + funcsave->opts[BANGHIST] != opts[BANGHIST] || + funcsave->opts[SHINSTDIN] != opts[SHINSTDIN]; + /* take care of SUNKEYBOARDHACK but not of EMACS/VI */ + if (funcsave->opts[SUNKEYBOARDHACK] != opts[SUNKEYBOARDHACK]) + keyboardhackchar = funcsave->opts[SUNKEYBOARDHACK] ? '`' : '\0'; /* restore all shell options except PRIVILEGED and RESTRICTED */ funcsave->opts[PRIVILEGED] = opts[PRIVILEGED]; funcsave->opts[RESTRICTED] = opts[RESTRICTED]; memcpy(opts, funcsave->opts, sizeof(opts)); emulation = funcsave->emulation; + if (init_typtab) + inittyptab(); } else { /* just restore a couple. */ opts[XTRACE] = funcsave->opts[XTRACE]; @@ -5998,6 +6113,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) } } OLDHEAPS; + freeeprog(marked_prog); unqueue_signals(); /* diff --git a/Src/glob.c b/Src/glob.c index 400be12d5..3e34f708e 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -133,7 +133,7 @@ typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figu #define TT_TERABYTES 5 -typedef int (*TestMatchFunc) _((char *, struct stat *, off_t, char *)); +typedef int (*TestMatchFunc) (char *, struct stat *, off_t, char *); struct qual { struct qual *next; /* Next qualifier, must match */ @@ -1264,7 +1264,7 @@ zglob(LinkList list, LinkNode np, int nountok) int sense, qualsfound; off_t data; char *sdata, *newcolonmod, *ptr; - int (*func) _((char *, Statptr, off_t, char *)); + int (*func) (char *, Statptr, off_t, char *); /* * Initialise state variables for current file pattern. @@ -1310,14 +1310,15 @@ zglob(LinkList list, LinkNode np, int nountok) if (*ptr == Dash) *ptr = '-'; while (*s && !newcolonmod) { - func = (int (*) _((char *, Statptr, off_t, char *)))0; + func = (int (*) (char *, Statptr, off_t, char *)) 0; if (*s == ',') { /* A comma separates alternative sets of qualifiers */ s++; sense = 0; if (qualct) { qn = (struct qual *)hcalloc(sizeof *qn); - qo->or = qn; + if (qo) + qo->or = qn; qo = qn; qualorct++; qualct = 0; @@ -1481,7 +1482,7 @@ zglob(LinkList list, LinkNode np, int nountok) sav = *tt; *tt = '\0'; - if ((pw = getpwnam(s + arglen))) + if ((pw = getpwnam(unmeta(s + arglen)))) data = pw->pw_uid; else { zerr("unknown username '%s'", s + arglen); @@ -1960,7 +1961,7 @@ zglob(LinkList list, LinkNode np, int nountok) /* Sort arguments in to lexical (and possibly numeric) order. * * This is reversed to facilitate insertion into the list. */ qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch), - (int (*) _((const void *, const void *)))gmatchcmp); + (int (*) (const void *, const void *)) gmatchcmp); } if (first < 0) { @@ -2418,11 +2419,11 @@ xpandbraces(LinkList list, LinkNode *np) memset(ccl, 0, sizeof(ccl) / sizeof(ccl[0])); for (p = str + 1; p < str2;) { if (itok(c1 = *p++)) - c1 = ztokens[c1 - STOUC(Pound)]; + c1 = ztokens[c1 - (unsigned char) Pound]; if ((char) c1 == Meta) c1 = 32 ^ *p++; if (itok(c2 = *p)) - c2 = ztokens[c2 - STOUC(Pound)]; + c2 = ztokens[c2 - (unsigned char) Pound]; if ((char) c2 == Meta) c2 = 32 ^ p[1]; if (IS_DASH((char)c1) && lastch >= 0 && diff --git a/Src/hashtable.c b/Src/hashtable.c index bb165505e..96675a393 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -836,10 +836,10 @@ static HashNode removeshfuncnode(UNUSED(HashTable ht), const char *nam) { HashNode hn; - int signum; + int sigidx; - if (!strncmp(nam, "TRAP", 4) && (signum = getsignum(nam + 4)) != -1) - hn = removetrap(signum); + if (!strncmp(nam, "TRAP", 4) && (sigidx = getsigidx(nam + 4)) != -1) + hn = removetrap(sigidx); else hn = removehashnode(shfunctab, nam); @@ -856,10 +856,10 @@ disableshfuncnode(HashNode hn, UNUSED(int flags)) { hn->flags |= DISABLED; if (!strncmp(hn->nam, "TRAP", 4)) { - int signum = getsignum(hn->nam + 4); - if (signum != -1) { - sigtrapped[signum] &= ~ZSIG_FUNC; - unsettrap(signum); + int sigidx = getsigidx(hn->nam + 4); + if (sigidx != -1) { + sigtrapped[sigidx] &= ~ZSIG_FUNC; + unsettrap(sigidx); } } } @@ -876,9 +876,9 @@ enableshfuncnode(HashNode hn, UNUSED(int flags)) shf->node.flags &= ~DISABLED; if (!strncmp(shf->node.nam, "TRAP", 4)) { - int signum = getsignum(shf->node.nam + 4); - if (signum != -1) { - settrap(signum, NULL, ZSIG_FUNC); + int sigidx = getsigidx(shf->node.nam + 4); + if (sigidx != -1) { + settrap(sigidx, NULL, ZSIG_FUNC); } } } @@ -1397,6 +1397,14 @@ histstrcmp(const char *str1, const char *str2) { while (inblank(*str1)) str1++; while (inblank(*str2)) str2++; + + /* If insignificant whitespace has already been eliminated, + * there is no reason to expend similar effort here. Also, + * this is more accurate in cases of quoted whitespace. + */ + if (isset(HISTREDUCEBLANKS)) + return strcmp(str1, str2); + while (*str1 && *str2) { if (inblank(*str1)) { if (!inblank(*str2)) diff --git a/Src/hist.c b/Src/hist.c index bff0abe61..1a00c30ed 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -34,25 +34,25 @@ * word control. */ /**/ -mod_export int (*hgetc) _((void)); +mod_export int (*hgetc) (void); /**/ -void (*hungetc) _((int)); +void (*hungetc) (int); /**/ -void (*hwaddc) _((int)); +void (*hwaddc) (int); /**/ -void (*hwbegin) _((int)); +void (*hwbegin) (int); /**/ -void (*hwabort) _((void)); +void (*hwabort) (void); /**/ -void (*hwend) _((void)); +void (*hwend) (void); /**/ -void (*addtoline) _((int)); +void (*addtoline) (int); /* != 0 means history substitution is turned off */ @@ -163,6 +163,11 @@ char *hsubl; /**/ char *hsubr; +/* state of histsubstpattern at last substitution */ + +/**/ +int hsubpatopt; + /* pointer into the history line */ /**/ @@ -624,7 +629,7 @@ histsubchar(int c) return substfailed(); if (!hsubl) return -1; - if (subst(&sline, hsubl, hsubr, gbal)) + if (subst(&sline, hsubl, hsubr, gbal, 0)) return substfailed(); } else { /* Line doesn't begin ^foo^bar */ @@ -831,7 +836,7 @@ histsubchar(int c) if ((c = ingetc()) == 'g') { gbal = 1; c = ingetc(); - if (c != 's' && c != '&') { + if (c != 's' && c != 'S' && c != '&') { zerr("'s' or '&' modifier expected after 'g'"); return -1; } @@ -891,11 +896,13 @@ histsubchar(int c) } break; case 's': + case 'S': + hsubpatopt = (c == 'S'); if (getsubsargs(sline, &gbal, &cflag)) return -1; /* fall through */ case '&': if (hsubl && hsubr) { - if (subst(&sline, hsubl, hsubr, gbal)) + if (subst(&sline, hsubl, hsubr, gbal, hsubpatopt)) return substfailed(); } else { herrflush(); @@ -1352,7 +1359,8 @@ putoldhistentryontop(short keep_going) do { if (max_unique_ct-- <= 0 || he == hist_ring) { max_unique_ct = 0; - he = hist_ring->down; + if (hist_ring) + he = hist_ring->down; next = hist_ring; break; } @@ -1360,12 +1368,16 @@ putoldhistentryontop(short keep_going) next = he->down; } while (!(he->node.flags & HIST_DUP)); } - if (he != hist_ring->down) { + /* Is it really possible for hist_ring to be NULL here? */ + if (he && (!hist_ring || he != hist_ring->down)) { he->up->down = he->down; he->down->up = he->up; he->up = hist_ring; - he->down = hist_ring->down; - hist_ring->down = he->down->up = he; + if (hist_ring) { + he->down = hist_ring->down; + hist_ring->down = he; + } + he->down->up = he; } hist_ring = he; } @@ -1461,7 +1473,7 @@ should_ignore_line(Eprog prog) mod_export int hend(Eprog prog) { - int flag, hookret, stack_pos = histsave_stack_pos; + int flag, hookret = 0, stack_pos = histsave_stack_pos; /* * save: * 0: don't save @@ -1643,12 +1655,17 @@ hend(Eprog prog) void ihwbegin(int offset) { + int pos = hptr - chline + offset; if (stophist == 2 || (histactive & HA_INWORD) || (inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS) return; if (chwordpos%2) chwordpos--; /* make sure we're on a word start, not end */ - chwords[chwordpos++] = hptr - chline + offset; + DPUTS1(pos < 0, "History word position < 0 in %s", + dupstrpfx(chline, hptr-chline)); + if (pos < 0) + pos = 0; + chwords[chwordpos++] = pos; } /* Abort current history word, not needed */ @@ -2235,7 +2252,7 @@ casemodify(char *str, int how) char *mbptr; for (mbptr = mbstr; mbptr < mbstr + len2; mbptr++) { - if (imeta(STOUC(*mbptr))) { + if (imeta((unsigned char) *mbptr)) { *ptr2++ = Meta; *ptr2++ = *mbptr ^ 32; } else @@ -2254,10 +2271,10 @@ casemodify(char *str, int how) int c; int mod = 0; if (*str == Meta) { - c = STOUC(str[1] ^ 32); + c = (unsigned char) (str[1] ^ 32); str += 2; } else - c = STOUC(*str++); + c = (unsigned char) *str++; switch (how) { case CASMOD_LOWER: if (isupper(c)) { @@ -2310,7 +2327,7 @@ casemodify(char *str, int how) /**/ int -subst(char **strptr, char *in, char *out, int gbal) +subst(char **strptr, char *in, char *out, int gbal, int forcepat) { char *str = *strptr, *substcut, *sptr; int off, inlen, outlen; @@ -2318,7 +2335,7 @@ subst(char **strptr, char *in, char *out, int gbal) if (!*in) in = str, gbal = 0; - if (isset(HISTSUBSTPATTERN)) { + if (isset(HISTSUBSTPATTERN) || forcepat) { int fl = SUB_LONG|SUB_REST|SUB_RETFAIL; char *oldin = in; if (gbal) @@ -3791,8 +3808,14 @@ histsplitwords(char *lineptr, short **wordsp, int *nwordsp, int *nwordposp, zrealloc(words, nwords*sizeof(*words)); } words[nwordpos++] = lineptr - start; - while (*lineptr && !inblank(*lineptr)) - lineptr++; + while (*lineptr) { + if (*lineptr == Meta && lineptr[1]) + lineptr += 2; + else if (!inblank(*lineptr)) + lineptr++; + else + break; + } words[nwordpos++] = lineptr - start; } } while (*lineptr); diff --git a/Src/init.c b/Src/init.c index 871d46b12..ec21521b1 100644 --- a/Src/init.c +++ b/Src/init.c @@ -246,6 +246,9 @@ loop(int toplevel, int justonce) static int restricted; +/* original argv[0]. This is already metafied */ +static char *argv0; + /**/ static void parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr, @@ -257,7 +260,7 @@ parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr, if (**argv == '-') flags |= PARSEARGS_LOGIN; - argzero = posixzero = *argv++; + argv0 = argzero = posixzero = *argv++; SHIN = 0; /* @@ -500,10 +503,10 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp, } } break; - } else if (isspace(STOUC(**argv))) { + } else if (isspace((unsigned char) **argv)) { /* zsh's typtab not yet set, have to use ctype */ while (*++*argv) - if (!isspace(STOUC(**argv))) { + if (!isspace((unsigned char) **argv)) { badoptionstring: WARN_OPTION("bad option string: '%s'", args); return 1; @@ -697,11 +700,14 @@ init_io(char *cmd) * process group leader. */ mypid = (zlong)getpid(); - if (opts[MONITOR] && (SHTTY != -1)) { - origpgrp = GETPGRP(); - acquire_pgrp(); /* might also clear opts[MONITOR] */ - } else - opts[MONITOR] = 0; + if (opts[MONITOR]) { + if (SHTTY == -1) + opts[MONITOR] = 0; + else if (!origpgrp) { + origpgrp = GETPGRP(); + acquire_pgrp(); /* might also clear opts[MONITOR] */ + } + } #else opts[MONITOR] = 0; #endif @@ -747,7 +753,7 @@ init_shout(void) static char *tccapnams[TC_COUNT] = { "cl", "le", "LE", "nd", "RI", "up", "UP", "do", "DO", "dc", "DC", "ic", "IC", "cd", "ce", "al", "dl", "ta", - "md", "so", "us", "me", "se", "ue", "ch", + "md", "mh", "so", "us", "ZH", "me", "se", "ue", "ZR", "ch", "ku", "kd", "kl", "kr", "sc", "rc", "bc", "AF", "AB" }; @@ -872,10 +878,127 @@ init_term(void) /* The following is an attempt at a heuristic, * but it fails in some cases */ /* rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT)); */ + + /* if there's no termcap entry for italics, use CSI 3 m */ + if (!tccan(TCITALICSBEG)) { + zsfree(tcstr[TCITALICSBEG]); + tcstr[TCITALICSBEG] = ztrdup("\033[3m"); + tclen[TCITALICSBEG] = 4; + } + if (!tccan(TCITALICSEND)) { + zsfree(tcstr[TCITALICSEND]); + tcstr[TCITALICSEND] = ztrdup("\033[23m"); + tclen[TCITALICSEND] = 5; + } + if (!tccan(TCFAINTBEG)) { + zsfree(tcstr[TCFAINTBEG]); + tcstr[TCFAINTBEG] = ztrdup("\033[2m"); + tclen[TCFAINTBEG] = 4; + } } return 1; } +/* + * Get (or guess) the absolute pathname of the current zsh exeutable. + * Try OS-specific method, and if it fails, guess the absolute pathname + * from argv0, pwd, and PATH. 'name' and 'cwd' are unmetefied versions of + * argv0 and pwd. + * Returns a zalloc()ed string (not metafied), or NULL if failed. + */ +#ifdef __APPLE__ +#include <mach-o/dyld.h> +#endif + +/**/ +static char * +getmypath(const char *name, const char *cwd) +{ + char *buf; + int namelen; + + if (!name) + return NULL; + if (*name == '-') + ++name; + if ((namelen = strlen(name)) == 0) + return NULL; +#if defined(__APPLE__) + { + uint32_t n = PATH_MAX; + int ret; + buf = (char *)zalloc(PATH_MAX); + if ((ret = _NSGetExecutablePath(buf, &n)) < 0) { + /* try again with increased buffer size */ + buf = (char *)zrealloc(buf, n); + ret = _NSGetExecutablePath(buf, &n); + } + if (ret == 0 && strlen(buf) > 0) + return buf; + else + free(buf); + } +#elif defined(PROC_SELF_EXE) + { + ssize_t n; + buf = (char *)zalloc(PATH_MAX); + n = readlink(PROC_SELF_EXE, buf, PATH_MAX); + if (n > 0 && n < PATH_MAX) { + buf[n] = '\0'; + return buf; + } + else + free(buf); + } +#endif + /* guess the absolute pathname of 'name' */ + if (name[namelen-1] == '/') /* name should not end with '/' */ + return NULL; + else if (name[0] == '/') { + /* name is already an absolute pathname */ + return ztrdup(name); + } + else if (strchr(name, '/')) { + /* relative path */ + if (!cwd) + return NULL; + buf = (char *)zalloc(strlen(cwd) + namelen + 2); + sprintf(buf, "%s/%s", cwd, name); + return buf; + } +#ifdef HAVE_REALPATH + else { + /* search each dir in PARH */ + const char *path, *sep; + char *real, *try; + int pathlen, dirlen; + + path = getenv("PATH"); + if (!path || (pathlen = strlen(path)) == 0) + return NULL; + /* for simplicity, allocate buf even if REALPATH_ACCEPTS_NULL is on */ + buf = (char *)zalloc(PATH_MAX); + try = (char *)zalloc(pathlen + namelen + 2); + do { + sep = strchr(path, ':'); + dirlen = sep ? sep - path : strlen(path); + strncpy(try, path, dirlen); + try[dirlen] = '/'; + try[dirlen+1] = '\0'; + strcat(try, name); + real = realpath(try, buf); + if (sep) + path = sep + 1; + } while (!real && sep); + free(try); + if (!real) + free(buf); + return real; /* this may be NULL */ + } +#endif + return NULL; +} + /* Initialize lots of global variables and hash tables */ /**/ @@ -1067,9 +1190,12 @@ setupvals(char *cmd, char *runscript, char *zsh_name) ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS); wordchars = ztrdup(DEFAULT_WORDCHARS); postedit = ztrdup(""); - zunderscore = (char *) zalloc(underscorelen = 32); - underscoreused = 1; - *zunderscore = '\0'; + /* If _ is set in environment then initialize our $_ by copying it */ + zunderscore = getenv("_"); + zunderscore = zunderscore ? metafy(zunderscore, -1, META_DUP) : ztrdup(""); + underscoreused = strlen(zunderscore) + 1; + underscorelen = (underscoreused + 31) & ~31; + zunderscore = (char *)zrealloc(zunderscore, underscorelen); zoptarg = ztrdup(""); zoptind = 1; @@ -1089,8 +1215,8 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #ifdef USE_GETPWUID if ((pswd = getpwuid(cached_uid))) { if (EMULATION(EMULATE_ZSH)) - home = metafy(pswd->pw_dir, -1, META_DUP); - cached_username = ztrdup(pswd->pw_name); + home = ztrdup_metafy(pswd->pw_dir); + cached_username = ztrdup_metafy(pswd->pw_name); } else #endif /* USE_GETPWUID */ @@ -1175,6 +1301,18 @@ setupvals(char *cmd, char *runscript, char *zsh_name) /* Colour sequences for outputting colours in prompts and zle */ set_default_colour_sequences(); + /* ZSH_EXEPATH */ + { + char *mypath, *exename, *cwd; + exename = unmetafy(ztrdup(argv0), NULL); + cwd = pwd ? unmetafy(ztrdup(pwd), NULL) : NULL; + mypath = getmypath(exename, cwd); + free(exename); + free(cwd); + if (mypath) { + setsparam("ZSH_EXEPATH", metafy(mypath, -1, META_REALLOC)); + } + } if (cmd) setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd)); if (runscript) @@ -1244,6 +1382,9 @@ setupshin(char *runscript) void init_signals(void) { + sigtrapped = (int *) hcalloc(TRAPCOUNT * sizeof(int)); + siglists = (Eprog *) hcalloc(TRAPCOUNT * sizeof(Eprog)); + if (interact) { int i; signal_setmask(signal_mask(0)); @@ -1654,8 +1795,7 @@ VA_DCL lp = va_arg(ap, char **); - pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL, - NULL), + pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL), &pptlen); write_loop(2, pptbuf, pptlen); free(pptbuf); @@ -1724,9 +1864,9 @@ zsh_main(UNUSED(int argc), char **argv) * interactive */ typtab['\0'] |= IMETA; - typtab[STOUC(Meta) ] |= IMETA; - typtab[STOUC(Marker)] |= IMETA; - for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++) + typtab[(unsigned char) Meta ] |= IMETA; + typtab[(unsigned char) Marker] |= IMETA; + for (t0 = (int) (unsigned char) Pound; t0 <= (int) (unsigned char) Nularg; t0++) typtab[t0] |= ITOK | IMETA; for (t = argv; *t; *t = metafy(*t, -1, META_ALLOC), t++); diff --git a/Src/input.c b/Src/input.c index 9898a7177..d8ac2c0e7 100644 --- a/Src/input.c +++ b/Src/input.c @@ -220,7 +220,7 @@ shingetchar(void) int nread, rsize = isset(SHINSTDIN) ? 1 : SHINBUFSIZE; if (shinbufptr < shinbufendptr) - return STOUC(*shinbufptr++); + return (unsigned char) *shinbufptr++; shinbufreset(); #ifdef USE_LSEEK @@ -242,7 +242,7 @@ shingetchar(void) zerr("lseek(%d, %d): %e", SHIN, -(nread - rsize), errno); } else shinbufendptr = shinbuffer + nread; - return STOUC(*shinbufptr++); + return (unsigned char) *shinbufptr++; } #endif for (;;) { @@ -259,7 +259,7 @@ shingetchar(void) } if (shinbufendptr == shinbuffer) return -1; - return STOUC(*shinbufptr++); + return (unsigned char) *shinbufptr++; } /* Read a line from SHIN. Convert tokens and * @@ -328,7 +328,7 @@ ingetc(void) if (inbufleft) { inbufleft--; inbufct--; - if (itok(lastc = STOUC(*inbufptr++))) + if (itok(lastc = (unsigned char) *inbufptr++)) continue; if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n') lineno++; @@ -402,7 +402,7 @@ inputline(void) char *pptbuf; int pptlen; pptbuf = unmetafy(promptexpand(ingetcpmptl ? *ingetcpmptl : NULL, - 0, NULL, NULL, NULL), &pptlen); + 0, NULL, NULL), &pptlen); write_loop(2, pptbuf, pptlen); free(pptbuf); } @@ -610,11 +610,11 @@ inungetc(int c) } } -/* stuff a whole file into the input queue and print it */ +/* stuff a whole file into memory and return it */ /**/ -int -stuff(char *fn) +off_t +zstuff(char **out, const char *fn) { FILE *in; char *buf; @@ -622,20 +622,39 @@ stuff(char *fn) if (!(in = fopen(unmeta(fn), "r"))) { zerr("can't open %s", fn); - return 1; + return -1; } + queue_signals(); fseek(in, 0, SEEK_END); len = ftell(in); fseek(in, 0, SEEK_SET); buf = (char *)zalloc(len + 1); - if (!(fread(buf, len, 1, in))) { + if (len && !(fread(buf, len, 1, in))) { zerr("read error on %s", fn); fclose(in); zfree(buf, len + 1); - return 1; + unqueue_signals(); + return -1; } fclose(in); buf[len] = '\0'; + *out = buf; + unqueue_signals(); + return len; +} + +/* stuff a whole file into the input queue and print it */ + +/**/ +int +stuff(char *fn) +{ + char *buf; + off_t len = zstuff(&buf, fn); + + if (len < 0) + return 1; + fwrite(buf, len, 1, stderr); fflush(stderr); inputsetline(metafy(buf, len, META_REALLOC), INP_FREE); @@ -816,6 +835,7 @@ char *input_hasalias(void) { int flags = inbufflags; struct instacks *instackptr = instacktop; + char *alias_nam = NULL; for (;;) { @@ -824,9 +844,9 @@ char *input_hasalias(void) DPUTS(instackptr == instack, "BUG: continuation at bottom of instack"); instackptr--; if (instackptr->alias) - return instackptr->alias->node.nam; + alias_nam = instackptr->alias->node.nam; flags = instackptr->flags; } - return NULL; + return alias_nam; } diff --git a/Src/jobs.c b/Src/jobs.c index e0e453ed8..07facc60b 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -427,11 +427,17 @@ storepipestats(Job jn, int inforeground, int fixlastval) } if (fixlastval) { - if (jn->stat & STAT_CURSH) { - if (!lastval && isset(PIPEFAIL)) - lastval = pipefail; - } else if (isset(PIPEFAIL)) - lastval = pipefail; + if (jn->stat & STAT_CURSH) { + if (!lastval && isset(PIPEFAIL)) { + if (inforeground) + this_noerrexit = 0; + lastval = pipefail; + } + } else if (isset(PIPEFAIL)) { + if (inforeground) + this_noerrexit = 0; + lastval = pipefail; + } } } @@ -544,16 +550,14 @@ update_job(Job jn) if (isset(MONITOR)) { pid_t pgrp = gettygrp(); /* get process group of tty */ + int deadpgrp = (mypgrp != pgrp && inforeground && pgrp > 1 && + kill(-pgrp, 0) == -1 && errno == ESRCH); /* is this job in the foreground of an interactive shell? */ if (mypgrp != pgrp && inforeground && - (jn->gleader == pgrp || - (pgrp > 1 && - (kill(-pgrp, 0) == -1 && errno == ESRCH)))) { + ((jn->gleader == pgrp && signalled) || deadpgrp)) { if (list_pipe) { - if (somestopped || (pgrp > 1 && - kill(-pgrp, 0) == -1 && - errno == ESRCH)) { + if (somestopped || deadpgrp) { attachtty(mypgrp); /* check window size and adjust if necessary */ adjustwinsize(0); @@ -656,6 +660,25 @@ update_job(Job jn) } } +/**/ +void +update_bg_job(Job jn, pid_t pid, int status) +{ + /* + * Accumulate a list of older jobs. We only do this for + * background jobs, which is something in the job table + * that's not marked as in the current shell or as shell builtin + * and is not equal to the current foreground job. + */ + if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) && + jn - jobtab != thisjob) { + if (WIFEXITED(status)) + addbgstatus(pid, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + addbgstatus(pid, 0200 | WTERMSIG(status)); + } +} + /* set the previous job to something reasonable */ /**/ @@ -890,8 +913,13 @@ printtime(struct timeval *real, child_times_t *ti, char *desc) break; #endif #ifdef HAVE_STRUCT_RUSAGE_RU_MAXRSS +#ifdef RU_MAXRSS_IS_IN_BYTES +# define MAXRSS_IN_KB(x) ((x)/1024) +#else +# define MAXRSS_IN_KB(x) (x) +#endif case 'M': - fprintf(stderr, "%ld", ti->ru_maxrss / 1024); + fprintf(stderr, "%ld", MAXRSS_IN_KB(ti->ru_maxrss)); break; #endif #ifdef HAVE_STRUCT_RUSAGE_RU_MAJFLT @@ -1029,7 +1057,7 @@ should_report_time(Job j) return 1; #else { - clktck = get_clktck(); + long clktck = get_clktck(); if ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime) return 1; } @@ -1038,13 +1066,28 @@ should_report_time(Job j) #ifdef HAVE_GETRUSAGE if (reportmemory >= 0 && - j->procs->ti.ru_maxrss / 1024 > reportmemory) + MAXRSS_IN_KB(j->procs->ti.ru_maxrss) > reportmemory) return 1; #endif return 0; } +/**/ +char * +sigmsg(int sig) +{ + static char *unknown = "unknown signal"; +#if defined(SIGRTMIN) && defined(SIGRTMAX) + static char rtmsg[] = "real-time event XXX"; + if (sig >= SIGRTMIN && sig <= SIGRTMAX) { + sprintf(rtmsg + sizeof(rtmsg) - 4, "%u", sig - SIGRTMIN + 1); + return rtmsg; + } +#endif + return sig <= SIGCOUNT ? sig_msg[sig] : unknown; +} + /* !(lng & 3) means jobs * * (lng & 1) means jobs -l * * (lng & 2) means jobs -p @@ -1374,8 +1417,10 @@ cleanfilelists(void) DPUTS(shell_exiting >= 0, "BUG: cleanfilelists() before exit"); - for (i = 1; i <= maxjob; i++) + for (i = 1; i <= maxjob; i++) { deletefilelist(jobtab[i].filelist, 0); + jobtab[i].filelist = 0; + } } /**/ @@ -1488,10 +1533,7 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime, * set it for that, too. */ if (gleader != -1) { - if (jobtab[thisjob].stat & STAT_CURSH) - jobtab[thisjob].gleader = gleader; - else - jobtab[thisjob].gleader = pid; + jobtab[thisjob].gleader = gleader; if (list_pipe_job_used != -1) jobtab[list_pipe_job_used].gleader = gleader; /* @@ -1500,7 +1542,7 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime, */ last_attached_pgrp = gleader; } else if (!jobtab[thisjob].gleader) - jobtab[thisjob].gleader = pid; + jobtab[thisjob].gleader = pid; /* attach this process to end of process list of current job */ pnlist = &jobtab[thisjob].procs; } @@ -1536,8 +1578,10 @@ havefiles(void) int i; for (i = 1; i <= maxjob; i++) - if (jobtab[i].stat && jobtab[i].filelist) + if (jobtab[i].stat && jobtab[i].filelist && + peekfirst(jobtab[i].filelist)) { return 1; + } return 0; } @@ -2402,7 +2446,7 @@ bin_fg(char *name, char **argv, Options ops, int func) int curmaxjob, ignorejob; if (unset(MONITOR) && oldmaxjob) { jobptr = oldjobtab; - curmaxjob = oldmaxjob ? oldmaxjob - 1 : 0; + curmaxjob = oldmaxjob; ignorejob = 0; } else { jobptr = jobtab; @@ -2506,6 +2550,7 @@ bin_fg(char *name, char **argv, Options ops, int func) jobtab[job].stat &= ~STAT_CURSH; } if ((stopped = (jobtab[job].stat & STAT_STOPPED))) { + /* WIFCONTINUED will makerunning() again at killjb() */ makerunning(jobtab + job); if (func == BIN_BG) { /* Set $! to indicate this was backgrounded */ @@ -2664,11 +2709,37 @@ static const struct { int bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) { - int sig = SIGTERM; + int status, sig = SIGTERM; int returnval = 0; +#ifdef HAVE_SIGQUEUE + union sigval sigqueue_info; +#endif + int use_sigqueue = 0, got_sig = 0; + + while (*argv && **argv == '-') { + if (!use_sigqueue && (*argv)[1] == 'q' && (*argv)[2] == '\0') { + char *endp; + + if (!*++argv) { + zwarnnam(nam, "-q: argument expected"); + return 1; + } +#ifdef HAVE_SIGQUEUE + sigqueue_info.sival_int = +#endif + zstrtol(*argv, &endp, 10); + if (*endp) { + zwarnnam(nam, "invalid number: %s", *argv); + return 1; + } + use_sigqueue = 1; + argv++; + continue; + } + if (got_sig) + break; /* check for, and interpret, a signal specifier */ - if (*argv && **argv == '-') { if (idigit((*argv)[1])) { char *endp; /* signal specified by number */ @@ -2684,23 +2755,29 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) if ((*argv)[1] == 'l' && (*argv)[2] == '\0') { if (argv[1]) { while (*++argv) { - sig = zstrtol(*argv, &signame, 10); + status = zstrtol(*argv, &signame, 10); if (signame == *argv) { + signame = casemodify(signame, CASMOD_UPPER); if (!strncmp(signame, "SIG", 3)) signame += 3; for (sig = 1; sig <= SIGCOUNT; sig++) - if (!strcasecmp(sigs[sig], signame)) + if (!strcmp(sigs[sig], signame)) break; if (sig > SIGCOUNT) { int i; for (i = 0; alt_sigs[i].name; i++) - if (!strcasecmp(alt_sigs[i].name, signame)) + if (!strcmp(alt_sigs[i].name, signame)) { sig = alt_sigs[i].num; break; } } +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig > SIGCOUNT && (sig = rtsigno(signame))) { + printf("%d\n", sig); + } else +#endif if (sig > SIGCOUNT) { zwarnnam(nam, "unknown signal: SIG%s", signame); @@ -2713,14 +2790,15 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) signame); returnval++; } else { - if (WIFSIGNALED(sig)) - sig = WTERMSIG(sig); - else if (WIFSTOPPED(sig)) - sig = WSTOPSIG(sig); + sig = status & ~0200; if (1 <= sig && sig <= SIGCOUNT) printf("%s\n", sigs[sig]); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + else if ((signame = rtsigname(sig, 0))) + printf("%s\n", signame); +#endif else - printf("%d\n", sig); + printf("%d\n", status); } } } @@ -2729,10 +2807,45 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) printf("%s", sigs[1]); for (sig = 2; sig <= SIGCOUNT; sig++) printf(" %s", sigs[sig]); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++) + printf(" %s", rtsigname(sig, 0)); +#endif putchar('\n'); return 0; } + /* with argument "-L" list signals with their numbers in a table */ + if ((*argv)[1] == 'L' && (*argv)[2] == '\0') { +#if defined(SIGRTMIN) && defined(SIGRTMAX) + const int width = SIGRTMAX >= 100 ? 3 : 2; +#else + const int width = SIGCOUNT >= 100 ? 3 : 2; +#endif + const int cols = zterm_columns >= 30 ? + (zterm_columns < 90 ? zterm_columns / 15 : 6) : 1; + + for (sig = 1; sig < SIGCOUNT +#if defined(SIGRTMIN) && defined(SIGRTMAX) + + 1 +#endif + ; sig++) + { + printf("%*d %-10s%c", width, sig, sigs[sig], + sig % cols ? ' ' : '\n'); + } +#if defined(SIGRTMIN) && defined(SIGRTMAX) + for (sig = SIGRTMIN; sig < SIGRTMAX; sig++) { + printf("%*d %-10s%c", width, sig, rtsigname(sig, 0), + (sig - SIGRTMIN + SIGCOUNT + 1) % cols ? ' ' : '\n'); + } + printf("%*d RTMAX\n", width, sig); +#else + printf("%*d %s\n", width, sig, sigs[sig]); +#endif + return 0; + } + if ((*argv)[1] == 'n' && (*argv)[2] == '\0') { char *endp; @@ -2777,14 +2890,19 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) break; } } - if (sig > SIGCOUNT) { + if (sig > SIGCOUNT +#if defined(SIGRTMIN) && defined(SIGRTMAX) + && !(sig = rtsigno(signame)) +#endif + ) { zwarnnam(nam, "unknown signal: SIG%s", signame); - zwarnnam(nam, "type kill -l for a list of signals"); + zwarnnam(nam, "type kill -L for a list of signals"); return 1; } } } argv++; + got_sig = 1; } /* Discard the standard "-" and "--" option breaks */ @@ -2833,7 +2951,12 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) returnval++; } else { int pid = atoi(*argv); - if (kill(pid, sig) == -1) { + if ( +#ifdef HAVE_SIGQUEUE + use_sigqueue ? sigqueue(pid, sig, sigqueue_info) : +#endif + kill(pid, sig) == -1) + { zwarnnam("kill", "kill %s failed: %e", *argv, errno); returnval++; } @@ -2854,18 +2977,19 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) return returnval < 126 ? returnval : 1; } -/* Get a signal number from a string */ + +/* Get index into table of traps from a string describing a signal */ /**/ mod_export int -getsignum(const char *s) +getsigidx(const char *s) { int x, i; /* check for a signal specified by number */ x = atoi(s); - if (idigit(*s) && x >= 0 && x < VSIGCOUNT) - return x; + if (idigit(*s) && x >= 0) + return SIGIDX(x); /* search for signal by name */ if (!strncmp(s, "SIG", 3)) @@ -2881,11 +3005,16 @@ getsignum(const char *s) return alt_sigs[i].num; } +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if ((x = rtsigno(s))) + return SIGIDX(x); +#endif + /* no matching signal */ return -1; } -/* Get the name for a signal. */ +/* Get the name for a signal given the index into the traps table. */ /**/ mod_export const char * @@ -2899,6 +3028,11 @@ getsigname(int sig) return alt_sigs[i].name; } else +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig >= VSIGCOUNT) + return rtsigname(SIGNUM(sig), 0); + else +#endif return sigs[sig]; /* shouldn't reach here */ @@ -2923,10 +3057,22 @@ gettrapnode(int sig, int ignoredisable) else getptr = shfunctab->getnode; +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig >= VSIGCOUNT) + sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 0)); + else +#endif sprintf(fname, "TRAP%s", sigs[sig]); if ((hn = getptr(shfunctab, fname))) return hn; +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig >= VSIGCOUNT) { + sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 1)); + return getptr(shfunctab, fname); + } +#endif + for (i = 0; alt_sigs[i].name; i++) { if (alt_sigs[i].num == sig) { sprintf(fname, "TRAP%s", alt_sigs[i].name); diff --git a/Src/lex.c b/Src/lex.c index ece02659e..700af2da1 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -423,7 +423,7 @@ initlextabs(void) for (t0 = 0; lx2[t0]; t0++) lexact2[(int)lx2[t0]] = t0; lexact2['&'] = LX2_BREAK; - lexact2[STOUC(Meta)] = LX2_META; + lexact2[(unsigned char) Meta] = LX2_META; lextok2['*'] = Star; lextok2['?'] = Quest; lextok2['{'] = Inbrace; @@ -722,7 +722,7 @@ gettok(void) } return peek; } - switch (lexact1[STOUC(c)]) { + switch (lexact1[(unsigned char) c]) { case LX1_BKSLASH: d = hgetc(); if (d == '\n') @@ -937,8 +937,8 @@ static enum lextok gettokstr(int c, int sub) { int bct = 0, pct = 0, brct = 0, seen_brct = 0, fdpar = 0; - int intpos = 1, in_brace_param = 0; - int inquote, unmatched = 0; + int intpos = 1, in_brace_param = 0, cmdsubst = 0; + int inquote, unmatched = 0, in_pattern = 0; enum lextok peek; #ifdef DEBUG int ocmdsp = cmdsp; @@ -960,8 +960,8 @@ gettokstr(int c, int sub) if (inbl && !in_brace_param && !pct) act = LX2_BREAK; else { - act = lexact2[STOUC(c)]; - c = lextok2[STOUC(c)]; + act = lexact2[(unsigned char) c]; + c = lextok2[(unsigned char) c]; } switch (act) { case LX2_BREAK: @@ -1135,7 +1135,7 @@ gettokstr(int c, int sub) c = Inpar; break; case LX2_INBRACE: - if (isset(IGNOREBRACES) || sub) + if ((isset(IGNOREBRACES) && !cmdsubst) || sub) c = '{'; else { if (!lexbuf.len && incmdpos) { @@ -1157,8 +1157,11 @@ gettokstr(int c, int sub) if (in_brace_param) { cmdpop(); } - if (bct-- == in_brace_param) - in_brace_param = 0; + if (bct-- == in_brace_param) { + if (cmdsubst) + cmdpop(); + in_brace_param = cmdsubst = in_pattern = 0; + } c = Outbrace; break; case LX2_COMMA: @@ -1230,7 +1233,7 @@ gettokstr(int c, int sub) else { int sav = *lexbuf.ptr; *lexbuf.ptr = '\0'; - t = itype_end(t, IIDENT, 0); + t = itype_end(t, INAMESPC, 0); if (t < lexbuf.ptr) { skipparens(Inbrack, Outbrack, &t); } else { @@ -1263,7 +1266,7 @@ gettokstr(int c, int sub) continue; } else { add(Bnull); - if (c == STOUC(Meta)) { + if (c == (unsigned char) Meta) { c = hgetc(); #ifdef DEBUG if (lexstop) { @@ -1306,7 +1309,8 @@ gettokstr(int c, int sub) lexbuf.ptr--, lexbuf.len--; else break; - } + } else if (in_pattern && c == '/') + add(Bnull); add(c); } ALLOWHIST @@ -1394,27 +1398,45 @@ gettokstr(int c, int sub) */ c = Dash; break; - case LX2_BANG: - /* - * Same logic as Dash, for ! to perform negation in range. - */ - if (seen_brct) - c = Bang; - else - c = '!'; - } - add(c); - c = hgetc(); + case LX2_BANG: + /* + * Same logic as Dash, for ! to perform negation in range. + */ + if (seen_brct) + c = Bang; + else + c = '!'; + case LX2_OTHER: + if (in_brace_param) { + if (c == '/') { + if (in_pattern == 0) + in_pattern = 2; + else + --in_pattern; + } + } + } + add(c); + c = hgetc(); if (intpos) intpos--; if (lexstop) break; + if (!cmdsubst && in_brace_param && act == LX2_STRING && + (c == '|' || c == Bar || c == '{' || c == Inbrace || inblank(c))) { + cmdsubst = in_brace_param; + cmdpush(CS_CURSH); + } else if (in_pattern == 2 && c != '/') + in_pattern = 1; } brk: if (errflag) { if (in_brace_param) { - while(bct-- >= in_brace_param) + while(bct >= in_brace_param) { + if (bct-- == cmdsubst) + cmdpop(); cmdpop(); + } } return LEXERR; } @@ -1422,17 +1444,28 @@ gettokstr(int c, int sub) if (unmatched && !(lexflags & LEXFLAGS_ACTIVE)) zerr("unmatched %c", unmatched); if (in_brace_param) { - while(bct-- >= in_brace_param) + while(bct >= in_brace_param) { + if (bct-- == cmdsubst) + cmdpop(); cmdpop(); + } zerr("closing brace expected"); } else if (unset(IGNOREBRACES) && !sub && lexbuf.len > 1 && peek == STRING && lexbuf.ptr[-1] == '}' && lexbuf.ptr[-2] != Bnull) { /* hack to get {foo} command syntax work */ + /* + * Alias expansion when parsing command substitution means that + * the case for raw lexical analysis may not be the same. + * (Just go with it, OK?) + */ + int lar = lex_add_raw; + lex_add_raw = lexbuf_raw.len > 0 && lexbuf_raw.ptr[-1] == '}'; lexbuf.ptr--; lexbuf.len--; lexstop = 0; hungetc('}'); + lex_add_raw = lar; } *lexbuf.ptr = '\0'; DPUTS(cmdsp != ocmdsp, "BUG: gettok: cmdstack changed."); @@ -1451,8 +1484,8 @@ gettokstr(int c, int sub) static int dquote_parse(char endchar, int sub) { - int pct = 0, brct = 0, bct = 0, intick = 0, err = 0; - int c; + int pct = 0, brct = 0, bct = 0, intick = 0, err = 0, cmdsubst = 0; + int c, bskip = 0; int math = endchar == ')' || endchar == ']' || infor; int zlemath = math && zlemetacs > zlemetall + addedx - inbufct; @@ -1521,11 +1554,25 @@ dquote_parse(char endchar, int sub) c = Qstring; } break; + case '{': + if (cmdsubst && !intick) { + /* In nofork substitution, tokenize as if unquoted */ + c = Inbrace; + bskip++; + } + break; case '}': if (intick || !bct) break; c = Outbrace; - bct--; + if (bskip) { + bskip--; + break; + } + if (bct-- == cmdsubst) { + cmdsubst = 0; + cmdpop(); + } cmdpop(); break; case '`': @@ -1580,14 +1627,34 @@ dquote_parse(char endchar, int sub) if (err || lexstop) break; add(c); + if (!cmdsubst && c == Inbrace) { + /* Check for ${|...} nofork command substitution */ + if ((c = hgetc()) && !lexstop) { + if (c == '|' || inblank(c)) { + cmdsubst = bct; + cmdpush(CS_CURSH); + } + hungetc(c); + } + } } if (intick == 2) ALLOWHIST if (intick) { cmdpop(); } - while (bct--) + while (bct) { + if (bct-- == cmdsubst) { + /* + * You would think this is an error, but if we call it one, + * parsestrnoerr() returns nonzero to subst_parse_str() and + * subsequently "bad substitution" is not reported + */ + /* err = 1 */ + cmdpop(); + } cmdpop(); + } if (lexstop) err = intick || endchar || err; else if (err == 1) { diff --git a/Src/loop.c b/Src/loop.c index db5b3e097..0f3847541 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -53,7 +53,7 @@ execfor(Estate state, int do_exec) wordcode code = state->pc[-1]; int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0; int last = 0; - char *name, *str, *cond = NULL, *advance = NULL; + char *str, *cond = NULL, *advance = NULL; zlong val = 0; LinkList vars = NULL, args = NULL; int old_simple_pline = simple_pline; @@ -151,7 +151,7 @@ execfor(Estate state, int do_exec) int count = 0; for (node = firstnode(vars); node; incnode(node)) { - name = (char *)getdata(node); + char *name = (char *)getdata(node); if (!args || !(str = (char *) ugetnode(args))) { if (count) { @@ -165,7 +165,7 @@ execfor(Estate state, int do_exec) fprintf(xtrerr, "%s=%s\n", name, str); fflush(xtrerr); } - setsparam(name, ztrdup(str)); + setloopvar(name, ztrdup(str)); count++; } if (!count) @@ -282,7 +282,7 @@ execselect(Estate state, UNUSED(int do_exec)) /* Keep any user interrupt error status */ errflag = oef | (errflag & ERRFLAG_INT); } else { - str = promptexpand(prompt3, 0, NULL, NULL, NULL); + str = promptexpand(prompt3, 0, NULL, NULL); zputs(str, stderr); free(str); fflush(stderr); @@ -428,7 +428,7 @@ execwhile(Estate state, UNUSED(int do_exec)) } else { for (;;) { state->pc = loop; - noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN; /* In case the test condition is a functional no-op, * make sure signal handlers recognize ^C to end the loop. */ @@ -569,23 +569,14 @@ execif(Estate state, int do_exec) s = 1; state->pc = next; } + noerrexit = olderrexit; if (run) { - /* we need to ignore lastval until we reach execcmd() */ - if (olderrexit || run == 2) - noerrexit = olderrexit; - else if (lastval) - noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_UNTIL_EXEC; - else - noerrexit &= ~ (NOERREXIT_EXIT | NOERREXIT_RETURN); cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN)); execlist(state, 1, do_exec); cmdpop(); - } else { - noerrexit = olderrexit; - if (!retflag && !errflag) - lastval = 0; - } + } else if (!retflag && !errflag) + lastval = 0; state->pc = end; this_noerrexit = 1; @@ -793,6 +784,7 @@ exectry(Estate state, int do_exec) cmdpop(); popheap(); state->pc = end; + this_noerrexit = 1; return endval; } diff --git a/Src/makepro.awk b/Src/makepro.awk index f69660531..56c4f4595 100644 --- a/Src/makepro.awk +++ b/Src/makepro.awk @@ -121,7 +121,7 @@ BEGIN { # initialiser. dcltor = substr(line, 1, RLENGTH-1) line = substr(line, RLENGTH+1) - sub(/=.*$/, "", dcltor) + sub(/\075.*$/, "", dcltor) match(dcltor, /^([^_0-9A-Za-z]| const )*/) dcltor = substr(dcltor, 1, RLENGTH) "@+" substr(dcltor, RLENGTH+1) match(dcltor, /^.*@\+[_0-9A-Za-z]+/) @@ -131,8 +131,8 @@ BEGIN { sub(/@-.*$/, "", dnam) # Put parens etc. back - gsub(/@[{]/, " _((", dcltor) - gsub(/@}/, "))", dcltor) + gsub(/@[{]/, " (", dcltor) + gsub(/@}/, ")", dcltor) gsub(/@</, "(", dcltor) gsub(/@>/, ")", dcltor) gsub(/@!/, ",", dcltor) diff --git a/Src/math.c b/Src/math.c index 777ad9c31..d97dae238 100644 --- a/Src/math.c +++ b/Src/math.c @@ -352,6 +352,8 @@ getmathparam(struct mathvalue *mptr) } return zero_mnumber; } + if (errflag) + return zero_mnumber; } result = getnumvalue(mptr->pval); if (isset(FORCEFLOAT) && result.type == MN_INTEGER) { @@ -641,7 +643,9 @@ zzlex(void) return MINUSEQ; } if (unary) { - if (idigit(*ptr) || *ptr == '.') { + if (idigit(*ptr) || + (*ptr == '.' && + (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0)))) { int ctype = lexconstant(); if (ctype == NUM) { @@ -835,7 +839,9 @@ zzlex(void) case Dnull: break; default: - if (idigit(*--ptr) || *ptr == '.') + if (idigit(*--ptr) || + (*ptr == '.' && + (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0)))) return lexconstant(); if (*ptr == '#') { if (*++ptr == '\\' || *ptr == '#') { @@ -857,7 +863,7 @@ zzlex(void) } cct = 1; } - if ((ie = itype_end(ptr, IIDENT, 0)) != ptr) { + if ((ie = itype_end(ptr, INAMESPC, 0)) != ptr) { int func = 0; char *p; @@ -955,7 +961,7 @@ getcvar(char *s) } } #endif - mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t); + mn.u.l = (unsigned char) (*t == Meta ? t[1] ^ 32 : *t); } unqueue_signals(); return mn; @@ -1363,8 +1369,11 @@ op(int what) } spval = &stack[sp].val; - if (stack[sp].val.type == MN_UNSET) + if (stack[sp].val.type == MN_UNSET) { *spval = getmathparam(stack + sp); + if (errflag) + return; + } switch (what) { case NOT: if (spval->type & MN_FLOAT) { @@ -1548,21 +1557,32 @@ checkunary(int mtokc, char *mptr) errmsg = 2; } if (errmsg) { - int len, over = 0; + int len = 0, over = 0; char *errtype = errmsg == 2 ? "operator" : "operand"; while (inblank(*mptr)) mptr++; - len = ztrlen(mptr); - if (len > 10) { - len = 10; - over = 1; + if (isset(MULTIBYTE)) + MB_CHARINIT(); + while (over < 10 && mptr[len]) { + if (isset(MULTIBYTE)) + len += MB_METACHARLEN(mptr+len); + else + len += (mptr[len] == Meta ? 2 : 1); + ++over; + } + if ((over = mptr[len])) { + mptr = dupstring(mptr); + if (mptr[len] == Meta) + mptr[len+1] = 0; + else + mptr[len] = 0; } if (!*mptr) zerr("bad math expression: %s expected at end of string", errtype); else - zerr("bad math expression: %s expected at `%l%s'", - errtype, mptr, len, over ? "..." : ""); + zerr("bad math expression: %s expected at `%s%s'", + errtype, mptr, over ? "..." : ""); } unary = !(tp & OP_OPF); } diff --git a/Src/mem.c b/Src/mem.c index fb4be47bf..0b6f76e46 100644 --- a/Src/mem.c +++ b/Src/mem.c @@ -1057,17 +1057,17 @@ zrealloc(void *ptr, size_t size) #if !defined(__hpux) && !defined(DGUX) && !defined(__osf__) # if defined(_BSD) # ifndef HAVE_BRK_PROTO - extern int brk _((caddr_t)); + extern int brk (caddr_t); # endif # ifndef HAVE_SBRK_PROTO - extern caddr_t sbrk _((int)); + extern caddr_t sbrk (int); # endif # else # ifndef HAVE_BRK_PROTO - extern int brk _((void *)); + extern int brk (void *); # endif # ifndef HAVE_SBRK_PROTO - extern void *sbrk _((int)); + extern void *sbrk (int); # endif # endif #endif diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh index c4611d8b3..1994ace60 100644 --- a/Src/mkbltnmlst.sh +++ b/Src/mkbltnmlst.sh @@ -76,6 +76,30 @@ for x_mod in $x_mods; do test "x$linked" = xno && echo "#endif" done +# if dynamic module 'mod' with load=no has moddeps in its .mdd, +# then output add_dep(mod, dep) for each 'dep' in moddeps. +dyn_mods="`grep ' link=dynamic .* load=no ' $CFMOD | \ + sed -e '/^#/d' -e 's/ .*/ /' -e 's/^name=/ /'`" + +for mod in $dyn_mods; do + modfile="`grep '^name='$mod' ' $CFMOD | \ + sed -e 's/^.* modfile=//' -e 's/ .*//'`" + if test "x$modfile" = x; then + echo >&2 "WARNING: no name for \`$mod' in $CFMOD (ignored)" + continue + fi + unset moddeps + . $srcdir/../$modfile + if test -n "$moddeps"; then + echo '#ifdef DYNAMIC' + echo "/* non-linked-in known module \`$mod' */" + for dep in $moddeps; do + echo " add_dep(\"$mod\", \"$dep\");" + done + echo '#endif' + fi +done + echo done_mods=" " for bin_mod in $bin_mods; do @@ -98,12 +122,12 @@ for bin_mod in $bin_mods; do esac done echo " {" - echo " extern int setup_${q_bin_mod} _((Module));" - echo " extern int boot_${q_bin_mod} _((Module));" - echo " extern int features_${q_bin_mod} _((Module,char***));" - echo " extern int enables_${q_bin_mod} _((Module,int**));" - echo " extern int cleanup_${q_bin_mod} _((Module));" - echo " extern int finish_${q_bin_mod} _((Module));" + echo " extern int setup_${q_bin_mod} (Module);" + echo " extern int boot_${q_bin_mod} (Module);" + echo " extern int features_${q_bin_mod} (Module,char***);" + echo " extern int enables_${q_bin_mod} (Module,int**);" + echo " extern int cleanup_${q_bin_mod} (Module);" + echo " extern int finish_${q_bin_mod} (Module);" echo echo " register_module(\"$bin_mod\"," echo " setup_${q_bin_mod}," diff --git a/Src/modentry.c b/Src/modentry.c index 4d8217f43..23c499d94 100644 --- a/Src/modentry.c +++ b/Src/modentry.c @@ -1,10 +1,10 @@ #include "zsh.mdh" -int setup_ _((Module)); -int boot_ _((Module)); -int cleanup_ _((Module)); -int finish_ _((Module)); -int modentry _((int boot, Module m, void *ptr)); +int setup_ (Module); +int boot_ (Module); +int cleanup_ (Module); +int finish_ (Module); +int modentry (int boot, Module m, void *ptr); /**/ int diff --git a/Src/module.c b/Src/module.c index bab4d8d73..b4b5d0a2c 100644 --- a/Src/module.c +++ b/Src/module.c @@ -356,7 +356,7 @@ finish_(UNUSED(Module m)) /**/ void -register_module(char *n, Module_void_func setup, +register_module(const char *n, Module_void_func setup, Module_features_func features, Module_enables_func enables, Module_void_func boot, @@ -846,7 +846,7 @@ Hookdef hooktab; /**/ Hookdef -gethookdef(char *n) +gethookdef(const char *n) { Hookdef p; @@ -974,7 +974,7 @@ deletehookdeffunc(Hookdef h, Hookfn f) /**/ mod_export int -deletehookfunc(char *n, Hookfn f) +deletehookfunc(const char *n, Hookfn f) { Hookdef h = gethookdef(n); @@ -1198,6 +1198,7 @@ add_autoparam(const char *module, const char *pnam, int flags) { Param pm; int ret; + int ne = noerrs; queue_signals(); if ((ret = checkaddparam(pnam, (flags & FEAT_IGNORE)))) { @@ -1212,14 +1213,18 @@ add_autoparam(const char *module, const char *pnam, int flags) return ret == 2 ? 0 : -1; } - pm = setsparam(dupstring(pnam), ztrdup(module)); - - pm->node.flags |= PM_AUTOLOAD; - if (flags & FEAT_AUTOALL) - pm->node.flags |= PM_AUTOALL; + noerrs = 2; + if ((pm = setsparam(dupstring(pnam), ztrdup(module)))) { + pm->node.flags |= PM_AUTOLOAD; + if (flags & FEAT_AUTOALL) + pm->node.flags |= PM_AUTOALL; + ret = 0; + } else + ret = -1; + noerrs = ne; unqueue_signals(); - return 0; + return ret; } /* Remove a parameter added with add_autoparam() */ @@ -1761,7 +1766,7 @@ dyn_finish_module(Module m) #else static Module_generic_func -module_func(Module m, char *name) +module_func(Module m, const char *name) { #ifdef DYNAMIC_NAME_CLASH_OK return (Module_generic_func) dlsym(m->u.handle, name); @@ -2438,7 +2443,7 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) int ops_au = OPT_ISSET(ops,'a') || OPT_ISSET(ops,'u'); int ret = 1, autoopts; /* options only allowed with -F */ - char *fonly = "lP", *fp; + const char *fonly = "lP", *fp; if (ops_bcpf && !ops_au) { zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u"); @@ -2474,7 +2479,7 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } for (fp = fonly; *fp; fp++) { - if (OPT_ISSET(ops,STOUC(*fp)) && !OPT_ISSET(ops,'F')) { + if (OPT_ISSET(ops,(unsigned char) *fp) && !OPT_ISSET(ops,'F')) { zwarnnam(nam, "-%c is only allowed with -F", *fp); return 1; } @@ -3177,7 +3182,7 @@ bin_zmodload_features(const char *nam, char **args, Options ops) } else if (OPT_ISSET(ops, 'L')) printf("zmodload -F %s ", m->node.nam); for (fp = features, ep = enables; *fp; fp++, ep++) { - char *onoff; + const char *onoff; int term; if (*args) { char **argp; @@ -3447,7 +3452,8 @@ autofeatures(const char *cmdnam, const char *module, char **features, defm = NULL; for (; *features; features++) { - char *fnam, *typnam, *feature; + char *fnam, *feature; + const char *typnam; int add, fchar, flags = defflags; autofeaturefn_t fn; diff --git a/Src/options.c b/Src/options.c index a1fe918fc..a0e1aa024 100644 --- a/Src/options.c +++ b/Src/options.c @@ -881,11 +881,12 @@ dosetopt(int optno, int value, int force, char *new_opts) } else if (!force && optno == MONITOR && value) { if (new_opts[optno] == value) return 0; - if (SHTTY != -1) { + if (SHTTY == -1) + return -1; + if (!origpgrp) { origpgrp = GETPGRP(); acquire_pgrp(); - } else - return -1; + } #else } else if(optno == MONITOR && value) { return -1; @@ -904,7 +905,11 @@ dosetopt(int optno, int value, int force, char *new_opts) keyboardhackchar = (value ? '`' : '\0'); } new_opts[optno] = value; - if (optno == BANGHIST || optno == SHINSTDIN) + if ( +#ifdef MULTIBYTE_SUPPORT + optno == MULTIBYTE || +#endif + optno == BANGHIST || optno == SHINSTDIN) inittyptab(); return 0; } diff --git a/Src/params.c b/Src/params.c index 27ea82298..f65aa1e80 100644 --- a/Src/params.c +++ b/Src/params.c @@ -475,6 +475,15 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \ ((V) && (!(V)->pm || ((V)->pm->node.flags & PM_UNSET) || \ !(V)->pm->node.nam || !*(V)->pm->node.nam)) +/* + * For named references. Simple named references are just like scalars + * for efficiency, but special named references need get/set functions. + */ +#define GETREFNAME(PM) (((PM)->node.flags & PM_SPECIAL) ? \ + (PM)->gsu.s->getfn(PM) : (PM)->u.str) +#define SETREFNAME(PM,S) (((PM)->node.flags & PM_SPECIAL) ? \ + (PM)->gsu.s->setfn(PM,(S)) : ((PM)->u.str = (S))) + static Param argvparam; /* "parameter table" - hash table containing the parameters @@ -520,7 +529,7 @@ getparamnode(HashTable ht, const char *nam) HashNode hn = gethashnode2(ht, nam); Param pm = (Param) hn; - if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) { + if (pm && (pm->node.flags & PM_AUTOLOAD) && pm->u.str) { char *mn = dupstring(pm->u.str); (void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL : @@ -536,6 +545,9 @@ getparamnode(HashTable ht, const char *nam) nam); } } + + if (hn && ht == realparamtab && !(hn->flags & PM_UNSET)) + hn = resolve_nameref((Param)hn, NULL); return hn; } @@ -732,7 +744,7 @@ split_env_string(char *env, char **name, char **value) tenv = strcpy(zhalloc(strlen(env) + 1), env); for (str = tenv; *str && *str != '='; str++) { - if (STOUC(*str) >= 128) { + if ((unsigned char) *str >= 128) { /* * We'll ignore environment variables with names not * from the portable character set since we don't @@ -838,12 +850,11 @@ createparamtable(void) setsparam("HOST", ztrdup_metafy(hostnam)); zfree(hostnam, 256); - setsparam("LOGNAME", ztrdup_metafy( + setsparam("LOGNAME", #ifndef DISABLE_DYNAMIC_NSS - (str = getlogin()) && *str ? str : + (str = getlogin()) && *str ? ztrdup_metafy(str) : #endif - cached_username - )); + ztrdup(cached_username)); #if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV) /* Copy the environment variables we are inheriting to dynamic * @@ -935,8 +946,18 @@ createparamtable(void) setsparam("ZSH_ARGZERO", ztrdup(posixzero)); setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION)); setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL)); - setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *))); - for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); ); + setaparam("signals", sigptr = zalloc((TRAPCOUNT + 1) * sizeof(char *))); + t = sigs; +#if defined(SIGRTMIN) && defined(SIGRTMAX) + while (t - sigs <= SIGCOUNT) + *sigptr++ = ztrdup_metafy(*t++); + { + int sig; + for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++) + *sigptr++ = ztrdup_metafy(rtsigname(sig, 0)); + } +#endif + while ((*sigptr++ = ztrdup_metafy(*t++))) /* empty */ ; noerrs = 0; } @@ -993,6 +1014,62 @@ createparam(char *name, int flags) gethashnode2(paramtab, name) : paramtab->getnode(paramtab, name)); + if (oldpm && (oldpm->node.flags & PM_RO_BY_DESIGN)) { + if (!(flags & PM_LOCAL)) { + /* Must call the API for namerefs and specials to work */ + pm = (Param) paramtab->getnode2(paramtab, oldpm->node.nam); + if (!pm || ((pm->node.flags & PM_NAMEREF) && + pm->level != locallevel)) { + zerr("%s: can't modify read-only parameter", name); + return NULL; + } + } + /** + * Implementation note: In the case of a readonly nameref, + * the right thing might be to insert a new global into + * the paramtab and point the local pm->old at it, rather + * than error. That is why gethashnode2() is called + * first, to avoid skipping up the stack prematurely. + **/ + } + + if (oldpm && !(flags & PM_NAMEREF) && + (oldpm->level == locallevel ? + !(oldpm->node.flags & PM_RO_BY_DESIGN) : !(flags & PM_LOCAL)) && + (oldpm->node.flags & PM_NAMEREF) && + (oldpm = upscope(oldpm, oldpm->base))) { + Param lastpm; + struct asgment stop; + stop.flags = PM_NAMEREF | (flags & PM_LOCAL); + stop.name = oldpm->node.nam; + stop.value.scalar = GETREFNAME(oldpm); + lastpm = (Param)resolve_nameref(oldpm, &stop); + if (lastpm) { + if (lastpm->node.flags & PM_NAMEREF) { + char *refname = GETREFNAME(lastpm); + if (refname && *refname) { + name = refname; + oldpm = NULL; + } else { + if (!(lastpm->node.flags & PM_READONLY)) { + if (flags) { + /* Only plain scalar assignment allowed */ + zerr("%s: can't change type of named reference", + name); /* Differs from ksh93u+ */ + return NULL; + } + } + return lastpm; + } + } else { + /* nameref pointing to an unset local */ + DPUTS(!(lastpm->node.flags & PM_UNSET), + "BUG: local parameter is not unset"); + oldpm = lastpm; + } + } + } + DPUTS(oldpm && oldpm->level > locallevel, "BUG: old local parameter not deleted"); if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) { @@ -1009,7 +1086,7 @@ createparam(char *name, int flags) /* POSIXBUILTINS horror: we need to retain 'export' flags */ (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) { if (oldpm->node.flags & PM_RO_BY_DESIGN) { - zerr("%s: can't change parameter attribute", + zerr("%s: can't modify read-only parameter", name); return NULL; } @@ -1185,6 +1262,26 @@ isident(char *s) if (!*s) /* empty string is definitely not valid */ return 0; + /* This partly duplicates code in itype_end(), but we need to + * distinguish the leading namespace at this point to check the + * correctness of the identifier that follows + */ + if (*s == '.') { + if (idigit(s[1])) + return 0; /* Namespace must not start with a digit */ + /* Reject identifiers beginning with a digit in namespaces. + * Move this out below this block to also reject v.1x form. + */ + if ((ss = itype_end(s + (*s == '.'), IIDENT, 0))) { + if (*ss == '.') { + if (!ss[1]) + return 0; + if (idigit(ss[1])) + s = ss + 1; + } + } + } + if (idigit(*s)) { /* If the first character is `s' is a digit, then all must be */ for (ss = ++s; *ss; ss++) @@ -1192,7 +1289,7 @@ isident(char *s) break; } else { /* Find the first character in `s' not in the iident type table */ - ss = itype_end(s, IIDENT, 0); + ss = itype_end(s, INAMESPC, 0); } /* If the next character is not [, then it is * @@ -1262,7 +1359,6 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, /* first parse any subscription flags */ if (v->pm && (*s == '(' || *s == Inpar)) { int escapes = 0; - int waste; for (s++; *s != ')' && *s != Outpar && s != *str; s++) { switch (*s) { case 'r': @@ -1339,8 +1435,13 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, sav = *t; *t = '\0'; s += arglen; - sep = escapes ? getkeystring(s, &waste, GETKEYS_SEP, NULL) - : dupstring(s); + if (escapes) { + int len; + sep = getkeystring(s, &len, GETKEYS_SEP, NULL); + sep = metafy(sep, len, META_HREALLOC); + } + else + sep = dupstring(s); *t = sav; s = t + arglen - 1; break; @@ -1432,10 +1533,14 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, if (ishash && (keymatch || !rev)) remnulargs(s); if (needtok) { + char exe = opts[EXECOPT]; s = dupstring(s); if (parsestr(&s)) return 0; + if (flags & SCANPM_NOEXEC) + opts[EXECOPT] = 0; singsub(&s); + opts[EXECOPT] = exe; } else if (rev) remnulargs(s); /* This is probably always a no-op, but ... */ if (!rev) { @@ -1665,7 +1770,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, /* Searching characters */ int slen; d = getstrvalue(v); - if (!d || !*d) + if (!d) return 0; /* * beg and len are character counts, not raw offsets. @@ -2047,6 +2152,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) char *s, *t, *ie; char sav, c; int ppar = 0; + int itype = (flags & SCANPM_NONAMESPC) ? IIDENT : INAMESPC; s = t = *pptr; @@ -2056,7 +2162,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) else ppar = *s++ - '0'; } - else if ((ie = itype_end(s, IIDENT, 0)) != s) + else if ((ie = itype_end(s, itype, 0)) != s) s = ie; else if (c == Quest) *s++ = '?'; @@ -2094,8 +2200,16 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) int isvarat; isvarat = (t[0] == '@' && !t[1]); - pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); - if (sav) + if (flags & SCANPM_NONAMEREF) + pm = (Param) paramtab->getnode2(paramtab, *t == '0' ? "0" : t); + else + pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); + if (!pm && *t == '.' && !isident(t)) { + /* badly formed namespace reference */ + if (sav) + *s = sav; + return NULL; + } else if (sav) *s = sav; *pptr = s; if (!pm || ((pm->node.flags & PM_UNSET) && @@ -2105,6 +2219,29 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) memset(v, 0, sizeof(*v)); else v = (Value) hcalloc(sizeof *v); + if ((pm->node.flags & PM_NAMEREF) && !(flags & SCANPM_NONAMEREF)) { + char *refname = GETREFNAME(pm); + if (refname && *refname) { + /* only happens for namerefs pointing to array elements */ + char *ref = dupstring(refname); + char *ss = pm->width ? ref + pm->width : NULL; + if (ss) { + sav = *ss; + *ss = 0; + } + Param p1 = (Param)gethashnode2(paramtab, ref); + if (!(p1 && (pm = upscope(p1, pm->base))) || + ((pm->node.flags & PM_UNSET) && + !(pm->node.flags & PM_DECLARED))) + return NULL; + if (ss) { + flags |= SCANPM_NOEXEC; + *ss = sav; + s = dyncat(ss,*pptr); + } else + s = *pptr; + } + } if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { /* Overload v->isarr as the flag bits for hashed arrays. */ v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0); @@ -2124,7 +2261,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) return v; } } else if (!(flags & SCANPM_ASSIGNING) && v->isarr && - itype_end(t, IIDENT, 1) != t && isset(KSHARRAYS)) + itype_end(t, INAMESPC, 1) != t && isset(KSHARRAYS)) v->end = 1, v->isarr = 0; } if (!bracks && *s) @@ -2673,6 +2810,7 @@ assignstrvalue(Value v, char *val, int flags) } break; } + setscope(v->pm); if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) && !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) || (v->pm->node.flags & PM_ARRAY) || v->pm->ename) @@ -3008,7 +3146,7 @@ check_warn_pm(Param pm, const char *pmtype, int created, } else return; - if (pm->node.flags & PM_SPECIAL) + if (pm->node.flags & (PM_SPECIAL|PM_NAMEREF)) return; for (i = funcstack; i; i = i->prev) { @@ -3080,11 +3218,20 @@ assignsparam(char *s, char *val, int flags) } } if (!v && !(v = getvalue(&vbuf, &t, 1))) { - unqueue_signals(); zsfree(val); + unqueue_signals(); /* errflag |= ERRFLAG_ERROR; */ return NULL; } + if (*val && (v->pm->node.flags & PM_NAMEREF)) { + if (!valid_refname(val)) { + zerr("invalid variable name: %s", val); + zsfree(val); + unqueue_signals(); + errflag |= ERRFLAG_ERROR; + return NULL; + } + } if (flags & ASSPM_WARN) check_warn_pm(v->pm, "scalar", created, 1); v->pm->node.flags &= ~PM_DEFAULTED; @@ -3111,8 +3258,8 @@ assignsparam(char *s, char *val, int flags) lhs.u.l = lhs.u.l + (zlong)rhs.u.d; } setnumvalue(v, lhs); - unqueue_signals(); zsfree(val); + unqueue_signals(); return v->pm; /* avoid later setstrvalue() call */ case PM_ARRAY: if (unset(KSHARRAYS)) { @@ -3137,9 +3284,9 @@ assignsparam(char *s, char *val, int flags) case PM_INTEGER: case PM_EFLOAT: case PM_FFLOAT: + zsfree(val); unqueue_signals(); zerr("attempt to add to slice of a numeric variable"); - zsfree(val); return NULL; case PM_ARRAY: kshappend: @@ -3214,7 +3361,7 @@ assignaparam(char *s, char **val, int flags) } else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) && !(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) { int uniq = v->pm->node.flags & PM_UNIQUE; - if (flags & ASSPM_AUGMENT) { + if ((flags & ASSPM_AUGMENT) && !(v->pm->node.flags & PM_UNSET)) { /* insert old value at the beginning of the val array */ char **new; int lv = arrlen(val); @@ -3505,9 +3652,18 @@ assignnparam(char *s, mnumber val, int flags) pm = createparam(t, ss ? PM_ARRAY : isset(POSIXIDENTIFIERS) ? PM_SCALAR : (val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT); - if (!pm) - pm = (Param) paramtab->getnode(paramtab, t); - DPUTS(!pm, "BUG: parameter not created"); + if (errflag) { + /* assume error message already output */ + unqueue_signals(); + return NULL; + } + if (!pm && !(pm = (Param) paramtab->getnode(paramtab, t))) { + DPUTS(!pm, "BUG: parameter not created"); + if (!errflag) + zerr("%s: parameter not found", t); + unqueue_signals(); + return NULL; + } if (ss) { *ss = '['; } else if (val.type & MN_INTEGER) { @@ -3578,7 +3734,7 @@ mod_export Param setiparam_no_convert(char *s, zlong val) { /* - * If the target is already an integer, thisgets converted + * If the target is already an integer, this gets converted * back. Low technology rules. */ char buf[BDIGBUFSIZE]; @@ -3598,7 +3754,8 @@ unsetparam(char *s) if ((pm = (Param) (paramtab == realparamtab ? /* getnode2() to avoid autoloading */ paramtab->getnode2(paramtab, s) : - paramtab->getnode(paramtab, s)))) + paramtab->getnode(paramtab, s))) && + !(pm->node.flags & PM_NAMEREF)) unsetparam_pm(pm, 0, 1); unqueue_signals(); } @@ -3617,7 +3774,9 @@ unsetparam_pm(Param pm, int altflag, int exp) char *altremove; if ((pm->node.flags & PM_READONLY) && pm->level <= locallevel) { - zerr("read-only variable: %s", pm->node.nam); + zerr("read-only %s: %s", + (pm->node.flags & PM_NAMEREF) ? "reference" : "variable", + pm->node.nam); return 1; } if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { @@ -3660,12 +3819,15 @@ unsetparam_pm(Param pm, int altflag, int exp) /* fudge things so removenode isn't called */ altpm->level = 1; } - unsetparam_pm(altpm, 1, exp); + unsetparam_pm(altpm, 1, exp); /* This resets pm to empty */ + pm->node.flags |= PM_UNSET; /* so we must repeat this */ } zsfree(altremove); - if (!(pm->node.flags & PM_SPECIAL)) + if (!(pm->node.flags & PM_SPECIAL)) { pm->gsu.s = &stdscalar_gsu; + pm->node.flags &= ~PM_ARRAY; + } } /* @@ -4119,7 +4281,8 @@ char * tiedarrgetfn(Param pm) { struct tieddata *dptr = (struct tieddata *)pm->u.data; - return *dptr->arrptr ? zjoin(*dptr->arrptr, STOUC(dptr->joinchar), 1) : ""; + return *dptr->arrptr ? + zjoin(*dptr->arrptr, (unsigned char) dptr->joinchar, 1) : ""; } /**/ @@ -4446,7 +4609,7 @@ usernamesetfn(UNUSED(Param pm), char *x) zwarn("failed to change user ID: %e", errno); else { zsfree(cached_username); - cached_username = ztrdup(pswd->pw_name); + cached_username = ztrdup_metafy(pswd->pw_name); cached_uid = pswd->pw_uid; } } @@ -4635,6 +4798,7 @@ setlang(char *x) if ((x = getsparam_u(ln->name)) && *x) setlocale(ln->category, x); unqueue_signals(); + inittyptab(); } /**/ @@ -4658,6 +4822,7 @@ lc_allsetfn(Param pm, char *x) else { setlocale(LC_ALL, unmeta(x)); clear_mbstate(); + inittyptab(); } } @@ -4696,6 +4861,7 @@ lcsetfn(Param pm, char *x) } unqueue_signals(); clear_mbstate(); /* LC_CTYPE may have changed */ + inittyptab(); } #endif /* USE_LOCALE */ @@ -4815,12 +4981,12 @@ keyboardhacksetfn(UNUSED(Param pm), char *x) zwarn("Only one KEYBOARD_HACK character can be defined"); /* could be changed if needed */ } for (i = 0; i < len; i++) { - if (!isascii(STOUC(x[i]))) { + if (!isascii((unsigned char) x[i])) { zwarn("KEYBOARD_HACK can only contain ASCII characters"); return; } } - keyboardhackchar = len ? STOUC(x[0]) : '\0'; + keyboardhackchar = len ? (unsigned char) x[0] : '\0'; free(x); } else keyboardhackchar = '\0'; @@ -4854,14 +5020,14 @@ histcharssetfn(UNUSED(Param pm), char *x) if (len > 3) len = 3; for (i = 0; i < len; i++) { - if (!isascii(STOUC(x[i]))) { + if (!isascii((unsigned char) x[i])) { zwarn("HISTCHARS can only contain ASCII characters"); return; } } - bangchar = len ? STOUC(x[0]) : '\0'; - hatchar = len > 1 ? STOUC(x[1]) : '\0'; - hashchar = len > 2 ? STOUC(x[2]) : '\0'; + bangchar = len ? (unsigned char) x[0] : '\0'; + hatchar = len > 1 ? (unsigned char) x[1] : '\0'; + hashchar = len > 2 ? (unsigned char) x[2] : '\0'; free(x); } else { bangchar = '!'; @@ -5083,7 +5249,7 @@ arrfixenv(char *s, char **t) if (pm->node.flags & PM_SPECIAL) joinchar = ':'; else - joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar); + joinchar = (unsigned char) ((struct tieddata *)pm->u.data)->joinchar; addenv(pm, t ? zjoin(t, joinchar, 1) : ""); } @@ -5105,9 +5271,9 @@ zputenv(char *str) char *ptr; int ret; - for (ptr = str; *ptr && STOUC(*ptr) < 128 && *ptr != '='; ptr++) + for (ptr = str; *ptr && (unsigned char) *ptr < 128 && *ptr != '='; ptr++) ; - if (STOUC(*ptr) >= 128) { + if ((unsigned char) *ptr >= 128) { /* * Environment variables not in the portable character * set are non-standard and we don't really know of @@ -5718,7 +5884,8 @@ scanendscope(HashNode hn, UNUSED(int flags)) export_param(pm); } else unsetparam_pm(pm, 0, 0); - } + } else if ((pm->node.flags & PM_NAMEREF) && pm->base > pm->level) + pm->base = locallevel; } @@ -5769,6 +5936,7 @@ static const struct paramtypes pmtypes[] = { { PM_ARRAY, "array", 'a', 0}, { PM_HASHED, "association", 'A', 0}, { 0, "local", 0, PMTF_TEST_LEVEL}, + { PM_HIDE, "hide", 'h', 0 }, { PM_LEFT, "left justified", 'L', PMTF_USE_WIDTH}, { PM_RIGHT_B, "right justified", 'R', PMTF_USE_WIDTH}, { PM_RIGHT_Z, "zero filled", 'Z', PMTF_USE_WIDTH}, @@ -5778,7 +5946,8 @@ static const struct paramtypes pmtypes[] = { { PM_TAGGED, "tagged", 't', 0}, { PM_EXPORTED, "exported", 'x', 0}, { PM_UNIQUE, "unique", 'U', 0}, - { PM_TIED, "tied", 'T', 0} + { PM_TIED, "tied", 'T', 0}, + { PM_NAMEREF, "nameref", 'n', 0} }; #define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes))) @@ -5875,7 +6044,12 @@ printparamnode(HashNode hn, int printflags) { Param p = (Param) hn; Param peer = NULL; + int altname = 0; + if (!(p->node.flags & PM_HASHELEM) && + !(printflags & PRINT_WITH_NAMESPACE) && *(p->node.nam) == '.') + return; + if (p->node.flags & PM_UNSET) { if ((printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) && p->node.flags & (PM_READONLY|PM_EXPORTED)) || @@ -5893,13 +6067,21 @@ printparamnode(HashNode hn, int printflags) printflags |= PRINT_NAMEONLY; if (printflags & (PRINT_TYPESET|PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT)) { - if (p->node.flags & (PM_RO_BY_DESIGN|PM_AUTOLOAD)) { + if (p->node.flags & PM_AUTOLOAD) { /* * It's not possible to restore the state of * these, so don't output. */ return; } + if (p->node.flags & PM_RO_BY_DESIGN) { + /* + * Compromise: cannot be restored out of context, + * but show anyway if printed in scope of declaration + */ + if (p->level != locallevel || p->level == 0) + return; + } /* * The zsh variants of export -p/readonly -p also report other * flags to indicate other attributes or scope. The POSIX variants @@ -5908,16 +6090,26 @@ printparamnode(HashNode hn, int printflags) if (printflags & PRINT_POSIX_EXPORT) { if (!(p->node.flags & PM_EXPORTED)) return; + altname = 'x'; printf("export "); } else if (printflags & PRINT_POSIX_READONLY) { if (!(p->node.flags & PM_READONLY)) return; + altname = 'r'; printf("readonly "); - } else if (locallevel && p->level >= locallevel) { - printf("typeset "); /* printf("local "); */ } else if ((p->node.flags & PM_EXPORTED) && !(p->node.flags & (PM_ARRAY|PM_HASHED))) { - printf("export "); + if (p->level && p->level >= locallevel) + printf("local "); + else { + altname = 'x'; + printf("export "); + } + } else if (locallevel && p->level >= locallevel) { + if (p->node.flags & PM_EXPORTED) + printf("local "); + else + printf("typeset "); /* printf("local "); */ } else if (locallevel) { printf("typeset -g "); } else @@ -5931,9 +6123,24 @@ printparamnode(HashNode hn, int printflags) for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) { int doprint = 0; + + if (altname && altname == pmptr->typeflag) + continue; + if (pmptr->flags & PMTF_TEST_LEVEL) { - if (p->level) + if (p->level) { + /* + if ((p->node.flags & PM_SPECIAL) && + (p->node.flags & PM_LOCAL) && + !(p->node.flags & PM_HIDE)) { + if (doneminus) + putchar(' '); + printf("+h "); + doneminus = 0; + } + */ doprint = 1; + } } else if ((pmptr->binflag != PM_EXPORTED || p->level || (p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) && (p->node.flags & pmptr->binflag)) @@ -6018,7 +6225,7 @@ printparamnode(HashNode hn, int printflags) * append the join char for tied parameters if different from colon * for typeset -p output. */ - unsigned char joinchar = STOUC(((struct tieddata *)peer->u.data)->joinchar); + unsigned char joinchar = (unsigned char) ((struct tieddata *)peer->u.data)->joinchar; if (joinchar != ':') { char buf[2]; buf[0] = joinchar; @@ -6032,3 +6239,220 @@ printparamnode(HashNode hn, int printflags) else if (!(printflags & PRINT_KV_PAIR)) putchar('\n'); } + +/**/ +mod_export HashNode +resolve_nameref(Param pm, const Asgment stop) +{ + HashNode hn = (HashNode)pm; + const char *seek = stop ? stop->value.scalar : NULL; + + if (pm && (pm->node.flags & PM_NAMEREF)) { + char *refname = GETREFNAME(pm); + if (pm->node.flags & (PM_UNSET|PM_TAGGED)) { + /* Semaphore with createparam() */ + pm->node.flags &= ~PM_UNSET; + if (pm->node.flags & PM_NEWREF) /* See setloopvar() */ + return NULL; + if (refname && *refname && (pm->node.flags & PM_TAGGED)) + pm->node.flags |= PM_SELFREF; /* See setscope() */ + return (HashNode) pm; + } else if (refname) { + if ((pm->node.flags & PM_TAGGED) || + (stop && strcmp(refname, stop->name) == 0)) { + /* zwarnnam(refname, "invalid self reference"); */ + return stop ? (HashNode)pm : NULL; + } + if (*refname) + seek = refname; + } + } + else if (pm) { + if (!(stop && (stop->flags & PM_NAMEREF))) + return (HashNode)pm; + if (!(pm->node.flags & PM_NAMEREF)) + return (pm->level < locallevel ? NULL : (HashNode)pm); + } + if (seek) { + queue_signals(); + /* pm->width is the offset of any subscript */ + if (pm && (pm->node.flags & PM_NAMEREF) && pm->width) { + if (stop) { + if (stop->flags & PM_NAMEREF) + hn = (HashNode)pm; + else + hn = NULL; + } else { + /* this has to be the end of any chain */ + hn = (HashNode)pm; /* see fetchvalue() */ + } + } else if ((hn = gethashnode2(realparamtab, seek))) { + if (pm) { + if (!(stop && (stop->flags & (PM_LOCAL)))) { + int scope = ((pm->node.flags & PM_NAMEREF) ? + ((pm->node.flags & PM_UPPER) ? -1 : + pm->base) : ((Param)hn)->level); + hn = (HashNode)upscope((Param)hn, scope); + } + /* user can't tag a nameref, safe for loop detection */ + pm->node.flags |= PM_TAGGED; + } + if (hn) { + if (hn->flags & PM_AUTOLOAD) + hn = getparamnode(realparamtab, seek); + if (!(hn->flags & PM_UNSET)) + hn = resolve_nameref((Param)hn, stop); + } + if (pm) + pm->node.flags &= ~PM_TAGGED; + } else if (stop && (stop->flags & PM_NAMEREF)) + hn = (pm && (pm->node.flags & PM_NEWREF)) ? NULL : (HashNode)pm; + unqueue_signals(); + } + + return hn; +} + +/**/ +mod_export void +setloopvar(char *name, char *value) +{ + Param pm = (Param) gethashnode2(realparamtab, name); + + if (pm && (pm->node.flags & PM_NAMEREF)) { + if (pm->node.flags & PM_READONLY) { + /* Bash error is: "%s: readonly variable" */ + zerr("read-only reference: %s", pm->node.nam); + return; + } + pm->base = pm->width = 0; + SETREFNAME(pm, ztrdup(value)); + pm->node.flags &= ~PM_UNSET; + pm->node.flags |= PM_NEWREF; + setscope(pm); + pm->node.flags &= ~PM_NEWREF; + } else + setsparam(name, value); +} + +/**/ +static void +setscope(Param pm) +{ + queue_signals(); + if (pm->node.flags & PM_NAMEREF) do { + Param basepm; + struct asgment stop; + char *refname = GETREFNAME(pm); + char *t = refname ? itype_end(refname, INAMESPC, 0) : NULL; + int q = queue_signal_level(); + + /* Temporarily change nameref to array parameter itself */ + if (t && *t == '[') + *t = 0; + else + t = 0; + stop.name = ""; + stop.value.scalar = NULL; + stop.flags = PM_NAMEREF; + if (locallevel && !(pm->node.flags & PM_UPPER)) + stop.flags |= PM_LOCAL; + dont_queue_signals(); /* Prevent unkillable loops */ + basepm = (Param)resolve_nameref(pm, &stop); + restore_queue_signals(q); + if (t) { + pm->width = t - refname; + *t = '['; + } + if (basepm) { + if (basepm->node.flags & PM_NAMEREF) { + if (pm == basepm) { + if (pm->node.flags & PM_SELFREF) { + /* Loop signalled by resolve_nameref() */ + if (upscope(pm, pm->base) == pm) { + zerr("%s: invalid self reference", refname); + unsetparam_pm(pm, 0, 1); + break; + } + pm->node.flags &= ~PM_SELFREF; + } else if (pm->base == pm->level) { + if (refname && *refname && + strcmp(pm->node.nam, refname) == 0) { + zerr("%s: invalid self reference", refname); + unsetparam_pm(pm, 0, 1); + break; + } + } + } else if ((t = GETREFNAME(basepm))) { + if (basepm->base <= basepm->level && + strcmp(pm->node.nam, t) == 0) { + zerr("%s: invalid self reference", refname); + unsetparam_pm(pm, 0, 1); + break; + } + } + } else + pm->base = basepm->level; + } + if (pm->base > pm->level) { + if (EMULATION(EMULATE_KSH)) { + zerr("%s: global reference cannot refer to local variable", + pm->node.nam); + unsetparam_pm(pm, 0, 1); + } else if (isset(WARNNESTEDVAR)) + zwarn("reference %s in enclosing scope set to local variable %s", + pm->node.nam, refname); + } + if (refname && upscope(pm, pm->base) == pm && + strcmp(pm->node.nam, refname) == 0) { + zerr("%s: invalid self reference", refname); + unsetparam_pm(pm, 0, 1); + } + } while (0); + unqueue_signals(); +} + +/**/ +mod_export Param +upscope(Param pm, int reflevel) +{ + Param up = pm->old; + while (up && up->level >= reflevel) { + pm = up; + up = up->old; + } + if (reflevel < 0 && locallevel > 0) + return pm->level == locallevel ? up : pm; + return pm; +} + +/**/ +mod_export int +valid_refname(char *val) +{ + char *t = itype_end(val, INAMESPC, 0); + + if (idigit(*val)) + return 0; + if (*t != 0) { + if (*t == '[') { + tokenize(t = dupstring(t+1)); + while ((t = parse_subscript(t, 0, ']')) && *t++ == Outbrack) { + if (*t == Inbrack) + ++t; + else + break; + } + if (t && *t) { + /* zwarn("%s: stuff after subscript: %s", val, t); */ + t = NULL; + } + } else if (t[1] || !(*t == '!' || *t == '?' || + *t == '$' || *t == '-' || + *t == '0' || *t == '_')) { + /* Skipping * @ # because of doshfunc() implementation */ + t = NULL; + } + } + return !!t; +} diff --git a/Src/parse.c b/Src/parse.c index 5054e59d5..334365649 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -120,7 +120,7 @@ struct heredocs *hdocs; * - if not (type & Z_END), followed by next WC_LIST * * WC_SUBLIST - * - data contains type (&&, ||, END) and flags (coprog, not) + * - data contains type (&&, ||, END) and flags (coproc, not) * - followed by code for sublist * - if not (type == END), followed by next WC_SUBLIST * @@ -433,9 +433,9 @@ ecstrcode(char *s) t = has_token(s); wordcode c = (t ? 3 : 2); switch (l) { - case 4: c |= ((wordcode) STOUC(s[2])) << 19; - case 3: c |= ((wordcode) STOUC(s[1])) << 11; - case 2: c |= ((wordcode) STOUC(s[0])) << 3; break; + case 4: c |= ((wordcode) (unsigned char) s[2]) << 19; + case 3: c |= ((wordcode) (unsigned char) s[1]) << 11; + case 2: c |= ((wordcode) (unsigned char) s[0]) << 3; break; case 1: c = (t ? 7 : 6); break; } return c; @@ -601,7 +601,7 @@ clear_hdocs(void) * | sublist [ SEPER | AMPER | AMPERBANG ] * * cmdsubst indicates our event is part of a command-style - * substitution terminated by the token indicationg, usual closing + * substitution terminated by the token indicated, usually closing * parenthesis. In other cases endtok is ENDINPUT. */ @@ -1935,6 +1935,8 @@ par_simple(int *cmplx, int nr) if (*ptr == Outbrace && ptr > tokstr + 1) { + /* Should we allow namespace FDs, {.foo.bar}>&file ? * + * If so, change IIDENT to INAMESPC here */ if (itype_end(tokstr+1, IIDENT, 0) >= ptr) { char *toksave = tokstr; @@ -2055,6 +2057,9 @@ par_simple(int *cmplx, int nr) if (isset(EXECOPT) && hasalias && !isset(ALIASFUNCDEF) && argc && hasalias != input_hasalias()) { zwarn("defining function based on alias `%s'", hasalias); + herrflush(); + if (noerrs != 2) + errflag |= ERRFLAG_ERROR; YYERROR(oecused); } @@ -2387,7 +2392,7 @@ par_nl_wordlist(void) */ /**/ -void (*condlex) _((void)) = zshlex; +void (*condlex) (void) = zshlex; /* * cond : cond_1 { SEPER } [ DBAR { SEPER } cond ] @@ -2725,11 +2730,10 @@ yyerror(int noerr) if (!t || !t[t0] || t[t0] == '\n') break; if (!(histdone & HISTFLAG_NOEXEC) && !(errflag & ERRFLAG_INT)) { - if (t0 == 20) - zwarn("parse error near `%l...'", t, 20); - else if (t0) - zwarn("parse error near `%l'", t, t0); - else + if (t0) { + t = metafy(t, t0, META_STATIC); + zwarn("parse error near `%s%s'", t, t0 == 20 ? "..." : ""); + } else zwarn("parse error"); } if (!noerr && noerrs != 2) @@ -3214,12 +3218,14 @@ bin_zcompile(char *nam, char **args, Options ops, UNUSED(int func)) if (!args[1] && !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a'))) { queue_signals(); - ret = build_dump(nam, dyncat(*args, FD_EXT), args, OPT_ISSET(ops,'U'), + dump = unmetafy(dyncat(*args, FD_EXT), NULL); + ret = build_dump(nam, dump, args, OPT_ISSET(ops,'U'), map, flags); unqueue_signals(); return ret; } - dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT)); + dump = (strsfx(FD_EXT, *args) ? dupstring(*args) : dyncat(*args, FD_EXT)); + unmetafy(dump, NULL); queue_signals(); ret = ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) ? @@ -3397,6 +3403,7 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) for (hlen = FD_PRELEN, tlen = 0; *files; files++) { struct stat st; + char *fnam; if (check_cond(*files, "k")) { flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD; @@ -3405,7 +3412,8 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD; continue; } - if ((fd = open(*files, O_RDONLY)) < 0 || + fnam = unmeta(*files); + if ((fd = open(fnam, O_RDONLY)) < 0 || fstat(fd, &st) != 0 || !S_ISREG(st.st_mode) || (flen = lseek(fd, 0, 2)) == -1) { if (fd >= 0) diff --git a/Src/pattern.c b/Src/pattern.c index e947d1216..1e0ae88d9 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -239,7 +239,7 @@ typedef unsigned long zrange_t; * a bit tricky... */ #define WCHAR_INVALID(ch) \ - ((wchar_t) (0xDC00 + STOUC(ch))) + ((wchar_t) (0xDC00 + (unsigned char) ch)) #endif /* MULTIBYTE_SUPPORT */ /* @@ -346,7 +346,7 @@ metacharinc(char **x) * set doesn't have the property that all bytes with the 8th * bit clear are single characters then we are stuffed. */ - if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*inptr) & 0x80)) + if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *inptr & 0x80)) { if (itok(*inptr)) inchar = ztokens[*inptr++ - Pound]; @@ -357,7 +357,7 @@ metacharinc(char **x) inchar = *inptr++; } *x = inptr; - return (wchar_t)STOUC(inchar); + return (wchar_t)(unsigned char) inchar; } while (*inptr) { @@ -1181,8 +1181,8 @@ pattern_range_to_string(char *rangestr, char *outstr) int len = 0; while (*rangestr) { - if (imeta(STOUC(*rangestr))) { - int swtype = STOUC(*rangestr) - STOUC(Meta); + if (imeta((unsigned char) *rangestr)) { + int swtype = (unsigned char) *rangestr - (unsigned char) Meta; if (swtype == 0) { /* Ordindary metafied character */ @@ -1278,17 +1278,17 @@ patcomppiece(int *flagp, int paren) kshchar = '\0'; if (*patparse && patparse[1] == Inpar) { if (*patparse == zpc_special[ZPC_KSH_PLUS]) - kshchar = STOUC('+'); + kshchar = (unsigned char) '+'; else if (*patparse == zpc_special[ZPC_KSH_BANG]) - kshchar = STOUC('!'); + kshchar = (unsigned char) '!'; else if (*patparse == zpc_special[ZPC_KSH_BANG2]) - kshchar = STOUC('!'); + kshchar = (unsigned char) '!'; else if (*patparse == zpc_special[ZPC_KSH_AT]) - kshchar = STOUC('@'); + kshchar = (unsigned char) '@'; else if (*patparse == zpc_special[ZPC_KSH_STAR]) - kshchar = STOUC('*'); + kshchar = (unsigned char) '*'; else if (*patparse == zpc_special[ZPC_KSH_QUEST]) - kshchar = STOUC('?'); + kshchar = (unsigned char) '?'; } /* @@ -1468,7 +1468,8 @@ patcomppiece(int *flagp, int paren) ch = range_type(patparse, len); patparse = nptr + 2; if (ch != PP_UNKWN) - patadd(NULL, STOUC(Meta) + ch, 1, PA_NOALIGN); + patadd(NULL, (unsigned char) Meta + ch, 1, + PA_NOALIGN); continue; } charstart = patparse; @@ -1476,10 +1477,10 @@ patcomppiece(int *flagp, int paren) if (*patparse == Dash && patparse[1] && patparse[1] != Outbrack) { - patadd(NULL, STOUC(Meta)+PP_RANGE, 1, PA_NOALIGN); + patadd(NULL, (unsigned char) Meta+PP_RANGE, 1, PA_NOALIGN); if (itok(*charstart)) { - patadd(0, STOUC(ztokens[*charstart - Pound]), 1, - PA_NOALIGN); + patadd(0, (unsigned char) ztokens[*charstart - Pound], + 1, PA_NOALIGN); } else { patadd(charstart, 0, patparse-charstart, PA_NOALIGN); } @@ -1487,7 +1488,7 @@ patcomppiece(int *flagp, int paren) METACHARINC(patparse); } if (itok(*charstart)) { - patadd(0, STOUC(ztokens[*charstart - Pound]), 1, + patadd(0, (unsigned char) ztokens[*charstart - Pound], 1, PA_NOALIGN); } else { patadd(charstart, 0, patparse-charstart, PA_NOALIGN); @@ -1910,8 +1911,8 @@ charref(char *x, char *y, int *zmb_ind) wchar_t wc; size_t ret; - if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80)) - return (wchar_t) STOUC(*x); + if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *x & 0x80)) + return (wchar_t) (unsigned char) *x; ret = mbrtowc(&wc, x, y-x, &shiftstate); @@ -1937,7 +1938,7 @@ charnext(char *x, char *y) wchar_t wc; size_t ret; - if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80)) + if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *x & 0x80)) return x + 1; ret = mbrtowc(&wc, x, y-x, &shiftstate); @@ -1965,8 +1966,8 @@ charrefinc(char **x, char *y, int *z) wchar_t wc; size_t ret; - if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(**x) & 0x80)) - return (wchar_t) STOUC(*(*x)++); + if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) **x & 0x80)) + return (wchar_t) (unsigned char) *(*x)++; ret = mbrtowc(&wc, *x, y-*x, &shiftstate); @@ -2025,13 +2026,13 @@ charsub(char *x, char *y) #else /* no MULTIBYTE_SUPPORT */ /* Get a character from the start point in a string */ -#define CHARREF(x, y) (STOUC(*(x))) +#define CHARREF(x, y) ((unsigned char) (*(x))) /* Get a pointer to the next character */ #define CHARNEXT(x, y) ((x)+1) /* Increment a pointer past the current character. */ #define CHARINC(x, y) ((x)++) /* Get a character and increment */ -#define CHARREFINC(x, y, z) (STOUC(*(x)++)) +#define CHARREFINC(x, y, z) ((unsigned char) (*(x)++)) /* Counter the number of characters between two pointers, smaller first */ #define CHARSUB(x,y) ((y) - (x)) @@ -2890,7 +2891,7 @@ patmatch(Upat prog) } if (!no && P_OP(next) == P_EXACTLY && (!P_LS_LEN(next) || - !idigit(STOUC(*P_LS_STR(next)))) && + !idigit((unsigned char) (*P_LS_STR(next)))) && !(patglobflags & 0xff)) return 0; patinput = --save; @@ -2986,14 +2987,15 @@ patmatch(Upat prog) case P_EXCSYNC: /* See the P_EXCLUDE code below for where syncptr comes from */ { - unsigned char *syncptr; + unsigned char *syncstart, *syncptr, *ptr; Upat after; after = P_OPERAND(scan); DPUTS(!P_ISEXCLUDE(after), "BUG: EXCSYNC not followed by EXCLUDE."); DPUTS(!P_OPERAND(after)->p, "BUG: EXCSYNC not handled by EXCLUDE"); - syncptr = P_OPERAND(after)->p + (patinput - patinstart); + syncstart = P_OPERAND(after)->p; + syncptr = syncstart + (patinput - patinstart); /* * If we already matched from here, this time we fail. * See WBRANCH code for story about error count. @@ -3008,6 +3010,23 @@ patmatch(Upat prog) * failed anyway. */ *syncptr = errsfound + 1; + /* + * Because of backtracking, any match before this point + * can't apply to the current branch we're on so is now + * a failure --- this can happen if, on a previous + * branch, we initially marked a success before failing + * on a later part of the pattern after marking up the + * P_EXCSYNC (even an end anchor will have this effect). + * To make sure we record the current match point + * correctly, mark those down now. + * + * This might have side effects on the efficiency of + * pathological cases involving nested branches. To + * fix that we'd probably need to record matches on + * different branches separately. + */ + for (ptr = syncstart; ptr < syncptr; ++ptr) + *ptr = 0; } break; case P_EXCEND: @@ -3600,8 +3619,8 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp) * ranges specially. */ while (*range) { - if (imeta(STOUC(*range))) { - int swtype = STOUC(*range++) - STOUC(Meta); + if (imeta((unsigned char) *range)) { + int swtype = (unsigned char) *range++ - (unsigned char) Meta; if (mtp) *mtp = swtype; switch (swtype) { @@ -3672,6 +3691,7 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp) return 1; break; case PP_IDENT: + /* Could use INAMESPC here? */ if (wcsitype(ch, IIDENT)) return 1; break; @@ -3753,8 +3773,8 @@ mb_patmatchindex(char *range, wint_t ind, wint_t *chr, int *mtp) *mtp = 0; while (*range) { - if (imeta(STOUC(*range))) { - int swtype = STOUC(*range++) - STOUC(Meta); + if (imeta((unsigned char) *range)) { + int swtype = (unsigned char) *range++ - (unsigned char) Meta; switch (swtype) { case 0: range--; @@ -3845,13 +3865,13 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp) * ranges specially. */ for (; *range; range++) { - if (imeta(STOUC(*range))) { - int swtype = STOUC(*range) - STOUC(Meta); + if (imeta((unsigned char) *range)) { + int swtype = (unsigned char) *range - (unsigned char) Meta; if (mtp) *mtp = swtype; switch (swtype) { case 0: - if (STOUC(*++range ^ 32) == ch) + if ((unsigned char) (*++range ^ 32) == ch) return 1; break; case PP_ALPHA: @@ -3931,9 +3951,9 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp) break; case PP_RANGE: range++; - r1 = STOUC(UNMETA(range)); + r1 = (unsigned char) UNMETA(range); METACHARINC(range); - r2 = STOUC(UNMETA(range)); + r2 = (unsigned char) UNMETA(range); if (*range == Meta) range++; if (r1 <= ch && ch <= r2) { @@ -3955,7 +3975,7 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp) DPUTS(1, "BUG: unknown metacharacter in range."); break; } - } else if (STOUC(*range) == ch) { + } else if ((unsigned char) *range == ch) { if (mtp) *mtp = 0; return 1; @@ -3989,12 +4009,12 @@ patmatchindex(char *range, int ind, int *chr, int *mtp) *mtp = 0; for (; *range; range++) { - if (imeta(STOUC(*range))) { - int swtype = STOUC(*range) - STOUC(Meta); + if (imeta((unsigned char) *range)) { + int swtype = (unsigned char) *range - (unsigned char) Meta; switch (swtype) { case 0: /* ordinary metafied character */ - rchr = STOUC(*++range) ^ 32; + rchr = (unsigned char) *++range ^ 32; if (!ind) { *chr = rchr; return 1; @@ -4028,9 +4048,9 @@ patmatchindex(char *range, int ind, int *chr, int *mtp) case PP_RANGE: range++; - r1 = STOUC(UNMETA(range)); + r1 = (unsigned char) UNMETA(range); METACHARINC(range); - r2 = STOUC(UNMETA(range)); + r2 = (unsigned char) UNMETA(range); if (*range == Meta) range++; rdiff = r2 - r1; @@ -4050,7 +4070,7 @@ patmatchindex(char *range, int ind, int *chr, int *mtp) } } else { if (!ind) { - *chr = STOUC(*range); + *chr = (unsigned char) *range; return 1; } } diff --git a/Src/prompt.c b/Src/prompt.c index 092de63a4..e10b05215 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -30,10 +30,20 @@ #include "zsh.mdh" #include "prompt.pro" -/* text attribute mask */ +/* current text attributes */ /**/ -mod_export zattr txtattrmask; +mod_export zattr txtcurrentattrs; + +/* pending changes for attributes */ + +/**/ +mod_export zattr txtpendingattrs; + +/* mask of attributes with an unknown state */ + +/**/ +mod_export zattr txtunknownattrs; /* the command stack for use with %_ in prompts */ @@ -160,15 +170,11 @@ promptpath(char *p, int npath, int tilde) * between spacing and non-spacing parts of the prompt, and * Nularg, which (in a non-spacing sequence) indicates a * `glitch' space. - * - * txtchangep gives an integer controlling the attributes of - * the prompt. This is for use in zle to maintain the attributes - * consistently. Other parts of the shell should not need to use it. */ /**/ mod_export char * -promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep) +promptexpand(char *s, int ns, char *rs, char *Rs) { struct buf_vars new_vars; @@ -212,7 +218,7 @@ promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep) new_vars.bp1 = NULL; new_vars.truncwidth = 0; - putpromptchar(1, '\0', txtchangep); + putpromptchar(1, '\0'); addbufspc(2); if (new_vars.dontcount) *new_vars.bp++ = Outpar; @@ -235,6 +241,68 @@ promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep) return new_vars.buf; } +/* Get the escape sequence for a given attribute. */ +/**/ +mod_export char * +zattrescape(zattr atr, int *len) +{ + struct buf_vars new_vars; + zattr savecurrent = txtcurrentattrs; + zattr saveunknown = txtunknownattrs; + + memset(&new_vars, 0, sizeof(new_vars)); + new_vars.last = bv; + bv = &new_vars; + new_vars.bufspc = 256; + new_vars.bp = new_vars.bufline = new_vars.buf = zshcalloc(new_vars.bufspc); + new_vars.dontcount = 1; + + txtunknownattrs = 0; + treplaceattrs(atr); + applytextattributes(TSC_PROMPT); + + bv = new_vars.last; + + txtpendingattrs = txtcurrentattrs = savecurrent; + txtunknownattrs = saveunknown; + + return unmetafy(new_vars.buf, len); +} + +/* Parse the argument for %H */ +/**/ +mod_export char * +parsehighlight(char *arg, char endchar, zattr *atr) +{ + static int entered = 0; + char *var = ".zle.hlgroups"; + struct value vbuf; + Value v; + char *ep, *attrs; + if ((ep = strchr(arg, endchar))) + *ep = '\0'; + if (!entered && (v = getvalue(&vbuf, &var, 0)) && + PM_TYPE(v->pm->node.flags) == PM_HASHED) + { + Param node; + HashTable ht = v->pm->gsu.h->getfn(v->pm); + if (ht && (node = (Param) ht->getnode(ht, arg))) { + attrs = node->gsu.s->getfn(node); + entered = 1; + if (match_highlight(attrs, atr, 0) == attrs) + *atr = TXT_ERROR; + } else + *atr = TXT_ERROR; + } else + *atr = TXT_ERROR; + if (ep) + *ep++ = endchar; + else + ep = strchr(arg, '\0'); + entered = 0; + return ep; +} + /* Parse the argument for %F and %K */ static zattr parsecolorchar(zattr arg, int is_fg) @@ -253,7 +321,7 @@ parsecolorchar(zattr arg, int is_fg) *ep = '\0'; /* expand the contents of the argument so you can use * %v for example */ - coll = col = promptexpand(bv->fm, 0, NULL, NULL, NULL); + coll = col = promptexpand(bv->fm, 0, NULL, NULL); *ep = oc; arg = match_colour((const char **)&coll, is_fg, 0); free(col); @@ -278,7 +346,7 @@ parsecolorchar(zattr arg, int is_fg) /**/ static int -putpromptchar(int doprint, int endchar, zattr *txtchangep) +putpromptchar(int doprint, int endchar) { char *ss, *hostnam; int t0, arg, test, sep, j, numjobs, len; @@ -430,10 +498,9 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep) /* Don't do the current truncation until we get back */ otruncwidth = bv->truncwidth; bv->truncwidth = 0; - if (!putpromptchar(test == 1 && doprint, sep, - txtchangep) || !*++bv->fm || - !putpromptchar(test == 0 && doprint, ')', - txtchangep)) { + if (!putpromptchar(test == 1 && doprint, sep) || + !*++bv->fm || + !putpromptchar(test == 0 && doprint, ')')) { bv->truncwidth = otruncwidth; return 0; } @@ -519,71 +586,67 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep) unqueue_signals(); break; case 'S': - txtchangeset(txtchangep, TXTSTANDOUT, TXTNOSTANDOUT); - txtset(TXTSTANDOUT); - tsetcap(TCSTANDOUTBEG, TSC_PROMPT); + tsetattrs(TXTSTANDOUT); + applytextattributes(TSC_PROMPT); break; case 's': - txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT); - txtunset(TXTSTANDOUT); - tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY); + tunsetattrs(TXTSTANDOUT); + applytextattributes(TSC_PROMPT); break; case 'B': - txtchangeset(txtchangep, TXTBOLDFACE, TXTNOBOLDFACE); - txtset(TXTBOLDFACE); - tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY); + tsetattrs(TXTBOLDFACE); + applytextattributes(TSC_PROMPT); break; case 'b': - txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE); - txtunset(TXTBOLDFACE); - tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY); + tunsetattrs(TXTBOLDFACE); + applytextattributes(TSC_PROMPT); break; case 'U': - txtchangeset(txtchangep, TXTUNDERLINE, TXTNOUNDERLINE); - txtset(TXTUNDERLINE); - tsetcap(TCUNDERLINEBEG, TSC_PROMPT); + tsetattrs(TXTUNDERLINE); + applytextattributes(TSC_PROMPT); break; case 'u': - txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE); - txtunset(TXTUNDERLINE); - tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY); + tunsetattrs(TXTUNDERLINE); + applytextattributes(TSC_PROMPT); break; case 'F': atr = parsecolorchar(arg, 1); - if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) { - txtchangeset(txtchangep, atr & TXT_ATTR_FG_ON_MASK, - TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK); - txtunset(TXT_ATTR_FG_COL_MASK); - txtset(atr & TXT_ATTR_FG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_FG, TSC_PROMPT); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + applytextattributes(TSC_PROMPT); break; } /* else FALLTHROUGH */ case 'f': - txtchangeset(txtchangep, TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK); - txtunset(TXT_ATTR_FG_ON_MASK); - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT); + tunsetattrs(TXTFGCOLOUR); + applytextattributes(TSC_PROMPT); break; case 'K': atr = parsecolorchar(arg, 0); - if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) { - txtchangeset(txtchangep, atr & TXT_ATTR_BG_ON_MASK, - TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK); - txtunset(TXT_ATTR_BG_COL_MASK); - txtset(atr & TXT_ATTR_BG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_BG, TSC_PROMPT); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + applytextattributes(TSC_PROMPT); break; } /* else FALLTHROUGH */ case 'k': - txtchangeset(txtchangep, TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK); - txtunset(TXT_ATTR_BG_ON_MASK); - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT); + tunsetattrs(TXTBGCOLOUR); + applytextattributes(TSC_PROMPT); + break; + case 'H': + if (bv->fm[1] == '{') { + bv->fm = parsehighlight(bv->fm + 2, '}', &atr); + --bv->fm; + if (atr != TXT_ERROR) { + treplaceattrs(atr); + applytextattributes(TSC_PROMPT); + } + } break; case '[': if (idigit(*++bv->fm)) arg = zstrtol(bv->fm, &bv->fm, 10); - if (!prompttrunc(arg, ']', doprint, endchar, txtchangep)) + if (!prompttrunc(arg, ']', doprint, endchar)) return *bv->fm; break; case '<': @@ -596,7 +659,7 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep) if (arg <= 0) arg = 1; } - if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep)) + if (!prompttrunc(arg, *bv->fm, doprint, endchar)) return *bv->fm; break; case '{': /*}*/ @@ -1013,9 +1076,8 @@ stradd(char *d) mod_export void tsetcap(int cap, int flags) { - if (tccan(cap) && !isset(SINGLELINEZLE) && - !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) { - switch (flags & TSC_OUTPUT_MASK) { + if (tccan(cap) && !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) { + switch (flags) { case TSC_RAW: tputs(tcstr[cap], 1, putraw); break; @@ -1045,20 +1107,6 @@ tsetcap(int cap, int flags) } break; } - - if (flags & TSC_DIRTY) { - flags &= ~TSC_DIRTY; - if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG) - tsetcap(TCBOLDFACEBEG, flags); - if (txtisset(TXTSTANDOUT)) - tsetcap(TCSTANDOUTBEG, flags); - if (txtisset(TXTUNDERLINE)) - tsetcap(TCUNDERLINEBEG, flags); - if (txtisset(TXTFGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_FG, flags); - if (txtisset(TXTBGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_BG, flags); - } } } @@ -1219,8 +1267,7 @@ countprompt(char *str, int *wp, int *hp, int overf) /**/ static int -prompttrunc(int arg, int truncchar, int doprint, int endchar, - zattr *txtchangep) +prompttrunc(int arg, int truncchar, int doprint, int endchar) { if (arg > 0) { char ch = *bv->fm, *ptr, *truncstr; @@ -1267,7 +1314,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar, w = bv->bp - bv->buf; bv->fm++; bv->trunccount = bv->dontcount; - putpromptchar(doprint, endchar, txtchangep); + putpromptchar(doprint, endchar); bv->trunccount = 0; ptr = bv->buf + w; /* putpromptchar() may have realloc()'d */ *bv->bp = '\0'; @@ -1547,7 +1594,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar, * With bv->truncwidth set to zero, we always reach endchar * * (or the terminating NULL) this time round. * */ - if (!putpromptchar(doprint, endchar, txtchangep)) + if (!putpromptchar(doprint, endchar)) return 0; } /* Now we have to trick it into matching endchar again */ @@ -1585,6 +1632,158 @@ cmdpop(void) cmdsp--; } +/* functions for handling attributes */ + +/**/ +mod_export void +applytextattributes(int flags) +{ + zattr change = txtcurrentattrs ^ txtpendingattrs; + zattr keepon = ~change & txtpendingattrs & TXT_ATTR_ALL; + zattr turnoff = change & ~txtpendingattrs & TXT_ATTR_ALL; + int keepcount, turncount = 0; + + /* bail out early if we wouldn't do anything */ + if (!change) + return; + + if (txtunknownattrs) { + txtunknownattrs &= ~change; /* changes cease to be unknown */ + /* can't turn unknown attrs back on so avoid wiping them */ + keepcount = 1; + } else { + /* If we want to turn off more attributes than we want to keep on + * then it takes fewer termcap sequences to just turn off all the + * attributes. */ + for (keepcount = 0; keepon; keepcount++) /* count bits */ + keepon &= keepon - 1; + for (; turnoff; turncount++) + turnoff &= turnoff - 1; + } + + /* enabling bold can be relied upon to disable faint + * (the converse not so as that commonly does nothing at all) */ + if (txtcurrentattrs & TXTFAINT && txtpendingattrs & TXTBOLDFACE) { + --turncount; + change &= ~TXTFAINT; + } + + if (keepcount < turncount || + (change & ~txtpendingattrs & TXT_ATTR_FONT_WEIGHT)) { + tsetcap(TCALLATTRSOFF, flags); + /* this cleared all attributes, may need to restore some */ + change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs; + txtunknownattrs = 0; + } else { + if (change & ~txtpendingattrs & TXTSTANDOUT) { + tsetcap(TCSTANDOUTEND, flags); + /* in some cases, that clears all attributes */ + change = (txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs) | + (TXTUNDERLINE & change); + } + if (change & ~txtpendingattrs & TXTUNDERLINE) { + tsetcap(TCUNDERLINEEND, flags); + /* in some cases, that clears all attributes */ + change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs; + } + if (change & ~txtpendingattrs & TXTITALIC) + tsetcap(TCITALICSEND, flags); + } + if (change & txtpendingattrs & TXTBOLDFACE) + tsetcap(TCBOLDFACEBEG, flags); + if (change & txtpendingattrs & TXTFAINT) + tsetcap(TCFAINTBEG, flags); + if (change & txtpendingattrs & TXTSTANDOUT) + tsetcap(TCSTANDOUTBEG, flags); + if (change & txtpendingattrs & TXTUNDERLINE) + tsetcap(TCUNDERLINEBEG, flags); + if (change & txtpendingattrs & TXTITALIC) + tsetcap(TCITALICSBEG, flags); + + if (change & TXT_ATTR_FG_MASK) + set_colour_attribute(txtpendingattrs, COL_SEQ_FG, flags); + if (change & TXT_ATTR_BG_MASK) + set_colour_attribute(txtpendingattrs, COL_SEQ_BG, flags); + + txtcurrentattrs = txtpendingattrs; +} + +/**/ +mod_export void +cleartextattributes(int flags) +{ + treplaceattrs(0); + applytextattributes(flags); +} + +/**/ +mod_export void +treplaceattrs(zattr newattrs) +{ + if (newattrs == TXT_ERROR) + return; + + if (txtunknownattrs) { + /* Set current attributes to the opposite of the new ones + * for any that are unknown so that applytextattributes() + * detects them as changed. */ + txtcurrentattrs &= ~txtunknownattrs; + txtcurrentattrs |= txtunknownattrs & ~newattrs; + } + + txtpendingattrs = newattrs; +} + +/**/ +mod_export void +tsetattrs(zattr newattrs) +{ + /* assume any unknown attributes that we're now setting were unset */ + txtcurrentattrs &= ~(newattrs & txtunknownattrs); + + txtpendingattrs |= newattrs & TXT_ATTR_ALL; + if (newattrs & TXTFGCOLOUR) { + txtpendingattrs &= ~TXT_ATTR_FG_MASK; + txtpendingattrs |= newattrs & TXT_ATTR_FG_MASK; + } + if (newattrs & TXTBGCOLOUR) { + txtpendingattrs &= ~TXT_ATTR_BG_MASK; + txtpendingattrs |= newattrs & TXT_ATTR_BG_MASK; + } +} + +/**/ +mod_export void +tunsetattrs(zattr newattrs) +{ + /* assume any unknown attributes that we're now unsetting were set */ + txtcurrentattrs |= newattrs & txtunknownattrs; + + txtpendingattrs &= ~(newattrs & TXT_ATTR_ALL); + if (newattrs & TXTFGCOLOUR) + txtpendingattrs &= ~TXT_ATTR_FG_MASK; + if (newattrs & TXTBGCOLOUR) + txtpendingattrs &= ~TXT_ATTR_BG_MASK; +} + +/* Merge two attribute sets. In an case where attributes might conflict + * choose those from the first parameter. Foreground and background + * colours are taken together - less likely to end up with unreadable + * combinations. */ + +/**/ +mod_export zattr +mixattrs(zattr primary, zattr secondary) +{ + zattr result = secondary; + /* take colours from primary */ + if (primary & (TXTFGCOLOUR|TXTBGCOLOUR)) + result &= ~TXT_ATTR_COLOUR_MASK; + /* take font weight from primary */ + if (primary & TXT_ATTR_FONT_WEIGHT) + result &= ~TXT_ATTR_FONT_WEIGHT; + return result | primary; +} /***************************************************************************** * Utilities dealing with colour and other forms of highlighting. @@ -1607,10 +1806,12 @@ struct highlight { }; static const struct highlight highlights[] = { - { "none", 0, TXT_ATTR_ON_MASK }, - { "bold", TXTBOLDFACE, 0 }, + { "none", 0, TXT_ATTR_ALL }, + { "bold", TXTBOLDFACE, TXTFAINT }, + { "faint", TXTFAINT, TXTBOLDFACE }, { "standout", TXTSTANDOUT, 0 }, { "underline", TXTUNDERLINE, 0 }, + { "italic", TXTITALIC, 0 }, { NULL, 0, 0 } }; @@ -1645,8 +1846,8 @@ match_named_colour(const char **teststrp) * Match just the colour part of a highlight specification. * If teststrp is NULL, use the already parsed numeric colour. * Return the attributes to set in the attribute variable. - * Return -1 for out of range. Does not check the character - * following the colour specification. + * Return TXT_ERROR for out of range. Does not check the + * character following the colour specification. */ /**/ @@ -1666,7 +1867,7 @@ match_colour(const char **teststrp, int is_fg, int colour) tc = TCBGCOLOUR; } if (teststrp) { - if (**teststrp == '#' && isxdigit(STOUC((*teststrp)[1]))) { + if (**teststrp == '#' && isxdigit((unsigned char) (*teststrp)[1])) { struct color_rgb color; char *end; zlong col = zstrtol(*teststrp+1, &end, 16); @@ -1693,10 +1894,8 @@ match_colour(const char **teststrp, int is_fg, int colour) } } else if ((named = ialpha(**teststrp))) { colour = match_named_colour(teststrp); - if (colour == 8) { - /* default */ - return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR; - } + if (colour == 8) /* default */ + return 0; if (colour < 0) return TXT_ERROR; } @@ -1717,23 +1916,30 @@ match_colour(const char **teststrp, int is_fg, int colour) /* * Match a set of highlights in the given teststr. * Set *on_var to reflect the values found. + * Set *layer to the layer * Return a pointer to the first character not consumed. */ /**/ mod_export const char * -match_highlight(const char *teststr, zattr *on_var) +match_highlight(const char *teststr, zattr *on_var, int *layer) { int found = 1; *on_var = 0; while (found && *teststr) { const struct highlight *hl; + zattr atr = 0; found = 0; - if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) { + if (strpfx("hl=", teststr)) { + teststr += 3; + teststr = parsehighlight((char *)teststr, ',', &atr); + if (atr != TXT_ERROR) + *on_var = atr; + found = 1; + } else if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) { int is_fg = (teststr[0] == 'f'); - zattr atr; teststr += 3; atr = match_colour(&teststr, is_fg, 0); @@ -1745,6 +1951,14 @@ match_highlight(const char *teststr, zattr *on_var) /* skip out of range colours but keep scanning attributes */ if (atr != TXT_ERROR) *on_var |= atr; + } else if (layer && strpfx("layer=", teststr)) { + teststr += 6; + *layer = (int) zstrtol(teststr, (char **) &teststr, 10); + if (*teststr == ',') + teststr++; + else if (*teststr && *teststr != ' ') + break; + found = 1; } else { for (hl = highlights; hl->name; hl++) { if (strpfx(hl->name, teststr)) { @@ -2024,13 +2238,13 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) if (fg_bg == COL_SEQ_FG) { colour = txtchangeget(atr, TXT_ATTR_FG_COL); tc = TCFGCOLOUR; - def = txtchangeisset(atr, TXTNOFGCOLOUR); - use_truecolor = txtchangeisset(atr, TXT_ATTR_FG_24BIT); + def = !(atr & TXTFGCOLOUR); + use_truecolor = atr & TXT_ATTR_FG_24BIT; } else { colour = txtchangeget(atr, TXT_ATTR_BG_COL); tc = TCBGCOLOUR; - def = txtchangeisset(atr, TXTNOBGCOLOUR); - use_truecolor = txtchangeisset(atr, TXT_ATTR_BG_24BIT); + def = !(atr & TXTBGCOLOUR); + use_truecolor = atr & TXT_ATTR_BG_24BIT; } /* Test if current zle_highlight settings are customized, or diff --git a/Src/prototypes.h b/Src/prototypes.h index e3db4f5ee..3578482d0 100644 --- a/Src/prototypes.h +++ b/Src/prototypes.h @@ -28,9 +28,9 @@ */ #ifndef HAVE_STDLIB_H -char *malloc _((size_t)); -char *realloc _((void *, size_t)); -char *calloc _((size_t, size_t)); +char *malloc (size_t); +char *realloc (void *, size_t); +char *calloc (size_t, size_t); #endif #if !(defined(USES_TERMCAP_H) || defined(USES_TERM_H)) @@ -45,11 +45,11 @@ char *calloc _((size_t, size_t)); #else #define TC_CONST #endif -extern int tgetent _((char *bp, TC_CONST char *name)); -extern int tgetnum _((char *id)); -extern int tgetflag _((char *id)); -extern char *tgetstr _((char *id, char **area)); -extern int tputs _((TC_CONST char *cp, int affcnt, int (*outc) (int))); +extern int tgetent (char *bp, TC_CONST char *name); +extern int tgetnum (char *id); +extern int tgetflag (char *id); +extern char *tgetstr (char *id, char **area); +extern int tputs (TC_CONST char *cp, int affcnt, int (*outc) (int)); #undef TC_CONST #endif @@ -70,30 +70,30 @@ char *tgoto(const char *cap, int col, int row); #endif #ifdef __osf__ -char *mktemp _((char *)); +char *mktemp (char *); #endif #if defined(__osf__) && defined(__alpha) && defined(__GNUC__) /* Digital cc does not need these prototypes, gcc does need them */ # ifndef HAVE_IOCTL_PROTO -int ioctl _((int d, unsigned long request, void *argp)); +int ioctl (int d, unsigned long request, void *argp); # endif # ifndef HAVE_MKNOD_PROTO -int mknod _((const char *pathname, int mode, dev_t device)); +int mknod (const char *pathname, int mode, dev_t device); # endif -int nice _((int increment)); -int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout)); +int nice (int increment); +int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout); #endif #if defined(DGUX) && defined(__STDC__) /* Just plain missing. */ -extern int getrlimit _((int resource, struct rlimit *rlp)); -extern int setrlimit _((int resource, const struct rlimit *rlp)); -extern int getrusage _((int who, struct rusage *rusage)); -extern int gettimeofday _((struct timeval *tv, struct timezone *tz)); -extern int wait3 _((union wait *wait_status, int options, struct rusage *rusage)); -extern int getdomainname _((char *name, int maxlength)); -extern int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout)); +extern int getrlimit (int resource, struct rlimit *rlp); +extern int setrlimit (int resource, const struct rlimit *rlp); +extern int getrusage (int who, struct rusage *rusage); +extern int gettimeofday (struct timeval *tv, struct timezone *tz); +extern int wait3 (union wait *wait_status, int options, struct rusage *rusage); +extern int getdomainname (char *name, int maxlength); +extern int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout); #endif /* DGUX and __STDC__ */ #ifdef __NeXT__ @@ -101,34 +101,34 @@ extern pid_t getppid(void); #endif #if defined(__sun__) && !defined(__SVR4) /* SunOS */ -extern char *strerror _((int errnum)); +extern char *strerror (int errnum); #endif /**************************************************/ /*** prototypes for functions built in compat.c ***/ #ifndef HAVE_STRSTR -extern char *strstr _((const char *s, const char *t)); +extern char *strstr (const char *s, const char *t); #endif #ifndef HAVE_GETHOSTNAME -extern int gethostname _((char *name, size_t namelen)); +extern int gethostname (char *name, size_t namelen); #endif #ifndef HAVE_GETTIMEOFDAY -extern int gettimeofday _((struct timeval *tv, struct timezone *tz)); +extern int gettimeofday (struct timeval *tv, struct timezone *tz); #endif #ifndef HAVE_DIFFTIME -extern double difftime _((time_t t2, time_t t1)); +extern double difftime (time_t t2, time_t t1); #endif #ifndef HAVE_STRERROR -extern char *strerror _((int errnum)); +extern char *strerror (int errnum); #endif /*** end of prototypes for functions in compat.c ***/ /***************************************************/ #ifndef HAVE_MEMMOVE -extern void bcopy _((const void *, void *, size_t)); +extern void bcopy (const void *, void *, size_t); #endif diff --git a/Src/signals.c b/Src/signals.c index a61368554..d28853ea6 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -31,10 +31,12 @@ #include "signals.pro" /* Array describing the state of each signal: an element contains * - * 0 for the default action or some ZSIG_* flags ored together. */ + * 0 for the default action or some ZSIG_* flags ored together. * + * Contains TRAPCOUNT elements but can't be allocated statically * + * because that's a dynamic value on Linux */ /**/ -mod_export int sigtrapped[VSIGCOUNT]; +mod_export int *sigtrapped; /* * Trap programme lists for each signal. @@ -48,7 +50,7 @@ mod_export int sigtrapped[VSIGCOUNT]; */ /**/ -mod_export Eprog siglists[VSIGCOUNT]; +mod_export Eprog *siglists; /* Total count of trapped signals */ @@ -556,9 +558,11 @@ wait_for_processes(void) jn->gleader = 0; } } + update_bg_job(jn, pid, status); update_job(jn); } else if (findproc(pid, &jn, &pn, 1)) { pn->status = status; + update_bg_job(jn, pid, status); update_job(jn); } else { /* If not found, update the shell record of time spent by @@ -567,19 +571,7 @@ wait_for_processes(void) * terminates. */ get_usage(); - } - /* - * Accumulate a list of older jobs. We only do this for - * background jobs, which is something in the job table - * that's not marked as in the current shell or as shell builtin - * and is not equal to the current foreground job. - */ - if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) && - jn - jobtab != thisjob) { - if (WIFEXITED(status)) - addbgstatus(pid, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - addbgstatus(pid, 0200 | WTERMSIG(status)); + update_bg_job(jn, pid, status); } unqueue_signals(); @@ -902,7 +894,7 @@ dosavetrap(int sig, int level) * Set a trap: note this does not handle manipulation of * the function table for TRAPNAL functions. * - * sig is the signal number. + * sig is index into the table of trapped signals. * * l is the list to be eval'd for a trap defined with the "trap" * builtin and should be NULL for a function trap. @@ -941,6 +933,10 @@ settrap(int sig, Eprog l, int flags) #endif sig != SIGCHLD) signal_ignore(sig); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + else if (sig >= VSIGCOUNT && sig < TRAPCOUNT) + signal_ignore(SIGNUM(sig)); +#endif } else { nsigtrapped++; sigtrapped[sig] = ZSIG_TRAPPED; @@ -950,6 +946,10 @@ settrap(int sig, Eprog l, int flags) #endif sig != SIGCHLD) install_handler(sig); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig >= VSIGCOUNT && sig < TRAPCOUNT) + install_handler(SIGNUM(sig)); +#endif } sigtrapped[sig] |= flags; /* @@ -1029,6 +1029,11 @@ removetrap(int sig) #endif sig != SIGCHLD) signal_default(sig); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + else if (sig >= VSIGCOUNT && sig < TRAPCOUNT) + signal_default(SIGNUM(sig)); +#endif + if (sig == SIGEXIT) exit_trap_posix = 0; @@ -1182,7 +1187,7 @@ endtrapscope(void) static int handletrap(int sig) { - if (!sigtrapped[sig]) + if (!sigtrapped[SIGIDX(sig)]) return 0; if (trap_queueing_enabled) @@ -1199,7 +1204,7 @@ handletrap(int sig) return 1; } - dotrap(sig); + dotrap(SIGIDX(sig)); if (sig == SIGALRM) { @@ -1491,3 +1496,60 @@ dotrap(int sig) restore_queue_signals(q); } + +#if defined(SIGRTMIN) && defined(SIGRTMAX) + +/* Realtime signals, these are a contiguous block that can + * be separated from the other signals with an unused gap. */ + +/**/ +int +rtsigno(const char* signame) +{ + const int maxofs = SIGRTMAX - SIGRTMIN; + const char *end = signame + 5; + int offset; + struct rtdir { int sig; int dir; char op; } x = { 0, 0, 0 }; + if (!strncmp(signame, "RTMIN", 5)) { + x = (struct rtdir) { SIGRTMIN, 1, '+' }; + } else if (!strncmp(signame, "RTMAX", 5)) { + x = (struct rtdir) { SIGRTMAX, -1, '-' }; + } else + return 0; + + if (signame[5] == x.op) { + if ((offset = strtol(signame + 6, (char **) &end, 10)) > maxofs) + return 0; + x.sig += offset * x.dir; + } + if (*end) + return 0; + + return x.sig; +} + +/**/ +char * +rtsigname(int signo, int alt) +{ + char* buf = (char *) zhalloc(10); + int minofs = signo - SIGRTMIN; + int maxofs = SIGRTMAX - signo; + int offset; + int form = alt ^ (maxofs < minofs); + + if (signo < SIGRTMIN || signo > SIGRTMAX) + return NULL; + + strcpy(buf, "RT"); + strcpy(buf+2, form ? "MAX-" : "MIN+"); + offset = form ? maxofs : minofs; + if (offset) { + snprintf(buf + 6, 4, "%d", offset); + } else { + buf[5] = '\0'; + } + return buf; +} + +#endif diff --git a/Src/signals.h b/Src/signals.h index 41ac88cce..7910f6b79 100644 --- a/Src/signals.h +++ b/Src/signals.h @@ -27,7 +27,7 @@ * */ -#define SIGNAL_HANDTYPE void (*)_((int)) +#define SIGNAL_HANDTYPE void (*)(int) #ifndef HAVE_KILLPG # define killpg(pgrp,sig) kill(-(pgrp),sig) @@ -36,6 +36,15 @@ #define SIGZERR (SIGCOUNT+1) #define SIGDEBUG (SIGCOUNT+2) #define VSIGCOUNT (SIGCOUNT+3) +#if defined(SIGRTMIN) && defined(SIGRTMAX) +# define TRAPCOUNT (VSIGCOUNT + SIGRTMAX - SIGRTMIN + 1) +# define SIGNUM(x) ((x) >= VSIGCOUNT ? (x) - VSIGCOUNT + SIGRTMIN : (x)) +# define SIGIDX(x) ((x) >= SIGRTMIN && (x) <= SIGRTMAX ? (x) - SIGRTMIN + VSIGCOUNT : (x)) +#else +# define TRAPCOUNT VSIGCOUNT +# define SIGNUM(x) (x) +# define SIGIDX(x) (x) +#endif #define SIGEXIT 0 #ifdef SV_BSDSIG @@ -136,7 +145,7 @@ #ifdef BSD_SIGNALS #define signal_block(S) sigblock(S) #else -extern sigset_t signal_block _((sigset_t)); +extern sigset_t signal_block (sigset_t); #endif /* BSD_SIGNALS */ -extern sigset_t signal_unblock _((sigset_t)); +extern sigset_t signal_unblock (sigset_t); diff --git a/Src/signames2.awk b/Src/signames2.awk index 4d15681d5..0b254f751 100644 --- a/Src/signames2.awk +++ b/Src/signames2.awk @@ -13,8 +13,9 @@ signam = substr(tmp[1], 4, 20) signum = tmp[2] if (signam == "CHLD" && sig[signum] == "CLD") sig[signum] = "" - if (signam == "POLL" && sig[signum] == "IO") sig[signum] = "" - if (sig[signum] == "") { + if (signam == "POLL" && sig[signum] == "IO") sig[signum] = "" + if (signam == "ABRT" && sig[signum] == "IOT") sig[signum] = "" + if (signam !~ /RTM(IN|AX)/ && sig[signum] == "") { sig[signum] = signam if (0 + max < 0 + signum && signum < 60) max = signum @@ -26,6 +27,7 @@ if (signam == "CONT") { msg[signum] = "continued" } if (signam == "EMT") { msg[signum] = "EMT instruction" } if (signam == "FPE") { msg[signum] = "floating point exception" } + if (signam == "FREEZE") { msg[signum] = "checkpoint freeze" } if (signam == "HUP") { msg[signum] = "hangup" } if (signam == "ILL") { msg[signum] = "illegal hardware instruction" } if (signam == "INFO") { msg[signum] = "status request from keyboard" } @@ -33,23 +35,25 @@ if (signam == "IO") { msg[signum] = "i/o ready" } if (signam == "IOT") { msg[signum] = "IOT instruction" } if (signam == "KILL") { msg[signum] = "killed" } - if (signam == "LOST") { msg[signum] = "resource lost" } + if (signam == "LOST") { msg[signum] = "resource lost" } if (signam == "PIPE") { msg[signum] = "broken pipe" } - if (signam == "POLL") { msg[signum] = "pollable event occurred" } + if (signam == "POLL") { msg[signum] = "pollable event occurred" } if (signam == "PROF") { msg[signum] = "profile signal" } if (signam == "PWR") { msg[signum] = "power fail" } if (signam == "QUIT") { msg[signum] = "quit" } if (signam == "SEGV") { msg[signum] = "segmentation fault" } if (signam == "SYS") { msg[signum] = "invalid system call" } if (signam == "TERM") { msg[signum] = "terminated" } + if (signam == "THAW") { msg[signum] = "checkpoint thaw" } if (signam == "TRAP") { msg[signum] = "trace trap" } - if (signam == "URG") { msg[signum] = "urgent condition" } + if (signam == "URG") { msg[signum] = "urgent condition" } if (signam == "USR1") { msg[signum] = "user-defined signal 1" } if (signam == "USR2") { msg[signum] = "user-defined signal 2" } if (signam == "VTALRM") { msg[signum] = "virtual time alarm" } if (signam == "WINCH") { msg[signum] = "window size changed" } if (signam == "XCPU") { msg[signum] = "cpu limit exceeded" } if (signam == "XFSZ") { msg[signum] = "file size limit exceeded" } + if (signam == "XRES") { msg[signum] = "resource control exceeded" } } } @@ -65,10 +69,6 @@ END { printf "#include %czsh.mdh%c\n", 34, 34 printf "\n" printf "/**/\n" - printf "#define sigmsg(sig) ((sig) <= SIGCOUNT ? sig_msg[sig]" - printf " : %c%s%c)", 34, "unknown signal", 34 - printf "\n" - printf "/**/\n" printf "mod_export char *sig_msg[SIGCOUNT+2] = {\n" printf "\t%c%s%c,\n", 34, "done", 34 diff --git a/Src/sort.c b/Src/sort.c index 26949ad9c..ce2b4bbc3 100644 --- a/Src/sort.c +++ b/Src/sort.c @@ -138,7 +138,7 @@ eltpcmp(const void *a, const void *b) int mul = 0; for (; *as == *bs && *as; as++, bs++); #ifndef HAVE_STRCOLL - cmp = (int)STOUC(*as) - (int)STOUC(*bs); + cmp = (int) (unsigned char) *as - (int) (unsigned char) *bs; #endif if (sortnumeric < 0) { if (*as == '-' && idigit(as[1]) && idigit(*bs)) { @@ -159,7 +159,7 @@ eltpcmp(const void *a, const void *b) bs++; for (; idigit(*as) && *as == *bs; as++, bs++); if (idigit(*as) || idigit(*bs)) { - cmp = mul * ((int)STOUC(*as) - (int)STOUC(*bs)); + cmp = mul * ((int) (unsigned char) *as - (int) (unsigned char) *bs); while (idigit(*as) && idigit(*bs)) as++, bs++; if (idigit(*as) && !idigit(*bs)) diff --git a/Src/subst.c b/Src/subst.c index 0f98e6ea3..a079672df 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -556,7 +556,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep, for ( ; *x; x += l) { char c = (l = *x == Meta) ? x[1] ^ 32 : *x; l++; - if (!iwsep(STOUC(c))) + if (!iwsep((unsigned char) c)) break; *ms_flags |= MULTSUB_WS_AT_START; } @@ -573,7 +573,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep, convchar_t c; if (*x == Dash) *x = '-'; - if (itok(STOUC(*x))) { + if (itok((unsigned char) *x)) { /* token, can't be separator, must be single byte */ rawc = *x; l = 1; @@ -582,7 +582,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep, if (!inq && !inp && WC_ZISTYPE(c, ISEP)) { *x = '\0'; for (x += l; *x; x += l) { - if (itok(STOUC(*x))) { + if (itok((unsigned char) *x)) { /* as above */ rawc = *x; l = 1; @@ -1489,21 +1489,27 @@ subst_parse_str(char **sp, int single, int err) static char * substevalchar(char *ptr) { - zlong ires = mathevali(ptr); + zlong ires; int len = 0; + int saved_errflag = errflag; - if (errflag) - return NULL; -#ifdef MULTIBYTE_SUPPORT - if (isset(MULTIBYTE) && ires > 127) { - /* '\\' + 'U' + 8 bytes of character + '\0' */ - char buf[11]; + errflag = 0; + ires = mathevali(ptr); - /* inefficient: should separate out \U handling from getkeystring */ - sprintf(buf, "\\U%.8x", (unsigned int)ires & 0xFFFFFFFFu); - ptr = getkeystring(buf, &len, GETKEYS_BINDKEY, NULL); + if (errflag) { /* not a valid numerical expression */ + errflag |= saved_errflag; + return noerrs ? dupstring(""): NULL; + } + errflag |= saved_errflag; + if (ires < 0) { + zerr("character not in range"); + } +#ifdef MULTIBYTE_SUPPORT + else if (isset(MULTIBYTE) && ires > 127) { + ptr = zhalloc(MB_CUR_MAX+1); + len = ucs4tomb((unsigned int)ires & 0xffffffff, ptr); } - if (len == 0) + if (len <= 0) #endif { ptr = zhalloc(2); @@ -1818,14 +1824,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Use for the (k) flag. Goes down into the parameter code, * sometimes. */ - char hkeys = 0; + int hkeys = 0; /* * Used for the (v) flag, ditto. Not quite sure why they're * separate, but the tradition seems to be that things only * get combined when that makes the result more obscure rather * than less. */ - char hvals = 0; + int hvals = 0; /* * Whether we had to evaluate a subexpression, i.e. an * internal ${...} or $(...) or plain $pm. We almost don't @@ -1860,6 +1866,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * joining the array into a string (for compatibility with ksh/bash). */ int quoted_array_with_offset = 0; + /* + * Nofork substitution controls + */ + char *rplyvar = NULL; /* Indicates ${|...;} or ${{var} ...;} */ + char *rplytmp = NULL; /* Indicates ${ ... ;} */ *s++ = '\0'; /* @@ -1870,8 +1881,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * these later on, too. */ c = *s; - if (itype_end(s, IIDENT, 1) == s && *s != '#' && c != Pound && - !IS_DASH(c) && + if (itype_end(s, (c == Inbrace ? INAMESPC : IIDENT), 1) == s && + *s != '#' && c != Pound && !IS_DASH(c) && c != '!' && c != '$' && c != String && c != Qstring && c != '?' && c != Quest && c != '*' && c != Star && c != '@' && c != '{' && @@ -1887,19 +1898,235 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * flags in parentheses, but also one ksh hack. */ if (c == Inbrace) { - inbrace = 1; - s++; + /* For processing nofork command substitution string */ + char *cmdarg = NULL, *endvar = NULL, inchar = *++s; + char *outbracep = s, sav = *s; + Param rplypm = NULL; + size_t slen = 0; + int trim = (!EMULATION(EMULATE_ZSH)) ? 2 : !qt; + + inbrace = 1; /* Outer scope boolean, see above */ + + /* Handling for nofork command substitution e.g. ${|cmd;} + * See other comments about kludges for why this is here. + * + * The command string is extracted and executed, and the + * substitution assigned. There's no (...)-flags processing, + * i.e. no ${|(U)cmd;}, because it looks quite awful and + * should not be part of command substitution in any case. + * Use ${(U)${|cmd;}} as you would for ${(U)$(cmd;)}. + */ + if (inchar == '|' || inchar == Bar || inblank(inchar)) { + *s = Inbrace; + if (skipparens(Inbrace, Outbrace, &outbracep) == 0) + slen = outbracep - s - 1; + *s = sav; + if (inchar == '|') + inchar = Bar; /* Simplify later compares */ + } else if (inchar == '{' || inchar == Inbrace) { + *s = Inbrace; + if ((outbracep = itype_end(s+1, INAMESPC, 0))) { + if (*outbracep == Inbrack && + (outbracep = parse_subscript(++outbracep, 1, ']'))) + ++outbracep; + } + + /* If we reached the first close brace, find the last */ + if (outbracep && *outbracep == Outbrace) { + char outchar = inchar == Inbrace ? Outbrace : '}'; + endvar = outbracep++; + + /* Require space to avoid ${{var}} typo for ${${var}} */ + if (!inblank(*outbracep)) { + zerr("bad substitution"); + return NULL; + } + + *endvar = '|'; /* Almost anything but braces/brackets */ + outbracep = s; + if (skipparens(Inbrace, outchar, &outbracep) == 0) + *endvar = Outbrace; + else { /* Never happens? */ + *endvar = outchar; + outbracep = endvar + 1; + } + slen = outbracep - s - 1; + if (inchar != Inbrace) + outbracep[-1] = Outbrace; + *s = sav; + inchar = Inbrace; /* Simplify later compares */ + } else { + zerr("bad substitution"); + return NULL; + } + } + if (slen > 1) { + char *outbracep = s + slen; + if (!itok(*s) || inblank(inchar)) { + /* This tokenize() is important */ + char sav = *outbracep; + *outbracep = '\0'; + tokenize(s); + *outbracep = sav; + } + if (*outbracep == Outbrace) { + if (endvar == s+1) { + /* For consistency with ${} we allow ${{}...} */ + rplyvar = NULL; + } + if (endvar && *endvar == Outbrace) { + cmdarg = dupstrpfx(endvar+1, outbracep-endvar-1); + rplyvar = dupstrpfx(s+1,endvar-s-1); + } else { + cmdarg = dupstrpfx(s+1, outbracep-s-1); + rplyvar = "REPLY"; + } + if (inblank(inchar)) { + /* + * Admittedly a hack. Take advantage of the added + * parameter scope and the semantics of $(<file) to + * construct a command to write/read a temporary file. + * Then fall through to the regular parameter handling + * to manage word splitting, expansion flags, etc. + */ + char *outfmt = ">| %s {\n%s\n;}"; /* 13 */ + if ((rplytmp = gettempname(NULL, 1))) { + /* Prevent shenanigans with $TMPPREFIX */ + char *tmpfile = quotestring(rplytmp, QT_BACKSLASH); + char *dummy = zhalloc(strlen(cmdarg) + + strlen(tmpfile) + + 13); + sprintf(dummy, outfmt, tmpfile, cmdarg); + cmdarg = dummy; + } else { + /* TMPPREFIX not writable? */ + cmdoutval = lastval; + cmdarg = NULL; + } + } + s = outbracep; + } + } + + if (rplyvar) { + /* char *rplyval = getsparam("REPLY"); cf. Future? below */ + startparamscope(); /* "local" behaves as if in a function */ + if (inchar == Bar) { + /* rplyvar should be REPLY at this point, but create + * hardwired name anyway to expose any bugs elsewhere + */ + rplypm = createparam("REPLY", PM_LOCAL|PM_UNSET|PM_HIDE); + if (rplypm) /* Shouldn't createparam() do this? */ + rplypm->level = locallevel; + /* Future? Expose global value of $REPLY if any? */ + /* if (rplyval) setsparam("REPLY", ztrdup(rplyval)); */ + } else if (inblank(inchar)) { + rplypm = createparam(".zsh.cmdsubst", + PM_LOCAL|PM_UNSET|PM_HIDE| + PM_READONLY_SPECIAL); + if (rplypm) + rplypm->level = locallevel; + } + if (inchar != Inbrace && !rplypm) { + zerr("failed to create scope for command substitution"); + return NULL; + } + } + + if (rplyvar && cmdarg && *cmdarg) { + int obreaks = breaks; + Eprog cmdprog; + /* Execute the shell command */ + queue_signals(); + untokenize(cmdarg); + cmdprog = parse_string(cmdarg, 0); + if (cmdprog) { + /* exec.c handles dont_queue_signals() */ + execode(cmdprog, 1, 0, "cmdsubst"); + cmdoutval = lastval; + /* "return" behaves as if in a function */ + if (retflag) { + retflag = 0; + breaks = obreaks; /* Is this ever not zero? */ + } + } else /* parse error */ + errflag |= ERRFLAG_ERROR; + if (rplypm) + rplypm->node.flags &= ~PM_READONLY_SPECIAL; + if (rplytmp && !errflag) { + int onoerrs = noerrs, rplylen; + noerrs = 2; + rplylen = zstuff(&cmdarg, rplytmp); + if (trim) { + /* bash and ksh strip trailing newlines here */ + while (rplylen > 0 && cmdarg[rplylen-1] == '\n') { + rplylen--; + if (trim == 1) + break; + } + cmdarg[rplylen] = 0; + } + noerrs = onoerrs; + if (rplylen >= 0) + setsparam(rplyvar, metafy(cmdarg, rplylen, META_REALLOC)); + } + unqueue_signals(); + } + + if (rplytmp) + unlink(rplytmp); + if (rplyvar) { + if (inchar != Inbrace) { + if ((val = dupstring(getsparam(rplyvar)))) + vunset = 0; + else { + vunset = 1; + val = dupstring(""); + } + } else { + s = dyncat(rplyvar, s); + rplyvar = NULL; + } + endparamscope(); + if (exit_pending) { + if (mypid == getpid()) { + /* + * paranoia: don't check for jobs, but there + * shouldn't be any if not interactive. + */ + stopmsg = 1; + zexit(exit_val, ZEXIT_NORMAL); + } else + _exit(exit_val); + } + } + /* * In ksh emulation a leading `!' is a special flag working - * sort of like our (k). + * sort of like our (k). This is true only for arrays or + * associative arrays and only with subscripts [*] or [@], + * so zsh's implementation is approximate. For namerefs + * in ksh, ${!ref} substitues the parameter name at the + * end of any chain of references, rather than the value. + * * TODO: this is one of very few cases tied directly to * the emulation mode rather than an option. Since ksh * doesn't have parameter flags it might be neater to * handle this with the ^, =, ~ stuff, below. */ if ((c = *s) == '!' && s[1] != Outbrace && EMULATION(EMULATE_KSH)) { - hkeys = SCANPM_WANTKEYS; + hkeys = SCANPM_WANTKEYS|SCANPM_NONAMEREF; s++; + /* There's a slew of other special bash meanings of parameter + * references that start with "!": + * ${!name} == ${(P)name} (when name is not a nameref) + * ${!name*} == ${(k)parameters[(I)name*]} + * ${!name@} == ${(@k)parameters[(I)name*]} + * ${!name[*]} == ${(k)name} (but indexes of ordinary arrays, too) + * ${!name[@]} == ${(@k)name} (ditto, as noted above for ksh) + * + * See also workers/34390, workers/34397, workers/34408. + */ } else if (c == '(' || c == Inpar) { char *t, sav; int tt = 0; @@ -2154,10 +2381,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, escapes = 1; break; + case '!': + if ((hkeys|hvals) & ~SCANPM_NONAMEREF) + goto flagerr; + hkeys = SCANPM_NONAMEREF; + break; case 'k': + if (hkeys & ~SCANPM_WANTKEYS) + goto flagerr; hkeys = SCANPM_WANTKEYS; break; case 'v': + if (hvals & ~SCANPM_WANTVALS) + goto flagerr; hvals = SCANPM_WANTVALS; break; @@ -2308,7 +2544,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, /* * Look for special unparenthesised flags. * TODO: could make these able to appear inside parentheses, too, - * i.e. ${(^)...} etc. + * i.e. ${(^)...} etc., but ${(~)...} already has another meaning. */ for (;;) { if ((c = *s) == '^' || c == Hat) { @@ -2332,7 +2568,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } } else if ((c == '#' || c == Pound) && (inbrace || !isset(POSIXIDENTIFIERS)) && - (itype_end(s+1, IIDENT, 0) != s + 1 + (itype_end(s+1, INAMESPC, 0) != s + 1 || (cc = s[1]) == '*' || cc == Star || cc == '@' || cc == '?' || cc == Quest || cc == '$' || cc == String || cc == Qstring @@ -2369,8 +2605,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Try to handle this when parameter is named * by (P) (second part of test). */ - if (itype_end(s+1, IIDENT, 0) != s+1 || (aspar && isstring(s[1]) && - (s[2] == Inbrace || s[2] == Inpar))) + if (itype_end(s+1, INAMESPC, 0) != s+1 || + (aspar && isstring(s[1]) && + (s[2] == Inbrace || s[2] == Inpar))) chkset = 1, s++; else if (!inbrace) { /* Special case for `$+' on its own --- leave unmodified */ @@ -2531,6 +2768,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, scanflags |= SCANPM_DQUOTED; if (chkset) scanflags |= SCANPM_CHECKING; + if (!inbrace) + scanflags |= SCANPM_NONAMESPC; /* * Second argument: decide whether to use the subexpression or * the string next on the line as the parameter name. @@ -2556,14 +2795,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * we let fetchvalue set the main string pointer s to * the end of the bit it's fetched. */ - if (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s), - (wantt ? -1 : - ((unset(KSHARRAYS) || inbrace) ? 1 : -1)), - scanflags)) || - (v->pm && (v->pm->node.flags & PM_UNSET)) || - (v->flags & VALFLAG_EMPTY)) + if (!rplyvar && + (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s), + (wantt ? -1 : + ((unset(KSHARRAYS) || inbrace) ? 1 : -1)), + scanflags)) || + (v->pm && (v->pm->node.flags & PM_UNSET)) || + (v->flags & VALFLAG_EMPTY))) vunset = 1; - if (wantt) { /* * Handle the (t) flag: value now becomes the type @@ -2573,13 +2812,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, !(v->pm->node.flags & PM_UNSET))) { int f = v->pm->node.flags; - switch (PM_TYPE(f)) { + 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; } val = dupstring(val); if (v->pm->level) @@ -2910,6 +3150,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, chuck(ptr); else ptr++; + } else if (c == Dnull) { + chuck(ptr); + while (*ptr && *ptr != c) + ptr++; + if (*ptr == Dnull) + chuck(ptr); + ptr--; /* Outer loop is about to increment */ } } replstr = (*ptr && ptr[1]) ? ptr+1 : ""; @@ -2926,6 +3173,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, */ if (!(flags & (SUB_MATCH|SUB_REST|SUB_BIND|SUB_EIND|SUB_LEN))) flags |= SUB_REST; + /* If matching at start and end, don't stop early */ + if ((flags & (SUB_START|SUB_END)) == (SUB_START|SUB_END)) + flags |= SUB_LONG; /* * With ":" treat a value as unset if the variable is set but @@ -3076,7 +3326,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, if (vunset) { if (isset(EXECOPT)) { *idend = '\0'; - zerr("%s: %s", idbeg, *s ? s : "parameter not set"); + if (*s){ + int l; + singsub(&s); + s = unmetafy(s, &l); + zerr("%s: %l", idbeg, s, l); + } else + zerr("%s: %s", idbeg, "parameter not set"); /* * In interactive shell we need to return to * top-level prompt --- don't clear this error @@ -3203,7 +3459,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, shortest = 0; ++s; } - if (*itype_end(s, IIDENT, 0)) { + if (*itype_end(s, INAMESPC, 0)) { untokenize(s); zerr("not an identifier: %s", s); return NULL; @@ -3219,6 +3475,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, char *sval; zip = getaparam(s); if (!zip) { + zip = gethparam(s); + } + if (!zip) { sval = getsparam(s); if (sval) zip = hmkarray(sval); @@ -3263,7 +3522,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, int intersect = (*s == '*' || *s == Star); char **compare, **ap, **apsrc; ++s; - if (*itype_end(s, IIDENT, 0)) { + if (*itype_end(s, INAMESPC, 0)) { untokenize(s); zerr("not an identifier: %s", s); return NULL; @@ -3716,6 +3975,8 @@ colonsubscript: if (presc) { int ops = opts[PROMPTSUBST], opb = opts[PROMPTBANG]; int opp = opts[PROMPTPERCENT]; + zattr savecurrent = txtcurrentattrs; + zattr saveunknown = txtunknownattrs; if (presc < 2) { opts[PROMPTPERCENT] = 1; @@ -3738,7 +3999,8 @@ colonsubscript: for (; *ap; ap++) { char *tmps; untokenize(*ap); - tmps = promptexpand(*ap, 0, NULL, NULL, NULL); + txtunknownattrs = TXT_ATTR_ALL; + tmps = promptexpand(*ap, 0, NULL, NULL); *ap = dupstring(tmps); free(tmps); } @@ -3747,10 +4009,14 @@ colonsubscript: if (!copied) val = dupstring(val), copied = 1; untokenize(val); - tmps = promptexpand(val, 0, NULL, NULL, NULL); + txtunknownattrs = TXT_ATTR_ALL; + tmps = promptexpand(val, 0, NULL, NULL); val = dupstring(tmps); free(tmps); } + + txtpendingattrs = txtcurrentattrs = savecurrent; + txtunknownattrs = saveunknown; opts[PROMPTSUBST] = ops; opts[PROMPTBANG] = opb; opts[PROMPTPERCENT] = opp; @@ -4309,6 +4575,8 @@ modify(char **str, char **ptr, int inbrace) break; case 's': + case 'S': + hsubpatopt = (**ptr == 'S'); c = **ptr; (*ptr)++; ptr1 = *ptr; @@ -4403,7 +4671,7 @@ modify(char **str, char **ptr, int inbrace) break; case '&': - c = 's'; + c = hsubpatopt ? 'S' : 's'; break; case 'g': @@ -4492,8 +4760,11 @@ modify(char **str, char **ptr, int inbrace) copy = casemodify(tt, CASMOD_UPPER); break; case 's': + case 'S': + hsubpatopt = (c == 'S'); if (hsubl && hsubr) - subst(©, hsubl, hsubr, gbal); + subst(©, dupstring(hsubl), dupstring(hsubr), + gbal, hsubpatopt); break; case 'q': copy = quotestring(copy, QT_BACKSLASH_SHOWNULL); @@ -4578,8 +4849,11 @@ modify(char **str, char **ptr, int inbrace) *str = casemodify(*str, CASMOD_UPPER); break; case 's': + case 'S': + hsubpatopt = (c == 'S'); if (hsubl && hsubr) - subst(str, hsubl, hsubr, gbal); + subst(str, dupstring(hsubl), dupstring(hsubr), + gbal, hsubpatopt); break; case 'q': *str = quotestring(*str, QT_BACKSLASH); diff --git a/Src/text.c b/Src/text.c index 56127c457..8b1bd96b6 100644 --- a/Src/text.c +++ b/Src/text.c @@ -335,7 +335,7 @@ getjobtext(Eprog prog, Wordcode c) tlim = tptr + JOBTEXTSIZE - 1; tjob = 1; gettext2(&s); - if (tptr[-1] == Meta) + if (tptr > jbuf && tptr[-1] == Meta) --tptr; *tptr = '\0'; freeeprog(prog); /* mark as unused */ diff --git a/Src/utils.c b/Src/utils.c index 62bd3e602..ce4e875fd 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -74,9 +74,6 @@ set_widearray(char *mb_array, Widechar_array wca) } wca->len = 0; - if (!isset(MULTIBYTE)) - return; - if (mb_array) { VARARR(wchar_t, tmpwcs, strlen(mb_array)); wchar_t *wcptr = tmpwcs; @@ -86,9 +83,8 @@ set_widearray(char *mb_array, Widechar_array wca) while (*mb_array) { int mblen; - if (STOUC(*mb_array) <= 0x7f) { - mb_array++; - *wcptr++ = (wchar_t)*mb_array; + if ((unsigned char) *mb_array <= 0x7f) { + *wcptr++ = (wchar_t)*mb_array++; continue; } @@ -126,14 +122,20 @@ set_widearray(char *mb_array, Widechar_array wca) (implemented by zerrmsg()): Code Argument types Prints - %s const char * C string (null terminated) - %l const char *, int C string of given length (null not required) + %s const char * C string (metafied, null terminated) + (output "nice") + %l const char *, int C string of given length (not metafied) + (output raw) %L long decimal value %d int decimal value %z zlong decimal value %% (none) literal '%' %c int character at that codepoint - %e int strerror() message (argument is typically 'errno') + %e int strerror() message (argument usually 'errno') + (output raw) + + For %s and %l, the caller is responsible for assuring end-of-string + is not in the middle of a metafy pair (%s) or a multibyte character. */ static void @@ -314,14 +316,9 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) nicezputs(str, file); break; case 'l': { - char *s; str = va_arg(ap, const char *); num = va_arg(ap, int); - num = metalen(str, num); - s = zhalloc(num + 1); - memcpy(s, str, num); - s[num] = '\0'; - nicezputs(s, file); + fwrite(str, num, 1, file); break; } case 'L': @@ -380,11 +377,13 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) fflush(file); } +#ifdef HAVE_SETUPTERM /* * Wrapper for setupterm() and del_curterm(). * These are called from terminfo.c and termcap.c. */ static int term_count; /* reference count of cur_term */ +#endif /**/ mod_export void @@ -717,7 +716,8 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep) */ /**/ -mod_export int is_wcs_nicechar(wchar_t c) +mod_export int +is_wcs_nicechar(wchar_t c) { if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) { if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20) @@ -1071,7 +1071,7 @@ get_username(void) cached_uid = current_uid; zsfree(cached_username); if ((pswd = getpwuid(current_uid))) - cached_username = ztrdup(pswd->pw_name); + cached_username = ztrdup_metafy(pswd->pw_name); else cached_username = ztrdup(""); } @@ -1543,7 +1543,8 @@ preprompt(void) if (!eolmark) eolmark = "%B%S%#%s%b"; opts[PROMPTPERCENT] = 1; - str = promptexpand(eolmark, 1, NULL, NULL, NULL); + txtunknownattrs = TXT_ATTR_ALL; + str = promptexpand(eolmark, 1, NULL, NULL); countprompt(str, &w, 0, -1); opts[PROMPTPERCENT] = percents; zputs(str, shout); @@ -1713,7 +1714,7 @@ printprompt4(void) opts[XTRACE] = 0; unmetafy(s, &l); s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC), - 0, NULL, NULL, NULL), &l); + 0, NULL, NULL), &l); opts[XTRACE] = t; fprintf(xtrerr, "%s", s); @@ -1732,6 +1733,13 @@ freestr(void *a) mod_export void gettyinfo(struct ttyinfo *ti) { + fdgettyinfo(SHTTY, ti); +} + +/**/ +mod_export void +fdgettyinfo(int SHTTY, struct ttyinfo *ti) +{ if (SHTTY != -1) { #ifdef HAVE_TERMIOS_H # ifdef HAVE_TCGETATTR @@ -1757,6 +1765,13 @@ gettyinfo(struct ttyinfo *ti) mod_export void settyinfo(struct ttyinfo *ti) { + fdsettyinfo(SHTTY, ti); +} + +/**/ +mod_export void +fdsettyinfo(int SHTTY, struct ttyinfo *ti) +{ if (SHTTY != -1) { #ifdef HAVE_TERMIOS_H # ifdef HAVE_TCGETATTR @@ -1995,6 +2010,28 @@ redup(int x, int y) { int ret = y; +#ifdef HAVE_FPURGE + /* Make sure buffers are cleared when changing descriptor for a + * FILE object. No fflush() here because the only way anything + * can legitimately be left in the buffer is when an error has + * occurred, so attempting flush here would at best error again + * and at worst squirt out something unexpected. + */ + if (stdout && y == fileno(stdout)) + fpurge(stdout); + if (stderr && y == fileno(stderr)) + fpurge(stderr); + if (shout && y == fileno(shout)) + fpurge(shout); + if (xtrerr && y == fileno(xtrerr)) + fpurge(xtrerr); +#ifndef _IONBF + /* See init.c setupshin() -- stdin otherwise unbuffered */ + if (stdin && y == fileno(stdin)) + fpurge(stdin); +#endif +#endif + if(x < 0) zclose(y); else if (x != y) { @@ -2920,7 +2957,7 @@ read1char(int echo) restore_queue_signals(q); if (echo) write_loop(SHTTY, &c, 1); - return STOUC(c); + return (unsigned char) c; } /**/ @@ -3122,7 +3159,7 @@ spckword(char **s, int hist, int cmd, int ask) if (**s == String && !*t) { guess = *s + 1; - if (itype_end(guess, IIDENT, 1) == guess) + if (itype_end(guess, INAMESPC, 1) == guess) return; ic = String; d = 100; @@ -3211,7 +3248,7 @@ spckword(char **s, int hist, int cmd, int ask) x = 'n'; } else if (shout) { char *pptbuf; - pptbuf = promptexpand(sprompt, 0, best, guess, NULL); + pptbuf = promptexpand(sprompt, 0, best, guess); zputs(pptbuf, shout); free(pptbuf); fflush(shout); @@ -4118,27 +4155,41 @@ inittyptab(void) * having IIDENT here is a good idea at all, but this code * should disappear into history... */ - for (t0 = 0240; t0 != 0400; t0++) - typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; + if isset(MULTIBYTE) + for (t0 = 0240; t0 != 0400; t0++) + typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; #endif /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */ typtab['_'] = IIDENT | IUSER; - typtab['-'] = typtab['.'] = typtab[STOUC(Dash)] = IUSER; + typtab['-'] = typtab['.'] = typtab[(unsigned char) Dash] = IUSER; typtab[' '] |= IBLANK | INBLANK; typtab['\t'] |= IBLANK | INBLANK; typtab['\n'] |= INBLANK; typtab['\0'] |= IMETA; - typtab[STOUC(Meta) ] |= IMETA; - typtab[STOUC(Marker)] |= IMETA; - for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(LAST_NORMAL_TOK); t0++) + typtab[(unsigned char) Meta ] |= IMETA; + typtab[(unsigned char) Marker] |= IMETA; + for (t0 = (int) (unsigned char) Pound; t0 <= (int) (unsigned char) LAST_NORMAL_TOK; t0++) typtab[t0] |= ITOK | IMETA; - for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++) + for (t0 = (int) (unsigned char) Snull; t0 <= (int) (unsigned char) Nularg; t0++) typtab[t0] |= ITOK | IMETA | INULL; - for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? - DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) { - int c = STOUC(*s == Meta ? *++s ^ 32 : *s); + /* ifs */ +#define CURRENT_DEFAULT_IFS (EMULATION(EMULATE_KSH|EMULATE_SH) ? \ + DEFAULT_IFS_SH : DEFAULT_IFS) +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + set_widearray(ifs ? ifs : CURRENT_DEFAULT_IFS, &ifs_wide); + if (ifs && !ifs_wide.chars) { + zwarn("IFS has an invalid character; resetting IFS to default"); + zsfree(ifs); + ifs = ztrdup(CURRENT_DEFAULT_IFS); + set_widearray(ifs, &ifs_wide); + } + } +#endif + for (s = ifs ? ifs : CURRENT_DEFAULT_IFS; *s; s++) { + int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT - if (!isascii(c)) { + if (isset(MULTIBYTE) && !isascii(c)) { /* see comment for wordchars below */ continue; } @@ -4151,10 +4202,15 @@ inittyptab(void) } typtab[c] |= ISEP; } + /* wordchars */ +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) + set_widearray(wordchars, &wordchars_wide); +#endif for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) { - int c = STOUC(*s == Meta ? *++s ^ 32 : *s); + int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT - if (!isascii(c)) { + if (isset(MULTIBYTE) && !isascii(c)) { /* * If we have support for multibyte characters, we don't * handle non-ASCII characters here; instead, we turn @@ -4167,22 +4223,17 @@ inittyptab(void) #endif typtab[c] |= IWORD; } -#ifdef MULTIBYTE_SUPPORT - set_widearray(wordchars, &wordchars_wide); - set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? - DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide); -#endif for (s = SPECCHARS; *s; s++) - typtab[STOUC(*s)] |= ISPECIAL; + typtab[(unsigned char) *s] |= ISPECIAL; if (typtab_flags & ZTF_SP_COMMA) - typtab[STOUC(',')] |= ISPECIAL; + typtab[(unsigned char) ','] |= ISPECIAL; if (isset(BANGHIST) && bangchar && (typtab_flags & ZTF_INTERACT)) { typtab_flags |= ZTF_BANGCHAR; typtab[bangchar] |= ISPECIAL; } else typtab_flags &= ~ZTF_BANGCHAR; for (s = PATCHARS; *s; s++) - typtab[STOUC(*s)] |= IPATTERN; + typtab[(unsigned char) *s] |= IPATTERN; unqueue_signals(); } @@ -4193,10 +4244,10 @@ makecommaspecial(int yesno) { if (yesno != 0) { typtab_flags |= ZTF_SP_COMMA; - typtab[STOUC(',')] |= ISPECIAL; + typtab[(unsigned char) ','] |= ISPECIAL; } else { typtab_flags &= ~ZTF_SP_COMMA; - typtab[STOUC(',')] &= ~ISPECIAL; + typtab[(unsigned char) ','] &= ~ISPECIAL; } } @@ -4309,13 +4360,27 @@ wcsitype(wchar_t c, int itype) * If "once" is set, just test the first character, i.e. (outptr != * inptr) tests whether the first character is valid in an identifier. * - * Currently this is only called with itype IIDENT, IUSER or ISEP. + * Currently called only with itype INAMESPC, IIDENT, IUSER or ISEP. */ /**/ mod_export char * itype_end(const char *ptr, int itype, int once) { + if (itype == INAMESPC) { + itype = IIDENT; + if (!isset(POSIXIDENTIFIERS) || EMULATION(EMULATE_KSH)) { + /* Special case for names containing ".", ksh93 namespaces */ + char *t = itype_end(ptr + (*ptr == '.'), itype, 0); + if (t > ptr + (*ptr == '.')) { + if (*t == '.') + ptr = t + 1; /* Fall through */ + else if (!once) + return t; + } + } + } + #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE) && (itype != IIDENT || !isset(POSIXIDENTIFIERS))) { @@ -4336,7 +4401,7 @@ itype_end(const char *ptr, int itype, int once) if (wc == WEOF) { /* invalid, treat as single character */ - int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr); + int chr = (unsigned char) (*ptr == Meta ? ptr[1] ^ 32 : *ptr); /* in this case non-ASCII characters can't match */ if (chr > 127 || !zistype(chr,itype)) break; @@ -4375,7 +4440,7 @@ itype_end(const char *ptr, int itype, int once) } else #endif for (;;) { - int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr); + int chr = (unsigned char) (*ptr == Meta ? ptr[1] ^ 32 : *ptr); if (!zistype(chr,itype)) break; ptr += (*ptr == Meta) ? 2 : 1; @@ -4983,11 +5048,11 @@ unmeta_one(const char *in, int *sz) *sz = mb_metacharlenconv_r(in, &wc, &wstate); #else if (in[0] == Meta) { - *sz = 2; - wc = STOUC(in[1] ^ 32); + *sz = 2; + wc = (unsigned char) (in[1] ^ 32); } else { - *sz = 1; - wc = STOUC(in[0]); + *sz = 1; + wc = (unsigned char) in[0]; } #endif return wc; @@ -5022,11 +5087,11 @@ ztrcmp(char const *s1, char const *s2) if(!(c1 = *s1)) c1 = -1; - else if(c1 == STOUC(Meta)) + else if(c1 == (unsigned char) Meta) c1 = *++s1 ^ 32; if(!(c2 = *s2)) c2 = -1; - else if(c2 == STOUC(Meta)) + else if(c2 == (unsigned char) Meta) c2 = *++s2 ^ 32; if(c1 == c2) @@ -5212,6 +5277,7 @@ nicedupstring(char const *s) } +/**/ #ifndef MULTIBYTE_SUPPORT /* Unmetafy and output a string, displaying special characters readably. */ @@ -5246,8 +5312,9 @@ niceztrlen(char const *s) } return l; } -#endif +/**/ +#endif /**/ #ifdef MULTIBYTE_SUPPORT @@ -5458,7 +5525,7 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp) const char *ptr; wchar_t wc; - if (STOUC(*s) <= 0x7f) { + if ((unsigned char) *s <= 0x7f) { if (wcp) *wcp = (wint_t)*s; return 1; @@ -5516,10 +5583,10 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp) mod_export int mb_metacharlenconv(const char *s, wint_t *wcp) { - if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) { + if (!isset(MULTIBYTE) || (unsigned char) *s <= 0x7f) { /* treat as single byte, possibly metafied */ if (wcp) - *wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s); + *wcp = (wint_t)(unsigned char) (*s == Meta ? s[1] ^ 32 : *s); return 1 + (*s == Meta); } /* @@ -5581,7 +5648,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr) inchar = *ptr; ptr++; - if (complete && STOUC(inchar) <= STOUC(0x7f)) { + if (complete && (unsigned char) inchar <= (unsigned char) 0x7f) { /* * We rely on 7-bit US-ASCII as a subset, so skip * multibyte handling if we have such a character. @@ -5657,7 +5724,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp) const char *ptr; wchar_t wc; - if (slen && STOUC(*s) <= 0x7f) { + if (slen && (unsigned char) *s <= 0x7f) { if (wcp) *wcp = (wint_t)*s; return 1; @@ -5698,7 +5765,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp) mod_export int mb_charlenconv(const char *s, int slen, wint_t *wcp) { - if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) { + if (!isset(MULTIBYTE) || (unsigned char) *s <= 0x7f) { if (wcp) *wcp = (wint_t)*s; return 1; @@ -5717,7 +5784,7 @@ mod_export int metacharlenconv(const char *x, int *c) { /* - * Here we don't use STOUC() on the chars since they + * Here we don't use an (unsigned char) cast on the chars since they * may be compared against other chars and this will fail * if chars are signed and the high bit is set. */ @@ -5779,7 +5846,7 @@ sb_niceformat(const char *s, FILE *stream, char **outstrp, int flags) eptr = ptr + umlen; while (ptr < eptr) { - int c = STOUC(*ptr); + int c = (unsigned char) *ptr; if (c == '\'' && (flags & NICEFLAG_QUOTE)) { fmt = "\\'"; newl = 2; @@ -5996,9 +6063,9 @@ addunprintable(char *v, const char *u, const char *uend) */ int c; if (*u == Meta) - c = STOUC(*++u ^ 32); + c = (unsigned char) (*++u ^ 32); else - c = STOUC(*u); + c = (unsigned char) *u; switch (c) { case '\0': *v++ = '\\'; @@ -6645,11 +6712,14 @@ dquotedzputs(char const *s, FILE *stream) # if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__) /* Convert a character from UCS4 encoding to UTF-8 */ -static size_t +static int ucs4toutf8(char *dest, unsigned int wval) { - size_t len; + int len; + /* UCS4 is now equvalent to UTF-32 and limited to 0 - 0x10_FFFF. + * This function accepts 0 - 0x7FFF_FFFF (old range of UCS4) to be + * compatible with wctomb(3) (in UTF-8 locale) on Linux. */ if (wval < 0x80) len = 1; else if (wval < 0x800) @@ -6660,8 +6730,12 @@ ucs4toutf8(char *dest, unsigned int wval) len = 4; else if (wval < 0x4000000) len = 5; - else + else if (wval < 0x80000000) len = 6; + else { + zerr("character not in range"); + return -1; + } switch (len) { /* falls through except to the last case */ case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6; @@ -6678,30 +6752,89 @@ ucs4toutf8(char *dest, unsigned int wval) } #endif +/* Convert UCS4 to a multibyte character in current locale. + * Result is saved in buf (must be at least MB_CUR_MAX bytes long). + * Returns the number of bytes saved in buf, or -1 if conversion fails. */ -/* - * The following only occurs once or twice in the code, but in different - * places depending how character set conversion is implemented. - */ -#define CHARSET_FAILED() \ - if (how & GETKEY_DOLLAR_QUOTE) { \ - while ((*tdest++ = *++s)) { \ - if (how & GETKEY_UPDATE_OFFSET) { \ - if (s - sstart > *misc) \ - (*misc)++; \ - } \ - if (*s == Snull) { \ - *len = (s - sstart) + 1; \ - *tdest = '\0'; \ - return buf; \ - } \ - } \ - *len = tdest - buf; \ - return buf; \ - } \ - *t = '\0'; \ - *len = t - buf; \ - return buf +/**/ +int +ucs4tomb(unsigned int wval, char *buf) +{ +#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) + int count = wctomb(buf, (wchar_t)wval); + if (count == -1) + zerr("character not in range"); + return count; +#else /* !(HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__) */ +# if defined(HAVE_NL_LANGINFO) && defined(CODESET) + if (!strcmp(nl_langinfo(CODESET), "UTF-8")) { + return ucs4toutf8(buf, wval); + } else { +# ifdef HAVE_ICONV + iconv_t cd; + char inbuf[4], *bsave = buf; + ICONV_CONST char *inptr = inbuf; + size_t inbytes = 4, outbytes = 6; + const char *codesetstr = nl_langinfo(CODESET); + size_t count; + int i; + + /* + * If the code set isn't handled, we'd better assume it's US-ASCII + * rather than just failing hopelessly. Solaris has a weird habit + * of returning 646. This is handled by the native iconv(), but + * not by GNU iconv; what's more, some versions of the native iconv + * don't handle standard names like ASCII. + * + * This should only be a problem if there's a mismatch between the + * NLS and the iconv in use, which probably only means if libiconv + * is in use. We checked at configure time if our libraries pulled + * in _libiconv_version, which should be a good test. + * + * It shouldn't ever be NULL, but while we're being paranoid... + */ +# ifdef ICONV_FROM_LIBICONV + if (!codesetstr || !*codesetstr) + codesetstr = "US-ASCII"; +# endif + cd = iconv_open(codesetstr, "UCS-4BE"); +# ifdef ICONV_FROM_LIBICONV + if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) { + codesetstr = "US-ASCII"; + cd = iconv_open(codesetstr, "UCS-4BE"); + } +# endif + if (cd == (iconv_t)-1) { + zerr("cannot do charset conversion (iconv failed)"); + return -1; + } + + /* store value in big endian form */ + for (i=3; i>=0; i--) { + inbuf[i] = wval & 0xff; + wval >>= 8; + } + count = iconv(cd, &inptr, &inbytes, &buf, &outbytes); + iconv_close(cd); + if (count) { + /* -1 indicates error. Positive value means number of "invalid" + * (or "non-reversible") conversions, which we consider as + * "out-of-range" characters. */ + zerr("character not in range"); + return -1; + } + return buf - bsave; +# else /* !HAVE_ICONV */ + zerr("cannot do charset conversion (iconv not available)"); + return -1; +# endif /* HAVE_ICONV */ + } +# else /* !(HAVE_NL_LANGINFO && CODESET) */ + zerr("cannot do charset conversion (NLS not supported)"); + return -1; +# endif /* HAVE_NL_LANGINFO && CODESET */ +#endif /* HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__ */ +} /* * Decode a key string, turning it into the literal characters. @@ -6758,21 +6891,6 @@ getkeystring(char *s, int *len, int how, int *misc) char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL; char svchar = '\0'; int meta = 0, control = 0, ignoring = 0; - int i; -#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) - wint_t wval; - int count; -#else - unsigned int wval; -# if defined(HAVE_NL_LANGINFO) && defined(CODESET) -# if defined(HAVE_ICONV) - iconv_t cd; - char inbuf[4]; - size_t inbytes, outbytes; -# endif - size_t count; -# endif -#endif DPUTS((how & GETKEY_UPDATE_OFFSET) && (how & ~(GETKEYS_DOLLARS_QUOTE|GETKEY_UPDATE_OFFSET)), @@ -6837,7 +6955,8 @@ getkeystring(char *s, int *len, int how, int *misc) } for (; *s; s++) { if (*s == '\\' && s[1]) { - int miscadded; + int miscadded, count, i; + unsigned int wval; if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) { (*misc)--; miscadded = 1; @@ -6952,86 +7071,32 @@ getkeystring(char *s, int *len, int how, int *misc) *misc = wval; return s+1; } -#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) - count = wctomb(t, (wchar_t)wval); + count = ucs4tomb(wval, t); if (count == -1) { - zerr("character not in range"); - CHARSET_FAILED(); + if (how & GETKEY_DOLLAR_QUOTE) { + while ((*tdest++ = *++s)) { + if (how & GETKEY_UPDATE_OFFSET) { + if (s - sstart > *misc) + (*misc)++; + } + if (*s == Snull) { + *len = (s - sstart) + 1; + *tdest = '\0'; + return buf; + } + } + *len = tdest - buf; + } + else { + *t = '\0'; + *len = t - buf; + } + return buf; } if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) (*misc) += count; t += count; -# else -# if defined(HAVE_NL_LANGINFO) && defined(CODESET) - if (!strcmp(nl_langinfo(CODESET), "UTF-8")) { - count = ucs4toutf8(t, wval); - t += count; - if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) - (*misc) += count; - } else { -# ifdef HAVE_ICONV - ICONV_CONST char *inptr = inbuf; - const char *codesetstr = nl_langinfo(CODESET); - inbytes = 4; - outbytes = 6; - /* store value in big endian form */ - for (i=3;i>=0;i--) { - inbuf[i] = wval & 0xff; - wval >>= 8; - } - /* - * If the code set isn't handled, we'd better - * assume it's US-ASCII rather than just failing - * hopelessly. Solaris has a weird habit of - * returning 646. This is handled by the - * native iconv(), but not by GNU iconv; what's - * more, some versions of the native iconv don't - * handle standard names like ASCII. - * - * This should only be a problem if there's a - * mismatch between the NLS and the iconv in use, - * which probably only means if libiconv is in use. - * We checked at configure time if our libraries - * pulled in _libiconv_version, which should be - * a good test. - * - * It shouldn't ever be NULL, but while we're - * being paranoid... - */ -#ifdef ICONV_FROM_LIBICONV - if (!codesetstr || !*codesetstr) - codesetstr = "US-ASCII"; -#endif - cd = iconv_open(codesetstr, "UCS-4BE"); -#ifdef ICONV_FROM_LIBICONV - if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) { - codesetstr = "US-ASCII"; - cd = iconv_open(codesetstr, "UCS-4BE"); - } -#endif - if (cd == (iconv_t)-1) { - zerr("cannot do charset conversion (iconv failed)"); - CHARSET_FAILED(); - } - count = iconv(cd, &inptr, &inbytes, &t, &outbytes); - iconv_close(cd); - if (count == (size_t)-1) { - zerr("character not in range"); - CHARSET_FAILED(); - } - if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) - (*misc) += count; -# else - zerr("cannot do charset conversion (iconv not available)"); - CHARSET_FAILED(); -# endif - } -# else - zerr("cannot do charset conversion (NLS not supported)"); - CHARSET_FAILED(); -# endif -# endif if (how & GETKEY_DOLLAR_QUOTE) { char *t2; for (t2 = tbuf; t2 < t; t2++) { @@ -7104,7 +7169,7 @@ getkeystring(char *s, int *len, int how, int *misc) continue; #ifdef MULTIBYTE_SUPPORT } else if ((how & GETKEY_SINGLE_CHAR) && - isset(MULTIBYTE) && STOUC(*s) > 127) { + isset(MULTIBYTE) && (unsigned char) *s > 127) { wint_t wc; int len; len = mb_metacharlenconv(s, &wc); @@ -7207,7 +7272,7 @@ getkeystring(char *s, int *len, int how, int *misc) t = tbuf; } if ((how & GETKEY_SINGLE_CHAR) && t != tmp) { - *misc = STOUC(tmp[0]); + *misc = (unsigned char) tmp[0]; return s + 1; } } @@ -7498,8 +7563,8 @@ restoredir(struct dirsav *d) else if (d->level < 0) err = -1; if (d->dev || d->ino) { - stat(".", &sbuf); - if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev) + if (stat(".", &sbuf) < 0 || + sbuf.st_ino != d->ino || sbuf.st_dev != d->dev) err = -2; } return err; @@ -7524,9 +7589,9 @@ privasserted(void) /* POSIX doesn't define a way to test whether a capability set * * is empty or not. Typical. I hope this is conforming... */ cap_flag_value_t val; - cap_value_t n; - for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++) - if(val) { + cap_value_t cap; + for(cap = 0; !cap_get_flag(caps, cap, CAP_EFFECTIVE, &val); cap++) + if(val && cap != CAP_WAKE_ALARM) { cap_free(caps); return 1; } @@ -7570,6 +7635,7 @@ mode_to_octal(mode_t mode) return m; } +/**/ #ifdef MAILDIR_SUPPORT /* * Stat a file. If it's a maildir, check all messages @@ -7693,4 +7759,6 @@ mailstat(char *path, struct stat *st) *st = st_ret_last = st_ret; return 0; } + +/**/ #endif diff --git a/Src/zsh.h b/Src/zsh.h index 40f9ea537..090abf8f5 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -135,19 +135,6 @@ struct mathfunc { #define STRMATHFUNC(name, func, id) \ { NULL, name, MFF_STR, NULL, func, NULL, 0, 0, id } -/* Character tokens are sometimes casted to (unsigned char)'s. * - * Unfortunately, some compilers don't correctly cast signed to * - * unsigned promotions; i.e. (int)(unsigned char)((char) -1) evaluates * - * to -1, instead of 255 like it should. We circumvent the troubles * - * of such shameful delinquency by casting to a larger unsigned type * - * then back down to unsigned char. */ - -#ifdef BROKEN_SIGNED_TO_UNSIGNED_CASTING -# define STOUC(X) ((unsigned char)(unsigned short)(X)) -#else -# define STOUC(X) ((unsigned char)(X)) -#endif - /* Meta together with the character following Meta denotes the character * * which is the exclusive or of 32 and the character following Meta. * * This is used to represent characters which otherwise has special * @@ -309,6 +296,9 @@ enum { /* * Lexical tokens: unlike the character tokens above, these never * appear in strings and don't necessarily represent a single character. + * + * See Src/lex.c:tokstrings[] for hints on what these mean. Note that + * SEPER or SEMI are both stringified as ";". */ enum lextok { @@ -628,7 +618,7 @@ union linkroot { /* Specific elements of linked lists */ /*************************************/ -typedef void (*voidvoidfnptr_t) _((void)); +typedef void (*voidvoidfnptr_t) (void); /* * Element of the prepromptfns list. @@ -688,7 +678,7 @@ struct timedfn { #define COND_MOD 18 #define COND_MODI 19 -typedef int (*CondHandler) _((char **, int)); +typedef int (*CondHandler) (char **, int); struct conddef { Conddef next; /* next in list */ @@ -1174,28 +1164,28 @@ struct dirsav { /* Definitions for Hash Tables */ /*******************************/ -typedef void *(*VFunc) _((void *)); -typedef void (*FreeFunc) _((void *)); +typedef void *(*VFunc) (void *); +typedef void (*FreeFunc) (void *); -typedef unsigned (*HashFunc) _((const char *)); -typedef void (*TableFunc) _((HashTable)); +typedef unsigned (*HashFunc) (const char *); +typedef void (*TableFunc) (HashTable); /* * Note that this is deliberately "char *", not "const char *", * since the AddNodeFunc is passed a pointer to a string that * will be stored and later freed. */ -typedef void (*AddNodeFunc) _((HashTable, char *, void *)); -typedef HashNode (*GetNodeFunc) _((HashTable, const char *)); -typedef HashNode (*RemoveNodeFunc) _((HashTable, const char *)); -typedef void (*FreeNodeFunc) _((HashNode)); -typedef int (*CompareFunc) _((const char *, const char *)); +typedef void (*AddNodeFunc) (HashTable, char *, void *); +typedef HashNode (*GetNodeFunc) (HashTable, const char *); +typedef HashNode (*RemoveNodeFunc) (HashTable, const char *); +typedef void (*FreeNodeFunc) (HashNode); +typedef int (*CompareFunc) (const char *, const char *); /* type of function that is passed to * * scanhashtable or scanmatchtable */ -typedef void (*ScanFunc) _((HashNode, int)); -typedef void (*ScanTabFunc) _((HashTable, ScanFunc, int)); +typedef void (*ScanFunc) (HashNode, int); +typedef void (*ScanTabFunc) (HashTable, ScanFunc, int); -typedef void (*PrintTableStats) _((HashTable)); +typedef void (*PrintTableStats) (HashTable); /* Hash table for standard open hashing. Instances of struct hashtable can be * * created only by newhashtable(). In fact, this function creates an instance * @@ -1362,7 +1352,7 @@ struct funcstack { /* node in list of function call wrappers */ -typedef int (*WrapFunc) _((Eprog, FuncWrap, char *)); +typedef int (*WrapFunc) (Eprog, FuncWrap, char *); struct funcwrap { FuncWrap next; @@ -1438,8 +1428,8 @@ enum { * builtin structure. */ -typedef int (*HandlerFunc) _((char *, char **, Options, int)); -typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int)); +typedef int (*HandlerFunc) (char *, char **, Options, int); +typedef int (*HandlerFuncAssign) (char *, char **, LinkList, Options, int); #define NULLBINCMD ((HandlerFunc) 0) struct builtin { @@ -1536,10 +1526,10 @@ struct module { /* Module record is an alias */ #define MOD_ALIAS (1<<6) -typedef int (*Module_generic_func) _((void)); -typedef int (*Module_void_func) _((Module)); -typedef int (*Module_features_func) _((Module, char ***)); -typedef int (*Module_enables_func) _((Module, int **)); +typedef int (*Module_generic_func) (void); +typedef int (*Module_void_func) (Module); +typedef int (*Module_features_func) (Module, char ***); +typedef int (*Module_enables_func) (Module, int **); struct linkedmod { char *name; @@ -1584,11 +1574,11 @@ struct feature_enables { /* C-function hooks */ -typedef int (*Hookfn) _((Hookdef, void *)); +typedef int (*Hookfn) (Hookdef, void *); struct hookdef { Hookdef next; - char *name; + const char *name; Hookfn def; int flags; LinkList funcs; @@ -1799,33 +1789,33 @@ typedef const struct gsu_array *GsuArray; typedef const struct gsu_hash *GsuHash; struct gsu_scalar { - char *(*getfn) _((Param)); - void (*setfn) _((Param, char *)); - void (*unsetfn) _((Param, int)); + char *(*getfn) (Param); + void (*setfn) (Param, char *); + void (*unsetfn) (Param, int); }; struct gsu_integer { - zlong (*getfn) _((Param)); - void (*setfn) _((Param, zlong)); - void (*unsetfn) _((Param, int)); + zlong (*getfn) (Param); + void (*setfn) (Param, zlong); + void (*unsetfn) (Param, int); }; struct gsu_float { - double (*getfn) _((Param)); - void (*setfn) _((Param, double)); - void (*unsetfn) _((Param, int)); + double (*getfn) (Param); + void (*setfn) (Param, double); + void (*unsetfn) (Param, int); }; struct gsu_array { - char **(*getfn) _((Param)); - void (*setfn) _((Param, char **)); - void (*unsetfn) _((Param, int)); + char **(*getfn) (Param); + void (*setfn) (Param, char **); + void (*unsetfn) (Param, int); }; struct gsu_hash { - HashTable (*getfn) _((Param)); - void (*setfn) _((Param, HashTable)); - void (*unsetfn) _((Param, int)); + HashTable (*getfn) (Param); + void (*setfn) (Param, HashTable); + void (*unsetfn) (Param, int); }; @@ -1862,8 +1852,9 @@ struct param { GsuHash h; } gsu; - int base; /* output base or floating point prec */ - int width; /* field width */ + int base; /* output base or floating point prec or */ + /* for namerefs, locallevel of reference */ + int width; /* field width or nameref subscript idx */ char *env; /* location in environment, if exported */ char *ename; /* name of corresponding environment var */ Param old; /* old struct for use with local */ @@ -1942,9 +1933,13 @@ struct tieddata { */ #define PM_HASHELEM (1<<28) /* is a hash-element */ #define PM_NAMEDDIR (1<<29) /* has a corresponding nameddirtab entry */ +#define PM_NAMEREF (1<<30) /* pointer to a different parameter */ + +#define PM_SELFREF PM_UNIQUE /* Overload when namerefs resolved */ +#define PM_NEWREF PM_SINGLE /* Overload in for-loop namerefs */ /* The option string corresponds to the first of the variables above */ -#define TYPESET_OPTSTR "aiEFALRZlurtxUhHTkz" +#define TYPESET_OPTSTR "aiEFALRZlurtxUhHT" /* These typeset options take an optional numeric argument */ #define TYPESET_OPTNUM "LRZiEF" @@ -1967,6 +1962,10 @@ struct tieddata { * elements */ #define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */ +#define SCANPM_NOEXEC (1<<11) /* No command substitutions, etc. */ +#define SCANPM_NONAMESPC (1<<12) /* namespace syntax not allowed */ +#define SCANPM_NONAMEREF (1<<13) /* named references are not followed */ + /* "$foo[@]"-style substitution * Only sign bit is significant */ @@ -2185,6 +2184,7 @@ typedef groupset *Groupset; #define PRINT_LINE (1<<6) #define PRINT_POSIX_EXPORT (1<<7) #define PRINT_POSIX_READONLY (1<<8) +#define PRINT_WITH_NAMESPACE (1<<9) /* flags for printing for the whence builtin */ #define PRINT_WHENCE_CSH (1<<7) @@ -2220,8 +2220,6 @@ enum noerrexit_bits { NOERREXIT_EXIT = 1, /* Suppress ERR_RETURN: per function call */ NOERREXIT_RETURN = 2, - /* NOERREXIT only needed on way down */ - NOERREXIT_UNTIL_EXEC = 4, /* Force exit on SIGINT */ NOERREXIT_SIGNAL = 8 }; @@ -2658,22 +2656,25 @@ struct ttyinfo { #define TCDELLINE 16 #define TCNEXTTAB 17 #define TCBOLDFACEBEG 18 -#define TCSTANDOUTBEG 19 -#define TCUNDERLINEBEG 20 -#define TCALLATTRSOFF 21 -#define TCSTANDOUTEND 22 -#define TCUNDERLINEEND 23 -#define TCHORIZPOS 24 -#define TCUPCURSOR 25 -#define TCDOWNCURSOR 26 -#define TCLEFTCURSOR 27 -#define TCRIGHTCURSOR 28 -#define TCSAVECURSOR 29 -#define TCRESTRCURSOR 30 -#define TCBACKSPACE 31 -#define TCFGCOLOUR 32 -#define TCBGCOLOUR 33 -#define TC_COUNT 34 +#define TCFAINTBEG 19 +#define TCSTANDOUTBEG 20 +#define TCUNDERLINEBEG 21 +#define TCITALICSBEG 22 +#define TCALLATTRSOFF 23 +#define TCSTANDOUTEND 24 +#define TCUNDERLINEEND 25 +#define TCITALICSEND 26 +#define TCHORIZPOS 27 +#define TCUPCURSOR 28 +#define TCDOWNCURSOR 29 +#define TCLEFTCURSOR 30 +#define TCRIGHTCURSOR 31 +#define TCSAVECURSOR 32 +#define TCRESTRCURSOR 33 +#define TCBACKSPACE 34 +#define TCFGCOLOUR 35 +#define TCBGCOLOUR 36 +#define TC_COUNT 37 #define tccan(X) (tclen[X]) @@ -2688,38 +2689,27 @@ struct ttyinfo { #endif #define TXTBOLDFACE 0x0001 -#define TXTSTANDOUT 0x0002 -#define TXTUNDERLINE 0x0004 -#define TXTFGCOLOUR 0x0008 -#define TXTBGCOLOUR 0x0010 - -#define TXT_ATTR_ON_MASK 0x001F - -#define txtisset(X) (txtattrmask & (X)) -#define txtset(X) (txtattrmask |= (X)) -#define txtunset(X) (txtattrmask &= ~(X)) - -#define TXTNOBOLDFACE 0x0020 -#define TXTNOSTANDOUT 0x0040 -#define TXTNOUNDERLINE 0x0080 -#define TXTNOFGCOLOUR 0x0100 -#define TXTNOBGCOLOUR 0x0200 - -#define TXT_ATTR_OFF_MASK 0x03E0 -/* Bits to shift off right to get on */ -#define TXT_ATTR_OFF_ON_SHIFT 5 -#define TXT_ATTR_OFF_FROM_ON(attr) \ - (((attr) & TXT_ATTR_ON_MASK) << TXT_ATTR_OFF_ON_SHIFT) -#define TXT_ATTR_ON_FROM_OFF(attr) \ - (((attr) & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT) +#define TXTFAINT 0x0002 +#define TXTSTANDOUT 0x0004 +#define TXTUNDERLINE 0x0008 +#define TXTITALIC 0x0010 +#define TXTFGCOLOUR 0x0020 +#define TXTBGCOLOUR 0x0040 + +#define TXT_ATTR_ALL 0x007F + /* * Indicates to zle_refresh.c that the character entry is an * index into the list of multiword symbols. */ #define TXT_MULTIWORD_MASK 0x0400 -/* used when, e.g an invalid colour is specified */ -#define TXT_ERROR 0x0800 +/* Used when, e.g an invalid colour is specified. Also used in REFRESH_ELEMENT + * to indicate that attributes should remain unchanged. */ +#define TXT_ERROR 0xF00000F000000003 + +/* Mask for font weight */ +#define TXT_ATTR_FONT_WEIGHT (TXTBOLDFACE|TXTFAINT) /* Mask for colour to use in foreground */ #define TXT_ATTR_FG_COL_MASK 0x000000FFFFFF0000 @@ -2735,26 +2725,19 @@ struct ttyinfo { /* Flag to indicate that background is a 24-bit colour */ #define TXT_ATTR_BG_24BIT 0x8000 -/* Things to turn on, including values for the colour elements */ -#define TXT_ATTR_ON_VALUES_MASK \ - (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\ - TXT_ATTR_FG_24BIT|TXT_ATTR_BG_24BIT) - /* Mask out everything to do with setting a foreground colour */ -#define TXT_ATTR_FG_ON_MASK \ +#define TXT_ATTR_FG_MASK \ (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_24BIT) /* Mask out everything to do with setting a background colour */ -#define TXT_ATTR_BG_ON_MASK \ +#define TXT_ATTR_BG_MASK \ (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_24BIT) /* Mask out everything to do with activating colours */ -#define TXT_ATTR_COLOUR_ON_MASK \ - (TXT_ATTR_FG_ON_MASK|TXT_ATTR_BG_ON_MASK) +#define TXT_ATTR_COLOUR_MASK \ + (TXT_ATTR_FG_MASK|TXT_ATTR_BG_MASK) -#define txtchangeisset(T,X) ((T) & (X)) #define txtchangeget(T,A) (((T) & A ## _MASK) >> A ## _SHIFT) -#define txtchangeset(T, X, Y) ((void)(T && (*T &= ~(Y), *T |= (X)))) /* * For outputting sequences to change colour: specify foreground @@ -2762,7 +2745,6 @@ struct ttyinfo { */ #define COL_SEQ_FG (0) #define COL_SEQ_BG (1) -#define COL_SEQ_COUNT (2) struct color_rgb { unsigned int red, green, blue; @@ -2778,11 +2760,7 @@ enum { /* Raw output: use stdout rather than shout */ TSC_RAW = 0x0001, /* Output to current prompt buffer: only used when assembling prompt */ - TSC_PROMPT = 0x0002, - /* Mask to get the output mode */ - TSC_OUTPUT_MASK = 0x0003, - /* Change needs reset of other attributes */ - TSC_DIRTY = 0x0004 + TSC_PROMPT = 0x0002 }; /****************************************/ @@ -3006,7 +2984,7 @@ enum errflag_bits { /* Sorting */ /***********/ -typedef int (*CompareFn) _((const void *, const void *)); +typedef int (*CompareFn) (const void *, const void *); enum { SORTIT_ANYOLDHOW = 0, /* Defaults */ @@ -3064,13 +3042,13 @@ struct hist_stack { short *chwords; int chwordlen; int chwordpos; - int (*hgetc) _((void)); - void (*hungetc) _((int)); - void (*hwaddc) _((int)); - void (*hwbegin) _((int)); - void (*hwabort) _((void)); - void (*hwend) _((void)); - void (*addtoline) _((int)); + int (*hgetc) (void); + void (*hungetc) (int); + void (*hwaddc) (int); + void (*hwbegin) (int); + void (*hwabort) (void); + void (*hwend) (void); + void (*addtoline) (int); unsigned char *cstack; int csp; int hist_keep_comment; @@ -3240,7 +3218,7 @@ enum { /* compctl entry point pointers */ -typedef int (*CompctlReadFn) _((char *, char **, Options, char *)); +typedef int (*CompctlReadFn) (char *, char **, Options, char *); /* ZLE entry point pointer */ diff --git a/Src/zsh_system.h b/Src/zsh_system.h index 6f4efce96..5c004d53e 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -82,12 +82,6 @@ */ #define _STRPTIME_DONTZERO -#ifdef PROTOTYPES -# define _(Args) Args -#else -# define _(Args) () -#endif - #ifndef HAVE_ALLOCA # define alloca zhalloc #else @@ -101,7 +95,7 @@ # pragma alloca # else # ifndef alloca -char *alloca _((size_t)); +char *alloca (size_t); # endif # endif # endif @@ -783,7 +777,8 @@ extern char **environ; * We always need setenv and unsetenv in pairs, because * we don't know how to do memory management on the values set. */ -#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && !defined(__APPLE__) +#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) \ + && !defined(SETENV_MANGLES_EQUAL) # define USE_SET_UNSET_ENV #endif diff --git a/Src/ztype.h b/Src/ztype.h index 5c85b0cd7..4675f73a9 100644 --- a/Src/ztype.h +++ b/Src/ztype.h @@ -43,7 +43,8 @@ #define IWSEP (1 << 13) #define INULL (1 << 14) #define IPATTERN (1 << 15) -#define zistype(X,Y) (typtab[STOUC(X)] & Y) +#define INAMESPC (1 << 16) +#define zistype(X,Y) (typtab[(unsigned char) (X)] & Y) #define idigit(X) zistype(X,IDIGIT) #define ialnum(X) zistype(X,IALNUM) #define iblank(X) zistype(X,IBLANK) /* blank, not including \n */ |