diff options
Diffstat (limited to 'Src')
56 files changed, 887 insertions, 347 deletions
diff --git a/Src/Builtins/rlimits.awk b/Src/Builtins/rlimits.awk index fe2d0e931..e9c576c66 100644 --- a/Src/Builtins/rlimits.awk +++ b/Src/Builtins/rlimits.awk @@ -59,6 +59,7 @@ BEGIN {limidx = 0} if (limnam == "NPTS") { msg[limnum] = "Npseudoterminals" } if (limnam == "SWAP") { msg[limnum] = "Mswapsize" } if (limnam == "KQUEUES") { msg[limnum] = "Nkqueues" } + if (limnam == "UMTXP") { msg[limnum] = "Numtxp" } } } } diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index 29f97b41d..6b552f3a9 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -413,6 +413,12 @@ printulimit(char *nam, int lim, int hard, int head) printf("-k: kqueues "); break; # endif /* HAVE_RLIMIT_KQUEUES */ +# ifdef HAVE_RLIMIT_UMTXP + case RLIMIT_UMTXP: + if (head) + printf("-o: umtx shared locks "); + break; +# endif /* HAVE_RLIMIT_UMTXP */ default: if (head) printf("-N %2d: ", lim); @@ -895,6 +901,11 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) res = RLIMIT_PTHREAD; break; # endif +# ifdef HAVE_RLIMIT_UMTXP + case 'o': + res = RLIMIT_UMTXP; + break; +# endif default: /* unrecognised limit */ zwarnnam(name, "bad option: -%c", *options); diff --git a/Src/Makefile.in b/Src/Makefile.in index 0577554f8..0fdbb73d2 100644 --- a/Src/Makefile.in +++ b/Src/Makefile.in @@ -94,7 +94,6 @@ zsh.res.o: $(sdir)/zsh.rc $(sdir)/zsh.ico stamp-modobjs: modobjs @if cmp -s stamp-modobjs.tmp stamp-modobjs; then \ rm -f stamp-modobjs.tmp; \ - echo "\`stamp-modobjs' is up to date."; \ else \ mv -f stamp-modobjs.tmp stamp-modobjs; \ echo "Updated \`stamp-modobjs'."; \ diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index a60dfcbf8..19f285e34 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -1519,7 +1519,7 @@ zccmd_resize(const char *nam, char **args) // is not available. return 0; } else { - // Without this call some window moves are innacurate. Tested on + // Without this call some window moves are inaccurate. Tested on // OS X ncurses 5.4, Homebrew ncursesw 6.0-2, Arch Linux ncursesw // 6.0, Ubuntu 14.04 ncurses 5.9, FreeBSD ncursesw.so.8 // diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index 12dd839cf..b8e7c76c6 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -766,7 +766,7 @@ static int remove_tied_name( const char *name ) { /* * Unmetafy that: - * - duplicates bufer to work on it, + * - duplicates buffer to work on it, * - does zalloc of exact size for the new string, * - restores work buffer to original content, to restore strlen */ diff --git a/Src/Modules/files.c b/Src/Modules/files.c index 6f816bac0..6d20e38a8 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -613,12 +613,52 @@ bin_rm(char *nam, char **args, Options ops, UNUSED(int func)) rmm.opt_interact = OPT_ISSET(ops,'i') && !OPT_ISSET(ops,'f'); rmm.opt_unlinkdir = OPT_ISSET(ops,'d'); err = recursivecmd(nam, OPT_ISSET(ops,'f'), - OPT_ISSET(ops,'r') && !OPT_ISSET(ops,'d'), + !OPT_ISSET(ops,'d') && (OPT_ISSET(ops,'R') || + OPT_ISSET(ops,'r')), OPT_ISSET(ops,'s'), args, recurse_donothing, rm_dirpost, rm_leaf, &rmm); return OPT_ISSET(ops,'f') ? 0 : err; } +/* chmod builtin */ + +struct chmodmagic { + char *nam; + mode_t mode; +}; + +/**/ +static int +chmod_dochmod(char *arg, char *rp, UNUSED(struct stat const *sp), void *magic) +{ + struct chmodmagic *chm = magic; + + if(chmod(rp, chm->mode)) { + zwarnnam(chm->nam, "%s: %e", arg, errno); + return 1; + } + return 0; +} + +/**/ +static int +bin_chmod(char *nam, char **args, Options ops, int func) +{ + struct chmodmagic chm; + char *str = args[0], *ptr; + + chm.nam = nam; + + chm.mode = zstrtol(str, &ptr, 8); + if(!*str || *ptr) { + zwarnnam(nam, "invalid mode `%s'", str); + return 1; + } + + return recursivecmd(nam, 0, OPT_ISSET(ops,'R'), OPT_ISSET(ops,'s'), + args + 1, chmod_dochmod, recurse_donothing, chmod_dochmod, &chm); +} + /* chown builtin */ struct chownmagic { @@ -754,20 +794,22 @@ static struct builtin bintab[] = { /* The names which overlap commands without necessarily being * fully compatible. */ BUILTIN("chgrp", 0, bin_chown, 2, -1, BIN_CHGRP, "hRs", NULL), + BUILTIN("chmod", 0, bin_chmod, 2, -1, 0, "Rs", NULL), BUILTIN("chown", 0, bin_chown, 2, -1, BIN_CHOWN, "hRs", NULL), BUILTIN("ln", 0, bin_ln, 1, -1, BIN_LN, LN_OPTS, NULL), BUILTIN("mkdir", 0, bin_mkdir, 1, -1, 0, "pm:", NULL), BUILTIN("mv", 0, bin_ln, 2, -1, BIN_MV, "fi", NULL), - BUILTIN("rm", 0, bin_rm, 1, -1, 0, "dfirs", NULL), + BUILTIN("rm", 0, bin_rm, 1, -1, 0, "dfiRrs", NULL), BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0, NULL, NULL), BUILTIN("sync", 0, bin_sync, 0, 0, 0, NULL, NULL), /* The "safe" zsh-only names */ BUILTIN("zf_chgrp", 0, bin_chown, 2, -1, BIN_CHGRP, "hRs", NULL), + BUILTIN("zf_chmod", 0, bin_chmod, 2, -1, 0, "Rs", NULL), BUILTIN("zf_chown", 0, bin_chown, 2, -1, BIN_CHOWN, "hRs", NULL), BUILTIN("zf_ln", 0, bin_ln, 1, -1, BIN_LN, LN_OPTS, NULL), BUILTIN("zf_mkdir", 0, bin_mkdir, 1, -1, 0, "pm:", NULL), BUILTIN("zf_mv", 0, bin_ln, 2, -1, BIN_MV, "fi", NULL), - BUILTIN("zf_rm", 0, bin_rm, 1, -1, 0, "dfirs", NULL), + BUILTIN("zf_rm", 0, bin_rm, 1, -1, 0, "dfiRrs", NULL), BUILTIN("zf_rmdir", 0, bin_rmdir, 1, -1, 0, NULL, NULL), BUILTIN("zf_sync", 0, bin_sync, 0, 0, 0, NULL, NULL), diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 76824cf58..ef9148d7b 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -1552,7 +1552,7 @@ setpmnameddirs(Param pm, HashTable ht) } } - /* The INTERACTIVE stuff ensures that the dirs are not immediatly removed + /* The INTERACTIVE stuff ensures that the dirs are not immediately removed * when the sub-pms are deleted. */ i = opts[INTERACTIVE]; diff --git a/Src/Modules/system.c b/Src/Modules/system.c index 7a4f4ee13..fb3d80773 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -174,7 +174,7 @@ bin_sysread(char *nam, char **args, Options ops, UNUSED(int func)) } while ((ret = select(infd+1, (SELECT_ARG_2_T) &fds, - NULL, NULL,&select_tv)) < 1) { + NULL, NULL,&select_tv)) < 0) { if (errno != EINTR || errflag || retflag || breaks || contflag) break; } @@ -316,7 +316,7 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) int o, fd, moved_fd, explicit = -1; mode_t perms = 0666; #if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) - int fdflags; + int fdflags = 0; #endif if (!OPT_ISSET(ops, 'u')) { @@ -396,8 +396,8 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) #endif /* O_CLOEXEC */ fcntl(moved_fd, F_SETFD, FD_CLOEXEC); #endif /* FD_CLOEXEC */ + fdtable[moved_fd] = FDT_EXTERNAL; if (explicit == -1) { - fdtable[moved_fd] = FDT_EXTERNAL; setiparam(fdvar, moved_fd); /* if setting the variable failed, close moved_fd to avoid leak */ if (errflag) diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index 4aaa1f072..e8e239e76 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -237,7 +237,7 @@ enum { /* * We keep an fd open for communication between the main shell * and forked off bits and pieces. This allows us to know - * if something happend in a subshell: mode changed, type changed, + * if something happened in a subshell: mode changed, type changed, * connection was closed. If something too substantial happened * in a subshell --- connection opened, ZFTP_USER and ZFTP_PWD changed * --- we don't try to track it because it's too complicated. @@ -1257,14 +1257,8 @@ zfstats(char *fnam, int remote, off_t *retsize, char **retmdtm, int fd) if (retmdtm) { /* use gmtime() rather than localtime() for consistency */ tm = gmtime(&statbuf.st_mtime); - /* - * FTP format for data is YYYYMMDDHHMMSS - * Using tm directly is easier than worrying about - * incompatible strftime()'s. - */ - sprintf(tmbuf, "%04d%02d%02d%02d%02d%02d", - tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + /* FTP format for date is YYYYMMDDHHMMSS */ + ztrftime(tmbuf, sizeof(tmbuf), "%Y%m%d%H%M%S", tm, 0L); mt = ztrdup(tmbuf); } } @@ -2518,7 +2512,8 @@ zftp_local(UNUSED(char *name), char **args, int flags) #ifdef OFF_T_IS_64_BIT printf("%s %s\n", output64(sz), mt); #else - DPUTS(sizeof(sz) > 4, "Shell compiled with wrong off_t size"); + DPUTS(sizeof(sz) > sizeof(long), + "Shell compiled with wrong off_t size"); printf("%ld %s\n", (long)sz, mt); #endif zsfree(mt); diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 2f83f7ce6..45fd15ee0 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -426,7 +426,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) execode(prog, 1, 0, "zpty"); stopmsg = 2; mypid = 0; /* trick to ensure we _exit() */ - zexit(lastval, 0); + zexit(lastval, ZEXIT_NORMAL); } master = movefd(master); if (master == -1) { diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 19a8306b5..24659cb16 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -88,7 +88,8 @@ typedef struct style *Style; struct style { struct hashnode node; - Stypat pats; /* patterns */ + Stypat pats; /* patterns, sorted by weight descending, then + by order of definition, newest first. */ }; struct stypat { @@ -337,7 +338,19 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) p->eval = eprog; p->next = NULL; - /* Calculate the weight. */ + /* Calculate the weight. + * + * The weight of a pattern is scored as follows: + * + * - The pattern is split to colon-separated components. + * - A component equal to '*' (with nothing else) scores 0 points. + * - A component that's a pattern, otherwise, scores 1 point. + * - A component that's a literal string scores 2 points. + * - The score of a pattern is the sum of the score of its components. + * + * This corresponds to the notion of 'more specific' in the zshmodules(1) + * documentation of zstyle. + */ for (weight = 0, tmp = 2, first = 1, str = pat; *str; str++) { if (first && *str == '*' && (!str[1] || str[1] == ':')) { @@ -362,9 +375,9 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) } p->weight = (weight += tmp); + /* Insert 'q' to 's->pats', using 'qq' as a temporary. */ for (qq = NULL, q = s->pats; q && q->weight >= weight; qq = q, q = q->next); - p->next = q; if (qq) qq->next = p; @@ -1644,7 +1657,7 @@ static int bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { char *o, *p, *n, **pp, **aval, **ap, *assoc = NULL, **cp, **np; - int del = 0, flags = 0, extract = 0, keep = 0; + int del = 0, flags = 0, extract = 0, fail = 0, keep = 0; Zoptdesc sopts[256], d; Zoptarr a, defarr = NULL; Zoptval v; @@ -1681,6 +1694,14 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } extract = 1; break; + case 'F': + if (o[2]) { + args--; + o = NULL; + break; + } + fail = 1; + break; case 'K': if (o[2]) { args--; @@ -1843,6 +1864,10 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (!(d = lookup_opt(o + 1))) { while (*++o) { if (!(d = sopts[STOUC(*o)])) { + if (fail) { + zwarnnam(nam, "bad option: %c", *o); + return 1; + } o = NULL; break; } diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 3e9834560..2e3249b52 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -35,7 +35,7 @@ typedef struct cexpl *Cexpl; typedef struct cmgroup *Cmgroup; typedef struct cmatch *Cmatch; -/* This is for explantion strings. */ +/* This is for explanation strings. */ struct cexpl { int always; /* display even without matches */ @@ -90,6 +90,9 @@ struct cmgroup { #define CGF_PACKED 32 /* LIST_PACKED for this group */ #define CGF_ROWS 64 /* LIST_ROWS_FIRST for this group */ #define CGF_FILES 128 /* contains file names */ +#define CGF_MATSORT 256 /* sort by match rather than by display string */ +#define CGF_NUMSORT 512 /* sort numerically */ +#define CGF_REVSORT 1024 /* sort in reverse */ /* This is the struct used to hold matches. */ @@ -123,8 +126,8 @@ struct cmatch { #define CMF_FILE (1<< 0) /* this is a file */ #define CMF_REMOVE (1<< 1) /* remove the suffix */ -#define CMF_ISPAR (1<< 2) /* is paramter expansion */ -#define CMF_PARBR (1<< 3) /* paramter expansion with a brace */ +#define CMF_ISPAR (1<< 2) /* is parameter expansion */ +#define CMF_PARBR (1<< 3) /* parameter expansion with a brace */ #define CMF_PARNEST (1<< 4) /* nested parameter expansion */ #define CMF_NOLIST (1<< 5) /* should not be listed */ #define CMF_DISPLINE (1<< 6) /* display strings one per line */ @@ -300,6 +303,9 @@ struct menuinfo { #define CAF_ARRAYS 32 /* compadd -a or -k: array/assoc parameter names */ #define CAF_KEYS 64 /* compadd -k: assoc parameter names */ #define CAF_ALL 128 /* compadd -C: _all_matches */ +#define CAF_MATSORT 256 /* compadd -o match: sort by match rather than by display string */ +#define CAF_NUMSORT 512 /* compadd -o numeric: sort numerically */ +#define CAF_REVSORT 1024 /* compadd -o numeric: sort in reverse */ /* Data for compadd and addmatches() */ diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 0a454ad5f..7e3badc57 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -1492,7 +1492,7 @@ set_comp_sep(void) * are specially handled (but currently only if RCQUOTES is not * set, which isn't necessarily correct if the quotes were typed by * the user). - * osq: c.f. odq, taking account of Snull's and embeded "'"'s. + * osq: c.f. odq, taking account of Snull's and embedded "'"'s. * qttype: type of quotes using standard QT_* definitions. * lsq: when quoting is single quotes (QT_SINGLE), counts the offset * adjustment needed in the word being examined in the lexer loop. @@ -2080,6 +2080,9 @@ addmatches(Cadata dat, char **argv) /* Select the group in which to store the matches. */ gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) | + ((dat->aflags & CAF_MATSORT) ? CGF_MATSORT : 0) | + ((dat->aflags & CAF_NUMSORT) ? CGF_NUMSORT : 0) | + ((dat->aflags & CAF_REVSORT) ? CGF_REVSORT : 0) | ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) | ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0)); if (dat->group) { @@ -3034,8 +3037,9 @@ begcmgroup(char *n, int flags) HEAP_ERROR(p->heap_id); } #endif - if (p->name && - flags == (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON)) && + if (p->name && flags == + (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON| + CGF_MATSORT|CGF_NUMSORT|CGF_REVSORT)) && !strcmp(n, p->name)) { mgroup = p; @@ -3118,32 +3122,35 @@ addexpl(int always) /* The comparison function for matches (used for sorting). */ +static int matchorder; + /**/ static int matchcmp(Cmatch *a, Cmatch *b) { - if ((*a)->disp && !((*a)->flags & CMF_MORDER)) { - if ((*b)->disp) { - if ((*a)->flags & CMF_DISPLINE) { - if ((*b)->flags & CMF_DISPLINE) - return strcmp((*a)->disp, (*b)->disp); - else - return -1; - } else { - if ((*b)->flags & CMF_DISPLINE) - return 1; - else - return strcmp((*a)->disp, (*b)->disp); - } - } - return -1; + const char *as, *bs; + int cmp = !!(*b)->disp - !!(*a)->disp; + int sortdir = (matchorder & CGF_REVSORT) ? -1 : 1; + + /* if match sorting selected or we have no display strings */ + if ((matchorder & CGF_MATSORT) || (!cmp && !(*a)->disp)) { + as = (*a)->str; + bs = (*b)->str; + } else { + if (cmp) /* matches with display strings come first */ + return cmp; + + cmp = ((*b)->flags & CMF_DISPLINE) - ((*a)->flags & CMF_DISPLINE); + if (cmp) /* sort one-per-line display strings first */ + return cmp; + + as = (*a)->disp; + bs = (*b)->disp; } - if ((*b)->disp && !((*b)->flags & CMF_MORDER)) - return 1; - return zstrcmp((*a)->str, (*b)->str, (SORTIT_IGNORING_BACKSLASHES| - (isset(NUMERICGLOBSORT) ? - SORTIT_NUMERICALLY : 0))); + return sortdir * zstrcmp(as, bs, SORTIT_IGNORING_BACKSLASHES| + ((isset(NUMERICGLOBSORT) || + matchorder & CGF_NUMSORT) ? SORTIT_NUMERICALLY : 0)); } /* This tests whether two matches are equal (would produce the same @@ -3205,6 +3212,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) } else { if (!(flags & CGF_NOSORT)) { /* Now sort the array (it contains matches). */ + matchorder = flags; qsort((void *) rp, n, sizeof(Cmatch), (int (*) _((const void *, const void *)))matchcmp); diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index fe87409cb..08355d1b9 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1726,7 +1726,7 @@ static Patprog patcomp, filecomp; * lppre/lpsuf -- the path prefix/suffix, unexpanded * * fpre/fsuf -- prefix/suffix of the pathname component the cursor is in * * prpre -- ppre in expanded form usable for opendir * - * qipre, qisuf-- ingnored quoted string * + * qipre, qisuf-- ignored quoted string * * * * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf, * * fpre, fsuf, lppre, and lpsuf. noreal is non-zero if we have rpre/rsuf. */ @@ -2511,7 +2511,7 @@ makecomplistcmd(char *os, int incmd, int flags) else if (!(cmdstr && (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) && (cc = ccp->cc)) || - ((s = dupstring(cmdstr)) && remlpaths(&s) && + ((s = dupstring(cmdstr)) && remlpaths(&s, 1) && (ccp = (Compctlp) compctltab->getnode(compctltab, s)) && (cc = ccp->cc))))) { if (flags & CFN_DEFAULT) @@ -3178,7 +3178,27 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) /* Compute line prefix/suffix. */ lpl = offs; lpre = zhalloc(lpl + 1); - memcpy(lpre, s, lpl); + if (comppatmatch) + { + int ccount; + char *psrc, *pdst; + for (ccount = 0, psrc = s, pdst = lpre; + ccount < lpl; + ++ccount, ++psrc, ++pdst) + { + if (*psrc == Meta) + { + ccount++; + *pdst++ = *psrc++; + *pdst = *psrc; + } else if (*psrc == Dash) + *pdst = '-'; + else + *pdst = *psrc; + } + } + else + memcpy(lpre, s, lpl); lpre[lpl] = '\0'; qlpre = quotename(lpre); lsuf = dupstring(s + offs); @@ -3331,13 +3351,11 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) zlemetaline[end] = save; if (brend) { Brinfo bp; - char *p; - int bl; for (bp = brend; bp; bp = bp->next) { - p = lpsuf + (we - zlemetacs) - bp->qpos - - (bl = strlen(bp->str)); - strcpy(p, p + bl); + char *p2 = lpsuf + (we - zlemetacs) - bp->qpos; + char *p1 = p2 - strlen(bp->str); + memmove(p1, p2, strlen(p2) + 1); } } if (!(lpsuf = strchr(lpsuf, '/')) && sf2) diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 1dc2b01c2..7beb6d847 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -84,7 +84,7 @@ char *compiprefix, Param *comprpms; /* - * An array of Param structures for elemens of $compstate; see + * An array of Param structures for elements of $compstate; see * 'compkparams' below. * * See CP_KEYPARAMS. @@ -558,12 +558,53 @@ parse_class(Cpattern p, char *iptr) return iptr; } +static struct { char *name; int abbrev; int oflag; } orderopts[] = { + { "nosort", 2, CAF_NOSORT }, + { "match", 3, CAF_MATSORT }, + { "numeric", 3, CAF_NUMSORT }, + { "reverse", 3, CAF_REVSORT } +}; + +/* Parse the option to compadd -o, if flags is non-NULL set it + * returns -1 if the argument isn't a valid ordering, 0 otherwise */ + +/**/ +static int +parse_ordering(const char *arg, int *flags) +{ + int o, fl = 0; + const char *next, *opt = arg; + do { + int found = 0; + next = strchr(opt, ','); + if (!next) + next = opt + strlen(opt); + + for (o = sizeof(orderopts)/sizeof(*orderopts) - 1; o >= 0 && + !found; --o) + { + if ((found = next - opt >= orderopts[o].abbrev && + !strncmp(orderopts[o].name, opt, next - opt))) + fl |= orderopts[o].oflag; + } + if (!found) { + if (flags) /* default to "match" */ + *flags = CAF_MATSORT; + return -1; + } + } while (*next && ((opt = next + 1))); + if (flags) + *flags |= fl; + return 0; +} + /**/ static int bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) { struct cadata dat; char *mstr = NULL; /* argument of -M options, accumulated */ + char *oarg = NULL; /* argument of -o option */ int added; /* return value */ Cmatcher match = NULL; @@ -572,7 +613,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) return 1; } dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.mesg = - dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = + dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL; dat.match = NULL; dat.flags = 0; @@ -587,6 +628,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) } for (p = *argv + 1; *p; p++) { char *m = NULL; /* argument of -M option (this one only) */ + int order = 0; /* if -o found (argument to which is optional) */ char **sp = NULL; /* the argument to an option should be copied to *sp. */ const char *e; /* error message */ @@ -710,7 +752,11 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) dat.flags |= CMF_DISPLINE; break; case 'o': - dat.flags |= CMF_MORDER; + /* we honour just the first -o option but need to skip + * over a valid argument to subsequent -o options */ + order = oarg ? -1 : 1; + sp = &oarg; + /* no error string because argument is optional */ break; case 'E': if (p[1]) { @@ -741,15 +787,18 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) if (sp) { if (p[1]) { /* Pasted argument: -Xfoo. */ - if (!*sp) + if (!*sp) /* take first option only */ *sp = p + 1; - p += strlen(p+1); + if (!order || !parse_ordering(oarg, order == 1 ? &dat.aflags : NULL)) + p += strlen(p+1); } else if (argv[1]) { /* Argument in a separate word: -X foo. */ argv++; if (!*sp) *sp = *argv; - } else { + if (order && parse_ordering(oarg, order == 1 ? &dat.aflags : NULL)) + --argv; + } else if (!order) { /* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */ zwarnnam(name, e, *p); zsfree(mstr); @@ -962,7 +1011,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) } } else #endif - if ((int)strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) + if ((int)strlen(test == CVT_PRENUM ? compprefix : compsuffix) < na) return 0; if (test == CVT_PRENUM) ignore_prefix(na); diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index 1cdbb8a48..cc4c3eca9 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -1399,7 +1399,7 @@ pattern_match_restrict(Cpattern p, Cpattern wp, convchar_t *wsc, int wsclen, if (prestrict->tp == CPAT_CHAR) { /* * Easy case: restricted to an exact character on - * the line. Procede as normal. + * the line. Proceed as normal. */ c = prestrict->u.chr; } else { diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 05799399d..30fc60b78 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -827,7 +827,7 @@ do_ambiguous(void) * if the completion is completely ambiguous') is set, and some * * prefix was inserted, return now, bypassing the list-displaying * * code. On the way, invalidate the list and note that we don't * - * want to enter an AUTO_MENU imediately. */ + * want to enter an AUTO_MENU immediately. */ if ((uselist == 3 || (!uselist && isset(BASHAUTOLIST) && isset(LISTAMBIGUOUS))) && la && iforcemenu != -1) { diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index cb1c01042..90db8b4b8 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -967,7 +967,7 @@ struct caarg { #define CAA_RARGS 4 #define CAA_RREST 5 -/* The cache of parsed descriptons. */ +/* The cache of parsed descriptions. */ #define MAX_CACACHE 8 static Cadef cadef_cache[MAX_CACACHE]; diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index 58310cd74..c95c7a491 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -99,7 +99,7 @@ "recursive-edit", recursiveedit, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "redo", redo, ZLE_KEEPSUFFIX -"reset-prompt", resetprompt, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"reset-prompt", resetprompt, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND | ZLE_NOLAST "reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "select-a-word", selectword, ZLE_KEEPSUFFIX diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index f06c56483..609493f8c 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -217,6 +217,7 @@ struct widget { #define ZLE_ISCOMP (1<<11) /* usable for new style completion */ #define WIDGET_INUSE (1<<12) /* widget is in use */ #define WIDGET_FREE (1<<13) /* request to free when no longer in use */ +#define ZLE_NOLAST (1<<14) /* widget should not alter lbindk */ /* thingies */ diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index a5cf1011b..d13aed594 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -55,7 +55,7 @@ struct keymapname { HashNode next; /* next in the hash chain */ char *nam; /* name of the keymap */ int flags; /* various flags (see below) */ - Keymap keymap; /* the keymap itsef */ + Keymap keymap; /* the keymap itself */ }; /* Can't be deleted (.safe) */ diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 3487b5d9f..be68f4722 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -632,7 +632,11 @@ raw_getbyte(long do_keytmout, char *cptr, int full) * with all fds, then try unsetting the special ones. */ if (selret < 0 && !errtry) { - errtry = 1; + /* Continue after irrelevant interrupt */ + if (errno != EINTR) { + /* Don't trust special FDs */ + errtry = 1; + } continue; } if (selret == 0) { @@ -704,7 +708,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full) */ if ( # ifdef HAVE_POLL - (fds[0].revents & POLLIN) + (fds[0].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL)) # else FD_ISSET(SHTTY, &foofd) # endif @@ -889,7 +893,7 @@ getbyte(long do_keytmout, int *timeout, int full) break; if (r == 0) { /* The test for IGNOREEOF was added to make zsh ignore ^Ds - that were typed while commands are running. Unfortuantely + that were typed while commands are running. Unfortunately this caused trouble under at least one system (SunOS 4.1). Here shells that lost their xterm (e.g. if it was killed with -9) didn't fail to read from the terminal but instead @@ -901,7 +905,7 @@ getbyte(long do_keytmout, int *timeout, int full) if ((zlereadflags & ZLRF_IGNOREEOF) && icnt++ < 20) continue; stopmsg = 1; - zexit(1, 0); + zexit(1, ZEXIT_NORMAL); } icnt = 0; if (errno == EINTR) { @@ -924,7 +928,7 @@ getbyte(long do_keytmout, int *timeout, int full) } else if (errno != 0) { zerr("error on TTY read: %e", errno); stopmsg = 1; - zexit(1, 0); + zexit(1, ZEXIT_NORMAL); } } if (cc == '\r') /* undo the exchange of \n and \r determined by */ @@ -1073,7 +1077,7 @@ redrawhook(void) * temporarily reset state for special variable handling etc. */ incompfunc = 0; - execzlefunc(initthingy, args, 1); + execzlefunc(initthingy, args, 1, 0); incompfunc = old_incompfunc; /* Restore errflag and retflag as zlecallhook() does */ @@ -1136,7 +1140,7 @@ zlecore(void) eofsent = 1; break; } - if (execzlefunc(bindk, zlenoargs, 0)) { + if (execzlefunc(bindk, zlenoargs, 0, 0)) { handlefeep(zlenoargs); if (eofsent) break; @@ -1256,7 +1260,6 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) resetneeded = 0; fetchttyinfo = 0; trashedzle = 0; - clearflag = 0; raw_lp = lp; lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL, &pmpt_attr); raw_rp = rp; @@ -1387,7 +1390,7 @@ execimmortal(Thingy func, char **args) { Thingy immortal = rthingy_nocreate(dyncat(".", func->nam)); if (immortal) - return execzlefunc(immortal, args, 0); + return execzlefunc(immortal, args, 0, 0); return 1; } @@ -1399,13 +1402,14 @@ execimmortal(Thingy func, char **args) /**/ int -execzlefunc(Thingy func, char **args, int set_bindk) +execzlefunc(Thingy func, char **args, int set_bindk, int set_lbindk) { int r = 0, ret = 0, remetafy = 0; int nestedvichg = vichgflag; int isrepeat = (viinrepeat == 3); Widget w; Thingy save_bindk = bindk; + Thingy save_lbindk = lbindk; if (set_bindk) bindk = func; @@ -1413,6 +1417,8 @@ execzlefunc(Thingy func, char **args, int set_bindk) unmetafy_line(); remetafy = 1; } + if (set_lbindk) + refthingy(save_lbindk); if (isrepeat) viinrepeat = 2; @@ -1536,7 +1542,10 @@ execzlefunc(Thingy func, char **args, int set_bindk) redup(osi, 0); } } - if (r) { + if (set_lbindk) { + unrefthingy(lbindk); + lbindk = save_lbindk; + } else if (r) { unrefthingy(lbindk); refthingy(func); lbindk = func; @@ -1869,13 +1878,17 @@ describekeybriefly(UNUSED(char **args)) { char *seq, *str, *msg, *is; Thingy func; + Keymap km; if (statusline) return 1; clearlist = 1; statusline = "Describe key briefly: _"; zrefresh(); + if (invicmdmode() && region_active && (km = openkeymap("visual"))) + selectlocalmap(km); seq = getkeymapcmd(curkeymap, &func, &str); + selectlocalmap(NULL); statusline = NULL; if(!*seq) return 1; diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 1f293845f..7b8593dec 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -388,7 +388,7 @@ zle_free_highlight(void) /* * Interface to the region_highlight ZLE parameter. - * Converts betwen a format like "P32 42 underline,bold" to + * Converts between a format like "P32 42 underline,bold" to * the format in the region_highlights variable. Note that * the region_highlights variable stores the internal (point/mark) * region in element zero. @@ -834,7 +834,7 @@ nextline(Rparams rpms, int wrapped) if (rpms->nvln != -1 && rpms->nvln != winh - 1 && (numscrolls != onumscrolls - 1 || rpms->nvln <= winh / 2)) - return 1; + return 1; numscrolls++; rpms->canscroll = winh / 2; } @@ -1151,20 +1151,20 @@ zrefresh(void) resetneeded = 0; /* unset */ oput_rpmpt = 0; /* no right-prompt currently on screen */ - if (!clearflag) { - if (tccan(TCCLEAREOD)) - tcoutclear(TCCLEAREOD); - else - cleareol = 1; /* request: clear to end of line */ + if (!clearflag) { + if (tccan(TCCLEAREOD)) + tcoutclear(TCCLEAREOD); + else + cleareol = 1; /* request: clear to end of line */ if (listshown > 0) listshown = 0; } - if (t0 > -1) - olnct = (t0 < winh) ? t0 : winh; - if (termflags & TERM_SHORT) - vcs = 0; + if (t0 > -1) + olnct = (t0 < winh) ? t0 : winh; + if (termflags & TERM_SHORT) + vcs = 0; else if (!clearflag && lpromptbuf[0]) { - zputs(lpromptbuf, shout); + zputs(lpromptbuf, shout); if (lpromptwof == winw) zputs("\n", shout); /* works with both hasam and !hasam */ } else { @@ -1678,7 +1678,12 @@ zrefresh(void) moveto(0, winw - rprompt_off - rpromptw); zputs(rpromptbuf, shout); - vcs = winw - rprompt_off; + if (rprompt_off) { + vcs = winw - rprompt_off; + } else { + zputc(&zr_cr); + vcs = 0; + } /* reset character attributes to that set by the main prompt */ txtchange = pmpt_attr; /* @@ -1885,7 +1890,7 @@ refreshline(int ln) if (hasam && vcs == winw) { if (nbuf[vln] && nbuf[vln][vcs + 1].chr == ZWC('\n')) { vln++, vcs = 1; - if (nbuf[vln] && nbuf[vln]->chr) { + if (nbuf[vln] && nbuf[vln]->chr) { zputc(nbuf[vln]); } else zputc(&zr_sp); /* I don't think this should happen */ @@ -1954,11 +1959,11 @@ refreshline(int ln) if (!nl->chr) { if (ccs == winw && hasam && char_ins > 0 && ins_last && vcs != winw) { - nl--; /* we can assume we can go back here */ + nl--; /* we can assume we can go back here */ moveto(ln, winw - 1); zputc(nl); vcs++; - return; /* write last character in line */ + return; /* write last character in line */ } if ((char_ins <= 0) || (ccs >= winw)) /* written everything */ return; @@ -2159,24 +2164,19 @@ moveto(int ln, int cl) const REFRESH_ELEMENT *rep; if (vcs == winw) { - if (rprompt_indent == 0 && tccan(TCLEFT)) { - tc_leftcurs(1); - vcs--; + vln++, vcs = 0; + if (!hasam) { + zputc(&zr_cr); + zputc(&zr_nl); } else { - vln++, vcs = 0; - if (!hasam) { - zputc(&zr_cr); - zputc(&zr_nl); - } else { - if ((vln < nlnct) && nbuf[vln] && nbuf[vln]->chr) - rep = nbuf[vln]; - else - rep = &zr_sp; - zputc(rep); - zputc(&zr_cr); - if ((vln < olnct) && obuf[vln] && obuf[vln]->chr) - *obuf[vln] = *rep; - } + if ((vln < nlnct) && nbuf[vln] && nbuf[vln]->chr) + rep = nbuf[vln]; + else + rep = &zr_sp; + zputc(rep); + zputc(&zr_cr); + if ((vln < olnct) && obuf[vln] && obuf[vln]->chr) + *obuf[vln] = *rep; } } @@ -2212,7 +2212,7 @@ moveto(int ln, int cl) } if (cl != vcs) - singmoveto(cl); + singmoveto(cl); } /**/ @@ -2292,7 +2292,7 @@ tc_rightcurs(int ct) /* it is cheaper to send TCRIGHT than reprint the whole prompt */ for (ct = lpromptw - i; ct--; ) tcout(TCRIGHT); - else { + else { if (i != 0) zputc(&zr_cr); tc_upcurs(lprompth - 1); diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index 6b892b822..ce61db27b 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -703,7 +703,7 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) { Thingy t; struct modifier modsave = zmod; - int ret, saveflag = 0, setbindk = 0, remetafy; + int ret, saveflag = 0, setbindk = 0, setlbindk, remetafy; char *wname = *args++, *keymap_restore = NULL, *keymap_tmp; if (!wname) @@ -787,7 +787,8 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) * a vi range to detect a repeated key */ setbindk = setbindk || (t->widget && (t->widget->flags & (WIDGET_INT | ZLE_VIOPER)) == WIDGET_INT); - ret = execzlefunc(t, args, setbindk); + setlbindk = t->widget && (t->widget->flags & ZLE_NOLAST) == ZLE_NOLAST; + ret = execzlefunc(t, args, setbindk, setlbindk); unrefthingy(t); if (saveflag) zmod = modsave; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 2b25d6b2e..fdd168763 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -1236,8 +1236,10 @@ get_comp_string(void) else if (tok == OUTPAR) { if (parct) parct--; - else + else if (linarr) { linarr = 0; + incmdpos = 1; + } } if (inredir && IS_REDIROP(tok)) { rdstr = rdstrbuf; diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index c6df3d89c..2b306fdcd 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -1607,7 +1607,11 @@ static int unapplychange(struct change *ch) { if(ch->hist != histline) { - zle_setline(quietgethist(ch->hist)); + Histent he = quietgethist(ch->hist); + DPUTS(he == NULL, "quietgethist(ch->hist) returned NULL"); + if(he == NULL) + return 1; + zle_setline(he); zlecs = ch->new_cs; return 0; } @@ -1647,7 +1651,11 @@ static int applychange(struct change *ch) { if(ch->hist != histline) { - zle_setline(quietgethist(ch->hist)); + Histent he = quietgethist(ch->hist); + DPUTS(he == NULL, "quietgethist(ch->hist) returned NULL"); + if(he == NULL) + return 1; + zle_setline(he); zlecs = ch->old_cs; return 0; } @@ -1733,7 +1741,7 @@ zlecallhook(char *name, char *arg) args[0] = arg; args[1] = NULL; - execzlefunc(thingy, args, 1); + execzlefunc(thingy, args, 1, 0); unrefthingy(thingy); /* Retain any user interrupt error status */ diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c index a5ff9200c..0f198d0e8 100644 --- a/Src/Zle/zle_vi.c +++ b/Src/Zle/zle_vi.c @@ -216,7 +216,7 @@ getvirange(int wf) * a number of lines is used. If the function used * returns 1, we fail. */ - if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs, 1)) + if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs, 1, 0)) ret = -1; if (viinrepeat) zmult = mult1; diff --git a/Src/builtin.c b/Src/builtin.c index 8dcdcc024..aa5767cf1 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -74,7 +74,7 @@ static struct builtin builtins[] = BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL), BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL), BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlp:%rtux", "E"), - BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMstTuUWx:z", NULL), + BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "ckmMstTuUWx:z", NULL), BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"), BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL), BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL), @@ -720,7 +720,7 @@ bin_set(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) /**** directory-handling builtins ****/ /**/ -int doprintdir = 0; /* set in exec.c (for autocd) */ +int doprintdir = 0; /* set in exec.c (for autocd, cdpath, etc.) */ /* pwd: display the name of the current directory */ @@ -912,7 +912,7 @@ cd_get_dest(char *nam, char **argv, int hard, int func) char *end; doprintdir++; - if (argv[0][1] && (argv[0][0] == '+' || argv[0][0] == '-') + if (!isset(POSIXCD) && argv[0][1] && (argv[0][0] == '+' || argv[0][0] == '-') && strspn(argv[0]+1, "0123456789") == strlen(argv[0]+1)) { dd = zstrtol(argv[0] + 1, &end, 10); if (*end == '\0') { @@ -1251,7 +1251,7 @@ cd_new_pwd(int func, LinkNode dir, int quiet) if (func != BIN_CD && isset(INTERACTIVE)) { if (unset(PUSHDSILENT) && !quiet) printdirstack(); - } else if (doprintdir) { + } else if (unset(CDSILENT) && doprintdir) { fprintdir(pwd, stdout); putchar('\n'); } @@ -1718,7 +1718,7 @@ fcsubs(char **sp, struct asgment *sub) newstr = sub->value.scalar; sub = (Asgment)sub->node.next; oldpos = s; - /* loop over occurences of oldstr in s, replacing them with newstr */ + /* loop over occurrences of oldstr in s, replacing them with newstr */ while ((newpos = (char *)strstr(oldpos, oldstr))) { newmem = (char *) zhalloc(1 + (newpos - s) + strlen(newstr) + strlen(newpos + strlen(oldstr))); @@ -2171,7 +2171,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), !ASG_VALUEP(asg)) on |= PM_UNSET; else if (usepm && (pm->node.flags & PM_READONLY) && - !(on & PM_READONLY)) { + !(on & PM_READONLY) && func != BIN_EXPORT) { zerr("read-only variable: %s", pm->node.nam); return NULL; } @@ -2526,7 +2526,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * Attempt to assign a scalar value to an array. * This can happen if the array is special. * We'll be lenient and guess what the user meant. - * This is how normal assigment works. + * This is how normal assignment works. */ if (*asg->value.scalar) { /* Array with one value */ @@ -2582,9 +2582,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } } pm->node.flags |= (on & PM_READONLY); - - if (OPT_ISSET(ops,'p')) - paramtab->printnode(&pm->node, PRINT_TYPESET); + DPUTS(OPT_ISSET(ops,'p'), "BUG: -p not handled"); return pm; } @@ -2714,7 +2712,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) (!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g'))) on |= PM_LOCAL; - if (on & PM_TIED) { + if ((on & PM_TIED) && !OPT_ISSET(ops, 'p')) { Param apm; struct asgment asg0, asg2; char *oldval = NULL, *joinstr; @@ -3031,7 +3029,7 @@ eval_autoload(Shfunc shf, char *name, Options ops, int func) } if (OPT_MINUS(ops,'X')) { char *fargv[3]; - fargv[0] = name; + fargv[0] = quotestring(name, QT_SINGLE_OPTIONAL); fargv[1] = "\"$@\""; fargv[2] = 0; shf->funcdef = mkautofn(shf); @@ -3250,11 +3248,50 @@ bin_functions(char *name, char **argv, Options ops, int func) if ((off & PM_UNDEFINED) || (OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) || (OPT_ISSET(ops,'x') && !OPT_HASARG(ops,'x')) || - (OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || !scriptname))) { + (OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || !scriptname)) || + (OPT_ISSET(ops,'c') && (OPT_ISSET(ops,'x') || OPT_ISSET(ops,'X') || + OPT_ISSET(ops,'m')))) { zwarnnam(name, "invalid option(s)"); return 1; } + if (OPT_ISSET(ops,'c')) { + Shfunc newsh; + if (!*argv || !argv[1] || argv[2]) { + zwarnnam(name, "-c: requires two arguments"); + return 1; + } + shf = (Shfunc) shfunctab->getnode(shfunctab, *argv); + if (!shf) { + zwarnnam(name, "no such function: %s", *argv); + return 1; + } + if (shf->node.flags & PM_UNDEFINED) { + if (shf->funcdef) { + freeeprog(shf->funcdef); + shf->funcdef = &dummy_eprog; + } + shf = loadautofn(shf, 1, 0, 0); + if (!shf) + return 1; + } + newsh = zalloc(sizeof(*newsh)); + memcpy(newsh, shf, sizeof(*newsh)); + if (newsh->node.flags & PM_LOADDIR) { + /* Expand original location of autoloaded file */ + newsh->node.flags &= ~PM_LOADDIR; + newsh->filename = tricat(shf->filename, "/", shf->node.nam); + } else + newsh->filename = ztrdup(shf->filename); + newsh->funcdef->nref++; + if (newsh->redir) + newsh->redir->nref++; + if (shf->sticky) + newsh->sticky = sticky_emulation_dup(sticky, 0); + shfunctab->addnode(shfunctab, ztrdup(argv[1]), &newsh->node); + return 0; + } + if (OPT_ISSET(ops,'x')) { char *eptr; expand = (int)zstrtol(OPT_ARG(ops,'x'), &eptr, 10); @@ -4993,8 +5030,7 @@ bin_print(char *name, char **args, Options ops, int func) narg = strtoul(c, &endptr, 0); if (*endptr == '$') { c = endptr + 1; - DPUTS(narg <= 0, "specified zero or negative arg"); - if (narg > argc) { + if (narg <= 0 || narg > argc) { zwarnnam(name, "%d: argument specifier out of range", narg); if (fout != stdout) @@ -5514,14 +5550,12 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun /* check for legality */ if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) { p = "?"; - err: zsfree(zoptarg); setsparam(var, ztrdup(p)); if(quiet) { zoptarg = metafy(optbuf, lenoptbuf, META_DUP); } else { - zwarn(*p == '?' ? "bad option: %c%c" : - "argument expected after %c%c option", + zwarn("bad option: %c%c", "?-+"[lenoptbuf], opch); zoptarg=ztrdup(""); } @@ -5532,8 +5566,17 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun if(p[1] == ':') { if(optcind == lenstr) { if(!args[zoptind]) { - p = ":"; - goto err; + zsfree(zoptarg); + if(quiet) { + setsparam(var, ztrdup(":")); + zoptarg = metafy(optbuf, lenoptbuf, META_DUP); + } else { + setsparam(var, ztrdup("?")); + zoptarg = ztrdup(""); + zwarn("argument expected after %c%c option", + "?-+"[lenoptbuf], opch); + } + return 0; } p = ztrdup(args[zoptind++]); } else @@ -5558,7 +5601,11 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun return 0; } -/* Flag that we should exit the shell as soon as all functions return. */ +/* Boolean flag that we should exit the shell as soon as all functions return. + * + * Set by the 'exit' builtin. + */ + /**/ mod_export int exit_pending; @@ -5622,7 +5669,7 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func) } return lastval; } - zexit(num, 0); /* else treat return as logout/exit */ + zexit(num, ZEXIT_NORMAL); /* else treat return as logout/exit */ break; case BIN_LOGOUT: if (unset(LOGINSHELL)) { @@ -5644,7 +5691,7 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func) * If we are already exiting... give this all up as * a bad job. */ - if (stopmsg || (zexit(0,2), !stopmsg)) { + if (stopmsg || (zexit(0, ZEXIT_DEFERRED), !stopmsg)) { retflag = 1; breaks = loops; exit_pending = 1; @@ -5652,7 +5699,7 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func) exit_val = num; } } else - zexit(num, 0); + zexit(num, ZEXIT_NORMAL); break; } return 0; @@ -5737,14 +5784,15 @@ _realexit(void) /* exit the shell. val is the return value of the shell. * * from_where is - * 1 if zexit is called because of a signal - * 2 if we can't actually exit yet (e.g. functions need - * terminating) but should perform the usual interactive tests. + * ZEXIT_SIGNAL if zexit is called because of a signal + * ZEXIT_DEFERRED if we can't actually exit yet (e.g., functions need + * terminating) but should perform the usual interactive + * tests. */ /**/ mod_export void -zexit(int val, int from_where) +zexit(int val, enum zexit_t from_where) { /* * Don't do anything recursively: see below. @@ -5755,7 +5803,7 @@ zexit(int val, int from_where) if (shell_exiting == -1) return; - if (isset(MONITOR) && !stopmsg && from_where != 1) { + if (isset(MONITOR) && !stopmsg && from_where != ZEXIT_SIGNAL) { scanjobs(); /* check if jobs need printing */ if (isset(CHECKJOBS)) checkjobs(); /* check if any jobs are running/stopped */ @@ -5764,8 +5812,9 @@ zexit(int val, int from_where) return; } } - /* Positive in_exit means we have been here before */ - if (from_where == 2 || (shell_exiting++ && from_where)) + /* Positive shell_exiting means we have been here before */ + if (from_where == ZEXIT_DEFERRED || + (shell_exiting++ && from_where != ZEXIT_NORMAL)) return; /* @@ -5781,12 +5830,12 @@ zexit(int val, int from_where) if (isset(MONITOR)) { /* send SIGHUP to any jobs left running */ - killrunjobs(from_where == 1); + killrunjobs(from_where == ZEXIT_SIGNAL); } if (isset(RCS) && interact) { if (!nohistsave) { int writeflags = HFILE_USE_OPTIONS; - if (from_where == 1) + if (from_where == ZEXIT_SIGNAL) writeflags |= HFILE_NO_REWRITE; saveandpophiststack(1, writeflags); savehistfile(NULL, 1, writeflags); @@ -7238,8 +7287,11 @@ bin_umask(char *nam, char **args, Options ops, UNUSED(int func)) char *s = *args; /* Get the current umask. */ - um = umask(0); + queue_signals(); + um = umask(0777); umask(um); + unqueue_signals(); + /* No arguments means to display the current setting. */ if (!s) { if (OPT_ISSET(ops,'S')) { diff --git a/Src/compat.c b/Src/compat.c index 7b5c4411c..8ab335aa1 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -30,8 +30,8 @@ #include "zsh.mdh" #include "compat.pro" -/* Return pointer to first occurence of string t * - * in string s. Return NULL if not present. */ +/* Return pointer to first occurrence of string t * + * in string s. Return NULL if not present. */ /**/ #ifndef HAVE_STRSTR @@ -361,8 +361,18 @@ zgetdir(struct dirsav *d) pino = sbuf.st_ino; pdev = sbuf.st_dev; - /* If they're the same, we've reached the root directory. */ + /* If they're the same, we've reached the root directory... */ if (ino == pino && dev == pdev) { + /* + * ...well, probably. If this was an orphaned . after + * an unmount, or something such, we could be in trouble... + */ + if (stat("/", &sbuf) < 0 || + sbuf.st_ino != ino || + sbuf.st_dev != dev) { + zerr("Failed to get current directory: path invalid"); + return NULL; + } if (!buf[pos]) buf[--pos] = '/'; if (d) { @@ -509,7 +519,7 @@ zgetcwd(void) #endif /* HAVE_GETCWD */ if (!ret) ret = unmeta(pwd); - if (!ret) + if (!ret || *ret == '\0') ret = dupstring("."); return ret; } diff --git a/Src/exec.c b/Src/exec.c index 042ba065a..50027654a 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -940,6 +940,8 @@ hashcmd(char *arg0, char **pp) char *s, buf[PATH_MAX+1]; char **pq; + if (*arg0 == '/') + return NULL; for (; *pp; pp++) if (**pp == '/') { s = buf; @@ -969,6 +971,10 @@ hashcmd(char *arg0, char **pp) return cn; } +/* The value that 'locallevel' had when we forked. When we get back to this + * level, the current process (which is a subshell) will terminate. + */ + /**/ int forklevel; @@ -1688,7 +1694,8 @@ execpline(Estate state, wordcode slcode, int how, int last1) lastwj = thisjob = newjob; - if (list_pipe || (pline_level && !(how & Z_TIMED))) + if (list_pipe || (pline_level && !(how & Z_TIMED) && + !(jn->stat & STAT_NOSTTY))) jn->stat |= STAT_NOPRINT; if (nowait) { @@ -2533,7 +2540,7 @@ setunderscore(char *str) { queue_signals(); if (str && *str) { - int l = strlen(str) + 1, nl = (l + 31) & ~31; + size_t l = strlen(str) + 1, nl = (l + 31) & ~31; if (nl > underscorelen || (underscorelen - nl) > 64) { zfree(zunderscore, underscorelen); @@ -2762,9 +2769,12 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc, sigtrapped[SIGEXIT] = 0; #ifdef HAVE_NICE /* Check if we should run background jobs at a lower priority. */ - if ((how & Z_ASYNC) && isset(BGNICE)) - if (nice(5) < 0) + if ((how & Z_ASYNC) && isset(BGNICE)) { + errno = 0; + nice(5); + if (errno) zwarn("nice(5) failed: %e", errno); + } #endif /* HAVE_NICE */ return 0; @@ -4293,7 +4303,7 @@ save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p) (unset(RESTRICTED) || !(pm->node.flags & PM_RESTRICTED))) { /* * In this case we're just saving parts of - * the parameter in a tempory, so use heap allocation + * the parameter in a temporary, so use heap allocation * and don't bother copying every detail. */ tpm = (Param) hcalloc(sizeof *tpm); @@ -4405,8 +4415,10 @@ closem(int how, int all) /* * Process substitution needs to be visible to user; * fd's are explicitly cleaned up by filelist handling. + * External FDs are managed directly by the user. */ - (all || fdtable[i] != FDT_PROC_SUBST) && + (all || (fdtable[i] != FDT_PROC_SUBST && + fdtable[i] != FDT_EXTERNAL)) && (how == FDT_UNUSED || (fdtable[i] & FDT_TYPE_MASK) == how)) { if (i == SHTTY) SHTTY = -1; @@ -4638,19 +4650,25 @@ getoutput(char *cmd, int qt) return NULL; } -/* read output of command substitution */ +/* read output of command substitution + * + * The file descriptor "in" is closed by the function. + * + * "qt" indicates if the substitution was in double quotes. + * + * "readerror", if not NULL, is used to return any error that + * occurred during the read. + */ /**/ mod_export LinkList readoutput(int in, int qt, int *readerror) { LinkList ret; - char *buf, *ptr; - int bsiz, c, cnt = 0; - FILE *fin; + char *buf, *bufptr, *ptr, inbuf[64]; + int bsiz, c, cnt = 0, readret; int q = queue_signal_level(); - fin = fdopen(in, "r"); ret = newlinklist(); ptr = buf = (char *) hcalloc(bsiz = 64); /* @@ -4662,33 +4680,38 @@ readoutput(int in, int qt, int *readerror) */ dont_queue_signals(); child_unblock(); - while ((c = fgetc(fin)) != EOF || errno == EINTR) { - if (c == EOF) { - errno = 0; - clearerr(fin); - continue; - } - if (imeta(c)) { - *ptr++ = Meta; - c ^= 32; - cnt++; + for (;;) { + readret = read(in, inbuf, 64); + if (readret <= 0) { + if (readret < 0 && errno == EINTR) + continue; + else + break; } - if (++cnt >= bsiz) { - char *pp; - queue_signals(); - pp = (char *) hcalloc(bsiz *= 2); - dont_queue_signals(); + for (bufptr = inbuf; bufptr < inbuf + readret; bufptr++) { + c = *bufptr; + if (imeta(c)) { + *ptr++ = Meta; + c ^= 32; + cnt++; + } + if (++cnt >= bsiz) { + char *pp; + queue_signals(); + pp = (char *) hcalloc(bsiz *= 2); + dont_queue_signals(); - memcpy(pp, buf, cnt - 1); - ptr = (buf = pp) + cnt - 1; + memcpy(pp, buf, cnt - 1); + ptr = (buf = pp) + cnt - 1; + } + *ptr++ = c; } - *ptr++ = c; } child_block(); restore_queue_signals(q); if (readerror) - *readerror = ferror(fin) ? errno : 0; - fclose(fin); + *readerror = readret < 0 ? errno : 0; + close(in); while (cnt && ptr[-1] == '\n') ptr--, cnt--; *ptr = '\0'; @@ -4821,6 +4844,7 @@ getoutputfile(char *cmd, char **eptr) } /* pid == 0 */ + closem(FDT_UNUSED, 0); redup(fd, 1); entersubsh(ESUB_PGRP|ESUB_NOMONITOR, NULL); cmdpush(CS_CMDSUBST); @@ -4982,7 +5006,7 @@ getpipe(char *cmd, int nullexec) procsubstpid = pid; return pipes[!out]; } - entersubsh(ESUB_PGRP, NULL); + entersubsh(ESUB_ASYNC|ESUB_PGRP, NULL); redup(pipes[out], out); closem(FDT_UNUSED, 0); /* this closes pipes[!out] as well */ cmdpush(CS_CMDSUBST); @@ -5929,7 +5953,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) * exit command was handled. */ stopmsg = 1; - zexit(exit_val, 0); + zexit(exit_val, ZEXIT_NORMAL); } } diff --git a/Src/glob.c b/Src/glob.c index ed2c90bd8..f67a376b9 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -274,7 +274,7 @@ addpath(char *s, int l) } /* stat the filename s appended to pathbuf. l should be true for lstat, * - * false for stat. If st is NULL, the file is only checked for existance. * + * false for stat. If st is NULL, the file is only checked for existence. * * s == "" is treated as s == ".". This is necessary since on most systems * * foo/ can be used to reference a non-directory foo. Returns nonzero if * * the file does not exists. */ @@ -400,7 +400,7 @@ insert(char *s, int checked) if (colonmod) { /* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */ char *mod = colonmod; - modify(&news, &mod); + modify(&news, &mod, 1); } if (!statted && (gf_sorts & GS_NORMAL)) { statfullpath(s, &buf, 1); @@ -566,7 +566,7 @@ scanner(Complist q, int shortcircuit) continue; errsfound = errssofar; if (pattry(p, fn)) { - /* if this name matchs the pattern... */ + /* if this name matches the pattern... */ if (pbcwdsav == pathbufcwd && strlen(fn) + pathpos - pathbufcwd >= PATH_MAX) { int err; @@ -2909,6 +2909,12 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, */ mb_charinit(); tmatch = NULL; + set_pat_start(p, l); + if (pattrylen(p, send, 0, 0, &patstralloc, umltot) && + !--n) { + *sp = get_match_ret(&imd, umltot, umltot); + return 1; + } for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) { set_pat_start(p, t-s); if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) @@ -3053,7 +3059,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, case (SUB_END|SUB_SUBSTR): case (SUB_END|SUB_LONG|SUB_SUBSTR): /* Longest/shortest at end, matching substrings. */ - if (!(fl & SUB_LONG)) { + { set_pat_start(p, l); if (pattrylen(p, send, 0, 0, &patstralloc, umltot) && !--n) { @@ -3391,7 +3397,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, case (SUB_END|SUB_SUBSTR): case (SUB_END|SUB_LONG|SUB_SUBSTR): /* Longest/shortest at end, matching substrings. */ - if (!(fl & SUB_LONG)) { + { set_pat_start(p, l); if (pattrylen(p, send, 0, 0, &patstralloc, uml) && !--n) { *sp = get_match_ret(&imd, uml, uml); diff --git a/Src/hashtable.c b/Src/hashtable.c index b7baa3142..e210ddeca 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -996,7 +996,7 @@ printshfuncnode(HashNode hn, int printflags) * expansion of leading tabs. * expand = 0 is standard: use hard tabs. * expand > 0 uses that many spaces. - * expand < 0 uses no identation. + * expand < 0 uses no indentation. * * Note this function and the following two are called with * interrupts queued, so saving and restoring text_expand_tabs diff --git a/Src/hist.c b/Src/hist.c index dbdc1e4e5..5281e8718 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -181,7 +181,7 @@ mod_export char *chline; * To avoid having to modify this every time we modify chline, * we set it when we push the stack, and unset it when we pop * the appropriate value off the stack. As it's never modified - * on the stack this is the only maintainance we ever do on it. + * on the stack this is the only maintenance we ever do on it. * In return, ZLE has to check both zle_chline and (if that's * NULL) chline to get the current value. */ @@ -216,6 +216,7 @@ static struct histfile_stats { char *text; time_t stim, mtim; off_t fpos, fsiz; + int interrupted; zlong next_write_ev; } lasthist; @@ -475,7 +476,7 @@ herrflush(void) * * Note that this is a side effect --- this is not the usual reason * for testing lex_add_raw which is to add the text to a different - * buffer used when we are actually parsing the command substituion + * buffer used when we are actually parsing the command substitution * (nothing to do with ZLE). Sorry. */ while (inbufct && (!strin || lex_add_raw)) { @@ -554,6 +555,27 @@ substfailed(void) return -1; } +/* + * Return a count given by decimal digits after a modifier. + */ +static int +digitcount(void) +{ + int c = ingetc(), count; + + if (idigit(c)) { + count = 0; + do { + count = 10 * count + (c - '0'); + c = ingetc(); + } while (idigit(c)); + } + else + count = 0; + inungetc(c); + return count; +} + /* Perform history substitution, returning the next character afterwards. */ /**/ @@ -834,7 +856,7 @@ histsubchar(int c) } break; case 'h': - if (!remtpath(&sline)) { + if (!remtpath(&sline, digitcount())) { herrflush(); zerr("modifier failed: h"); return -1; @@ -855,7 +877,7 @@ histsubchar(int c) } break; case 't': - if (!remlpaths(&sline)) { + if (!remlpaths(&sline, digitcount())) { herrflush(); zerr("modifier failed: t"); return -1; @@ -898,6 +920,16 @@ histsubchar(int c) case 'u': sline = casemodify(sline, CASMOD_UPPER); break; + case 'P': + if (*sline != '/') { + char *here = zgetcwd(); + if (here[strlen(here)-1] != '/') + sline = zhtricat(metafy(here, -1, META_HEAPDUP), "/", sline); + else + sline = dyncat(here, sline); + } + sline = xsymlink(sline, 1); + break; default: herrflush(); zerr("illegal modifier: %c", c); @@ -1197,8 +1229,9 @@ histreduceblanks(void) chline[pos] = '\0'; } else { ptr = chline + pos; - while ((*ptr++ = *lastptr++)) - ; + if (ptr < lastptr) + while ((*ptr++ = *lastptr++)) + ; } } @@ -1972,16 +2005,18 @@ chrealpath(char **junkptr) /**/ int -remtpath(char **junkptr) +remtpath(char **junkptr, int count) { char *str = strend(*junkptr); /* ignore trailing slashes */ while (str >= *junkptr && IS_DIRSEP(*str)) --str; - /* skip filename */ - while (str >= *junkptr && !IS_DIRSEP(*str)) - --str; + if (!count) { + /* skip filename */ + while (str >= *junkptr && !IS_DIRSEP(*str)) + --str; + } if (str < *junkptr) { if (IS_DIRSEP(**junkptr)) *junkptr = dupstring ("/"); @@ -1990,6 +2025,34 @@ remtpath(char **junkptr) return 0; } + + if (count) + { + /* + * Return this many components, so start from the front. + * Leading slash counts as one component, consistent with + * behaviour of repeated applications of :h. + */ + char *strp = *junkptr; + while (strp < str) { + if (IS_DIRSEP(*strp)) { + if (--count <= 0) { + if (strp == *junkptr) + ++strp; + *strp = '\0'; + return 1; + } + /* Count consecutive separators as one */ + while (IS_DIRSEP(strp[1])) + ++strp; + } + ++strp; + } + + /* Full string needed */ + return 1; + } + /* repeated slashes are considered like a single slash */ while (str > *junkptr && IS_DIRSEP(str[-1])) --str; @@ -2038,7 +2101,7 @@ rembutext(char **junkptr) /**/ mod_export int -remlpaths(char **junkptr) +remlpaths(char **junkptr, int count) { char *str = strend(*junkptr); @@ -2048,12 +2111,29 @@ remlpaths(char **junkptr) --str; str[1] = '\0'; } - for (; str >= *junkptr; --str) - if (IS_DIRSEP(*str)) { - *str = '\0'; - *junkptr = dupstring(str + 1); - return 1; + for (;;) { + for (; str >= *junkptr; --str) { + if (IS_DIRSEP(*str)) { + if (--count > 0) { + if (str > *junkptr) { + --str; + break; + } else { + /* Whole string needed */ + return 1; + } + } + *str = '\0'; + *junkptr = dupstring(str + 1); + return 1; + } } + /* Count consecutive separators as 1 */ + while (str >= *junkptr && IS_DIRSEP(*str)) + --str; + if (str <= *junkptr) + break; + } return 0; } @@ -2544,11 +2624,13 @@ readhistfile(char *fn, int err, int readflags) sb.st_size == 0) return; if (readflags & HFILE_FAST) { - if ((lasthist.fsiz == sb.st_size && lasthist.mtim == sb.st_mtime) - || lockhistfile(fn, 0)) + if (!lasthist.interrupted && + ((lasthist.fsiz == sb.st_size && lasthist.mtim == sb.st_mtime) + || lockhistfile(fn, 0))) return; lasthist.fsiz = sb.st_size; lasthist.mtim = sb.st_mtime; + lasthist.interrupted = 0; } else if ((ret = lockhistfile(fn, 1))) { if (ret == 2) { zwarn("locking failed for %s: %e: reading anyway", fn, errno); @@ -2694,8 +2776,11 @@ readhistfile(char *fn, int err, int readflags) */ if (uselex || remeta) freeheap(); - if (errflag & ERRFLAG_INT) + if (errflag & ERRFLAG_INT) { + /* Can't assume fast read next time if interrupted. */ + lasthist.interrupted = 1; break; + } } if (start && readflags & HFILE_USE_OPTIONS) { zsfree(lasthist.text); @@ -3236,6 +3321,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags) int owb = wb, owe = we, oadx = addedx, onc = nocomments; int ona = noaliases, ocs = zlemetacs, oll = zlemetall; int forloop = 0, rcquotes = opts[RCQUOTES]; + int envarray = 0; char *p, *addedspaceptr; if (!list) @@ -3319,6 +3405,14 @@ bufferwords(LinkList list, char *buf, int *index, int flags) ctxtlex(); if (tok == ENDINPUT || tok == LEXERR) break; + /* + * After an array assignment, return to the initial + * start-of-command state. There could be a second ENVARRAY. + */ + if (tok == OUTPAR && envarray) { + incmdpos = 1; + envarray = 0; + } if (tok == FOR) { /* * The way for (( expr1 ; expr2; expr3 )) is parsed is: @@ -3356,6 +3450,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags) switch (tok) { case ENVARRAY: p = dyncat(tokstr, "=("); + envarray = 1; break; case DINPAR: diff --git a/Src/init.c b/Src/init.c index e7e62e2f7..04a5856ff 100644 --- a/Src/init.c +++ b/Src/init.c @@ -45,7 +45,10 @@ int noexitct = 0; char *zunderscore; /**/ -int underscorelen, underscoreused; +size_t underscorelen; + +/**/ +int underscoreused; /* what level of sourcing we are at */ @@ -134,7 +137,7 @@ loop(int toplevel, int justonce) else stophist = hstop; /* - * Reset all errors, including user interupts. + * Reset all errors, including user interrupts. * This is what allows ^C in an interactive shell * to return us to the command line. */ @@ -158,7 +161,7 @@ loop(int toplevel, int justonce) * Handle that now. */ stopmsg = 1; - zexit(exit_val, 0); + zexit(exit_val, ZEXIT_NORMAL); } if (tok == LEXERR && !lastval) lastval = 1; @@ -200,7 +203,7 @@ loop(int toplevel, int justonce) * that would be inconsistent with the case where * we didn't execute a preexec function. This is * an implementation detail that an interrupting user - * does't care about. + * doesn't care about. */ errflag &= ~ERRFLAG_ERROR; } @@ -359,7 +362,7 @@ static void parseopts_setemulate(char *nam, int flags) * Parse shell options. * * If (flags & PARSEARGS_TOPLEVEL): - * - we are doing shell initilisation + * - we are doing shell initialisation * - nam is the name under which the shell was started * - set up emulation and standard options based on that. * Otherwise: @@ -879,7 +882,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name) char *ptr; int i, j; #if defined(SITEFPATH_DIR) || defined(FPATH_DIR) || defined (ADDITIONAL_FPATH) || defined(FIXED_FPATH_DIR) -#define FPATH_NEEDS_INIT 1 +# define FPATH_NEEDS_INIT 1 char **fpathptr; # if defined(FPATH_DIR) && defined(FPATH_SUBDIRS) char *fpath_subdirs[] = FPATH_SUBDIRS; @@ -991,18 +994,29 @@ setupvals(char *cmd, char *runscript, char *zsh_name) # endif /* ADDITONAL_FPATH */ fpath = fpathptr = (char **)zalloc((fpathlen+1)*sizeof(char *)); # ifdef FIXED_FPATH_DIR + /* Zeroth: /usr/local/share/zsh/site-functions */ *fpathptr++ = ztrdup(FIXED_FPATH_DIR); fpathlen--; # endif # ifdef SITEFPATH_DIR + /* First: the directory from --enable-site-fndir + * + * default: /usr/local/share/zsh/site-functions + * (but changeable by passing --prefix or --datadir to configure) */ *fpathptr++ = ztrdup(SITEFPATH_DIR); fpathlen--; # endif /* SITEFPATH_DIR */ # if defined(ADDITIONAL_FPATH) + /* Second: the directories from --enable-additional-fpath + * + * default: empty list */ for (j = 0; j < more_fndirs_len; j++) *fpathptr++ = ztrdup(more_fndirs[j]); # endif # ifdef FPATH_DIR + /* Third: The directory from --enable-fndir + * + * default: /usr/local/share/zsh/${ZSH_VERSION}/functions */ # ifdef FPATH_SUBDIRS # ifdef ADDITIONAL_FPATH for (j = more_fndirs_len; j < fpathlen; j++) @@ -1010,7 +1024,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name) # else for (j = 0; j < fpathlen; j++) *fpathptr++ = tricat(FPATH_DIR, "/", fpath_subdirs[j]); -#endif +# endif # else *fpathptr++ = ztrdup(FPATH_DIR); # endif @@ -1234,6 +1248,15 @@ init_signals(void) intr(); +#ifdef POSIX_SIGNALS + { + struct sigaction act; + if (!sigaction(SIGQUIT, NULL, &act) && + act.sa_handler == SIG_IGN) + sigtrapped[SIGQUIT] = ZSIG_IGNORED; + } +#endif + #ifndef QDEBUG signal_ignore(SIGQUIT); #endif @@ -1359,7 +1382,7 @@ init_misc(char *cmd, char *zsh_name) bshin = fdopen(SHIN, "r"); execstring(cmd, 0, 1, "cmdarg"); stopmsg = 1; - zexit((exit_pending || shell_exiting) ? exit_val : lastval, 0); + zexit((exit_pending || shell_exiting) ? exit_val : lastval, ZEXIT_NORMAL); } if (interact && isset(RCS)) @@ -1766,20 +1789,20 @@ zsh_main(UNUSED(int argc), char **argv) if (!lastval) lastval = 1; stopmsg = 1; - zexit(lastval, 0); + zexit(lastval, ZEXIT_NORMAL); } if (!(isset(IGNOREEOF) && interact)) { #if 0 if (interact) fputs(islogin ? "logout\n" : "exit\n", shout); #endif - zexit(lastval, 0); + zexit(lastval, ZEXIT_NORMAL); continue; } noexitct++; if (noexitct >= 10) { stopmsg = 1; - zexit(lastval, 0); + zexit(lastval, ZEXIT_NORMAL); } /* * Don't print the message if it was already handled by diff --git a/Src/jobs.c b/Src/jobs.c index 73d7f26da..e7438251e 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1085,7 +1085,7 @@ printjob(Job jn, int lng, int synch) { /* * A subjob still has process, which must finish before - * further excution of the superjob, which the user wants to + * further execution of the superjob, which the user wants to * know about. So report the status of the subjob as if it * were the user-visible superjob. */ @@ -1932,7 +1932,7 @@ getjob(const char *s, const char *prog) /* a digit here means we have a job number */ if (idigit(*s)) { jobnum = atoi(s); - if (jobnum && jobnum <= mymaxjob && myjobtab[jobnum].stat && + if (jobnum > 0 && jobnum <= mymaxjob && myjobtab[jobnum].stat && !(myjobtab[jobnum].stat & STAT_SUBJOB) && /* * If running jobs in a subshell, we are allowed to @@ -2933,6 +2933,7 @@ acquire_pgrp(void) sigaddset(&blockset, SIGTTOU); sigaddset(&blockset, SIGTSTP); oldset = signal_block(blockset); + int loop_count = 0; while ((ttpgrp = gettygrp()) != -1 && ttpgrp != mypgrp) { mypgrp = GETPGRP(); if (mypgrp == mypid) { @@ -2948,8 +2949,21 @@ acquire_pgrp(void) if (read(0, NULL, 0) != 0) {} /* Might generate SIGT* */ signal_block(blockset); mypgrp = GETPGRP(); - if (mypgrp == lastpgrp && !interact) - break; /* Unlikely that pgrp will ever change */ + if (mypgrp == lastpgrp) { + if (!interact) + break; /* Unlikely that pgrp will ever change */ + if (++loop_count == 100) + { + /* + * It's time to give up. The count is arbitrary; + * this is just to fix up unusual cases, so it's + * left large in an attempt not to break normal + * cases where there's some delay in the system + * setting up the terminal. + */ + break; + } + } lastpgrp = mypgrp; } if (mypgrp != mypid) { @@ -2109,7 +2109,7 @@ skipcomm(void) hist_in_word(1); } else { /* - * Set up for nested command subsitution, however + * Set up for nested command substitution, however * we don't actually need the string until we get * back to the top level and recover the lot. * The $() body just appears empty. diff --git a/Src/loop.c b/Src/loop.c index 1013aeb50..01abc6cc9 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -425,7 +425,7 @@ execwhile(Estate state, UNUSED(int do_exec)) breaks--; simple_pline = old_simple_pline; - } else + } else { for (;;) { state->pc = loop; noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; @@ -445,8 +445,11 @@ execwhile(Estate state, UNUSED(int do_exec)) lastval = oldval; break; } - if (retflag) + if (retflag) { + if (breaks) + breaks--; break; + } /* In case the loop body is also a functional no-op, * make sure signal handlers recognize ^C as above. */ @@ -470,6 +473,7 @@ execwhile(Estate state, UNUSED(int do_exec)) freeheap(); oldval = lastval; } + } cmdpop(); popheap(); loops--; @@ -566,7 +570,7 @@ execif(Estate state, int do_exec) if (run) { /* we need to ignore lastval until we reach execcmd() */ - if (olderrexit) + if (olderrexit || run == 2) noerrexit = olderrexit; else if (lastval) noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_UNTIL_EXEC; @@ -728,7 +732,7 @@ exectry(Estate state, int do_exec) Wordcode end, always; int endval; int save_retflag, save_breaks, save_contflag; - zlong save_try_errflag, save_try_tryflag, save_try_interrupt; + zlong save_try_errflag, save_try_interrupt; end = state->pc + WC_TRY_SKIP(state->pc[-1]); always = state->pc + 1 + WC_TRY_SKIP(*state->pc); @@ -737,12 +741,9 @@ exectry(Estate state, int do_exec) cmdpush(CS_CURSH); /* The :try clause */ - save_try_tryflag = try_tryflag; - try_tryflag = 1; - + ++try_tryflag; execlist(state, 1, do_exec); - - try_tryflag = save_try_tryflag; + --try_tryflag; /* Don't record errflag here, may be reset. However, */ /* endval should show failure when there is an error. */ diff --git a/Src/main.c b/Src/main.c index 30eef5a25..bad698ee7 100644 --- a/Src/main.c +++ b/Src/main.c @@ -34,7 +34,7 @@ * Support for Cygwin binary/text mode filesystems. * Peter A. Castro <doctor@fruitbat.org> * - * This deserves some explaination, because it uses Cygwin specific + * This deserves some explanation, because it uses Cygwin specific * runtime functions. * * Cygwin supports the notion of binary or text mode access to files @@ -43,7 +43,7 @@ * and all. If it's on a text mounted filesystem, Cygwin will strip out * the CRs. This presents a problem because zsh code doesn't allow for * CRLF's as line terminators. So, we must force all open files to be - * in text mode reguardless of the underlying filesystem attributes. + * in text mode regardless of the underlying filesystem attributes. * However, we only want to do this for reading, not writing as we still * want to write files in the mode of the filesystem. To do this, * we have two options: augment all {f}open() calls to have O_TEXT added to diff --git a/Src/makepro.awk b/Src/makepro.awk index 0498c1545..226d3f96b 100644 --- a/Src/makepro.awk +++ b/Src/makepro.awk @@ -121,7 +121,7 @@ BEGIN { # initialiser. dcltor = substr(line, 1, RLENGTH-1) line = substr(line, RLENGTH+1) - sub(/\=.*$/, "", dcltor) + sub(/=.*$/, "", dcltor) match(dcltor, /^([^_0-9A-Za-z]| const )*/) dcltor = substr(dcltor, 1, RLENGTH) "@+" substr(dcltor, RLENGTH+1) match(dcltor, /^.*@\+[_0-9A-Za-z]+/) diff --git a/Src/math.c b/Src/math.c index a38770073..905b910ec 100644 --- a/Src/math.c +++ b/Src/math.c @@ -1133,8 +1133,7 @@ notzero(mnumber a) /* macro to pop three values off the value stack */ -/**/ -void +static void op(int what) { mnumber a, b, c, *spval; @@ -1569,14 +1568,19 @@ mathparse(int pc) if (errflag) return; + queue_signals(); mtok = zzlex(); /* Handle empty input */ - if (pc == TOPPREC && mtok == EOI) + if (pc == TOPPREC && mtok == EOI) { + unqueue_signals(); return; + } checkunary(mtok, optr); while (prec[mtok] <= pc) { - if (errflag) + if (errflag) { + unqueue_signals(); return; + } switch (mtok) { case NUM: push(yyval, NULL, 0); @@ -1595,6 +1599,7 @@ mathparse(int pc) if (mtok != M_OUTPAR) { if (!errflag) zerr("bad math expression: ')' expected"); + unqueue_signals(); return; } break; @@ -1613,6 +1618,7 @@ mathparse(int pc) if (mtok != COLON) { if (!errflag) zerr("bad math expression: ':' expected"); + unqueue_signals(); return; } if (q) @@ -1636,4 +1642,5 @@ mathparse(int pc) mtok = zzlex(); checkunary(mtok, optr); } + unqueue_signals(); } @@ -1120,7 +1120,7 @@ struct m_hdr { /* length of memory header, length of first field of memory header and minimal size of a block left free (if we allocate memory and take a block from the free list that is larger than needed, it must have at - least M_MIN extra bytes to be splitted; if it has, the rest is put on + least M_MIN extra bytes to be split; if it has, the rest is put on the free list) */ #define M_HSIZE (sizeof(struct m_hdr)) diff --git a/Src/module.c b/Src/module.c index 33d75ebbd..f41b82f25 100644 --- a/Src/module.c +++ b/Src/module.c @@ -442,7 +442,7 @@ add_autobin(const char *module, const char *bnam, int flags) } /* Remove the builtin added previously by addbuiltin(). Returns * - * zero on succes and -1 if there is no builtin with that name. */ + * zero on success and -1 if there is no builtin with that name. */ /**/ int diff --git a/Src/options.c b/Src/options.c index 600b649e4..48c14c179 100644 --- a/Src/options.c +++ b/Src/options.c @@ -108,6 +108,7 @@ static struct optname optns[] = { {{NULL, "cbases", 0}, CBASES}, {{NULL, "cprecedences", OPT_EMULATE|OPT_NONZSH}, CPRECEDENCES}, {{NULL, "cdablevars", OPT_EMULATE}, CDABLEVARS}, +{{NULL, "cdsilent", 0}, CDSILENT}, {{NULL, "chasedots", OPT_EMULATE}, CHASEDOTS}, {{NULL, "chaselinks", OPT_EMULATE}, CHASELINKS}, {{NULL, "checkjobs", OPT_EMULATE|OPT_ZSH}, CHECKJOBS}, diff --git a/Src/params.c b/Src/params.c index 089a958ae..863b32600 100644 --- a/Src/params.c +++ b/Src/params.c @@ -44,7 +44,11 @@ #endif #endif -/* what level of localness we are at */ +/* What level of localness we are at. + * + * Hand-wavingly, this is incremented at every function call and decremented + * at every function return. See startparamscope(). + */ /**/ mod_export int locallevel; @@ -474,7 +478,13 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \ static Param argvparam; -/* hash table containing the parameters */ +/* "parameter table" - hash table containing the parameters + * + * realparamtab always points to the shell's global table. paramtab is sometimes + * temporarily changed to point at another table, while dealing with the keys + * of an associative array (for example, see makecompparams() which initializes + * the associative array ${compstate}). + */ /**/ mod_export HashTable paramtab, realparamtab; @@ -1124,8 +1134,10 @@ copyparam(Param tpm, Param pm, int fakecopy) tpm->base = pm->base; tpm->width = pm->width; tpm->level = pm->level; - if (!fakecopy) + if (!fakecopy) { + tpm->old = pm->old; tpm->node.flags &= ~PM_SPECIAL; + } switch (PM_TYPE(pm->node.flags)) { case PM_SCALAR: tpm->u.str = ztrdup(pm->gsu.s->getfn(pm)); @@ -2201,10 +2213,10 @@ getstrvalue(Value v) if (v->flags & VALFLAG_SUBST) { if (v->pm->node.flags & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z)) { - unsigned int fwidth = v->pm->width ? v->pm->width : MB_METASTRLEN(s); + size_t fwidth = v->pm->width ? (unsigned int)v->pm->width : MB_METASTRLEN(s); switch (v->pm->node.flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) { char *t, *tend; - unsigned int t0; + size_t t0; case PM_LEFT: case PM_LEFT | PM_RIGHT_Z: @@ -2585,7 +2597,7 @@ assignstrvalue(Value v, char *val, int flags) Param pm = v->pm; /* Size doesn't change, can limit actions to only * overwriting bytes in already allocated string */ - strncpy(z + v->start, val, vlen); + memcpy(z + v->start, val, vlen); /* Implement remainder of strsetfn */ if (!(pm->node.flags & PM_HASHELEM) && ((pm->node.flags & PM_NAMEDDIR) || @@ -3546,7 +3558,7 @@ setiparam(char *s, zlong val) /* * Set an integer parameter without forcing creation of an integer type. - * This is useful if the integer is going to be set to a parmaeter which + * This is useful if the integer is going to be set to a parameter which * would usually be scalar but may not exist. */ @@ -3617,10 +3629,18 @@ unsetparam_pm(Param pm, int altflag, int exp) altpm = (Param) paramtab->getnode(paramtab, altremove); /* tied parameters are at the same local level as each other */ oldpm = NULL; - while (altpm && altpm->level > pm->level) { - /* param under alternate name hidden by a local */ - oldpm = altpm; - altpm = altpm->old; + /* + * Look for param under alternate name hidden by a local. + * If this parameter is special, however, the visible + * parameter is the special and the hidden one is keeping + * an old value --- we just mark the visible one as unset. + */ + if (altpm && !(altpm->node.flags & PM_SPECIAL)) + { + while (altpm && altpm->level > pm->level) { + oldpm = altpm; + altpm = altpm->old; + } } if (altpm) { if (oldpm && !altpm->level) { @@ -5858,7 +5878,7 @@ printparamnode(HashNode hn, int printflags) doneminus = 0; } if ((pmptr->flags & PMTF_USE_WIDTH) && p->width) { - printf("%d ", p->width); + printf("%u ", p->width); doneminus = 0; } } diff --git a/Src/parse.c b/Src/parse.c index 83383f10c..de1b27967 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -414,10 +414,10 @@ ecstrcode(char *s) return c; } else { Eccstr p, *pp; - int cmp; + long cmp; for (pp = &ecstrs; (p = *pp); ) { - if (!(cmp = p->nfunc - ecnfunc) && !(cmp = (((signed)p->hashval) - ((signed)val))) && !(cmp = strcmp(p->str, s))) { + if (!(cmp = p->nfunc - ecnfunc) && !(cmp = (((long)p->hashval) - ((long)val))) && !(cmp = strcmp(p->str, s))) { return p->offs; } pp = (cmp < 0 ? &(p->left) : &(p->right)); @@ -1811,7 +1811,7 @@ par_simple(int *cmplx, int nr) for (ptr = str; *ptr; ptr++) { /* * We can't treat this as "simple" if it contains - * expansions that require process subsitution, since then + * expansions that require process substitution, since then * we need process handling. */ if (ptr[1] == Inpar && @@ -1899,6 +1899,14 @@ par_simple(int *cmplx, int nr) p += nrediradd; sr += nrediradd; } + else if (postassigns) + { + /* C.f. normal case below */ + postassigns++; + ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0)); + ecstr(toksave); + ecstr(""); /* TBD can possibly optimise out */ + } else { ecstr(toksave); @@ -2271,7 +2279,8 @@ par_redir(int *rp, char *idstring) void setheredoc(int pc, int type, char *str, char *termstr, char *munged_termstr) { - ecbuf[pc] = WCB_REDIR(type | REDIR_FROM_HEREDOC_MASK); + int varid = WC_REDIR_VARID(ecbuf[pc]) ? REDIR_VARID_MASK : 0; + ecbuf[pc] = WCB_REDIR(type | REDIR_FROM_HEREDOC_MASK | varid); ecbuf[pc + 2] = ecstrcode(str); ecbuf[pc + 3] = ecstrcode(termstr); ecbuf[pc + 4] = ecstrcode(munged_termstr); diff --git a/Src/pattern.c b/Src/pattern.c index 737f5cdcb..c7c2c8bea 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -156,7 +156,7 @@ typedef union upat *Upat; * P_BRANCH, but applies to the immediately preceding branch. The code in * the corresponding branch is followed by a P_EXCSYNC, which simply * acts as a marker that a P_EXCLUDE comes next. The P_EXCLUDE - * has a pointer to char embeded in it, which works + * has a pointer to char embedded in it, which works * like P_WBRANCH: if we get to the P_EXCSYNC, and we already matched * up to the same position, fail. Thus we are forced to backtrack * on closures in the P_BRANCH if the first attempt was excluded. @@ -502,7 +502,7 @@ patcompcharsset(void) } } -/* Called before parsing a set of file matchs to initialize flags */ +/* Called before parsing a set of file matches to initialize flags */ /**/ void @@ -2030,6 +2030,16 @@ int errsfound; /* Total error count so far */ /**/ int forceerrs; /* Forced maximum error count */ +/* + * exactpos is used to remember how far down an exact string we have + * matched, if we are doing approximation and can therefore redo from + * the same point; we never need to otherwise. + * + * exactend is a pointer to the end of the string, which isn't + * null-terminated. + */ +static char *exactpos, *exactend; + /**/ void pattrystart(void) @@ -2072,7 +2082,7 @@ patmungestring(char **string, int *stringlen, int *unmetalenin) } /* - * Allocate memeory for pattern match. Note this is specific to use + * Allocate memory for pattern match. Note this is specific to use * of pattern *and* trial string. * * Unmetafy a trial string for use in pattern matching, if needed. @@ -2093,7 +2103,7 @@ patmungestring(char **string, int *stringlen, int *unmetalenin) * In patstralloc (supplied by caller, must last until last pattry is done) * unmetalen is the unmetafied length of the string; it will be * calculated if the input value is negative. - * unmetalenp is the umetafied length of a path segment preceeding + * unmetalenp is the umetafied length of a path segment preceding * the trial string needed for file mananagement; it is calculated as * needed so does not need to be initialised. * alloced is the memory allocated on the heap --- same as return value from @@ -2227,7 +2237,7 @@ pattrylen(Patprog prog, char *string, int len, int unmetalen, * depends on both prog *and* the trial string). This should only be * done if there is no path prefix (pathpos == 0) as otherwise the path * buffer and unmetafied string may not match. To do this, - * patallocstr() is callled (use force = 1 to ensure it is alway + * patallocstr() is called (use force = 1 to ensure it is always * unmetafied); paststralloc points to existing storage. Memory is * on the heap. * @@ -2321,7 +2331,7 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, if (patstralloc->alloced) { /* - * Unmetafied; we need pattern sring that's also unmetafied. + * Unmetafied; we need pattern string that's also unmetafied. * We'll cache it in the patstralloc structure. * Note it's on the heap. */ @@ -2379,7 +2389,7 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, /* * Remember the length in case used for ${..#..} etc. * In this case, we didn't unmetafy the pattern string - * In the orignal structure, but it might be unmetafied + * in the original structure, but it might be unmetafied * for use with an unmetafied test string. */ patinlen = pstrlen; @@ -2463,6 +2473,8 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, patinput = patinstart; + exactpos = exactend = NULL; + /* The only external call to patmatch --- all others are recursive */ if (patmatch((Upat)progstr)) { /* * we were lazy and didn't save the globflags if an exclusion @@ -2480,7 +2492,7 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, * Optimization: if we didn't find any Meta characters * to begin with, we don't need to look for them now. * - * For patstralloc pased in, we want the unmetafied length. + * For patstralloc passed in, we want the unmetafied length. */ if (patstralloc == &patstralloc_struct && patstralloc->unmetalen != origlen) { @@ -2607,10 +2619,10 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, } /* - * Return length of previous succesful match. This is + * Return length of previous successful match. This is * in metafied bytes, i.e. includes a count of Meta characters, * unless the match was done on an unmetafied string using - * a patstralloc stuct, in which case it, too is unmetafed. + * a patstralloc struct, in which case it too is unmetafied. * Unusual and futile attempt at modular encapsulation. */ @@ -2653,16 +2665,6 @@ patmatchlen(void) (charmatch_cache = (expr), CHARMATCH(charmatch_cache, chpa)) /* - * exactpos is used to remember how far down an exact string we have - * matched, if we are doing approximation and can therefore redo from - * the same point; we never need to otherwise. - * - * exactend is a pointer to the end of the string, which isn't - * null-terminated. - */ -static char *exactpos, *exactend; - -/* * Main matching routine. * * Testing the tail end of a match is usually done by recursion, but diff --git a/Src/prompt.c b/Src/prompt.c index f2b3f161e..b65bfb86b 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -163,7 +163,7 @@ promptpath(char *p, int npath, int tilde) * * txtchangep gives an integer controlling the attributes of * the prompt. This is for use in zle to maintain the attributes - * consistenly. Other parts of the shell should not need to use it. + * consistently. Other parts of the shell should not need to use it. */ /**/ @@ -1075,10 +1075,9 @@ putstr(int d) mod_export void countprompt(char *str, int *wp, int *hp, int overf) { - int w = 0, h = 1; + int w = 0, h = 1, multi = 0, wcw = 0; int s = 1; #ifdef MULTIBYTE_SUPPORT - int wcw, multi = 0; char inchar; mbstate_t mbs; wchar_t wc; @@ -1087,10 +1086,28 @@ countprompt(char *str, int *wp, int *hp, int overf) #endif for (; *str; str++) { - if (w > zterm_columns && overf >= 0) { - w = 0; + /* + * Avoid double-incrementing the height when there's a newline in the + * prompt and the line it terminates takes up exactly the width of the + * terminal + */ + while (w > zterm_columns && overf >= 0 && !multi) { h++; + if (wcw) { + /* + * Wide characters don't get split off. They move to the + * next line if there is not enough space. + */ + w = wcw; + break; + } else { + /* + * Tabs overflow to the next line as if they were made of spaces. + */ + w -= zterm_columns; + } } + wcw = 0; /* * Input string should be metafied, so tokens in it should * be real tokens, even if there are multibyte characters. @@ -1171,12 +1188,19 @@ countprompt(char *str, int *wp, int *hp, int overf) * This isn't easy to handle generally; just assume there's no * output. */ - if(w >= zterm_columns && overf >= 0) { - if (!overf || w > zterm_columns) { - w = 0; - h++; + while (w > zterm_columns && overf >= 0) { + h++; + if (wcw) { + w = wcw; + break; + } else { + w -= zterm_columns; } } + if (w == zterm_columns && overf == 0) { + w = 0; + h++; + } if(wp) *wp = w; if(hp) diff --git a/Src/signals.c b/Src/signals.c index f294049c2..96ff9e9b3 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -654,7 +654,7 @@ zhandler(int sig) _exit(SIGPIPE); else if (!isatty(SHTTY)) { stopmsg = 1; - zexit(SIGPIPE, 1); + zexit(SIGPIPE, ZEXIT_SIGNAL); } } break; @@ -662,7 +662,7 @@ zhandler(int sig) case SIGHUP: if (!handletrap(SIGHUP)) { stopmsg = 1; - zexit(SIGHUP, 1); + zexit(SIGHUP, ZEXIT_SIGNAL); } break; @@ -670,7 +670,7 @@ zhandler(int sig) if (!handletrap(SIGINT)) { if ((isset(PRIVILEGED) || isset(RESTRICTED)) && isset(INTERACTIVE) && (noerrexit & NOERREXIT_SIGNAL)) - zexit(SIGINT, 1); + zexit(SIGINT, ZEXIT_SIGNAL); if (list_pipe || chline || simple_pline) { breaks = loops; errflag |= ERRFLAG_INT; @@ -703,7 +703,7 @@ zhandler(int sig) errflag = noerrs = 0; zwarn("timeout"); stopmsg = 1; - zexit(SIGALRM, 1); + zexit(SIGALRM, ZEXIT_SIGNAL); } } break; @@ -1011,10 +1011,6 @@ removetrap(int sig) (!trapped || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT))) dosavetrap(sig, locallevel); - if (!trapped) { - unqueue_signals(); - return NULL; - } if (sigtrapped[sig] & ZSIG_TRAPPED) nsigtrapped--; sigtrapped[sig] = 0; diff --git a/Src/sort.c b/Src/sort.c index 92ee1c0d4..8faf9349c 100644 --- a/Src/sort.c +++ b/Src/sort.c @@ -33,6 +33,9 @@ /* Flag for direction of sort: 1 forwards, -1 reverse */ static int sortdir; +/* Flag that sort ignores backslashes */ +static int sortnobslash; + /* Flag that sort is numeric */ static int sortnumeric; @@ -113,9 +116,24 @@ eltpcmp(const void *a, const void *b) bs += (laststarta - as); as += (laststarta - as); } + + if (sortnobslash) { + while (*as && *bs) { + if (*as == '\\') + as++; + if (*bs == '\\') + bs++; + if (*as != *bs || !*as) + break; + as++; + bs++; + } + } + #ifdef HAVE_STRCOLL cmp = strcoll(as, bs); #endif + if (sortnumeric) { for (; *as == *bs && *as; as++, bs++); #ifndef HAVE_STRCOLL @@ -162,7 +180,10 @@ mod_export int zstrcmp(const char *as, const char *bs, int sortflags) { struct sortelt ae, be, *aeptr, *beptr; - int oldsortdir = sortdir, oldsortnumeric = sortnumeric, ret; + int oldsortdir = sortdir; + int oldsortnobslash = sortnobslash; + int oldsortnumeric = sortnumeric; + int ret; ae.cmp = as; be.cmp = bs; @@ -173,11 +194,13 @@ zstrcmp(const char *as, const char *bs, int sortflags) beptr = &be; sortdir = 1; + sortnobslash = (sortflags & SORTIT_IGNORING_BACKSLASHES) ? 1 : 0; sortnumeric = (sortflags & SORTIT_NUMERICALLY) ? 1 : 0; ret = eltpcmp(&aeptr, &beptr); /* Paranoia: I don't think we ever need to restore these. */ + sortnobslash = oldsortnobslash; sortnumeric = oldsortnumeric; sortdir = oldsortdir; @@ -227,7 +250,7 @@ strmetasort(char **array, int sortwhat, int *unmetalenp) if (unmetalenp) { /* * Already unmetafied. We just need to check for - * embededded nulls. + * embedded nulls. */ int count = unmetalenp[arrptr-array]; /* Remember this length for sorted array */ diff --git a/Src/subst.c b/Src/subst.c index 60eb33390..79efc9ad2 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -91,7 +91,7 @@ keyvalpairelement(LinkList list, LinkNode node) * "flag"s contains PREFORK_* flags, defined in zsh.h. * * "ret_flags" is used to return PREFORK_* values from nested parameter - * substitions. It may be NULL in which case PREFORK_SUBEXP must not + * substitutions. It may be NULL in which case PREFORK_SUBEXP must not * appear in flags; any return value from below will be discarded. */ @@ -1548,7 +1548,7 @@ untok_and_escape(char *s, int escapes, int tok_arg) /* * See if an argument str looks like a subscript or length following * a colon and parse it. It must be followed by a ':' or nothing. - * If this succeeds, expand and return the evaulated expression if + * If this succeeds, expand and return the evaluated expression if * found, else return NULL. * * We assume this is what is meant if the first character is not @@ -1682,7 +1682,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, */ int wantt = 0; /* - * Indicates spliting a string into an array. There aren't + * Indicates splitting a string into an array. There aren't * actually that many special cases for this --- which may * be why it doesn't work properly; we split in some cases * where we shouldn't, in particular on the multsubs for @@ -1732,7 +1732,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, int mods = 0; /* * The (z) flag, nothing to do with SH_WORD_SPLIT which is tied - * spbreak, see above; fairly straighforward in use but c.f. + * spbreak, see above; fairly straightforward in use but cf. * the comment for mods. * * This gets set to one of the LEXFLAGS_* values. @@ -2725,7 +2725,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * substitution is in quotes) always good enough? Potentially * we may be OK by now --- all potential `@'s and subexpressions * have been handled, including any [@] index which comes up - * by virture of v->isarr being set to SCANPM_ISVAR_AT which + * by virtue of v->isarr being set to SCANPM_ISVAR_AT which * is now in isarr. * * However, if we are replacing multsub() with something that @@ -3044,7 +3044,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * shouldn't be any if not interactive. */ stopmsg = 1; - zexit(1, 0); + zexit(1, ZEXIT_NORMAL); } else _exit(1); } @@ -3110,7 +3110,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, /* * Either loop over an array doing replacements or - * do the replacment on a string. + * do the replacement on a string. * * We need an untokenized value for matching. */ @@ -3438,7 +3438,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, s--; if (unset(KSHARRAYS) || inbrace) { if (!isarr) - modify(&val, &s); + modify(&val, &s, inbrace); else { char *ss; char **ap = aval; @@ -3447,12 +3447,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, while ((*pp = *ap++)) { ss = s; - modify(pp++, &ss); + modify(pp++, &ss, inbrace); } if (pp == aval) { char *t = ""; ss = s; - modify(&t, &ss); + modify(&t, &ss, inbrace); } s = ss; } @@ -4182,6 +4182,12 @@ arithsubst(char *a, char **bptr, char *rest) * PTR is an in/out parameter. On entry it contains the string of colon * modifiers. On return it points past the last recognised modifier. * + * INBRACE is non-zero if we are in some form of a bracketed or + * parenthesised expression; it is zero for modifiers ocurring + * in an an unbracketed variable substitution. This means that + * $foo:t222 is treated ias ${foo:t}222 rather than ${foo:t222} + * for backward compatibility. + * * Example: * ENTRY: *str is "." *ptr is ":AN" * RETURN: *str is "/home/foobar" (equal to $PWD) *ptr points to the "N" @@ -4189,7 +4195,7 @@ arithsubst(char *a, char **bptr, char *rest) /**/ void -modify(char **str, char **ptr) +modify(char **str, char **ptr, int inbrace) { char *ptr1, *ptr2, *ptr3, *lptr, c, *test, *sep, *t, *tt, tc, *e; char *copy, *all, *tmp, sav, sav1, *ptr1end; @@ -4202,6 +4208,8 @@ modify(char **str, char **ptr) *str = dupstring(*str); while (**ptr == ':') { + int count = 0; + lptr = *ptr; (*ptr)++; wall = gbal = 0; @@ -4214,10 +4222,8 @@ modify(char **str, char **ptr) case 'a': case 'A': case 'c': - case 'h': case 'r': case 'e': - case 't': case 'l': case 'u': case 'q': @@ -4226,6 +4232,17 @@ modify(char **str, char **ptr) c = **ptr; break; + case 'h': + case 't': + c = **ptr; + if (inbrace && idigit((*ptr)[1])) { + do { + count = 10 * count + ((*ptr)[1] - '0'); + ++(*ptr); + } while (idigit((*ptr)[1])); + } + break; + case 's': c = **ptr; (*ptr)++; @@ -4392,7 +4409,7 @@ modify(char **str, char **ptr) break; } case 'h': - remtpath(©); + remtpath(©, count); break; case 'r': remtext(©); @@ -4401,7 +4418,7 @@ modify(char **str, char **ptr) rembutext(©); break; case 't': - remlpaths(©); + remlpaths(©, count); break; case 'l': copy = casemodify(tt, CASMOD_LOWER); @@ -4478,7 +4495,7 @@ modify(char **str, char **ptr) break; } case 'h': - remtpath(str); + remtpath(str, count); break; case 'r': remtext(str); @@ -4487,7 +4504,7 @@ modify(char **str, char **ptr) rembutext(str); break; case 't': - remlpaths(str); + remlpaths(str, count); break; case 'l': *str = casemodify(*str, CASMOD_LOWER); diff --git a/Src/text.c b/Src/text.c index 3658b1bc6..69530ae79 100644 --- a/Src/text.c +++ b/Src/text.c @@ -470,8 +470,13 @@ gettext2(Estate state) " || " : " && "); s->code = *state->pc++; s->pop = (WC_SUBLIST_TYPE(s->code) == WC_SUBLIST_END); - if (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_NOT) - taddstr("! "); + if (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_NOT) { + if (WC_SUBLIST_SKIP(s->code) == 0) + stack = 1; + taddstr((stack || (!(WC_SUBLIST_FLAGS(s->code) & + WC_SUBLIST_SIMPLE) && wc_code(*state->pc) != + WC_PIPE)) ? "!" : "! "); + } if (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_COPROC) taddstr("coproc "); } @@ -579,7 +584,7 @@ gettext2(Estate state) state->pc = end; if (!nargs) { /* - * Unnamed fucntion. + * Unnamed function. * We're not going to pull any arguments off * later, so skip them now... */ diff --git a/Src/utils.c b/Src/utils.c index 32f600858..f5667f389 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -287,9 +287,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) { const char *str; int num; -#ifdef DEBUG long lnum; -#endif #ifdef HAVE_STRERROR_R #define ERRBUFSIZE (80) int olderrno; @@ -325,12 +323,10 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) nicezputs(s, file); break; } -#ifdef DEBUG case 'L': lnum = va_arg(ap, long); fprintf(file, "%ld", lnum); break; -#endif case 'd': num = va_arg(ap, int); fprintf(file, "%d", num); @@ -2201,6 +2197,31 @@ gettempname(const char *prefix, int use_heap) #ifdef HAVE__MKTEMP /* Zsh uses mktemp() safely, so silence the warnings */ ret = (char *) _mktemp(ret); +#elif HAVE_MKSTEMP && defined(DEBUG) + { + /* zsh uses mktemp() safely (all callers use O_EXCL, and one of them + * uses mkfifo()/mknod(), as opposed to open()), but some compilers + * warn about this anyway and give no way to disable the warning. To + * appease them, use mkstemp() and then close the fd and unlink the + * filename, to match callers' expectations. + * + * But do this in debug builds only, because we don't want to suffer + * x3 the disk access (touch, unlink, touch again) in production. + */ + int fd; + errno = 0; + fd = mkstemp(ret); + if (fd < 0) + zwarn("can't get a temporary filename: %e", errno); + else { + close(fd); + ret = ztrdup(ret); + + errno = 0; + if (unlink(ret) < 0) + zwarn("unlinking a temporary filename failed: %e", errno); + } + } #else ret = (char *) mktemp(ret); #endif @@ -3159,6 +3180,8 @@ spckword(char **s, int hist, int cmd, int ask) scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0); if (autocd) { char **pp; + if (cd_able_vars(unmeta(guess))) + return; for (pp = cdpath; *pp; pp++) { char bestcd[PATH_MAX + 1]; int thisdist; @@ -3336,7 +3359,7 @@ morefmt: case '.': { long fnsec = nsec; - if (digs > 9) + if (digs < 0 || digs > 9) digs = 9; if (ztrftimebuf(&bufsize, digs)) return -1; diff --git a/Src/watch.c b/Src/watch.c index cd7dc643d..93b3cb134 100644 --- a/Src/watch.c +++ b/Src/watch.c @@ -98,7 +98,7 @@ /* * In utmpx, the ut_name field is replaced by ut_user. - * Howver, on some systems ut_name may already be defined this + * However, on some systems ut_name may already be defined this * way for the purposes of utmp. */ # ifndef ut_name @@ -455,7 +455,7 @@ enum { */ #define FDT_FLOCK_EXEC 6 /* - * Entry used by a process substition. + * Entry used by a process substitution. * This marker is not tested internally as we associated the file * descriptor with a job for closing. * @@ -1254,8 +1254,8 @@ enum { /* * Assignment has value? - * If the assignment is an arrray, then it certainly has a value --- we - * can only tell if there's an expicit assignment. + * If the assignment is an array, then it certainly has a value --- we + * can only tell if there's an explicit assignment. */ #define ASG_VALUEP(asg) (ASG_ARRAYP(asg) || \ @@ -1444,8 +1444,8 @@ struct builtin { */ #define BINF_HANDLES_OPTS (1<<18) /* - * Handles the assignement interface. The argv list actually contains - * two nested litsts, the first of normal arguments, and the second of + * Handles the assignment interface. The argv list actually contains + * two nested lists, the first of normal arguments, and the second of * assignment structures. */ #define BINF_ASSIGN (1<<19) @@ -2006,7 +2006,7 @@ enum { enum { /* * Set if the string had whitespace at the start - * that should cause word splitting against any preceeding string. + * that should cause word splitting against any preceding string. */ MULTSUB_WS_AT_START = 1, /* @@ -2272,9 +2272,9 @@ struct histent { */ #define LEXFLAGS_NEWLINE 0x0010 -/******************************************/ -/* Definitions for programable completion */ -/******************************************/ +/*******************************************/ +/* Definitions for programmable completion */ +/*******************************************/ /* Nothing special. */ #define IN_NOTHING 0 @@ -2348,6 +2348,7 @@ enum { CASEMATCH, CBASES, CDABLEVARS, + CDSILENT, CHASEDOTS, CHASELINKS, CHECKJOBS, @@ -2998,7 +2999,7 @@ struct sortelt { int origlen; /* * The length of the string, if needed, else -1. - * The length is only needed if there are embededded nulls. + * The length is only needed if there are embedded nulls. */ int len; }; @@ -3221,6 +3222,14 @@ enum { /* Hooks in core. */ /***************************************/ +/* The type of zexit()'s second parameter, which see. */ +enum zexit_t { + /* This isn't a bitfield. The values are here just for explicitness. */ + ZEXIT_NORMAL = 0, + ZEXIT_SIGNAL = 1, + ZEXIT_DEFERRED = 2 +}; + #define EXITHOOK (zshhooks + 0) #define BEFORETRAPHOOK (zshhooks + 1) #define AFTERTRAPHOOK (zshhooks + 2) diff --git a/Src/zsh_system.h b/Src/zsh_system.h index e7d529b6e..85e198f2e 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -522,7 +522,7 @@ struct timespec { # define RLIMIT_VMEM RLIMIT_AS #endif -#ifdef HAVE_SYS_CAPABILITY_H +#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_CAP_GET_PROC) # include <sys/capability.h> #endif diff --git a/Src/ztype.h b/Src/ztype.h index ae7236774..5c85b0cd7 100644 --- a/Src/ztype.h +++ b/Src/ztype.h @@ -66,7 +66,7 @@ * shell initialisation. */ #define ZTF_INIT (0x0001) /* One-off initialisation done */ -#define ZTF_INTERACT (0x0002) /* Shell interative and reading from stdin */ +#define ZTF_INTERACT (0x0002) /* Shell interactive and reading from stdin */ #define ZTF_SP_COMMA (0x0004) /* Treat comma as a special characters */ #define ZTF_BANGCHAR (0x0008) /* Treat bangchar as a special character */ |