diff options
author | Axel Beckert <abe@deuxchevaux.org> | 2016-12-04 04:32:03 +0100 |
---|---|---|
committer | Axel Beckert <abe@deuxchevaux.org> | 2016-12-04 04:32:03 +0100 |
commit | 3e439c3863f14c82f70666804c8570a13b3732e6 (patch) | |
tree | 07036c43e0f3f9242bb6dd42cd2a849ec8ea8aca /Src | |
parent | 2aedc4b88fd0e87b89583983951b04b96f48efd3 (diff) | |
parent | 7b7e84f0815ed22a0ee348a217776529035dccf3 (diff) | |
download | zsh-3e439c3863f14c82f70666804c8570a13b3732e6.tar.gz zsh-3e439c3863f14c82f70666804c8570a13b3732e6.zip |
Merge tag 'zsh-5.2-test-1' into debian
Diffstat (limited to 'Src')
75 files changed, 4069 insertions, 1008 deletions
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index 85ec1811c..29f97b41d 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -610,14 +610,16 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func)) return 1; } } else { - /* memory-type resource -- `k' and `M' modifiers are permitted, - meaning (respectively) 2^10 and 2^20. */ + /* memory-type resource -- `k', `M' and `G' modifiers are * + * permitted, meaning (respectively) 2^10, 2^20 and 2^30. */ val = zstrtorlimt(s, &s, 10); if (!*s || ((*s == 'k' || *s == 'K') && !s[1])) { if (val != RLIM_INFINITY) val *= 1024L; } else if ((*s == 'M' || *s == 'm') && !s[1]) val *= 1024L * 1024; + else if ((*s == 'G' || *s == 'g') && !s[1]) + val *= 1024L * 1024 * 1024; else { zwarnnam(nam, "unknown scaling factor: %s", s); return 1; @@ -1037,7 +1039,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c index 5d5dac6b7..835e72cb7 100644 --- a/Src/Builtins/sched.c +++ b/Src/Builtins/sched.c @@ -58,7 +58,7 @@ static int schedcmdtimed; /**/ static void -schedaddtimed(time_t t) +schedaddtimed(void) { /* * The following code shouldn't be necessary and indicates @@ -140,7 +140,7 @@ checksched(void) */ DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (1)"); - schedaddtimed(schedcmds->time); + schedaddtimed(); } } } @@ -180,7 +180,7 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) schedcmds = sch->next; if (schedcmds) { DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (2)"); - schedaddtimed(schedcmds->time); + schedaddtimed(); } } zsfree(sch->cmd); @@ -317,7 +317,7 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) sch->next = schedcmds; schedcmds = sch; DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (3)"); - schedaddtimed(t); + schedaddtimed(); } else { for (sch2 = schedcmds; sch2->next && sch2->next->time < sch->time; @@ -330,7 +330,7 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) sch->next = NULL; schedcmds = sch; DPUTS(timedfns && firstnode(timedfns), "BUG: already timed fn (4)"); - schedaddtimed(t); + schedaddtimed(); } return 0; } @@ -353,7 +353,11 @@ schedgetfn(UNUSED(Param pm)) time_t t; t = sch->time; - sprintf(tbuf, "%ld", t); +#if defined(PRINTF_HAS_LLD) + sprintf(tbuf, "%lld", (long long)t); +#else + sprintf(tbuf, "%ld", (long)t); +#endif if (sch->flags & SCHEDFLAG_TRASH_ZLE) flagstr = "-o"; else @@ -411,7 +415,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { addprepromptfn(&checksched); return 0; diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index 64329f643..63c6748f5 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -202,7 +202,7 @@ static const struct zcurses_mouse_event zcurses_mouse_map[] = { { 0, 0, 0 } }; -mmask_t zcurses_mouse_mask = ALL_MOUSE_EVENTS; +static mmask_t zcurses_mouse_mask = ALL_MOUSE_EVENTS; #endif @@ -299,7 +299,7 @@ zcurses_free_window(ZCWin w) } static struct zcurses_namenumberpair * -zcurses_attrget(WINDOW *w, char *attr) +zcurses_attrget(UNUSED(WINDOW *w), char *attr) { struct zcurses_namenumberpair *zca; @@ -324,7 +324,7 @@ zcurses_color(const char *color) return (short)zc->number; } - return (short)-1; + return (short)-2; } static Colorpairnode @@ -350,13 +350,25 @@ zcurses_colorget(const char *nam, char *colorpair) } *bg = '\0'; - f = zcurses_color(cp); - b = zcurses_color(bg+1); - if (f==-1 || b==-1) { - if (f == -1) + // cp/bg can be {number}/{number} or {name}/{name} + + if( cp[0] >= '0' && cp[0] <= '9' ) { + f = atoi(cp); + } else { + f = zcurses_color(cp); + } + + if( (bg+1)[0] >= '0' && (bg+1)[0] <= '9' ) { + b = atoi(bg+1); + } else { + b = zcurses_color(bg+1); + } + + if (f==-2 || b==-2) { + if (f == -2) zwarnnam(nam, "foreground color `%s' not known", cp); - if (b == -1) + if (b == -2) zwarnnam(nam, "background color `%s' not known", bg+1); *bg = '/'; zsfree(cp); @@ -419,7 +431,7 @@ freecolorpairnode(HashNode hn) *************/ static int -zccmd_init(const char *nam, char **args) +zccmd_init(UNUSED(const char *nam), UNUSED(char **args)) { LinkNode stdscr_win = zcurses_getwindowbyname("stdscr"); @@ -808,7 +820,7 @@ zccmd_border(const char *nam, char **args) static int -zccmd_endwin(const char *nam, char **args) +zccmd_endwin(UNUSED(const char *nam), UNUSED(char **args)) { LinkNode stdscr_win = zcurses_getwindowbyname("stdscr"); @@ -1496,7 +1508,7 @@ zccmd_touch(const char *nam, char **args) /**/ static int -bin_zcurses(char *nam, char **args, Options ops, UNUSED(int func)) +bin_zcurses(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { char **saargs; struct zcurses_subcommand *zcsc; @@ -1693,7 +1705,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { zcurses_windows = znewlinklist(); diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c index 86c61cf1c..bb82c542f 100644 --- a/Src/Modules/datetime.c +++ b/Src/Modules/datetime.c @@ -292,7 +292,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index 032963262..8dd60fc0d 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -390,7 +390,7 @@ boot_(UNUSED(Module m)) /**/ int -cleanup_(UNUSED(Module m)) +cleanup_(Module m) { return setfeatureenables(m, &module_features, NULL); } diff --git a/Src/Modules/files.c b/Src/Modules/files.c index dbcff6307..6f816bac0 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -805,7 +805,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/langinfo.c b/Src/Modules/langinfo.c index eb45d161b..520b0370c 100644 --- a/Src/Modules/langinfo.c +++ b/Src/Modules/langinfo.c @@ -498,7 +498,7 @@ boot_(UNUSED(Module m)) /**/ int -cleanup_(UNUSED(Module m)) +cleanup_(Module m) { return setfeatureenables(m, &module_features, NULL); } diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c index 422c7077c..2503b361e 100644 --- a/Src/Modules/mapfile.c +++ b/Src/Modules/mapfile.c @@ -306,7 +306,7 @@ boot_(UNUSED(Module m)) /**/ int -cleanup_(UNUSED(Module m)) +cleanup_(Module m) { return setfeatureenables(m, &module_features, NULL); } diff --git a/Src/Modules/mathfunc.c b/Src/Modules/mathfunc.c index efadd86ff..a7e8b294c 100644 --- a/Src/Modules/mathfunc.c +++ b/Src/Modules/mathfunc.c @@ -411,7 +411,11 @@ math_func(char *name, int argc, mnumber *argv, int id) break; case MF_SCALB: +#ifdef HAVE_SCALBN + retd = scalbn(argd, argi); +#else retd = scalb(argd, argi); +#endif break; #ifdef HAVE_SIGNGAM @@ -599,7 +603,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/newuser.c b/Src/Modules/newuser.c index efdb2abba..6f0d2e0aa 100644 --- a/Src/Modules/newuser.c +++ b/Src/Modules/newuser.c @@ -41,14 +41,14 @@ setup_(UNUSED(Module m)) /**/ int -features_(Module m, char ***features) +features_(UNUSED(Module m), UNUSED(char ***features)) { return 1; } /**/ int -enables_(Module m, int **enables) +enables_(UNUSED(Module m), UNUSED(int **enables)) { return 0; } diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index e13813c3d..86416c5c5 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -42,19 +42,19 @@ struct gsu_closure { void *g; }; -const struct gsu_scalar scalar_private_gsu = +static const struct gsu_scalar scalar_private_gsu = { pps_getfn, pps_setfn, pps_unsetfn }; -const struct gsu_integer integer_private_gsu = +static const struct gsu_integer integer_private_gsu = { ppi_getfn, ppi_setfn, ppi_unsetfn }; -const struct gsu_float float_private_gsu = +static const struct gsu_float float_private_gsu = { ppf_getfn, ppf_setfn, ppf_unsetfn }; -const struct gsu_array array_private_gsu = +static const struct gsu_array array_private_gsu = { ppa_getfn, ppa_setfn, ppa_unsetfn }; -const struct gsu_hash hash_private_gsu = +static const struct gsu_hash hash_private_gsu = { pph_getfn, pph_setfn, pph_unsetfn }; /* @@ -490,7 +490,7 @@ wrap_private(Eprog prog, FuncWrap w, char *name) return 1; } -static HashNode (*getparamnode) _((HashTable, const char *)); +static GetNodeFunc getparamnode; /**/ static HashNode @@ -567,6 +567,8 @@ static struct features module_features = { }; static struct builtin save_local; +static GetNodeFunc save_getnode2; +static ScanFunc save_printnode; static struct reswd reswd_private = {{NULL, "private", 0}, TYPESET}; /**/ @@ -577,6 +579,8 @@ setup_(UNUSED(Module m)) /* Horrible, horrible hack */ getparamnode = realparamtab->getnode; + save_getnode2 = realparamtab->getnode2; + save_printnode = realparamtab->printnode; realparamtab->getnode = getprivatenode; realparamtab->getnode2 = getprivatenode2; realparamtab->printnode = printprivatenode; @@ -624,8 +628,8 @@ cleanup_(Module m) removehashnode(reswdtab, "private"); realparamtab->getnode = getparamnode; - realparamtab->getnode2 = gethashnode2; - realparamtab->printnode = printparamnode; + realparamtab->getnode2 = save_getnode2; + realparamtab->printnode = save_printnode; deletewrapper(m, wrapper); return setfeatureenables(m, &module_features, NULL); diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 04d448529..98bcaba6e 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -487,11 +487,6 @@ scanfunctions(UNUSED(HashTable ht), ScanFunc func, int flags, int dis) strcat(pm.u.str, " \"$@\""); } else pm.u.str = dyncat(start, t); - /* - * TBD: Is this unmetafy correct? Surely as this - * is a parameter value it stays metafied? - */ - unmetafy(pm.u.str, NULL); zsfree(t); if (shf->redir) { @@ -2190,7 +2185,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index aa5c8ed5b..5fd67963d 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -228,7 +228,7 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar, /* Find the start offset */ MB_CHARINIT(); leftlen = ipair[0]; - while (leftlen) { + while (leftlen > 0) { offs++; clen = MB_CHARLEN(ptr, leftlen); ptr += clen; @@ -479,7 +479,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/regex.c b/Src/Modules/regex.c index 16cc77f30..edb7234d4 100644 --- a/Src/Modules/regex.c +++ b/Src/Modules/regex.c @@ -248,7 +248,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/socket.c b/Src/Modules/socket.c index 7c3fb5ebe..c65b7dfce 100644 --- a/Src/Modules/socket.c +++ b/Src/Modules/socket.c @@ -310,7 +310,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index 396177149..66baa1292 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -659,7 +659,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/system.c b/Src/Modules/system.c index 1ab1fb17e..afaec262a 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -439,7 +439,7 @@ bin_sysseek(char *nam, char **args, Options ops, UNUSED(int func)) /**/ static mnumber -math_systell(UNUSED(char *name), int argc, mnumber *argv, UNUSED(int id)) +math_systell(UNUSED(char *name), UNUSED(int argc), mnumber *argv, UNUSED(int id)) { int fd = (argv->type == MN_INTEGER) ? argv->u.l : (int)argv->u.d; mnumber ret; @@ -521,7 +521,7 @@ static int bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { int cloexec = 1, unlock = 0, readlock = 0; - time_t timeout = 0; + zlong timeout = -1; char *fdvar = NULL; #ifdef HAVE_FCNTL_H struct flock lck; @@ -573,7 +573,7 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } else { optarg = *args++; } - timeout = (time_t)mathevali(optarg); + timeout = mathevali(optarg); break; case 'u': @@ -650,7 +650,7 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) sleep(1); } } else { - while (fcntl(flock_fd, F_SETLKW, &lck) < 0) { + while (fcntl(flock_fd, timeout == 0 ? F_SETLK : F_SETLKW, &lck) < 0) { if (errflag) return 1; if (errno == EINTR) @@ -834,7 +834,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/system.mdd b/Src/Modules/system.mdd index eed0c1b9d..00a3e7896 100644 --- a/Src/Modules/system.mdd +++ b/Src/Modules/system.mdd @@ -15,7 +15,12 @@ errnames.c: errnames1.awk errnames2.awk $(dir_top)/config.h @ERRNO_H@ touch errtmp.out; \ else \ $(AWK) -f $(sdir)/errnames1.awk @ERRNO_H@ >errtmp.c; \ - $(CPP) errtmp.c >errtmp.out; \ + case "`$(CPP) --version </dev/null 2>&1`" in \ + *"Free Software Foundation"*) \ + $(CPP) -P errtmp.c >errtmp.out;; \ + *) \ + $(CPP) errtmp.c >errtmp.out;; \ + esac; \ fi $(AWK) -f $(sdir)/errnames2.awk errtmp.out > $@ rm -f errtmp.c errtmp.out diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c index 9fc1b29a2..dec12142b 100644 --- a/Src/Modules/tcp.c +++ b/Src/Modules/tcp.c @@ -208,7 +208,7 @@ freehostent(UNUSED(struct hostent *ptr)) /**/ #endif /* !HAVE_GETIPNODEBYNAME */ -LinkList ztcp_sessions; +static LinkList ztcp_sessions; /* "allocate" a tcp_session */ static Tcp_session @@ -732,7 +732,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { ztcp_sessions = znewlinklist(); return 0; diff --git a/Src/Modules/termcap.c b/Src/Modules/termcap.c index cd0e85885..60a6e138a 100644 --- a/Src/Modules/termcap.c +++ b/Src/Modules/termcap.c @@ -295,7 +295,7 @@ scantermcap(UNUSED(HashTable ht), ScanFunc func, int flags) } } -struct paramdef partab[] = { +static struct paramdef partab[] = { SPECIALPMDEF("termcap", PM_READONLY, NULL, gettermcap, scantermcap) }; @@ -342,7 +342,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { #ifdef HAVE_TGETENT # ifdef HAVE_SETUPTERM diff --git a/Src/Modules/terminfo.c b/Src/Modules/terminfo.c index 88e326f63..bbd325899 100644 --- a/Src/Modules/terminfo.c +++ b/Src/Modules/terminfo.c @@ -99,7 +99,7 @@ bin_echoti(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) return 1; } /* check that the number of arguments provided is not too high */ - if (arrlen(argv) > 9) { + if (arrlen_gt(argv, 9)) { zwarnnam(name, "too many arguments"); return 1; } @@ -335,7 +335,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { #ifdef USE_TERMINFO_MODULE # ifdef HAVE_SETUPTERM diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index b4081df5f..deed35e2f 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -344,10 +344,10 @@ static int zfsesscnt; */ /* flags for alarm set, alarm gone off */ -int zfalarmed, zfdrrrring; +static int zfalarmed, zfdrrrring; /* remember old alarm status */ -time_t oaltime; -unsigned int oalremain; +static time_t oaltime; +static unsigned int oalremain; /* * Where to jump to when the alarm goes off. This is much @@ -357,7 +357,7 @@ unsigned int oalremain; * * gcc -O gives apparently spurious `may be clobbered by longjmp' warnings. */ -jmp_buf zfalrmbuf; +static jmp_buf zfalrmbuf; /* The signal handler itself */ diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 63ff7578c..0ef753915 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -891,7 +891,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { ptycmds = NULL; diff --git a/Src/Modules/zselect.c b/Src/Modules/zselect.c index 30a3f51a5..8c1267240 100644 --- a/Src/Modules/zselect.c +++ b/Src/Modules/zselect.c @@ -307,7 +307,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 376cd8402..d95c0c254 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -472,7 +472,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) Patprog prog; char *pat; - if (arrlen(args) < 2) { + if (arrlen_lt(args, 2)) { zwarnnam(nam, "not enough arguments"); return 1; } @@ -491,7 +491,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) Style s; char *context, *stylename; - switch (arrlen(args)) { + switch (arrlen_ge(args, 3) ? 3 : arrlen(args)) { case 2: context = args[0]; stylename = args[1]; @@ -1745,13 +1745,15 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) for (p = o; *p; p++) { if (*p == '\\' && p[1]) p++; - else if (*p == '+') { - f |= ZOF_MULT; - *p = '\0'; - p++; - break; - } else if (*p == ':' || *p == '=') - break; + else if (p > o) { /* At least one character of option name */ + if (*p == '+') { + f |= ZOF_MULT; + *p = '\0'; + p++; + break; + } else if (*p == ':' || *p == '=') + break; + } } if (*p == ':') { f |= ZOF_ARG; @@ -1789,6 +1791,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) p++; *n++ = *p; } + *n = '\0'; if (get_opt_desc(o)) { zwarnnam(nam, "option defined more than once: %s", o); return 1; @@ -1833,7 +1836,8 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (o[1]) { add_opt_val(d, o + 1); break; - } else if (!(d->flags & ZOF_OPT)) { + } else if (!(d->flags & ZOF_OPT) || + (pp[1] && pp[1][0] != '-')) { if (!pp[1]) { zwarnnam(nam, "missing argument for option: %s", d->name); @@ -1859,7 +1863,8 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (*e) add_opt_val(d, e); - else if (!(d->flags & ZOF_OPT)) { + else if (!(d->flags & ZOF_OPT) || + (pp[1] && pp[1][0] != '-')) { if (!pp[1]) { zwarnnam(nam, "missing argument for option: %s", d->name); @@ -1990,7 +1995,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 023c41814..3711fde29 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -125,7 +125,7 @@ struct cmatch { #define CMF_REMOVE (1<< 1) /* remove the suffix */ #define CMF_ISPAR (1<< 2) /* is paramter expansion */ #define CMF_PARBR (1<< 3) /* paramter expansion with a brace */ -#define CMF_PARNEST (1<< 4) /* nested paramter expansion */ +#define CMF_PARNEST (1<< 4) /* nested parameter expansion */ #define CMF_NOLIST (1<< 5) /* should not be listed */ #define CMF_DISPLINE (1<< 6) /* display strings one per line */ #define CMF_HIDE (1<< 7) /* temporarily hide this one */ @@ -160,9 +160,15 @@ struct cmatcher { int ralen; /* length of right anchor */ }; +/* Flags for cmatcher::flags */ +/* Upon match, insert the string from the line rather than the string + * from the trial completion ("word"). */ #define CMF_LINE 1 +/* Match with an anchor on the left. */ #define CMF_LEFT 2 +/* Match with an anchor on the right. */ #define CMF_RIGHT 4 +/* ... */ #define CMF_INTER 8 /* @@ -229,7 +235,6 @@ struct cpattern { * the anchor. */ typedef struct cline *Cline; -typedef struct clsub Clsub; struct cline { Cline next; @@ -285,14 +290,14 @@ struct menuinfo { /* Flags for compadd and addmatches(). */ -#define CAF_QUOTE 1 -#define CAF_NOSORT 2 -#define CAF_MATCH 4 -#define CAF_UNIQCON 8 -#define CAF_UNIQALL 16 -#define CAF_ARRAYS 32 -#define CAF_KEYS 64 -#define CAF_ALL 128 +#define CAF_QUOTE 1 /* compadd -Q: positional arguments already quoted */ +#define CAF_NOSORT 2 /* compadd -V: don't sort */ +#define CAF_MATCH 4 /* compadd without -U: do matching */ +#define CAF_UNIQCON 8 /* compadd -2: don't deduplicate */ +#define CAF_UNIQALL 16 /* compadd -1: deduplicate */ +#define CAF_ARRAYS 32 /* compadd -a or -k: array/assoc parameter names */ +#define CAF_KEYS 64 /* compadd -k: assoc parameter names */ +#define CAF_ALL 128 /* compadd -C: _all_matches */ /* Data for compadd and addmatches() */ @@ -367,7 +372,7 @@ typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int); #define CP_QISUFFIX (1 << CPN_QISUFFIX) #define CPN_COMPSTATE 9 #define CP_COMPSTATE (1 << CPN_COMPSTATE) - +/* See comprpms */ #define CP_REALPARAMS 10 #define CP_ALLREALS ((unsigned int) 0x3ff) @@ -424,7 +429,7 @@ typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int); #define CP_QUOTES (1 << CPN_QUOTES) #define CPN_IGNORED 25 #define CP_IGNORED (1 << CPN_IGNORED) - +/* See compkpms */ #define CP_KEYPARAMS 26 #define CP_ALLKEYS ((unsigned int) 0x3ffffff) diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index ae3a64074..d1cf7a08a 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -30,10 +30,6 @@ #include "complete.mdh" #include "compcore.pro" -/* The last completion widget called. */ - -static Widget lastcompwidget; - /* Flags saying what we have to do with the result. */ /**/ @@ -429,6 +425,7 @@ do_completion(UNUSED(Hookdef dummy), Compldat dat) } } else { invalidatelist(); + lastambig = isset(BASHAUTOLIST); if (forcelist) clearlist = 1; zlemetacs = 0; @@ -471,8 +468,7 @@ before_complete(UNUSED(Hookdef dummy), int *lst) /* If we are doing a menu-completion... */ - if (minfo.cur && menucmp && *lst != COMP_LIST_EXPAND && - (menucmp != 1 || !compwidget || compwidget == lastcompwidget)) { + if (minfo.cur && menucmp && *lst != COMP_LIST_EXPAND) { do_menucmp(*lst); return 1; } @@ -481,7 +477,6 @@ before_complete(UNUSED(Hookdef dummy), int *lst) onlyexpl = listdat.valid = 0; return 1; } - lastcompwidget = compwidget; /* We may have to reset the cursor to its position after the * * string inserted by the last completion. */ @@ -841,6 +836,7 @@ callcompfunc(char *s, char *fn) endparamscope(); lastcmd = 0; incompfunc = icf; + startauto = 0; if (!complist) uselist = 0; @@ -888,8 +884,13 @@ callcompfunc(char *s, char *fn) useline = 1, usemenu = 1; else if (strpfx("auto", compinsert)) useline = 1, usemenu = 2; - else + else { useline = usemenu = 0; + /* if compstate[insert] was emptied, no unambiguous prefix + * ever gets inserted so allow the next tab to already start + * menu completion */ + startauto = lastambig = isset(AUTOMENU); + } if (useline && (p = strchr(compinsert, ':'))) { insmnum = atoi(++p); @@ -902,7 +903,7 @@ callcompfunc(char *s, char *fn) #endif } } - startauto = ((compinsert && + startauto = startauto || ((compinsert && !strcmp(compinsert, "automenu-unambiguous")) || (bashlistfirst && isset(AUTOMENU) && (!compinsert || !*compinsert))); @@ -1049,6 +1050,13 @@ makecomplist(char *s, int incmd, int lst) } } +/* + * Quote 's' according to compqstack, aka $compstate[all_quotes]. + * + * If 'ign' is 1, skip the innermost quoting level. Otherwise 'ign' + * must be 0. + */ + /**/ mod_export char * multiquote(char *s, int ign) @@ -1056,12 +1064,11 @@ multiquote(char *s, int ign) if (s) { char *os = s, *p = compqstack; - if (p && *p && (ign < 1 || p[ign])) { - if (ign > 0) - p += ign; + if (p && *p && (ign == 0 || p[1])) { + if (ign) + p++; while (*p) { - if (ign >= 0 || p[1]) - s = quotestring(s, NULL, *p); + s = quotestring(s, *p); p++; } } @@ -1071,6 +1078,12 @@ multiquote(char *s, int ign) return NULL; } +/* + * tildequote(s, ign): Equivalent to multiquote(s, ign), except that if + * compqstack[0] == QT_BACKSLASH and s[0] == '~', then that tilde is not + * quoted. + */ + /**/ mod_export char * tildequote(char *s, int ign) @@ -1970,6 +1983,11 @@ get_user_var(char *nam) } } +/* + * If KEYS, then NAME is an associative array; return its keys. + * Else, NAME is a plain array; return its elements. + */ + static char ** get_data_arr(char *name, int keys) { @@ -2031,16 +2049,17 @@ addmatch(char *str, int flags, char ***dispp, int line) int addmatches(Cadata dat, char **argv) { + /* ms: "match string" - string to use as completion. + * Overloaded at one place as a temporary. */ char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL; char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre; char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL; char **arrays = NULL; - int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0; + int lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0; int ppl = 0, psl = 0, ilen = 0; int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern; int isexact, doadd, ois = instring, oib = inbackt; Cline lc = NULL, pline = NULL, sline = NULL; - Cmatch cm; struct cmlist mst; Cmlist oms = mstack; Patprog cp = NULL, *pign = NULL; @@ -2199,9 +2218,14 @@ addmatches(Cadata dat, char **argv) /* Test if there is an existing -P prefix. */ if (dat->pre && *dat->pre) { - pl = pfxlen(dat->pre, lpre); - llpl -= pl; - lpre += pl; + int prefix_length = pfxlen(dat->pre, lpre); + if (dat->pre[prefix_length] == '\0' || + lpre[prefix_length] == '\0') { + /* $compadd_args[-P] is a prefix of ${PREFIX}, or + * vice-versa. */ + llpl -= prefix_length; + lpre += prefix_length; + } } } /* Now duplicate the strings we have from the command line. */ @@ -2413,6 +2437,7 @@ addmatches(Cadata dat, char **argv) if (dat->psuf) psl = strlen(dat->psuf); for (; (s = *argv); argv++) { + int sl; bpl = obpl; bsl = obsl; if (disp) { @@ -2473,6 +2498,7 @@ addmatches(Cadata dat, char **argv) goto next_array; } if (doadd) { + Cmatch cm; Brinfo bp; for (bp = obpl; bp; bp = bp->next) diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 8381867d0..52c6f1233 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1400,7 +1400,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat) untokenize(p); quotedzputs(p, stdout); } else - quotedzputs(quotestring(s, NULL, QT_BACKSLASH), stdout); + quotedzputs(quotestring(s, QT_BACKSLASH), stdout); } /* loop through flags w/o args that are set, printing them if so */ @@ -1536,7 +1536,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat) char *p = dupstring(s); untokenize(p); - quotedzputs(quotestring(p, NULL, QT_BACKSLASH), stdout); + quotedzputs(quotestring(p, QT_BACKSLASH), stdout); } } putchar('\n'); @@ -1740,8 +1740,8 @@ static int addwhat; * This uses the instring variable exported from zle_tricky.c. */ -#define quotename(s, e) \ -quotestring(s, e, instring == QT_NONE ? QT_BACKSLASH : instring) +#define quotename(s) \ +quotestring(s, instring == QT_NONE ? QT_BACKSLASH : instring) /* Hook functions */ @@ -1965,7 +1965,7 @@ addmatch(char *s, char *t) if (!ms) return; - if (addwhat == -7 && !findcmd(s, 0)) + if (addwhat == -7 && !findcmd(s, 0, 0)) return; isfile = CMF_FILE; } else if (addwhat == CC_QUOTEFLAG || addwhat == -2 || @@ -2135,7 +2135,7 @@ gen_matches_files(int dirs, int execs, int all) { DIR *d; struct stat buf; - char *n, p[PATH_MAX], *q = NULL, *e, *pathpref; + char *n, p[PATH_MAX+1], *q = NULL, *e, *pathpref; LinkList l = NULL; int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat, pathpreflen; @@ -2469,7 +2469,7 @@ makecomplistcmd(char *os, int incmd, int flags) /* If the command string starts with `=', try the path name of the * * command. */ if (cmdstr && cmdstr[0] == Equals) { - char *c = findcmd(cmdstr + 1, 1); + char *c = findcmd(cmdstr + 1, 1, 0); if (c) { zsfree(cmdstr); @@ -2509,7 +2509,8 @@ makecomplistpc(char *os, int incmd) int ret = 0; s = ((shfunctab->getnode(shfunctab, cmdstr) || - builtintab->getnode(builtintab, cmdstr)) ? NULL : findcmd(cmdstr, 1)); + builtintab->getnode(builtintab, cmdstr)) ? NULL : + findcmd(cmdstr, 1, 0)); for (pc = patcomps; pc; pc = pc->next) { if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) && @@ -3153,10 +3154,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) lpre = zhalloc(lpl + 1); memcpy(lpre, s, lpl); lpre[lpl] = '\0'; - qlpre = quotename(lpre, NULL); + qlpre = quotename(lpre); lsuf = dupstring(s + offs); lsl = strlen(lsuf); - qlsuf = quotename(lsuf, NULL); + qlsuf = quotename(lsuf); /* First check for ~.../... */ if (ic == Tilde) { @@ -3175,11 +3176,11 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) rpre = (*p || *lpre == Tilde || *lpre == Equals) ? (noreal = 0, getreal(tt)) : dupstring(tt); - qrpre = quotename(rpre, NULL); + qrpre = quotename(rpre); for (p = lsuf; *p && *p != String && *p != Tick; p++); rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf); - qrsuf = quotename(rsuf, NULL); + qrsuf = quotename(rsuf); /* Check if word is a pattern. */ @@ -3315,10 +3316,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) /* And get the file prefix. */ fpre = dupstring(((s1 == s || s1 == rpre || ic) && (*s != '/' || zlemetacs == wb)) ? s1 : s1 + 1); - qfpre = quotename(fpre, NULL); + qfpre = quotename(fpre); /* And the suffix. */ fsuf = dupstrpfx(rsuf, s2 - rsuf); - qfsuf = quotename(fsuf, NULL); + qfsuf = quotename(fsuf); if (comppatmatch && *comppatmatch && (ispattern & 2)) { int t2; @@ -3992,7 +3993,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { addhookfunc("compctl_make", (Hookfn) ccmakehookfn); addhookfunc("compctl_cleanup", (Hookfn) cccleanuphookfn); diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index ee4e5b0a5..7980518b7 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -52,7 +52,7 @@ char **compwords, *compqiprefix, *compqisuffix, *compquote, - *compqstack, + *compqstack, /* compstate[all_quotes] */ *comppatmatch, *complastprompt; /**/ @@ -72,8 +72,26 @@ char *compiprefix, *compoldins, *compvared; +/* + * An array of Param structures for compsys special parameters; + * see 'comprparams' below. An entry for $compstate is added + * by makecompparams(). + * + * See CP_REALPARAMS. + */ + /**/ -Param *comprpms, *compkpms; +Param *comprpms; + +/* + * An array of Param structures for elemens of $compstate; see + * 'compkparams' below. + * + * See CP_KEYPARAMS. + */ + +/**/ +Param *compkpms; /**/ mod_export void @@ -231,16 +249,17 @@ parse_cmatcher(char *name, char *s) if (!*s) break; switch (*s) { - case 'b': fl2 = CMF_INTER; + case 'b': fl2 = CMF_INTER; /* FALLTHROUGH */ case 'l': fl = CMF_LEFT; break; - case 'e': fl2 = CMF_INTER; + case 'e': fl2 = CMF_INTER; /* FALLTHROUGH */ case 'r': fl = CMF_RIGHT; break; case 'm': fl = 0; break; - case 'B': fl2 = CMF_INTER; + case 'B': fl2 = CMF_INTER; /* FALLTHROUGH */ case 'L': fl = CMF_LEFT | CMF_LINE; break; - case 'E': fl2 = CMF_INTER; + case 'E': fl2 = CMF_INTER; /* FALLTHROUGH */ case 'R': fl = CMF_RIGHT | CMF_LINE; break; case 'M': fl = CMF_LINE; break; + case 'x': break; default: if (name) zwarnnam(name, "unknown match specification character `%c'", @@ -252,6 +271,15 @@ parse_cmatcher(char *name, char *s) zwarnnam(name, "missing `:'"); return pcm_err; } + if (*s == 'x') { + if (s[2] && !inblank(s[2])) { + if (name) + zwarnnam(name, + "unexpected pattern following x: specification"); + return pcm_err; + } + return ret; + } s += 2; if (!*s) { if (name) @@ -527,8 +555,8 @@ static int bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) { struct cadata dat; - char *p, **sp, *e, *m = NULL, *mstr = NULL; - int dm; + char *mstr = NULL; /* argument of -M options, accumulated */ + int added; /* return value */ Cmatcher match = NULL; if (incompfunc != 1) { @@ -544,14 +572,16 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) dat.dummies = -1; for (; *argv && **argv == '-'; argv++) { + char *p; /* loop variable, points into argv */ if (!(*argv)[1]) { argv++; break; } for (p = *argv + 1; *p; p++) { - sp = NULL; - e = NULL; - dm = 0; + char *m = NULL; /* argument of -M option (this one only) */ + char **sp = NULL; /* the argument to an option should be copied + to *sp. */ + const char *e; /* error message */ switch (*p) { case 'q': dat.flags |= CMF_REMOVE; @@ -633,7 +663,6 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) case 'M': sp = &m; e = "matching specification expected after -%c"; - dm = 1; break; case 'X': sp = &(dat.exp); @@ -704,27 +733,29 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) } if (sp) { if (p[1]) { + /* Pasted argument: -Xfoo. */ if (!*sp) *sp = p + 1; p = "" - 1; } else if (argv[1]) { + /* Argument in a separate word: -X foo. */ argv++; if (!*sp) *sp = *argv; p = "" - 1; } else { + /* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */ zwarnnam(name, e, *p); zsfree(mstr); return 1; } - if (dm) { + if (m) { if (mstr) { char *tmp = tricat(mstr, " ", m); zsfree(mstr); mstr = tmp; } else mstr = ztrdup(m); - m = NULL; } } } @@ -743,10 +774,10 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) return 1; dat.match = match = cpcmatcher(match); - dm = addmatches(&dat, argv); + added = addmatches(&dat, argv); freecmatcher(match); - return dm; + return added; } #define CVT_RANGENUM 0 @@ -1207,8 +1238,9 @@ makecompparams(void) addcompparams(comprparams, comprpms); - if (!(cpm = createparam(COMPSTATENAME, - PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED))) + if (!(cpm = createparam( + COMPSTATENAME, + PM_SPECIAL|PM_REMOVABLE|PM_SINGLE|PM_LOCAL|PM_HASHED))) cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME); DPUTS(!cpm, "param not set in makecompparams"); diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 0ccb88505..2edaf6eca 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -34,8 +34,9 @@ /* Information about the list shown. */ /* - * noselect: 1 if complistmatches indicated we shouldn't do selection. - * Tested in domenuselect. + * noselect: 1 if complistmatches indicated we shouldn't do selection; + * -1 if interactive mode needs to reset the selection list. + * Tested in domenuselect, and in complistmatches to skip redraw. * mselect: Local copy of the index of the currently selected match. * Initialised to the gnum entry of the current match for * each completion. @@ -113,7 +114,7 @@ static Cmgroup *mgtab, *mgtabp; * Allow us to keep track of pointer arithmetic for mgtab; could * just as well have been for mtab but wasn't. */ -int mgtabsize; +static int mgtabsize; #endif /* @@ -346,9 +347,10 @@ getcoldef(char *s) char sav = p[1]; p[1] = '\0'; + s = metafy(s, -1, META_USEHEAP); tokenize(s); gprog = patcompile(s, 0, NULL); - p[1] =sav; + p[1] = sav; s = p + 1; } @@ -414,6 +416,7 @@ getcoldef(char *s) break; *s++ = '\0'; } + p = metafy(p, -1, META_USEHEAP); tokenize(p); if ((prog = patcompile(p, 0, NULL))) { Patcol pc, po; @@ -661,7 +664,9 @@ clprintfmt(char *p, int ml) initiscol(); - for (; *p; p++) { + while (*p) { + convchar_t chr; + int chrlen = MB_METACHARLENCONV(p, &chr); doiscol(i++); cc++; if (*p == '\n') { @@ -672,11 +677,16 @@ clprintfmt(char *p, int ml) if (ml == mlend - 1 && (cc % zterm_columns) == zterm_columns - 1) return 0; - if (*p == Meta) { + while (chrlen) { + if (*p == Meta) { + p++; + chrlen--; + putc(*p ^ 32, shout); + } else + putc(*p, shout); + chrlen--; p++; - putc(*p ^ 32, shout); - } else - putc(*p, shout); + } if ((beg = !(cc % zterm_columns))) ml++; if (mscroll && !(cc % zterm_columns) && @@ -989,6 +999,7 @@ asklistscroll(int ml) fflush(shout); zsetterm(); + menuselect_bindings(); /* sanity in case deleted by user */ selectlocalmap(lskeymap); if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) ret = 1; @@ -1979,7 +1990,8 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat) } #endif - noselect = 0; + if (noselect > 0) + noselect = 0; if ((minfo.asked == 2 && mselect < 0) || nlnct >= zterm_lines) { showinglist = 0; @@ -2077,9 +2089,11 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat) last_cap = (char *) zhalloc(max_caplen + 1); *last_cap = '\0'; - if (!mnew && inselect && onlnct == nlnct && mlbeg >= 0 && mlbeg == molbeg) - singledraw(); - else if (!compprintlist(mselect >= 0) || !clearflag) + if (!mnew && inselect && + onlnct == nlnct && mlbeg >= 0 && mlbeg == molbeg) { + if (!noselect) + singledraw(); + } else if (!compprintlist(mselect >= 0) || !clearflag) noselect = 1; onlnct = nlnct; @@ -2092,7 +2106,7 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat) popheap(); opts[EXTENDEDGLOB] = extendedglob; - return noselect; + return (noselect < 0 ? 0 : noselect); } static int @@ -2433,6 +2447,7 @@ domenuselect(Hookdef dummy, Chdata dat) unqueue_signals(); mhasstat = (mstatus && *mstatus); fdat = dat; + menuselect_bindings(); /* sanity in case deleted by user */ selectlocalmap(mskeymap); noselect = 1; while ((menuacc && @@ -2545,14 +2560,23 @@ domenuselect(Hookdef dummy, Chdata dat) } else { statusline = NULL; } + if (noselect < 0) { + showinglist = clearlist = 0; + clearflag = 1; + } zrefresh(); statusline = NULL; inselect = 1; + selected = 1; if (noselect) { + if (noselect < 0) { + /* no selection until after processing keystroke */ + noselect = 0; + goto getk; + } broken = 1; break; } - selected = 1; if (!i) { i = mcols * mlines; while (i--) @@ -2750,6 +2774,7 @@ domenuselect(Hookdef dummy, Chdata dat) if (nmessages) { showinglist = -2; zrefresh(); + noselect = -1; } else { trashzle(); zsetterm(); @@ -3383,7 +3408,7 @@ domenuselect(Hookdef dummy, Chdata dat) do_single(*(minfo.cur)); } if (wasnext || broken) { - menucmp = 2; + menucmp = 1; showinglist = ((validlist && !nolist) ? -2 : 0); minfo.asked = 0; if (!noselect) { @@ -3486,6 +3511,37 @@ enables_(Module m, int **enables) } /**/ +static void +menuselect_bindings(void) +{ + if (!(mskeymap = openkeymap("menuselect"))) { + mskeymap = newkeymap(NULL, "menuselect"); + linkkeymap(mskeymap, "menuselect", 1); + bindkey(mskeymap, "\t", refthingy(t_completeword), NULL); + bindkey(mskeymap, "\n", refthingy(t_acceptline), NULL); + bindkey(mskeymap, "\r", refthingy(t_acceptline), NULL); + bindkey(mskeymap, "\33[A", refthingy(t_uplineorhistory), NULL); + bindkey(mskeymap, "\33[B", refthingy(t_downlineorhistory), NULL); + bindkey(mskeymap, "\33[C", refthingy(t_forwardchar), NULL); + bindkey(mskeymap, "\33[D", refthingy(t_backwardchar), NULL); + bindkey(mskeymap, "\33OA", refthingy(t_uplineorhistory), NULL); + bindkey(mskeymap, "\33OB", refthingy(t_downlineorhistory), NULL); + bindkey(mskeymap, "\33OC", refthingy(t_forwardchar), NULL); + bindkey(mskeymap, "\33OD", refthingy(t_backwardchar), NULL); + } + if (!(lskeymap = openkeymap("listscroll"))) { + lskeymap = newkeymap(NULL, "listscroll"); + linkkeymap(lskeymap, "listscroll", 1); + bindkey(lskeymap, "\t", refthingy(t_completeword), NULL); + bindkey(lskeymap, " ", refthingy(t_completeword), NULL); + bindkey(lskeymap, "\n", refthingy(t_acceptline), NULL); + bindkey(lskeymap, "\r", refthingy(t_acceptline), NULL); + bindkey(lskeymap, "\33[B", refthingy(t_downlineorhistory), NULL); + bindkey(lskeymap, "\33OB", refthingy(t_downlineorhistory), NULL); + } +} + +/**/ int boot_(Module m) { @@ -3503,27 +3559,7 @@ boot_(Module m) } addhookfunc("comp_list_matches", (Hookfn) complistmatches); addhookfunc("menu_start", (Hookfn) domenuselect); - mskeymap = newkeymap(NULL, "menuselect"); - linkkeymap(mskeymap, "menuselect", 1); - bindkey(mskeymap, "\t", refthingy(t_completeword), NULL); - bindkey(mskeymap, "\n", refthingy(t_acceptline), NULL); - bindkey(mskeymap, "\r", refthingy(t_acceptline), NULL); - bindkey(mskeymap, "\33[A", refthingy(t_uplineorhistory), NULL); - bindkey(mskeymap, "\33[B", refthingy(t_downlineorhistory), NULL); - bindkey(mskeymap, "\33[C", refthingy(t_forwardchar), NULL); - bindkey(mskeymap, "\33[D", refthingy(t_backwardchar), NULL); - bindkey(mskeymap, "\33OA", refthingy(t_uplineorhistory), NULL); - bindkey(mskeymap, "\33OB", refthingy(t_downlineorhistory), NULL); - bindkey(mskeymap, "\33OC", refthingy(t_forwardchar), NULL); - bindkey(mskeymap, "\33OD", refthingy(t_backwardchar), NULL); - lskeymap = newkeymap(NULL, "listscroll"); - linkkeymap(lskeymap, "listscroll", 1); - bindkey(lskeymap, "\t", refthingy(t_completeword), NULL); - bindkey(lskeymap, " ", refthingy(t_completeword), NULL); - bindkey(lskeymap, "\n", refthingy(t_acceptline), NULL); - bindkey(lskeymap, "\r", refthingy(t_acceptline), NULL); - bindkey(lskeymap, "\33[B", refthingy(t_downlineorhistory), NULL); - bindkey(lskeymap, "\33OB", refthingy(t_downlineorhistory), NULL); + menuselect_bindings(); return 0; } diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index 0e41ac3a5..f82f00e1d 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -498,14 +498,27 @@ add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl) /**/ int match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, - int sfx, int test, int part) + const int sfx, int test, int part) { - int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw, exact = 0, wexact = 0; - int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc, bslash; + /* How many characters from the line string and from the word string are + * yet to be matched. */ + int ll = strlen(l), lw = strlen(w); + /* Number of characters from the line string and word string matched. */ + int il = 0, iw = 0; + /* How many characters were matched exactly in the line and in the word. */ + int exact = 0, wexact = 0; + int he = 0; + int bslash; char *ow; - Cmlist ms; + Cmlist ms; /* loop variable */ Cmatcher mp, lm = NULL; Brinfo bp = NULL; + const int obc = bc; + const int ind = (sfx ? -1 : 0); + const int add = (sfx ? -1 : 1); + const int original_ll = ll, original_lw = lw; + + /* INVARIANT: il+ll == original_ll; iw+lw == original_lw */ if (!test) { start_match(); @@ -516,9 +529,6 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, if (sfx) { l += ll; w += lw; - ind = -1; add = -1; - } else { - ind = 0; add = 1; } /* ow will always point to the beginning (or end) of that sub-string * in w that wasn't put in the match-variables yet. */ @@ -559,8 +569,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, bslash = 0; if (!sfx && lw && (!part || test) && (l[ind] == w[ind] || - (bslash = (lw > 1 && w[ind] == '\\' && - (ind ? (w[0] == l[0]) : (w[1] == l[0])))))) { + (bslash = (lw > 1 && w[ind] == '\\' && w[ind+1] == l[0])))) { /* No matcher could be used, but the strings have the same * character here, skip over it. */ l += add; w += (bslash ? (add + add) : add); @@ -583,9 +592,8 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, /* First try the matchers. Err... see above. */ for (mp = NULL, ms = mstack; !mp && ms; ms = ms->next) { for (mp = ms->matcher; mp; mp = mp->next) { - t = 1; if ((lm && lm == mp) || - ((oll == ll || olw == lw) && + ((original_ll == ll || original_lw == lw) && (test == 1 || (test && !mp->left && !mp->right)) && mp->wlen < 0)) /* If we were called recursively, don't use `*' patterns @@ -593,9 +601,88 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, continue; if (mp->wlen < 0) { - int both, loff, aoff, llen, alen, zoff, moff, ct, ict, aol; - char *tp, savl = '\0', savw; - Cpattern ap, aop; + /* `*'-pattern. */ + /* + * Similar to the identically-named variable in the 'else' + * block. + */ + int t; + /* + * 1 iff the anchor and the word are on the same side of + * the line pattern; that is: if either + * - the anchor is on the left and we are matching + * a prefix; or + * - the anchor is on the right and we are matching + * a suffix. + */ + int both; + /* + * Offset from the line pattern pointer ('l') to the start + * of the line pattern. + */ + int loff; + /* + * Offset from the line pattern pointer ('l') to the start + * of the anchor. + */ + int aoff; + /* + * The length of the line pattern. + */ + int llen; + /* + * The length of the anchor. + * + * SEE: ap; aol, aop + */ + int alen; + /* + * ### Related to 'zoff', which was removed in 2016. + */ + int moff; + /* + * ### These two are related. + * + * ### They may have a relation similar to that of lw/iw + * ### (q.v.), at least during the 'for' loop. They may be + * ### overloaded/repurposed after it. + */ + int ct, ict; + /* + * The length of the OTHER anchor: the left anchor when + * we're anchored on the right, and of the right anchor + * when we're anchored on the left. + */ + int aol; + /* + * LOST: Documentation comment. Last seen 10 years ago in + * the temporal lobe. Reward promised for its safe return. + * Contact zsh-workers@zsh.org. + */ + char *tp; + /* + * Temporary variable. Used as temporary storage for a + * + * { + * () { + * local foo="$foo" + * foo[1]=bar + * ... + * } + * (use original $foo here) + * } + * + * operation. Similar to savw. + */ + char savl; + /* + * The anchor on this end. + */ + Cpattern ap; + /* + * The anchor on the other end. + */ + Cpattern aop; /* This is for `*' patterns, first initialise some * local variables. */ @@ -611,14 +698,14 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, continue; if (mp->flags & CMF_LEFT) { - ap = mp->left; zoff = 0; moff = alen; aop = mp->right; + ap = mp->left; moff = alen; aop = mp->right; if (sfx) { both = 0; loff = -llen; aoff = -(llen + alen); } else { both = 1; loff = alen; aoff = 0; } } else { - ap = mp->right; zoff = alen; moff = 0; aop = mp->left; + ap = mp->right; moff = 0; aop = mp->left; if (sfx) { both = 1; loff = -(llen + alen); aoff = -alen; } else { @@ -644,8 +731,8 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, /* Fine, now we call ourselves recursively to find the * string matched by the `*'. */ - if (sfx && (savl = l[-(llen + zoff)])) - l[-(llen + zoff)] = '\0'; + if (sfx && (savl = l[-(llen + alen)])) + l[-(llen + alen)] = '\0'; for (t = 0, tp = w, ct = 0, ict = lw - alen + 1; ict; tp += add, ct++, ict--) { @@ -667,22 +754,25 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, !match_parts(l + aoff , tp - moff, alen, part)) break; if (sfx) { - if ((savw = tp[-zoff])) - tp[-zoff] = '\0'; + /* Call ourselves recursively with the + * anchor removed. */ + char savw; + if ((savw = tp[-alen])) + tp[-alen] = '\0'; t = match_str(l - ll, w - lw, - NULL, 0, NULL, 1, 2, part); + NULL, 0, NULL, sfx, 2, part); if (savw) - tp[-zoff] = savw; + tp[-alen] = savw; } else t = match_str(l + llen + moff, tp + moff, - NULL, 0, NULL, 0, 1, part); + NULL, 0, NULL, sfx, 1, part); if (t || (mp->wlen == -1 && !both)) break; } } ict = ct; if (sfx && savl) - l[-(llen + zoff)] = savl; + l[-(llen + alen)] = savl; /* Have we found a position in w where the rest of l * matches? */ @@ -752,18 +842,22 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, bc += llen; exact = 0; - if (!test) + if (!test) { + int bpc; while (bp && bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) { bp->curpos = matchbufadded + bpc - bc + obc; bp = bp->next; } + } ow = w; if (!llen && !alen) { lm = mp; - if (he) + if (he) { + /* Signal the outer for loop to continue. */ mp = NULL; + } else he = 1; } else { @@ -772,6 +866,11 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, break; } else if (ll >= mp->llen && lw >= mp->wlen) { /* Non-`*'-pattern. */ + /* + * Similar to the identically-named variable in the 'if' + * block. + */ + int t = 1; char *tl, *tw; int tll, tlw, til, tiw; @@ -875,12 +974,14 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, bc += mp->llen; exact = 0; - if (!test) + if (!test) { + int bpc; while (bp && bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) { bp->curpos = matchbufadded + bpc - bc + obc; bp = bp->next; } + } ow = w; lm = NULL; he = 0; @@ -896,8 +997,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, bslash = 0; if ((!test || sfx) && lw && (l[ind] == w[ind] || - (bslash = (lw > 1 && w[ind] == '\\' && - (ind ? (w[0] == l[0]) : (w[1] == l[0])))))) { + (bslash = (lw > 1 && w[ind] == '\\' && w[ind+1] == l[0])))) { /* No matcher could be used, but the strings have the same * character here, skip over it. */ l += add; w += (bslash ? (add + add ) : add); diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 7fec7c804..05799399d 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -1174,6 +1174,10 @@ do_single(Cmatch m) zlemetacs = minfo.end; if (zlemetacs + m->qisl == lastend) zlemetacs += minfo.insc; + + /* Advance CURSOR past compadd -s/-S suffixes. */ + zlemetacs += strlen(psuf); + zlemetacs += m->suf ? strlen(m->suf) : 0; } { Cmatch *om = minfo.cur; @@ -1191,6 +1195,7 @@ do_single(Cmatch m) if (menucmp) minfo.cur = &m; runhookdef(INSERTMATCHHOOK, (void *) &dat); + redrawhook(); minfo.cur = om; } } diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 60fb096be..c78167329 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -199,11 +199,11 @@ cd_calc(void) set->count++; if ((l = strlen(str->str)) > cd_state.pre) cd_state.pre = l; - if ((l = MB_METASTRWIDTH(str->str)) > cd_state.premaxw) + if ((l = ZMB_nicewidth(str->str)) > cd_state.premaxw) cd_state.premaxw = l; if (str->desc) { set->desc++; - if ((l = strlen(str->desc)) > cd_state.suf) + if ((l = strlen(str->desc)) > cd_state.suf) /* ### strlen() assumes no \n */ cd_state.suf = l; } } @@ -490,7 +490,7 @@ cd_init(char *nam, char *hide, char *mlen, char *sep, setp = &(cd_state.sets); cd_state.sep = ztrdup(sep); cd_state.slen = strlen(sep); - cd_state.swidth = MB_METASTRWIDTH(sep); + cd_state.swidth = ZMB_nicewidth(sep); cd_state.sets = NULL; cd_state.showd = disp; cd_state.maxg = cd_state.groups = cd_state.descs = 0; @@ -526,7 +526,8 @@ cd_init(char *nam, char *hide, char *mlen, char *sep, str->other = NULL; str->set = set; - for (tmp = *ap; *tmp && *tmp != ':'; tmp++) + /* Advance tmp to the first unescaped colon. */ + for (tmp = *ap; *tmp && *tmp != ':'; tmp++) if (*tmp == '\\' && tmp[1]) tmp++; @@ -537,7 +538,7 @@ cd_init(char *nam, char *hide, char *mlen, char *sep, *tmp = '\0'; str->str = str->match = ztrdup(rembslash(*ap)); str->len = strlen(str->str); - str->width = MB_METASTRWIDTH(str->str); + str->width = ZMB_nicewidth(str->str); str->sortstr = NULL; } if (str) @@ -692,7 +693,7 @@ cd_get(char **params) * end of screen as safety margin */ d = str->desc; - w = MB_METASTRWIDTH(d); + w = ZMB_nicewidth(d); if (w <= remw) strcpy(p, d); else { @@ -701,7 +702,7 @@ cd_get(char **params) l = MB_METACHARLEN(d); memcpy(pp, d, l); pp[l] = '\0'; - w = MB_METASTRWIDTH(pp); + w = ZMB_nicewidth(pp); if (w > remw) { *pp = '\0'; break; @@ -792,7 +793,7 @@ cd_get(char **params) cd_state.swidth - CM_SPACE; p = pp = dbuf + cd_state.slen; d = str->desc; - w = MB_METASTRWIDTH(d); + w = ZMB_nicewidth(d); if (w <= remw) { strcpy(p, d); remw -= w; @@ -802,7 +803,7 @@ cd_get(char **params) l = MB_METACHARLEN(d); memcpy(pp, d, l); pp[l] = '\0'; - w = MB_METASTRWIDTH(pp); + w = ZMB_nicewidth(pp); if (w > remw) { *pp = '\0'; break; @@ -1693,10 +1694,10 @@ ca_get_opt(Cadef d, char *line, int full, char **end) for (p = d->opts; p; p = p->next) if (p->active && ((!p->args || p->type == CAO_NEXT) ? !strcmp(p->name, line) : strpfx(p->name, line))) { - int l = strlen(p->name); - if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) && - line[l] && line[l] != '=') - continue; + int l = strlen(p->name); + if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) && + line[l] && line[l] != '=') + continue; if (end) { /* Return a pointer to the end of the option. */ @@ -2158,7 +2159,8 @@ ca_parse_line(Cadef d, int multi, int first) state.opt = 0; else state.curopt = NULL; - } else if (multi && (*line == '-' || *line == '+') && cur != compcurrent + } else if (multi && (*line == '-' || *line == '+') && cur != compcurrent && + ca_get_opt(d, line, 0, NULL) #if 0 /**** Ouch. Using this will disable the mutual exclusion of different sets. Not using it will make the -A @@ -2167,9 +2169,11 @@ ca_parse_line(Cadef d, int multi, int first) #endif ) return 1; - else if (state.arg && (!napat || !pattry(napat, line))) { + else if (state.arg && + (!napat || cur <= compcurrent || !pattry(napat, line))) { /* Otherwise it's a normal argument. */ - if (napat && ca_inactive(d, NULL, cur + 1, 1, NULL)) + if (napat && cur <= compcurrent && + ca_inactive(d, NULL, cur + 1, 1, NULL)) return 1; arglast = 1; @@ -2308,7 +2312,10 @@ ca_parse_line(Cadef d, int multi, int first) return 0; } -/* Build a colon-list from a list. */ +/* Build a colon-list from a list. + * + * This is only used to populate values of $opt_args. + */ static char * ca_colonlist(LinkList l) @@ -2318,16 +2325,19 @@ ca_colonlist(LinkList l) int len = 0; char *p, *ret, *q; + /* Compute the length to be allocated. */ for (n = firstnode(l); n; incnode(n)) { len++; for (p = (char *) getdata(n); *p; p++) - len += (*p == ':' ? 2 : 1); + len += (*p == ':' || *p == '\\') ? 2 : 1; } ret = q = (char *) zalloc(len); + /* Join L into RET, joining with colons and escaping colons and + * backslashes. */ for (n = firstnode(l); n;) { for (p = (char *) getdata(n); *p; p++) { - if (*p == ':') + if (*p == ':' || *p == '\\') *q++ = '\\'; *q++ = *p; } @@ -3546,7 +3556,7 @@ comp_quote(char *str, int prefix) if ((x = (prefix && *str == '='))) *str = 'x'; - ret = quotestring(str, NULL, *compqstack); + ret = quotestring(str, *compqstack); if (x) *str = *ret = '='; @@ -4480,6 +4490,10 @@ cfp_matcher_pats(char *matcher, char *add) return add; } +/* + * ### This function call is skipped by _approximate, so "opt" probably means "optimize". + */ + static void cfp_opt_pats(char **pats, char *matcher) { @@ -4732,7 +4746,7 @@ cf_ignore(char **names, LinkList ign, char *style, char *path) for (; (n = *names); names++) { if (!ztat(n, &nst, 1) && S_ISDIR(nst.st_mode)) { if (tpwd && nst.st_dev == est.st_dev && nst.st_ino == est.st_ino) { - addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH)); + addlinknode(ign, quotestring(n, QT_BACKSLASH)); continue; } if (tpar && !strncmp((c = dupstring(n)), path, pl)) { @@ -4748,7 +4762,7 @@ cf_ignore(char **names, LinkList ign, char *style, char *path) if (found || ((e = strrchr(c, '/')) && e > c + pl && !ztat(c, &st, 1) && st.st_dev == nst.st_dev && st.st_ino == nst.st_ino)) - addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH)); + addlinknode(ign, quotestring(n, QT_BACKSLASH)); } } } @@ -4811,6 +4825,20 @@ cf_remove_other(char **names, char *pre, int *amb) return NULL; } +/* + * SYNOPSIS: + * 1. compfiles -p parnam1 parnam2 skipped matcher sdirs parnam3 varargs [..varargs] + * 2. compfiles -p- parnam1 parnam2 skipped matcher sdirs parnam3 varargs [..varargs] + * 3. compfiles -P parnam1 parnam2 skipped matcher sdirs parnam3 + * + * 1. Set parnam1 to an array of patterns.... + * ${(P)parnam1} is an in/out parameter. + * 2. Like #1 but without calling cfp_opt_pats(). (This is only used by _approximate.) + * 3. Like #1 but varargs is implicitly set to char *varargs[2] = { "*(-/)", NULL };. + * + * parnam2 has to do with the accept-exact style (see cfp_test_exact()). + */ + static int bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { @@ -4839,11 +4867,12 @@ bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } queue_signals(); if (!(tmp = getaparam(args[1]))) { + unqueue_signals(); zwarnnam(nam, "unknown parameter: %s", args[1]); return 0; } for (l = newlinklist(); *tmp; tmp++) - addlinknode(l, *tmp); + addlinknode(l, quotestring(*tmp, QT_BACKSLASH_PATTERN)); set_list_array(args[1], cf_pats((args[0][1] == 'P'), !!args[0][2], l, getaparam(args[2]), args[3], args[4], args[5], @@ -4994,7 +5023,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { return 0; } diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index 2b2654c5d..58310cd74 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -143,6 +143,7 @@ "vi-delete", videlete, ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_VIOPER "vi-delete-char", videletechar, ZLE_KEEPSUFFIX "vi-digit-or-beginning-of-line", vidigitorbeginningofline, 0 +"vi-down-case", vidowncase, ZLE_LASTCOL | ZLE_VIOPER "vi-down-line-or-history", vidownlineorhistory, ZLE_LINEMOVE "vi-end-of-line", viendofline, ZLE_LASTCOL "vi-fetch-history", vifetchhistory, ZLE_LINEMOVE @@ -188,6 +189,7 @@ "vi-swap-case", viswapcase, ZLE_LASTCOL "vi-undo-change", viundochange, ZLE_KEEPSUFFIX "vi-unindent", viunindent, ZLE_LASTCOL | ZLE_VIOPER +"vi-up-case", viupcase, ZLE_LASTCOL | ZLE_VIOPER "vi-up-line-or-history", viuplineorhistory, ZLE_LINEMOVE "vi-yank", viyank, ZLE_LASTCOL | ZLE_VIOPER "vi-yank-eol", viyankeol, 0 diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c index 9b3277a97..3db0781ff 100644 --- a/Src/Zle/textobjects.c +++ b/Src/Zle/textobjects.c @@ -1,5 +1,5 @@ /* - * textobjects.c - ZLE module implementing Vim style text objects + * textobjects.c - ZLE widgets implementing Vim style text objects * * This file is part of zsh, the Z shell. * @@ -54,11 +54,7 @@ selectword(UNUSED(char **args)) int sclass = viclass(zleline[zlecs]); int doblanks = all && sclass; - if (!invicmdmode()) { - region_active = 1; - mark = zlecs; - } - if (!region_active || zlecs == mark) { + if (!region_active || zlecs == mark || mark == -1) { /* search back to first character of same class as the start position * also stop at the beginning of the line */ mark = zlecs; @@ -207,8 +203,12 @@ selectword(UNUSED(char **args)) /* Adjustment: vi operators don't include the cursor position, in insert * or emacs mode the region also doesn't but for vi visual mode it is * included. */ - if (zlecs && zlecs > mark && !virangeflag) - DECCS(); + if (!virangeflag) { + if (!invicmdmode()) + region_active = 1; + else if (zlecs && zlecs > mark) + DECCS(); + } return 0; } @@ -315,7 +315,7 @@ selectargument(UNUSED(char **args)) } /* Adjustment: vi operators don't include the cursor position */ - if (!virangeflag) + if (!virangeflag && invicmdmode()) DECCS(); return 0; diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index e9b14281d..8f92e5611 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -284,6 +284,20 @@ struct change { #define CH_NEXT (1<<0) /* next structure is also part of this change */ #define CH_PREV (1<<1) /* previous structure is also part of this change */ +/* vi change handling for vi-repeat-change */ + +/* + * Examination of the code suggests vichgbuf is consistently tied + * to raw byte input, so it is left as a character array rather + * than turned into wide characters. In particular, when we replay + * it we use ungetbytes(). + */ +struct vichange { + struct modifier mod; /* value of zmod associated with vi change */ + char *buf; /* bytes for keys that make up the vi command */ + int bufsz, bufptr; /* allocated and in use sizes of buf */ +}; + /* known thingies */ #define Th(X) (&thingies[X]) diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 382eb8d41..04eb70675 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -135,7 +135,10 @@ mod_export HashTable keymapnamtab; /**/ char *keybuf; -static int keybuflen, keybufsz = 20; +/**/ +int keybuflen; + +static int keybufsz = 20; /* last command executed with execute-named-command */ @@ -1322,15 +1325,15 @@ default_bindings(void) amap->first[i] = refthingy(t_undefinedkey); /* safe fallback keymap: - * 0-255 self-insert, except: * - * '\n' accept-line * - * '\r' accept-line */ + * 0-255 .self-insert, except: * + * '\n' .accept-line * + * '\r' .accept-line */ for (i = 0; i < 256; i++) - smap->first[i] = refthingy(t_selfinsert); - unrefthingy(t_selfinsert); - unrefthingy(t_selfinsert); - smap->first['\n'] = refthingy(t_acceptline); - smap->first['\r'] = refthingy(t_acceptline); + smap->first[i] = refthingy(t_Dselfinsert); + unrefthingy(t_Dselfinsert); + unrefthingy(t_Dselfinsert); + smap->first['\n'] = refthingy(t_Dacceptline); + smap->first['\r'] = refthingy(t_Dacceptline); /* vt100 arrow keys are bound by default, for historical reasons. * * Both standard and keypad modes are supported. */ @@ -1366,6 +1369,8 @@ default_bindings(void) bindkey(vismap, "\33", refthingy(t_deactivateregion), NULL); bindkey(vismap, "o", refthingy(t_exchangepointandmark), NULL); bindkey(vismap, "p", refthingy(t_putreplaceselection), NULL); + bindkey(vismap, "u", refthingy(t_vidowncase), NULL); + bindkey(vismap, "U", refthingy(t_viupcase), NULL); bindkey(vismap, "x", refthingy(t_videlete), NULL); bindkey(vismap, "~", refthingy(t_vioperswapcase), NULL); @@ -1374,8 +1379,12 @@ default_bindings(void) bindkey(amap, "ge", refthingy(t_vibackwardwordend), NULL); bindkey(amap, "gE", refthingy(t_vibackwardblankwordend), NULL); bindkey(amap, "gg", refthingy(t_beginningofbufferorhistory), NULL); + bindkey(amap, "gu", refthingy(t_vidowncase), NULL); + bindkey(amap, "gU", refthingy(t_viupcase), NULL); bindkey(amap, "g~", refthingy(t_vioperswapcase), NULL); bindkey(amap, "g~~", NULL, "g~g~"); + bindkey(amap, "guu", NULL, "gugu"); + bindkey(amap, "gUU", NULL, "gUgU"); /* emacs mode: arrow keys */ add_cursor_key(emap, TCUPCURSOR, t_uplineorhistory, 'A'); @@ -1615,11 +1624,18 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) else lastchar = lastc; if(lastlen != keybuflen) { + /* + * We want to keep only the first lastlen bytes of the key + * buffer in the key buffer that were marked as used by the key + * binding above, and make the rest available for input again. + * That rest (but not what we are keeping) needs to be + * unmetafied. + */ unmetafy(keybuf + lastlen, &keybuflen); ungetbytes(keybuf+lastlen, keybuflen); if(vichgflag) - vichgbufptr -= keybuflen; - keybuf[lastlen] = 0; + curvichg.bufptr -= keybuflen; + keybuf[keybuflen = lastlen] = 0; } *funcp = func; *strp = str; @@ -1672,7 +1688,7 @@ getkeybuf(int w) mod_export void ungetkeycmd(void) { - ungetbytes(keybuf, keybuflen); + ungetbytes_unmeta(keybuf, keybuflen); } /* read a command from the current keymap, with widgets */ @@ -1690,17 +1706,12 @@ getkeycmd(void) if(!*seq) return NULL; if(!func) { - int len; - char *pb; - if (++hops == 20) { zerr("string inserting another one too many times"); hops = 0; return NULL; } - pb = unmetafy(ztrdup(str), &len); - ungetbytes(pb, len); - zfree(pb, strlen(str) + 1); + ungetbytes_unmeta(str, strlen(str)); goto sentstring; } if (func == Th(z_executenamedcmd) && !statusline) { diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 6e2bfded8..15ea79643 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -357,6 +357,21 @@ ungetbytes(char *s, int len) ungetbyte(*--s); } +/**/ +void +ungetbytes_unmeta(char *s, int len) +{ + s += len; + while (len--) { + if (len && s[-2] == Meta) { + ungetbyte(*--s ^ 32); + len--; + s--; + } else + ungetbyte(*--s); + } +} + #if defined(pyr) && defined(HAVE_SELECT) static int breakread(int fd, char *buf, int n) @@ -456,7 +471,7 @@ calc_timeout(struct ztmout *tmoutp, long do_keytmout) tfdat = (Timedfn)getdata(tfnode); diff = tfdat->when - time(NULL); - if (diff < 0) { + if (diff <= 0) { /* Already due; call it and rescan. */ tfdat->func(); continue; @@ -909,13 +924,13 @@ getbyte(long do_keytmout, int *timeout) ret = STOUC(cc); } /* - * vichgbuf is raw bytes, not wide characters, so is dealt + * curvichg.buf is raw bytes, not wide characters, so is dealt * with here. */ if (vichgflag) { - if (vichgbufptr == vichgbufsz) - vichgbuf = realloc(vichgbuf, vichgbufsz *= 2); - vichgbuf[vichgbufptr++] = ret; + if (curvichg.bufptr == curvichg.bufsz) + curvichg.buf = realloc(curvichg.buf, curvichg.bufsz *= 2); + curvichg.buf[curvichg.bufptr++] = ret; } errno = old_errno; return lastchar = ret; @@ -1026,28 +1041,43 @@ getrestchar(int inchar, char *outstr, int *outcount) #endif /**/ -void redrawhook(void) +void +redrawhook(void) { Thingy initthingy; if ((initthingy = rthingy_nocreate("zle-line-pre-redraw"))) { + /* Duplicating most of zlecallhook() to save additional state */ + int saverrflag = errflag, savretflag = retflag; int lastcmd_prev = lastcmd; int old_incompfunc = incompfunc; char *args[2]; Thingy lbindk_save = lbindk, bindk_save = bindk; + refthingy(lbindk_save); refthingy(bindk_save); args[0] = initthingy->nam; args[1] = NULL; + + /* The generic redraw hook cannot be a completion function, so + * temporarily reset state for special variable handling etc. + */ incompfunc = 0; - execzlefunc(initthingy, args, 0); + execzlefunc(initthingy, args, 1); incompfunc = old_incompfunc; + + /* Restore errflag and retflag as zlecallhook() does */ + errflag = saverrflag | (errflag & ERRFLAG_INT); + retflag = savretflag; + unrefthingy(initthingy); unrefthingy(lbindk); unrefthingy(bindk); lbindk = lbindk_save; bindk = bindk_save; + /* we can't set ZLE_NOTCOMMAND since it's not a legit widget, so - * restore lastcmd manually so that we don't mess up the global state */ + * restore lastcmd manually so that we don't mess up the global state + */ lastcmd = lastcmd_prev; } } @@ -1144,11 +1174,19 @@ zlecore(void) } - region_active = 0; popheap(); } -/* Read a line. It is returned metafied. */ +/* Read a line. It is returned metafied. + * + * Parameters: + * - lp: left prompt, e.g., $PS1 + * - rp: right prompt, e.g., $RPS1 + * - flags: ZLRF_* flags (I think), see zlereadflags + * - context: ZLCON_* flags (I think), see zlecontext + * - init: "zle-line-init" + * - finish: "zle-line-finish" + */ /**/ char * @@ -1222,6 +1260,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) *zleline = ZWC('\0'); virangeflag = lastcmd = done = zlecs = zlell = mark = yankb = yanke = 0; vichgflag = 0; + viinrepeat = 0; viinsbegin = 0; statusline = NULL; selectkeymap("main", 1); @@ -1277,6 +1316,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) lastcol = -1; initmodifier(&zmod); prefixflag = 0; + region_active = 0; zrefresh(); @@ -1284,6 +1324,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) zlecallhook(init, NULL); + if (zleline && *zleline) + redrawhook(); + if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2) fputs(*bracket, shout); @@ -1326,6 +1369,16 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) return s; } +/**/ +static int +execimmortal(Thingy func, char **args) +{ + Thingy immortal = rthingy_nocreate(dyncat(".", func->nam)); + if (immortal) + return execzlefunc(immortal, args, 0); + return 1; +} + /* * Execute a widget. The third argument indicates that the global * variable bindk should be set temporarily so that WIDGET etc. @@ -1337,6 +1390,8 @@ int execzlefunc(Thingy func, char **args, int set_bindk) { int r = 0, ret = 0, remetafy = 0; + int nestedvichg = vichgflag; + int isrepeat = (viinrepeat == 3); Widget w; Thingy save_bindk = bindk; @@ -1346,8 +1401,10 @@ execzlefunc(Thingy func, char **args, int set_bindk) unmetafy_line(); remetafy = 1; } + if (isrepeat) + viinrepeat = 2; - if(func->flags & DISABLED) { + if (func->flags & DISABLED) { /* this thingy is not the name of a widget */ char *nm = nicedup(func->nam, 0); char *msg = tricat("No such widget `", nm, "'"); @@ -1355,7 +1412,7 @@ execzlefunc(Thingy func, char **args, int set_bindk) zsfree(nm); showmsg(msg); zsfree(msg); - ret = 1; + ret = execimmortal(func, args); } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) { int wflags = w->flags; @@ -1419,7 +1476,7 @@ execzlefunc(Thingy func, char **args, int set_bindk) zsfree(nm); showmsg(msg); zsfree(msg); - ret = 1; + ret = execimmortal(func, args); } else { int osc = sfcontext, osi = movefd(0); int oxt = isset(XTRACE); @@ -1441,6 +1498,12 @@ execzlefunc(Thingy func, char **args, int set_bindk) opts[XTRACE] = oxt; sfcontext = osc; endparamscope(); + if (errflag == ERRFLAG_ERROR) { + int saverr = errflag; + errflag &= ~ERRFLAG_ERROR; + if ((ret = execimmortal(func, args)) != 0) + errflag |= saverr; + } lastcmd = w->flags & ~(WIDGET_INUSE|WIDGET_FREE); if (inuse) { w->flags &= WIDGET_INUSE|WIDGET_FREE; @@ -1469,6 +1532,25 @@ execzlefunc(Thingy func, char **args, int set_bindk) CCRIGHT(); if (remetafy) metafy_line(); + + /* if this widget constituted the vi change, end it */ + if (vichgflag == 2 && !nestedvichg) { + if (invicmdmode()) { + if (ret) { + free(curvichg.buf); + } else { + if (lastvichg.buf) + free(lastvichg.buf); + lastvichg = curvichg; + } + vichgflag = 0; + curvichg.buf = NULL; + } else + vichgflag = 1; /* vi change continues while in insert mode */ + } + if (isrepeat) + viinrepeat = !invicmdmode(); + return ret; } @@ -1604,6 +1686,7 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func)) return 1; } else if (v) { if (*s) { + unqueue_signals(); zwarnnam(name, "not an identifier: `%s'", args[0]); return 1; } @@ -1840,11 +1923,17 @@ int recursiveedit(UNUSED(char **args)) { int locerror; + int q = queue_signal_level(); + + /* zlecore() expects to be entered with signal queue disabled */ + dont_queue_signals(); redrawhook(); zrefresh(); zlecore(); + restore_queue_signals(q); + locerror = errflag ? 1 : 0; errflag = done = eofsent = 0; @@ -1856,6 +1945,7 @@ void reexpandprompt(void) { static int reexpanding; + static int looping; if (!reexpanding++) { /* @@ -1866,15 +1956,33 @@ reexpandprompt(void) int local_lastval = lastval; lastval = pre_zle_status; - free(lpromptbuf); - lpromptbuf = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL, - &pmpt_attr); - rpmpt_attr = pmpt_attr; - free(rpromptbuf); - rpromptbuf = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL, - &rpmpt_attr); + do { + /* A new SIGWINCH may arrive while in promptexpand(), causing + * looping to increment. This only happens when a command + * substitution is used in a PROMPT_SUBST prompt, but + * nevertheless keep trying until we see no more changes. + */ + char *new_lprompt, *new_rprompt; + looping = reexpanding; + + new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL, + &pmpt_attr); + 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); + free(rpromptbuf); + rpromptbuf = new_rprompt; + } while (looping != reexpanding); + lastval = local_lastval; - } + } else + looping = reexpanding; reexpanding--; } @@ -2150,7 +2258,7 @@ finish_(UNUSED(Module m)) cleanup_keymaps(); deletehashtable(thingytab); - zfree(vichgbuf, vichgbufsz); + zfree(lastvichg.buf, lastvichg.bufsz); zfree(kungetbuf, kungetsz); free_isrch_spots(); if (rdstrs) diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c index 0483f758d..898b552de 100644 --- a/Src/Zle/zle_misc.c +++ b/Src/Zle/zle_misc.c @@ -597,7 +597,7 @@ static void pastebuf(Cutbuffer buf, int mult, int position) zlecs += cc; } yanke = zlecs; - if (zlecs) + if (zlecs && invicmdmode()) DECCS(); } } @@ -609,8 +609,10 @@ viputbefore(UNUSED(char **args)) int n = zmult; startvichange(-1); - if (n < 0 || zmod.flags & MOD_NULL) + if (n < 0) return 1; + if (zmod.flags & MOD_NULL) + return 0; if (zmod.flags & MOD_VIBUF) kctbuf = &vibuf[zmod.vibuf]; else @@ -630,8 +632,10 @@ viputafter(UNUSED(char **args)) int n = zmult; startvichange(-1); - if (n < 0 || zmod.flags & MOD_NULL) + if (n < 0) return 1; + if (zmod.flags & MOD_NULL) + return 0; if (zmod.flags & MOD_VIBUF) kctbuf = &vibuf[zmod.vibuf]; else @@ -780,7 +784,7 @@ bracketedpaste(char **args) int n; ZLE_STRING_T wpaste; wpaste = stringaszleline((zmult == 1) ? pbuf : - quotestring(pbuf, NULL, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL); + quotestring(pbuf, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL); cuttext(wpaste, n, CUT_REPLACE); if (!(zmod.flags & MOD_VIBUF)) { kct = -1; @@ -1497,7 +1501,7 @@ struct suffixset { }; /* The list of suffix structures */ -struct suffixset *suffixlist; +static struct suffixset *suffixlist; /* Shell function to call to remove the suffix. */ diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c index b5bb288f1..1e4c5b832 100644 --- a/Src/Zle/zle_params.c +++ b/Src/Zle/zle_params.c @@ -103,9 +103,27 @@ static const struct gsu_integer yankend_gsu = { get_yankend, set_yankend, zleunsetfn }; static const struct gsu_integer yankactive_gsu = { get_yankactive, NULL, zleunsetfn }; +static const struct gsu_integer isearchmatchstart_gsu = +{ get_isearchmatchstart, NULL, zleunsetfn }; +static const struct gsu_integer isearchmatchend_gsu = +{ get_isearchmatchend, NULL, zleunsetfn }; +static const struct gsu_integer isearchmatchactive_gsu = +{ get_isearchmatchactive, NULL, zleunsetfn }; +static const struct gsu_integer suffixstart_gsu = +{ get_suffixstart, NULL, zleunsetfn }; +static const struct gsu_integer suffixend_gsu = +{ get_suffixend, NULL, zleunsetfn }; +static const struct gsu_integer suffixactive_gsu = +{ get_suffixactive, NULL, zleunsetfn }; static const struct gsu_array killring_gsu = { get_killring, set_killring, unset_killring }; + +static const struct gsu_scalar register_gsu = +{ strgetfn, set_register, unset_register }; +static const struct gsu_hash registers_gsu = +{ hashgetfn, set_registers, zleunsetfn }; + /* implementation is in zle_refresh.c */ static const struct gsu_array region_highlight_gsu = { get_region_highlight, set_region_highlight, unset_region_highlight }; @@ -152,6 +170,12 @@ static struct zleparam { { "YANK_START", PM_INTEGER, GSU(yankstart_gsu), NULL }, { "YANK_END", PM_INTEGER, GSU(yankend_gsu), NULL }, { "YANK_ACTIVE", PM_INTEGER | PM_READONLY, GSU(yankactive_gsu), NULL }, + { "ISEARCHMATCH_START", PM_INTEGER, GSU(isearchmatchstart_gsu), NULL }, + { "ISEARCHMATCH_END", PM_INTEGER, GSU(isearchmatchend_gsu), NULL }, + { "ISEARCHMATCH_ACTIVE", PM_INTEGER | PM_READONLY, GSU(isearchmatchactive_gsu), NULL }, + { "SUFFIX_START", PM_INTEGER, GSU(suffixstart_gsu), NULL }, + { "SUFFIX_END", PM_INTEGER, GSU(suffixend_gsu), NULL }, + { "SUFFIX_ACTIVE", PM_INTEGER | PM_READONLY, GSU(suffixactive_gsu), NULL }, { "ZLE_STATE", PM_SCALAR | PM_READONLY, GSU(zle_state_gsu), NULL }, { NULL, 0, NULL, NULL } }; @@ -163,6 +187,7 @@ mod_export void makezleparams(int ro) { struct zleparam *zp; + Param reg_param; for(zp = zleparams; zp->name; zp++) { Param pm = createparam(zp->name, (zp->type |PM_SPECIAL|PM_REMOVABLE| @@ -188,6 +213,11 @@ makezleparams(int ro) if ((zp->type & PM_UNSET) && (zmod.flags & (MOD_MULT|MOD_TMULT))) pm->node.flags &= ~PM_UNSET; } + + reg_param = createspecialhash("registers", get_registers, &scan_registers, + PM_LOCAL|PM_REMOVABLE); + reg_param->gsu.h = ®isters_gsu; + reg_param->level = locallevel + 1; } /* Special unset function for ZLE special parameters: act like the standard * @@ -521,6 +551,48 @@ set_yankend(UNUSED(Param pm), zlong i) } /**/ +static zlong +get_isearchmatchstart(UNUSED(Param pm)) +{ + return isearch_startpos; +} + +/**/ +static zlong +get_isearchmatchend(UNUSED(Param pm)) +{ + return isearch_endpos; +} + +/**/ +static zlong +get_isearchmatchactive(UNUSED(Param pm)) +{ + return isearch_active; +} + +/**/ +static zlong +get_suffixstart(UNUSED(Param pm)) +{ + return zlecs - suffixlen; +} + +/**/ +static zlong +get_suffixend(UNUSED(Param pm)) +{ + return zlecs; +} + +/**/ +static zlong +get_suffixactive(UNUSED(Param pm)) +{ + return suffixlen; +} + +/**/ static char * get_cutbuffer(UNUSED(Param pm)) { @@ -652,6 +724,111 @@ unset_killring(Param pm, int exp) } } +/**/ +static void +set_register(Param pm, char *value) +{ + int n = 0; + int offset = -1; + Cutbuffer vbuf; + + if (!pm->node.nam || pm->node.nam[1]) + ; + else if (*pm->node.nam >= '0' && *pm->node.nam <= '9') + offset = '0' - 26; + else if (*pm->node.nam >= 'a' && *pm->node.nam <= 'z') + offset = 'a'; + + if (offset == -1) { + zerr("invalid zle register: %s", pm->node.nam); + return; + } + + vbuf = &vibuf[*pm->node.nam - offset]; + if (*value) + vbuf->buf = stringaszleline(value, 0, &n, NULL, NULL); + vbuf->len = n; +} + +/**/ +static void +unset_register(Param pm, UNUSED(int exp)) +{ + set_register(pm, ""); +} + +/**/ +static void +scan_registers(UNUSED(HashTable ht), ScanFunc func, int flags) +{ + int i; + char ch; + struct param pm; + + memset((void *)&pm, 0, sizeof(struct param)); + pm.node.flags = PM_SCALAR | PM_READONLY; + pm.gsu.s = &nullsetscalar_gsu; + + for (i = 0, ch = 'a'; i < 36; i++) { + pm.node.nam = zhalloc(2); + *pm.node.nam = ch; + pm.node.nam[1] = '\0'; + pm.u.str = zlelineasstring(vibuf[i].buf, vibuf[i].len, 0, NULL, NULL, 1); + func(&pm.node, flags); + if (ch++ == 'z') + ch = '0'; + } +} + +/**/ +static HashNode +get_registers(UNUSED(HashTable ht), const char *name) +{ + Param pm = (Param) hcalloc(sizeof(struct param)); + int vbuf = -1; + pm->node.nam = dupstring(name); + pm->node.flags = PM_SCALAR; + pm->gsu.s = ®ister_gsu; + + if (name[1]) + ; + else if (*name >= '0' && *name <= '9') + vbuf = *name - '0' + 26; + else if (*name >= 'a' && *name <= 'z') + vbuf = *name - 'a'; + + if (vbuf == -1) { + pm->u.str = dupstring(""); + pm->node.flags |= (PM_UNSET|PM_SPECIAL); + } else + pm->u.str = zlelineasstring(vibuf[vbuf].buf, vibuf[vbuf].len, 0, NULL, NULL, 1); + + return &pm->node; +} + +/**/ +static void +set_registers(UNUSED(Param pm), HashTable ht) +{ + int i; + HashNode hn; + + if (!ht) + return; + + for (i = 0; i < ht->hsize; i++) + for (hn = ht->nodes[i]; hn; hn = hn->next) { + struct value v; + v.isarr = v.flags = v.start = 0; + v.end = -1; + v.arr = NULL; + v.pm = (Param) hn; + + set_register(v.pm, getstrvalue(&v)); + } + deleteparamtable(ht); +} + static void set_prepost(ZLE_STRING_T *textvar, int *lenvar, char *x) { diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 3d2471e27..8d173cda1 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -946,7 +946,7 @@ addmultiword(REFRESH_ELEMENT *base, ZLE_STRING_T tptr, int ichars) /* * Swap the old and new video buffers, plus any associated multiword - * buffers. The new buffer becomes the old one; the new new buffer + * buffers. The new buffer becomes the old one; the new buffer * will be filled with the command line next time. */ static void @@ -1143,8 +1143,7 @@ zrefresh(void) tsetcap(TCALLATTRSOFF, 0); tsetcap(TCSTANDOUTEND, 0); tsetcap(TCUNDERLINEEND, 0); - /* cheat on attribute unset */ - txtunset(TXTBOLDFACE|TXTSTANDOUT|TXTUNDERLINE); + txtattrmask = 0; if (trashedzle && !clearflag) reexpandprompt(); @@ -2424,6 +2423,7 @@ clearscreen(UNUSED(char **args)) tcoutclear(TCCLEARSCREEN); resetneeded = 1; clearflag = 0; + reexpandprompt(); return 0; } @@ -2434,8 +2434,8 @@ redisplay(UNUSED(char **args)) moveto(0, 0); zputc(&zr_cr); /* extra care */ tc_upcurs(lprompth - 1); - resetneeded = 1; - clearflag = 0; + resetneeded = !showinglist; + clearflag = showinglist; return 0; } diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index 21495b6f2..c7092854a 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -269,7 +269,7 @@ freewidget(Widget w) zfree(w, sizeof(*w)); } -/* Add am internal widget provided by a module. The name given is the * +/* Add an internal widget provided by a module. The name given is the * * canonical one, which must not begin with a dot. The widget is first * * bound to the dotted canonical name; if that name is already taken by * * an internal widget, failure is indicated. The same widget is then * @@ -678,7 +678,16 @@ bin_zle_flags(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) else if (!strcmp(*flag, "keepsuffix")) w->flags |= ZLE_KEEPSUFFIX; */ - else { + else if (!strcmp(*flag, "vichange")) { + if (invicmdmode()) { + startvichange(-1); + if (zmod.flags & (MOD_MULT|MOD_TMULT)) { + Param pm = (Param) paramtab->getnode(paramtab, "NUMERIC"); + if (pm && pm->node.flags & PM_SPECIAL) + pm->node.flags &= ~PM_UNSET; + } + } + } else { zwarnnam(name, "invalid flag `%s' given to zle -f", *flag); ret = 1; } @@ -756,6 +765,10 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) } t = rthingy(wname); + /* for internal widgets we set bindk except for when getting + * a vi range to detect a repeated key */ + setbindk = setbindk || + (t->widget && (t->widget->flags & (WIDGET_INT | ZLE_VIOPER)) == WIDGET_INT); ret = execzlefunc(t, args, setbindk); unrefthingy(t); if (saveflag) diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index cc4b7d673..3d8679119 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -100,8 +100,7 @@ mod_export int usemenu, useglob; /**/ mod_export int wouldinstab; -/* != 0 if we are in the middle of a menu completion. May be == 2 to force * - * menu completion even if using different widgets. */ +/* != 0 if we are in the middle of a menu completion. */ /**/ mod_export int menucmp; @@ -425,8 +424,8 @@ mod_export int instring, inbackt; * This uses the instring variable above. */ -#define quotename(s, e) \ -quotestring(s, e, instring == QT_NONE ? QT_BACKSLASH : instring) +#define quotename(s) \ +quotestring(s, instring == QT_NONE ? QT_BACKSLASH : instring) /* Check if the given string is the name of a parameter and if this * * parameter is one worth expanding. */ @@ -710,7 +709,8 @@ docomplete(int lst) for (t0 = cmdnamtab->hsize - 1; t0 >= 0; t0--) for (hn = cmdnamtab->nodes[t0]; hn; hn = hn->next) { - if (strpfx(q, hn->nam) && findcmd(hn->nam, 0)) + if (strpfx(q, hn->nam) && + findcmd(hn->nam, 0, 0)) n++; if (n == 2) break; @@ -1162,6 +1162,7 @@ get_comp_string(void) inpush(dupstrspace(linptr), 0, NULL); strinbeg(0); wordpos = cp = rd = ins = oins = linarr = parct = ia = redirpos = 0; + we = wb = zlemetacs; tt0 = NULLTOK; /* This loop is possibly the wrong way to do this. It goes through * @@ -1239,6 +1240,20 @@ get_comp_string(void) /* Record if we haven't had the command word yet */ if (wordpos == redirpos) redirpos++; + if (zlemetacs < (zlemetall - inbufct) && + zlemetacs >= wordbeg && wb == we) { + /* Cursor is in the middle of a redirection, treat as a word */ + we = zlemetall - (inbufct + addedx); + if (addedx && we > wb) { + /* Assume we are in {param}> form, wb points at "{" */ + wb++; + /* Should complete parameter names here */ + } else { + /* In "2>" form, zlemetacs points at "2" */ + wb = zlemetacs; + /* Should insert a space under cursor here */ + } + } } if (tok == DINPAR) tokstr = NULL; @@ -1290,8 +1305,12 @@ get_comp_string(void) zsfree(cmdstr); cmdstr = ztrdup(tokstr); cmdtok = tok; - /* If everything before is a redirection, don't reset the index */ - if (wordpos != redirpos) + /* + * If everything before is a redirection, or anything + * complicated enough that we've seen the word the + * cursor is on, don't reset the index. + */ + if (wordpos != redirpos && clwpos == -1) wordpos = redirpos = 0; } else if (tok == SEPER) { /* @@ -1399,9 +1418,17 @@ get_comp_string(void) /* If this is the word the cursor is in and we added a `x', * * remove it. */ if (clwpos == wordpos++ && addedx) { + int chuck_at, word_diff; zlemetacs_qsub = zlemetacs - qsub; - chuck(&clwords[wordpos - 1][((zlemetacs_qsub - wb) >= sl) ? - (sl - 1) : (zlemetacs_qsub - wb)]); + word_diff = zlemetacs_qsub - wb; + /* Ensure we chuck within the word... */ + if (word_diff >= sl) + chuck_at = sl -1; + else if (word_diff < 0) + chuck_at = 0; + else + chuck_at = word_diff; + chuck(&clwords[wordpos - 1][chuck_at]); } } while (tok != LEXERR && tok != ENDINPUT && (tok != SEPER || (lexflags && tt0 == NULLTOK))); @@ -1449,7 +1476,9 @@ get_comp_string(void) t0 = STRING; } else if (t0 == STRING || t0 == TYPESET) { /* We found a simple string. */ - s = ztrdup(clwords[clwpos]); + s = clwords[clwpos]; + DPUTS(!s, "Completion word has disappeared!"); + s = ztrdup(s ? s : ""); } else if (t0 == ENVSTRING) { char sav; /* The cursor was inside a parameter assignment. */ @@ -1850,8 +1879,12 @@ get_comp_string(void) ocs = zlemetacs; zlemetacs = i; foredel(skipchars, CUT_RAW); - if ((zlemetacs = ocs) > --i) + if ((zlemetacs = ocs) > --i) { zlemetacs -= skipchars; + /* do not skip past the beginning of the word */ + if (wb > zlemetacs) + zlemetacs = wb; + } we -= skipchars; } } else { @@ -1862,6 +1895,9 @@ get_comp_string(void) zlemetacs = we - skipchars; else zlemetacs = ocs; + /* do not skip past the beginning of the word */ + if (wb > zlemetacs) + zlemetacs = wb; we -= skipchars; } /* we need to get rid of all the quotation bits... */ @@ -1983,11 +2019,11 @@ get_comp_string(void) new->next = NULL; new->str = dupstrpfx(bbeg, len); - new->str = ztrdup(quotename(new->str, NULL)); + new->str = ztrdup(quotename(new->str)); untokenize(new->str); new->pos = begi; *dbeg = '\0'; - new->qpos = strlen(quotename(predup, NULL)); + new->qpos = strlen(quotename(predup)); *dbeg = '{'; i -= len; boffs -= len; @@ -2046,11 +2082,11 @@ get_comp_string(void) lastbrbeg = new; new->str = dupstrpfx(bbeg, len); - new->str = ztrdup(quotename(new->str, NULL)); + new->str = ztrdup(quotename(new->str)); untokenize(new->str); new->pos = begi; *dbeg = '\0'; - new->qpos = strlen(quotename(predup, NULL)); + new->qpos = strlen(quotename(predup)); *dbeg = '{'; i -= len; boffs -= len; @@ -2096,7 +2132,7 @@ get_comp_string(void) brend = new; new->str = dupstrpfx(bbeg, len); - new->str = ztrdup(quotename(new->str, NULL)); + new->str = ztrdup(quotename(new->str)); untokenize(new->str); new->pos = dp - predup - len + 1; new->qpos = len; @@ -2125,11 +2161,11 @@ get_comp_string(void) lastbrbeg = new; new->str = dupstrpfx(bbeg, len); - new->str = ztrdup(quotename(new->str, NULL)); + new->str = ztrdup(quotename(new->str)); untokenize(new->str); new->pos = begi; *dbeg = '\0'; - new->qpos = strlen(quotename(predup, NULL)); + new->qpos = strlen(quotename(predup)); *dbeg = '{'; boffs -= len; memmove(dbeg, dbeg + len, 1+strlen(dbeg+len)); @@ -2144,7 +2180,7 @@ get_comp_string(void) p = bp->pos; l = bp->qpos; bp->pos = strlen(predup + p + l); - bp->qpos = strlen(quotename(predup + p + l, NULL)); + bp->qpos = strlen(quotename(predup + p + l)); memmove(predup + p, predup + p + l, 1+bp->pos); } } @@ -2267,7 +2303,7 @@ doexpansion(char *s, int lst, int olst, int explincmd) foredel(we - wb, CUT_RAW); while ((ss = (char *)ugetnode(vl))) { ret = 0; - ss = quotename(ss, NULL); + ss = quotename(ss); untokenize(ss); inststr(ss); if (nonempty(vl) || !first) { @@ -2976,7 +3012,7 @@ processcmd(UNUSED(char **args)) inststr(" "); untokenize(s); - inststr(quotename(s, NULL)); + inststr(quotename(s)); zsfree(s); done = 1; @@ -3006,7 +3042,7 @@ expandcmdpath(UNUSED(char **args)) return 1; } - str = findcmd(s, 1); + str = findcmd(s, 1, 0); zsfree(s); if (!str) return 1; diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 6e9a98bde..c6df3d89c 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -587,7 +587,7 @@ struct zle_position { }; /* LIFO stack of positions */ -struct zle_position *zle_positions; +static struct zle_position *zle_positions; /* * Save positions including cursor, end-of-line and @@ -1412,7 +1412,7 @@ zlong undo_changeno; /* If positive, don't undo beyond this point */ -zlong undo_limitno; +static zlong undo_limitno; /**/ void @@ -1589,9 +1589,14 @@ undo(char **args) break; if (prev->changeno <= undo_limitno && !*args) return 1; - if (!unapplychange(prev) && last_change >= 0) - unapplychange(prev); - curchange = prev; + if (!unapplychange(prev)) { + if (last_change >= 0) { + unapplychange(prev); + curchange = prev; + } + } else { + curchange = prev; + } } while (last_change >= (zlong)0 || (curchange->flags & CH_PREV)); setlastline(); return 0; @@ -1678,7 +1683,7 @@ viundochange(char **args) /**/ int -splitundo(char **args) +splitundo(UNUSED(char **args)) { if (vistartchange >= 0) { mergeundo(); @@ -1699,6 +1704,7 @@ mergeundo(void) current->flags |= CH_PREV; current->prev->flags |= CH_NEXT; } + vistartchange = -1; } /* @@ -1720,6 +1726,8 @@ zlecallhook(char *name, char *arg) if (!thingy) return; + /* If anything here needs changing, see also redrawhook() */ + saverrflag = errflag; savretflag = retflag; diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c index 86840bdd6..e0923db3e 100644 --- a/Src/Zle/zle_vi.c +++ b/Src/Zle/zle_vi.c @@ -45,61 +45,71 @@ int wordflag; /**/ int vilinerange; -/* last vi change buffer, for vi change repetition */ +/* + * lastvichg: last vi change buffer, for vi change repetition + * curvichg: current incomplete vi change + */ + +/**/ +struct vichange lastvichg, curvichg; + +/* + * true whilst a vi change is active causing keys to be + * accumulated in curvichg.buf + * first set to 2 and when the initial widget finishes, reduced to 1 if + * in insert mode implying that the change continues until returning to + * normal mode + */ /**/ -int vichgbufsz, vichgbufptr, vichgflag; +int vichgflag; /* - * Examination of the code suggests vichgbuf is consistently tied - * to raw byte input, so it is left as a character array rather - * than turned into wide characters. In particular, when we replay - * it we use ungetbytes(). + * analogous to vichgflag for a repeated change with the value following + * a similar pattern (is 3 until first repeated widget starts) */ + /**/ -char *vichgbuf; +int viinrepeat; /* point where vi insert mode was last entered */ /**/ int viinsbegin; -static struct modifier lastmod; -static int inrepeat, vichgrepeat; - /** * im: >= 0: is an insertmode - * -1: skip setting insert mode + * -1: skip setting insert/overwrite mode * -2: entering viins at start of editing from clean --- don't use - * inrepeat or lastchar, synthesise an i to enter insert mode. + * inrepeat or keybuf, synthesise an entry to insert mode. + * Note that zmult is updated so this should be called before zmult is used. */ /**/ void startvichange(int im) { - if (im != -1) { - vichgflag = 1; - if (im > -1) - insmode = im; - } - if (inrepeat && im != -2) { - zmod = lastmod; - inrepeat = vichgflag = 0; - vichgrepeat = 1; - } else { - lastmod = zmod; - if (vichgbuf) - free(vichgbuf); - vichgbuf = (char *)zalloc(vichgbufsz = 16); + if (im > -1) + insmode = im; + if (viinrepeat && im != -2) { + zmod = lastvichg.mod; + vichgflag = 0; + } else if (!vichgflag) { + curvichg.mod = zmod; + if (curvichg.buf) + free(curvichg.buf); + curvichg.buf = (char *)zalloc(curvichg.bufsz = 16 + keybuflen); if (im == -2) { - vichgbuf[0] = + vichgflag = 1; + curvichg.buf[0] = zlell ? (insmode ? (zlecs < zlell ? 'i' : 'a') : 'R') : 'o'; + curvichg.buf[1] = '\0'; + curvichg.bufptr = 1; } else { - vichgbuf[0] = lastchar; + vichgflag = 2; + strcpy(curvichg.buf, keybuf); + unmetafy(curvichg.buf, &curvichg.bufptr); } - vichgbufptr = 1; - vichgrepeat = 0; } } @@ -208,10 +218,13 @@ getvirange(int wf) */ if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs, 1)) ret = -1; - if(vichgrepeat) + if (viinrepeat) zmult = mult1; - else + else { zmult = mult1 * zmod.tmult; + if (vichgflag == 2) + curvichg.mod.mult = zmult; + } } while(prefixflag && !ret); wordflag = 0; selectlocalmap(NULL); @@ -255,7 +268,7 @@ getvirange(int wf) } /* visual selection mode needs to include additional position */ - if (visual == 1 && invicmdmode()) + if (visual == 1 && pos < zlell && invicmdmode()) INCPOS(pos); /* Was it a line-oriented move? If so, the command will have set * @@ -383,7 +396,6 @@ videlete(UNUSED(char **args)) vifirstnonblank(zlenoargs); } } - vichgflag = 0; return ret; } @@ -391,9 +403,10 @@ videlete(UNUSED(char **args)) int videletechar(char **args) { - int n = zmult; + int n; startvichange(-1); + n = zmult; /* handle negative argument */ if (n < 0) { @@ -440,9 +453,10 @@ vichange(UNUSED(char **args)) int visubstitute(UNUSED(char **args)) { - int n = zmult; + int n; startvichange(1); + n = zmult; if (n < 0) return 1; /* it is an error to be on the end of line */ @@ -498,7 +512,6 @@ viyank(UNUSED(char **args)) cut(zlecs, c2 - zlecs, CUT_YANK); ret = 0; } - vichgflag = 0; /* cursor now at the start of the range yanked. For line mode * restore the column position */ if (vilinerange && lastcol != -1) { @@ -536,9 +549,10 @@ int viyankwholeline(UNUSED(char **args)) { int bol = findbol(), oldcs = zlecs; - int n = zmult; + int n; startvichange(-1); + n = zmult; if (n < 1) return 1; while(n--) { @@ -572,16 +586,17 @@ vireplace(UNUSED(char **args)) * a change, we always read the argument normally, even if the count * * was bad. When recording a change for repeating, and a bad count is * * given, we squash the repeat buffer to avoid repeating the partial * - * command; we've lost the previous change, but that can't be avoided * - * without a rewrite of the repeat code. */ + * command. */ /**/ int vireplacechars(UNUSED(char **args)) { ZLE_INT_T ch; - int n = zmult, fail = 0, newchars = 0; + int n, fail = 0, newchars = 0; + startvichange(1); + n = zmult; if (n > 0) { if (region_active) { int a, b; @@ -618,21 +633,15 @@ vireplacechars(UNUSED(char **args)) n = pos - zlecs; } } - startvichange(1); + /* check argument range */ if (n < 1 || fail) { - if(vichgrepeat) + if (viinrepeat) vigetkey(); - if(vichgflag) { - free(vichgbuf); - vichgbuf = NULL; - vichgflag = 0; - } return 1; } /* get key */ if((ch = vigetkey()) == ZLEEOF) { - vichgflag = 0; return 1; } /* do change */ @@ -659,7 +668,6 @@ vireplacechars(UNUSED(char **args)) zleline[zlecs++] = ch; zlecs--; } - vichgflag = 0; return 0; } @@ -670,7 +678,16 @@ vicmdmode(UNUSED(char **args)) if (invicmdmode() || selectkeymap("vicmd", 0)) return 1; mergeundo(); - vichgflag = 0; + insmode = unset(OVERSTRIKE); + if (vichgflag == 1) { + vichgflag = 0; + if (lastvichg.buf) + free(lastvichg.buf); + lastvichg = curvichg; + curvichg.buf = NULL; + } + if (viinrepeat == 1) + viinrepeat = 0; if (zlecs != findbol()) DECCS(); return 0; @@ -725,7 +742,50 @@ vioperswapcase(UNUSED(char **args)) vifirstnonblank(); #endif } - vichgflag = 0; + return ret; +} + +/**/ +int +viupcase(UNUSED(char **args)) +{ + int oldcs, c2, ret = 1; + + /* get the range */ + startvichange(1); + if ((c2 = getvirange(0)) != -1) { + oldcs = zlecs; + /* covert the case of all letters within range */ + while (zlecs < c2) { + zleline[zlecs] = ZC_toupper(zleline[zlecs]); + INCCS(); + } + /* go back to the first line of the range */ + zlecs = oldcs; + ret = 0; + } + return ret; +} + +/**/ +int +vidowncase(UNUSED(char **args)) +{ + int oldcs, c2, ret = 1; + + /* get the range */ + startvichange(1); + if ((c2 = getvirange(0)) != -1) { + oldcs = zlecs; + /* convert the case of all letters within range */ + while (zlecs < c2) { + zleline[zlecs] = ZC_tolower(zleline[zlecs]); + INCCS(); + } + /* go back to the first line of the range */ + zlecs = oldcs; + ret = 0; + } return ret; } @@ -734,21 +794,23 @@ int virepeatchange(UNUSED(char **args)) { /* make sure we have a change to repeat */ - if (!vichgbuf || vichgflag) + if (!lastvichg.buf || vichgflag || virangeflag) return 1; /* restore or update the saved count and buffer */ if (zmod.flags & MOD_MULT) { - lastmod.mult = zmod.mult; - lastmod.flags |= MOD_MULT; + lastvichg.mod.mult = zmod.mult; + lastvichg.mod.flags |= MOD_MULT; } if (zmod.flags & MOD_VIBUF) { - lastmod.vibuf = zmod.vibuf; - lastmod.flags = (lastmod.flags & ~MOD_VIAPP) | + lastvichg.mod.vibuf = zmod.vibuf; + lastvichg.mod.flags = (lastvichg.mod.flags & ~MOD_VIAPP) | MOD_VIBUF | (zmod.flags & MOD_VIAPP); - } + } else if (lastvichg.mod.flags & MOD_VIBUF && + lastvichg.mod.vibuf >= 27 && lastvichg.mod.vibuf <= 34) + lastvichg.mod.vibuf++; /* for "1 to "8 advance to next buffer */ /* repeat the command */ - inrepeat = 1; - ungetbytes(vichgbuf, vichgbufptr); + viinrepeat = 3; + ungetbytes(lastvichg.buf, lastvichg.bufptr); return 0; } @@ -764,10 +826,8 @@ viindent(UNUSED(char **args)) region_active = 2; /* get the range */ if ((c2 = getvirange(0)) == -1) { - vichgflag = 0; return 1; } - vichgflag = 0; /* must be a line range */ if (!vilinerange) { zlecs = oldcs; @@ -802,10 +862,8 @@ viunindent(UNUSED(char **args)) region_active = 2; /* get the range */ if ((c2 = getvirange(0)) == -1) { - vichgflag = 0; return 1; } - vichgflag = 0; /* must be a line range */ if (!vilinerange) { zlecs = oldcs; @@ -828,12 +886,13 @@ viunindent(UNUSED(char **args)) int vibackwarddeletechar(char **args) { - int n = zmult; + int n; if (invicmdmode()) startvichange(-1); /* handle negative argument */ + n = zmult; if (n < 0) { int ret; zmult = -n; @@ -873,10 +932,11 @@ int vijoin(UNUSED(char **args)) { int x, pos; - int n = zmult; + int n; int visual = region_active; startvichange(-1); + n = zmult; if (n < 1) return 1; if (visual && zlecs > mark) { @@ -915,9 +975,10 @@ vijoin(UNUSED(char **args)) int viswapcase(UNUSED(char **args)) { - int eol, n = zmult; + int eol, n; startvichange(-1); + n = zmult; if (n < 1) return 1; eol = findeol(); diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c index 2e6d75e86..e4a878eab 100644 --- a/Src/Zle/zle_word.c +++ b/Src/Zle/zle_word.c @@ -153,7 +153,7 @@ emacsforwardword(char **args) /**/ int -viforwardblankwordend(UNUSED(char **args)) +viforwardblankwordend(char **args) { int n = zmult; @@ -678,46 +678,54 @@ killword(char **args) int transposewords(UNUSED(char **args)) { - int p1, p2, p3, p4, len, x = zlecs, pos; + int p1, p2, p3, p4, pt, len, x = zlecs, pos; ZLE_STRING_T temp, pp; int n = zmult; int neg = n < 0, ocs = zlecs; if (neg) n = -n; - while (n--) { - while (x != zlell && zleline[x] != ZWC('\n') && !ZC_iword(zleline[x])) - INCPOS(x); - if (x == zlell || zleline[x] == ZWC('\n')) { - x = zlecs; - while (x) { - if (ZC_iword(zleline[x])) - break; - pos = x; - DECPOS(pos); - if (zleline[pos] == ZWC('\n')) - break; - x = pos; - } - if (!x) - return 1; + + while (x != zlell && zleline[x] != ZWC('\n') && !ZC_iword(zleline[x])) + INCPOS(x); + + if (x == zlell || zleline[x] == ZWC('\n')) { + x = zlecs; + while (x) { + if (ZC_iword(zleline[x])) + break; pos = x; DECPOS(pos); if (zleline[pos] == ZWC('\n')) - return 1; - } - for (p4 = x; p4 != zlell && ZC_iword(zleline[p4]); INCPOS(p4)) - ; - for (p3 = p4; p3; ) { - pos = p3; - DECPOS(pos); - if (!ZC_iword(zleline[pos])) break; - p3 = pos; + x = pos; } - if (!p3) + if (!x) return 1; - for (p2 = p3; p2; ) { + pos = x; + DECPOS(pos); + if (zleline[pos] == ZWC('\n')) + return 1; + } + + for (p4 = x; p4 != zlell && ZC_iword(zleline[p4]); INCPOS(p4)) + ; + + for (p3 = p4; p3; ) { + pos = p3; + DECPOS(pos); + if (!ZC_iword(zleline[pos])) + break; + p3 = pos; + } + + if (!p3) + return 1; + + p1 = p2 = pt = p3; + + while (n--) { + for (p2 = pt; p2; ) { pos = p2; DECPOS(pos); if (ZC_iword(zleline[pos])) @@ -733,20 +741,24 @@ transposewords(UNUSED(char **args)) break; p1 = pos; } - pp = temp = (ZLE_STRING_T)zhalloc((p4 - p1)*ZLE_CHAR_SIZE); - len = p4 - p3; - ZS_memcpy(pp, zleline + p3, len); - pp += len; - len = p3 - p2; - ZS_memcpy(pp, zleline + p2, len); - pp += len; - ZS_memcpy(pp, zleline + p1, p2 - p1); + pt = p1; + } - ZS_memcpy(zleline + p1, temp, p4 - p1); + pp = temp = (ZLE_STRING_T)zhalloc((p4 - p1)*ZLE_CHAR_SIZE); + len = p4 - p3; + ZS_memcpy(pp, zleline + p3, len); + pp += len; + len = p3 - p2; + ZS_memcpy(pp, zleline + p2, len); + pp += len; + ZS_memcpy(pp, zleline + p1, p2 - p1); + + ZS_memcpy(zleline + p1, temp, p4 - p1); - zlecs = p4; - } if (neg) zlecs = ocs; + else + zlecs = p4; + return 0; } diff --git a/Src/builtin.c b/Src/builtin.c index dd20f9eab..e641a97a1 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -249,7 +249,7 @@ int execbuiltin(LinkList args, LinkList assigns, Builtin bn) { char *pp, *name, *optstr; - int flags, sense, argc, execop, xtr = isset(XTRACE); + int flags, argc, execop, xtr = isset(XTRACE); struct options ops; /* initialise options structure */ @@ -294,6 +294,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn) /* Sort out the options. */ if (optstr) { char *arg = *argv; + int sense; /* 1 for -x, 0 for +x */ /* while arguments look like options ... */ while (arg && /* Must begin with - or maybe + */ @@ -387,7 +388,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn) if (*arg) { if(*arg == Meta) *++arg ^= 32; - zwarnnam(name, "bad option: -%c", *arg); + zwarnnam(name, "bad option: %c%c", "+-"[sense], *arg); return 1; } arg = *++argv; @@ -973,7 +974,7 @@ cd_do_chdir(char *cnam, char *dest, int hard) * Normalize path under Cygwin to avoid messing with * DOS style names with drives in them */ - static char buf[PATH_MAX]; + static char buf[PATH_MAX+1]; #ifdef HAVE_CYGWIN_CONV_PATH cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, dest, buf, PATH_MAX); @@ -1273,7 +1274,23 @@ fixdir(char *src) #ifdef __CYGWIN__ char *s0 = src; #endif - int ret = 0; + /* This function is always called with n path containing at + * least one slash, either because one was input by the user or + * because the caller has prepended either pwd or a cdpath dir. + * If asked to make a relative change and pwd is set to ".", + * the current directory has been removed out from under us, + * so force links to be chased. + * + * Ordinarily we can't get here with "../" as the first component + * but handle the silly special case of ".." in cdpath. + * + * Order of comparisons here looks funny, but it short-circuits + * most rapidly in the event of a false condition. Set to 2 + * here so we still obey the (lack of) CHASEDOTS option after + * the first "../" is preserved (test chasedots > 1 below). + */ + int chasedots = (src[0] == '.' && pwd[0] == '.' && pwd[1] == '\0' && + (src[1] == '/' || (src[1] == '.' && src[2] == '/'))) * 2; /*** if have RFS superroot directory ***/ #ifdef HAVE_SUPERROOT @@ -1305,12 +1322,12 @@ fixdir(char *src) while (dest > d0 + 1 && dest[-1] == '/') dest--; *dest = '\0'; - return ret; + return chasedots; } if (src[0] == '.' && src[1] == '.' && (src[2] == '\0' || src[2] == '/')) { - if (isset(CHASEDOTS)) { - ret = 1; + if (isset(CHASEDOTS) || chasedots > 1) { + chasedots = 1; /* and treat as normal path segment */ } else { if (dest > d0 + 1) { @@ -1348,6 +1365,7 @@ fixdir(char *src) dest[-1] = *src++ ^ 32; } } + /* unreached */ } /**/ @@ -1472,6 +1490,7 @@ bin_fc(char *nam, char **argv, Options ops, int func) } if (zleactive) { + unqueue_signals(); zwarnnam(nam, "no interactive history within ZLE"); return 1; } @@ -1610,7 +1629,7 @@ bin_fc(char *nam, char **argv, Options ops, int func) unqueue_signals(); if (fcedit(editor, fil)) { if (stuff(fil)) - zwarnnam("fc", "%e: %s", errno, s); + zwarnnam("fc", "%e: %s", errno, fil); else { loop(0,1); retval = lastval; @@ -1990,11 +2009,12 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * handled in createparam(). Here we just avoid using it for the * present tests if it's unset. * - * POSIXBUILTINS horror: we need to retain the 'readonly' flag - * of an unset parameter. + * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export' + * flags of an unset parameter. */ usepm = pm && (!(pm->node.flags & PM_UNSET) || - (isset(POSIXBUILTINS) && (pm->node.flags & PM_READONLY))); + (isset(POSIXBUILTINS) && + (pm->node.flags & (PM_READONLY|PM_EXPORTED)))); /* * We need to compare types with an existing pm if special, @@ -2117,7 +2137,8 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), /* * Stricter rules about retaining readonly attribute in this case. */ - if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) && + if ((on & (PM_READONLY|PM_EXPORTED)) && + (!usepm || (pm->node.flags & PM_UNSET)) && !ASG_VALUEP(asg)) on |= PM_UNSET; else if (usepm && (pm->node.flags & PM_READONLY) && @@ -2125,6 +2146,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), zerr("read-only variable: %s", pm->node.nam); return NULL; } + /* This is handled by createparam(): + if (usepm && (pm->node.flags & PM_EXPORTED) && !(off & PM_EXPORTED)) + on |= PM_EXPORTED; + */ } /* @@ -2266,6 +2291,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), zerrnam(cname, "%s: restricted", pname); return pm; } + if (pm->node.flags & PM_SINGLE) { + zerrnam(cname, "%s: can only have a single instance", pname); + return pm; + } /* * For specials, we keep the same struct but zero everything. * Maybe it would be easier to create a new struct but copy @@ -2787,6 +2816,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) return 0; } if (off & PM_TIED) { + unqueue_signals(); zerrnam(name, "use unset to remove tied variables"); return 1; } @@ -3117,6 +3147,7 @@ bin_functions(char *name, char **argv, Options ops, int func) queue_signals(); for (q = mathfuncs; q; q = q->next) { if (!strcmp(q->name, funcname)) { + unqueue_signals(); zwarnnam(name, "-M %s: function already exists", funcname); zsfree(p->name); @@ -3142,15 +3173,33 @@ bin_functions(char *name, char **argv, Options ops, int func) queue_signals(); if (OPT_MINUS(ops,'X')) { - if ((shf = (Shfunc) shfunctab->getnode(shfunctab, scriptname))) { - DPUTS(!shf->funcdef, - "BUG: Calling autoload from empty function"); + Funcstack fs; + char *funcname = NULL; + for (fs = funcstack; fs; fs = fs->prev) { + if (fs->tp == FS_FUNC) { + /* + * dupstring here is paranoia but unlikely to be + * problematic + */ + funcname = dupstring(fs->name); + break; + } + } + if (!funcname) + { + zerrnam(name, "bad autoload"); + ret = 1; } else { - shf = (Shfunc) zshcalloc(sizeof *shf); - shfunctab->addnode(shfunctab, ztrdup(scriptname), shf); + if ((shf = (Shfunc) shfunctab->getnode(shfunctab, funcname))) { + DPUTS(!shf->funcdef, + "BUG: Calling autoload from empty function"); + } else { + shf = (Shfunc) zshcalloc(sizeof *shf); + shfunctab->addnode(shfunctab, ztrdup(funcname), shf); + } + shf->node.flags = on; + ret = eval_autoload(shf, funcname, ops, func); } - shf->node.flags = on; - ret = eval_autoload(shf, scriptname, ops, func); } else { if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u')) on &= ~PM_UNDEFINED; @@ -3332,15 +3381,24 @@ bin_unset(char *name, char **argv, Options ops, int func) /* do not glob -- unset the given parameter */ queue_signals(); while ((s = *argv++)) { - char *ss = strchr(s, '['); - char *sse = ss; + char *ss = strchr(s, '['), *subscript = 0; if (ss) { - if (skipparens('[', ']', &sse) || *sse) { - zerrnam(name, "%s: invalid parameter name", s); - returnval = 1; - continue; - } + char *sse; *ss = 0; + if ((sse = parse_subscript(ss+1, 1, ']'))) { + *sse = 0; + subscript = dupstring(ss+1); + *sse = ']'; + remnulargs(subscript); + untokenize(subscript); + } + } + if ((ss && !subscript) || !isident(s)) { + if (ss) + *ss = '['; + zerrnam(name, "%s: invalid parameter name", s); + returnval = 1; + continue; } pm = (Param) (paramtab == realparamtab ? /* getnode2() to avoid autoloading */ @@ -3358,11 +3416,8 @@ bin_unset(char *name, char **argv, Options ops, int func) } else if (ss) { if (PM_TYPE(pm->node.flags) == PM_HASHED) { HashTable tht = paramtab; - if ((paramtab = pm->gsu.h->getfn(pm))) { - *--sse = 0; - unsetparam(ss+1); - *sse = ']'; - } + if ((paramtab = pm->gsu.h->getfn(pm))) + unsetparam(subscript); paramtab = tht; } else if (PM_TYPE(pm->node.flags) == PM_SCALAR || PM_TYPE(pm->node.flags) == PM_ARRAY) { @@ -3382,7 +3437,7 @@ bin_unset(char *name, char **argv, Options ops, int func) } else { /* start is after the element for reverse index */ int start = vbuf.start - !!(vbuf.flags & VALFLAG_INV); - if (start < arrlen(vbuf.pm->u.arr)) { + if (arrlen_gt(vbuf.pm->u.arr, start)) { char *arr[2]; arr[0] = ""; arr[1] = 0; @@ -3609,12 +3664,23 @@ bin_whence(char *nam, char **argv, Options ops, int func) } } if (!informed && (wd || v || csh)) { + /* this is information and not an error so, as in csh, use stdout */ zputs(*argv, stdout); puts(wd ? ": none" : " not found"); returnval = 1; } popheap(); - } else if ((cnam = findcmd(*argv, 1))) { + } else if (func == BIN_COMMAND && OPT_ISSET(ops,'p') && + (hn = builtintab->getnode(builtintab, *argv))) { + /* + * Special case for "command -p[vV]" which needs to + * show a builtin in preference to an external command. + */ + builtintab->printnode(hn, printflags); + informed = 1; + } else if ((cnam = findcmd(*argv, 1, + func == BIN_COMMAND && + OPT_ISSET(ops,'p')))) { /* Found external command. */ if (wd) { printf("%s: command\n", *argv); @@ -3628,7 +3694,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) } informed = 1; } else { - /* Not found at all. */ + /* Not found at all. That's not an error as such so this goes to stdout */ if (v || csh || wd) zputs(*argv, stdout), puts(wd ? ": none" : " not found"); returnval = 1; @@ -3724,6 +3790,7 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func)) if (!(asg = getasg(&argv, NULL))) { zwarnnam(name, "bad assignment"); returnval = 1; + break; } else if (ASG_VALUEP(asg)) { if(isset(RESTRICTED)) { zwarnnam(name, "restricted: %s", asg->value.scalar); @@ -4021,10 +4088,11 @@ bin_print(char *name, char **args, Options ops, int func) { int flen, width, prec, type, argc, n, narg, curlen = 0; int nnl = 0, fmttrunc = 0, ret = 0, maxarg = 0, nc = 0; - int flags[6], *len; + int flags[6], *len, visarr = 0; char *start, *endptr, *c, *d, *flag, *buf = NULL, spec[14], *fmt = NULL; char **first, **argp, *curarg, *flagch = "'0+- #", save = '\0', nullstr = '\0'; - size_t rcount, count = 0; + size_t rcount = 0, count = 0; + size_t *cursplit, *splits = 0; FILE *fout = stdout; #ifdef HAVE_OPEN_MEMSTREAM size_t mcount; @@ -4056,7 +4124,7 @@ bin_print(char *name, char **args, Options ops, int func) return 1; \ } \ unlink(tmpf); \ - if ((fout = fdopen(tempfd, "w+")) == NULL) { \ + if ((FOUT = fdopen(tempfd, "w+")) == NULL) { \ close(tempfd); \ zwarnnam(name, "can't open temp file: %e", errno); \ return 1; \ @@ -4485,6 +4553,7 @@ bin_print(char *name, char **args, Options ops, int func) short *words; if (nwords > 1) { zwarnnam(name, "option -S takes a single argument"); + unqueue_signals(); return 1; } words = NULL; @@ -4554,7 +4623,8 @@ bin_print(char *name, char **args, Options ops, int func) OPT_ISSET(ops,'N') ? '\0' : ' ', fout); } } - if (!(OPT_ISSET(ops,'n') || OPT_ISSET(ops, 'v') || nnl)) + if (!(OPT_ISSET(ops,'n') || nnl || + (OPT_ISSET(ops, 'v') && !OPT_ISSET(ops, 'l')))) fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout); if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1) ret = 1; @@ -4581,11 +4651,23 @@ bin_print(char *name, char **args, Options ops, int func) * special cases of printing to a ZLE buffer or the history, however. */ + if (OPT_ISSET(ops,'v')) { + struct value vbuf; + char* s = OPT_ARG(ops,'v'); + Value v = getvalue(&vbuf, &s, 0); + visarr = v && PM_TYPE(v->pm->node.flags) == PM_ARRAY; + } /* printf style output */ *spec = '%'; argp = args; do { rcount = count; + if (argp > args && visarr) { /* reusing format string */ + if (!splits) + cursplit = splits = (size_t *)zhalloc(sizeof(size_t) * + (arrlen(args) / (argp - args) + 1)); + *cursplit++ = count; + } if (maxarg) { first += maxarg; argc -= maxarg; @@ -4712,7 +4794,8 @@ bin_print(char *name, char **args, Options ops, int func) } else if (idigit(*c)) { prec = strtoul(c, &endptr, 0); c = endptr; - } + } else + prec = 0; if (prec >= 0) *d++ = '.', *d++ = '*'; } @@ -4813,9 +4896,10 @@ bin_print(char *name, char **args, Options ops, int func) break; case 'q': stringval = curarg ? - quotestring(curarg, NULL, QT_BACKSLASH_SHOWNULL) : &nullstr; + quotestring(metafy(curarg, curlen, META_USEHEAP), + QT_BACKSLASH_SHOWNULL) : &nullstr; *d = 's'; - print_val(stringval); + print_val(unmetafy(stringval, &curlen)); break; case 'd': case 'i': @@ -4951,18 +5035,30 @@ bin_print(char *name, char **args, Options ops, int func) if (buf) free(buf); } else { - stringval = metafy(buf, rcount, META_REALLOC); - if (OPT_ISSET(ops,'z')) { - zpushnode(bufstack, stringval); - } else if (OPT_ISSET(ops,'v')) { - setsparam(OPT_ARG(ops, 'v'), stringval); + if (visarr && splits) { + char **arrayval = zshcalloc((cursplit - splits + 2) * sizeof(char *)); + for (;cursplit >= splits; cursplit--) { + int start = cursplit == splits ? 0 : cursplit[-1]; + arrayval[cursplit - splits] = + metafy(buf + start, count - start, META_DUP); + count = start; + } + setaparam(OPT_ARG(ops, 'v'), arrayval); + free(buf); } else { - ent = prepnexthistent(); - ent->node.nam = stringval; - ent->stim = ent->ftim = time(NULL); - ent->node.flags = 0; - ent->words = (short *)NULL; - addhistnode(histtab, ent->node.nam, ent); + stringval = metafy(buf, rcount, META_REALLOC); + if (OPT_ISSET(ops,'z')) { + zpushnode(bufstack, stringval); + } else if (OPT_ISSET(ops,'v')) { + setsparam(OPT_ARG(ops, 'v'), stringval); + } else { + ent = prepnexthistent(); + ent->node.nam = stringval; + ent->stim = ent->ftim = time(NULL); + ent->node.flags = 0; + ent->words = (short *)NULL; + addhistnode(histtab, ent->node.nam, ent); + } } } unqueue_signals(); @@ -4999,7 +5095,7 @@ bin_shift(char *name, char **argv, Options ops, UNUSED(int func)) if (*argv) { for (; *argv; argv++) if ((s = getaparam(*argv))) { - if (num > arrlen(s)) { + if (arrlen_lt(s, num)) { zwarnnam(name, "shift count must be <= $#"); ret++; continue; @@ -5068,7 +5164,7 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun zoptind = 1; optcind = 0; } - if(zoptind > arrlen(args)) + if (arrlen_lt(args, zoptind)) /* no more options */ return 1; @@ -5340,6 +5436,11 @@ zexit(int val, int from_where) } } lastval = val; + /* + * Now we are committed to exiting any previous state + * is irrelevant. Ensure trap can run. + */ + errflag = intrap = 0; if (sigtrapped[SIGEXIT]) dotrap(SIGEXIT); callhookfunc("zshexit", NULL, 1, NULL); @@ -6531,7 +6632,7 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) for (s = argv; *s; s++); if (s == argv || strcmp(s[-1], "]")) { zwarnnam(name, "']' expected"); - return 1; + return 2; } s[-1] = NULL; } @@ -6574,19 +6675,19 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) if (errflag) { errflag &= ~ERRFLAG_ERROR; zcontext_restore(); - return 1; + return 2; } if (!prog || tok == LEXERR) { zwarnnam(name, tokstr ? "parse error" : "argument expected"); zcontext_restore(); - return 1; + return 2; } zcontext_restore(); if (*curtestarg) { zwarnnam(name, "too many arguments"); - return 1; + return 2; } /* syntax is OK, so evaluate */ diff --git a/Src/compat.c b/Src/compat.c index 9041c0bed..a2956946f 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -33,7 +33,10 @@ /* Return pointer to first occurence of string t * * in string s. Return NULL if not present. */ +/**/ #ifndef HAVE_STRSTR + +/**/ char * strstr(const char *s, const char *t) { @@ -48,10 +51,15 @@ strstr(const char *s, const char *t) } return NULL; } + +/**/ #endif +/**/ #ifndef HAVE_GETHOSTNAME + +/**/ int gethostname(char *name, size_t namelen) { @@ -65,10 +73,15 @@ gethostname(char *name, size_t namelen) strcpy(name, uts.nodename); return 0; } + +/**/ #endif +/**/ #ifndef HAVE_GETTIMEOFDAY + +/**/ int gettimeofday(struct timeval *tv, struct timezone *tz) { @@ -76,20 +89,28 @@ gettimeofday(struct timeval *tv, struct timezone *tz) tv->tv_sec = (long)time((time_t) 0); return 0; } + +/**/ #endif /* compute the difference between two calendar times */ +/**/ #ifndef HAVE_DIFFTIME + +/**/ double difftime(time_t t2, time_t t1) { return ((double)t2 - (double)t1); } + +/**/ #endif +/**/ #ifndef HAVE_STRERROR extern char *sys_errlist[]; @@ -97,11 +118,14 @@ extern char *sys_errlist[]; * error number, and returns a pointer to that string. * * This is not a particularly robust version of strerror. */ +/**/ char * strerror(int errnum) { return (sys_errlist[errnum]); } + +/**/ #endif @@ -186,6 +210,7 @@ zpathmax(char *dir) } #endif /* 0 */ +/**/ #ifdef HAVE_SYSCONF /* * This is replaced by a macro from system.h if not HAVE_SYSCONF. @@ -230,6 +255,8 @@ zopenmax(void) return (max_zsh_fd > openmax) ? max_zsh_fd : openmax; } + +/**/ #endif /* @@ -270,7 +297,7 @@ zgetdir(struct dirsav *d) int len; #endif - buf = zhalloc(bufsiz = PATH_MAX); + buf = zhalloc(bufsiz = PATH_MAX+1); pos = bufsiz - 1; buf[pos] = '\0'; strcpy(nbuf, "../"); @@ -439,11 +466,11 @@ zgetcwd(void) free(cwd); } #else - char *cwdbuf = zalloc(PATH_MAX); + char *cwdbuf = zalloc(PATH_MAX+1); ret = getcwd(cwdbuf, PATH_MAX); if (ret) ret = dupstring(ret); - zfree(cwdbuf, PATH_MAX); + zfree(cwdbuf, PATH_MAX+1); #endif /* GETCWD_CALLS_MALLOC */ } #endif /* HAVE_GETCWD */ @@ -532,6 +559,7 @@ output64(zlong val) /**/ #endif /* ZSH_64_BIT_TYPE */ +/**/ #ifndef HAVE_STRTOUL /* @@ -569,6 +597,8 @@ output64(zlong val) * Ignores `locale' stuff. Assumes that the upper and lower case * alphabets and digits are each contiguous. */ + +/**/ unsigned long strtoul(nptr, endptr, base) const char *nptr; @@ -632,10 +662,26 @@ strtoul(nptr, endptr, base) *endptr = any ? s - 1 : nptr; return (acc); } + +/**/ #endif /* HAVE_STRTOUL */ /**/ -#if defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__)) +#ifdef ENABLE_UNICODE9 +#include "./wcwidth9.h" + +/**/ +int +mk_wcwidth(wchar_t ucs) +{ + int w = wcwidth9(ucs); + if (w < -1) + return 1; + return w; +} + +/**/ +#elif defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__)) /* * This is an implementation of wcwidth() and wcswidth() (defined in diff --git a/Src/cond.c b/Src/cond.c index c5ab65eea..42e9de30f 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -30,10 +30,11 @@ #include "zsh.mdh" #include "cond.pro" -int tracingcond; +/**/ +int tracingcond; /* updated by execcond() in exec.c */ static char *condstr[COND_MOD] = { - "!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", + "!", "&&", "||", "=", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", "-ne", "-lt", "-gt", "-le", "-ge", "=~" }; @@ -195,7 +196,8 @@ evalcond(Estate state, char *fromtest) cond_subst(&left, !fromtest); untokenize(left); } - if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) { + if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRDEQ && + ctype != COND_STRNEQ) { right = ecgetstr(state, EC_DUPTOK, &htok); if (htok) { cond_subst(&right, !fromtest); @@ -207,7 +209,8 @@ evalcond(Estate state, char *fromtest) fputc(' ',xtrerr); quotedzputs(left, xtrerr); fprintf(xtrerr, " %s ", condstr[ctype]); - if (ctype == COND_STREQ || ctype == COND_STRNEQ) { + if (ctype == COND_STREQ || ctype == COND_STRDEQ || + ctype == COND_STRNEQ) { char *rt = dupstring(ecrawstr(state->prog, state->pc, NULL)); cond_subst(&rt, !fromtest); quote_tokenized_output(rt, xtrerr); @@ -286,6 +289,7 @@ evalcond(Estate state, char *fromtest) switch (ctype) { case COND_STREQ: + case COND_STRDEQ: case COND_STRNEQ: { int test, npat = state->pc[1]; @@ -312,7 +316,7 @@ evalcond(Estate state, char *fromtest) state->pc += 2; test = (pprog && pattry(pprog, left)); - return !(ctype == COND_STREQ ? test : !test); + return !(ctype == COND_STRNEQ ? !test : test); } case COND_STRLT: return !(strcmp(left, right) < 0); @@ -347,6 +351,8 @@ evalcond(Estate state, char *fromtest) return (!S_ISSOCK(dostat(left))); case 'u': return (!(dostat(left) & S_ISUID)); + case 'v': + return (!issetvar(left)); case 'w': return (!doaccess(left, W_OK)); case 'x': diff --git a/Src/exec.c b/Src/exec.c index 352615c83..f544a33e7 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -207,7 +207,7 @@ static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = { /* structure for command builtin for when it is used with -v or -V */ static struct builtin commandbn = - BUILTIN(0, 0, bin_whence, 0, -1, BIN_COMMAND, "vV", NULL); + BUILTIN("command", 0, bin_whence, 0, -1, BIN_COMMAND, "pvV", NULL); /* parse string into a list */ @@ -437,7 +437,7 @@ static int zexecve(char *pth, char **argv, char **newenvp) { int eno; - static char buf[PATH_MAX * 2]; + static char buf[PATH_MAX * 2+1]; char **eep; unmetafy(pth, NULL); @@ -568,11 +568,49 @@ commandnotfound(char *arg0, LinkList args) Shfunc shf = (Shfunc) shfunctab->getnode(shfunctab, "command_not_found_handler"); - if (!shf) - return 127; + if (!shf) { + lastval = 127; + return 1; + } pushnode(args, arg0); - return doshfunc(shf, args, 1); + lastval = doshfunc(shf, args, 1); + return 0; +} + +/* + * Search the default path for cmd. + * pbuf of length plen is the buffer to use. + * Return NULL if not found. + */ + +static char * +search_defpath(char *cmd, char *pbuf, int plen) +{ + char *ps = DEFAULT_PATH, *pe = NULL, *s; + + for (ps = DEFAULT_PATH; ps; ps = pe ? pe+1 : NULL) { + pe = strchr(ps, ':'); + if (*ps == '/') { + s = pbuf; + if (pe) { + if (pe - ps >= plen) + continue; + struncpy(&s, ps, pe-ps); + } else { + if (strlen(ps) >= plen) + continue; + strucpy(&s, ps); + } + *s++ = '/'; + if ((s - pbuf) + strlen(cmd) >= plen) + continue; + strucpy(&s, cmd); + if (iscom(pbuf)) + return pbuf; + } + } + return NULL; } /* execute an external command */ @@ -582,7 +620,7 @@ static void execute(LinkList args, int flags, int defpath) { Cmdnam cn; - char buf[MAXCMDLEN], buf2[MAXCMDLEN]; + char buf[MAXCMDLEN+1], buf2[MAXCMDLEN+1]; char *s, *z, *arg0; char **argv, **pp, **newenvp = NULL; int eno = 0, ee; @@ -663,29 +701,12 @@ execute(LinkList args, int flags, int defpath) /* for command -p, search the default path */ if (defpath) { - char *s, pbuf[PATH_MAX]; - char *dptr, *pe, *ps = DEFAULT_PATH; - - for(;ps;ps = pe ? pe+1 : NULL) { - pe = strchr(ps, ':'); - if (*ps == '/') { - s = pbuf; - if (pe) - struncpy(&s, ps, pe-ps); - else - strucpy(&s, ps); - *s++ = '/'; - if ((s - pbuf) + strlen(arg0) >= PATH_MAX) - continue; - strucpy(&s, arg0); - if (iscom(pbuf)) - break; - } - } + char pbuf[PATH_MAX+1]; + char *dptr; - if (!ps) { + if (!search_defpath(arg0, pbuf, PATH_MAX)) { if (commandnotfound(arg0, args) == 0) - _exit(0); + _exit(lastval); zerr("command not found: %s", arg0); _exit(127); } @@ -700,7 +721,7 @@ execute(LinkList args, int flags, int defpath) } else { if ((cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0))) { - char nn[PATH_MAX], *dptr; + char nn[PATH_MAX+1], *dptr; if (cn->node.flags & HASHED) strcpy(nn, cn->u.cmd); @@ -749,7 +770,7 @@ execute(LinkList args, int flags, int defpath) if (eno) zerr("%e: %s", eno, arg0); else if (commandnotfound(arg0, args) == 0) - _exit(0); + _exit(lastval); else zerr("command not found: %s", arg0); _exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127); @@ -760,32 +781,40 @@ execute(LinkList args, int flags, int defpath) /* * Get the full pathname of an external command. * If the second argument is zero, return the first argument if found; - * if non-zero, return the path using heap memory. (RET_IF_COM(X), above). + * if non-zero, return the path using heap memory. (RET_IF_COM(X), + * above). + * If the third argument is non-zero, use the system default path + * instead of the current path. */ /**/ mod_export char * -findcmd(char *arg0, int docopy) +findcmd(char *arg0, int docopy, int default_path) { char **pp; char *z, *s, buf[MAXCMDLEN]; Cmdnam cn; + if (default_path) + { + if (search_defpath(arg0, buf, MAXCMDLEN)) + return docopy ? dupstring(buf) : arg0; + return NULL; + } cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0); - if (!cn && isset(HASHCMDS)) + if (!cn && isset(HASHCMDS) && !isrelative(arg0)) cn = hashcmd(arg0, path); if ((int) strlen(arg0) > PATH_MAX) return NULL; - for (s = arg0; *s; s++) - if (*s == '/') { - RET_IF_COM(arg0); - if (arg0 == s || unset(PATHDIRS)) { - return NULL; - } - break; + if ((s = strchr(arg0, '/'))) { + RET_IF_COM(arg0); + if (arg0 == s || unset(PATHDIRS) || !strncmp(arg0, "./", 2) || + !strncmp(arg0, "../", 3)) { + return NULL; } + } if (cn) { - char nn[PATH_MAX]; + char nn[PATH_MAX+1]; if (cn->node.flags & HASHED) strcpy(nn, cn->u.cmd); @@ -818,6 +847,11 @@ findcmd(char *arg0, int docopy) return NULL; } +/* + * Return TRUE if the given path denotes an executable regular file, or a + * symlink to one. + */ + /**/ int iscom(char *s) @@ -847,6 +881,11 @@ isreallycom(Cmdnam cn) return iscom(fullnam); } +/* + * Return TRUE if the given path contains a dot or dot-dot component + * and does not start with a slash. + */ + /**/ int isrelative(char *s) @@ -866,7 +905,7 @@ mod_export Cmdnam hashcmd(char *arg0, char **pp) { Cmdnam cn; - char *s, buf[PATH_MAX]; + char *s, buf[PATH_MAX+1]; char **pq; for (; *pp; pp++) @@ -994,9 +1033,18 @@ entersubsh(int flags) if ((flags & ESUB_REVERTPGRP) && getpid() == mypgrp) release_pgrp(); shout = NULL; - if (!job_control_ok) { + if (flags & ESUB_NOMONITOR) { + /* + * Allowing any form of interactive signalling here is + * actively harmful as we are in a context where there is no + * control over the process. + */ + signal_ignore(SIGTTOU); + signal_ignore(SIGTTIN); + signal_ignore(SIGTSTP); + } else if (!job_control_ok) { /* - * If this process is not goign to be doing job control, + * If this process is not going to be doing job control, * we don't want to do special things with the corresponding * signals. If it is, we need to keep the special behaviour: * see note about attachtty() above. @@ -1190,6 +1238,7 @@ execlist(Estate state, int dont_change_job, int exiting) } while (wc_code(code) == WC_LIST && !breaks && !retflag && !errflag) { int donedebug; + int this_noerrexit = 0, this_donetrap = 0; ltype = WC_LIST_TYPE(code); csp = cmdsp; @@ -1273,9 +1322,17 @@ 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); next = state->pc + WC_SUBLIST_SKIP(code); if (!oldnoerrexit) - noerrexit = (WC_SUBLIST_TYPE(code) != WC_SUBLIST_END); + noerrexit = !isend; + 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 = 1; + } switch (WC_SUBLIST_TYPE(code)) { case WC_SUBLIST_END: /* End of sublist; just execute, ignoring status. */ @@ -1305,10 +1362,10 @@ execlist(Estate state, int dont_change_job, int exiting) /* We've skipped to the end of the list, not executing * * the final pipeline, so don't perform error handling * * for this sublist. */ - donetrap = 1; + this_donetrap = 1; goto sublist_done; } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) { - donetrap = 1; + this_donetrap = 1; /* * Treat this in the same way as if we reached * the end of the sublist normally. @@ -1338,10 +1395,10 @@ execlist(Estate state, int dont_change_job, int exiting) /* We've skipped to the end of the list, not executing * * the final pipeline, so don't perform error handling * * for this sublist. */ - donetrap = 1; + this_donetrap = 1; goto sublist_done; } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) { - donetrap = 1; + this_donetrap = 1; /* * Treat this in the same way as if we reached * the end of the sublist normally. @@ -1391,7 +1448,7 @@ sublist_done: /* Check whether we are suppressing traps/errexit * * (typically in init scripts) and if we haven't * * already performed them for this sublist. */ - if (!noerrexit && !donetrap) { + if (!noerrexit && !this_noerrexit && !donetrap && !this_donetrap) { if (sigtrapped[SIGZERR] && lastval) { dotrap(SIGZERR); donetrap = 1; @@ -1561,6 +1618,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) if (nowait) { if(!pline_level) { + int jobsub; struct process *pn, *qn; curjob = newjob; @@ -1573,6 +1631,20 @@ execpline(Estate state, wordcode slcode, int how, int last1) if (!jn->procs->next || lpforked == 2) { jn->gleader = list_pipe_pid; jn->stat |= STAT_SUBLEADER; + /* + * Pick up any subjob that's still lying around + * as it's now our responsibility. + * If we find it we're a SUPERJOB. + */ + for (jobsub = 1; jobsub <= maxjob; jobsub++) { + Job jnsub = jobtab + jobsub; + if (jnsub->stat & STAT_SUBJOB_ORPHANED) { + jn->other = jobsub; + jn->stat |= STAT_SUPERJOB; + jnsub->stat &= ~STAT_SUBJOB_ORPHANED; + jnsub->other = list_pipe_pid; + } + } } for (pn = jobtab[jn->other].procs; pn; pn = pn->next) if (WIFSTOPPED(pn->status)) @@ -1584,7 +1656,8 @@ execpline(Estate state, wordcode slcode, int how, int last1) } jn->stat &= ~(STAT_DONE | STAT_NOPRINT); - jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED; + jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED | + STAT_INUSE; printjob(jn, !!isset(LONGLISTJOBS), 1); } else if (newjob != list_pipe_job) @@ -1627,7 +1700,13 @@ execpline(Estate state, wordcode slcode, int how, int last1) int synch[2]; struct timeval bgtime; + /* + * A pipeline with the shell handling the right + * hand side was stopped. We'll fork to allow + * it to continue. + */ if (pipe(synch) < 0 || (pid = zfork(&bgtime)) == -1) { + /* Failure */ if (pid < 0) { close(synch[0]); close(synch[1]); @@ -1641,6 +1720,18 @@ execpline(Estate state, wordcode slcode, int how, int last1) thisjob = newjob; } else if (pid) { + /* + * Parent: job control is here. If the job + * started for the RHS of the pipeline is still + * around, then its a SUBJOB and the job for + * earlier parts of the pipeeline is its SUPERJOB. + * The newly forked shell isn't recorded as a + * separate job here, just as list_pipe_pid. + * If the superjob exits (it may already have + * done so, see child branch below), we'll use + * list_pipe_pid to form the basis of a + * replacement job --- see SUBLEADER code above. + */ char dummy; lpforked = @@ -1659,7 +1750,9 @@ execpline(Estate state, wordcode slcode, int how, int last1) jobtab[list_pipe_job].other = newjob; jobtab[list_pipe_job].stat |= STAT_SUPERJOB; jn->stat |= STAT_SUBJOB | STAT_NOPRINT; - jn->other = pid; + jn->other = list_pipe_pid; /* see zsh.h */ + if (hasprocs(list_pipe_job)) + jn->gleader = jobtab[list_pipe_job].gleader; } if ((list_pipe || last1) && hasprocs(list_pipe_job)) killpg(jobtab[list_pipe_job].gleader, SIGSTOP); @@ -1668,13 +1761,21 @@ execpline(Estate state, wordcode slcode, int how, int last1) else { close(synch[0]); entersubsh(ESUB_ASYNC); - if (jobtab[list_pipe_job].procs) { - if (setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader) - == -1) { - setpgrp(0L, mypgrp = getpid()); - } - } else - setpgrp(0L, mypgrp = getpid()); + /* + * At this point, we used to attach this process + * to the process group of list_pipe_job (the + * new superjob) any time that was still available. + * That caused problems in at least two + * cases because this forked shell was then + * suspended with the right hand side of the + * pipeline, and the SIGSTOP below suspended + * it a second time when it was continued. + * + * It's therefore not clear entirely why you'd ever + * do anything other than the following, but no + * doubt we'll find out... + */ + setpgrp(0L, mypgrp = getpid()); close(synch[1]); kill(getpid(), SIGSTOP); list_pipe = 0; @@ -1711,6 +1812,8 @@ execpline(Estate state, wordcode slcode, int how, int last1) deletejob(jn, 0); thisjob = pj; } + else + unqueue_signals(); if ((slflags & WC_SUBLIST_NOT) && !errflag) lastval = !lastval; } @@ -1728,6 +1831,7 @@ execpline2(Estate state, wordcode pcode, { pid_t pid; int pipes[2]; + struct execcmd_params eparams; if (breaks || retflag) return; @@ -1745,17 +1849,16 @@ execpline2(Estate state, wordcode pcode, else list_pipe_text[0] = '\0'; } - if (WC_PIPE_TYPE(pcode) == WC_PIPE_END) - execcmd(state, input, output, how, last1 ? 1 : 2); - else { + if (WC_PIPE_TYPE(pcode) == WC_PIPE_END) { + execcmd_analyse(state, &eparams); + execcmd_exec(state, &eparams, input, output, how, last1 ? 1 : 2); + } else { int old_list_pipe = list_pipe; int subsh_close = -1; - Wordcode next = state->pc + (*state->pc), pc; - wordcode code; + Wordcode next = state->pc + (*state->pc), start_pc; - state->pc++; - for (pc = state->pc; wc_code(code = *pc) == WC_REDIR; - pc += WC_REDIR_WORDS(code)); + start_pc = ++state->pc; + execcmd_analyse(state, &eparams); if (mpipe(pipes) < 0) { /* FIXME */ @@ -1764,7 +1867,8 @@ execpline2(Estate state, wordcode pcode, /* if we are doing "foo | bar" where foo is a current * * shell command, do foo in a subshell and do the * * rest of the pipeline in the current shell. */ - if (wc_code(code) >= WC_CURSH && (how & Z_SYNC)) { + if ((eparams.type >= WC_CURSH || !eparams.args) + && (how & Z_SYNC)) { int synch[2]; struct timeval bgtime; @@ -1782,7 +1886,7 @@ execpline2(Estate state, wordcode pcode, } else if (pid) { char dummy, *text; - text = getjobtext(state->prog, state->pc); + text = getjobtext(state->prog, start_pc); addproc(pid, text, 0, &bgtime); close(synch[1]); read_loop(synch[0], &dummy, 1); @@ -1793,14 +1897,18 @@ execpline2(Estate state, wordcode pcode, entersubsh(((how & Z_ASYNC) ? ESUB_ASYNC : 0) | ESUB_PGRP | ESUB_KEEPTRAP); close(synch[1]); - execcmd(state, input, pipes[1], how, 1); + if (sigtrapped[SIGEXIT]) + { + unsettrap(SIGEXIT); + } + execcmd_exec(state, &eparams, input, pipes[1], how, 1); _exit(lastval); } } else { /* otherwise just do the pipeline normally. */ addfilelist(NULL, pipes[0]); subsh_close = pipes[0]; - execcmd(state, input, pipes[1], how, 0); + execcmd_exec(state, &eparams, input, pipes[1], how, 0); } zclose(pipes[1]); state->pc = next; @@ -2456,55 +2564,51 @@ resolvebuiltin(const char *cmdarg, HashNode hn) return hn; } +/* + * We are about to execute a command at the lowest level of the + * hierarchy. Analyse the parameters from the wordcode. + */ + /**/ static void -execcmd(Estate state, int input, int output, int how, int last1) +execcmd_analyse(Estate state, Execcmd_params eparams) { - HashNode hn = NULL; - LinkList args, filelist = NULL; - LinkNode node; - Redir fn; - struct multio *mfds[10]; - char *text; - int save[10]; - int fil, dfil, is_cursh, type, do_exec = 0, redir_err = 0, i, htok = 0; - int nullexec = 0, assign = 0, forked = 0, postassigns = 0; - int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0; - /* Various flags to the command. */ - int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1; - LinkList redir; wordcode code; - Wordcode beg = state->pc, varspc, assignspc = (Wordcode)0; - FILE *oxtrerr = xtrerr, *newxtrerr = NULL; + int i; - doneps4 = 0; - redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL); + eparams->beg = state->pc; + eparams->redir = + (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL); if (wc_code(*state->pc) == WC_ASSIGN) { cmdoutval = 0; - varspc = state->pc; + eparams->varspc = state->pc; while (wc_code((code = *state->pc)) == WC_ASSIGN) state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ? 3 : WC_ASSIGN_NUM(code) + 2); } else - varspc = NULL; + eparams->varspc = NULL; code = *state->pc++; - type = wc_code(code); + eparams->type = wc_code(code); + eparams->postassigns = 0; /* It would be nice if we could use EC_DUPTOK instead of EC_DUP here. * But for that we would need to check/change all builtins so that * they don't modify their argument strings. */ - switch (type) { + switch (eparams->type) { case WC_SIMPLE: - args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok); + eparams->args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, + &eparams->htok); + eparams->assignspc = NULL; break; case WC_TYPESET: - args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP, &htok); - postassigns = *state->pc++; - assignspc = state->pc; - for (i = 0; i < postassigns; i++) { + eparams->args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP, + &eparams->htok); + eparams->postassigns = *state->pc++; + eparams->assignspc = state->pc; + for (i = 0; i < eparams->postassigns; i++) { code = *state->pc; DPUTS(wc_code(code) != WC_ASSIGN, "BUG: miscounted typeset assignments"); @@ -2514,8 +2618,45 @@ execcmd(Estate state, int input, int output, int how, int last1) break; default: - args = NULL; + eparams->args = NULL; + eparams->assignspc = NULL; + eparams->htok = 0; + break; } +} + +/* + * Execute a command at the lowest level of the hierarchy. + */ + +/**/ +static void +execcmd_exec(Estate state, Execcmd_params eparams, + int input, int output, int how, int last1) +{ + HashNode hn = NULL; + LinkList filelist = NULL; + LinkNode node; + Redir fn; + struct multio *mfds[10]; + char *text; + int save[10]; + int fil, dfil, is_cursh, do_exec = 0, redir_err = 0, i; + int nullexec = 0, assign = 0, forked = 0; + int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0; + /* Various flags to the command. */ + int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1; + FILE *oxtrerr = xtrerr, *newxtrerr = NULL; + /* + * Retrieve parameters for quick reference (they are unique + * to us so we can modify the structure if we want). + */ + LinkList args = eparams->args; + LinkList redir = eparams->redir; + Wordcode varspc = eparams->varspc; + int type = eparams->type; + + doneps4 = 0; /* * If assignment but no command get the status from variable @@ -2609,24 +2750,76 @@ execcmd(Estate state, int input, int output, int how, int last1) } checked = 0; if ((cflags & BINF_COMMAND) && nextnode(firstnode(args))) { - /* check for options to command builtin */ - char *next = (char *) getdata(nextnode(firstnode(args))); + /* + * Check for options to "command". + * If just -p, this is handled here: use the default + * path to execute. + * If -v or -V, possibly with -p, dispatch to bin_whence + * but with flag to indicate special handling of -p. + * Otherwise, just leave marked as BINF_COMMAND + * modifier with no additional action. + */ + LinkNode argnode = nextnode(firstnode(args)); + char *argdata = (char *) getdata(argnode); char *cmdopt; - if (next && *next == '-' && strlen(next) == 2 && - (cmdopt = strchr("pvV", next[1]))) - { - if (*cmdopt == 'p') { - uremnode(args, firstnode(args)); - use_defpath = 1; - if (nextnode(firstnode(args))) - next = (char *) getdata(nextnode(firstnode(args))); - } else { - hn = &commandbn.node; - is_builtin = 1; + int has_p = 0, has_vV = 0, has_other = 0; + while (*argdata == '-') { + /* Just to be definite, stop on single "-", too, */ + if (!argdata[1] || (argdata[1] == '-' && !argdata[2])) + break; + for (cmdopt = argdata+1; *cmdopt; cmdopt++) { + switch (*cmdopt) { + case 'p': + /* + * If we've got this multiple times (command + * -p -p) we'll treat the second -p as a + * command because we only remove one below. + * Don't think that's a big issue, and it's + * also traditional behaviour. + */ + has_p = 1; + break; + case 'v': + case 'V': + has_vV = 1; + break; + default: + has_other = 1; + break; + } + } + if (has_other) { + /* Don't know how to handle this, so don't */ + has_p = has_vV = 0; break; } + + argnode = nextnode(argnode); + if (!argnode) + break; + argdata = (char *) getdata(argnode); } - if (!strcmp(next, "--")) + if (has_vV) { + /* Leave everything alone, dispatch to whence */ + hn = &commandbn.node; + is_builtin = 1; + break; + } else if (has_p) { + /* Use default path; absorb command and option. */ + uremnode(args, firstnode(args)); + use_defpath = 1; + if ((argnode = nextnode(firstnode(args)))) + argdata = (char *) getdata(argnode); + } + /* + * Else just absorb command and any trailing + * end-of-options marker. This can only occur + * if we just had -p or something including more + * than just -p, -v and -V, in which case we behave + * as if this is command [non-option-stuff]. This + * isn't a good place for standard option handling. + */ + if (!strcmp(argdata, "--")) uremnode(args, firstnode(args)); } if ((cflags & BINF_EXEC) && nextnode(firstnode(args))) { @@ -2721,7 +2914,7 @@ execcmd(Estate state, int input, int output, int how, int last1) /* Do prefork substitutions */ esprefork = (assign || isset(MAGICEQUALSUBST)) ? PREFORK_TYPESET : 0; - if (args && htok) + if (args && eparams->htok) prefork(args, esprefork, NULL); if (type == WC_SIMPLE || type == WC_TYPESET) { @@ -2866,7 +3059,7 @@ execcmd(Estate state, int input, int output, int how, int last1) /* Get the text associated with this command. */ if ((how & Z_ASYNC) || (!sfcontext && !sourcelevel && (jobbing || (how & Z_TIMED)))) - text = getjobtext(state->prog, beg); + text = getjobtext(state->prog, eparams->beg); else text = NULL; @@ -2893,11 +3086,11 @@ execcmd(Estate state, int input, int output, int how, int last1) if (s[0] == Star && !s[1]) { if (!checkrmall(pwd)) uremnode(args, node); - } else if (l > 2 && s[l - 2] == '/' && s[l - 1] == Star) { + } else if (l >= 2 && s[l - 2] == '/' && s[l - 1] == Star) { char t = s[l - 2]; s[l - 2] = 0; - if (!checkrmall(s)) + if (!checkrmall(*s ? s : "/")) uremnode(args, node); s[l - 2] = t; } @@ -3125,7 +3318,7 @@ execcmd(Estate state, int input, int output, int how, int last1) forked = 1; } - if ((esglob = !(cflags & BINF_NOGLOB)) && args && htok) { + if ((esglob = !(cflags & BINF_NOGLOB)) && args && eparams->htok) { LinkList oargs = args; globlist(args, 0); args = oargs; @@ -3439,7 +3632,7 @@ execcmd(Estate state, int input, int output, int how, int last1) } if (type == WC_FUNCDEF) { Eprog redir_prog; - if (!redir && wc_code(*beg) == WC_REDIR) { + if (!redir && wc_code(*eparams->beg) == WC_REDIR) { /* * We're not using a redirection from the currently * parsed environment, which is what we'd do for an @@ -3449,7 +3642,7 @@ execcmd(Estate state, int input, int output, int how, int last1) struct estate s; s.prog = state->prog; - s.pc = beg; + s.pc = eparams->beg; s.strs = state->prog->strs; /* @@ -3491,7 +3684,7 @@ execcmd(Estate state, int input, int output, int how, int last1) * if it's got "command" in front. * If it's a normal command --- save. */ - if (is_shfunc || (hn->flags & BINF_PSPECIAL)) + if (is_shfunc || (hn->flags & (BINF_PSPECIAL|BINF_ASSIGN))) do_save = (orig_cflags & BINF_COMMAND); else do_save = 1; @@ -3500,7 +3693,7 @@ execcmd(Estate state, int input, int output, int how, int last1) * Save if it's got "command" in front or it's * not a magic-equals assignment. */ - if ((cflags & BINF_COMMAND) || !assign) + if ((cflags & (BINF_COMMAND|BINF_ASSIGN)) || !assign) do_save = 1; } if (do_save && varspc) @@ -3533,13 +3726,15 @@ execcmd(Estate state, int input, int output, int how, int last1) } else { /* It's a builtin */ LinkList assigns = (LinkList)0; + int postassigns = eparams->postassigns; if (forked) closem(FDT_INTERNAL); if (postassigns) { Wordcode opc = state->pc; - state->pc = assignspc; + state->pc = eparams->assignspc; assigns = newlinklist(); while (postassigns--) { + int htok; wordcode ac = *state->pc++; char *name = ecgetstr(state, EC_DUPTOK, &htok); Asgment asg; @@ -3648,7 +3843,8 @@ execcmd(Estate state, int input, int output, int how, int last1) state->pc = opc; } dont_queue_signals(); - lastval = execbuiltin(args, assigns, (Builtin) hn); + if (!errflag) + lastval = execbuiltin(args, assigns, (Builtin) hn); if (do_save & BINF_COMMAND) errflag &= ~ERRFLAG_ERROR; restore_queue_signals(q); @@ -3685,12 +3881,15 @@ execcmd(Estate state, int input, int output, int how, int last1) restore_params(restorelist, removelist); } else { - if (!forked) - setiparam("SHLVL", --shlvl); - if (do_exec) { + if (!subsh) { + /* for either implicit or explicit "exec", decrease $SHLVL + * as we're now done as a shell */ + if (!forked) + setiparam("SHLVL", --shlvl); + /* If we are exec'ing a command, and we are not * * in a subshell, then save the history file. */ - if (!subsh && isset(RCS) && interact && !nohistsave) + if (do_exec && isset(RCS) && interact && !nohistsave) savehistfile(NULL, 1, HFILE_USE_OPTIONS); } if (type == WC_SIMPLE || type == WC_TYPESET) { @@ -4022,7 +4221,7 @@ gethere(char **strp, int typ) parsestr(&buf); - if (!errflag) { + if (!(errflag & ERRFLAG_ERROR)) { /* Retain any user interrupt error */ errflag = ef | (errflag & ERRFLAG_INT); } @@ -4275,11 +4474,26 @@ getoutputfile(char *cmd, char **eptr) untokenize(s); } - addfilelist(nam, 0); + if (!s) /* Unclear why we need to do this before open() */ + child_block(); /* but it has been so for a long time: leave it */ - if (!s) - child_block(); - fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600); + 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; + } else { + char *suffix = getsparam("TMPSUFFIX"); + if (suffix && *suffix && !strstr(suffix, "/")) { + suffix = dyncat(nam, unmeta(suffix)); + if (link(nam, suffix) == 0) { + addfilelist(nam, 0); + nam = ztrdup(suffix); + } + } + } + addfilelist(nam, 0); if (s) { /* optimised here-string */ @@ -4290,7 +4504,7 @@ getoutputfile(char *cmd, char **eptr) return nam; } - if (fd < 0 || (cmdoutpid = pid = zfork(NULL)) == -1) { + if ((cmdoutpid = pid = zfork(NULL)) == -1) { /* fork or open error */ child_unblock(); return nam; @@ -4518,8 +4732,6 @@ spawnpipes(LinkList l, int nullexec) } } -extern int tracingcond; - /* evaluate a [[ ... ]] */ /**/ @@ -4608,6 +4820,8 @@ exectime(Estate state, UNUSED(int do_exec)) /* Define a shell function */ +static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)"; + /**/ static int execfuncdef(Estate state, Eprog redir_prog) @@ -4725,7 +4939,7 @@ execfuncdef(Estate state, Eprog redir_prog) if (!args) args = newlinklist(); - shf->node.nam = "(anon)"; + shf->node.nam = (char *) ANONYMOUS_FUNCTION_NAME; pushnode(args, shf->node.nam); execshfunc(shf, args); @@ -4906,7 +5120,8 @@ execautofn_basic(Estate state, UNUSED(int do_exec)) oldscriptname = scriptname; oldscriptfilename = scriptfilename; - scriptname = scriptfilename = dupstring(shf->node.nam); + scriptname = dupstring(shf->node.nam); + scriptfilename = dupstring(shf->filename); execode(shf->funcdef, 1, 0, "loadautofunc"); scriptname = oldscriptname; scriptfilename = oldscriptfilename; @@ -5158,8 +5373,12 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) if (flags & (PM_TAGGED|PM_TAGGED_LOCAL)) opts[XTRACE] = 1; - else if (oflags & PM_TAGGED_LOCAL) - opts[XTRACE] = 0; + else if (oflags & PM_TAGGED_LOCAL) { + if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME /* pointer comparison */) + flags |= PM_TAGGED_LOCAL; + else + opts[XTRACE] = 0; + } ooflags = oflags; /* * oflags is static, because we compare it on the next recursive @@ -5360,6 +5579,7 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name) if (!cont) { if (ou) zfree(ou, ouu); + unqueue_signals(); return; } wrap = wrap->next; @@ -5382,7 +5602,7 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name) Eprog getfpfunc(char *s, int *ksh, char **fname) { - char **pp, buf[PATH_MAX]; + char **pp, buf[PATH_MAX+1]; off_t len; off_t rlen; char *d; @@ -5512,7 +5732,7 @@ cancd(char *s) char *t; if (*s != '/') { - char sbuf[PATH_MAX], **cp; + char sbuf[PATH_MAX+1], **cp; if (cancd2(s)) return s; diff --git a/Src/glob.c b/Src/glob.c index 69de15544..623e6f1d6 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -41,7 +41,10 @@ typedef struct gmatch *Gmatch; struct gmatch { + /* Metafied file name */ char *name; + /* Unmetafied file name; embedded nulls can't occur in file names */ + char *uname; /* * Array of sort strings: one for each GS_EXEC sort type in * the glob qualifiers. @@ -280,7 +283,7 @@ addpath(char *s, int l) static int statfullpath(const char *s, struct stat *st, int l) { - char buf[PATH_MAX]; + char buf[PATH_MAX+1]; DPUTS(strlen(s) + !*s + pathpos - pathbufcwd >= PATH_MAX, "BUG: statfullpath(): pathname too long"); @@ -306,7 +309,7 @@ statfullpath(const char *s, struct stat *st, int l) /* This may be set by qualifier functions to an array of strings to insert * into the list instead of the original string. */ -char **inserts; +static char **inserts; /* add a match to the list */ @@ -776,7 +779,7 @@ parsepat(char *str) /* Now there is no (#X) in front, we can check the path. */ if (!pathbuf) - pathbuf = zalloc(pathbufsz = PATH_MAX); + pathbuf = zalloc(pathbufsz = PATH_MAX+1); DPUTS(pathbufcwd, "BUG: glob changed directory"); if (*str == '/') { /* pattern has absolute path */ str++; @@ -911,7 +914,8 @@ gmatchcmp(Gmatch a, Gmatch b) for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) { switch (s->tp & ~GS_DESC) { case GS_NAME: - r = zstrcmp(b->name, a->name, gf_numsort ? SORTIT_NUMERICALLY : 0); + r = zstrcmp(b->uname, a->uname, + gf_numsort ? SORTIT_NUMERICALLY : 0); break; case GS_DEPTH: { @@ -1170,7 +1174,7 @@ checkglobqual(char *str, int sl, int nobareglob, char **sp) } /* Main entry point to the globbing code for filename globbing. * - * np points to a node in the list list which will be expanded * + * np points to a node in the list which will be expanded * * into a series of nodes. */ /**/ @@ -1278,14 +1282,7 @@ zglob(LinkList list, LinkNode np, int nountok) *ptr = '-'; while (*s && !newcolonmod) { func = (int (*) _((char *, Statptr, off_t, char *)))0; - if (idigit(*s)) { - /* Store numeric argument for qualifier */ - func = qualflags; - data = 0; - sdata = NULL; - while (idigit(*s)) - data = data * 010 + (*s++ - '0'); - } else if (*s == ',') { + if (*s == ',') { /* A comma separates alternative sets of qualifiers */ s++; sense = 0; @@ -1859,6 +1856,7 @@ zglob(LinkList list, LinkNode np, int nountok) int nexecs = 0; struct globsort *sortp; struct globsort *lastsortp = gf_sortlist + gf_nsorts; + Gmatch gmptr; /* First find out if there are any GS_EXECs, counting them. */ for (sortp = gf_sortlist; sortp < lastsortp; sortp++) @@ -1910,6 +1908,23 @@ zglob(LinkList list, LinkNode np, int nountok) } } + /* + * Where necessary, create unmetafied version of names + * for comparison. If no Meta characters just point + * to original string. All on heap. + */ + for (gmptr = matchbuf; gmptr < matchptr; gmptr++) + { + if (strchr(gmptr->name, Meta)) + { + int dummy; + gmptr->uname = dupstring(gmptr->name); + unmetafy(gmptr->uname, &dummy); + } else { + gmptr->uname = gmptr->name; + } + } + /* 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), @@ -3484,6 +3499,10 @@ zshtokenize(char *s, int flags) for (; *s; s++) { cont: switch (*s) { + case Meta: + /* skip both Meta and following character */ + s++; + break; case Bnull: case Bnullkeep: case '\\': diff --git a/Src/hashtable.c b/Src/hashtable.c index 0664c3694..7c3367568 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -558,7 +558,7 @@ printhashtabinfo(HashTable ht) /**/ int -bin_hashinfo(char *nam, char **args, Options ops, int func) +bin_hashinfo(UNUSED(char *nam), UNUSED(char **args), UNUSED(Options ops), UNUSED(int func)) { HashTable ht; @@ -1291,9 +1291,9 @@ printaliasnode(HashNode hn, int printflags) else if (a->node.flags & ALIAS_GLOBAL) printf("-g "); - /* If an alias begins with `-', then we must output `-- ' * + /* If an alias begins with `-' or `+', then we must output `-- ' * first, so that it is not interpreted as an option. */ - if(a->node.nam[0] == '-') + if(a->node.nam[0] == '-' || a->node.nam[0] == '+') printf("-- "); } diff --git a/Src/hist.c b/Src/hist.c index 007366a49..97fd34039 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -468,7 +468,7 @@ herrflush(void) /**/ static int -getsubsargs(char *subline, int *gbalp, int *cflagp) +getsubsargs(UNUSED(char *subline), int *gbalp, int *cflagp) { int del, follow; char *ptr1, *ptr2; @@ -653,6 +653,7 @@ histsubchar(int c) (c == '}' || c == ';' || c == '\'' || c == '"' || c == '`')) { /* Neither event nor word designator, no expansion */ safeinungetc(c); + unqueue_signals(); return bangchar; } *ptr = 0; @@ -1037,7 +1038,7 @@ hbegin(int dohist) /* * pws: We used to test for "|| (inbufflags & INP_ALIAS)" * in this test, but at this point we don't have input - * set up up so this can trigger unnecessarily. + * set up so this can trigger unnecessarily. * I don't see how the test at this point could ever be * useful, since we only get here when we're initialising * the history mechanism, before we've done any input. @@ -1378,7 +1379,6 @@ should_ignore_line(Eprog prog) mod_export int hend(Eprog prog) { - LinkList hookargs = newlinklist(); int flag, hookret, stack_pos = histsave_stack_pos; /* * save: @@ -1418,9 +1418,17 @@ hend(Eprog prog) DPUTS(hptr < chline, "History end pointer off start of line"); *hptr = '\0'; } - addlinknode(hookargs, "zshaddhistory"); - addlinknode(hookargs, chline); - callhookfunc("zshaddhistory", hookargs, 1, &hookret); + { + LinkList hookargs = newlinklist(); + int save_errflag = errflag; + errflag = 0; + + addlinknode(hookargs, "zshaddhistory"); + addlinknode(hookargs, chline); + callhookfunc("zshaddhistory", hookargs, 1, &hookret); + + errflag |= save_errflag; + } /* For history sharing, lock history file once for both read and write */ hf = getsparam("HISTFILE"); if (isset(SHAREHISTORY) && !lockhistfile(hf, 0)) { @@ -1835,7 +1843,7 @@ chrealpath(char **junkptr) # ifdef REALPATH_ACCEPTS_NULL char *lastpos, *nonreal, *real; # else - char *lastpos, *nonreal, pathbuf[PATH_MAX]; + char *lastpos, *nonreal, pathbuf[PATH_MAX+1]; char *real = pathbuf; # endif #endif diff --git a/Src/init.c b/Src/init.c index 4097327ee..c12043b88 100644 --- a/Src/init.c +++ b/Src/init.c @@ -712,7 +712,7 @@ init_term(void) if (tgetent(termbuf, term) != TGETENT_SUCCESS) #endif { - if (isset(INTERACTIVE)) + if (interact) zerr("can't find terminal definition for %s", term); errflag &= ~ERRFLAG_ERROR; termflags |= TERM_BAD; @@ -802,7 +802,7 @@ init_term(void) /**/ void -setupvals(char *cmd) +setupvals(char *cmd, char *runscript, char *zsh_name) { #ifdef USE_GETPWUID struct passwd *pswd; @@ -1089,6 +1089,9 @@ setupvals(char *cmd) if (cmd) setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd)); + if (runscript) + setsparam("ZSH_SCRIPT", ztrdup(runscript)); + setsparam("ZSH_NAME", ztrdup(zsh_name)); /* NOTE: already metafied early in zsh_main() */ } /* @@ -1202,19 +1205,22 @@ run_init_scripts(void) if (islogin) source("/etc/profile"); if (unset(PRIVILEGED)) { - char *s = getsparam("ENV"); if (islogin) sourcehome(".profile"); - noerrs = 2; - if (s) { - s = dupstring(s); - if (!parsestr(&s)) { - singsub(&s); - noerrs = 0; - source(s); + + if (interact) { + noerrs = 2; + char *s = getsparam("ENV"); + if (s) { + s = dupstring(s); + if (!parsestr(&s)) { + singsub(&s); + noerrs = 0; + source(s); + } } + noerrs = 0; } - noerrs = 0; } else source("/etc/suid_profile"); } else { @@ -1224,7 +1230,7 @@ run_init_scripts(void) if (isset(RCS) && unset(PRIVILEGED)) { - if (isset(INTERACTIVE)) { + if (interact) { /* * Always attempt to load the newuser module to perform * checks for new zsh users. Don't care if we can't load it. @@ -1270,7 +1276,7 @@ run_init_scripts(void) /**/ void -init_misc(char *cmd) +init_misc(char *cmd, char *zsh_name) { #ifndef RESTRICTED_R if ( restricted ) @@ -1436,8 +1442,10 @@ sourcehome(char *s) queue_signals(); if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam_u("ZDOTDIR"))) { h = home; - if (!h) + if (!h) { + unqueue_signals(); return; + } } { @@ -1606,7 +1614,7 @@ mod_export int use_exit_printed; mod_export int zsh_main(UNUSED(int argc), char **argv) { - char **t, *runscript = NULL; + char **t, *runscript = NULL, *zsh_name; char *cmd; /* argument to -c */ int t0; #ifdef USE_LOCALE @@ -1660,14 +1668,14 @@ zsh_main(UNUSED(int argc), char **argv) SHTTY = -1; init_io(cmd); - setupvals(cmd); + setupvals(cmd, runscript, zsh_name); init_signals(); init_bltinmods(); init_builtins(); run_init_scripts(); setupshin(runscript); - init_misc(cmd); + init_misc(cmd, zsh_name); for (;;) { /* diff --git a/Src/input.c b/Src/input.c index eb968ea72..fe94b8ef7 100644 --- a/Src/input.c +++ b/Src/input.c @@ -51,7 +51,7 @@ * Note that the input string is itself used as the input buffer: it is not * copied, nor is it every written back to, so using a constant string * should work. Consequently, when passing areas of memory from the heap - * it is necessary that that heap last as long as the operation of reading + * it is necessary that the heap last as long as the operation of reading * the string. After the string is read, the stack should be popped with * inpop(), which effectively flushes any unread input as well as restoring * the previous input state. diff --git a/Src/jobs.c b/Src/jobs.c index b47ba8c60..d1b98ac4d 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -128,7 +128,7 @@ makerunning(Job jn) Process pn; jn->stat &= ~STAT_STOPPED; - for (pn = jn->procs; pn; pn = pn->next) + for (pn = jn->procs; pn; pn = pn->next) { #if 0 if (WIFSTOPPED(pn->status) && (!(jn->stat & STAT_SUPERJOB) || pn->next)) @@ -136,6 +136,7 @@ makerunning(Job jn) #endif if (WIFSTOPPED(pn->status)) pn->status = SP_RUNNING; + } if (jn->stat & STAT_SUPERJOB) makerunning(jobtab + jn->other); @@ -231,12 +232,13 @@ super_job(int sub) static int handle_sub(int job, int fg) { + /* job: superjob; sj: subjob. */ Job jn = jobtab + job, sj = jobtab + jn->other; if ((sj->stat & STAT_DONE) || (!sj->procs && !sj->auxprocs)) { struct process *p; - - for (p = sj->procs; p; p = p->next) + + for (p = sj->procs; p; p = p->next) { if (WIFSIGNALED(p->status)) { if (jn->gleader != mypgrp && jn->procs->next) killpg(jn->gleader, WTERMSIG(p->status)); @@ -246,6 +248,7 @@ handle_sub(int job, int fg) kill(sj->other, WTERMSIG(p->status)); break; } + } if (!p) { int cp; @@ -884,37 +887,62 @@ should_report_time(Job j) struct value vbuf; Value v; char *s = "REPORTTIME"; - zlong reporttime; + zlong reporttime = -1; +#ifdef HAVE_GETRUSAGE + char *sm = "REPORTMEMORY"; + zlong reportmemory = -1; +#endif /* if the time keyword was used */ if (j->stat & STAT_TIMED) return 1; queue_signals(); - if (!(v = getvalue(&vbuf, &s, 0)) || - (reporttime = getintvalue(v)) < 0) { - unqueue_signals(); - return 0; - } + if ((v = getvalue(&vbuf, &s, 0))) + reporttime = getintvalue(v); +#ifdef HAVE_GETRUSAGE + if ((v = getvalue(&vbuf, &sm, 0))) + reportmemory = getintvalue(v); +#endif unqueue_signals(); + if (reporttime < 0 +#ifdef HAVE_GETRUSAGE + && reportmemory < 0 +#endif + ) + return 0; /* can this ever happen? */ if (!j->procs) return 0; if (zleactive) return 0; + if (reporttime >= 0) + { #ifdef HAVE_GETRUSAGE - reporttime -= j->procs->ti.ru_utime.tv_sec + j->procs->ti.ru_stime.tv_sec; - if (j->procs->ti.ru_utime.tv_usec + - j->procs->ti.ru_stime.tv_usec >= 1000000) - reporttime--; - return reporttime <= 0; + reporttime -= j->procs->ti.ru_utime.tv_sec + + j->procs->ti.ru_stime.tv_sec; + if (j->procs->ti.ru_utime.tv_usec + + j->procs->ti.ru_stime.tv_usec >= 1000000) + reporttime--; + if (reporttime <= 0) + return 1; #else - { - clktck = get_clktck(); - return ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime); + { + clktck = get_clktck(); + if ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime) + return 1; + } +#endif } + +#ifdef HAVE_GETRUSAGE + if (reportmemory >= 0 && + j->procs->ti.ru_maxrss / 1024 > reportmemory) + return 1; #endif + + return 0; } /* !(lng & 3) means jobs * @@ -1291,6 +1319,11 @@ deletejob(Job jn, int disowning) attachtty(mypgrp); adjustwinsize(0); } + if (jn->stat & STAT_SUPERJOB) { + Job jno = jobtab + jn->other; + if (jno->stat & STAT_SUBJOB) + jno->stat |= STAT_SUBJOB_ORPHANED; + } freejob(jn, 1); } @@ -1447,7 +1480,7 @@ zwaitjob(int job, int wait_cmd) */ pipecleanfilelist(jn->filelist, 0); } - while (!errflag && jn->stat && + while (!(errflag & ERRFLAG_ERROR) && jn->stat && !(jn->stat & STAT_DONE) && !(interact && (jn->stat & STAT_STOPPED))) { signal_suspend(SIGCHLD, wait_cmd); @@ -1469,6 +1502,13 @@ zwaitjob(int job, int wait_cmd) that's the one related to ^C. But that doesn't work. There's something more here we don't understand. --pws + The change above to ignore ERRFLAG_INT in the loop test + solves a problem wherein child processes that ignore the + INT signal were never waited-for. Clearing the flag here + still seems the wrong thing, but perhaps ERRFLAG_INT + should be saved and restored around signal_suspend() to + prevent it being lost within a signal trap? --Bart + errflag = 0; */ if (subsh) { @@ -1970,9 +2010,9 @@ struct bgstatus { }; typedef struct bgstatus *Bgstatus; /* The list of those entries */ -LinkList bgstatus_list; +static LinkList bgstatus_list; /* Count of entries. Reaches value of _SC_CHILD_MAX and stops. */ -long bgstatus_count; +static long bgstatus_count; /* * Remove and free a bgstatus entry. @@ -2372,7 +2412,7 @@ bin_fg(char *name, char **argv, Options ops, int func) return retval; } -const struct { +static const struct { const char *name; int num; } alt_sigs[] = { @@ -2527,6 +2567,10 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) argv++; } + /* Discard the standard "-" and "--" option breaks */ + if (*argv && (*argv)[0] == '-' && (!(*argv)[1] || (*argv)[1] == '-')) + argv++; + if (!*argv) { zwarnnam(nam, "not enough arguments"); return 1; @@ -267,9 +267,13 @@ zshlex(void) { if (tok == LEXERR) return; - do + do { + if (inrepeat_) + ++inrepeat_; + if (inrepeat_ == 3 && isset(SHORTLOOPS)) + incmdpos = 1; tok = gettok(); - while (tok != ENDINPUT && exalias()); + } while (tok != ENDINPUT && exalias()); nocorrect &= 1; if (tok == NEWLIN || tok == ENDINPUT) { while (hdocs) { @@ -609,7 +613,7 @@ gettok(void) if (lexstop) return (errflag) ? LEXERR : ENDINPUT; isfirstln = 0; - if ((lexflags & LEXFLAGS_ZLE)) + if ((lexflags & LEXFLAGS_ZLE) && !(inbufflags & INP_ALIAS)) wordbeg = inbufct - (qbang && c == bangchar); hwbegin(-1-(qbang && c == bangchar)); /* word includes the last character read and possibly \ before ! */ @@ -1026,8 +1030,10 @@ gettokstr(int c, int sub) c = Inbrace; ++bct; cmdpush(CS_BRACEPAR); - if (!in_brace_param) - in_brace_param = bct; + if (!in_brace_param) { + if ((in_brace_param = bct)) + seen_brct = 0; + } } else { hungetc(e); lexstop = 0; @@ -1783,9 +1789,17 @@ parse_subst_string(char *s) static void gotword(void) { - we = zlemetall + 1 - inbufct + (addedx == 2 ? 1 : 0); - if (zlemetacs <= we) { - wb = zlemetall - wordbeg + addedx; + int nwe = zlemetall + 1 - inbufct + (addedx == 2 ? 1 : 0); + if (zlemetacs <= nwe) { + int nwb = zlemetall - wordbeg + addedx; + if (zlemetacs >= nwb) { + wb = nwb; + we = nwe; + } else { + wb = zlemetacs + addedx; + if (we < wb) + we = wb; + } lexflags = 0; } } @@ -1829,9 +1843,9 @@ checkalias(void) suf > zshlextext && suf[-1] != Meta && (an = (Alias)sufaliastab->getnode(sufaliastab, suf+1)) && !an->inuse && incmdpos) { - inpush(dupstring(zshlextext), INP_ALIAS, NULL); + inpush(dupstring(zshlextext), INP_ALIAS, an); inpush(" ", INP_ALIAS, NULL); - inpush(an->text, INP_ALIAS, an); + inpush(an->text, INP_ALIAS, NULL); lexstop = 0; return 1; } @@ -1897,6 +1911,7 @@ exalias(void) zshlextext[0] == '}' && !zshlextext[1])) && (rw = (Reswd) reswdtab->getnode(reswdtab, zshlextext))) { tok = rw->token; + inrepeat_ = (tok == REPEAT); if (tok == DINBRACK) incond = 1; } else if (incond && !strcmp(zshlextext, "]]")) { @@ -2123,8 +2138,17 @@ skipcomm(void) lexflags &= ~LEXFLAGS_ZLE; dbparens = 0; /* restored by zcontext_restore_partial() */ - if (!parse_event(OUTPAR) || tok != OUTPAR) - lexstop = 1; + if (!parse_event(OUTPAR) || tok != OUTPAR) { + if (strin) { + /* + * Get the rest of the string raw since we don't + * know where this token ends. + */ + while (!lexstop) + (void)ingetc(); + } else + lexstop = 1; + } /* Outpar lexical token gets added in caller if present */ /* diff --git a/Src/loop.c b/Src/loop.c index 4def9b652..367c0df5c 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -289,6 +289,8 @@ execselect(Estate state, UNUSED(int do_exec)) } } else str = (char *)getlinknode(bufstack); + if (!str && !errflag) + setsparam("REPLY", ztrdup("")); /* EOF (user pressed Ctrl+D) */ if (!str || errflag) { if (breaks) breaks--; @@ -437,13 +439,12 @@ execwhile(Estate state, UNUSED(int do_exec)) if (!((lastval == 0) ^ isuntil)) { if (breaks) breaks--; - lastval = oldval; + if (!retflag) + lastval = oldval; break; } - if (retflag) { - lastval = oldval; + if (retflag) break; - } /* In case the loop body is also a functional no-op, * make sure signal handlers recognize ^C as above. */ @@ -493,7 +494,9 @@ execrepeat(Estate state, UNUSED(int do_exec)) tmp = ecgetstr(state, EC_DUPTOK, &htok); if (htok) singsub(&tmp); - count = atoi(tmp); + count = mathevali(tmp); + if (errflag) + return 1; pushheap(); cmdpush(CS_REPEAT); loops++; @@ -566,7 +569,8 @@ execif(Estate state, int do_exec) cmdpop(); } else { noerrexit = olderrexit; - lastval = 0; + if (!retflag) + lastval = 0; } state->pc = end; @@ -580,7 +584,7 @@ execcase(Estate state, int do_exec) Wordcode end, next; wordcode code = state->pc[-1]; char *word, *pat; - int npat, save, nalts, ialt, patok; + int npat, save, nalts, ialt, patok, anypatok; Patprog *spprog, pprog; end = state->pc + WC_CASE_SKIP(code); @@ -588,7 +592,7 @@ execcase(Estate state, int do_exec) word = ecgetstr(state, EC_DUP, NULL); singsub(&word); untokenize(word); - lastval = 0; + anypatok = 0; cmdpush(CS_CASE); while (state->pc < end) { @@ -645,7 +649,7 @@ execcase(Estate state, int do_exec) *spprog = pprog; } if (pprog && pattry(pprog, word)) - patok = 1; + patok = anypatok = 1; state->pc += 2; nalts--; } @@ -658,7 +662,7 @@ execcase(Estate state, int do_exec) execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) && do_exec)); while (!retflag && wc_code(code) == WC_CASE && - WC_CASE_TYPE(code) == WC_CASE_AND) { + WC_CASE_TYPE(code) == WC_CASE_AND && state->pc < end) { state->pc = next; code = *state->pc++; next = state->pc + WC_CASE_SKIP(code); @@ -676,6 +680,9 @@ execcase(Estate state, int do_exec) state->pc = end; + if (!anypatok) + lastval = 0; + return lastval; } @@ -157,13 +157,13 @@ mod_export Heapid last_heap_id; * Assumes old_heaps() will come along and restore it later * (outputs an error if old_heaps() is called out of sequence). */ -LinkList heaps_saved; +static LinkList heaps_saved; /* * Debugging verbosity. This must be set from a debugger. * An 'or' of bits from the enum heap_debug_verbosity. */ -volatile int heap_debug_verbosity; +static volatile int heap_debug_verbosity; /* * Generate a heap identifier that's unique up to unsigned integer wrap. @@ -497,7 +497,8 @@ popheap(void) continue; } h->next = NULL; - } + } else if (hl == h) /* This is the last arena of all */ + hl = NULL; #ifdef USE_MMAP munmap((void *) h, h->size); #else @@ -903,27 +904,36 @@ memory_validate(Heapid heap_id) queue_signals(); for (h = heaps; h; h = h->next) { - if (h->heap_id == heap_id) + if (h->heap_id == heap_id) { + unqueue_signals(); return 0; + } for (hs = heaps->sp; hs; hs = hs->next) { - if (hs->heap_id == heap_id) + if (hs->heap_id == heap_id) { + unqueue_signals(); return 0; + } } } if (heaps_saved) { for (node = firstnode(heaps_saved); node; incnode(node)) { for (h = (Heap)getdata(node); h; h = h->next) { - if (h->heap_id == heap_id) + if (h->heap_id == heap_id) { + unqueue_signals(); return 0; + } for (hs = heaps->sp; hs; hs = hs->next) { - if (hs->heap_id == heap_id) + if (hs->heap_id == heap_id) { + unqueue_signals(); return 0; + } } } } } + unqueue_signals(); return 1; } /**/ @@ -966,18 +976,10 @@ zalloc(size_t size) mod_export void * zshcalloc(size_t size) { - void *ptr; - + void *ptr = zalloc(size); if (!size) size = 1; - queue_signals(); - if (!(ptr = (void *) malloc(size))) { - zerr("fatal error: out of memory"); - exit(1); - } - unqueue_signals(); memset(ptr, 0, size); - return ptr; } diff --git a/Src/module.c b/Src/module.c index 368254c29..41f142adb 100644 --- a/Src/module.c +++ b/Src/module.c @@ -2242,6 +2242,7 @@ load_module(char const *name, Feature_enables enablesarr, int silent) return 0; } if (m->node.flags & MOD_BUSY) { + unqueue_signals(); zerr("circular dependencies for module ;%s", name); return 1; } @@ -3350,6 +3351,8 @@ setfeatureenables(Module m, Features f, int *e) if (f->mf_size) { if (setmathfuncs(m->node.nam, f->mf_list, f->mf_size, e)) ret = 1; + if (e) + e += f->mf_size; } if (f->pd_size) { if (setparamdefs(m->node.nam, f->pd_list, f->pd_size, e)) diff --git a/Src/options.c b/Src/options.c index 17c46c311..18619c851 100644 --- a/Src/options.c +++ b/Src/options.c @@ -847,9 +847,10 @@ printoptionnodestate(HashNode hn, int hadplus) int optno = on->optno; if (hadplus) { - if (defset(on, emulation) != isset(optno)) - printf("set -o %s%s\n", defset(on, emulation) ? - "no" : "", on->node.nam); + printf("set %co %s%s\n", + defset(on, emulation) != isset(optno) ? '-' : '+', + defset(on, emulation) ? "no" : "", + on->node.nam); } else { if (defset(on, emulation)) printf("no%-19s %s\n", on->node.nam, isset(optno) ? "off" : "on"); diff --git a/Src/params.c b/Src/params.c index b2e889738..aa8b196bd 100644 --- a/Src/params.c +++ b/Src/params.c @@ -80,14 +80,14 @@ char *argzero, /* $0 */ *rprompt, /* $RPROMPT */ *rprompt2, /* $RPROMPT2 */ *sprompt, /* $SPROMPT */ - *wordchars, /* $WORDCHARS */ - *zsh_name; /* $ZSH_NAME */ + *wordchars; /* $WORDCHARS */ /**/ mod_export char *ifs, /* $IFS */ *postedit, /* $POSTEDIT */ *term, /* $TERM */ *zsh_terminfo, /* $TERMINFO */ + *zsh_terminfodirs, /* $TERMINFO_DIRS */ *ttystrname, /* $TTY */ *pwd; /* $PWD */ @@ -209,6 +209,8 @@ static const struct gsu_scalar term_gsu = { termgetfn, termsetfn, stdunsetfn }; static const struct gsu_scalar terminfo_gsu = { terminfogetfn, terminfosetfn, stdunsetfn }; +static const struct gsu_scalar terminfodirs_gsu = +{ terminfodirsgetfn, terminfodirssetfn, stdunsetfn }; static const struct gsu_scalar wordchars_gsu = { wordcharsgetfn, wordcharssetfn, stdunsetfn }; static const struct gsu_scalar ifs_gsu = @@ -284,8 +286,9 @@ IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT), IPDEF2("HOME", home_gsu, PM_UNSET), IPDEF2("TERM", term_gsu, PM_UNSET), IPDEF2("TERMINFO", terminfo_gsu, PM_UNSET), +IPDEF2("TERMINFO_DIRS", terminfodirs_gsu, PM_UNSET), IPDEF2("WORDCHARS", wordchars_gsu, 0), -IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT), +IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT | PM_RESTRICTED), IPDEF2("_", underscore_gsu, PM_DONTIMPORT), IPDEF2("KEYBOARD_HACK", keyboard_hack_gsu, PM_DONTIMPORT), IPDEF2("0", argzero_gsu, 0), @@ -334,6 +337,7 @@ IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu), IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu), #define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} +#define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_SUID},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} #define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} IPDEF7("OPTARG", &zoptarg), IPDEF7("NULLCMD", &nullcmd), @@ -346,7 +350,7 @@ IPDEF7("PS2", &prompt2), IPDEF7U("RPS2", &rprompt2), IPDEF7U("RPROMPT2", &rprompt2), IPDEF7("PS3", &prompt3), -IPDEF7("PS4", &prompt4), +IPDEF7R("PS4", &prompt4), IPDEF7("SPROMPT", &sprompt), #define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0} @@ -416,7 +420,7 @@ IPDEF10("pipestatus", pipestatus_gsu), * and $@, this is not readonly. This parameter is not directly * visible in user space. */ -initparam argvparam_pm = IPDEF9F("", &pparams, NULL, \ +static initparam argvparam_pm = IPDEF9F("", &pparams, NULL, \ PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT); #undef BR @@ -627,6 +631,36 @@ getvaluearr(Value v) return NULL; } +/* Return whether the variable is set * + * checks that array slices are within range * + * used for [[ -v ... ]] condition test */ + +/**/ +int +issetvar(char *name) +{ + struct value vbuf; + Value v; + int slice; + char **arr; + + if (!(v = getvalue(&vbuf, &name, 1)) || *name) + return 0; /* no value or more chars after the variable name */ + if (v->isarr & ~SCANPM_ARRONLY) + return v->end > 1; /* for extracted elements, end gives us a count */ + + slice = v->start != 0 || v->end != -1; + if (PM_TYPE(v->pm->node.flags) != PM_ARRAY || !slice) + return !slice && !(v->pm->node.flags & PM_UNSET); + + if (!v->end) /* empty array slice */ + return 0; + /* get the array and check end is within range */ + if (!(arr = getvaluearr(v))) + return 0; + return arrlen_ge(arr, v->end < 0 ? - v->end : v->end); +} + /* * Split environment string into (name, value) pair. * this is used to avoid in-place editing of environment table @@ -660,7 +694,28 @@ split_env_string(char *env, char **name, char **value) } else return 0; } - + +/** + * Check parameter flags to see if parameter shouldn't be imported + * from environment at start. + * + * return 1: don't import: 0: ok to import. + */ +static int dontimport(int flags) +{ + /* If explicitly marked as don't export */ + if (flags & PM_DONTIMPORT) + return 1; + /* If value already exported */ + if (flags & PM_EXPORTED) + return 1; + /* If security issue when importing and running with some privilege */ + if ((flags & PM_DONTIMPORT_SUID) && isset(PRIVILEGED)) + return 1; + /* OK to import */ + return 0; +} + /* Set up parameter hash table. This will add predefined * * parameter entries as well as setting up parameter table * * entries for environment variables we inherit. */ @@ -752,8 +807,13 @@ createparamtable(void) envp2 = environ; *envp2; envp2++) { if (split_env_string(*envp2, &iname, &ivalue)) { if (!idigit(*iname) && isident(iname) && !strchr(iname, '[')) { + /* + * Parameters that aren't already in the parameter table + * aren't special to the shell, so it's always OK to + * import. Otherwise, check parameter flags. + */ if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) || - !(pm->node.flags & PM_DONTIMPORT || pm->node.flags & PM_EXPORTED)) && + !dontimport(pm->node.flags)) && (pm = assignsparam(iname, metafy(ivalue, -1, META_DUP), ASSPM_ENV_IMPORT))) { pm->node.flags |= PM_EXPORTED; @@ -771,7 +831,7 @@ createparamtable(void) } popheap(); #ifndef USE_SET_UNSET_ENV - *envp = '\0'; + *envp = NULL; #endif opts[ALLEXPORT] = oae; @@ -812,7 +872,7 @@ createparamtable(void) setsparam("OSTYPE", ztrdup_metafy(OSTYPE)); setsparam("TTY", ztrdup_metafy(ttystrname)); setsparam("VENDOR", ztrdup_metafy(VENDOR)); - setsparam("ZSH_NAME", ztrdup_metafy(zsh_name)); + 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 *))); @@ -884,7 +944,10 @@ createparam(char *name, int flags) zerr("%s: restricted", name); return NULL; } - if (!(oldpm->node.flags & PM_UNSET) || (oldpm->node.flags & PM_SPECIAL)) { + if (!(oldpm->node.flags & PM_UNSET) || + (oldpm->node.flags & PM_SPECIAL) || + /* POSIXBUILTINS horror: we need to retain 'export' flags */ + (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) { oldpm->node.flags &= ~PM_UNSET; if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) { Param altpm = @@ -2001,6 +2064,7 @@ getstrvalue(Value v) { char *s, **ss; char buf[BDIGBUFSIZE]; + int len; if (!v) return hcalloc(1); @@ -2027,7 +2091,7 @@ getstrvalue(Value v) else { if (v->start < 0) v->start += arrlen(ss); - s = (v->start >= arrlen(ss) || v->start < 0) ? + s = (arrlen_le(ss, v->start) || v->start < 0) ? (char *) hcalloc(1) : ss[v->start]; } return s; @@ -2177,23 +2241,27 @@ getstrvalue(Value v) if (v->start == 0 && v->end == -1) return s; + len = strlen(s); if (v->start < 0) { - v->start += strlen(s); + v->start += len; if (v->start < 0) v->start = 0; } if (v->end < 0) { - v->end += strlen(s); + v->end += len; if (v->end >= 0) { char *eptr = s + v->end; if (*eptr) v->end += MB_METACHARLEN(eptr); } } - s = (v->start > (int)strlen(s)) ? dupstring("") : dupstring(s + v->start); + + s = (v->start > len) ? dupstring("") : + dupstring_wlen(s + v->start, len - v->start); + if (v->end <= v->start) s[0] = '\0'; - else if (v->end - v->start <= (int)strlen(s)) + else if (v->end - v->start <= len - v->start) s[v->end - v->start] = '\0'; return s; @@ -2226,14 +2294,31 @@ getarrvalue(Value v) v->start += arrlen(s); if (v->end < 0) v->end += arrlen(s) + 1; - if (v->start > arrlen(s) || v->start < 0) - s = arrdup(nular); - else - s = arrdup(s + v->start); - if (v->end <= v->start) - s[0] = NULL; - else if (v->end - v->start <= arrlen(s)) - s[v->end - v->start] = NULL; + + /* Null if 1) array too short, 2) index still negative */ + if (v->end <= v->start) { + s = arrdup_max(nular, 0); + } + else if (v->start < 0) { + s = arrdup_max(nular, 1); + } + else if (arrlen_le(s, v->start)) { + /* Handle $ary[i,i] consistently for any $i > $#ary + * and $ary[i,j] consistently for any $j > $i > $#ary + */ + s = arrdup_max(nular, v->end - (v->start + 1)); + } + else { + /* Copy to a point before the end of the source array: + * arrdup_max will copy at most v->end - v->start elements, + * starting from v->start element. Original code said: + * s[v->end - v->start] = NULL + * which means that there are exactly the same number of + * elements as the value of the above *0-based* index. + */ + s = arrdup_max(s + v->start, v->end - v->start); + } + return s; } @@ -2362,10 +2447,11 @@ assignstrvalue(Value v, char *val, int flags) v->pm->width = strlen(val); } else { char *z, *x; - int zlen; + int zlen, vlen, newsize; + + z = v->pm->gsu.s->getfn(v->pm); + zlen = strlen(z); - z = dupstring(v->pm->gsu.s->getfn(v->pm)); - zlen = strlen(z); if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS)) v->start--, v->end--; if (v->start < 0) { @@ -2395,12 +2481,34 @@ assignstrvalue(Value v, char *val, int flags) } else if (v->end > zlen) v->end = zlen; - x = (char *) zalloc(v->start + strlen(val) + zlen - v->end + 1); - strncpy(x, z, v->start); - strcpy(x + v->start, val); - strcat(x + v->start, z + v->end); - v->pm->gsu.s->setfn(v->pm, x); - zsfree(val); + + vlen = strlen(val); + /* Characters preceding start index + + characters of what is assigned + + characters following end index */ + newsize = v->start + vlen + (zlen - v->end); + + /* Does new size differ? */ + if (newsize != zlen || v->pm->gsu.s->setfn != strsetfn) { + x = (char *) zalloc(newsize + 1); + strncpy(x, z, v->start); + strcpy(x + v->start, val); + strcat(x + v->start, z + v->end); + v->pm->gsu.s->setfn(v->pm, x); + } else { + Param pm = v->pm; + /* Size doesn't change, can limit actions to only + * overwriting bytes in already allocated string */ + strncpy(z + v->start, val, vlen); + /* Implement remainder of strsetfn */ + if (!(pm->node.flags & PM_HASHELEM) && + ((pm->node.flags & PM_NAMEDDIR) || + isset(AUTONAMEDIRS))) { + pm->node.flags |= PM_NAMEDDIR; + adduserdir(pm->node.nam, z, 0, 0); + } + } + zsfree(val); } break; case PM_INTEGER: @@ -2580,23 +2688,36 @@ setarrvalue(Value v, char **val) v->end = v->start; post_assignment_length = v->start + arrlen(val); - if (v->end <= pre_assignment_length) - post_assignment_length += pre_assignment_length - v->end + 1; + if (v->end < pre_assignment_length) { + /* + * Allocate room for array elements between the end of the slice `v' + * and the original array's end. + */ + post_assignment_length += pre_assignment_length - v->end; + } - p = new = (char **) zshcalloc(sizeof(char *) - * (post_assignment_length + 1)); + p = new = (char **) zalloc(sizeof(char *) + * (post_assignment_length + 1)); for (i = 0; i < v->start; i++) *p++ = i < pre_assignment_length ? ztrdup(*q++) : ztrdup(""); - for (r = val; *r;) - *p++ = ztrdup(*r++); + for (r = val; *r;) { + /* Give away ownership of the string */ + *p++ = *r++; + } if (v->end < pre_assignment_length) for (q = old + v->end; *q;) *p++ = ztrdup(*q++); *p = NULL; + DPUTS2(p - new != post_assignment_length, "setarrvalue: wrong allocation: %d 1= %lu", + post_assignment_length, (unsigned long)(p - new)); + v->pm->gsu.a->setfn(v->pm, new); - freearray(val); + + /* Ownership of all strings has been + * given away, can plainly free */ + free(val); } } @@ -2747,6 +2868,7 @@ assignsparam(char *s, char *val, int flags) zerr("read-only variable: %s", v->pm->node.nam); *ss = '['; zsfree(val); + unqueue_signals(); return NULL; } flags &= ~ASSPM_WARN_CREATE; @@ -2983,7 +3105,6 @@ sethparam(char *s, char **val) return NULL; queue_signals(); if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) { - DPUTS(!v, "BUG: assigning to undeclared associative array"); createparam(t, PM_HASHED); checkcreate = isset(WARNCREATEGLOBAL) && locallevel > forklevel; } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) && @@ -3062,6 +3183,7 @@ setnparam(char *s, mnumber val) if (!(v = getvalue(&vbuf, &t, 1))) { DPUTS(!v, "BUG: value not found for new parameter"); /* errflag |= ERRFLAG_ERROR; */ + unqueue_signals(); return NULL; } if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > forklevel) @@ -3803,8 +3925,8 @@ intsecondsgetfn(UNUSED(Param pm)) gettimeofday(&now, &dummy_tz); - return (zlong)(now.tv_sec - shtimer.tv_sec) + - (zlong)(now.tv_usec - shtimer.tv_usec) / (zlong)1000000; + return (zlong)(now.tv_sec - shtimer.tv_sec - + (now.tv_usec < shtimer.tv_usec ? 1 : 0)); } /* Function to set value of special parameter `SECONDS' */ @@ -3822,7 +3944,7 @@ intsecondssetfn(UNUSED(Param pm), zlong x) shtimer.tv_sec = diff; if ((zlong)shtimer.tv_sec != diff) zwarn("SECONDS truncated on assignment"); - shtimer.tv_usec = 0; + shtimer.tv_usec = now.tv_usec; } /**/ @@ -4157,7 +4279,9 @@ static void argzerosetfn(UNUSED(Param pm), char *x) { if (x) { - if (!isset(POSIXARGZERO)) { + if (isset(POSIXARGZERO)) + zerr("read-only variable: 0"); + else { zsfree(argzero); argzero = ztrdup(x); } @@ -4336,7 +4460,7 @@ void homesetfn(UNUSED(Param pm), char *x) { zsfree(home); - if (x && isset(CHASELINKS) && (home = xsymlink(x))) + if (x && isset(CHASELINKS) && (home = xsymlink(x, 0))) zsfree(x); else home = x ? x : ztrdup(""); @@ -4435,6 +4559,33 @@ terminfosetfn(Param pm, char *x) term_reinit_from_pm(); } +/* Function to get value of special parameter `TERMINFO_DIRS' */ + +/**/ +char * +terminfodirsgetfn(UNUSED(Param pm)) +{ + return zsh_terminfodirs ? zsh_terminfodirs : dupstring(""); +} + +/* Function to set value of special parameter `TERMINFO_DIRS' */ + +/**/ +void +terminfodirssetfn(Param pm, char *x) +{ + zsfree(zsh_terminfodirs); + zsh_terminfodirs = x; + + /* + * terminfo relies on the value being exported before + * we reinitialise the terminal. This is a bit inefficient. + */ + if ((pm->node.flags & PM_EXPORTED) && x) + addenv(pm, x); + + term_reinit_from_pm(); +} /* Function to get value for special parameter `pipestatus' */ /**/ @@ -4670,6 +4821,7 @@ addenv(Param pm, char *value) if (pm->env) zsfree(pm->env); pm->env = newenv; + pm->node.flags |= PM_EXPORTED; #else /* * Under Cygwin we must use putenv() to maintain consistency. @@ -5165,10 +5317,6 @@ printparamvalue(Param p, int printflags) { char *t, **u; - if (p->node.flags & PM_AUTOLOAD) { - putchar('\n'); - return; - } if (printflags & PRINT_KV_PAIR) putchar(' '); else @@ -5252,20 +5400,33 @@ printparamnode(HashNode hn, int printflags) */ printflags |= PRINT_NAMEONLY; } + else if (p->node.flags & PM_EXPORTED) + printflags |= PRINT_NAMEONLY; else return; } + if (p->node.flags & PM_AUTOLOAD) + printflags |= PRINT_NAMEONLY; if (printflags & PRINT_TYPESET) { if ((p->node.flags & (PM_READONLY|PM_SPECIAL)) == - (PM_READONLY|PM_SPECIAL)) { + (PM_READONLY|PM_SPECIAL) || + (p->node.flags & PM_AUTOLOAD)) { /* * It's not possible to restore the state of * these, so don't output. */ return; } - printf("typeset "); + 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 "); + } else if (locallevel) { + printf("typeset -g "); + } else + printf("typeset "); } /* Print the attributes of the parameter */ @@ -5278,7 +5439,9 @@ printparamnode(HashNode hn, int printflags) if (pmptr->flags & PMTF_TEST_LEVEL) { if (p->level) doprint = 1; - } else if (p->node.flags & pmptr->binflag) + } else if ((pmptr->binflag != PM_EXPORTED || p->level || + (p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) && + (p->node.flags & pmptr->binflag)) doprint = 1; if (doprint) { @@ -5290,9 +5453,8 @@ printparamnode(HashNode hn, int printflags) } putchar(pmptr->typeflag); } - } else { + } else printf("%s ", pmptr->string); - } if ((pmptr->flags & PMTF_USE_BASE) && p->base) { printf("%d ", p->base); doneminus = 0; diff --git a/Src/parse.c b/Src/parse.c index 4829e3a6d..50a0d5f9f 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -63,6 +63,12 @@ int isnewlin; /**/ int infor; +/* != 0 if we are after a repeat keyword; if it's nonzero it's a 1-based index + * of the current token from the last-seen command position */ + +/**/ +int inrepeat_; /* trailing underscore because of name clash with Zle/zle_vi.c */ + /* != 0 if parsing arguments of typeset etc. */ /**/ @@ -271,6 +277,7 @@ parse_context_save(struct parse_stack *ps, int toplevel) ps->incasepat = incasepat; ps->isnewlin = isnewlin; ps->infor = infor; + ps->inrepeat_ = inrepeat_; ps->intypeset = intypeset; ps->hdocs = hdocs; @@ -302,9 +309,9 @@ parse_context_restore(const struct parse_stack *ps, int toplevel) incond = ps->incond; inredir = ps->inredir; incasepat = ps->incasepat; - incasepat = ps->incasepat; isnewlin = ps->isnewlin; infor = ps->infor; + inrepeat_ = ps->inrepeat_; intypeset = ps->intypeset; hdocs = ps->hdocs; @@ -447,6 +454,7 @@ init_parse_status(void) * using the lexical analyser for strings as well as here. */ incasepat = incond = inredir = infor = intypeset = 0; + inrepeat_ = 0; incmdpos = 1; } @@ -586,7 +594,7 @@ par_event(int endtok) if (tok == ENDINPUT) return 0; if (tok == endtok) - return 0; + return 1; p = ecadd(0); @@ -709,7 +717,7 @@ set_sublist_code(int p, int type, int flags, int skip, int cmplx) */ /**/ -static int +static void par_list(int *cmplx) { int p, lp = -1, c; @@ -738,19 +746,15 @@ par_list(int *cmplx) goto rec; } else set_list_code(p, (Z_SYNC | Z_END), c); - return 1; } else { ecused--; - if (lp >= 0) { + if (lp >= 0) ecbuf[lp] |= wc_bdata(Z_END); - return 1; - } - return 0; } } /**/ -static int +static void par_list1(int *cmplx) { int p = ecadd(0), c = 0; @@ -758,11 +762,8 @@ par_list1(int *cmplx) if (par_sublist(&c)) { set_list_code(p, (Z_SYNC | Z_END), c); *cmplx |= c; - return 1; - } else { + } else ecused--; - return 0; - } } /* @@ -1404,7 +1405,7 @@ par_if(int *cmplx) } } cmdpop(); - if (xtok == ELSE) { + if (xtok == ELSE || tok == ELSE) { pp = ecadd(0); cmdpush(CS_ELSE); while (tok == SEPER) @@ -1482,6 +1483,7 @@ par_while(int *cmplx) static void par_repeat(int *cmplx) { + /* ### what to do about inrepeat_ here? */ int oecused = ecused, p; p = ecadd(0); @@ -2383,7 +2385,7 @@ par_cond_2(void) s1 = tokstr; dble = (s1 && *s1 == '-' && (!n_testargs - || strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1) + || strspn(s1+1, "abcdefghknoprstuvwxzLONGS") == 1) && !s1[2]); if (tok != STRING) { /* Check first argument for [[ STRING ]] re-interpretation */ @@ -2462,7 +2464,7 @@ par_cond_double(char *a, char *b) { if (a[0] != '-' || !a[1]) COND_ERROR("parse error: condition expected: %s", a); - else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1) { + else if (!a[2] && strspn(a+1, "abcdefgknoprstuvwxzhLONGS") == 1) { ecadd(WCB_COND(a[1], 0)); ecstr(b); } else { @@ -2495,12 +2497,17 @@ par_cond_triple(char *a, char *b, char *c) { int t0; - if ((b[0] == Equals || b[0] == '=') && - (!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2]))) { + if ((b[0] == Equals || b[0] == '=') && !b[1]) { ecadd(WCB_COND(COND_STREQ, 0)); ecstr(a); ecstr(c); ecadd(ecnpats++); + } else if ((b[0] == Equals || b[0] == '=') && + (b[1] == Equals || b[1] == '=') && !b[2]) { + ecadd(WCB_COND(COND_STRDEQ, 0)); + ecstr(a); + ecstr(c); + ecadd(ecnpats++); } else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) { ecadd(WCB_COND(COND_STRNEQ, 0)); ecstr(a); diff --git a/Src/pattern.c b/Src/pattern.c index 72c7d97d5..1f2e94bd9 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -669,12 +669,14 @@ patcompile(char *exp, int inflags, char **endexp) nmeta++; if (nmeta) { char *oldpatout = patout; + ptrdiff_t pd; patadd(NULL, 0, nmeta, 0); /* * Yuk. */ p = (Patprog)patout; - opnd = patout + (opnd - oldpatout); + pd = patout - oldpatout; + opnd += pd; dst = patout + startoff; } @@ -686,6 +688,8 @@ patcompile(char *exp, int inflags, char **endexp) *dst++ = *opnd++; } } + /* Only one string in a PAT_PURES, so now done. */ + break; } } p->size = dst - patout; @@ -1838,7 +1842,8 @@ pattail(long p, long val) /* do pattail, but on operand of first argument; nop if operandless */ /**/ -static void patoptail(long p, long val) +static void +patoptail(long p, long val) { Upat ptr = (Upat)patout + p; int op = P_OP(ptr); @@ -1854,19 +1859,34 @@ static void patoptail(long p, long val) /* * Run a pattern. */ -static char *patinstart; /* Start of input string */ -static char *patinend; /* End of input string */ -static char *patinput; /* String input pointer */ -static char *patinpath; /* Full path for use with ~ exclusions */ -static int patinlen; /* Length of last successful match. +struct rpat { + char *patinstart; /* Start of input string */ + char *patinend; /* End of input string */ + char *patinput; /* String input pointer */ + char *patinpath; /* Full path for use with ~ exclusions */ + int patinlen; /* Length of last successful match. * Includes count of Meta characters. */ -static char *patbeginp[NSUBEXP]; /* Pointer to backref beginnings */ -static char *patendp[NSUBEXP]; /* Pointer to backref ends */ -static int parsfound; /* parentheses (with backrefs) found */ + char *patbeginp[NSUBEXP]; /* Pointer to backref beginnings */ + char *patendp[NSUBEXP]; /* Pointer to backref ends */ + int parsfound; /* parentheses (with backrefs) found */ + + int globdots; /* Glob initial dots? */ +}; + +static struct rpat pattrystate; + +#define patinstart (pattrystate.patinstart) +#define patinend (pattrystate.patinend) +#define patinput (pattrystate.patinput) +#define patinpath (pattrystate.patinpath) +#define patinlen (pattrystate.patinlen) +#define patbeginp (pattrystate.patbeginp) +#define patendp (pattrystate.patendp) +#define parsfound (pattrystate.parsfound) +#define globdots (pattrystate.globdots) -static int globdots; /* Glob initial dots? */ /* * Character functions operating on unmetafied strings. @@ -2302,7 +2322,7 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, * Either we are testing against a pure string, * or we can match anything at all. */ - int ret, pstrlen; + int pstrlen; char *pstr; if (patstralloc->alloced) { @@ -2404,11 +2424,7 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, } } } - - return ret; } else { - int q = queue_signal_level(); - /* * Test for a `must match' string, unless we're scanning for a match * in which case we don't need to do this each time. @@ -2440,9 +2456,8 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, ret = 0; } } - if (!ret) { + if (!ret) return 0; - } patglobflags = prog->globflags; if (!(patflags & PAT_FILE)) { @@ -2454,8 +2469,6 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, patinput = patinstart; - dont_queue_signals(); - if (patmatch((Upat)progstr)) { /* * we were lazy and didn't save the globflags if an exclusion @@ -2594,11 +2607,9 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, ret = 1; } else ret = 0; - - restore_queue_signals(q); - - return ret; } + + return ret; } /* @@ -2674,6 +2685,26 @@ patmatch(Upat prog) int savglobflags, op, no, min, fail = 0, saverrsfound; zrange_t from, to, comp; patint_t nextch; + int q = queue_signal_level(); + + /* + * To avoid overhead of saving state if there are no queued signals + * waiting, we pierce the signals.h veil and examine queue state. + */ +#define check_for_signals() do if (queue_front != queue_rear) { \ + int savpatflags = patflags, savpatglobflags = patglobflags; \ + char *savexactpos = exactpos, *savexactend = exactend; \ + struct rpat savpattrystate = pattrystate; \ + dont_queue_signals(); \ + restore_queue_signals(q); \ + exactpos = savexactpos; \ + exactend = savexactend; \ + patflags = savpatflags; \ + patglobflags = savpatglobflags; \ + pattrystate = savpattrystate; \ + } while (0) + + check_for_signals(); while (scan && !errflag) { next = PATNEXT(scan); @@ -3508,6 +3539,9 @@ patmatch(Upat prog) } scan = next; + + /* Allow handlers to run once per loop */ + check_for_signals(); } return 0; diff --git a/Src/prompt.c b/Src/prompt.c index be067ee7e..ee77c8bc8 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -395,11 +395,11 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) test = 1; break; case 'v': - if (arrlen(psvar) >= arg) + if (arrlen_ge(psvar, arg)) test = 1; break; case 'V': - if (arrlen(psvar) >= arg) { + if (arrlen_ge(psvar, arg)) { if (*psvar[(arg ? arg : 1) - 1]) test = 1; } @@ -491,8 +491,10 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) if (!arg) arg++; queue_signals(); - if (!(hostnam = getsparam("HOST"))) + if (!(hostnam = getsparam("HOST"))) { + unqueue_signals(); break; + } if (arg < 0) { for (ss = hostnam + strlen(hostnam); ss > hostnam; ss--) if (ss[-1] == '.' && !++arg) @@ -523,8 +525,6 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) break; case 'b': txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE); - txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT); - txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE); txtunset(TXTBOLDFACE); tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY); break; @@ -542,7 +542,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) arg = parsecolorchar(arg, 1); if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) { txtchangeset(txtchangep, arg & TXT_ATTR_FG_ON_MASK, - TXTNOFGCOLOUR); + TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK); + txtunset(TXT_ATTR_FG_COL_MASK); txtset(arg & TXT_ATTR_FG_ON_MASK); set_colour_attribute(arg, COL_SEQ_FG, TSC_PROMPT); break; @@ -557,7 +558,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) arg = parsecolorchar(arg, 0); if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) { txtchangeset(txtchangep, arg & TXT_ATTR_BG_ON_MASK, - TXTNOBGCOLOUR); + TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK); + txtunset(TXT_ATTR_BG_COL_MASK); txtset(arg & TXT_ATTR_BG_ON_MASK); set_colour_attribute(arg, COL_SEQ_BG, TSC_PROMPT); break; @@ -736,7 +738,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) arg = 1; else if (arg < 0) arg += arrlen(psvar) + 1; - if (arg > 0 && arrlen(psvar) >= arg) + if (arg > 0 && arrlen_ge(psvar, arg)) stradd(psvar[arg - 1]); break; case 'E': @@ -1041,6 +1043,10 @@ tsetcap(int cap, int flags) tsetcap(TCSTANDOUTBEG, flags); if (txtisset(TXTUNDERLINE)) tsetcap(TCUNDERLINEBEG, flags); + if (txtisset(TXTFGCOLOUR)) + set_colour_attribute(txtattrmask, COL_SEQ_FG, TSC_PROMPT); + if (txtisset(TXTBGCOLOUR)) + set_colour_attribute(txtattrmask, COL_SEQ_BG, TSC_PROMPT); } } } @@ -1831,7 +1837,7 @@ struct colour_sequences { char *end; /* Escape sequence terminator */ char *def; /* Code to reset default colour */ }; -struct colour_sequences fg_bg_sequences[2]; +static struct colour_sequences fg_bg_sequences[2]; /* * We need a buffer for colour sequence composition. It may diff --git a/Src/signals.c b/Src/signals.c index aa0b5aaa7..9e05add09 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -55,6 +55,15 @@ mod_export Eprog siglists[VSIGCOUNT]; /**/ mod_export int nsigtrapped; +/* + * Flag that exit trap has been set in POSIX mode. + * The setter's expectation is therefore that it is run + * on programme exit, not function exit. + */ + +/**/ +static int exit_trap_posix; + /* Variables used by signal queueing */ /**/ @@ -63,6 +72,10 @@ mod_export int queueing_enabled, queue_front, queue_rear; mod_export int signal_queue[MAX_QUEUE_SIZE]; /**/ mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE]; +#ifdef DEBUG +/**/ +mod_export int queue_in; +#endif /* Variables used by trap queueing */ @@ -637,6 +650,7 @@ zhandler(int sig) inerrflush(); check_cursh_sig(SIGINT); } + lastval = 128 + SIGINT; } break; @@ -714,7 +728,7 @@ killjb(Job jn, int sig) { Process pn; int err = 0; - + if (jobbing) { if (jn->stat & STAT_SUPERJOB) { if (sig == SIGCONT) { @@ -722,11 +736,21 @@ killjb(Job jn, int sig) if (killpg(pn->pid, sig) == -1) if (kill(pn->pid, sig) == -1 && errno != ESRCH) err = -1; - + + /* + * Note this does not kill the last process, + * which is assumed to be the one controlling the + * subjob, i.e. the forked zsh that was originally + * list_pipe_pid... + */ for (pn = jn->procs; pn->next; pn = pn->next) if (kill(pn->pid, sig) == -1 && errno != ESRCH) err = -1; + /* + * ...we only continue that once the external processes + * currently associated with the subjob are finished. + */ if (!jobtab[jn->other].procs && pn) if (kill(pn->pid, sig) == -1 && errno != ESRCH) err = -1; @@ -735,7 +759,7 @@ killjb(Job jn, int sig) } if (killpg(jobtab[jn->other].gleader, sig) == -1 && errno != ESRCH) err = -1; - + if (killpg(jn->gleader, sig) == -1 && errno != ESRCH) err = -1; @@ -755,7 +779,7 @@ killjb(Job jn, int sig) * at once, so just use a linked list. */ struct savetrap { - int sig, flags, local; + int sig, flags, local, posix; void *list; }; @@ -774,6 +798,7 @@ dosavetrap(int sig, int level) st = (struct savetrap *)zalloc(sizeof(*st)); st->sig = sig; st->local = level; + st->posix = (sig == SIGEXIT) ? exit_trap_posix : 0; if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) { /* * Get the old function: this assumes we haven't added @@ -867,12 +892,21 @@ settrap(int sig, Eprog l, int flags) sig != SIGCHLD) install_handler(sig); } + sigtrapped[sig] |= flags; /* * Note that introducing the locallevel does not affect whether * sigtrapped[sig] is zero or not, i.e. a test without a mask * works just the same. */ - sigtrapped[sig] |= (locallevel << ZSIG_SHIFT) | flags; + if (sig == SIGEXIT) { + /* Make POSIX behaviour of EXIT trap sticky */ + exit_trap_posix = isset(POSIXTRAPS); + /* POSIX exit traps are not local. */ + if (!exit_trap_posix) + sigtrapped[sig] |= (locallevel << ZSIG_SHIFT); + } + else + sigtrapped[sig] |= (locallevel << ZSIG_SHIFT); unqueue_signals(); return 0; } @@ -906,6 +940,11 @@ removetrap(int sig) * Note that we save the trap here even if there isn't an existing * one, to aid in removing this one. However, if there's * already one at the current locallevel we just overwrite it. + * + * Note we save EXIT traps based on the *current* setting of + * POSIXTRAPS --- so if there is POSIX EXIT trap set but + * we are in native mode it can be saved, replaced by a function + * trap, and then restored. */ if (!dontsavetrap && (sig == SIGEXIT ? !isset(POSIXTRAPS) : isset(LOCALTRAPS)) && @@ -935,6 +974,8 @@ removetrap(int sig) #endif sig != SIGCHLD) signal_default(sig); + if (sig == SIGEXIT) + exit_trap_posix = 0; /* * At this point we free the appropriate structs. If we don't @@ -978,7 +1019,7 @@ starttrapscope(void) * so give it the next higher one. dosavetrap() is called * automatically where necessary. */ - if (sigtrapped[SIGEXIT] && !isset(POSIXTRAPS)) { + if (sigtrapped[SIGEXIT] && !exit_trap_posix) { locallevel++; unsettrap(SIGEXIT); locallevel--; @@ -1005,7 +1046,7 @@ endtrapscope(void) * Don't do this inside another trap. */ if (!intrap && - !isset(POSIXTRAPS) && (exittr = sigtrapped[SIGEXIT])) { + !exit_trap_posix && (exittr = sigtrapped[SIGEXIT])) { if (exittr & ZSIG_FUNC) { exitfn = removehashnode(shfunctab, "TRAPEXIT"); } else { @@ -1031,7 +1072,9 @@ endtrapscope(void) if (st->flags & ZSIG_FUNC) settrap(sig, NULL, ZSIG_FUNC); else - settrap(sig, (Eprog) st->list, 0); + settrap(sig, (Eprog) st->list, 0); + if (sig == SIGEXIT) + exit_trap_posix = st->posix; dontsavetrap--; /* * counting of nsigtrapped should presumably be handled @@ -1042,16 +1085,26 @@ endtrapscope(void) if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC) shfunctab->addnode(shfunctab, ((Shfunc)st->list)->node.nam, (Shfunc) st->list); - } else if (sigtrapped[sig]) - unsettrap(sig); + } else if (sigtrapped[sig]) { + /* + * Don't restore the old state if someone has set a + * POSIX-style exit trap --- allow this to propagate. + */ + if (sig != SIGEXIT || !exit_trap_posix) + unsettrap(sig); + } zfree(st, sizeof(*st)); } } if (exittr) { - if (!isset(POSIXTRAPS)) - dotrapargs(SIGEXIT, &exittr, exitfn); + /* + * We already made sure this wasn't set as a POSIX exit trap. + * We respect the user's intention when the trap in question + * was set. + */ + dotrapargs(SIGEXIT, &exittr, exitfn); if (exittr & ZSIG_FUNC) shfunctab->freenode((HashNode)exitfn); else diff --git a/Src/signals.h b/Src/signals.h index d68096891..1904f4326 100644 --- a/Src/signals.h +++ b/Src/signals.h @@ -82,8 +82,6 @@ #define MAX_QUEUE_SIZE 128 -#define queue_signals() (queueing_enabled++) - #define run_queued_signals() do { \ while (queue_front != queue_rear) { /* while signals in queue */ \ sigset_t oset; \ @@ -94,12 +92,35 @@ } \ } while (0) +#ifdef DEBUG + +#define queue_signals() (queue_in++, queueing_enabled++) + #define unqueue_signals() do { \ DPUTS(!queueing_enabled, "BUG: unqueue_signals called but not queueing"); \ + --queue_in; \ if (!--queueing_enabled) run_queued_signals(); \ } while (0) -#define queue_signal_level() queueing_enabled +#define dont_queue_signals() do { \ + queue_in = queueing_enabled; \ + queueing_enabled = 0; \ + run_queued_signals(); \ +} while (0) + +#define restore_queue_signals(q) do { \ + DPUTS2(queueing_enabled && queue_in != q, \ + "BUG: q = %d != queue_in = %d", q, queue_in); \ + queue_in = (queueing_enabled = (q)); \ +} while (0) + +#else /* !DEBUG */ + +#define queue_signals() (queueing_enabled++) + +#define unqueue_signals() do { \ + if (!--queueing_enabled) run_queued_signals(); \ +} while (0) #define dont_queue_signals() do { \ queueing_enabled = 0; \ @@ -108,6 +129,10 @@ #define restore_queue_signals(q) (queueing_enabled = (q)) +#endif /* DEBUG */ + +#define queue_signal_level() queueing_enabled + #ifdef BSD_SIGNALS #define signal_block(S) sigblock(S) #else diff --git a/Src/string.c b/Src/string.c index 04e7446c9..a8da14fe0 100644 --- a/Src/string.c +++ b/Src/string.c @@ -41,6 +41,36 @@ dupstring(const char *s) return t; } +/* Duplicate string on heap when length is known */ + +/**/ +mod_export char * +dupstring_wlen(const char *s, unsigned len) +{ + char *t; + + if (!s) + return NULL; + t = (char *) zhalloc(len + 1); + strcpy(t, s); + return t; +} + +/* Duplicate string on heap, returning length of string */ + +/**/ +mod_export char * +dupstring_glen(const char *s, unsigned *len_ret) +{ + char *t; + + if (!s) + return NULL; + t = (char *) zhalloc((*len_ret = strlen((char *)s)) + 1); + strcpy(t, s); + return t; +} + /**/ mod_export char * ztrdup(const char *s) diff --git a/Src/subst.c b/Src/subst.c index bb1dd8939..06d2c9ea9 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -145,8 +145,12 @@ stringsubstquote(char *strstart, char **pstrdpos) strret = dyncat(strstart, strsub); } else if (strdpos[len]) strret = dyncat(strsub, strdpos + len); - else + else if (*strsub) strret = strsub; + else { + /* This ensures a $'' doesn't get elided. */ + strret = dupstring(nulstring); + } *pstrdpos = strret + (strdpos - strstart) + strlen(strsub); @@ -407,7 +411,9 @@ globlist(LinkList list, int nountok) next = nextnode(node); zglob(list, node, nountok); } - if (badcshglob == 1) + if (noerrs) + badcshglob = 0; + else if (badcshglob == 1) zerr("no match"); } @@ -623,7 +629,7 @@ equalsubstr(char *str, int assign, int nomatch) cmdstr = dupstrpfx(str, pp-str); untokenize(cmdstr); remnulargs(cmdstr); - if (!(cnam = findcmd(cmdstr, 1))) { + if (!(cnam = findcmd(cmdstr, 1, 0))) { if (nomatch) zerr("%s not found", cmdstr); return NULL; @@ -680,19 +686,19 @@ filesubstr(char **namptr, int assign) *namptr = dyncat(ds, ptr); return 1; } else if ((ptr = itype_end(str+1, IUSER, 0)) != str+1) { /* ~foo */ - char *hom, save; + char *untok, *hom; - save = *ptr; - if (!isend(save)) + if (!isend(*ptr)) return 0; - *ptr = 0; - if (!(hom = getnameddir(++str))) { - if (isset(NOMATCH)) - zerr("no such user or named directory: %s", str); - *ptr = save; + untok = dupstring(++str); + untok[ptr-str] = 0; + untokenize(untok); + + if (!(hom = getnameddir(untok))) { + if (isset(NOMATCH) && isset(EXECOPT)) + zerr("no such user or named directory: %s", untok); return 0; } - *ptr = save; *namptr = dyncat(hom, ptr); return 1; } @@ -2364,7 +2370,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * This is the inner handling for the case referred to above * where we have something like ${${(P)name}...}. * - * Treat this as as a normal value here; all transformations on + * Treat this as a normal value here; all transformations on * result are in outer instance. */ aspar = 0; @@ -2544,12 +2550,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * necessary joining of arrays until this point * to avoid the multsub() horror. */ - int tmplen = arrlen(v->pm->gsu.a->getfn(v->pm)); - if (v->start < 0) + /* arrlen() is expensive, so only compute it if needed. */ + int tmplen = -1; + + if (v->start < 0) { + tmplen = arrlen(v->pm->gsu.a->getfn(v->pm)); v->start += tmplen + ((v->flags & VALFLAG_INV) ? 1 : 0); - if (!(v->flags & VALFLAG_INV) && - (v->start >= tmplen || v->start < 0)) + } + if (!(v->flags & VALFLAG_INV)) + if (v->start < 0 || + (tmplen != -1 + ? v->start >= tmplen + : arrlen_le(v->pm->gsu.a->getfn(v->pm), v->start))) vunset = 1; } if (!vunset) { @@ -2886,6 +2899,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, aval = paramvalarr(pm->gsu.h->getfn(pm), hkeys|hvals); } else setaparam(idbeg, a); + isarr = 1; } else { untokenize(val); setsparam(idbeg, ztrdup(val)); @@ -2910,6 +2924,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, if (isset(EXECOPT)) { *idend = '\0'; zerr("%s: %s", idbeg, *s ? s : "parameter not set"); + /* + * In interactive shell we need to return to + * top-level prompt --- don't clear this error + * after handling a command as we do with + * most errors. + */ + errflag |= ERRFLAG_HARD; if (!interact) { if (mypid == getpid()) { /* @@ -3443,13 +3464,24 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * exception is that ${name:-word} and ${name:+word} will have already * done any requested splitting of the word value with quoting preserved. */ - if (ssub || (spbreak && isarr >= 0) || spsep || sep) { + if (ssub || spbreak || spsep || sep) { + int force_split = !ssub && (spbreak || spsep); if (isarr) { - val = sepjoin(aval, sep, 1); - isarr = 0; - ms_flags = 0; + /* sep non-null here means F or j flag, force join */ + if (nojoin == 0 || sep) { + val = sepjoin(aval, sep, 1); + isarr = 0; + ms_flags = 0; + } else if (force_split && (spsep || nojoin == 2)) { + /* Hack to simulate splitting individual elements: + * forced joining as previously determined, or + * join on what we later use to forcibly split + */ + val = sepjoin(aval, (nojoin == 1 ? NULL : spsep), 1); + isarr = 0; + } } - if (!ssub && (spbreak || spsep)) { + if (force_split && !isarr) { aval = sepsplit(val, spsep, 0, 1); if (!aval || !aval[0]) val = dupstring(""); @@ -3516,7 +3548,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } /* * TODO: It would be really quite nice to abstract the - * isarr and !issarr code into a function which gets + * isarr and !isarr code into a function which gets * passed a pointer to a function with the effect of * the promptexpand bit. Then we could use this for * a lot of stuff and bury val/aval/isarr inside a structure @@ -3592,20 +3624,20 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, char *tmp; for (; *ap; ap++) { - tmp = quotestring(*ap, NULL, quotetype); + tmp = quotestring(*ap, quotetype); sl = strlen(tmp); *ap = (char *) zhalloc(pre + sl + post + 1); strcpy((*ap) + pre, tmp); if (pre) ap[0][pre - 1] = ap[0][pre + sl] = (quotetype != QT_DOUBLE ? '\'' : '"'); - ap[0][pre + sl + 1] = '\0'; + ap[0][pre + sl + post] = '\0'; if (quotetype == QT_DOLLARS) ap[0][0] = '$'; } } else for (; *ap; ap++) - *ap = quotestring(*ap, NULL, QT_BACKSLASH_SHOWNULL); + *ap = quotestring(*ap, QT_BACKSLASH_SHOWNULL); } else { int one = noerrs, oef = errflag, haserr = 0; @@ -3635,18 +3667,18 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } else if (quotetype > QT_BACKSLASH) { int sl; char *tmp; - tmp = quotestring(val, NULL, quotetype); + tmp = quotestring(val, quotetype); sl = strlen(tmp); - val = (char *) zhalloc(pre + sl + 2); + val = (char *) zhalloc(pre + sl + post + 1); strcpy(val + pre, tmp); if (pre) val[pre - 1] = val[pre + sl] = (quotetype != QT_DOUBLE ? '\'' : '"'); - val[pre + sl + 1] = '\0'; + val[pre + sl + post] = '\0'; if (quotetype == QT_DOLLARS) val[0] = '$'; } else - val = quotestring(val, NULL, QT_BACKSLASH_SHOWNULL); + val = quotestring(val, QT_BACKSLASH_SHOWNULL); } else { int one = noerrs, oef = errflag, haserr; @@ -4011,6 +4043,19 @@ arithsubst(char *a, char **bptr, char *rest) return t; } +/* This function implements colon modifiers. + * + * STR is an in/out parameter. On entry it is the string (e.g., path) + * to modified. On return it is the modified path. + * + * PTR is an in/out parameter. On entry it contains the string of colon + * modifiers. On return it points past the last recognised modifier. + * + * Example: + * ENTRY: *str is "." *ptr is ":AN" + * RETURN: *str is "/home/foobar" (equal to $PWD) *ptr points to the "N" + */ + /**/ void modify(char **str, char **ptr) @@ -4046,6 +4091,7 @@ modify(char **str, char **ptr) case 'u': case 'q': case 'Q': + case 'P': c = **ptr; break; @@ -4237,7 +4283,7 @@ modify(char **str, char **ptr) subst(©, hsubl, hsubr, gbal); break; case 'q': - copy = quotestring(copy, NULL, QT_BACKSLASH_SHOWNULL); + copy = quotestring(copy, QT_BACKSLASH_SHOWNULL); break; case 'Q': { @@ -4252,6 +4298,12 @@ modify(char **str, char **ptr) untokenize(copy); } break; + case 'P': + if (*copy != '/') { + copy = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", copy); + } + copy = xsymlink(copy, 1); + break; } tc = *tt; *tt = '\0'; @@ -4313,7 +4365,7 @@ modify(char **str, char **ptr) subst(str, hsubl, hsubr, gbal); break; case 'q': - *str = quotestring(*str, NULL, QT_BACKSLASH); + *str = quotestring(*str, QT_BACKSLASH); break; case 'Q': { @@ -4328,6 +4380,12 @@ modify(char **str, char **ptr) untokenize(*str); } break; + case 'P': + if (**str != '/') { + *str = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", *str); + } + *str = xsymlink(*str, 1); + break; } } if (rec < 0) { diff --git a/Src/text.c b/Src/text.c index 04acd2aac..3658b1bc6 100644 --- a/Src/text.c +++ b/Src/text.c @@ -46,8 +46,8 @@ int text_expand_tabs; * et seq. in zsh.h. */ static const char *cond_binary_ops[] = { - "=", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", - "-ne", "-lt", "-gt", "-le", "-ge", "=~" + "=", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", + "-ne", "-lt", "-gt", "-le", "-ge", "=~", NULL }; static char *tptr, *tbuf, *tlim, *tpending; @@ -934,6 +934,7 @@ gettext2(Estate state) taddstr(" "); taddstr(ecgetstr(state, EC_NODUP, NULL)); if (ctype == COND_STREQ || + ctype == COND_STRDEQ || ctype == COND_STRNEQ) state->pc++; } else { @@ -1068,11 +1069,11 @@ getredirs(LinkList redirs) */ if (!has_token(f->name)) { taddchr('\''); - taddstr(quotestring(f->name, NULL, QT_SINGLE)); + taddstr(quotestring(f->name, QT_SINGLE)); taddchr('\''); } else { taddchr('"'); - taddstr(quotestring(f->name, NULL, QT_DOUBLE)); + taddstr(quotestring(f->name, QT_DOUBLE)); taddchr('"'); } if (sav) diff --git a/Src/utils.c b/Src/utils.c index fd0bab320..7f3ddad40 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -56,12 +56,12 @@ typedef struct widechar_array *Widechar_array; * The wordchars variable turned into a wide character array. * This is much more convenient for testing. */ -struct widechar_array wordchars_wide; +static struct widechar_array wordchars_wide; /* * The same for the separators (IFS) array. */ -struct widechar_array ifs_wide; +static struct widechar_array ifs_wide; /* Function to set one of the above from the multibyte array */ @@ -84,7 +84,15 @@ set_widearray(char *mb_array, Widechar_array wca) mb_charinit(); while (*mb_array) { - int mblen = mb_metacharlenconv(mb_array, &wci); + int mblen; + + if (STOUC(*mb_array) <= 0x7f) { + mb_array++; + *wcptr++ = (wchar_t)*mb_array; + continue; + } + + mblen = mb_metacharlenconv(mb_array, &wci); if (!mblen) break; @@ -133,9 +141,11 @@ zwarning(const char *cmd, const char *fmt, va_list ap) if (isatty(2)) zleentry(ZLE_CMD_TRASH); + char *prefix = scriptname ? scriptname : (argzero ? argzero : ""); + if (cmd) { if (unset(SHINSTDIN) || locallevel) { - nicezputs(scriptname ? scriptname : argzero, stderr); + nicezputs(prefix, stderr); fputc((unsigned char)':', stderr); } nicezputs(cmd, stderr); @@ -147,8 +157,7 @@ zwarning(const char *cmd, const char *fmt, va_list ap) * program/script is running. It's also set in shell functions, * so test locallevel, too. */ - nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" : - scriptname ? scriptname : argzero, stderr); + nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" : prefix, stderr); fputc((unsigned char)':', stderr); } @@ -169,12 +178,12 @@ VA_DCL errflag |= ERRFLAG_ERROR; return; } + errflag |= ERRFLAG_ERROR; VA_START(ap, fmt); VA_GET_ARG(ap, fmt, const char *); zwarning(NULL, fmt, ap); va_end(ap); - errflag |= ERRFLAG_ERROR; } /**/ @@ -188,13 +197,13 @@ VA_DCL if (errflag || noerrs) return; + errflag |= ERRFLAG_ERROR; VA_START(ap, fmt); VA_GET_ARG(ap, cmd, const char *); VA_GET_ARG(ap, fmt, const char *); zwarning(cmd, fmt, ap); va_end(ap); - errflag |= ERRFLAG_ERROR; } /**/ @@ -800,9 +809,9 @@ findpwd(char *s) char *t; if (*s == '/') - return xsymlink(s); + return xsymlink(s, 0); s = tricat((pwd[1]) ? pwd : "", "/", s); - t = xsymlink(s); + t = xsymlink(s, 0); zsfree(s); return t; } @@ -836,7 +845,7 @@ ispwd(char *s) return 0; } -static char xbuf[PATH_MAX*2]; +static char xbuf[PATH_MAX*2+1]; /**/ static char ** @@ -875,7 +884,7 @@ static int xsymlinks(char *s, int full) { char **pp, **opp; - char xbuf2[PATH_MAX*3], xbuf3[PATH_MAX*2]; + char xbuf2[PATH_MAX*3+1], xbuf3[PATH_MAX*2+1]; int t0, ret = 0; zulong xbuflen = strlen(xbuf); @@ -968,11 +977,13 @@ xsymlinks(char *s, int full) /* * expand symlinks in s, and remove other weird things: * note that this always expands symlinks. + * + * 'heap' indicates whether to malloc() or allocate on the heap. */ /**/ char * -xsymlink(char *s) +xsymlink(char *s, int heap) { if (*s != '/') return NULL; @@ -980,8 +991,8 @@ xsymlink(char *s) if (xsymlinks(s + 1, 1) < 0) zwarn("path expansion failed, using root directory"); if (!*xbuf) - return ztrdup("/"); - return ztrdup(xbuf); + return heap ? dupstring("/") : ztrdup("/"); + return heap ? dupstring(xbuf) : ztrdup(xbuf); } /**/ @@ -992,7 +1003,7 @@ print_if_link(char *s, int all) *xbuf = '\0'; if (all) { char *start = s + 1; - char xbuflink[PATH_MAX]; + char xbuflink[PATH_MAX+1]; for (;;) { if (xsymlinks(start, 0) > 0) { printf(" -> "); @@ -1044,9 +1055,9 @@ substnamedir(char *s) Nameddir d = finddir(s); if (!d) - return quotestring(s, NULL, QT_BACKSLASH); + return quotestring(s, QT_BACKSLASH); return zhtricat("~", d->node.nam, quotestring(s + strlen(d->dir), - NULL, QT_BACKSLASH)); + QT_BACKSLASH)); } @@ -1129,7 +1140,7 @@ finddir(char *s) if(homenode.diff==1) homenode.diff = 0; if(!finddir_full) - finddir_full = zalloc(ffsz = PATH_MAX); + finddir_full = zalloc(ffsz = PATH_MAX+1); finddir_full[0] = 0; return finddir_last = NULL; } @@ -1156,7 +1167,7 @@ finddir(char *s) scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0); ares = subst_string_by_hook("zsh_directory_name", "d", finddir_full); - if (ares && arrlen(ares) >= 2 && + if (ares && arrlen_ge(ares, 2) && (len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) { /* better duplicate this string since it's come from REPLY */ finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir)); @@ -1259,7 +1270,7 @@ getnameddir(char *name) /* Retrieve an entry from the password table/database for this user. */ struct passwd *pw; if ((pw = getpwnam(name))) { - char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir) + char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir, 0) : ztrdup(pw->pw_dir); if (dir) { adduserdir(name, dir, ND_USERNAME, 1); @@ -1633,7 +1644,7 @@ checkmailpath(char **s) } else if (S_ISDIR(st.st_mode)) { LinkList l; DIR *lock = opendir(unmeta(*s)); - char buf[PATH_MAX * 2], **arr, **ap; + char buf[PATH_MAX * 2 + 1], **arr, **ap; int ct = 1; if (lock) { @@ -2161,6 +2172,7 @@ gettempfile(const char *prefix, int use_heap, char **tempname) #if HAVE_MKSTEMP char *suffix = prefix ? ".XXXXXX" : "XXXXXX"; + queue_signals(); if (!prefix && !(prefix = getsparam("TMPPREFIX"))) prefix = DEFAULT_TMPPREFIX; if (use_heap) @@ -2177,6 +2189,7 @@ gettempfile(const char *prefix, int use_heap, char **tempname) #else int failures = 0; + queue_signals(); do { if (!(fn = gettempname(prefix, use_heap))) { fd = -1; @@ -2190,6 +2203,8 @@ gettempfile(const char *prefix, int use_heap, char **tempname) } while (errno == EEXIST && ++failures < 16); #endif *tempname = fn; + + unqueue_signals(); return fd; } @@ -2279,6 +2294,46 @@ arrlen(char **s) return count; } +/* Return TRUE iff arrlen(s) >= lower_bound, but more efficiently. */ + +/**/ +mod_export char +arrlen_ge(char **s, unsigned lower_bound) +{ + while (lower_bound--) + if (!*s++) + return 0 /* FALSE */; + + return 1 /* TRUE */; +} + +/* Return TRUE iff arrlen(s) > lower_bound, but more efficiently. */ + +/**/ +mod_export char +arrlen_gt(char **s, unsigned lower_bound) +{ + return arrlen_ge(s, 1+lower_bound); +} + +/* Return TRUE iff arrlen(s) <= upper_bound, but more efficiently. */ + +/**/ +mod_export char +arrlen_le(char **s, unsigned upper_bound) +{ + return arrlen_lt(s, 1+upper_bound); +} + +/* Return TRUE iff arrlen(s) < upper_bound, but more efficiently. */ + +/**/ +mod_export char +arrlen_lt(char **s, unsigned upper_bound) +{ + return !arrlen_ge(s, upper_bound); +} + /* Skip over a balanced pair of parenthesis. */ /**/ @@ -2533,7 +2588,7 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds) #endif #endif - if (fd >= 0 && ret < 0) { + if (fd >= 0 && ret < 0 && !errflag) { /* * Final attempt: set non-blocking read and try to read a character. * Praise Bill, this works under Cygwin (nothing else seems to). @@ -2633,13 +2688,36 @@ zsleep_random(long max_us, time_t end_time) int checkrmall(char *s) { + DIR *rmd; + int count = 0; if (!shout) return 1; - fprintf(shout, "zsh: sure you want to delete all the files in "); if (*s != '/') { - nicezputs(pwd[1] ? pwd : "", shout); - fputc('/', shout); - } + if (pwd[1]) + s = zhtricat(pwd, "/", s); + else + s = dyncat("/", s); + } + const int max_count = 100; + if ((rmd = opendir(unmeta(s)))) { + int ignoredots = !isset(GLOBDOTS); + while (zreaddir(rmd, ignoredots)) { + count++; + if (count > max_count) + break; + } + closedir(rmd); + } + if (count > max_count) + fprintf(shout, "zsh: sure you want to delete more than %d files in ", + max_count); + else if (count == 1) + fprintf(shout, "zsh: sure you want to delete the only file in "); + else if (count > 0) + fprintf(shout, "zsh: sure you want to delete all %d files in ", + count); + else + fprintf(shout, "zsh: sure you want to delete all the files in "); nicezputs(s, shout); if(isset(RMSTARWAIT)) { fputs("? (waiting ten seconds)", shout); @@ -2866,9 +2944,7 @@ mod_export void spckword(char **s, int hist, int cmd, int ask) { char *t, *correct_ignore; - int x; char ic = '\0'; - int ne; int preflen = 0; int autocd = cmd && isset(AUTOCD) && strcmp(*s, ".") && strcmp(*s, ".."); @@ -2937,6 +3013,7 @@ spckword(char **s, int hist, int cmd, int ask) } else { guess = *s; if (*guess == Tilde || *guess == String) { + int ne; ic = *guess; if (!*++t) return; @@ -2981,6 +3058,7 @@ spckword(char **s, int hist, int cmd, int ask) if (errflag) return; if (best && (int)strlen(best) > 1 && strcmp(best, guess)) { + int x; if (ic) { char *u; if (preflen) { @@ -3010,14 +3088,14 @@ spckword(char **s, int hist, int cmd, int ask) free(pptbuf); fflush(shout); zbeep(); - x = getquery("nyae \t", 0); + x = getquery("nyae", 0); if (cmd && x == 'n') pathchecked = path; } else x = 'n'; } else x = 'y'; - if (x == 'y' || x == ' ' || x == '\t') { + if (x == 'y') { *s = dupstring(best); if (hist) hwrep(best); @@ -3881,7 +3959,7 @@ inittyptab(void) #endif /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */ typtab['_'] = IIDENT | IUSER; - typtab['-'] = typtab['.'] = IUSER; + typtab['-'] = typtab['.'] = typtab[STOUC(Dash)] = IUSER; typtab[' '] |= IBLANK | INBLANK; typtab['\t'] |= IBLANK | INBLANK; typtab['\n'] |= INBLANK; @@ -4079,42 +4157,50 @@ itype_end(const char *ptr, int itype, int once) (itype != IIDENT || !isset(POSIXIDENTIFIERS))) { mb_charinit(); while (*ptr) { - wint_t wc; - int len = mb_metacharlenconv(ptr, &wc); - - if (!len) - break; - - if (wc == WEOF) { - /* invalid, treat as single character */ - int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr); - /* in this case non-ASCII characters can't match */ - if (chr > 127 || !zistype(chr,itype)) - break; - } else if (len == 1 && isascii(*ptr)) { - /* ASCII: can't be metafied, use standard test */ + int len; + if (itok(*ptr)) { + /* Not untokenised yet --- can happen in raw command line */ + len = 1; if (!zistype(*ptr,itype)) break; } else { - /* - * Valid non-ASCII character. - */ - switch (itype) { - case IWORD: - if (!iswalnum(wc) && - !wmemchr(wordchars_wide.chars, wc, - wordchars_wide.len)) - return (char *)ptr; - break; + wint_t wc; + len = mb_metacharlenconv(ptr, &wc); - case ISEP: - if (!wmemchr(ifs_wide.chars, wc, ifs_wide.len)) - return (char *)ptr; + if (!len) break; - default: - if (!iswalnum(wc)) - return (char *)ptr; + if (wc == WEOF) { + /* invalid, treat as single character */ + int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr); + /* in this case non-ASCII characters can't match */ + if (chr > 127 || !zistype(chr,itype)) + break; + } else if (len == 1 && isascii(*ptr)) { + /* ASCII: can't be metafied, use standard test */ + if (!zistype(*ptr,itype)) + break; + } else { + /* + * Valid non-ASCII character. + */ + switch (itype) { + case IWORD: + if (!iswalnum(wc) && + !wmemchr(wordchars_wide.chars, wc, + wordchars_wide.len)) + return (char *)ptr; + break; + + case ISEP: + if (!wmemchr(ifs_wide.chars, wc, ifs_wide.len)) + return (char *)ptr; + break; + + default: + if (!iswalnum(wc)) + return (char *)ptr; + } } } ptr += len; @@ -4159,6 +4245,32 @@ arrdup(char **s) return y; } +/* Duplicate at most max elements of the array s with heap memory */ + +/**/ +mod_export char ** +arrdup_max(char **s, unsigned max) +{ + char **x, **y, **send; + int len = 0; + + if (max) + len = arrlen(s); + + /* Limit has sense only if not equal to len */ + if (max > len) + max = len; + + y = x = (char **) zhalloc(sizeof(char *) * (max + 1)); + + send = s + max; + while (s < send) + *x++ = dupstring(*s++); + *x = NULL; + + return y; +} + /**/ mod_export char ** zarrdup(char **s) @@ -5016,8 +5128,10 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int flags) cnt = 1; /* FALL THROUGH */ default: - if (c == L'\'' && (flags & NICEFLAG_QUOTE)) + if (c == L'\'' && (flags & NICEFLAG_QUOTE)) { fmt = "\\'"; + newl = 2; + } else fmt = wcs_nicechar_sel(c, &newl, NULL, flags & NICEFLAG_QUOTE); break; @@ -5152,6 +5266,12 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp) const char *ptr; wchar_t wc; + if (STOUC(*s) <= 0x7f) { + if (wcp) + *wcp = (wint_t)*s; + return 1; + } + for (ptr = s; *ptr; ) { if (*ptr == Meta) { inchar = *++ptr ^ 32; @@ -5204,7 +5324,7 @@ 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)) { + if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) { /* treat as single byte, possibly metafied */ if (wcp) *wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s); @@ -5251,7 +5371,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr) char inchar, *laststart; size_t ret; wchar_t wc; - int num, num_in_char; + int num, num_in_char, complete; if (!isset(MULTIBYTE)) return ztrlen(ptr); @@ -5259,6 +5379,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr) laststart = ptr; ret = MB_INVALID; num = num_in_char = 0; + complete = 1; memset(&mb_shiftstate, 0, sizeof(mb_shiftstate)); while (*ptr && !(eptr && ptr >= eptr)) { @@ -5267,6 +5388,18 @@ mb_metastrlenend(char *ptr, int width, char *eptr) else inchar = *ptr; ptr++; + + if (complete && STOUC(inchar) <= STOUC(0x7f)) { + /* + * We rely on 7-bit US-ASCII as a subset, so skip + * multibyte handling if we have such a character. + */ + num++; + laststart = ptr; + num_in_char = 0; + continue; + } + ret = mbrtowc(&wc, &inchar, 1, &mb_shiftstate); if (ret == MB_INCOMPLETE) { @@ -5286,6 +5419,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr) * so we don't count characters twice. */ num_in_char++; + complete = 0; } else { if (ret == MB_INVALID) { /* Reset, treat as single character */ @@ -5308,6 +5442,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr) num++; laststart = ptr; num_in_char = 0; + complete = 1; } } @@ -5330,6 +5465,12 @@ 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 (wcp) + *wcp = (wint_t)*s; + return 1; + } + for (ptr = s; slen; ) { inchar = *ptr; ptr++; @@ -5365,7 +5506,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)) { + if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) { if (wcp) *wcp = (wint_t)*s; return 1; @@ -5581,10 +5722,6 @@ addunprintable(char *v, const char *u, const char *uend) /* * Quote the string s and return the result as a string from the heap. * - * If e is non-zero, the - * pointer it points to may point to a position in s and in e the position - * of the corresponding character in the quoted string is returned. - * * The last argument is a QT_ value defined in zsh.h other than QT_NONE. * * Most quote styles other than backslash assume the quotes are to @@ -5597,13 +5734,13 @@ addunprintable(char *v, const char *u, const char *uend) /**/ mod_export char * -quotestring(const char *s, char **e, int instring) +quotestring(const char *s, int instring) { const char *u; char *v; int alloclen; char *buf; - int sf = 0, shownull = 0; + int shownull = 0; /* * quotesub is used with QT_SINGLE_OPTIONAL. * quotesub = 0: mechanism not active @@ -5674,10 +5811,6 @@ quotestring(const char *s, char **e, int instring) while (*u) { uend = u + MB_METACHARLENCONV(u, &cc); - if (e && !sf && *e <= u) { - *e = v; - sf = 1; - } if ( #ifdef MULTIBYTE_SUPPORT cc != WEOF && @@ -5704,11 +5837,6 @@ quotestring(const char *s, char **e, int instring) } } else if (instring == QT_BACKSLASH_PATTERN) { while (*u) { - if (e && !sf && *e == u) { - *e = v; - sf = 1; - } - if (ipattern(*u)) *v++ = '\\'; *v++ = *u++; @@ -5727,8 +5855,6 @@ quotestring(const char *s, char **e, int instring) */ while (*u) { int dobackslash = 0; - if (e && *e == u) - *e = v, sf = 1; if (*u == Tick || *u == Qtick) { char c = *u++; @@ -5916,10 +6042,6 @@ quotestring(const char *s, char **e, int instring) *v++ = '\''; *v = '\0'; - if (e && *e == u) - *e = v, sf = 1; - DPUTS(e && !sf, "BUG: Wild pointer *e in quotestring()"); - v = dupstring(buf); zfree(buf, alloclen); return v; @@ -6794,7 +6916,7 @@ strsfx(char *s, char *t) static int upchdir(int n) { - char buf[PATH_MAX]; + char buf[PATH_MAX+1]; char *s; int err = -1; diff --git a/Src/wcwidth9.h b/Src/wcwidth9.h new file mode 100644 index 000000000..07e6bae1c --- /dev/null +++ b/Src/wcwidth9.h @@ -0,0 +1,1321 @@ +#ifndef WCWIDTH9_H +#define WCWIDTH9_H + +#include <stdlib.h> +#include <stdbool.h> + +struct wcwidth9_interval { + long first; + long last; +}; + +static const struct wcwidth9_interval wcwidth9_private[] = { + {0x00e000, 0x00f8ff}, + {0x0f0000, 0x0ffffd}, + {0x100000, 0x10fffd}, +}; + +static const struct wcwidth9_interval wcwidth9_nonprint[] = { + {0x0000, 0x001f}, + {0x007f, 0x009f}, + {0x00ad, 0x00ad}, + {0x070f, 0x070f}, + {0x180b, 0x180e}, + {0x200b, 0x200f}, + {0x202a, 0x202e}, + {0x206a, 0x206f}, + {0xd800, 0xdfff}, + {0xfeff, 0xfeff}, + {0xfff9, 0xfffb}, + {0xfffe, 0xffff}, +}; + +static const struct wcwidth9_interval wcwidth9_combining[] = { + {0x0300, 0x036f}, + {0x0483, 0x0489}, + {0x0591, 0x05bd}, + {0x05bf, 0x05bf}, + {0x05c1, 0x05c2}, + {0x05c4, 0x05c5}, + {0x05c7, 0x05c7}, + {0x0610, 0x061a}, + {0x064b, 0x065f}, + {0x0670, 0x0670}, + {0x06d6, 0x06dc}, + {0x06df, 0x06e4}, + {0x06e7, 0x06e8}, + {0x06ea, 0x06ed}, + {0x0711, 0x0711}, + {0x0730, 0x074a}, + {0x07a6, 0x07b0}, + {0x07eb, 0x07f3}, + {0x0816, 0x0819}, + {0x081b, 0x0823}, + {0x0825, 0x0827}, + {0x0829, 0x082d}, + {0x0859, 0x085b}, + {0x08d4, 0x08e1}, + {0x08e3, 0x0903}, + {0x093a, 0x093c}, + {0x093e, 0x094f}, + {0x0951, 0x0957}, + {0x0962, 0x0963}, + {0x0981, 0x0983}, + {0x09bc, 0x09bc}, + {0x09be, 0x09c4}, + {0x09c7, 0x09c8}, + {0x09cb, 0x09cd}, + {0x09d7, 0x09d7}, + {0x09e2, 0x09e3}, + {0x0a01, 0x0a03}, + {0x0a3c, 0x0a3c}, + {0x0a3e, 0x0a42}, + {0x0a47, 0x0a48}, + {0x0a4b, 0x0a4d}, + {0x0a51, 0x0a51}, + {0x0a70, 0x0a71}, + {0x0a75, 0x0a75}, + {0x0a81, 0x0a83}, + {0x0abc, 0x0abc}, + {0x0abe, 0x0ac5}, + {0x0ac7, 0x0ac9}, + {0x0acb, 0x0acd}, + {0x0ae2, 0x0ae3}, + {0x0b01, 0x0b03}, + {0x0b3c, 0x0b3c}, + {0x0b3e, 0x0b44}, + {0x0b47, 0x0b48}, + {0x0b4b, 0x0b4d}, + {0x0b56, 0x0b57}, + {0x0b62, 0x0b63}, + {0x0b82, 0x0b82}, + {0x0bbe, 0x0bc2}, + {0x0bc6, 0x0bc8}, + {0x0bca, 0x0bcd}, + {0x0bd7, 0x0bd7}, + {0x0c00, 0x0c03}, + {0x0c3e, 0x0c44}, + {0x0c46, 0x0c48}, + {0x0c4a, 0x0c4d}, + {0x0c55, 0x0c56}, + {0x0c62, 0x0c63}, + {0x0c81, 0x0c83}, + {0x0cbc, 0x0cbc}, + {0x0cbe, 0x0cc4}, + {0x0cc6, 0x0cc8}, + {0x0cca, 0x0ccd}, + {0x0cd5, 0x0cd6}, + {0x0ce2, 0x0ce3}, + {0x0d01, 0x0d03}, + {0x0d3e, 0x0d44}, + {0x0d46, 0x0d48}, + {0x0d4a, 0x0d4d}, + {0x0d57, 0x0d57}, + {0x0d62, 0x0d63}, + {0x0d82, 0x0d83}, + {0x0dca, 0x0dca}, + {0x0dcf, 0x0dd4}, + {0x0dd6, 0x0dd6}, + {0x0dd8, 0x0ddf}, + {0x0df2, 0x0df3}, + {0x0e31, 0x0e31}, + {0x0e34, 0x0e3a}, + {0x0e47, 0x0e4e}, + {0x0eb1, 0x0eb1}, + {0x0eb4, 0x0eb9}, + {0x0ebb, 0x0ebc}, + {0x0ec8, 0x0ecd}, + {0x0f18, 0x0f19}, + {0x0f35, 0x0f35}, + {0x0f37, 0x0f37}, + {0x0f39, 0x0f39}, + {0x0f3e, 0x0f3f}, + {0x0f71, 0x0f84}, + {0x0f86, 0x0f87}, + {0x0f8d, 0x0f97}, + {0x0f99, 0x0fbc}, + {0x0fc6, 0x0fc6}, + {0x102b, 0x103e}, + {0x1056, 0x1059}, + {0x105e, 0x1060}, + {0x1062, 0x1064}, + {0x1067, 0x106d}, + {0x1071, 0x1074}, + {0x1082, 0x108d}, + {0x108f, 0x108f}, + {0x109a, 0x109d}, + {0x135d, 0x135f}, + {0x1712, 0x1714}, + {0x1732, 0x1734}, + {0x1752, 0x1753}, + {0x1772, 0x1773}, + {0x17b4, 0x17d3}, + {0x17dd, 0x17dd}, + {0x180b, 0x180d}, + {0x1885, 0x1886}, + {0x18a9, 0x18a9}, + {0x1920, 0x192b}, + {0x1930, 0x193b}, + {0x1a17, 0x1a1b}, + {0x1a55, 0x1a5e}, + {0x1a60, 0x1a7c}, + {0x1a7f, 0x1a7f}, + {0x1ab0, 0x1abe}, + {0x1b00, 0x1b04}, + {0x1b34, 0x1b44}, + {0x1b6b, 0x1b73}, + {0x1b80, 0x1b82}, + {0x1ba1, 0x1bad}, + {0x1be6, 0x1bf3}, + {0x1c24, 0x1c37}, + {0x1cd0, 0x1cd2}, + {0x1cd4, 0x1ce8}, + {0x1ced, 0x1ced}, + {0x1cf2, 0x1cf4}, + {0x1cf8, 0x1cf9}, + {0x1dc0, 0x1df5}, + {0x1dfb, 0x1dff}, + {0x20d0, 0x20f0}, + {0x2cef, 0x2cf1}, + {0x2d7f, 0x2d7f}, + {0x2de0, 0x2dff}, + {0x302a, 0x302f}, + {0x3099, 0x309a}, + {0xa66f, 0xa672}, + {0xa674, 0xa67d}, + {0xa69e, 0xa69f}, + {0xa6f0, 0xa6f1}, + {0xa802, 0xa802}, + {0xa806, 0xa806}, + {0xa80b, 0xa80b}, + {0xa823, 0xa827}, + {0xa880, 0xa881}, + {0xa8b4, 0xa8c5}, + {0xa8e0, 0xa8f1}, + {0xa926, 0xa92d}, + {0xa947, 0xa953}, + {0xa980, 0xa983}, + {0xa9b3, 0xa9c0}, + {0xa9e5, 0xa9e5}, + {0xaa29, 0xaa36}, + {0xaa43, 0xaa43}, + {0xaa4c, 0xaa4d}, + {0xaa7b, 0xaa7d}, + {0xaab0, 0xaab0}, + {0xaab2, 0xaab4}, + {0xaab7, 0xaab8}, + {0xaabe, 0xaabf}, + {0xaac1, 0xaac1}, + {0xaaeb, 0xaaef}, + {0xaaf5, 0xaaf6}, + {0xabe3, 0xabea}, + {0xabec, 0xabed}, + {0xfb1e, 0xfb1e}, + {0xfe00, 0xfe0f}, + {0xfe20, 0xfe2f}, + {0x101fd, 0x101fd}, + {0x102e0, 0x102e0}, + {0x10376, 0x1037a}, + {0x10a01, 0x10a03}, + {0x10a05, 0x10a06}, + {0x10a0c, 0x10a0f}, + {0x10a38, 0x10a3a}, + {0x10a3f, 0x10a3f}, + {0x10ae5, 0x10ae6}, + {0x11000, 0x11002}, + {0x11038, 0x11046}, + {0x1107f, 0x11082}, + {0x110b0, 0x110ba}, + {0x11100, 0x11102}, + {0x11127, 0x11134}, + {0x11173, 0x11173}, + {0x11180, 0x11182}, + {0x111b3, 0x111c0}, + {0x111ca, 0x111cc}, + {0x1122c, 0x11237}, + {0x1123e, 0x1123e}, + {0x112df, 0x112ea}, + {0x11300, 0x11303}, + {0x1133c, 0x1133c}, + {0x1133e, 0x11344}, + {0x11347, 0x11348}, + {0x1134b, 0x1134d}, + {0x11357, 0x11357}, + {0x11362, 0x11363}, + {0x11366, 0x1136c}, + {0x11370, 0x11374}, + {0x11435, 0x11446}, + {0x114b0, 0x114c3}, + {0x115af, 0x115b5}, + {0x115b8, 0x115c0}, + {0x115dc, 0x115dd}, + {0x11630, 0x11640}, + {0x116ab, 0x116b7}, + {0x1171d, 0x1172b}, + {0x11c2f, 0x11c36}, + {0x11c38, 0x11c3f}, + {0x11c92, 0x11ca7}, + {0x11ca9, 0x11cb6}, + {0x16af0, 0x16af4}, + {0x16b30, 0x16b36}, + {0x16f51, 0x16f7e}, + {0x16f8f, 0x16f92}, + {0x1bc9d, 0x1bc9e}, + {0x1d165, 0x1d169}, + {0x1d16d, 0x1d172}, + {0x1d17b, 0x1d182}, + {0x1d185, 0x1d18b}, + {0x1d1aa, 0x1d1ad}, + {0x1d242, 0x1d244}, + {0x1da00, 0x1da36}, + {0x1da3b, 0x1da6c}, + {0x1da75, 0x1da75}, + {0x1da84, 0x1da84}, + {0x1da9b, 0x1da9f}, + {0x1daa1, 0x1daaf}, + {0x1e000, 0x1e006}, + {0x1e008, 0x1e018}, + {0x1e01b, 0x1e021}, + {0x1e023, 0x1e024}, + {0x1e026, 0x1e02a}, + {0x1e8d0, 0x1e8d6}, + {0x1e944, 0x1e94a}, + {0xe0100, 0xe01ef}, +}; + +static const struct wcwidth9_interval wcwidth9_doublewidth[] = { + {0x1100, 0x115f}, + {0x231a, 0x231b}, + {0x2329, 0x232a}, + {0x23e9, 0x23ec}, + {0x23f0, 0x23f0}, + {0x23f3, 0x23f3}, + {0x25fd, 0x25fe}, + {0x2614, 0x2615}, + {0x2648, 0x2653}, + {0x267f, 0x267f}, + {0x2693, 0x2693}, + {0x26a1, 0x26a1}, + {0x26aa, 0x26ab}, + {0x26bd, 0x26be}, + {0x26c4, 0x26c5}, + {0x26ce, 0x26ce}, + {0x26d4, 0x26d4}, + {0x26ea, 0x26ea}, + {0x26f2, 0x26f3}, + {0x26f5, 0x26f5}, + {0x26fa, 0x26fa}, + {0x26fd, 0x26fd}, + {0x2705, 0x2705}, + {0x270a, 0x270b}, + {0x2728, 0x2728}, + {0x274c, 0x274c}, + {0x274e, 0x274e}, + {0x2753, 0x2755}, + {0x2757, 0x2757}, + {0x2795, 0x2797}, + {0x27b0, 0x27b0}, + {0x27bf, 0x27bf}, + {0x2b1b, 0x2b1c}, + {0x2b50, 0x2b50}, + {0x2b55, 0x2b55}, + {0x2e80, 0x2e99}, + {0x2e9b, 0x2ef3}, + {0x2f00, 0x2fd5}, + {0x2ff0, 0x2ffb}, + {0x3000, 0x303e}, + {0x3041, 0x3096}, + {0x3099, 0x30ff}, + {0x3105, 0x312d}, + {0x3131, 0x318e}, + {0x3190, 0x31ba}, + {0x31c0, 0x31e3}, + {0x31f0, 0x321e}, + {0x3220, 0x3247}, + {0x3250, 0x32fe}, + {0x3300, 0x4dbf}, + {0x4e00, 0xa48c}, + {0xa490, 0xa4c6}, + {0xa960, 0xa97c}, + {0xac00, 0xd7a3}, + {0xf900, 0xfaff}, + {0xfe10, 0xfe19}, + {0xfe30, 0xfe52}, + {0xfe54, 0xfe66}, + {0xfe68, 0xfe6b}, + {0xff01, 0xff60}, + {0xffe0, 0xffe6}, + {0x16fe0, 0x16fe0}, + {0x17000, 0x187ec}, + {0x18800, 0x18af2}, + {0x1b000, 0x1b001}, + {0x1f004, 0x1f004}, + {0x1f0cf, 0x1f0cf}, + {0x1f18e, 0x1f18e}, + {0x1f191, 0x1f19a}, + {0x1f200, 0x1f202}, + {0x1f210, 0x1f23b}, + {0x1f240, 0x1f248}, + {0x1f250, 0x1f251}, + {0x1f300, 0x1f320}, + {0x1f32d, 0x1f335}, + {0x1f337, 0x1f37c}, + {0x1f37e, 0x1f393}, + {0x1f3a0, 0x1f3ca}, + {0x1f3cf, 0x1f3d3}, + {0x1f3e0, 0x1f3f0}, + {0x1f3f4, 0x1f3f4}, + {0x1f3f8, 0x1f43e}, + {0x1f440, 0x1f440}, + {0x1f442, 0x1f4fc}, + {0x1f4ff, 0x1f53d}, + {0x1f54b, 0x1f54e}, + {0x1f550, 0x1f567}, + {0x1f57a, 0x1f57a}, + {0x1f595, 0x1f596}, + {0x1f5a4, 0x1f5a4}, + {0x1f5fb, 0x1f64f}, + {0x1f680, 0x1f6c5}, + {0x1f6cc, 0x1f6cc}, + {0x1f6d0, 0x1f6d2}, + {0x1f6eb, 0x1f6ec}, + {0x1f6f4, 0x1f6f6}, + {0x1f910, 0x1f91e}, + {0x1f920, 0x1f927}, + {0x1f930, 0x1f930}, + {0x1f933, 0x1f93e}, + {0x1f940, 0x1f94b}, + {0x1f950, 0x1f95e}, + {0x1f980, 0x1f991}, + {0x1f9c0, 0x1f9c0}, + {0x20000, 0x2fffd}, + {0x30000, 0x3fffd}, +}; + +static const struct wcwidth9_interval wcwidth9_ambiguous[] = { + {0x00a1, 0x00a1}, + {0x00a4, 0x00a4}, + {0x00a7, 0x00a8}, + {0x00aa, 0x00aa}, + {0x00ad, 0x00ae}, + {0x00b0, 0x00b4}, + {0x00b6, 0x00ba}, + {0x00bc, 0x00bf}, + {0x00c6, 0x00c6}, + {0x00d0, 0x00d0}, + {0x00d7, 0x00d8}, + {0x00de, 0x00e1}, + {0x00e6, 0x00e6}, + {0x00e8, 0x00ea}, + {0x00ec, 0x00ed}, + {0x00f0, 0x00f0}, + {0x00f2, 0x00f3}, + {0x00f7, 0x00fa}, + {0x00fc, 0x00fc}, + {0x00fe, 0x00fe}, + {0x0101, 0x0101}, + {0x0111, 0x0111}, + {0x0113, 0x0113}, + {0x011b, 0x011b}, + {0x0126, 0x0127}, + {0x012b, 0x012b}, + {0x0131, 0x0133}, + {0x0138, 0x0138}, + {0x013f, 0x0142}, + {0x0144, 0x0144}, + {0x0148, 0x014b}, + {0x014d, 0x014d}, + {0x0152, 0x0153}, + {0x0166, 0x0167}, + {0x016b, 0x016b}, + {0x01ce, 0x01ce}, + {0x01d0, 0x01d0}, + {0x01d2, 0x01d2}, + {0x01d4, 0x01d4}, + {0x01d6, 0x01d6}, + {0x01d8, 0x01d8}, + {0x01da, 0x01da}, + {0x01dc, 0x01dc}, + {0x0251, 0x0251}, + {0x0261, 0x0261}, + {0x02c4, 0x02c4}, + {0x02c7, 0x02c7}, + {0x02c9, 0x02cb}, + {0x02cd, 0x02cd}, + {0x02d0, 0x02d0}, + {0x02d8, 0x02db}, + {0x02dd, 0x02dd}, + {0x02df, 0x02df}, + {0x0300, 0x036f}, + {0x0391, 0x03a1}, + {0x03a3, 0x03a9}, + {0x03b1, 0x03c1}, + {0x03c3, 0x03c9}, + {0x0401, 0x0401}, + {0x0410, 0x044f}, + {0x0451, 0x0451}, + {0x2010, 0x2010}, + {0x2013, 0x2016}, + {0x2018, 0x2019}, + {0x201c, 0x201d}, + {0x2020, 0x2022}, + {0x2024, 0x2027}, + {0x2030, 0x2030}, + {0x2032, 0x2033}, + {0x2035, 0x2035}, + {0x203b, 0x203b}, + {0x203e, 0x203e}, + {0x2074, 0x2074}, + {0x207f, 0x207f}, + {0x2081, 0x2084}, + {0x20ac, 0x20ac}, + {0x2103, 0x2103}, + {0x2105, 0x2105}, + {0x2109, 0x2109}, + {0x2113, 0x2113}, + {0x2116, 0x2116}, + {0x2121, 0x2122}, + {0x2126, 0x2126}, + {0x212b, 0x212b}, + {0x2153, 0x2154}, + {0x215b, 0x215e}, + {0x2160, 0x216b}, + {0x2170, 0x2179}, + {0x2189, 0x2189}, + {0x2190, 0x2199}, + {0x21b8, 0x21b9}, + {0x21d2, 0x21d2}, + {0x21d4, 0x21d4}, + {0x21e7, 0x21e7}, + {0x2200, 0x2200}, + {0x2202, 0x2203}, + {0x2207, 0x2208}, + {0x220b, 0x220b}, + {0x220f, 0x220f}, + {0x2211, 0x2211}, + {0x2215, 0x2215}, + {0x221a, 0x221a}, + {0x221d, 0x2220}, + {0x2223, 0x2223}, + {0x2225, 0x2225}, + {0x2227, 0x222c}, + {0x222e, 0x222e}, + {0x2234, 0x2237}, + {0x223c, 0x223d}, + {0x2248, 0x2248}, + {0x224c, 0x224c}, + {0x2252, 0x2252}, + {0x2260, 0x2261}, + {0x2264, 0x2267}, + {0x226a, 0x226b}, + {0x226e, 0x226f}, + {0x2282, 0x2283}, + {0x2286, 0x2287}, + {0x2295, 0x2295}, + {0x2299, 0x2299}, + {0x22a5, 0x22a5}, + {0x22bf, 0x22bf}, + {0x2312, 0x2312}, + {0x2460, 0x24e9}, + {0x24eb, 0x254b}, + {0x2550, 0x2573}, + {0x2580, 0x258f}, + {0x2592, 0x2595}, + {0x25a0, 0x25a1}, + {0x25a3, 0x25a9}, + {0x25b2, 0x25b3}, + {0x25b6, 0x25b7}, + {0x25bc, 0x25bd}, + {0x25c0, 0x25c1}, + {0x25c6, 0x25c8}, + {0x25cb, 0x25cb}, + {0x25ce, 0x25d1}, + {0x25e2, 0x25e5}, + {0x25ef, 0x25ef}, + {0x2605, 0x2606}, + {0x2609, 0x2609}, + {0x260e, 0x260f}, + {0x261c, 0x261c}, + {0x261e, 0x261e}, + {0x2640, 0x2640}, + {0x2642, 0x2642}, + {0x2660, 0x2661}, + {0x2663, 0x2665}, + {0x2667, 0x266a}, + {0x266c, 0x266d}, + {0x266f, 0x266f}, + {0x269e, 0x269f}, + {0x26bf, 0x26bf}, + {0x26c6, 0x26cd}, + {0x26cf, 0x26d3}, + {0x26d5, 0x26e1}, + {0x26e3, 0x26e3}, + {0x26e8, 0x26e9}, + {0x26eb, 0x26f1}, + {0x26f4, 0x26f4}, + {0x26f6, 0x26f9}, + {0x26fb, 0x26fc}, + {0x26fe, 0x26ff}, + {0x273d, 0x273d}, + {0x2776, 0x277f}, + {0x2b56, 0x2b59}, + {0x3248, 0x324f}, + {0xe000, 0xf8ff}, + {0xfe00, 0xfe0f}, + {0xfffd, 0xfffd}, + {0x1f100, 0x1f10a}, + {0x1f110, 0x1f12d}, + {0x1f130, 0x1f169}, + {0x1f170, 0x1f18d}, + {0x1f18f, 0x1f190}, + {0x1f19b, 0x1f1ac}, + {0xe0100, 0xe01ef}, + {0xf0000, 0xffffd}, + {0x100000, 0x10fffd}, +}; + +static const struct wcwidth9_interval wcwidth9_emoji_width[] = { + {0x1f1e6, 0x1f1ff}, + {0x1f321, 0x1f321}, + {0x1f324, 0x1f32c}, + {0x1f336, 0x1f336}, + {0x1f37d, 0x1f37d}, + {0x1f396, 0x1f397}, + {0x1f399, 0x1f39b}, + {0x1f39e, 0x1f39f}, + {0x1f3cb, 0x1f3ce}, + {0x1f3d4, 0x1f3df}, + {0x1f3f3, 0x1f3f5}, + {0x1f3f7, 0x1f3f7}, + {0x1f43f, 0x1f43f}, + {0x1f441, 0x1f441}, + {0x1f4fd, 0x1f4fd}, + {0x1f549, 0x1f54a}, + {0x1f56f, 0x1f570}, + {0x1f573, 0x1f579}, + {0x1f587, 0x1f587}, + {0x1f58a, 0x1f58d}, + {0x1f590, 0x1f590}, + {0x1f5a5, 0x1f5a5}, + {0x1f5a8, 0x1f5a8}, + {0x1f5b1, 0x1f5b2}, + {0x1f5bc, 0x1f5bc}, + {0x1f5c2, 0x1f5c4}, + {0x1f5d1, 0x1f5d3}, + {0x1f5dc, 0x1f5de}, + {0x1f5e1, 0x1f5e1}, + {0x1f5e3, 0x1f5e3}, + {0x1f5e8, 0x1f5e8}, + {0x1f5ef, 0x1f5ef}, + {0x1f5f3, 0x1f5f3}, + {0x1f5fa, 0x1f5fa}, + {0x1f6cb, 0x1f6cf}, + {0x1f6e0, 0x1f6e5}, + {0x1f6e9, 0x1f6e9}, + {0x1f6f0, 0x1f6f0}, + {0x1f6f3, 0x1f6f3}, +}; + +static const struct wcwidth9_interval wcwidth9_not_assigned[] = { + {0x0378, 0x0379}, + {0x0380, 0x0383}, + {0x038b, 0x038b}, + {0x038d, 0x038d}, + {0x03a2, 0x03a2}, + {0x0530, 0x0530}, + {0x0557, 0x0558}, + {0x0560, 0x0560}, + {0x0588, 0x0588}, + {0x058b, 0x058c}, + {0x0590, 0x0590}, + {0x05c8, 0x05cf}, + {0x05eb, 0x05ef}, + {0x05f5, 0x05ff}, + {0x061d, 0x061d}, + {0x070e, 0x070e}, + {0x074b, 0x074c}, + {0x07b2, 0x07bf}, + {0x07fb, 0x07ff}, + {0x082e, 0x082f}, + {0x083f, 0x083f}, + {0x085c, 0x085d}, + {0x085f, 0x089f}, + {0x08b5, 0x08b5}, + {0x08be, 0x08d3}, + {0x0984, 0x0984}, + {0x098d, 0x098e}, + {0x0991, 0x0992}, + {0x09a9, 0x09a9}, + {0x09b1, 0x09b1}, + {0x09b3, 0x09b5}, + {0x09ba, 0x09bb}, + {0x09c5, 0x09c6}, + {0x09c9, 0x09ca}, + {0x09cf, 0x09d6}, + {0x09d8, 0x09db}, + {0x09de, 0x09de}, + {0x09e4, 0x09e5}, + {0x09fc, 0x0a00}, + {0x0a04, 0x0a04}, + {0x0a0b, 0x0a0e}, + {0x0a11, 0x0a12}, + {0x0a29, 0x0a29}, + {0x0a31, 0x0a31}, + {0x0a34, 0x0a34}, + {0x0a37, 0x0a37}, + {0x0a3a, 0x0a3b}, + {0x0a3d, 0x0a3d}, + {0x0a43, 0x0a46}, + {0x0a49, 0x0a4a}, + {0x0a4e, 0x0a50}, + {0x0a52, 0x0a58}, + {0x0a5d, 0x0a5d}, + {0x0a5f, 0x0a65}, + {0x0a76, 0x0a80}, + {0x0a84, 0x0a84}, + {0x0a8e, 0x0a8e}, + {0x0a92, 0x0a92}, + {0x0aa9, 0x0aa9}, + {0x0ab1, 0x0ab1}, + {0x0ab4, 0x0ab4}, + {0x0aba, 0x0abb}, + {0x0ac6, 0x0ac6}, + {0x0aca, 0x0aca}, + {0x0ace, 0x0acf}, + {0x0ad1, 0x0adf}, + {0x0ae4, 0x0ae5}, + {0x0af2, 0x0af8}, + {0x0afa, 0x0b00}, + {0x0b04, 0x0b04}, + {0x0b0d, 0x0b0e}, + {0x0b11, 0x0b12}, + {0x0b29, 0x0b29}, + {0x0b31, 0x0b31}, + {0x0b34, 0x0b34}, + {0x0b3a, 0x0b3b}, + {0x0b45, 0x0b46}, + {0x0b49, 0x0b4a}, + {0x0b4e, 0x0b55}, + {0x0b58, 0x0b5b}, + {0x0b5e, 0x0b5e}, + {0x0b64, 0x0b65}, + {0x0b78, 0x0b81}, + {0x0b84, 0x0b84}, + {0x0b8b, 0x0b8d}, + {0x0b91, 0x0b91}, + {0x0b96, 0x0b98}, + {0x0b9b, 0x0b9b}, + {0x0b9d, 0x0b9d}, + {0x0ba0, 0x0ba2}, + {0x0ba5, 0x0ba7}, + {0x0bab, 0x0bad}, + {0x0bba, 0x0bbd}, + {0x0bc3, 0x0bc5}, + {0x0bc9, 0x0bc9}, + {0x0bce, 0x0bcf}, + {0x0bd1, 0x0bd6}, + {0x0bd8, 0x0be5}, + {0x0bfb, 0x0bff}, + {0x0c04, 0x0c04}, + {0x0c0d, 0x0c0d}, + {0x0c11, 0x0c11}, + {0x0c29, 0x0c29}, + {0x0c3a, 0x0c3c}, + {0x0c45, 0x0c45}, + {0x0c49, 0x0c49}, + {0x0c4e, 0x0c54}, + {0x0c57, 0x0c57}, + {0x0c5b, 0x0c5f}, + {0x0c64, 0x0c65}, + {0x0c70, 0x0c77}, + {0x0c84, 0x0c84}, + {0x0c8d, 0x0c8d}, + {0x0c91, 0x0c91}, + {0x0ca9, 0x0ca9}, + {0x0cb4, 0x0cb4}, + {0x0cba, 0x0cbb}, + {0x0cc5, 0x0cc5}, + {0x0cc9, 0x0cc9}, + {0x0cce, 0x0cd4}, + {0x0cd7, 0x0cdd}, + {0x0cdf, 0x0cdf}, + {0x0ce4, 0x0ce5}, + {0x0cf0, 0x0cf0}, + {0x0cf3, 0x0d00}, + {0x0d04, 0x0d04}, + {0x0d0d, 0x0d0d}, + {0x0d11, 0x0d11}, + {0x0d3b, 0x0d3c}, + {0x0d45, 0x0d45}, + {0x0d49, 0x0d49}, + {0x0d50, 0x0d53}, + {0x0d64, 0x0d65}, + {0x0d80, 0x0d81}, + {0x0d84, 0x0d84}, + {0x0d97, 0x0d99}, + {0x0db2, 0x0db2}, + {0x0dbc, 0x0dbc}, + {0x0dbe, 0x0dbf}, + {0x0dc7, 0x0dc9}, + {0x0dcb, 0x0dce}, + {0x0dd5, 0x0dd5}, + {0x0dd7, 0x0dd7}, + {0x0de0, 0x0de5}, + {0x0df0, 0x0df1}, + {0x0df5, 0x0e00}, + {0x0e3b, 0x0e3e}, + {0x0e5c, 0x0e80}, + {0x0e83, 0x0e83}, + {0x0e85, 0x0e86}, + {0x0e89, 0x0e89}, + {0x0e8b, 0x0e8c}, + {0x0e8e, 0x0e93}, + {0x0e98, 0x0e98}, + {0x0ea0, 0x0ea0}, + {0x0ea4, 0x0ea4}, + {0x0ea6, 0x0ea6}, + {0x0ea8, 0x0ea9}, + {0x0eac, 0x0eac}, + {0x0eba, 0x0eba}, + {0x0ebe, 0x0ebf}, + {0x0ec5, 0x0ec5}, + {0x0ec7, 0x0ec7}, + {0x0ece, 0x0ecf}, + {0x0eda, 0x0edb}, + {0x0ee0, 0x0eff}, + {0x0f48, 0x0f48}, + {0x0f6d, 0x0f70}, + {0x0f98, 0x0f98}, + {0x0fbd, 0x0fbd}, + {0x0fcd, 0x0fcd}, + {0x0fdb, 0x0fff}, + {0x10c6, 0x10c6}, + {0x10c8, 0x10cc}, + {0x10ce, 0x10cf}, + {0x1249, 0x1249}, + {0x124e, 0x124f}, + {0x1257, 0x1257}, + {0x1259, 0x1259}, + {0x125e, 0x125f}, + {0x1289, 0x1289}, + {0x128e, 0x128f}, + {0x12b1, 0x12b1}, + {0x12b6, 0x12b7}, + {0x12bf, 0x12bf}, + {0x12c1, 0x12c1}, + {0x12c6, 0x12c7}, + {0x12d7, 0x12d7}, + {0x1311, 0x1311}, + {0x1316, 0x1317}, + {0x135b, 0x135c}, + {0x137d, 0x137f}, + {0x139a, 0x139f}, + {0x13f6, 0x13f7}, + {0x13fe, 0x13ff}, + {0x169d, 0x169f}, + {0x16f9, 0x16ff}, + {0x170d, 0x170d}, + {0x1715, 0x171f}, + {0x1737, 0x173f}, + {0x1754, 0x175f}, + {0x176d, 0x176d}, + {0x1771, 0x1771}, + {0x1774, 0x177f}, + {0x17de, 0x17df}, + {0x17ea, 0x17ef}, + {0x17fa, 0x17ff}, + {0x180f, 0x180f}, + {0x181a, 0x181f}, + {0x1878, 0x187f}, + {0x18ab, 0x18af}, + {0x18f6, 0x18ff}, + {0x191f, 0x191f}, + {0x192c, 0x192f}, + {0x193c, 0x193f}, + {0x1941, 0x1943}, + {0x196e, 0x196f}, + {0x1975, 0x197f}, + {0x19ac, 0x19af}, + {0x19ca, 0x19cf}, + {0x19db, 0x19dd}, + {0x1a1c, 0x1a1d}, + {0x1a5f, 0x1a5f}, + {0x1a7d, 0x1a7e}, + {0x1a8a, 0x1a8f}, + {0x1a9a, 0x1a9f}, + {0x1aae, 0x1aaf}, + {0x1abf, 0x1aff}, + {0x1b4c, 0x1b4f}, + {0x1b7d, 0x1b7f}, + {0x1bf4, 0x1bfb}, + {0x1c38, 0x1c3a}, + {0x1c4a, 0x1c4c}, + {0x1c89, 0x1cbf}, + {0x1cc8, 0x1ccf}, + {0x1cf7, 0x1cf7}, + {0x1cfa, 0x1cff}, + {0x1df6, 0x1dfa}, + {0x1f16, 0x1f17}, + {0x1f1e, 0x1f1f}, + {0x1f46, 0x1f47}, + {0x1f4e, 0x1f4f}, + {0x1f58, 0x1f58}, + {0x1f5a, 0x1f5a}, + {0x1f5c, 0x1f5c}, + {0x1f5e, 0x1f5e}, + {0x1f7e, 0x1f7f}, + {0x1fb5, 0x1fb5}, + {0x1fc5, 0x1fc5}, + {0x1fd4, 0x1fd5}, + {0x1fdc, 0x1fdc}, + {0x1ff0, 0x1ff1}, + {0x1ff5, 0x1ff5}, + {0x1fff, 0x1fff}, + {0x2065, 0x2065}, + {0x2072, 0x2073}, + {0x208f, 0x208f}, + {0x209d, 0x209f}, + {0x20bf, 0x20cf}, + {0x20f1, 0x20ff}, + {0x218c, 0x218f}, + {0x23ff, 0x23ff}, + {0x2427, 0x243f}, + {0x244b, 0x245f}, + {0x2b74, 0x2b75}, + {0x2b96, 0x2b97}, + {0x2bba, 0x2bbc}, + {0x2bc9, 0x2bc9}, + {0x2bd2, 0x2beb}, + {0x2bf0, 0x2bff}, + {0x2c2f, 0x2c2f}, + {0x2c5f, 0x2c5f}, + {0x2cf4, 0x2cf8}, + {0x2d26, 0x2d26}, + {0x2d28, 0x2d2c}, + {0x2d2e, 0x2d2f}, + {0x2d68, 0x2d6e}, + {0x2d71, 0x2d7e}, + {0x2d97, 0x2d9f}, + {0x2da7, 0x2da7}, + {0x2daf, 0x2daf}, + {0x2db7, 0x2db7}, + {0x2dbf, 0x2dbf}, + {0x2dc7, 0x2dc7}, + {0x2dcf, 0x2dcf}, + {0x2dd7, 0x2dd7}, + {0x2ddf, 0x2ddf}, + {0x2e45, 0x2e7f}, + {0x2e9a, 0x2e9a}, + {0x2ef4, 0x2eff}, + {0x2fd6, 0x2fef}, + {0x2ffc, 0x2fff}, + {0x3040, 0x3040}, + {0x3097, 0x3098}, + {0x3100, 0x3104}, + {0x312e, 0x3130}, + {0x318f, 0x318f}, + {0x31bb, 0x31bf}, + {0x31e4, 0x31ef}, + {0x321f, 0x321f}, + {0x32ff, 0x32ff}, + {0x4db6, 0x4dbf}, + {0x9fd6, 0x9fff}, + {0xa48d, 0xa48f}, + {0xa4c7, 0xa4cf}, + {0xa62c, 0xa63f}, + {0xa6f8, 0xa6ff}, + {0xa7af, 0xa7af}, + {0xa7b8, 0xa7f6}, + {0xa82c, 0xa82f}, + {0xa83a, 0xa83f}, + {0xa878, 0xa87f}, + {0xa8c6, 0xa8cd}, + {0xa8da, 0xa8df}, + {0xa8fe, 0xa8ff}, + {0xa954, 0xa95e}, + {0xa97d, 0xa97f}, + {0xa9ce, 0xa9ce}, + {0xa9da, 0xa9dd}, + {0xa9ff, 0xa9ff}, + {0xaa37, 0xaa3f}, + {0xaa4e, 0xaa4f}, + {0xaa5a, 0xaa5b}, + {0xaac3, 0xaada}, + {0xaaf7, 0xab00}, + {0xab07, 0xab08}, + {0xab0f, 0xab10}, + {0xab17, 0xab1f}, + {0xab27, 0xab27}, + {0xab2f, 0xab2f}, + {0xab66, 0xab6f}, + {0xabee, 0xabef}, + {0xabfa, 0xabff}, + {0xd7a4, 0xd7af}, + {0xd7c7, 0xd7ca}, + {0xd7fc, 0xd7ff}, + {0xfa6e, 0xfa6f}, + {0xfada, 0xfaff}, + {0xfb07, 0xfb12}, + {0xfb18, 0xfb1c}, + {0xfb37, 0xfb37}, + {0xfb3d, 0xfb3d}, + {0xfb3f, 0xfb3f}, + {0xfb42, 0xfb42}, + {0xfb45, 0xfb45}, + {0xfbc2, 0xfbd2}, + {0xfd40, 0xfd4f}, + {0xfd90, 0xfd91}, + {0xfdc8, 0xfdef}, + {0xfdfe, 0xfdff}, + {0xfe1a, 0xfe1f}, + {0xfe53, 0xfe53}, + {0xfe67, 0xfe67}, + {0xfe6c, 0xfe6f}, + {0xfe75, 0xfe75}, + {0xfefd, 0xfefe}, + {0xff00, 0xff00}, + {0xffbf, 0xffc1}, + {0xffc8, 0xffc9}, + {0xffd0, 0xffd1}, + {0xffd8, 0xffd9}, + {0xffdd, 0xffdf}, + {0xffe7, 0xffe7}, + {0xffef, 0xfff8}, + {0xfffe, 0xffff}, + {0x1000c, 0x1000c}, + {0x10027, 0x10027}, + {0x1003b, 0x1003b}, + {0x1003e, 0x1003e}, + {0x1004e, 0x1004f}, + {0x1005e, 0x1007f}, + {0x100fb, 0x100ff}, + {0x10103, 0x10106}, + {0x10134, 0x10136}, + {0x1018f, 0x1018f}, + {0x1019c, 0x1019f}, + {0x101a1, 0x101cf}, + {0x101fe, 0x1027f}, + {0x1029d, 0x1029f}, + {0x102d1, 0x102df}, + {0x102fc, 0x102ff}, + {0x10324, 0x1032f}, + {0x1034b, 0x1034f}, + {0x1037b, 0x1037f}, + {0x1039e, 0x1039e}, + {0x103c4, 0x103c7}, + {0x103d6, 0x103ff}, + {0x1049e, 0x1049f}, + {0x104aa, 0x104af}, + {0x104d4, 0x104d7}, + {0x104fc, 0x104ff}, + {0x10528, 0x1052f}, + {0x10564, 0x1056e}, + {0x10570, 0x105ff}, + {0x10737, 0x1073f}, + {0x10756, 0x1075f}, + {0x10768, 0x107ff}, + {0x10806, 0x10807}, + {0x10809, 0x10809}, + {0x10836, 0x10836}, + {0x10839, 0x1083b}, + {0x1083d, 0x1083e}, + {0x10856, 0x10856}, + {0x1089f, 0x108a6}, + {0x108b0, 0x108df}, + {0x108f3, 0x108f3}, + {0x108f6, 0x108fa}, + {0x1091c, 0x1091e}, + {0x1093a, 0x1093e}, + {0x10940, 0x1097f}, + {0x109b8, 0x109bb}, + {0x109d0, 0x109d1}, + {0x10a04, 0x10a04}, + {0x10a07, 0x10a0b}, + {0x10a14, 0x10a14}, + {0x10a18, 0x10a18}, + {0x10a34, 0x10a37}, + {0x10a3b, 0x10a3e}, + {0x10a48, 0x10a4f}, + {0x10a59, 0x10a5f}, + {0x10aa0, 0x10abf}, + {0x10ae7, 0x10aea}, + {0x10af7, 0x10aff}, + {0x10b36, 0x10b38}, + {0x10b56, 0x10b57}, + {0x10b73, 0x10b77}, + {0x10b92, 0x10b98}, + {0x10b9d, 0x10ba8}, + {0x10bb0, 0x10bff}, + {0x10c49, 0x10c7f}, + {0x10cb3, 0x10cbf}, + {0x10cf3, 0x10cf9}, + {0x10d00, 0x10e5f}, + {0x10e7f, 0x10fff}, + {0x1104e, 0x11051}, + {0x11070, 0x1107e}, + {0x110c2, 0x110cf}, + {0x110e9, 0x110ef}, + {0x110fa, 0x110ff}, + {0x11135, 0x11135}, + {0x11144, 0x1114f}, + {0x11177, 0x1117f}, + {0x111ce, 0x111cf}, + {0x111e0, 0x111e0}, + {0x111f5, 0x111ff}, + {0x11212, 0x11212}, + {0x1123f, 0x1127f}, + {0x11287, 0x11287}, + {0x11289, 0x11289}, + {0x1128e, 0x1128e}, + {0x1129e, 0x1129e}, + {0x112aa, 0x112af}, + {0x112eb, 0x112ef}, + {0x112fa, 0x112ff}, + {0x11304, 0x11304}, + {0x1130d, 0x1130e}, + {0x11311, 0x11312}, + {0x11329, 0x11329}, + {0x11331, 0x11331}, + {0x11334, 0x11334}, + {0x1133a, 0x1133b}, + {0x11345, 0x11346}, + {0x11349, 0x1134a}, + {0x1134e, 0x1134f}, + {0x11351, 0x11356}, + {0x11358, 0x1135c}, + {0x11364, 0x11365}, + {0x1136d, 0x1136f}, + {0x11375, 0x113ff}, + {0x1145a, 0x1145a}, + {0x1145c, 0x1145c}, + {0x1145e, 0x1147f}, + {0x114c8, 0x114cf}, + {0x114da, 0x1157f}, + {0x115b6, 0x115b7}, + {0x115de, 0x115ff}, + {0x11645, 0x1164f}, + {0x1165a, 0x1165f}, + {0x1166d, 0x1167f}, + {0x116b8, 0x116bf}, + {0x116ca, 0x116ff}, + {0x1171a, 0x1171c}, + {0x1172c, 0x1172f}, + {0x11740, 0x1189f}, + {0x118f3, 0x118fe}, + {0x11900, 0x11abf}, + {0x11af9, 0x11bff}, + {0x11c09, 0x11c09}, + {0x11c37, 0x11c37}, + {0x11c46, 0x11c4f}, + {0x11c6d, 0x11c6f}, + {0x11c90, 0x11c91}, + {0x11ca8, 0x11ca8}, + {0x11cb7, 0x11fff}, + {0x1239a, 0x123ff}, + {0x1246f, 0x1246f}, + {0x12475, 0x1247f}, + {0x12544, 0x12fff}, + {0x1342f, 0x143ff}, + {0x14647, 0x167ff}, + {0x16a39, 0x16a3f}, + {0x16a5f, 0x16a5f}, + {0x16a6a, 0x16a6d}, + {0x16a70, 0x16acf}, + {0x16aee, 0x16aef}, + {0x16af6, 0x16aff}, + {0x16b46, 0x16b4f}, + {0x16b5a, 0x16b5a}, + {0x16b62, 0x16b62}, + {0x16b78, 0x16b7c}, + {0x16b90, 0x16eff}, + {0x16f45, 0x16f4f}, + {0x16f7f, 0x16f8e}, + {0x16fa0, 0x16fdf}, + {0x16fe1, 0x16fff}, + {0x187ed, 0x187ff}, + {0x18af3, 0x1afff}, + {0x1b002, 0x1bbff}, + {0x1bc6b, 0x1bc6f}, + {0x1bc7d, 0x1bc7f}, + {0x1bc89, 0x1bc8f}, + {0x1bc9a, 0x1bc9b}, + {0x1bca4, 0x1cfff}, + {0x1d0f6, 0x1d0ff}, + {0x1d127, 0x1d128}, + {0x1d1e9, 0x1d1ff}, + {0x1d246, 0x1d2ff}, + {0x1d357, 0x1d35f}, + {0x1d372, 0x1d3ff}, + {0x1d455, 0x1d455}, + {0x1d49d, 0x1d49d}, + {0x1d4a0, 0x1d4a1}, + {0x1d4a3, 0x1d4a4}, + {0x1d4a7, 0x1d4a8}, + {0x1d4ad, 0x1d4ad}, + {0x1d4ba, 0x1d4ba}, + {0x1d4bc, 0x1d4bc}, + {0x1d4c4, 0x1d4c4}, + {0x1d506, 0x1d506}, + {0x1d50b, 0x1d50c}, + {0x1d515, 0x1d515}, + {0x1d51d, 0x1d51d}, + {0x1d53a, 0x1d53a}, + {0x1d53f, 0x1d53f}, + {0x1d545, 0x1d545}, + {0x1d547, 0x1d549}, + {0x1d551, 0x1d551}, + {0x1d6a6, 0x1d6a7}, + {0x1d7cc, 0x1d7cd}, + {0x1da8c, 0x1da9a}, + {0x1daa0, 0x1daa0}, + {0x1dab0, 0x1dfff}, + {0x1e007, 0x1e007}, + {0x1e019, 0x1e01a}, + {0x1e022, 0x1e022}, + {0x1e025, 0x1e025}, + {0x1e02b, 0x1e7ff}, + {0x1e8c5, 0x1e8c6}, + {0x1e8d7, 0x1e8ff}, + {0x1e94b, 0x1e94f}, + {0x1e95a, 0x1e95d}, + {0x1e960, 0x1edff}, + {0x1ee04, 0x1ee04}, + {0x1ee20, 0x1ee20}, + {0x1ee23, 0x1ee23}, + {0x1ee25, 0x1ee26}, + {0x1ee28, 0x1ee28}, + {0x1ee33, 0x1ee33}, + {0x1ee38, 0x1ee38}, + {0x1ee3a, 0x1ee3a}, + {0x1ee3c, 0x1ee41}, + {0x1ee43, 0x1ee46}, + {0x1ee48, 0x1ee48}, + {0x1ee4a, 0x1ee4a}, + {0x1ee4c, 0x1ee4c}, + {0x1ee50, 0x1ee50}, + {0x1ee53, 0x1ee53}, + {0x1ee55, 0x1ee56}, + {0x1ee58, 0x1ee58}, + {0x1ee5a, 0x1ee5a}, + {0x1ee5c, 0x1ee5c}, + {0x1ee5e, 0x1ee5e}, + {0x1ee60, 0x1ee60}, + {0x1ee63, 0x1ee63}, + {0x1ee65, 0x1ee66}, + {0x1ee6b, 0x1ee6b}, + {0x1ee73, 0x1ee73}, + {0x1ee78, 0x1ee78}, + {0x1ee7d, 0x1ee7d}, + {0x1ee7f, 0x1ee7f}, + {0x1ee8a, 0x1ee8a}, + {0x1ee9c, 0x1eea0}, + {0x1eea4, 0x1eea4}, + {0x1eeaa, 0x1eeaa}, + {0x1eebc, 0x1eeef}, + {0x1eef2, 0x1efff}, + {0x1f02c, 0x1f02f}, + {0x1f094, 0x1f09f}, + {0x1f0af, 0x1f0b0}, + {0x1f0c0, 0x1f0c0}, + {0x1f0d0, 0x1f0d0}, + {0x1f0f6, 0x1f0ff}, + {0x1f10d, 0x1f10f}, + {0x1f12f, 0x1f12f}, + {0x1f16c, 0x1f16f}, + {0x1f1ad, 0x1f1e5}, + {0x1f203, 0x1f20f}, + {0x1f23c, 0x1f23f}, + {0x1f249, 0x1f24f}, + {0x1f252, 0x1f2ff}, + {0x1f6d3, 0x1f6df}, + {0x1f6ed, 0x1f6ef}, + {0x1f6f7, 0x1f6ff}, + {0x1f774, 0x1f77f}, + {0x1f7d5, 0x1f7ff}, + {0x1f80c, 0x1f80f}, + {0x1f848, 0x1f84f}, + {0x1f85a, 0x1f85f}, + {0x1f888, 0x1f88f}, + {0x1f8ae, 0x1f90f}, + {0x1f91f, 0x1f91f}, + {0x1f928, 0x1f92f}, + {0x1f931, 0x1f932}, + {0x1f93f, 0x1f93f}, + {0x1f94c, 0x1f94f}, + {0x1f95f, 0x1f97f}, + {0x1f992, 0x1f9bf}, + {0x1f9c1, 0x1ffff}, + {0x2a6d7, 0x2a6ff}, + {0x2b735, 0x2b73f}, + {0x2b81e, 0x2b81f}, + {0x2cea2, 0x2f7ff}, + {0x2fa1e, 0xe0000}, + {0xe0002, 0xe001f}, + {0xe0080, 0xe00ff}, + {0xe01f0, 0xeffff}, + {0xffffe, 0xfffff}, +}; + +#define WCWIDTH9_ARRAY_SIZE(arr) ((sizeof(arr)/sizeof((arr)[0])) / ((size_t)(!(sizeof(arr) % sizeof((arr)[0]))))) + +static inline bool wcwidth9_intable(const struct wcwidth9_interval *table, size_t n_items, int c) { + int mid, bot, top; + + if (c < table[0].first) { + return false; + } + + bot = 0; + top = (int)(n_items - 1); + while (top >= bot) { + mid = (bot + top) / 2; + + if (table[mid].last < c) { + bot = mid + 1; + } else if (table[mid].first > c) { + top = mid - 1; + } else { + return true; + } + } + + return false; +} + +static inline int wcwidth9(int c) { + if (c < 0|| c > 0x10ffff) { + return -1; + } + + if (wcwidth9_intable(wcwidth9_nonprint, WCWIDTH9_ARRAY_SIZE(wcwidth9_nonprint), c)) { + return -1; + } + + if (wcwidth9_intable(wcwidth9_combining, WCWIDTH9_ARRAY_SIZE(wcwidth9_combining), c)) { + return -1; + } + + if (wcwidth9_intable(wcwidth9_not_assigned, WCWIDTH9_ARRAY_SIZE(wcwidth9_not_assigned), c)) { + return -1; + } + + if (wcwidth9_intable(wcwidth9_private, WCWIDTH9_ARRAY_SIZE(wcwidth9_private), c)) { + return -3; + } + + if (wcwidth9_intable(wcwidth9_ambiguous, WCWIDTH9_ARRAY_SIZE(wcwidth9_ambiguous), c)) { + return -2; + } + + if (wcwidth9_intable(wcwidth9_doublewidth, WCWIDTH9_ARRAY_SIZE(wcwidth9_doublewidth), c)) { + return 2; + } + + if (wcwidth9_intable(wcwidth9_emoji_width, WCWIDTH9_ARRAY_SIZE(wcwidth9_emoji_width), c)) { + return 2; + } + + return 1; +} + +#endif /* WCWIDTH9_H */ @@ -489,6 +489,7 @@ typedef struct complist *Complist; typedef struct conddef *Conddef; typedef struct dirsav *Dirsav; typedef struct emulation_options *Emulation_options; +typedef struct execcmd_params *Execcmd_params; typedef struct features *Features; typedef struct feature_enables *Feature_enables; typedef struct funcstack *Funcstack; @@ -622,27 +623,34 @@ struct timedfn { /* (1<<4) is used for Z_END, see the wordcode definitions */ /* (1<<5) is used for Z_SIMPLE, see the wordcode definitions */ -/* Condition types. */ +/* + * Condition types. + * + * Careful when changing these: both cond_binary_ops in text.c and + * condstr in cond.c depend on these. (The zsh motto is "two instances + * are better than one". Or something.) + */ #define COND_NOT 0 #define COND_AND 1 #define COND_OR 2 #define COND_STREQ 3 -#define COND_STRNEQ 4 -#define COND_STRLT 5 -#define COND_STRGTR 6 -#define COND_NT 7 -#define COND_OT 8 -#define COND_EF 9 -#define COND_EQ 10 -#define COND_NE 11 -#define COND_LT 12 -#define COND_GT 13 -#define COND_LE 14 -#define COND_GE 15 -#define COND_REGEX 16 -#define COND_MOD 17 -#define COND_MODI 18 +#define COND_STRDEQ 4 +#define COND_STRNEQ 5 +#define COND_STRLT 6 +#define COND_STRGTR 7 +#define COND_NT 8 +#define COND_OT 9 +#define COND_EF 10 +#define COND_EQ 11 +#define COND_NE 12 +#define COND_LT 13 +#define COND_GT 14 +#define COND_LE 15 +#define COND_GE 16 +#define COND_REGEX 17 +#define COND_MOD 18 +#define COND_MODI 19 typedef int (*CondHandler) _((char **, int)); @@ -976,7 +984,8 @@ struct jobfile { struct job { pid_t gleader; /* process group leader of this job */ - pid_t other; /* subjob id or subshell pid */ + pid_t other; /* subjob id (SUPERJOB) + * or subshell pid (SUBJOB) */ int stat; /* see STATs below */ char *pwd; /* current working dir of shell when * * this job was spawned */ @@ -1008,6 +1017,8 @@ struct job { #define STAT_SUBLEADER (0x2000) /* is super-job, but leader is sub-shell */ #define STAT_BUILTIN (0x4000) /* job at tail of pipeline is a builtin */ +#define STAT_SUBJOB_ORPHANED (0x8000) + /* STAT_SUBJOB with STAT_SUPERJOB exited */ #define SP_RUNNING -1 /* fake status for jobs currently running */ @@ -1381,6 +1392,21 @@ struct builtin { */ #define BINF_ASSIGN (1<<19) +/** + * Parameters passed to execcmd(). + * These are not opaque --- they are also used by the pipeline manager. + */ +struct execcmd_params { + LinkList args; /* All command prefixes, arguments & options */ + LinkList redir; /* Redirections */ + Wordcode beg; /* The code at the start of the command */ + Wordcode varspc; /* The code for assignment parsed as such */ + Wordcode assignspc; /* The code for assignment parsed as typeset */ + int type; /* The WC_* type of the command */ + int postassigns; /* The number of assignspc assiguments */ + int htok; /* tokens in parameter list */ +}; + struct module { struct hashnode node; union { @@ -1563,7 +1589,7 @@ struct zpc_disables_save { /* * Bit vector of ZPC_COUNT disabled characters. * We'll live dangerously and assumed ZPC_COUNT is no greater - * than the number of bits an an unsigned int. + * than the number of bits an unsigned int. */ unsigned int disables; }; @@ -1792,6 +1818,8 @@ struct tieddata { #define PM_ZSHSTORED (1<<18) /* function stored in zsh form */ /* Remaining flags do not correspond directly to command line arguments */ +#define PM_DONTIMPORT_SUID (1<<19) /* do not import if running setuid */ +#define PM_SINGLE (1<<20) /* special can only have a single instance */ #define PM_LOCAL (1<<21) /* this parameter will be made local */ #define PM_SPECIAL (1<<22) /* special builtin parameter */ #define PM_DONTIMPORT (1<<23) /* do not import this variable */ @@ -2567,7 +2595,7 @@ struct ttyinfo { #define txtchangeisset(T,X) ((T) & (X)) #define txtchangeget(T,A) (((T) & A ## _MASK) >> A ## _SHIFT) -#define txtchangeset(T, X, Y) ((void)(T && (*T |= (X), *T &= ~(Y)))) +#define txtchangeset(T, X, Y) ((void)(T && (*T &= ~(Y), *T |= (X)))) /* * For outputting sequences to change colour: specify foreground @@ -2799,7 +2827,14 @@ enum errflag_bits { /* * User interrupt. */ - ERRFLAG_INT = 2 + ERRFLAG_INT = 2, + /* + * Hard error --- return to top-level prompt in interactive + * shell. In non-interactive shell we'll typically already + * have exited. This is reset by "errflag = 0" in + * loop(toplevel = 1, ...). + */ + ERRFLAG_HARD = 4 }; /***********/ @@ -2921,6 +2956,7 @@ struct parse_stack { int incasepat; int isnewlin; int infor; + int inrepeat_; int intypeset; int eclen, ecused, ecnpats; @@ -3095,7 +3131,9 @@ typedef wint_t convchar_t; * much what the definition tells us. However, we happen to know this * works on MacOS which doesn't define that. */ -#if defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__)) +#ifdef ENABLE_UNICODE9 +#define WCWIDTH(wc) mk_wcwidth(wc) +#elif defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__)) #define WCWIDTH(wc) mk_wcwidth(wc) #else #define WCWIDTH(wc) wcwidth(wc) diff --git a/Src/zsh.mdd b/Src/zsh.mdd index 86dd58866..324435d62 100644 --- a/Src/zsh.mdd +++ b/Src/zsh.mdd @@ -2,7 +2,7 @@ name=zsh/main link=static load=yes # load=static should replace use of alwayslink -functions='Functions/Chpwd/* Functions/Exceptions/* Functions/Misc/* Functions/MIME/* Functions/Prompts/* Functions/VCS_Info/* Functions/VCS_Info/Backends/*' +functions='Functions/Chpwd/* Functions/Exceptions/* Functions/Math/* Functions/Misc/* Functions/MIME/* Functions/Prompts/* Functions/VCS_Info/* Functions/VCS_Info/Backends/*' nozshdep=1 alwayslink=1 diff --git a/Src/zsh_system.h b/Src/zsh_system.h index 811340d42..5339b496f 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -37,7 +37,7 @@ #endif #endif -#if defined(__linux) || defined(__GNU__) || defined(__GLIBC__) || defined(LIBC_MUSL) +#if defined(__linux) || defined(__GNU__) || defined(__GLIBC__) || defined(LIBC_MUSL) || defined(__CYGWIN__) /* * Turn on numerous extensions. * This is in order to get the functions for manipulating /dev/ptmx. @@ -728,7 +728,7 @@ 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) +#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && !defined(__APPLE__) # define USE_SET_UNSET_ENV #endif @@ -882,6 +882,10 @@ extern short ospeed; # endif #endif +#ifdef HAVE_SRAND_DETERMINISTIC +# define srand srand_deterministic +#endif + #ifdef ZSH_VALGRIND # include "valgrind/valgrind.h" # include "valgrind/memcheck.h" |