diff options
Diffstat (limited to 'Src')
-rw-r--r-- | Src/Builtins/rlimits.awk | 6 | ||||
-rw-r--r-- | Src/Builtins/rlimits.c | 56 | ||||
-rw-r--r-- | Src/Modules/mathfunc.c | 9 | ||||
-rw-r--r-- | Src/Modules/parameter.c | 40 | ||||
-rw-r--r-- | Src/Modules/parameter.mdd | 2 | ||||
-rw-r--r-- | Src/Modules/stat.c | 4 | ||||
-rw-r--r-- | Src/Modules/zpty.c | 33 | ||||
-rw-r--r-- | Src/Zle/compcore.c | 5 | ||||
-rw-r--r-- | Src/Zle/compctl.c | 1 | ||||
-rw-r--r-- | Src/Zle/computil.c | 63 | ||||
-rw-r--r-- | Src/Zle/zle_main.c | 47 | ||||
-rw-r--r-- | Src/Zle/zle_move.c | 16 | ||||
-rw-r--r-- | Src/Zle/zle_tricky.c | 29 | ||||
-rw-r--r-- | Src/Zle/zle_utils.c | 1 | ||||
-rw-r--r-- | Src/builtin.c | 76 | ||||
-rw-r--r-- | Src/exec.c | 158 | ||||
-rw-r--r-- | Src/glob.c | 59 | ||||
-rw-r--r-- | Src/hist.c | 118 | ||||
-rw-r--r-- | Src/init.c | 9 | ||||
-rw-r--r-- | Src/input.c | 4 | ||||
-rw-r--r-- | Src/jobs.c | 137 | ||||
-rw-r--r-- | Src/lex.c | 1 | ||||
-rw-r--r-- | Src/math.c | 21 | ||||
-rw-r--r-- | Src/options.c | 3 | ||||
-rw-r--r-- | Src/params.c | 2 | ||||
-rw-r--r-- | Src/parse.c | 45 | ||||
-rw-r--r-- | Src/pattern.c | 480 | ||||
-rw-r--r-- | Src/prompt.c | 43 | ||||
-rw-r--r-- | Src/signals.c | 8 | ||||
-rw-r--r-- | Src/signals.h | 8 | ||||
-rw-r--r-- | Src/subst.c | 10 | ||||
-rw-r--r-- | Src/utils.c | 22 | ||||
-rw-r--r-- | Src/zsh.h | 82 | ||||
-rw-r--r-- | Src/zsh.mdd | 12 | ||||
-rw-r--r-- | Src/zsh_system.h | 2 |
35 files changed, 1235 insertions, 377 deletions
diff --git a/Src/Builtins/rlimits.awk b/Src/Builtins/rlimits.awk index bf914814d..fe2d0e931 100644 --- a/Src/Builtins/rlimits.awk +++ b/Src/Builtins/rlimits.awk @@ -42,7 +42,7 @@ BEGIN {limidx = 0} if (limnam == "MEMLOCK") { msg[limnum] = "Mmemorylocked" } if (limnam == "NOFILE") { msg[limnum] = "Ndescriptors" } if (limnam == "NPROC") { msg[limnum] = "Nmaxproc" } - if (limnam == "NTHR") { msg[limnum] = "Nmaxthr" } + if (limnam == "NTHR") { msg[limnum] = "Nmaxpthreads" } if (limnam == "OFILE") { msg[limnum] = "Ndescriptors" } if (limnam == "PTHREAD") { msg[limnum] = "Nmaxpthreads" } if (limnam == "RSS") { msg[limnum] = "Mresident" } @@ -55,6 +55,10 @@ BEGIN {limidx = 0} if (limnam == "NICE") { msg[limnum] = "Nnice" } if (limnam == "RTPRIO") { msg[limnum] = "Nrt_priority" } if (limnam == "RTTIME") { msg[limnum] = "Urt_time" } + if (limnam == "POSIXLOCKS") { msg[limnum] = "Nposixlocks" } + if (limnam == "NPTS") { msg[limnum] = "Npseudoterminals" } + if (limnam == "SWAP") { msg[limnum] = "Mswapsize" } + if (limnam == "KQUEUES") { msg[limnum] = "Nkqueues" } } } } diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index eedfa969c..fd4c94aaa 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -32,6 +32,14 @@ #if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY) +#ifdef RLIMIT_POSIXLOCKS +# define RLIMIT_LOCKS RLIMIT_POSIXLOCKS +#endif + +#ifdef RLIMIT_NTHR +# define RLIMIT_PTHREAD RLIMIT_NTHR +#endif + enum { ZLIMTYPE_MEMORY, ZLIMTYPE_NUMBER, @@ -314,12 +322,6 @@ printulimit(char *nam, int lim, int hard, int head) printf("-u: processes "); break; # endif /* HAVE_RLIMIT_NPROC */ -# ifdef HAVE_RLIMIT_NTHR - case RLIMIT_NTHR: - if (head) - printf("-r: threads "); - break; -#endif /* HAVE_RLIMIT_NTHR */ # if defined(HAVE_RLIMIT_VMEM) && (!defined(HAVE_RLIMIT_RSS) || !defined(RLIMIT_VMEM_IS_RSS)) case RLIMIT_VMEM: if (head) @@ -371,7 +373,7 @@ printulimit(char *nam, int lim, int hard, int head) # ifdef HAVE_RLIMIT_PTHREAD case RLIMIT_PTHREAD: if (head) - printf("-N %2d: threads per process ", RLIMIT_PTHREAD); + printf("-T: threads per process "); break; # endif /* HAVE_RLIMIT_PTHREAD */ # ifdef HAVE_RLIMIT_NICE @@ -386,6 +388,26 @@ printulimit(char *nam, int lim, int hard, int head) printf("-r: max rt priority "); break; # endif /* HAVE_RLIMIT_RTPRIO */ +# ifdef HAVE_RLIMIT_NPTS + case RLIMIT_NPTS: + if (head) + printf("-p: pseudo-terminals "); + break; +# endif /* HAVE_RLIMIT_NPTS */ +# ifdef HAVE_RLIMIT_SWAP + case RLIMIT_SWAP: + if (head) + printf("-w: swap size (kbytes) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; +# endif /* HAVE_RLIMIT_SWAP */ +# ifdef HAVE_RLIMIT_KQUEUES + case RLIMIT_KQUEUES: + if (head) + printf("-k: kqueues "); + break; +# endif /* HAVE_RLIMIT_KQUEUES */ default: if (head) printf("-N %2d: ", lim); @@ -800,11 +822,6 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) res = RLIMIT_NOFILE; break; # endif /* HAVE_RLIMIT_NOFILE */ -# ifdef HAVE_RLIMIT_NTHR - case 'r': - res = RLIMIT_NTHR; - break; -# endif /* HAVE_RLIMIT_NTHR */ # ifdef HAVE_RLIMIT_NPROC case 'u': res = RLIMIT_NPROC; @@ -844,6 +861,21 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) res = RLIMIT_RTPRIO; break; # endif +# ifdef HAVE_RLIMIT_NPTS + case 'p': + res = RLIMIT_NPTS; + break; +# endif +# ifdef HAVE_RLIMIT_SWAP + case 'w': + res = RLIMIT_SWAP; + break; +# endif +# ifdef HAVE_RLIMIT_KQUEUES + case 'k': + res = RLIMIT_KQUEUES; + break; +# endif default: /* unrecognised limit */ zwarnnam(name, "bad option: -%c", *options); diff --git a/Src/Modules/mathfunc.c b/Src/Modules/mathfunc.c index 04483b555..efadd86ff 100644 --- a/Src/Modules/mathfunc.c +++ b/Src/Modules/mathfunc.c @@ -340,7 +340,16 @@ math_func(char *name, int argc, mnumber *argv, int id) break; case MF_GAMMA: +#ifdef HAVE_TGAMMA + retd = tgamma(argd); +#else +#ifdef HAVE_SIGNGAM + retd = lgamma(argd); + retd = signgam*exp(retd); +#else /*XXX assume gamma(x) returns Gamma(x), not log(|Gamma(x)|) */ retd = gamma(argd); +#endif +#endif break; case MF_HYPOT: diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index a029c9cb4..22148f991 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -759,6 +759,38 @@ disreswordsgetfn(UNUSED(Param pm)) return getreswords(DISABLED); } +/* Functions for the patchars special parameter. */ + +/**/ +static char ** +getpatchars(int dis) +{ + int i; + char **ret, **p; + + p = ret = (char **) zhalloc(ZPC_COUNT * sizeof(char *)); + + for (i = 0; i < ZPC_COUNT; i++) + if (zpc_strings[i] && !dis == !zpc_disables[i]) + *p++ = dupstring(zpc_strings[i]); + + *p = NULL; + + return ret; +} + +static char ** +patcharsgetfn(UNUSED(Param pm)) +{ + return getpatchars(0); +} + +static char ** +dispatcharsgetfn(UNUSED(Param pm)) +{ + return getpatchars(1); +} + /* Functions for the options special parameter. */ /**/ @@ -2018,6 +2050,10 @@ static const struct gsu_array reswords_gsu = { reswordsgetfn, arrsetfn, stdunsetfn }; static const struct gsu_array disreswords_gsu = { disreswordsgetfn, arrsetfn, stdunsetfn }; +static const struct gsu_array patchars_gsu = +{ patcharsgetfn, arrsetfn, stdunsetfn }; +static const struct gsu_array dispatchars_gsu = +{ dispatcharsgetfn, arrsetfn, stdunsetfn }; static const struct gsu_array dirs_gsu = { dirsgetfn, dirssetfn, stdunsetfn }; static const struct gsu_array historywords_gsu = @@ -2038,6 +2074,8 @@ static struct paramdef partab[] = { &pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions), SPECIALPMDEF("dis_galiases", 0, &pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases), + SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY, + &dispatchars_gsu, NULL, NULL), SPECIALPMDEF("dis_reswords", PM_ARRAY|PM_READONLY, &disreswords_gsu, NULL, NULL), SPECIALPMDEF("dis_saliases", 0, @@ -2072,6 +2110,8 @@ static struct paramdef partab[] = { &pmoptions_gsu, getpmoption, scanpmoptions), SPECIALPMDEF("parameters", PM_READONLY, NULL, getpmparameter, scanpmparameters), + SPECIALPMDEF("patchars", PM_ARRAY|PM_READONLY, + &patchars_gsu, NULL, NULL), SPECIALPMDEF("reswords", PM_ARRAY|PM_READONLY, &reswords_gsu, NULL, NULL), SPECIALPMDEF("saliases", 0, diff --git a/Src/Modules/parameter.mdd b/Src/Modules/parameter.mdd index eb48d5f2a..a91a5dc09 100644 --- a/Src/Modules/parameter.mdd +++ b/Src/Modules/parameter.mdd @@ -2,6 +2,6 @@ name=zsh/parameter link=either load=yes -autofeatures="p:parameters p:commands p:functions p:dis_functions p:funcfiletrace p:funcsourcetrace p:funcstack p:functrace p:builtins p:dis_builtins p:reswords p:dis_reswords p:options p:modules p:dirstack p:history p:historywords p:jobtexts p:jobdirs p:jobstates p:nameddirs p:userdirs p:aliases p:dis_aliases p:galiases p:dis_galiases p:saliases p:dis_saliases" +autofeatures="p:parameters p:commands p:functions p:dis_functions p:funcfiletrace p:funcsourcetrace p:funcstack p:functrace p:builtins p:dis_builtins p:reswords p:dis_reswords p:patchars p:dis_patchars p:options p:modules p:dirstack p:history p:historywords p:jobtexts p:jobdirs p:jobstates p:nameddirs p:userdirs p:aliases p:dis_aliases p:galiases p:dis_galiases p:saliases p:dis_saliases" objects="parameter.o" diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index a3e95bb59..edae0841e 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -339,7 +339,7 @@ statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags) * -H hash: as for -A array, but returns a hash with the keys being those * from stat -l * -F fmt: specify a $TIME-like format for printing times; the default - * is the (CTIME-like) "%a %b %e %k:%M:%S". This option implies + * is the (CTIME-like) "%a %b %e %k:%M:%S %Z %Y". This option implies * -s as it is not useful for numerical times. * * +type selects just element type of stat buffer (-l gives list): @@ -361,7 +361,7 @@ bin_stat(char *name, char **args, Options ops, UNUSED(int func)) struct stat statbuf; int found = 0, nargs; - timefmt = "%a %b %e %k:%M:%S"; + timefmt = "%a %b %e %k:%M:%S %Z %Y"; for (; *args && (**args == '+' || **args == '-'); args++) { char *arg = *args+1; diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 25ec7dfea..fca0cc172 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -293,8 +293,8 @@ static int newptycmd(char *nam, char *pname, char **args, int echo, int nblock) { Ptycmd p; - int master, slave, pid, oineval = ineval; - char *oscriptname = scriptname; + int master, slave, pid, oineval = ineval, ret; + char *oscriptname = scriptname, syncch; Eprog prog; /* code borrowed from bin_eval() */ @@ -344,6 +344,8 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) if (get_pty(0, &slave)) exit(1); + SHTTY = slave; + attachtty(mypid); #ifdef TIOCGWINSZ /* Set the window size before associating with the terminal * * so that we don't get hit with a SIGWINCH. I'm paranoid. */ @@ -396,8 +398,23 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) setsparam("TTY", ztrdup(ttystrname)); opts[INTERACTIVE] = 0; + + syncch = 0; + do { + ret = write(1, &syncch, 1); + } while (ret != 1 && ( +#ifdef EWOULDBLOCK + errno == EWOULDBLOCK || +#else +#ifdef EAGAIN + errno == EAGAIN || +#endif +#endif + errno == EINTR)); + execode(prog, 1, 0, "zpty"); stopmsg = 2; + mypid = 0; /* trick to ensure we _exit() */ zexit(lastval, 0); } master = movefd(master); @@ -430,6 +447,18 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) scriptname = oscriptname; ineval = oineval; + do { + ret = read(master, &syncch, 1); + } while (ret != 1 && ( +#ifdef EWOULDBLOCK + errno == EWOULDBLOCK || +#else +#ifdef EAGAIN + errno == EAGAIN || +#endif +#endif + errno == EINTR)); + return 0; } diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 39d41bdb5..5c5628a8d 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -826,7 +826,6 @@ callcompfunc(char *s, char *fn) sfcontext = SFC_CWIDGET; NEWHEAPS(compheap) { LinkList largs = NULL; - int olv = lastval; if (*cfargs) { char **p = cfargs; @@ -836,9 +835,7 @@ callcompfunc(char *s, char *fn) while (*p) addlinknode(largs, dupstring(*p++)); } - doshfunc(shfunc, largs, 0); - cfret = lastval; - lastval = olv; + cfret = doshfunc(shfunc, largs, 1); } OLDHEAPS; sfcontext = osc; endparamscope(); diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 0143370c7..ab1857c0a 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -398,7 +398,6 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl) if (cl) return 1; else { - freecompctl(cc); cclist = COMP_REMOVE; return 0; } diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index cd508d0ac..f5e6ba195 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -465,6 +465,7 @@ cd_init(char *nam, char *hide, char *mlen, char *sep, cd_state.showd = disp; cd_state.maxg = cd_state.groups = cd_state.descs = 0; cd_state.maxmlen = atoi(mlen); + cd_state.premaxw = 0; itmp = zterm_columns - cd_state.swidth - 4; if (cd_state.maxmlen > itmp) cd_state.maxmlen = itmp; @@ -643,35 +644,43 @@ cd_get(char **params) p += str->len; memset(p, ' ', (l = (cd_state.premaxw - str->width + CM_SPACE))); p += l; - strcpy(p, cd_state.sep); - p += cd_state.slen; - /* - * copy a character at once until no more screen width - * is available. Leave 1 character at the end of screen - * as safety margin - */ remw = zterm_columns - cd_state.premaxw - cd_state.swidth - 3; - d = str->desc; - w = MB_METASTRWIDTH(d); - if (w <= remw) - strcpy(p, d); - else { - pp = p; - while (remw > 0 && *d) { - l = MB_METACHARLEN(d); - memcpy(pp, d, l); - pp[l] = '\0'; - w = MB_METASTRWIDTH(pp); - if (w > remw) { - *pp = '\0'; - break; - } + while (remw < 0 && zterm_columns) { + /* line wrapped, use remainder of the extra line */ + remw += zterm_columns; + } + if (cd_state.slen < remw) { + strcpy(p, cd_state.sep); + p += cd_state.slen; + remw -= cd_state.slen; - pp += l; - d += l; - remw -= w; + /* + * copy a character at once until no more screen + * width is available. Leave 1 character at the + * end of screen as safety margin + */ + d = str->desc; + w = MB_METASTRWIDTH(d); + if (w <= remw) + strcpy(p, d); + else { + pp = p; + while (remw > 0 && *d) { + l = MB_METACHARLEN(d); + memcpy(pp, d, l); + pp[l] = '\0'; + w = MB_METASTRWIDTH(pp); + if (w > remw) { + *pp = '\0'; + break; + } + + pp += l; + d += l; + remw -= w; + } } } @@ -1608,7 +1617,7 @@ get_cadef(char *nam, char **args) return *p; } else if (!min || !*p || (*p)->lastt < (*min)->lastt) min = p; - if (i) + if (i > 0) min = p; if ((new = parse_cadef(nam, args))) { freecadef(*min); @@ -2992,7 +3001,7 @@ get_cvdef(char *nam, char **args) return *p; } else if (!min || !*p || (*p)->lastt < (*min)->lastt) min = p; - if (i) + if (i > 0) min = p; if ((new = parse_cvdef(nam, args))) { freecvdef(*min); diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index e1a575bdb..5798e74b4 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -567,7 +567,9 @@ raw_getbyte(long do_keytmout, char *cptr) gettyinfo(&ti); ti.tio.c_cc[VMIN] = 0; settyinfo(&ti); + winch_unblock(); ret = read(SHTTY, cptr, 1); + winch_block(); ti.tio.c_cc[VMIN] = 1; settyinfo(&ti); if (ret > 0) @@ -597,7 +599,9 @@ raw_getbyte(long do_keytmout, char *cptr) else poll_timeout = -1; + winch_unblock(); selret = poll(fds, errtry ? 1 : nfds, poll_timeout); + winch_block(); # else int fdmax = SHTTY; struct timeval *tvptr; @@ -622,8 +626,10 @@ raw_getbyte(long do_keytmout, char *cptr) else tvptr = NULL; + winch_unblock(); selret = select(fdmax+1, (SELECT_ARG_2_T) & foofd, NULL, NULL, tvptr); + winch_block(); # endif /* * Make sure a user interrupt gets passed on straight away. @@ -788,7 +794,9 @@ raw_getbyte(long do_keytmout, char *cptr) # else ioctl(SHTTY, TCSETA, &ti.tio); # endif + winch_unblock(); ret = read(SHTTY, cptr, 1); + winch_block(); # ifdef HAVE_TERMIOS_H tcsetattr(SHTTY, TCSANOW, &shttyinfo.tio); # else @@ -799,7 +807,9 @@ raw_getbyte(long do_keytmout, char *cptr) #endif } + winch_unblock(); ret = read(SHTTY, cptr, 1); + winch_block(); return ret; } @@ -1105,7 +1115,7 @@ zlecore(void) /**/ char * -zleread(char **lp, char **rp, int flags, int context) +zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) { char *s; int old_errno = errno; @@ -1178,6 +1188,13 @@ zleread(char **lp, char **rp, int flags, int context) viinsbegin = 0; statusline = NULL; selectkeymap("main", 1); + /* + * If main is linked to the viins keymap, we need to register + * explicitly that we're now in vi insert mode as there's + * no user operation to indicate this. + */ + if (openkeymap("main") == openkeymap("viins")) + viinsert(NULL); selectlocalmap(NULL); fixsuffix(); if ((s = getlinknode(bufstack))) { @@ -1223,7 +1240,9 @@ zleread(char **lp, char **rp, int flags, int context) unqueue_signals(); /* Should now be safe to acknowledge SIGWINCH */ - zlecallhook("zle-line-init", NULL); + zlecallhook(init, NULL); + + zrefresh(); zlecore(); @@ -1231,7 +1250,7 @@ zleread(char **lp, char **rp, int flags, int context) setsparam("ZLE_LINE_ABORTED", zlegetline(NULL, NULL)); if (done && !exit_pending && !errflag) - zlecallhook("zle-line-finish", NULL); + zlecallhook(finish, NULL); statusline = NULL; invalidatelist(); @@ -1471,7 +1490,7 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func)) Param pm = 0; int ifl; int type = PM_SCALAR, obreaks = breaks, haso = 0, oSHTTY = 0; - char *p1, *p2, *main_keymapname, *vicmd_keymapname; + char *p1, *p2, *main_keymapname, *vicmd_keymapname, *init, *finish; Keymap main_keymapsave = NULL, vicmd_keymapsave = NULL; FILE *oshout = NULL; @@ -1499,6 +1518,8 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func)) p2 = OPT_ARG_SAFE(ops,'r'); main_keymapname = OPT_ARG_SAFE(ops,'M'); vicmd_keymapname = OPT_ARG_SAFE(ops,'m'); + init = OPT_ARG_SAFE(ops,'i'); + finish = OPT_ARG_SAFE(ops,'f'); if (type != PM_SCALAR && !OPT_ISSET(ops,'c')) { zwarnnam(name, "-%s ignored", type == PM_ARRAY ? "a" : "A"); @@ -1600,6 +1621,7 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func)) haso = 1; } + /* edit the parameter value */ zpushnode(bufstack, s); @@ -1615,7 +1637,10 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func)) if (OPT_ISSET(ops,'h')) hbegin(2); isfirstln = OPT_ISSET(ops,'e'); - t = zleread(&p1, &p2, OPT_ISSET(ops,'h') ? ZLRF_HISTORY : 0, ZLCON_VARED); + + t = zleread(&p1, &p2, OPT_ISSET(ops,'h') ? ZLRF_HISTORY : 0, ZLCON_VARED, + init ? init : "zle-line-init", + finish ? finish : "zle-line-finish"); if (OPT_ISSET(ops,'h')) hend(NULL); isfirstln = ifl; @@ -1880,7 +1905,8 @@ zle_main_entry(int cmd, va_list ap) flags = va_arg(ap, int); context = va_arg(ap, int); - return zleread(lp, rp, flags, context); + return zleread(lp, rp, flags, context, + "zle-line-init", "zle-line-finish"); } case ZLE_CMD_ADD_TO_LINE: @@ -1915,6 +1941,13 @@ zle_main_entry(int cmd, va_list ap) break; } + case ZLE_CMD_SET_HIST_LINE: + { + histline = va_arg(ap, zlong); + + break; + } + default: #ifdef DEBUG dputs("Bad command %d in zle_main_entry", cmd); @@ -1926,7 +1959,7 @@ zle_main_entry(int cmd, va_list ap) static struct builtin bintab[] = { BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaM:ldDANmrsLRp", NULL), - BUILTIN("vared", 0, bin_vared, 1, 1, 0, "aAcehM:m:p:r:t:", NULL), + BUILTIN("vared", 0, bin_vared, 1, 1, 0, "aAcef:hi:M:m:p:r:t:", NULL), BUILTIN("zle", 0, bin_zle, 0, -1, 0, "aAcCDFgGIKlLmMNrRTU", NULL), }; diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c index d5f464c2c..7312b3f20 100644 --- a/Src/Zle/zle_move.c +++ b/Src/Zle/zle_move.c @@ -30,7 +30,7 @@ #include "zle.mdh" #include "zle_move.pro" -static int vimarkcs[26], vimarkline[26]; +static int vimarkcs[27], vimarkline[27]; #ifdef MULTIBYTE_SUPPORT /* @@ -825,11 +825,17 @@ int vigotomark(UNUSED(char **args)) { ZLE_INT_T ch; + int oldcs = zlecs; + int oldline = histline; ch = getfullchar(0); - if (ch < ZWC('a') || ch > ZWC('z')) - return 1; - ch -= ZWC('a'); + if (ch == ZWC('\'') || ch == ZWC('`')) + ch = 26; + else { + if (ch < ZWC('a') || ch > ZWC('z')) + return 1; + ch -= ZWC('a'); + } if (!vimarkline[ch]) return 1; if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) { @@ -837,6 +843,8 @@ vigotomark(UNUSED(char **args)) return 1; } zlecs = vimarkcs[ch]; + vimarkcs[26] = oldcs; + vimarkline[26] = oldline; if (zlecs > zlell) zlecs = zlell; return 0; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 78a9fa490..e30e0b1aa 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -1095,6 +1095,7 @@ get_comp_string(void) * the command word is not at index zero in the array. */ int redirpos; + int noword; char *s = NULL, *tmp, *p, *tt = NULL, rdop[20]; char *linptr, *u; @@ -1165,7 +1166,7 @@ get_comp_string(void) * and whatnot. */ do { - qsub = 0; + qsub = noword = 0; lincmd = ((incmdpos && !ins && !incond) || (oins == 2 && wordpos == 2) || @@ -1239,6 +1240,19 @@ get_comp_string(void) * leave the loop. */ if (tt) break; + if (ins < 2) { + /* + * Don't add this as a word, because we're about to start + * a new command line: pretend there's no string here. + * We don't dare do this if we're using one of the + * *really* gross hacks with ins to get later words + * to look like command words, because we don't + * understand how they work. Quite possibly we + * should be using a mechanism like the one here rather + * than the ins thing. + */ + noword = 1; + } /* Otherwise reset the variables we are collecting data in. */ wordpos = cp = rd = ins = redirpos = 0; tt0 = NULLTOK; @@ -1253,6 +1267,14 @@ get_comp_string(void) /* If everything before is a redirection, don't reset the index */ if (wordpos != redirpos) wordpos = redirpos = 0; + } else if (tok == SEPER) { + /* + * A following DOLOOP should cause us to reset to the start + * of the command line. For some reason we only recognise + * DOLOOP for this purpose (above) if ins is set. Why? + * Don't ask pointless questions. + */ + ins = 1; } if (!lexflags && tt0 == NULLTOK) { /* This is done when the lexer reached the word the cursor is on. */ @@ -1322,7 +1344,7 @@ get_comp_string(void) else if (tok == DAMPER) tokstr = "&&"; } - if (!tokstr) + if (!tokstr || noword) continue; /* Hack to allow completion after `repeat n do'. */ if (oins == 2 && !wordpos && !strcmp(tokstr, "do")) @@ -2114,8 +2136,8 @@ inststrlen(char *str, int move, int len) return 0; if (len == -1) len = strlen(str); - spaceinline(len); if (zlemetaline != NULL) { + spaceinline(len); strncpy(zlemetaline + zlemetacs, str, len); if (move) zlemetacs += len; @@ -2126,6 +2148,7 @@ inststrlen(char *str, int move, int len) instr = ztrduppfx(str, len); zlestr = stringaszleline(instr, 0, &zlelen, NULL, NULL); + spaceinline(zlelen); ZS_strncpy(zleline + zlecs, zlestr, zlelen); free(zlestr); zsfree(instr); diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index d0e7b5542..b84d253bb 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -145,6 +145,7 @@ zlecharasstring(ZLE_CHAR_T inchar, char *buf) ptr2--; } *ptr = Meta; + ptr[1] ^= 32; ret++; } diff --git a/Src/builtin.c b/Src/builtin.c index 90fe1a6c5..c3f0169c7 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -55,11 +55,11 @@ static struct builtin builtins[] = BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL), BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL), BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL), - BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmrs", NULL), + BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), BUILTIN("echo", BINF_SKIPINVALID, bin_print, 0, -1, BIN_ECHO, "neE", "-"), BUILTIN("emulate", 0, bin_emulate, 0, -1, 0, "LR", NULL), - BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmrs", NULL), + BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL), BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL), BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL), BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"), @@ -467,7 +467,9 @@ bin_enable(char *name, char **argv, Options ops, int func) int match = 0, returnval = 0; /* Find out which hash table we are working with. */ - if (OPT_ISSET(ops,'f')) + if (OPT_ISSET(ops,'p')) { + return pat_enables(name, argv, func == BIN_ENABLE); + } else if (OPT_ISSET(ops,'f')) ht = shfunctab; else if (OPT_ISSET(ops,'r')) ht = reswdtab; @@ -937,11 +939,16 @@ cd_do_chdir(char *cnam, char *dest, int hard) * DOS style names with drives in them */ static char buf[PATH_MAX]; +#ifdef HAVE_CYGWIN_CONV_PATH + cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, dest, buf, + PATH_MAX); +#else #ifndef _SYS_CYGWIN_H void cygwin_conv_to_posix_path(const char *, char *); #endif cygwin_conv_to_posix_path(dest, buf); +#endif dest = buf; #endif nocdpath = dest[0] == '.' && @@ -1118,7 +1125,8 @@ cd_try_chdir(char *pfix, char *dest, int hard) * argument to cd relatively. This is useful if the cwd * or a parent directory is renamed in the interim. */ - if (lchdir(buf, NULL, hard) && lchdir(dest, NULL, hard)) { + if (lchdir(buf, NULL, hard) && + (pfix || *dest == '/' || lchdir(dest, NULL, hard))) { free(buf); return NULL; } @@ -2470,7 +2478,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) setsparam(asg0.name, ztrdup(asg0.value)); return 0; } else { - zerrnam(name, "can't tie already tied scalar: %s", + zwarnnam(name, "can't tie already tied scalar: %s", asg0.name); } return 1; @@ -2680,7 +2688,7 @@ bin_functions(char *name, char **argv, Options ops, int func) Patprog pprog; Shfunc shf; int i, returnval = 0; - int on = 0, off = 0, pflags = 0; + int on = 0, off = 0, pflags = 0, roff; /* Do we have any flags defined? */ if (OPT_PLUS(ops,'u')) @@ -2699,16 +2707,21 @@ bin_functions(char *name, char **argv, Options ops, int func) on |= PM_TAGGED_LOCAL; else if (OPT_PLUS(ops,'T')) off |= PM_TAGGED_LOCAL; + roff = off; if (OPT_MINUS(ops,'z')) { on |= PM_ZSHSTORED; off |= PM_KSHSTORED; - } else if (OPT_PLUS(ops,'z')) + } else if (OPT_PLUS(ops,'z')) { off |= PM_ZSHSTORED; + roff |= PM_ZSHSTORED; + } if (OPT_MINUS(ops,'k')) { on |= PM_KSHSTORED; off |= PM_ZSHSTORED; - } else if (OPT_PLUS(ops,'k')) + } else if (OPT_PLUS(ops,'k')) { off |= PM_KSHSTORED; + roff |= PM_KSHSTORED; + } if ((off & PM_UNDEFINED) || (OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) || (OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || *argv || !scriptname))) { @@ -2716,7 +2729,7 @@ bin_functions(char *name, char **argv, Options ops, int func) return 1; } - if (OPT_PLUS(ops,'f') || OPT_ISSET(ops,'+')) + if (OPT_PLUS(ops,'f') || roff || OPT_ISSET(ops,'+')) pflags |= PRINT_NAMEONLY; if (OPT_MINUS(ops,'M') || OPT_PLUS(ops,'M')) { @@ -3785,11 +3798,11 @@ bin_print(char *name, char **args, Options ops, int func) /* -u and -p -- output to other than standard output */ if (OPT_HASARG(ops,'u') || OPT_ISSET(ops,'p')) { - int fd; + int fdarg, fd; if (OPT_ISSET(ops, 'p')) { - fd = coprocout; - if (fd < 0) { + fdarg = coprocout; + if (fdarg < 0) { zwarnnam(name, "-p: no coprocess"); return 1; } @@ -3797,13 +3810,13 @@ bin_print(char *name, char **args, Options ops, int func) char *argptr = OPT_ARG(ops,'u'), *eptr; /* Handle undocumented feature that -up worked */ if (!strcmp(argptr, "p")) { - fd = coprocout; - if (fd < 0) { + fdarg= coprocout; + if (fdarg < 0) { zwarnnam(name, "-p: no coprocess"); return 1; } } else { - fd = (int)zstrtol(argptr, &eptr, 10); + fdarg = (int)zstrtol(argptr, &eptr, 10); if (*eptr) { zwarnnam(name, "number expected after -%c: %s", 'u', argptr); @@ -3812,8 +3825,8 @@ bin_print(char *name, char **args, Options ops, int func) } } - if ((fd = dup(fd)) < 0) { - zwarnnam(name, "bad file number: %d", fd); + if ((fd = dup(fdarg)) < 0) { + zwarnnam(name, "bad file number: %d", fdarg); return 1; } if ((fout = fdopen(fd, "w")) == 0) { @@ -5015,6 +5028,7 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) int opt_R = OPT_ISSET(ops, 'R'); int saveemulation, savehackchar; int ret = 1, new_emulation; + unsigned int savepatterns; char saveopts[OPT_SIZE], new_opts[OPT_SIZE]; char *cmd = 0; const char *shname = *argv; @@ -5056,7 +5070,8 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) if (!argv[1]) { emulate(shname, OPT_ISSET(ops,'R'), &emulation, opts); if (OPT_ISSET(ops,'L')) - opts[LOCALOPTIONS] = opts[LOCALTRAPS] = 1; + opts[LOCALOPTIONS] = opts[LOCALTRAPS] = opts[LOCALPATTERNS] = 1; + clearpatterndisables(); return 0; } @@ -5077,6 +5092,13 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) goto restore; } + savepatterns = savepatterndisables(); + /* + * All emulations start with an empty set of pattern disables, + * hence no special "sticky" behaviour is required. + */ + clearpatterndisables(); + saveemulation = emulation; emulation = new_emulation; memcpy(opts, new_opts, sizeof(opts)); @@ -5126,6 +5148,7 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func)) sticky = save_sticky; emulation = saveemulation; memcpy(opts, saveopts, sizeof(opts)); + restorepatterndisables(savepatterns); restore: keyboardhackchar = savehackchar; inittyptab(); /* restore banghist */ @@ -5490,6 +5513,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) eof = 2; else eof = (bptr - buf != 1 || (buf[0] != 'y' && buf[0] != 'Y')); + buf[0] = eof ? 'n' : 'y'; + bptr = buf + 1; } if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) fwrite(buf, bptr - buf, 1, stdout); @@ -5667,7 +5692,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) zputs(buf, stdout); putchar('\n'); } - if (!OPT_ISSET(ops,'e') && (*buf || first)) { + if (!OPT_ISSET(ops,'e') && (*buf || first || gotnl)) { if (OPT_ISSET(ops,'A')) { addlinknode(readll, buf); al++; @@ -5970,7 +5995,7 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) char **s; Eprog prog; struct estate state; - int nargs; + int nargs, sense = 0, ret; /* if "test" was invoked as "[", it needs a matching "]" * * which is subsequently ignored */ @@ -5989,7 +6014,7 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) /* * Implement some XSI extensions to POSIX here. * See - * http://www.opengroup.org/onlinepubs/009695399/utilities/test.html. + * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html */ nargs = arrlen(argv); if (nargs == 3 || nargs == 4) @@ -5998,6 +6023,10 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) argv[nargs-1] = NULL; argv++; } + if (nargs == 4 && !strcmp("!", argv[0])) { + sense = 1; + argv++; + } } lexsave(); @@ -6032,8 +6061,11 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) state.pc = prog->prog; state.strs = prog->strs; + ret = evalcond(&state, name); + if (ret < 2 && sense) + ret = ! ret; - return evalcond(&state, name); + return ret; } /* display a time, provided in units of 1/60s, as minutes and seconds */ diff --git a/Src/exec.c b/Src/exec.c index 1ecbc3967..df915e152 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -448,10 +448,13 @@ zexecve(char *pth, char **argv, char **newenvp) else sprintf(buf + 2, "%s/%s", pwd, pth); zputenv(buf); +#ifndef FD_CLOEXEC closedumps(); +#endif if (newenvp == NULL) newenvp = environ; + winch_unblock(); execve(pth, argv, newenvp); /* If the execve returns (which in general shouldn't happen), * @@ -486,6 +489,7 @@ zexecve(char *pth, char **argv, char **newenvp) (pprog = pathprog(ptr2, NULL))) { argv[-2] = ptr2; argv[-1] = ptr + 1; + winch_unblock(); execve(pprog, argv - 2, newenvp); } zerr("%s: bad interpreter: %s: %e", pth, ptr2, @@ -494,13 +498,16 @@ zexecve(char *pth, char **argv, char **newenvp) *ptr = '\0'; argv[-2] = ptr2; argv[-1] = ptr + 1; + winch_unblock(); execve(ptr2, argv - 2, newenvp); } else { argv[-1] = ptr2; + winch_unblock(); execve(ptr2, argv - 1, newenvp); } } else if (eno == ENOEXEC) { argv[-1] = "sh"; + winch_unblock(); execve("/bin/sh", argv - 1, newenvp); } } else if (eno == ENOEXEC) { @@ -509,6 +516,7 @@ zexecve(char *pth, char **argv, char **newenvp) break; if (t0 == ct) { argv[-1] = "sh"; + winch_unblock(); execve("/bin/sh", argv - 1, newenvp); } } @@ -1019,6 +1027,11 @@ execstring(char *s, int dont_change_job, int exiting, char *context) Eprog prog; pushheap(); + if (isset(VERBOSE)) { + zputs(s, stderr); + fputc('\n', stderr); + fflush(stderr); + } if ((prog = parse_string(s, 0))) execode(prog, dont_change_job, exiting, context); popheap(); @@ -1079,6 +1092,9 @@ execsimple(Estate state) if (errflag) return (lastval = 1); + if (!isset(EXECOPT)) + return lastval = 0; + /* In evaluated traps, don't modify the line number. */ if (!IN_EVAL_TRAP() && !ineval && code) lineno = code - 1; @@ -1645,8 +1661,6 @@ execpline(Estate state, wordcode slcode, int how, int last1) return lastval; } -static int subsh_close = -1; - /* execute pipeline. This function assumes the `pline' is not NULL. */ /**/ @@ -1723,7 +1737,7 @@ execpline2(Estate state, wordcode pcode, } } else { /* otherwise just do the pipeline normally. */ - subsh_close = pipes[0]; + addfilelist(NULL, pipes[0]); execcmd(state, input, pipes[1], how, 0); } zclose(pipes[1]); @@ -1736,8 +1750,6 @@ execpline2(Estate state, wordcode pcode, execpline2(state, *state->pc++, how, pipes[0], output, last1); list_pipe = old_list_pipe; cmdpop(); - zclose(pipes[0]); - subsh_close = -1; } } @@ -1841,9 +1853,22 @@ quote_tokenized_output(char *str, FILE *file) case '*': case '?': case '$': + case ' ': putc('\\', file); break; + case '\t': + fputs("$'\\t'", file); + continue; + + case '\n': + fputs("$'\\n'", file); + continue; + + case '\r': + fputs("$'\\r'", file); + continue; + case '=': if (s == str) putc('\\', file); @@ -1885,7 +1910,14 @@ checkclobberparam(struct redir *f) return 0; } - if (!isset(CLOBBER) && (fd = (int)getintvalue(v)) && + /* + * We can't clobber the value in the parameter if it's + * already an opened file descriptor --- that means it's a decimal + * integer corresponding to an opened file descriptor, + * not merely an expression that evaluates to a file descriptor. + */ + if (!isset(CLOBBER) && (s = getstrvalue(v)) && + (fd = (int)zstrtol(s, &s, 10)) >= 0 && !*s && fd <= max_zsh_fd && fdtable[fd] == FDT_EXTERNAL) { zwarn("can't clobber parameter %s containing file descriptor %d", f->varid, fd); @@ -1935,7 +1967,7 @@ clobber_open(struct redir *f) /**/ static void -closemn(struct multio **mfds, int fd) +closemn(struct multio **mfds, int fd, int type) { if (fd >= 0 && mfds[fd] && mfds[fd]->ct >= 2) { struct multio *mn = mfds[fd]; @@ -1994,7 +2026,7 @@ closemn(struct multio **mfds, int fd) } } _exit(0); - } else if (fd >= 0) + } else if (fd >= 0 && type == REDIR_CLOSE) mfds[fd] = NULL; } @@ -2147,8 +2179,6 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, mfds[fd1]->fds[mfds[fd1]->ct++] = fdN; } } - if (subsh_close >= 0 && fdtable[subsh_close] == FDT_UNUSED) - subsh_close = -1; } /**/ @@ -2847,9 +2877,6 @@ execcmd(Estate state, int input, int output, int how, int last1) close(synch[1]); read_loop(synch[0], &dummy, 1); close(synch[0]); -#ifdef PATH_DEV_FD - closem(FDT_PROC_SUBST); -#endif if (how & Z_ASYNC) { lastpid = (zlong) pid; /* indicate it's possible to set status for lastpid */ @@ -3066,7 +3093,7 @@ execcmd(Estate state, int input, int output, int how, int last1) } } if (fn->fd1 < 10) - closemn(mfds, fn->fd1); + closemn(mfds, fn->fd1, REDIR_CLOSE); if (!closed && zclose(fn->fd1) < 0) { zwarn("failed to close file descriptor %d: %e", fn->fd1, errno); @@ -3075,7 +3102,7 @@ execcmd(Estate state, int input, int output, int how, int last1) case REDIR_MERGEIN: case REDIR_MERGEOUT: if (fn->fd2 < 10) - closemn(mfds, fn->fd2); + closemn(mfds, fn->fd2, fn->type); if (!checkclobberparam(fn)) fil = -1; else if (fn->fd2 > 9 && @@ -3096,7 +3123,7 @@ execcmd(Estate state, int input, int output, int how, int last1) int fd = fn->fd2; if(fd == -2) fd = (fn->type == REDIR_MERGEOUT) ? coprocout : coprocin; - fil = dup(fd); + fil = movefd(dup(fd)); } if (fil == -1) { char fdstr[4]; @@ -3124,7 +3151,7 @@ execcmd(Estate state, int input, int output, int how, int last1) else fil = clobber_open(fn); if(fil != -1 && IS_ERROR_REDIR(fn->type)) - dfil = dup(fil); + dfil = movefd(dup(fil)); else dfil = 0; if (fil == -1 || dfil == -1) { @@ -3154,7 +3181,7 @@ execcmd(Estate state, int input, int output, int how, int last1) * spawning tee/cat processes as necessary. */ for (i = 0; i < 10; i++) if (mfds[i] && mfds[i]->ct >= 2) - closemn(mfds, i); + closemn(mfds, i, REDIR_CLOSE); if (nullexec) { if (nullexec == 1) { @@ -3233,33 +3260,12 @@ execcmd(Estate state, int input, int output, int how, int last1) if (is_shfunc) { /* It's a shell function */ - -#ifdef PATH_DEV_FD - int i; - - for (i = 10; i <= max_zsh_fd; i++) - if (fdtable[i] >= FDT_PROC_SUBST) - fdtable[i]++; -#endif - if (subsh_close >= 0) - zclose(subsh_close); - subsh_close = -1; - execshfunc((Shfunc) hn, args); -#ifdef PATH_DEV_FD - for (i = 10; i <= max_zsh_fd; i++) - if (fdtable[i] >= FDT_PROC_SUBST) - if (--(fdtable[i]) <= FDT_PROC_SUBST) - zclose(i); -#endif } else { /* It's a builtin */ if (forked) closem(FDT_INTERNAL); lastval = execbuiltin(args, (Builtin) hn); -#ifdef PATH_DEV_FD - closem(FDT_PROC_SUBST); -#endif fflush(stdout); if (save[1] == -2) { if (ferror(stdout)) { @@ -3329,9 +3335,6 @@ execcmd(Estate state, int input, int output, int how, int last1) DPUTS(varspc, "BUG: assignment before complex command"); list_pipe = 0; - if (subsh_close >= 0) - zclose(subsh_close); - subsh_close = -1; /* If we're forked (and we should be), no need to return */ DPUTS(last1 != 1 && !forked, "BUG: not exiting?"); DPUTS(type != WC_SUBSH, "Not sure what we're doing."); @@ -3874,9 +3877,7 @@ getoutputfile(char *cmd, char **eptr) untokenize(s); } - if (!jobtab[thisjob].filelist) - jobtab[thisjob].filelist = znewlinklist(); - zaddlinknode(jobtab[thisjob].filelist, nam); + addfilelist(nam, 0); if (!s) child_block(); @@ -3962,9 +3963,7 @@ getproc(char *cmd, char **eptr) return NULL; if (!(prog = parsecmd(cmd, eptr))) return NULL; - if (!jobtab[thisjob].filelist) - jobtab[thisjob].filelist = znewlinklist(); - zaddlinknode(jobtab[thisjob].filelist, ztrdup(pnam)); + addfilelist(pnam, 0); if ((pid = zfork(&bgtime))) { if (pid == -1) @@ -3982,7 +3981,7 @@ getproc(char *cmd, char **eptr) entersubsh(ESUB_ASYNC|ESUB_PGRP); redup(fd, out); #else /* PATH_DEV_FD */ - int pipes[2]; + int pipes[2], fd; if (thisjob == -1) return NULL; @@ -3999,7 +3998,9 @@ getproc(char *cmd, char **eptr) zclose(pipes[!out]); return NULL; } - fdtable[pipes[!out]] = FDT_PROC_SUBST; + fd = pipes[!out]; + fdtable[fd] = FDT_PROC_SUBST; + addfilelist(NULL, fd); if (!out) { addproc(pid, NULL, 1, &bgtime); @@ -4614,6 +4615,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) } starttrapscope(); + startpatternscope(); pptab = pparams; if (!(flags & PM_UNDEFINED)) @@ -4661,6 +4663,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) offptr++) opts[*offptr] = 0; } + /* All emulations start with pattern disables clear */ + clearpatterndisables(); } else restore_sticky = 0; @@ -4761,6 +4765,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) scriptname = oldscriptname; oflags = ooflags; + endpatternscope(); /* before restoring old LOCALPATTERNS */ + if (restore_sticky) { /* * If we switched to an emulation environment just for @@ -5070,7 +5076,6 @@ execsave(void) es->trapisfunc = trapisfunc; es->traplocallevel = traplocallevel; es->noerrs = noerrs; - es->subsh_close = subsh_close; es->underscore = ztrdup(zunderscore); es->next = exstack; exstack = es; @@ -5081,30 +5086,33 @@ execsave(void) void execrestore(void) { - struct execstack *en; + struct execstack *en = exstack; DPUTS(!exstack, "BUG: execrestore() without execsave()"); - list_pipe_pid = exstack->list_pipe_pid; - nowait = exstack->nowait; - pline_level = exstack->pline_level; - list_pipe_child = exstack->list_pipe_child; - list_pipe_job = exstack->list_pipe_job; - strcpy(list_pipe_text, exstack->list_pipe_text); - lastval = exstack->lastval; - noeval = exstack->noeval; - badcshglob = exstack->badcshglob; - cmdoutpid = exstack->cmdoutpid; - cmdoutval = exstack->cmdoutval; - use_cmdoutval = exstack->use_cmdoutval; - trap_return = exstack->trap_return; - trap_state = exstack->trap_state; - trapisfunc = exstack->trapisfunc; - traplocallevel = exstack->traplocallevel; - noerrs = exstack->noerrs; - subsh_close = exstack->subsh_close; - setunderscore(exstack->underscore); - zsfree(exstack->underscore); - en = exstack->next; - free(exstack); - exstack = en; + + queue_signals(); + exstack = exstack->next; + + list_pipe_pid = en->list_pipe_pid; + nowait = en->nowait; + pline_level = en->pline_level; + list_pipe_child = en->list_pipe_child; + list_pipe_job = en->list_pipe_job; + strcpy(list_pipe_text, en->list_pipe_text); + lastval = en->lastval; + noeval = en->noeval; + badcshglob = en->badcshglob; + cmdoutpid = en->cmdoutpid; + cmdoutval = en->cmdoutval; + use_cmdoutval = en->use_cmdoutval; + trap_return = en->trap_return; + trap_state = en->trap_state; + trapisfunc = en->trapisfunc; + traplocallevel = en->traplocallevel; + noerrs = en->noerrs; + setunderscore(en->underscore); + zsfree(en->underscore); + free(en); + + unqueue_signals(); } diff --git a/Src/glob.c b/Src/glob.c index ca2ffaf51..e0d0cf68e 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -445,41 +445,6 @@ insert(char *s, int checked) unqueue_signals(); } -/* Check to see if str is eligible for filename generation. */ - -/**/ -mod_export int -haswilds(char *str) -{ - /* `[' and `]' are legal even if bad patterns are usually not. */ - if ((*str == Inbrack || *str == Outbrack) && !str[1]) - return 0; - - /* If % is immediately followed by ?, then that ? is * - * not treated as a wildcard. This is so you don't have * - * to escape job references such as %?foo. */ - if (str[0] == '%' && str[1] == Quest) - str[1] = '?'; - - for (; *str; str++) { - switch (*str) { - case Inpar: - case Bar: - case Star: - case Inbrack: - case Inang: - case Quest: - return 1; - case Pound: - case Hat: - if (isset(EXTENDEDGLOB)) - return 1; - break; - } - } - return 0; -} - /* Do the globbing: scanner is called recursively * * with successive bits of the path until we've * * tried all of it. */ @@ -708,8 +673,9 @@ parsecomplist(char *instr) } /* Parse repeated directories such as (dir/)# and (dir/)## */ - if (*(str = instr) == Inpar && !skipparens(Inpar, Outpar, (char **)&str) && - *str == Pound && isset(EXTENDEDGLOB) && str[-2] == '/') { + if (*(str = instr) == zpc_special[ZPC_INPAR] && + !skipparens(Inpar, Outpar, (char **)&str) && + *str == zpc_special[ZPC_HASH] && str[-2] == '/') { instr++; if (!(p1 = patcompile(instr, compflags, &instr))) return NULL; @@ -761,9 +727,9 @@ parsepat(char *str) * Check for initial globbing flags, so that they don't form * a bogus path component. */ - if ((*str == Inpar && str[1] == Pound && isset(EXTENDEDGLOB)) || - (isset(KSHGLOB) && *str == '@' && str[1] == Inpar && - str[2] == Pound)) { + if ((*str == zpc_special[ZPC_INPAR] && str[1] == zpc_special[ZPC_HASH]) || + (*str == zpc_special[ZPC_KSH_AT] && str[1] == Inpar && + str[2] == zpc_special[ZPC_HASH])) { str += (*str == Inpar) ? 2 : 3; if (!patgetglobflags(&str, &assert, &ignore)) return NULL; @@ -1146,7 +1112,8 @@ zglob(LinkList list, LinkNode np, int nountok) gf_pre_words = NULL; /* Check for qualifiers */ - while (!nobareglob || isset(EXTENDEDGLOB)) { + while (!nobareglob || + (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH])) { struct qual *newquals; char *s; int sense, paren; @@ -1192,10 +1159,11 @@ zglob(LinkList list, LinkNode np, int nountok) case Outpar: paren++; /*FALLTHROUGH*/ case Bar: - nobareglob = 1; + if (!zpc_disables[ZPC_BAR]) + nobareglob = 1; break; case Tilde: - if (isset(EXTENDEDGLOB)) + if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE]) nobareglob = 1; break; case Inpar: @@ -1205,7 +1173,7 @@ zglob(LinkList list, LinkNode np, int nountok) } if (*s != Inpar) break; - if (isset(EXTENDEDGLOB) && s[1] == Pound) { + if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) { if (s[2] == 'q') { *s = 0; s += 2; @@ -1644,7 +1612,8 @@ zglob(LinkList list, LinkNode np, int nountok) break; } default: - zerr("unknown file attribute"); + untokenize(--s); + zerr("unknown file attribute: %c", *s); restore_globstate(saved); return; } diff --git a/Src/hist.c b/Src/hist.c index 561e2acd5..1845bd8ad 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -76,6 +76,9 @@ mod_export int excs, exlast; * and a temporary history entry is inserted while the user is editing. * If the resulting line was not added to the list, a flag is set so * that curhist will be decremented in hbegin(). + * + * Note curhist is passed to zle on variable length argument list: + * type must match that retrieved in zle_main_entry. */ /**/ @@ -518,6 +521,12 @@ histsubchar(int c) } c = ingetc(); } + if (ptr == buf && + (c == '}' || c == ';' || c == '\'' || c == '"' || c == '`')) { + /* Neither event nor word designator, no expansion */ + safeinungetc(c); + return bangchar; + } *ptr = 0; if (!*buf) { if (c != '%') { @@ -868,6 +877,8 @@ unlinkcurline(void) mod_export void hbegin(int dohist) { + char *hf; + isfirstln = isfirstch = 1; errflag = histdone = 0; if (!dohist) @@ -921,13 +932,35 @@ hbegin(int dohist) defev = addhistnum(curhist, -1, HIST_FOREIGN); } else histactive = HA_ACTIVE | HA_NOINC; + + hf = getsparam("HISTFILE"); + /* + * For INCAPPENDHISTORY, when interactive, save the history here + * as it gives a better estimate of the times of commands. + * + * If SHAREHISTORY is also set continue to do so in the + * standard place, because that's safer about reading and + * rewriting history atomically. + * + * The histsave_stack_pos test won't usually fail here. + * We need to test the opposite for the hend() case because we + * need to save in the history file we've switched to, but then + * we pop immediately after that so the variable becomes zero. + * We will already have saved the line and restored the history + * so that (correctly) nothing happens here. But it shows + * I thought about it. + */ + if (isset(INCAPPENDHISTORY) && !isset(SHAREHISTORY) && + !(histactive & HA_NOINC) && !strin && histsave_stack_pos == 0) + savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST); } /**/ void histreduceblanks(void) { - int i, len, pos, needblank, spacecount = 0; + int i, len, pos, needblank, spacecount = 0, trunc_ok; + char *lastptr, *ptr; if (isset(HISTIGNORESPACE)) while (chline[spacecount] == ' ') spacecount++; @@ -939,17 +972,41 @@ histreduceblanks(void) if (chline[len] == '\0') return; + /* Remember where the delimited words end */ + if (chwordpos) + lastptr = chline + chwords[chwordpos-1]; + else + lastptr = chline; + for (i = 0, pos = spacecount; i < chwordpos; i += 2) { len = chwords[i+1] - chwords[i]; needblank = (i < chwordpos-2 && chwords[i+2] > chwords[i+1]); if (pos != chwords[i]) { - memcpy(chline + pos, chline + chwords[i], len + needblank); + memmove(chline + pos, chline + chwords[i], len + needblank); chwords[i] = pos; chwords[i+1] = chwords[i] + len; } pos += len + needblank; } - chline[pos] = '\0'; + + /* + * A terminating comment isn't recorded as a word. + * Only truncate the line if just whitespace remains. + */ + trunc_ok = 1; + for (ptr = lastptr; *ptr; ptr++) { + if (!inblank(*ptr)) { + trunc_ok = 0; + break; + } + } + if (trunc_ok) { + chline[pos] = '\0'; + } else { + ptr = chline + pos; + while ((*ptr++ = *lastptr++)) + ; + } } /**/ @@ -1169,7 +1226,15 @@ mod_export int hend(Eprog prog) { LinkList hookargs = newlinklist(); - int flag, save = 1, hookret, stack_pos = histsave_stack_pos; + int flag, hookret, stack_pos = histsave_stack_pos; + /* + * save: + * 0: don't save + * 1: save normally + * -1: save temporarily, delete after next line + * -2: save internally but mark for not writing + */ + int save = 1; char *hf; DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline, @@ -1222,7 +1287,11 @@ hend(Eprog prog) } if (chwordpos <= 2) save = 0; - else if (hookret || should_ignore_line(prog)) + else if (should_ignore_line(prog)) + save = -1; + else if (hookret == 2) + save = -2; + else if (hookret) save = -1; } if (flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) { @@ -1268,7 +1337,12 @@ hend(Eprog prog) if (isset(HISTREDUCEBLANKS)) histreduceblanks(); } - newflags = save > 0? 0 : HIST_TMPSTORE; + if (save == -1) + newflags = HIST_TMPSTORE; + else if (save == -2) + newflags = HIST_NOWRITE; + else + newflags = 0; if ((isset(HISTIGNOREDUPS) || isset(HISTIGNOREALLDUPS)) && save > 0 && hist_ring && histstrcmp(chline, hist_ring->node.nam) == 0) { /* This history entry compares the same as the previous. @@ -1300,7 +1374,11 @@ hend(Eprog prog) chline = hptr = NULL; chwords = NULL; histactive = 0; - if (isset(SHAREHISTORY)? histfileIsLocked() : isset(INCAPPENDHISTORY)) + /* + * For normal INCAPPENDHISTORY case and reasoning, see hbegin(). + */ + if (isset(SHAREHISTORY) ? histfileIsLocked() : + (isset(INCAPPENDHISTORY) && histsave_stack_pos != 0)) savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST); unlockhistfile(hf); /* It's OK to call this even if we aren't locked */ /* @@ -2389,6 +2467,9 @@ readhistfile(char *fn, int err, int readflags) zerr("can't read history file %s", fn); unlockhistfile(fn); + + if (zleactive) + zleentry(ZLE_CMD_SET_HIST_LINE, curhist); } #ifdef HAVE_FCNTL_H @@ -2519,14 +2600,29 @@ savehistfile(char *fn, int err, int writeflags) } } if (out) { + char *history_ignore; + Patprog histpat = NULL; + + pushheap(); + + if ((history_ignore = getsparam("HISTORY_IGNORE")) != NULL) { + tokenize(history_ignore = dupstring(history_ignore)); + remnulargs(history_ignore); + histpat = patcompile(history_ignore, 0, NULL); + } + ret = 0; for (; he && he->histnum <= xcurhist; he = down_histent(he)) { if ((writeflags & HFILE_SKIPDUPS && he->node.flags & HIST_DUP) || (writeflags & HFILE_SKIPFOREIGN && he->node.flags & HIST_FOREIGN) || he->node.flags & HIST_TMPSTORE) continue; + if (histpat && + pattry(histpat, metafy(he->node.nam, -1, META_HEAPDUP))) { + continue; + } if (writeflags & HFILE_SKIPOLD) { - if (he->node.flags & HIST_OLD) + if (he->node.flags & (HIST_OLD|HIST_NOWRITE)) continue; he->node.flags |= HIST_OLD; if (writeflags & HFILE_USE_OPTIONS) @@ -2604,6 +2700,8 @@ savehistfile(char *fn, int err, int writeflags) histactive = remember_histactive; } } + + popheap(); } else ret = -1; @@ -3314,6 +3412,8 @@ pushhiststack(char *hf, zlong hs, zlong shs, int level) } hist_ring = NULL; curhist = histlinect = 0; + if (zleactive) + zleentry(ZLE_CMD_SET_HIST_LINE, curhist); histsiz = hs; savehistsiz = shs; inithist(); /* sets histtab */ @@ -3353,6 +3453,8 @@ pophiststack(void) histtab = h->histtab; hist_ring = h->hist_ring; curhist = h->curhist; + if (zleactive) + zleentry(ZLE_CMD_SET_HIST_LINE, curhist); histlinect = h->histlinect; histsiz = h->histsiz; savehistsiz = h->savehistsiz; diff --git a/Src/init.c b/Src/init.c index 8467a739c..7032ff8ae 100644 --- a/Src/init.c +++ b/Src/init.c @@ -281,9 +281,10 @@ parseargs(char **argv, char **runscript) /**/ static void -parseopts_insert(LinkList optlist, void *ptr) +parseopts_insert(LinkList optlist, void *base, int optno) { LinkNode node; + void *ptr = base + (optno < 0 ? -optno : optno); for (node = firstnode(optlist); node; incnode(node)) { if (ptr < getdata(node)) { @@ -390,7 +391,7 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp, if (dosetopt(optno, action, !nam, new_opts) && nam) { WARN_OPTION("can't change option: %s", *argv); } else if (optlist) { - parseopts_insert(optlist, new_opts+optno); + parseopts_insert(optlist, new_opts, optno); } } break; @@ -415,7 +416,7 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp, if (dosetopt(optno, action, !nam, new_opts) && nam) { WARN_OPTION("can't change option: -%c", **argv); } else if (optlist) { - parseopts_insert(optlist, new_opts+optno); + parseopts_insert(optlist, new_opts, optno); } } } @@ -1113,6 +1114,7 @@ init_signals(void) install_handler(SIGCHLD); #ifdef SIGWINCH install_handler(SIGWINCH); + winch_block(); /* See utils.c:preprompt() */ #endif if (interact) { install_handler(SIGALRM); @@ -1582,6 +1584,7 @@ zsh_main(UNUSED(int argc), char **argv) fdtable_size = zopenmax(); fdtable = zshcalloc(fdtable_size*sizeof(*fdtable)); + fdtable[0] = fdtable[1] = fdtable[2] = FDT_EXTERNAL; createoptiontable(); emulate(zsh_name, 1, &emulation, opts); /* initialises most options */ diff --git a/Src/input.c b/Src/input.c index 5cff22da4..4ac7e6ec8 100644 --- a/Src/input.c +++ b/Src/input.c @@ -142,12 +142,14 @@ shingetline(void) char *p; p = buf; + winch_unblock(); for (;;) { do { errno = 0; c = fgetc(bshin); } while (c < 0 && errno == EINTR); if (c < 0 || c == '\n') { + winch_block(); if (c == '\n') *p++ = '\n'; if (p > buf) { @@ -163,11 +165,13 @@ shingetline(void) } else *p++ = c; if (p >= buf + BUFSIZ - 1) { + winch_block(); line = zrealloc(line, ll + (p - buf) + 1); memcpy(line + ll, buf, p - buf); ll += p - buf; line[ll] = '\0'; p = buf; + winch_unblock(); } } } diff --git a/Src/jobs.c b/Src/jobs.c index 0dbb10b4f..371b8eb32 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -376,6 +376,36 @@ check_cursh_sig(int sig) } } +/**/ +void +storepipestats(Job jn, int inforeground, int fixlastval) +{ + int i, pipefail = 0, jpipestats[MAX_PIPESTATS]; + Process p; + + for (p = jn->procs, i = 0; p && i < MAX_PIPESTATS; p = p->next, i++) { + jpipestats[i] = ((WIFSIGNALED(p->status)) ? + 0200 | WTERMSIG(p->status) : + WEXITSTATUS(p->status)); + if (jpipestats[i]) + pipefail = jpipestats[i]; + } + if (inforeground) { + memcpy(pipestats, jpipestats, sizeof(int)*i); + if ((jn->stat & STAT_CURSH) && i < MAX_PIPESTATS) + pipestats[i++] = lastval; + numpipestats = i; + } + + if (fixlastval) { + if (jn->stat & STAT_CURSH) { + if (!lastval && isset(PIPEFAIL)) + lastval = pipefail; + } else if (isset(PIPEFAIL)) + lastval = pipefail; + } +} + /* Update status of job, possibly printing it */ /**/ @@ -507,17 +537,13 @@ update_job(Job jn) return; jn->stat |= (somestopped) ? STAT_CHANGED | STAT_STOPPED : STAT_CHANGED | STAT_DONE; - if (job == thisjob && (jn->stat & STAT_DONE)) { - int i; - Process p; - - for (p = jn->procs, i = 0; p && i < MAX_PIPESTATS; p = p->next, i++) - pipestats[i] = ((WIFSIGNALED(p->status)) ? - 0200 | WTERMSIG(p->status) : - WEXITSTATUS(p->status)); - if ((jn->stat & STAT_CURSH) && i < MAX_PIPESTATS) - pipestats[i++] = lastval; - numpipestats = i; + if (jn->stat & STAT_DONE) { + /* This may be redundant with printjob() but note that inforeground + * is true here for STAT_CURSH jobs even when job != thisjob, most + * likely because thisjob = -1 from exec.c:execsimple() trickery. + * However, if we reset lastval here we break it for printjob(). + */ + storepipestats(jn, inforeground, 0); } if (!inforeground && (jn->stat & (STAT_SUBJOB | STAT_DONE)) == (STAT_SUBJOB | STAT_DONE)) { @@ -915,10 +941,13 @@ printjob(Job jn, int lng, int synch) int doneprint = 0, skip_print = 0; FILE *fout = (synch == 2 || !shout) ? stdout : shout; - if (oldjobtab != NULL) + if (synch > 1 && oldjobtab != NULL) job = jn - oldjobtab; else job = jn - jobtab; + DPUTS3(job < 0 || job > (oldjobtab && synch > 1 ? oldmaxjob : maxjob), + "bogus job number, jn = %L, jobtab = %L, oldjobtab = %L", + (long)jn, (long)jobtab, (long)oldjobtab); if (jn->stat & STAT_NOPRINT) { skip_print = 1; @@ -968,6 +997,9 @@ printjob(Job jn, int lng, int synch) if (skip_print) { if (jn->stat & STAT_DONE) { + /* This looks silly, but see update_job() */ + if (synch <= 1) + storepipestats(jn, job == thisjob, job == thisjob); if (should_report_time(jn)) dumptime(jn); deletejob(jn, 0); @@ -1083,9 +1115,9 @@ printjob(Job jn, int lng, int synch) fflush(fout); } -/* print "(pwd now: foo)" messages: with (lng & 4) we are printing - * the directory where the job is running, otherwise the current directory - */ + /* print "(pwd now: foo)" messages: with (lng & 4) we are printing + * the directory where the job is running, otherwise the current directory + */ if ((lng & 4) || (interact && job == thisjob && jn->pwd && strcmp(jn->pwd, pwd))) { @@ -1095,9 +1127,13 @@ printjob(Job jn, int lng, int synch) fprintf(fout, ")\n"); fflush(fout); } -/* delete job if done */ + + /* delete job if done */ if (jn->stat & STAT_DONE) { + /* This looks silly, but see update_job() */ + if (synch <= 1) + storepipestats(jn, job == thisjob, job == thisjob); if (should_report_time(jn)) dumptime(jn); deletejob(jn, 0); @@ -1113,16 +1149,48 @@ printjob(Job jn, int lng, int synch) return doneprint; } +/* Add a file to be deleted or fd to be closed to the current job */ + +/**/ +void +addfilelist(const char *name, int fd) +{ + Jobfile jf = (Jobfile)zalloc(sizeof(struct jobfile)); + LinkList ll = jobtab[thisjob].filelist; + + if (!ll) + ll = jobtab[thisjob].filelist = znewlinklist(); + if (name) + { + jf->u.name = ztrdup(name); + jf->is_fd = 0; + } + else + { + jf->u.fd = fd; + jf->is_fd = 1; + } + zaddlinknode(ll, jf); +} + +/* Finished with list of files for a job */ + /**/ void deletefilelist(LinkList file_list, int disowning) { - char *s; + Jobfile jf; if (file_list) { - while ((s = (char *)getlinknode(file_list))) { - if (!disowning) - unlink(s); - zsfree(s); + while ((jf = (Jobfile)getlinknode(file_list))) { + if (jf->is_fd) { + if (!disowning) + zclose(jf->u.fd); + } else { + if (!disowning) + unlink(jf->u.name); + zsfree(jf->u.name); + } + zfree(jf, sizeof(*jf)); } zfree(file_list, sizeof(struct linklist)); } @@ -1336,6 +1404,31 @@ zwaitjob(int job, int wait_cmd) jn->stat |= STAT_LOCKED; if (jn->stat & STAT_CHANGED) printjob(jn, !!isset(LONGLISTJOBS), 1); + if (jn->filelist) { + /* + * The main shell is finished with any file descriptors used + * for process substitution associated with this job: close + * them to indicate to listeners there's no more input. + * + * Note we can't safely delete temporary files yet as these + * are directly visible to other processes. However, + * we can't deadlock on the fact that those still exist, so + * that's not a problem. + */ + LinkNode node = firstnode(jn->filelist); + while (node) { + Jobfile jf = (Jobfile)getdata(node); + if (jf->is_fd) { + LinkNode next = nextnode(node); + (void)remnode(jn->filelist, node); + zclose(jf->u.fd); + zfree(jf, sizeof(*jf)); + node = next; + } else { + incnode(node); + } + } + } while (!errflag && jn->stat && !(jn->stat & STAT_DONE) && !(interact && (jn->stat & STAT_STOPPED))) { @@ -2522,6 +2615,8 @@ acquire_pgrp(void) while ((ttpgrp = gettygrp()) != -1 && ttpgrp != mypgrp) { mypgrp = GETPGRP(); if (mypgrp == mypid) { + if (!interact) + break; /* attachtty() will be a no-op, give up */ signal_setmask(oldset); attachtty(mypgrp); /* Might generate SIGT* */ signal_block(blockset); @@ -778,6 +778,7 @@ gettok(void) bptr = tokstr = (char *)hcalloc(bsiz = 32); add(c); } + hwend(); while ((c = ingetc()) != '\n' && !lexstop) { hwaddc(c); addtoline(c); diff --git a/Src/math.c b/Src/math.c index e90d6a59a..eae283d19 100644 --- a/Src/math.c +++ b/Src/math.c @@ -362,8 +362,9 @@ mathevall(char *s, enum prec_type prec_tp, char **ep) if (mlevel >= MAX_MLEVEL) { xyyval.type = MN_INTEGER; xyyval.u.l = 0; + *ep = s; - zerr("math recursion limit exceeded"); + zerr("math recursion limit exceeded: %s", *ep); return xyyval; } @@ -456,6 +457,11 @@ lexconstant(void) yyval.u.l = zstrtol_underscore(ptr, &ptr, 0, 1); /* Should we set lastbase here? */ lastbase = 16; + if (isset(FORCEFLOAT)) + { + yyval.type = MN_FLOAT; + yyval.u.d = (double)yyval.u.l; + } return NUM; } else if (isset(OCTALZEROES)) @@ -475,6 +481,11 @@ lexconstant(void) { yyval.u.l = zstrtol_underscore(ptr, &ptr, 0, 1); lastbase = 8; + if (isset(FORCEFLOAT)) + { + yyval.type = MN_FLOAT; + yyval.u.d = (double)yyval.u.l; + } return NUM; } nptr = ptr2; @@ -537,6 +548,11 @@ lexconstant(void) lastbase = yyval.u.l; yyval.u.l = zstrtol_underscore(ptr, &ptr, lastbase, 1); } + if (isset(FORCEFLOAT)) + { + yyval.type = MN_FLOAT; + yyval.u.d = (double)yyval.u.l; + } } return NUM; } @@ -1443,7 +1459,8 @@ mathparse(int pc) case QUEST: if (stack[sp].val.type == MN_UNSET) stack[sp].val = getmathparam(stack + sp); - q = (stack[sp].val.type == MN_FLOAT) ? (zlong)stack[sp].val.u.d : + q = (stack[sp].val.type == MN_FLOAT) ? + (stack[sp].val.u.d == 0 ? 0 : 1) : stack[sp].val.u.l; if (!q) diff --git a/Src/options.c b/Src/options.c index b36bd9944..ce73d9901 100644 --- a/Src/options.c +++ b/Src/options.c @@ -131,6 +131,7 @@ static struct optname optns[] = { {{NULL, "extendedhistory", OPT_CSH}, EXTENDEDHISTORY}, {{NULL, "evallineno", OPT_EMULATE|OPT_ZSH}, EVALLINENO}, {{NULL, "flowcontrol", OPT_ALL}, FLOWCONTROL}, +{{NULL, "forcefloat", 0}, FORCEFLOAT}, {{NULL, "functionargzero", OPT_EMULATE|OPT_NONBOURNE},FUNCTIONARGZERO}, {{NULL, "glob", OPT_EMULATE|OPT_ALL}, GLOBOPT}, {{NULL, "globalexport", OPT_EMULATE|OPT_ZSH}, GLOBALEXPORT}, @@ -178,6 +179,7 @@ static struct optname optns[] = { {{NULL, "listrowsfirst", 0}, LISTROWSFIRST}, {{NULL, "listtypes", OPT_ALL}, LISTTYPES}, {{NULL, "localoptions", OPT_EMULATE|OPT_KSH}, LOCALOPTIONS}, +{{NULL, "localpatterns", OPT_EMULATE}, LOCALPATTERNS}, {{NULL, "localtraps", OPT_EMULATE|OPT_KSH}, LOCALTRAPS}, {{NULL, "login", OPT_SPECIAL}, LOGINSHELL}, {{NULL, "longlistjobs", 0}, LONGLISTJOBS}, @@ -203,6 +205,7 @@ static struct optname optns[] = { {{NULL, "overstrike", 0}, OVERSTRIKE}, {{NULL, "pathdirs", OPT_EMULATE}, PATHDIRS}, {{NULL, "pathscript", OPT_EMULATE|OPT_BOURNE}, PATHSCRIPT}, +{{NULL, "pipefail", OPT_EMULATE}, PIPEFAIL}, {{NULL, "posixaliases", OPT_EMULATE|OPT_BOURNE}, POSIXALIASES}, {{NULL, "posixbuiltins", OPT_EMULATE|OPT_BOURNE}, POSIXBUILTINS}, {{NULL, "posixcd", OPT_EMULATE|OPT_BOURNE}, POSIXCD}, diff --git a/Src/params.c b/Src/params.c index 8649178ef..d6711e4fa 100644 --- a/Src/params.c +++ b/Src/params.c @@ -4667,10 +4667,12 @@ startparamscope(void) mod_export void endparamscope(void) { + queue_signals(); locallevel--; /* This pops anything from a higher locallevel */ saveandpophiststack(0, HFILE_USE_OPTIONS); scanhashtable(paramtab, 0, 0, 0, scanendscope, 0); + unqueue_signals(); } /**/ diff --git a/Src/parse.c b/Src/parse.c index 753080d70..f0d0855d3 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -2088,9 +2088,17 @@ par_cond_2(void) } } if (tok == BANG) { - condlex(); - ecadd(WCB_COND(COND_NOT, 0)); - return par_cond_2(); + /* + * In "test" compatibility mode, "! -a ..." and "! -o ..." + * are treated as "[string] [and] ..." and "[string] [or] ...". + */ + if (!(condlex == testlex && *testargs && + (!strcmp(*testargs, "-a") || !strcmp(*testargs, "-o")))) + { + condlex(); + ecadd(WCB_COND(COND_NOT, 0)); + return par_cond_2(); + } } if (tok == INPAR) { int r; @@ -3171,6 +3179,9 @@ load_dump_file(char *dump, struct stat *sbuf, int other, int len) d->dev = sbuf->st_dev; d->ino = sbuf->st_ino; d->fd = fd; +#ifdef FD_CLOEXEC + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif d->map = addr + (other ? (len - off) / sizeof(wordcode) : 0); d->addr = addr; d->len = len; @@ -3415,6 +3426,16 @@ incrdumpcount(FuncDump f) f->count++; } +/**/ +static void +freedump(FuncDump f) +{ + munmap((void *) f->addr, f->len); + zclose(f->fd); + zsfree(f->filename); + zfree(f, sizeof(*f)); +} + /* Decrement the reference counter for a dump file. If zero, unmap the file. */ /**/ @@ -3431,23 +3452,23 @@ decrdumpcount(FuncDump f) q->next = p->next; else dumps = p->next; - munmap((void *) f->addr, f->len); - zclose(f->fd); - zsfree(f->filename); - zfree(f, sizeof(*f)); + freedump(f); } } } +#ifndef FD_CLOEXEC /**/ mod_export void closedumps(void) { - FuncDump p; - - for (p = dumps; p; p = p->next) - zclose(p->fd); + while (dumps) { + FuncDump p = dumps->next; + freedump(dumps); + dumps = p; + } } +#endif #else @@ -3461,11 +3482,13 @@ decrdumpcount(FuncDump f) { } +#ifndef FD_CLOEXEC /**/ mod_export void closedumps(void) { } +#endif #endif diff --git a/Src/pattern.c b/Src/pattern.c index 53ada0f8d..a7ef12573 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -225,34 +225,50 @@ typedef unsigned long zrange_t; #endif /* - * Characters which terminate a pattern segment. We actually use - * a pointer patendseg which skips the first character if we are not - * parsing a file pattern. - * Note that the size of this and the next array are hard-wired - * via the definitions. + * Array of characters corresponding to zpc_chars enum, which it must match. */ +static const char zpc_chars[ZPC_COUNT] = { + '/', '\0', Bar, Outpar, Tilde, Inpar, Quest, Star, Inbrack, Inang, + Hat, Pound, Bnullkeep, Quest, Star, '+', '!', '@' +}; -static char endseg[] = { - '/', /* file only */ - '\0', Bar, Outpar, /* all patterns */ - Tilde /* extended glob only */ +/* + * Corresponding strings used in enable/disable -p. + * NULL means no way of turning this on or off. + */ +/**/ +mod_export const char *zpc_strings[ZPC_COUNT] = { + NULL, NULL, "|", NULL, "~", "(", "?", "*", "[", "<", + "^", "#", NULL, "?(", "*(", "+(", "!(", "@(" }; -#define PATENDSEGLEN_NORM 4 -#define PATENDSEGLEN_EXT 5 +/* + * Corresponding array of pattern disables as set by the user + * using "disable -p". + */ +/**/ +mod_export char zpc_disables[ZPC_COUNT]; -/* Characters which terminate a simple string */ +/* + * Stack of saved (compressed) zpc_disables for function scope. + */ -static char endstr[] = { - '/', /* file only */ - '\0', Bar, Outpar, Quest, Star, Inbrack, Inpar, Inang, Bnullkeep, - /* all patterns */ - Tilde, Hat, Pound /* extended glob only */ -}; +static struct zpc_disables_save *zpc_disables_stack; -#define PATENDSTRLEN_NORM 10 -#define PATENDSTRLEN_EXT 13 +/* + * Characters which terminate a simple string (ZPC_COUNT) or + * an entire pattern segment (the first ZPC_SEG_COUNT). + * Each entry is either the corresponding character in zpc_chars + * or Marker which is guaranteed not to match a character in a + * pattern we are compiling. + * + * The complete list indicates characters that are special, so e.g. + * (testchar == special[ZPC_TILDE]) succeeds only if testchar is a Tilde + * *and* Tilde is currently special. + */ +/**/ +char zpc_special[ZPC_COUNT]; /* Default size for pattern buffer */ #define P_DEF_ALLOC 256 @@ -264,10 +280,6 @@ static char *patcode; /* point of code emission */ static long patsize; /* size of code */ static char *patout; /* start of code emission string */ static long patalloc; /* size allocated for same */ -static char *patendseg; /* characters ending segment */ -static int patendseglen; /* length of same */ -static char *patendstr; /* characters ending plain string */ -static int patendstrlen; /* length of sameo */ /* Flags used in both compilation and execution */ static int patflags; /* flags passed down to patcompile */ @@ -417,12 +429,68 @@ static long rn_offs; (P_OP(p) == P_BACK) ? \ ((p)-rn_offs) : ((p)+rn_offs) : NULL) +/* + * Set up zpc_special with characters that end a string segment. + * "Marker" cannot occur in the pattern we are compiling so + * is used to mark "invalid". + */ +static void +patcompcharsset(void) +{ + char *spp, *disp; + int i; + + /* Initialise enabled special characters */ + memcpy(zpc_special, zpc_chars, ZPC_COUNT); + /* Apply user disables from disable -p */ + for (i = 0, spp = zpc_special, disp = zpc_disables; + i < ZPC_COUNT; + i++, spp++, disp++) { + if (*disp) + *spp = Marker; + } + + if (!isset(EXTENDEDGLOB)) { + /* Extended glob characters are not active */ + zpc_special[ZPC_TILDE] = zpc_special[ZPC_HAT] = + zpc_special[ZPC_HASH] = Marker; + } + if (!isset(KSHGLOB)) { + /* + * Ksh glob characters are not active. + * * and ? are shared with normal globbing, but for their + * use here we are looking for a following Inpar. + */ + zpc_special[ZPC_KSH_QUEST] = zpc_special[ZPC_KSH_STAR] = + zpc_special[ZPC_KSH_PLUS] = zpc_special[ZPC_KSH_BANG] = + zpc_special[ZPC_KSH_AT] = Marker; + } + /* + * Note that if we are using KSHGLOB, then we test for a following + * Inpar, not zpc_special[ZPC_INPAR]: the latter makes an Inpar on + * its own active. The zpc_special[ZPC_KSH_*] followed by any old Inpar + * discriminate ksh globbing. + */ + if (isset(SHGLOB)) { + /* + * Grouping and numeric ranges are not valid. + * We do allow alternation, however; it's needed for + * "case". This may not be entirely consistent. + * + * Don't disable Outpar: we may need to match the end of KSHGLOB + * parentheses and it would be difficult to tell them apart. + */ + zpc_special[ZPC_INPAR] = zpc_special[ZPC_INANG] = Marker; + } +} + /* Called before parsing a set of file matchs to initialize flags */ /**/ void patcompstart(void) { + patcompcharsset(); if (isset(CASEGLOB)) patglobflags = 0; else @@ -469,16 +537,9 @@ patcompile(char *exp, int inflags, char **endexp) patnpar = 1; patflags = inflags & ~(PAT_PURES|PAT_HAS_EXCLUDP); - patendseg = endseg; - patendseglen = isset(EXTENDEDGLOB) ? PATENDSEGLEN_EXT : PATENDSEGLEN_NORM; - patendstr = endstr; - patendstrlen = isset(EXTENDEDGLOB) ? PATENDSTRLEN_EXT : PATENDSTRLEN_NORM; - if (!(patflags & PAT_FILE)) { - patendseg++; - patendstr++; - patendseglen--; - patendstrlen--; + patcompcharsset(); + zpc_special[ZPC_SLASH] = Marker; remnulargs(patparse); if (isset(MULTIBYTE)) patglobflags = GF_MULTIBYTE; @@ -687,7 +748,7 @@ patcompswitch(int paren, int *flagp) starter = 0; br = patnode(P_BRANCH); - if (!patcompbranch(&flags)) + if (!patcompbranch(&flags, paren)) return 0; if (patglobflags != (int)savglobflags) gfchanged++; @@ -698,11 +759,11 @@ patcompswitch(int paren, int *flagp) *flagp |= flags & (P_HSTART|P_PURESTR); - while (*patparse == Bar || - (isset(EXTENDEDGLOB) && *patparse == Tilde && + while (*patparse == zpc_chars[ZPC_BAR] || + (*patparse == zpc_special[ZPC_TILDE] && (patparse[1] == '/' || - !memchr(patendseg, patparse[1], patendseglen)))) { - int tilde = *patparse++ == Tilde; + !memchr(zpc_special, patparse[1], ZPC_SEG_COUNT)))) { + int tilde = *patparse++ == zpc_special[ZPC_TILDE]; long gfnode = 0, newbr; *flagp &= ~P_PURESTR; @@ -739,12 +800,9 @@ patcompswitch(int paren, int *flagp) up.p = NULL; patadd((char *)&up, 0, sizeof(up), 0); /* / is not treated as special if we are at top level */ - if (!paren && *patendseg == '/') { + if (!paren && zpc_special[ZPC_SLASH] == '/') { tilde++; - patendseg++; - patendseglen--; - patendstr++; - patendstrlen--; + zpc_special[ZPC_SLASH] = Marker; } } else { excsync = 0; @@ -781,13 +839,10 @@ patcompswitch(int paren, int *flagp) patglobflags = (int)savglobflags; } } - newbr = patcompbranch(&flags); + newbr = patcompbranch(&flags, paren); if (tilde == 2) { /* restore special treatment of / */ - patendseg--; - patendseglen++; - patendstr--; - patendstrlen++; + zpc_special[ZPC_SLASH] = '/'; } if (!newbr) return 0; @@ -847,7 +902,7 @@ patcompswitch(int paren, int *flagp) /**/ static long -patcompbranch(int *flagp) +patcompbranch(int *flagp, int paren) { long chain, latest = 0, starter; int flags = 0; @@ -855,14 +910,13 @@ patcompbranch(int *flagp) *flagp = P_PURESTR; starter = chain = 0; - while (!memchr(patendseg, *patparse, patendseglen) || - (*patparse == Tilde && patparse[1] != '/' && - memchr(patendseg, patparse[1], patendseglen))) { - if (isset(EXTENDEDGLOB) && - ((!isset(SHGLOB) && - (*patparse == Inpar && patparse[1] == Pound)) || - (isset(KSHGLOB) && *patparse == '@' && patparse[1] == Inpar && - patparse[2] == Pound))) { + while (!memchr(zpc_special, *patparse, ZPC_SEG_COUNT) || + (*patparse == zpc_special[ZPC_TILDE] && patparse[1] != '/' && + memchr(zpc_special, patparse[1], ZPC_SEG_COUNT))) { + if ((*patparse == zpc_special[ZPC_INPAR] && + patparse[1] == zpc_special[ZPC_HASH]) || + (*patparse == zpc_special[ZPC_KSH_AT] && patparse[1] == Inpar && + patparse[2] == zpc_special[ZPC_HASH])) { /* Globbing flags. */ char *pp1 = patparse; int oldglobflags = patglobflags, ignore; @@ -910,7 +964,7 @@ patcompbranch(int *flagp) break; else continue; - } else if (isset(EXTENDEDGLOB) && *patparse == Hat) { + } else if (*patparse == zpc_special[ZPC_HAT]) { /* * ^pat: anything but pat. For proper backtracking, * etc., we turn this into (*~pat), except without the @@ -919,7 +973,7 @@ patcompbranch(int *flagp) patparse++; latest = patcompnot(0, &flags); } else - latest = patcomppiece(&flags); + latest = patcomppiece(&flags, paren); if (!latest) return 0; if (!starter) @@ -1167,11 +1221,11 @@ pattern_range_to_string(char *rangestr, char *outstr) /**/ static long -patcomppiece(int *flagp) +patcomppiece(int *flagp, int paren) { long starter = 0, next, op, opnd; int flags, flags2, kshchar, len, ch, patch, nmeta; - int pound, count; + int hash, count; union upat up; char *nptr, *str0, *ptr, *patprev; zrange_t from, to; @@ -1185,25 +1239,39 @@ patcomppiece(int *flagp) * the string doesn't introduce a ksh-like parenthesized expression. */ kshchar = '\0'; - if (isset(KSHGLOB) && *patparse && patparse[1] == Inpar) { - if (strchr("?*+!@", *patparse)) - kshchar = STOUC(*patparse); - else if (*patparse == Star || *patparse == Quest) - kshchar = STOUC(ztokens[*patparse - Pound]); + if (*patparse && patparse[1] == Inpar) { + if (*patparse == zpc_special[ZPC_KSH_PLUS]) + kshchar = STOUC('+'); + else if (*patparse == zpc_special[ZPC_KSH_BANG]) + kshchar = STOUC('!'); + else if (*patparse == zpc_special[ZPC_KSH_AT]) + kshchar = STOUC('@'); + else if (*patparse == zpc_special[ZPC_KSH_STAR]) + kshchar = STOUC('*'); + else if (*patparse == zpc_special[ZPC_KSH_QUEST]) + kshchar = STOUC('?'); } /* - * End of string (or no string at all) if ksh-type parentheses, - * or special character, unless that character is a tilde and - * the character following is an end-of-segment character. Thus - * tildes are not special if there is nothing following to - * be excluded. + * If '(' is disabled as a pattern char, allow ')' as + * an ordinary string character if there are no parentheses to + * close. Don't allow it otherwise, it changes the syntax. */ - if (kshchar || (memchr(patendstr, *patparse, patendstrlen) && - (*patparse != Tilde || - patparse[1] == '/' || - !memchr(patendseg, patparse[1], patendseglen)))) - break; + if (zpc_special[ZPC_INPAR] != Marker || *patparse != Outpar || + paren) { + /* + * End of string (or no string at all) if ksh-type parentheses, + * or special character, unless that character is a tilde and + * the character following is an end-of-segment character. Thus + * tildes are not special if there is nothing following to + * be excluded. + */ + if (kshchar || (memchr(zpc_special, *patparse, ZPC_COUNT) && + (*patparse != zpc_special[ZPC_TILDE] || + patparse[1] == '/' || + !memchr(zpc_special, patparse[1], ZPC_SEG_COUNT)))) + break; + } /* Remember the previous character for backtracking */ patprev = patparse; @@ -1227,10 +1295,14 @@ patcomppiece(int *flagp) * If we have more than one character, a following hash * or (#c...) only applies to the last, so backtrack one character. */ - if (isset(EXTENDEDGLOB) && - (*patparse == Pound || - (*patparse == Inpar && patparse[1] == Pound && - patparse[2] == 'c')) && morelen) + if ((*patparse == zpc_special[ZPC_HASH] || + (*patparse == zpc_special[ZPC_INPAR] && + patparse[1] == zpc_special[ZPC_HASH] && + patparse[2] == 'c') || + (*patparse == zpc_special[ZPC_KSH_AT] && + patparse[1] == Inpar && + patparse[2] == zpc_special[ZPC_HASH] && + patparse[3] == 'c')) && morelen) patparse = patprev; /* * If len is 1, we can't have an active # following, so doesn't @@ -1306,15 +1378,21 @@ patcomppiece(int *flagp) METACHARINC(patparse); switch(patch) { case Quest: + DPUTS(zpc_special[ZPC_QUEST] == Marker, + "Treating '?' as pattern character although disabled"); flags |= P_SIMPLE; starter = patnode(P_ANY); break; case Star: + DPUTS(zpc_special[ZPC_STAR] == Marker, + "Treating '*' as pattern character although disabled"); /* kshchar is used as a sign that we can't have #'s. */ kshchar = -1; starter = patnode(P_STAR); break; case Inbrack: + DPUTS(zpc_special[ZPC_INBRACK] == Marker, + "Treating '[' as pattern character although disabled"); flags |= P_SIMPLE; if (*patparse == Hat || *patparse == '^' || *patparse == '!') { patparse++; @@ -1368,9 +1446,10 @@ patcomppiece(int *flagp) patadd(NULL, 0, 1, 0); break; case Inpar: - /* is this how to treat parentheses in SHGLOB? */ - if (isset(SHGLOB) && !kshchar) - return 0; + DPUTS(!kshchar && zpc_special[ZPC_INPAR] == Marker, + "Treating '(' as pattern character although disabled"); + DPUTS(isset(SHGLOB) && !kshchar, + "Treating bare '(' as pattern character with SHGLOB"); if (kshchar == '!') { /* This is nasty, we should really either handle all * kshglobbing below or here. But most of the @@ -1393,6 +1472,9 @@ patcomppiece(int *flagp) break; case Inang: /* Numeric glob */ + DPUTS(zpc_special[ZPC_INANG] == Marker, + "Treating '<' as pattern character although disabled"); + DPUTS(isset(SHGLOB), "Treating <..> as numeric range with SHGLOB"); len = 0; /* beginning present 1, end present 2 */ if (idigit(*patparse)) { from = (zrange_t) zstrtol((char *)patparse, @@ -1435,6 +1517,8 @@ patcomppiece(int *flagp) */ break; case Pound: + DPUTS(zpc_special[ZPC_HASH] == Marker, + "Treating '#' as pattern character although disabled"); DPUTS(!isset(EXTENDEDGLOB), "BUG: # not treated as string"); /* * A hash here is an error; it should follow something @@ -1447,7 +1531,7 @@ patcomppiece(int *flagp) * Marker for restoring a backslash in output: * does not match a character. */ - next = patcomppiece(flagp); + next = patcomppiece(flagp, paren); /* * Can't match a pure string since we need to do this * as multiple chunks. @@ -1465,16 +1549,21 @@ patcomppiece(int *flagp) } count = 0; - if (!(pound = (*patparse == Pound && isset(EXTENDEDGLOB))) && - !(count = (isset(EXTENDEDGLOB) && *patparse == Inpar && - patparse[1] == Pound && patparse[2] == 'c')) && + if (!(hash = (*patparse == zpc_special[ZPC_HASH])) && + !(count = ((*patparse == zpc_special[ZPC_INPAR] && + patparse[1] == zpc_special[ZPC_HASH] && + patparse[2] == 'c') || + (*patparse == zpc_special[ZPC_KSH_AT] && + patparse[1] == Inpar && + patparse[2] == zpc_special[ZPC_HASH] && + patparse[3] == 'c'))) && (kshchar <= 0 || kshchar == '@' || kshchar == '!')) { *flagp = flags; return starter; } /* too much at once doesn't currently work */ - if (kshchar && pound) + if (kshchar && (hash || count)) return 0; if (kshchar == '*') { @@ -1490,7 +1579,7 @@ patcomppiece(int *flagp) op = P_COUNT; patparse += 3; *flagp = P_HSTART; - } else if (*++patparse == Pound) { + } else if (*++patparse == zpc_special[ZPC_HASH]) { op = P_TWOHASH; patparse++; *flagp = P_HSTART; @@ -1600,7 +1689,7 @@ patcomppiece(int *flagp) pattail(starter, next); patoptail(starter, next); } - if (*patparse == Pound) + if (*patparse == zpc_special[ZPC_HASH]) return 0; return starter; @@ -1629,7 +1718,7 @@ patcompnot(int paren, int *flagsp) pattail(starter, excl = patnode(P_EXCLUDE)); up.p = NULL; patadd((char *)&up, 0, sizeof(up), 0); - if (!(br = (paren ? patcompswitch(1, &dummy) : patcompbranch(&dummy)))) + if (!(br = (paren ? patcompswitch(1, &dummy) : patcompbranch(&dummy, 0)))) return 0; pattail(br, patnode(P_EXCEND)); n = patnode(P_NOTHING); /* just so much easier */ @@ -3753,3 +3842,212 @@ freepatprog(Patprog prog) if (prog && prog != dummy_patprog1 && prog != dummy_patprog2) zfree(prog, prog->size); } + +/* Disable or reenable a pattern character */ + +/**/ +int +pat_enables(const char *cmd, char **patp, int enable) +{ + int ret = 0; + const char **stringp; + char *disp; + + if (!*patp) { + int done = 0; + for (stringp = zpc_strings, disp = zpc_disables; + stringp < zpc_strings + ZPC_COUNT; + stringp++, disp++) { + if (!*stringp) + continue; + if (enable ? *disp : !*disp) + continue; + if (done) + putc(' ', stdout); + printf("'%s'", *stringp); + done = 1; + } + if (done) + putc('\n', stdout); + return 0; + } + + for (; *patp; patp++) { + for (stringp = zpc_strings, disp = zpc_disables; + stringp < zpc_strings + ZPC_COUNT; + stringp++, disp++) { + if (*stringp && !strcmp(*stringp, *patp)) { + *disp = (char)!enable; + break; + } + } + if (stringp == zpc_strings + ZPC_COUNT) { + zerrnam(cmd, "invalid pattern: %s", *patp); + ret = 1; + } + } + + return ret; +} + +/* + * Save the current state of pattern disables, returning the saved value. + */ + +/**/ +unsigned int +savepatterndisables(void) +{ + unsigned int disables, bit; + char *disp; + + disables = 0; + for (bit = 1, disp = zpc_disables; + disp < zpc_disables + ZPC_COUNT; + bit <<= 1, disp++) { + if (*disp) + disables |= bit; + } + return disables; +} + +/* + * Function scope saving pattern enables. + */ + +/**/ +void +startpatternscope(void) +{ + Zpc_disables_save newdis; + + newdis = (Zpc_disables_save)zalloc(sizeof(*newdis)); + newdis->next = zpc_disables_stack; + newdis->disables = savepatterndisables(); + + zpc_disables_stack = newdis; +} + +/* + * Restore completely the state of pattern disables. + */ + +/**/ +void +restorepatterndisables(unsigned int disables) +{ + char *disp; + unsigned int bit; + + for (bit = 1, disp = zpc_disables; + disp < zpc_disables + ZPC_COUNT; + bit <<= 1, disp++) { + if (disables & bit) + *disp = 1; + else + *disp = 0; + } +} + +/* + * Function scope to restore pattern enables if localpatterns is turned on. + */ + +/**/ +void +endpatternscope(void) +{ + Zpc_disables_save olddis; + + olddis = zpc_disables_stack; + zpc_disables_stack = olddis->next; + + if (isset(LOCALPATTERNS)) + restorepatterndisables(olddis->disables); + + zfree(olddis, sizeof(*olddis)); +} + +/* Reinitialise pattern disables */ + +/**/ +void +clearpatterndisables(void) +{ + memset(zpc_disables, 0, ZPC_COUNT); +} + + +/* Check to see if str is eligible for filename generation. */ + +/**/ +mod_export int +haswilds(char *str) +{ + char *start; + + /* `[' and `]' are legal even if bad patterns are usually not. */ + if ((*str == Inbrack || *str == Outbrack) && !str[1]) + return 0; + + /* If % is immediately followed by ?, then that ? is * + * not treated as a wildcard. This is so you don't have * + * to escape job references such as %?foo. */ + if (str[0] == '%' && str[1] == Quest) + str[1] = '?'; + + /* + * Note that at this point zpc_special has not been set up. + */ + start = str; + for (; *str; str++) { + switch (*str) { + case Inpar: + if ((!isset(SHGLOB) && !zpc_disables[ZPC_INPAR]) || + (str > start && isset(KSHGLOB) && + ((str[-1] == Quest && !zpc_disables[ZPC_KSH_QUEST]) || + (str[-1] == Star && !zpc_disables[ZPC_KSH_STAR]) || + (str[-1] == '+' && !zpc_disables[ZPC_KSH_PLUS]) || + (str[-1] == '!' && !zpc_disables[ZPC_KSH_BANG]) || + (str[-1] == '@' && !zpc_disables[ZPC_KSH_AT])))) + return 1; + break; + + case Bar: + if (!zpc_disables[ZPC_BAR]) + return 1; + break; + + case Star: + if (!zpc_disables[ZPC_STAR]) + return 1; + break; + + case Inbrack: + if (!zpc_disables[ZPC_INBRACK]) + return 1; + break; + + case Inang: + if (!zpc_disables[ZPC_INANG]) + return 1; + break; + + case Quest: + if (!zpc_disables[ZPC_QUEST]) + return 1; + break; + + case Pound: + if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH]) + return 1; + break; + + case Hat: + if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HAT]) + return 1; + break; + } + } + return 0; +} diff --git a/Src/prompt.c b/Src/prompt.c index e51ce2451..95a7d4969 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -232,6 +232,33 @@ promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep) return new_vars.buf; } +/* Parse the argument for %F and %K */ +static int +parsecolorchar(int arg, int is_fg) +{ + if (bv->fm[1] == '{') { + char *ep; + bv->fm += 2; /* skip over F{ */ + if ((ep = strchr(bv->fm, '}'))) { + char oc = *ep, *col, *coll; + *ep = '\0'; + /* expand the contents of the argument so you can use + * %v for example */ + coll = col = promptexpand(bv->fm, 0, NULL, NULL, NULL); + *ep = oc; + arg = match_colour((const char **)&coll, is_fg, 0); + free(col); + bv->fm = ep; + } else { + arg = match_colour((const char **)&bv->fm, is_fg, 0); + if (*bv->fm != '}') + bv->fm--; + } + } else + arg = match_colour(NULL, 1, arg); + return arg; +} + /* Perform %- and !-expansion as required on a section of the prompt. The * * section is ended by an instance of endchar. If doprint is 0, the valid * * % sequences are merely skipped over, and nothing is stored. */ @@ -494,13 +521,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY); break; case 'F': - if (bv->fm[1] == '{') { - bv->fm += 2; - arg = match_colour((const char **)&bv->fm, 1, 0); - if (*bv->fm != '}') - bv->fm--; - } else - arg = match_colour(NULL, 1, arg); + arg = parsecolorchar(arg, 1); if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) { txtchangeset(txtchangep, arg & TXT_ATTR_FG_ON_MASK, TXTNOFGCOLOUR); @@ -515,13 +536,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT); break; case 'K': - if (bv->fm[1] == '{') { - bv->fm += 2; - arg = match_colour((const char **)&bv->fm, 0, 0); - if (*bv->fm != '}') - bv->fm--; - } else - arg = match_colour(NULL, 0, arg); + arg = parsecolorchar(arg, 0); if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) { txtchangeset(txtchangep, arg & TXT_ATTR_BG_ON_MASK, TXTNOBGCOLOUR); diff --git a/Src/signals.c b/Src/signals.c index 046ee6a4a..c8f5fbcca 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -185,7 +185,7 @@ noholdintr(void) * only the given signal */ /**/ -sigset_t +mod_export sigset_t signal_mask(int sig) { sigset_t set; @@ -202,7 +202,8 @@ signal_mask(int sig) /**/ #ifndef BSD_SIGNALS -sigset_t +/**/ +mod_export sigset_t signal_block(sigset_t set) { sigset_t oset; @@ -245,7 +246,8 @@ signal_block(sigset_t set) /* Unblock the signals in the given signal * * set. Return the old signal set. */ -sigset_t +/**/ +mod_export sigset_t signal_unblock(sigset_t set) { sigset_t oset; diff --git a/Src/signals.h b/Src/signals.h index 9541a1a02..d68096891 100644 --- a/Src/signals.h +++ b/Src/signals.h @@ -59,6 +59,14 @@ #define child_block() signal_block(sigchld_mask) #define child_unblock() signal_unblock(sigchld_mask) +#ifdef SIGWINCH +# define winch_block() signal_block(signal_mask(SIGWINCH)) +# define winch_unblock() signal_unblock(signal_mask(SIGWINCH)) +#else +# define winch_block() 0 +# define winch_unblock() 0 +#endif + /* ignore a signal */ #define signal_ignore(S) signal(S, SIG_IGN) diff --git a/Src/subst.c b/Src/subst.c index 974a8456d..a4df2567f 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3707,6 +3707,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) char *y; x = val; + if (!x) { + /* Shouldn't have got here with a NULL string. */ + DPUTS(1, "value is NULL in paramsubst"); + return NULL; + } if (prenum || postnum) x = dopadding(x, prenum, postnum, preone, postone, premul, postmul @@ -4021,7 +4026,10 @@ modify(char **str, char **ptr) all = tmp; t = e; } - *str = all; + if (!all) + *str = dupstring(""); + else + *str = all; } else { switch (c) { diff --git a/Src/utils.c b/Src/utils.c index 26e2a5c2c..d1d9406c2 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1287,9 +1287,17 @@ void preprompt(void) { static time_t lastperiodic; + time_t currentmailcheck; LinkNode ln; - int period = getiparam("PERIOD"); - int mailcheck = getiparam("MAILCHECK"); + zlong period = getiparam("PERIOD"); + zlong mailcheck = getiparam("MAILCHECK"); + + /* + * Handle any pending window size changes before we compute prompts, + * then block them again to avoid interrupts during prompt display. + */ + winch_unblock(); + winch_block(); if (isset(PROMPTSP) && isset(PROMPTCR) && !use_exit_printed && shout) { /* The PROMPT_SP heuristic will move the prompt down to a new line @@ -1330,7 +1338,7 @@ preprompt(void) /* If 1) the parameter PERIOD exists, 2) a hook function for * * "periodic" exists, 3) it's been greater than PERIOD since we * * executed any such hook, then execute it now. */ - if (period && (time(NULL) > lastperiodic + period) && + if (period && ((zlong)time(NULL) > (zlong)lastperiodic + period) && !callhookfunc("periodic", NULL, 1, NULL)) lastperiodic = time(NULL); if (errflag) @@ -1348,7 +1356,9 @@ preprompt(void) return; /* Check mail */ - if (mailcheck && (int) difftime(time(NULL), lastmailcheck) > mailcheck) { + currentmailcheck = time(NULL); + if (mailcheck && + (zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) { char *mailfile; if (mailpath && *mailpath && **mailpath) @@ -1364,7 +1374,7 @@ preprompt(void) } unqueue_signals(); } - lastmailcheck = time(NULL); + lastmailcheck = currentmailcheck; } if (prepromptfns) { @@ -1424,7 +1434,7 @@ checkmailpath(char **s) } } else if (shout) { if (st.st_size && st.st_atime <= st.st_mtime && - st.st_mtime > lastmailcheck) { + st.st_mtime >= lastmailcheck) { if (!u) { fprintf(shout, "You have new mail.\n"); fflush(shout); @@ -179,7 +179,11 @@ struct mathfunc { * Take care to update the use of IMETA appropriately when adding * tokens here. */ -/* Marker used in paramsubst for rc_expand_param */ +/* + * Marker used in paramsubst for rc_expand_param. + * Also used in pattern character arrays as guaranteed not to + * mark a character in a string. + */ #define Marker ((char) 0xa0) /* chars that need to be quoted if meant literally */ @@ -370,9 +374,8 @@ enum { #ifdef PATH_DEV_FD /* * Entry used by a process substition. - * The value will be incremented on entering a function and - * decremented on exit; we don't close entries greater than - * FDT_PROC_SUBST except when closing everything. + * This marker is not tested internally as we associated the file + * descriptor with a job for closing. */ #define FDT_PROC_SUBST 6 #endif @@ -418,6 +421,7 @@ typedef struct heap *Heap; typedef struct heapstack *Heapstack; typedef struct histent *Histent; typedef struct hookdef *Hookdef; +typedef struct jobfile *Jobfile; typedef struct job *Job; typedef struct linkedmod *Linkedmod; typedef struct linknode *LinkNode; @@ -874,6 +878,18 @@ struct eccstr { /* Definitions for job table and job control */ /********************************************/ +/* Entry in filelist linked list in job table */ + +struct jobfile { + /* Record to be deleted or closed */ + union { + char *name; /* Name of file to delete */ + int fd; /* File descriptor to close */ + } u; + /* Discriminant */ + int is_fd; +}; + /* entry in the job table */ struct job { @@ -885,6 +901,7 @@ struct job { struct process *procs; /* list of processes */ struct process *auxprocs; /* auxiliary processes e.g multios */ LinkList filelist; /* list of files to delete when done */ + /* elements are struct jobfile */ int stty_in_env; /* if STTY=... is present */ struct ttyinfo *ty; /* the modes specified by STTY */ }; @@ -960,7 +977,6 @@ struct execstack { int trapisfunc; int traplocallevel; int noerrs; - int subsh_close; char *underscore; }; @@ -1375,6 +1391,55 @@ struct patprog { #define PAT_HAS_EXCLUDP 0x0800 /* (internal): top-level path1~path2. */ #define PAT_LCMATCHUC 0x1000 /* equivalent to setting (#l) */ +/** + * Indexes into the array of active pattern characters. + * This must match the array zpc_chars in pattern.c. + */ +enum zpc_chars { + /* + * These characters both terminate a pattern segment and + * a pure string segment. + */ + ZPC_SLASH, /* / active as file separator */ + ZPC_NULL, /* \0 as string terminator */ + ZPC_BAR, /* | for "or" */ + ZPC_OUTPAR, /* ) for grouping */ + ZPC_TILDE, /* ~ for exclusion (extended glob) */ + ZPC_SEG_COUNT, /* No. of the above characters */ + /* + * These characters terminate a pure string segment. + */ + ZPC_INPAR = ZPC_SEG_COUNT, /* ( for grouping */ + ZPC_QUEST, /* ? as wildcard */ + ZPC_STAR, /* * as wildcard */ + ZPC_INBRACK, /* [ for character class */ + ZPC_INANG, /* < for numeric glob */ + ZPC_HAT, /* ^ for exclusion (extended glob) */ + ZPC_HASH, /* # for repetition (extended glob) */ + ZPC_BNULLKEEP, /* Special backslashed null not removed */ + ZPC_KSH_QUEST, /* ? for ?(...) in KSH_GLOB */ + ZPC_KSH_STAR, /* * for *(...) in KSH_GLOB */ + ZPC_KSH_PLUS, /* + for +(...) in KSH_GLOB */ + ZPC_KSH_BANG, /* ! for !(...) in KSH_GLOB */ + ZPC_KSH_AT, /* @ for @(...) in KSH_GLOB */ + ZPC_COUNT /* Number of special chararacters */ +}; + +/* + * Structure to save disables special characters for function scope. + */ +struct zpc_disables_save { + struct zpc_disables_save *next; + /* + * 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. + */ + unsigned int disables; +}; + +typedef struct zpc_disables_save *Zpc_disables_save; + /* * Special match types used in character classes. These * are represented as tokens, with Meta added. The character @@ -1829,6 +1894,7 @@ struct histent { #define HIST_DUP 0x00000008 /* Command duplicates a later line */ #define HIST_FOREIGN 0x00000010 /* Command came from another shell */ #define HIST_TMPSTORE 0x00000020 /* Kill when user enters another cmd */ +#define HIST_NOWRITE 0x00000040 /* Keep internally but don't write */ #define GETHIST_UPWARD (-1) #define GETHIST_DOWNWARD 1 @@ -1988,6 +2054,7 @@ enum { EXTENDEDHISTORY, EVALLINENO, FLOWCONTROL, + FORCEFLOAT, FUNCTIONARGZERO, GLOBOPT, GLOBALEXPORT, @@ -2035,6 +2102,7 @@ enum { LISTROWSFIRST, LISTTYPES, LOCALOPTIONS, + LOCALPATTERNS, LOCALTRAPS, LOGINSHELL, LONGLISTJOBS, @@ -2054,6 +2122,7 @@ enum { OVERSTRIKE, PATHDIRS, PATHSCRIPT, + PIPEFAIL, POSIXALIASES, POSIXBUILTINS, POSIXCD, @@ -2689,7 +2758,8 @@ enum { ZLE_CMD_RESET_PROMPT, ZLE_CMD_REFRESH, ZLE_CMD_SET_KEYMAP, - ZLE_CMD_GET_KEY + ZLE_CMD_GET_KEY, + ZLE_CMD_SET_HIST_LINE }; /***************************************/ diff --git a/Src/zsh.mdd b/Src/zsh.mdd index 6e9077676..4bc884b3d 100644 --- a/Src/zsh.mdd +++ b/Src/zsh.mdd @@ -39,10 +39,14 @@ params.o: patchlevel.h version.h: $(sdir_top)/Config/version.mk zshcurses.h zshterm.h echo '#define ZSH_VERSION "'$(VERSION)'"' > $@ -patchlevel.h: $(sdir_top)/ChangeLog - sed -ne \ - 's/^\* *\$$''Revision: \(.*\) ''\$$/#define ZSH_PATCHLEVEL "\1"/p' \ - $(sdir_top)/ChangeLog >patchlevel.h +patchlevel.h: FORCE + @if [ -f $(sdir)/$@.release ]; then \ + cp -f $(sdir)/$@.release $@; \ + else \ + echo '#define ZSH_PATCHLEVEL "'`cd $(sdir) && git describe --tags --long`'"' > $@.tmp; \ + cmp $@ $@.tmp && rm -f $@.tmp || mv $@.tmp $@; \ + fi +FORCE: zshcurses.h: ../config.h @if test x$(ZSH_CURSES_H) != x; then \ diff --git a/Src/zsh_system.h b/Src/zsh_system.h index f38533023..e68fd62f9 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -773,7 +773,7 @@ extern short ospeed; # define IS_DIRSEP(c) ((c) == '/') #endif -#if defined(__GNUC__) && !defined(__APPLE__) +#if defined(__GNUC__) && (!defined(__APPLE__) || defined(__clang__)) /* Does the OS X port of gcc still gag on __attribute__? */ #define UNUSED(x) x __attribute__((__unused__)) #else |