diff options
Diffstat (limited to 'Src')
-rw-r--r-- | Src/Modules/clone.c | 2 | ||||
-rw-r--r-- | Src/Modules/pcre.c | 33 | ||||
-rw-r--r-- | Src/Modules/zpty.c | 26 | ||||
-rw-r--r-- | Src/Zle/complist.c | 6 | ||||
-rw-r--r-- | Src/Zle/zle.h | 2 | ||||
-rw-r--r-- | Src/Zle/zle_hist.c | 2 | ||||
-rw-r--r-- | Src/Zle/zle_keymap.c | 111 | ||||
-rw-r--r-- | Src/Zle/zle_main.c | 53 | ||||
-rw-r--r-- | Src/Zle/zle_refresh.c | 6 | ||||
-rw-r--r-- | Src/Zle/zle_thingy.c | 7 | ||||
-rw-r--r-- | Src/builtin.c | 256 | ||||
-rw-r--r-- | Src/exec.c | 20 | ||||
-rw-r--r-- | Src/glob.c | 17 | ||||
-rw-r--r-- | Src/hashtable.c | 9 | ||||
-rw-r--r-- | Src/hashtable.h | 2 | ||||
-rw-r--r-- | Src/init.c | 30 | ||||
-rw-r--r-- | Src/lex.c | 49 | ||||
-rw-r--r-- | Src/options.c | 2 | ||||
-rw-r--r-- | Src/params.c | 64 | ||||
-rw-r--r-- | Src/parse.c | 34 | ||||
-rw-r--r-- | Src/pattern.c | 17 | ||||
-rw-r--r-- | Src/subst.c | 14 | ||||
-rw-r--r-- | Src/text.c | 32 | ||||
-rw-r--r-- | Src/utils.c | 429 | ||||
-rw-r--r-- | Src/zsh.h | 34 |
25 files changed, 956 insertions, 301 deletions
diff --git a/Src/Modules/clone.c b/Src/Modules/clone.c index 5db1c9222..930429248 100644 --- a/Src/Modules/clone.c +++ b/Src/Modules/clone.c @@ -93,7 +93,7 @@ bin_clone(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) /* Clear mygrp so that acquire_pgrp() gets the new process group. * (acquire_pgrp() is called from init_io()) */ mypgrp = 0; - init_io(); + init_io(NULL); setsparam("TTY", ztrdup(ttystrname)); } close(ttyfd); diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index 2393cd1e7..aa5c8ed5b 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -190,18 +190,25 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar, if (want_begin_end) { char *ptr = arg; zlong offs = 0; + int clen, leftlen; /* Count the characters before the match */ - MB_METACHARINIT(); - while (ptr < arg + ovec[0]) { + MB_CHARINIT(); + leftlen = ovec[0]; + while (leftlen) { offs++; - ptr += MB_METACHARLEN(ptr); + clen = MB_CHARLEN(ptr, leftlen); + ptr += clen; + leftlen -= clen; } setiparam("MBEGIN", offs + !isset(KSHARRAYS)); /* Add on the characters in the match */ - while (ptr < arg + ovec[1]) { + leftlen = ovec[1] - ovec[0]; + while (leftlen) { offs++; - ptr += MB_METACHARLEN(ptr); + clen = MB_CHARLEN(ptr, leftlen); + ptr += clen; + leftlen -= clen; } setiparam("MEND", offs + !isset(KSHARRAYS) - 1); if (nelem) { @@ -219,17 +226,23 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar, ptr = arg; offs = 0; /* Find the start offset */ - MB_METACHARINIT(); - while (ptr < arg + ipair[0]) { + MB_CHARINIT(); + leftlen = ipair[0]; + while (leftlen) { offs++; - ptr += MB_METACHARLEN(ptr); + clen = MB_CHARLEN(ptr, leftlen); + ptr += clen; + leftlen -= clen; } convbase(buf, offs + !isset(KSHARRAYS), 10); *bptr = ztrdup(buf); /* Continue to the end offset */ - while (ptr < arg + ipair[1]) { + leftlen = ipair[1] - ipair[0]; + while (leftlen) { offs++; - ptr += MB_METACHARLEN(ptr); + clen = MB_CHARLEN(ptr, leftlen); + ptr += clen; + leftlen -= clen; } convbase(buf, offs + !isset(KSHARRAYS) - 1, 10); *eptr = ztrdup(buf); diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 3b8366076..63ff7578c 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -399,7 +399,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) close(master); close(coprocin); close(coprocout); - init_io(); + init_io(NULL); setsparam("TTY", ztrdup(ttystrname)); opts[INTERACTIVE] = 0; @@ -614,14 +614,23 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch) break; } if (cmd->read != -1 || (ret = read(cmd->fd, buf + used, 1)) == 1) { + int readchar; if (cmd->read != -1) { ret = 1; - buf[used] = (char) cmd->read; + readchar = cmd->read; cmd->read = -1; - } + } else + readchar = STOUC(buf[used]); + if (imeta(readchar)) { + buf[used++] = Meta; + buf[used++] = (char) (readchar ^ 32); + } else + buf[used++] = (char) readchar; seen = 1; - if (++used == blen) { + if (used >= blen-1) { if (!*args) { + buf[used] = '\0'; + unmetafy(buf, &used); write_loop(1, buf, used); used = 0; } else { @@ -633,7 +642,8 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch) buf[used] = '\0'; if (!prog) { - if (ret <= 0 || (*args && buf[used - 1] == '\n')) + if (ret <= 0 || (*args && buf[used - 1] == '\n' && + (used < 2 || buf[used-2] != Meta))) break; } else { if (ret < 0 @@ -666,9 +676,11 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch) return 1; } if (*args) - setsparam(*args, ztrdup(metafy(buf, used, META_HREALLOC))); - else if (used) + setsparam(*args, ztrdup(buf)); + else if (used) { + unmetafy(buf, &used); write_loop(1, buf, used); + } if (seen && (!prog || matchok || !mustmatch)) return 0; diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 29aaee82a..0ccb88505 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -2584,6 +2584,12 @@ domenuselect(Hookdef dummy, Chdata dat) if (!do_last_key) { zmult = 1; cmd = getkeycmd(); + /* + * On interrupt, we'll exit due to cmd being empty. + * Don't propagate the interrupt any further, which + * can screw up redrawing. + */ + errflag &= ~ERRFLAG_INT; if (mtab_been_reallocated) { do_last_key = 1; continue; diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 2d672de3b..e9b14281d 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -213,6 +213,8 @@ struct widget { #define ZLE_KEEPSUFFIX (1<<9) /* DON'T remove added suffix */ #define ZLE_NOTCOMMAND (1<<10) /* widget should not alter lastcmd */ #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 */ /* thingies */ diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index 95d96c95c..abd6e1749 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -1480,6 +1480,7 @@ doisearch(char **args, int dir, int pattern) isearch_active = 0; ref: zlecallhook("zle-isearch-update", NULL); + redrawhook(); zrefresh(); if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) { int i; @@ -1694,6 +1695,7 @@ doisearch(char **args, int dir, int pattern) statusline = NULL; unmetafy_line(); zlecallhook("zle-isearch-exit", NULL); + redrawhook(); if (exitfn) exitfn(zlenoargs); selectkeymap(okeymap, 1); diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 069580f8a..382eb8d41 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -1449,6 +1449,104 @@ default_bindings(void) /*************************/ /* reading key sequences */ /*************************/ +/**/ +#ifdef MULTIBYTE_SUPPORT +/* + * Get the remainder of a character if we support multibyte + * input strings. It may not require any more input, but + * we haven't yet checked. What's read in so far is available + * in keybuf; if we read more we will top keybuf up. + * + * This version is used when we are still resolving the input key stream + * into bindings. Once that has been done this function shouldn't be + * used: instead, see getrestchar() in zle_main.c. + * + * This supports a self-insert binding at any stage of a key sequence. + * Typically we handle 8-bit characters by having only the first byte + * bound to self insert; then we immediately get here and read in as + * many further bytes as necessary. However, it's possible that any set + * of bytes up to full character is bound to self-insert; then we get + * here later and read as much as possible, which could be a complete + * character, from keybuf before attempting further input. + * + * At the end of the process, the full multibyte character is available + * in keybuf, so the return value may be superfluous. + */ + +/**/ +mod_export ZLE_INT_T +getrestchar_keybuf(void) +{ + char c; + wchar_t outchar; + int inchar, timeout, bufind = 0, buflen = keybuflen; + static mbstate_t mbs; + size_t cnt; + + /* + * We are guaranteed to set a valid wide last character, + * although it may be WEOF (which is technically not + * a wide character at all...) + */ + lastchar_wide_valid = 1; + memset(&mbs, 0, sizeof mbs); + + /* + * Return may be zero if we have a NULL; handle this like + * any other character. + */ + while (1) { + if (bufind < buflen) { + c = STOUC(keybuf[bufind++]); + if (c == Meta) { + DPUTS(bufind == buflen, "Meta at end of keybuf"); + c = STOUC(keybuf[bufind++]) ^ 32; + } + } else { + /* + * Always apply KEYTIMEOUT to the remains of the input + * character. The parts of a multibyte character should + * arrive together. If we don't do this the input can + * get stuck if an invalid byte sequence arrives. + */ + inchar = getbyte(1L, &timeout); + /* getbyte deliberately resets lastchar_wide_valid */ + lastchar_wide_valid = 1; + if (inchar == EOF) { + memset(&mbs, 0, sizeof mbs); + if (timeout) + { + /* + * This case means that we got a valid initial byte + * (since we tested for EOF above), but the followup + * timed out. This probably indicates a duff character. + * Return a '?'. + */ + lastchar = '?'; + return lastchar_wide = L'?'; + } + else + return lastchar_wide = WEOF; + } + c = inchar; + addkeybuf(inchar); + } + + cnt = mbrtowc(&outchar, &c, 1, &mbs); + if (cnt == MB_INVALID) { + /* + * Invalid input. Hmm, what's the right thing to do here? + */ + memset(&mbs, 0, sizeof mbs); + return lastchar_wide = WEOF; + } + if (cnt != MB_INCOMPLETE) + break; + } + return lastchar_wide = (ZLE_INT_T)outchar; +} +/**/ +#endif /* read a sequence of keys that is bound to some command in a keymap */ @@ -1503,16 +1601,9 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) f->widget->flags & ZLE_VIOPER); #ifdef MULTIBYTE_SUPPORT if ((f == Th(z_selfinsert) || f == Th(z_selfinsertunmeta)) && - !lastchar_wide_valid) { - int len; - VARARR(char, mbc, MB_CUR_MAX); - ZLE_INT_T inchar = getrestchar(lastchar, mbc, &len); - if (inchar != WEOF && len) { - char *ptr = mbc; - while (len--) - addkeybuf(STOUC(*ptr++)); - lastlen = keybuflen; - } + !lastchar_wide_valid && !ispfx) { + (void)getrestchar_keybuf(); + lastlen = keybuflen; } #endif } diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 38427e8e3..6e2bfded8 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -1025,6 +1025,32 @@ getrestchar(int inchar, char *outstr, int *outcount) /**/ #endif +/**/ +void redrawhook(void) +{ + Thingy initthingy; + if ((initthingy = rthingy_nocreate("zle-line-pre-redraw"))) { + int lastcmd_prev = lastcmd; + int old_incompfunc = incompfunc; + char *args[2]; + Thingy lbindk_save = lbindk, bindk_save = bindk; + refthingy(lbindk_save); + refthingy(bindk_save); + args[0] = initthingy->nam; + args[1] = NULL; + incompfunc = 0; + execzlefunc(initthingy, args, 0); + incompfunc = old_incompfunc; + unrefthingy(initthingy); + unrefthingy(lbindk); + unrefthingy(bindk); + lbindk = lbindk_save; + bindk = bindk_save; + /* we can't set ZLE_NOTCOMMAND since it's not a legit widget, so + * restore lastcmd manually so that we don't mess up the global state */ + lastcmd = lastcmd_prev; + } +} /**/ void @@ -1084,6 +1110,8 @@ zlecore(void) errflag |= ERRFLAG_ERROR; break; } + + redrawhook(); #ifdef HAVE_POLL if (baud && !(lastcmd & ZLE_MENUCMP)) { struct pollfd pfd; @@ -1113,6 +1141,7 @@ zlecore(void) zrefresh(); freeheap(); + } region_active = 0; @@ -1191,7 +1220,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) vistartchange = -1; zleline = (ZLE_STRING_T)zalloc(((linesz = 256) + 2) * ZLE_CHAR_SIZE); *zleline = ZWC('\0'); - virangeflag = lastcmd = done = zlecs = zlell = mark = 0; + virangeflag = lastcmd = done = zlecs = zlell = mark = yankb = yanke = 0; vichgflag = 0; viinsbegin = 0; statusline = NULL; @@ -1344,6 +1373,8 @@ execzlefunc(Thingy func, char **args, int set_bindk) eofsent = 1; ret = 1; } else { + int inuse = wflags & WIDGET_INUSE; + w->flags |= WIDGET_INUSE; if(!(wflags & ZLE_KEEPSUFFIX)) removesuffix(); if(!(wflags & ZLE_MENUCMP)) { @@ -1367,6 +1398,12 @@ execzlefunc(Thingy func, char **args, int set_bindk) ret = w->u.fn(args); unqueue_signals(); } + if (!inuse) { + if (w->flags & WIDGET_FREE) + freewidget(w); + else + w->flags &= ~WIDGET_INUSE; + } if (!(wflags & ZLE_NOTCOMMAND)) lastcmd = wflags; } @@ -1387,6 +1424,8 @@ execzlefunc(Thingy func, char **args, int set_bindk) int osc = sfcontext, osi = movefd(0); int oxt = isset(XTRACE); LinkList largs = NULL; + int inuse = w->flags & WIDGET_INUSE; + w->flags |= WIDGET_INUSE; if (*args) { largs = newlinklist(); @@ -1402,8 +1441,15 @@ execzlefunc(Thingy func, char **args, int set_bindk) opts[XTRACE] = oxt; sfcontext = osc; endparamscope(); - lastcmd = w->flags; - w->flags = 0; + lastcmd = w->flags & ~(WIDGET_INUSE|WIDGET_FREE); + if (inuse) { + w->flags &= WIDGET_INUSE|WIDGET_FREE; + } else { + if (w->flags & WIDGET_FREE) + freewidget(w); + else + w->flags = 0; + } r = 1; redup(osi, 0); } @@ -1795,6 +1841,7 @@ recursiveedit(UNUSED(char **args)) { int locerror; + redrawhook(); zrefresh(); zlecore(); diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 6facff429..3d2471e27 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -338,9 +338,9 @@ zle_set_highlight(void) for (; *atrs; atrs++) { if (!strcmp(*atrs, "none")) { /* reset attributes for consistency... usually unnecessary */ - special_atr_on = default_atr_on = - paste_atr_on_set = 0; - special_atr_on_set = region_atr_on_set = + special_atr_on = default_atr_on = 0; + special_atr_on_set = 1; + paste_atr_on_set = region_atr_on_set = isearch_atr_on_set = suffix_atr_on_set = 1; } else if (strpfx("default:", *atrs)) { match_highlight(*atrs + 8, &default_atr_on); diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index 271fd8efc..21495b6f2 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -253,9 +253,14 @@ unbindwidget(Thingy t, int override) /* Free a widget. */ /**/ -static void +void freewidget(Widget w) { + if (w->flags & WIDGET_INUSE) { + w->flags |= WIDGET_FREE; + return; + } + if (w->flags & WIDGET_NCOMP) { zsfree(w->u.comp.wid); zsfree(w->u.comp.func); diff --git a/Src/builtin.c b/Src/builtin.c index cac4f42f9..dd20f9eab 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -62,7 +62,7 @@ static struct builtin builtins[] = BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL), BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL), BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL), - BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"), + BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"), BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL), /* * We used to behave as if the argument to -e was optional. @@ -99,14 +99,14 @@ static struct builtin builtins[] = #endif BUILTIN("popd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 1, BIN_POPD, "q", NULL), - BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:x:X:z-", NULL), - BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, NULL, NULL), + BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:v:x:X:z-", NULL), + BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, "v:", NULL), BUILTIN("pushd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_PUSHD, "qsPL", NULL), BUILTIN("pushln", 0, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"), BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL), BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "IlLnr", NULL), BUILTIN("read", 0, bin_read, 0, -1, 0, "cd:ek:%lnpqrst:%zu:AE", NULL), - BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"), + BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_READONLY, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"), BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"), BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL), BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL), @@ -387,7 +387,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn) if (*arg) { if(*arg == Meta) *++arg ^= 32; - zwarn("bad option: -%c", *arg); + zwarnnam(name, "bad option: -%c", *arg); return 1; } arg = *++argv; @@ -2213,6 +2213,8 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), mkarray(NULL), 0))) return NULL; } + if (errflag) + return NULL; pm->node.flags |= (on & PM_READONLY); if (OPT_ISSET(ops,'p')) paramtab->printnode(&pm->node, PRINT_TYPESET); @@ -2533,6 +2535,10 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) if (OPT_ISSET(ops,'f')) return bin_functions(name, argv, ops, func); + /* POSIX handles "readonly" specially */ + if (func == BIN_READONLY && isset(POSIXBUILTINS) && !OPT_PLUS(ops, 'g')) + ops->ind['g'] = 1; + /* Translate the options into PM_* flags. * * Unfortunately, this depends on the order * * these flags are defined in zsh.h */ @@ -4019,12 +4025,62 @@ bin_print(char *name, char **args, Options ops, int func) char *start, *endptr, *c, *d, *flag, *buf = NULL, spec[14], *fmt = NULL; char **first, **argp, *curarg, *flagch = "'0+- #", save = '\0', nullstr = '\0'; size_t rcount, count = 0; + FILE *fout = stdout; #ifdef HAVE_OPEN_MEMSTREAM size_t mcount; +#define ASSIGN_MSTREAM(BUF,FOUT) \ + do { \ + if ((FOUT = open_memstream(&BUF, &mcount)) == NULL) { \ + zwarnnam(name, "open_memstream failed"); \ + return 1; \ + } \ + } while (0) + /* + * Some implementations of open_memstream() have a bug such that, + * if fflush() is followed by fclose(), another NUL byte is written + * to the buffer at the wrong position. Therefore we must fclose() + * before reading. + */ +#define READ_MSTREAM(BUF,FOUT) \ + ((fclose(FOUT) == 0) ? mcount : (size_t)-1) +#define CLOSE_MSTREAM(FOUT) 0 + +#else /* simulate HAVE_OPEN_MEMSTREAM */ + +#define ASSIGN_MSTREAM(BUF,FOUT) \ + do { \ + int tempfd; \ + char *tmpf; \ + if ((tempfd = gettempfile(NULL, 1, &tmpf)) < 0) { \ + zwarnnam(name, "can't open temp file: %e", errno); \ + return 1; \ + } \ + unlink(tmpf); \ + if ((fout = fdopen(tempfd, "w+")) == NULL) { \ + close(tempfd); \ + zwarnnam(name, "can't open temp file: %e", errno); \ + return 1; \ + } \ + } while (0) +#define READ_MSTREAM(BUF,FOUT) \ + ((((count = ftell(FOUT)), (BUF = (char *)zalloc(count + 1))) && \ + ((fseek(FOUT, 0L, SEEK_SET) == 0) && !(BUF[count] = '\0')) && \ + (fread(BUF, 1, count, FOUT) == count)) ? count : (size_t)-1) +#define CLOSE_MSTREAM(FOUT) fclose(FOUT) + #endif - FILE *fout = stdout; - Histent ent; +#define IS_MSTREAM(FOUT) \ + (FOUT != stdout && \ + (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s') || OPT_ISSET(ops,'v'))) + + /* Testing EBADF special-cases >&- redirections */ +#define CLOSE_CLEANLY(FOUT) \ + (IS_MSTREAM(FOUT) ? CLOSE_MSTREAM(FOUT) == 0 : \ + ((FOUT == stdout) ? (fflush(FOUT) == 0 || errno == EBADF) : \ + (fclose(FOUT) == 0))) /* implies error for -u on a closed fd */ + + Histent ent; mnumber mnumval; double doubleval; int intval; @@ -4032,6 +4088,47 @@ bin_print(char *name, char **args, Options ops, int func) zulong zulongval; char *stringval; + /* Error check option combinations and option arguments */ + + if (OPT_ISSET(ops, 'z') + + OPT_ISSET(ops, 's') + OPT_ISSET(ops, 'S') + + OPT_ISSET(ops, 'v') > 1) { + zwarnnam(name, "only one of -s, -S, -v, or -z allowed"); + return 1; + } + if ((OPT_ISSET(ops, 'z') | OPT_ISSET(ops, 's') | OPT_ISSET(ops, 'S')) + + (OPT_ISSET(ops, 'c') | OPT_ISSET(ops, 'C')) > 1) { + zwarnnam(name, "-c or -C not allowed with -s, -S, or -z"); + return 1; + } + if ((OPT_ISSET(ops, 'z') | OPT_ISSET(ops, 'v') | + OPT_ISSET(ops, 's') | OPT_ISSET(ops, 'S')) + + (OPT_ISSET(ops, 'p') | OPT_ISSET(ops, 'u')) > 1) { + zwarnnam(name, "-p or -u not allowed with -s, -S, -v, or -z"); + return 1; + } + /* + if (OPT_ISSET(ops, 'f') && + (OPT_ISSET(ops, 'S') || OPT_ISSET(ops, 'c') || OPT_ISSET(ops, 'C'))) { + zwarnnam(name, "-f not allowed with -c, -C, or -S"); + return 1; + } + */ + + /* -C -- number of columns */ + if (!fmt && OPT_ISSET(ops,'C')) { + char *eptr, *argptr = OPT_ARG(ops,'C'); + nc = (int)zstrtol(argptr, &eptr, 10); + if (*eptr) { + zwarnnam(name, "number expected after -%c: %s", 'C', argptr); + return 1; + } + if (nc <= 0) { + zwarnnam(name, "invalid number of columns: %s", argptr); + return 1; + } + } + if (func == BIN_PRINTF) { if (!strcmp(*args, "--") && !*++args) { zwarnnam(name, "not enough arguments"); @@ -4096,7 +4193,7 @@ bin_print(char *name, char **args, Options ops, int func) } } /* -P option -- interpret as a prompt sequence */ - if(OPT_ISSET(ops,'P')) { + if (OPT_ISSET(ops,'P')) { /* * promptexpand uses permanent storage: to avoid * messy memory management, stick it on the heap @@ -4110,13 +4207,13 @@ bin_print(char *name, char **args, Options ops, int func) free(str); } /* -D option -- interpret as a directory, and use ~ */ - if(OPT_ISSET(ops,'D')) { + if (OPT_ISSET(ops,'D')) { Nameddir d; queue_signals(); /* TODO: finddir takes a metafied file */ d = finddir(args[n]); - if(d) { + if (d) { int dirlen = strlen(d->dir); char *arg = zhalloc(len[n] - dirlen + strlen(d->node.nam) + 2); sprintf(arg, "~%s%s", d->node.nam, args[n] + dirlen); @@ -4139,26 +4236,12 @@ bin_print(char *name, char **args, Options ops, int func) strmetasort(args, flags, len); } - /* -C -- number of columns */ - if (!fmt && OPT_ISSET(ops,'C')) { - char *eptr, *argptr = OPT_ARG(ops,'C'); - nc = (int)zstrtol(argptr, &eptr, 10); - if (*eptr) { - zwarnnam(name, "number expected after -%c: %s", 'C', argptr); - return 1; - } - if (nc <= 0) { - zwarnnam(name, "invalid number of columns: %s", argptr); - return 1; - } - } - /* -u and -p -- output to other than standard output */ if ((OPT_HASARG(ops,'u') || OPT_ISSET(ops,'p')) && /* rule out conflicting options -- historical precedence */ ((!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) || - !(OPT_ISSET(ops, 'z') || - OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')))) { + !(OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') || + OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')))) { int fdarg, fd; if (OPT_ISSET(ops, 'p')) { @@ -4179,8 +4262,7 @@ bin_print(char *name, char **args, Options ops, int func) } else { fdarg = (int)zstrtol(argptr, &eptr, 10); if (*eptr) { - zwarnnam(name, "number expected after -%c: %s", 'u', - argptr); + zwarnnam(name, "number expected after -u: %s", argptr); return 1; } } @@ -4197,6 +4279,10 @@ bin_print(char *name, char **args, Options ops, int func) } } + if (OPT_ISSET(ops, 'v') || + (fmt && (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')))) + ASSIGN_MSTREAM(buf,fout); + /* -c -- output in columns */ if (!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) { int l, nr, sc, n, t, i; @@ -4348,18 +4434,29 @@ bin_print(char *name, char **args, Options ops, int func) } fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout); } - /* Testing EBADF special-cases >&- redirections */ - if ((fout != stdout) ? (fclose(fout) != 0) : - (fflush(fout) != 0 && errno != EBADF)) { + if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1) + ret = 1; + if (!CLOSE_CLEANLY(fout) || ret) { zwarnnam(name, "write error: %e", errno); ret = 1; } + if (buf) { + /* assert: we must be doing -v at this point */ + queue_signals(); + if (ret) + free(buf); + else + setsparam(OPT_ARG(ops, 'v'), + metafy(buf, rcount, META_REALLOC)); + unqueue_signals(); + } return ret; } /* normal output */ if (!fmt) { - if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 's')) { + if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') || + OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')) { /* * We don't want the arguments unmetafied after all. */ @@ -4457,14 +4554,24 @@ bin_print(char *name, char **args, Options ops, int func) OPT_ISSET(ops,'N') ? '\0' : ' ', fout); } } - if (!(OPT_ISSET(ops,'n') || nnl)) + if (!(OPT_ISSET(ops,'n') || OPT_ISSET(ops, 'v') || nnl)) fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout); - /* Testing EBADF special-cases >&- redirections */ - if ((fout != stdout) ? (fclose(fout) != 0) : - (fflush(fout) != 0 && errno != EBADF)) { + if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1) + ret = 1; + if (!CLOSE_CLEANLY(fout) || ret) { zwarnnam(name, "write error: %e", errno); ret = 1; } + if (buf) { + /* assert: we must be doing -v at this point */ + queue_signals(); + if (ret) + free(buf); + else + setsparam(OPT_ARG(ops, 'v'), + metafy(buf, rcount, META_REALLOC)); + unqueue_signals(); + } return ret; } @@ -4474,20 +4581,6 @@ bin_print(char *name, char **args, Options ops, int func) * special cases of printing to a ZLE buffer or the history, however. */ - if (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')) { -#ifdef HAVE_OPEN_MEMSTREAM - if ((fout = open_memstream(&buf, &mcount)) == NULL) - zwarnnam(name, "open_memstream failed"); -#else - int tempfd; - char *tmpf; - if ((tempfd = gettempfile(NULL, 1, &tmpf)) < 0 - || (fout = fdopen(tempfd, "w+")) == NULL) - zwarnnam(name, "can't open temp file: %e", errno); - unlink(tmpf); -#endif - } - /* printf style output */ *spec = '%'; argp = args; @@ -4751,11 +4844,9 @@ bin_print(char *name, char **args, Options ops, int func) } zwarnnam(name, "%s: invalid directive", start); if (*c) c[1] = save; - /* Testing EBADF special-cases >&- redirections */ - if ((fout != stdout) ? (fclose(fout) != 0) : - (fflush(fout) != 0 && errno != EBADF)) { + /* Why do we care about a clean close here? */ + if (!CLOSE_CLEANLY(fout)) zwarnnam(name, "write error: %e", errno); - } #ifdef HAVE_OPEN_MEMSTREAM if (buf) free(buf); @@ -4853,41 +4944,34 @@ bin_print(char *name, char **args, Options ops, int func) /* if there are remaining args, reuse format string */ } while (*argp && argp != first && !fmttrunc && !OPT_ISSET(ops,'r')); - if (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')) { -#ifdef HAVE_OPEN_MEMSTREAM - putc(0, fout); - fclose(fout); - fout = NULL; -#else - rewind(fout); - buf = (char *)zalloc(count + 1); - fread(buf, count, 1, fout); - buf[count] = '\0'; -#endif + if (IS_MSTREAM(fout)) { queue_signals(); - if (OPT_ISSET(ops,'z')) { - zpushnode(bufstack, buf); + if ((rcount = READ_MSTREAM(buf,fout)) == -1) { + zwarnnam(name, "i/o error: %e", errno); + if (buf) + free(buf); } else { - ent = prepnexthistent(); - ent->node.nam = buf; - ent->stim = ent->ftim = time(NULL); - ent->node.flags = 0; - ent->words = (short *)NULL; - addhistnode(histtab, ent->node.nam, ent); + stringval = metafy(buf, rcount, META_REALLOC); + if (OPT_ISSET(ops,'z')) { + zpushnode(bufstack, stringval); + } else if (OPT_ISSET(ops,'v')) { + setsparam(OPT_ARG(ops, 'v'), stringval); + } else { + ent = prepnexthistent(); + ent->node.nam = stringval; + ent->stim = ent->ftim = time(NULL); + ent->node.flags = 0; + ent->words = (short *)NULL; + addhistnode(histtab, ent->node.nam, ent); + } } unqueue_signals(); } -#ifdef HAVE_OPEN_MEMSTREAM - if (fout) -#endif + if (!CLOSE_CLEANLY(fout)) { - /* Testing EBADF special-cases >&- redirections */ - if ((fout != stdout) ? (fclose(fout) != 0) : - (fflush(fout) != 0 && errno != EBADF)) { - zwarnnam(name, "write error: %e", errno); - ret = 1; - } + zwarnnam(name, "write error: %e", errno); + ret = 1; } return ret; } @@ -6463,7 +6547,13 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) nargs = arrlen(argv); if (nargs == 3 || nargs == 4) { - if (*argv[0] == '(' && *argv[nargs-1] == ')') { + /* + * As parentheses are an extension, we need to be careful --- + * if this is a three-argument expression that could + * be a binary operator, prefer that. + */ + if (!strcmp(argv[0], "(") && !strcmp(argv[nargs-1],")") && + (nargs != 3 || !is_cond_binary_op(argv[1]))) { argv[nargs-1] = NULL; argv++; } diff --git a/Src/exec.c b/Src/exec.c index c0ee527b7..352615c83 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -471,9 +471,10 @@ zexecve(char *pth, char **argv, char **newenvp) if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) { argv0 = *argv; *argv = pth; + execvebuf[0] = '\0'; ct = read(fd, execvebuf, POUNDBANGLIMIT); close(fd); - if (ct > 0) { + if (ct >= 0) { if (execvebuf[0] == '#') { if (execvebuf[1] == '!') { for (t0 = 0; t0 != ct; t0++) @@ -2264,7 +2265,7 @@ addvars(Estate state, Wordcode pc, int addflags) * is implicitly scoped. */ flags = (!(addflags & ADDVAR_RESTORE) && - locallevel > 0 && isset(WARNCREATEGLOBAL)) ? + locallevel > forklevel && isset(WARNCREATEGLOBAL)) ? ASSPM_WARN_CREATE : 0; xtr = isset(XTRACE); if (xtr) { @@ -2784,6 +2785,11 @@ execcmd(Estate state, int input, int output, int how, int last1) * arguments before and no command substitution * has provided a status. */ + if (badcshglob == 1) { + zerr("no match"); + lastval = 1; + return; + } cmdoutval = use_cmdoutval ? lastval : 0; if (varspc) addvars(state, varspc, 0); @@ -3225,7 +3231,7 @@ execcmd(Estate state, int input, int output, int how, int last1) * not terminal, unless `file' is a terminal. */ if (nullexec == 1 && fn->fd1 == 0 && isset(SHINSTDIN) && interact && !zleactive) - init_io(); + init_io(NULL); break; case REDIR_CLOSE: if (fn->varid) { @@ -3475,10 +3481,10 @@ execcmd(Estate state, int input, int output, int how, int last1) restore_queue_signals(q); } else if (is_builtin || is_shfunc) { LinkList restorelist = 0, removelist = 0; + int do_save = 0; /* builtin or shell function */ - if (!forked && varspc) { - int do_save = 0; + if (!forked) { if (isset(POSIXBUILTINS)) { /* * If it's a function or special builtin --- save @@ -3497,7 +3503,7 @@ execcmd(Estate state, int input, int output, int how, int last1) if ((cflags & BINF_COMMAND) || !assign) do_save = 1; } - if (do_save) + if (do_save && varspc) save_params(state, varspc, &restorelist, &removelist); } if (varspc) { @@ -3643,6 +3649,8 @@ execcmd(Estate state, int input, int output, int how, int last1) } dont_queue_signals(); lastval = execbuiltin(args, assigns, (Builtin) hn); + if (do_save & BINF_COMMAND) + errflag &= ~ERRFLAG_ERROR; restore_queue_signals(q); fflush(stdout); if (save[1] == -2) { diff --git a/Src/glob.c b/Src/glob.c index 94b3f620d..69de15544 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -1230,7 +1230,7 @@ zglob(LinkList list, LinkNode np, int nountok) char *s; int sense, qualsfound; off_t data; - char *sdata, *newcolonmod; + char *sdata, *newcolonmod, *ptr; int (*func) _((char *, Statptr, off_t, char *)); /* @@ -1273,6 +1273,9 @@ zglob(LinkList list, LinkNode np, int nountok) *s++ = 0; if (qualsfound == 2) s += 2; + for (ptr = s; *ptr; ptr++) + if (*ptr == Dash) + *ptr = '-'; while (*s && !newcolonmod) { func = (int (*) _((char *, Statptr, off_t, char *)))0; if (idigit(*s)) { @@ -3512,6 +3515,7 @@ zshtokenize(char *s, int flags) case ')': if (flags & ZSHTOK_SHGLOB) break; + /*FALLTHROUGH*/ case '>': case '^': case '#': @@ -3521,7 +3525,9 @@ zshtokenize(char *s, int flags) case '*': case '?': case '=': - for (t = ztokens; *t; t++) + case '-': + case '!': + for (t = ztokens; *t; t++) { if (*t == *s) { if (bslash) s[-1] = (flags & ZSHTOK_SUBST) ? Bnullkeep : Bnull; @@ -3529,6 +3535,8 @@ zshtokenize(char *s, int flags) *s = (t - ztokens) + Pound; break; } + } + break; } bslash = 0; } @@ -3802,13 +3810,16 @@ qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str) if ((prog = parse_string(str, 0))) { int ef = errflag, lv = lastval, ret; + int cshglob = badcshglob; unsetparam("reply"); setsparam("REPLY", ztrdup(name)); + badcshglob = 0; execode(prog, 1, 0, "globqual"); - ret = lastval; + if ((ret = lastval)) + badcshglob |= cshglob; /* Retain any user interrupt error status */ errflag = ef | (errflag & ERRFLAG_INT); lastval = lv; diff --git a/Src/hashtable.c b/Src/hashtable.c index 2d1ff87cb..0664c3694 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -1276,6 +1276,15 @@ printaliasnode(HashNode hn, int printflags) } if (printflags & PRINT_LIST) { + /* Fast fail on unrepresentable values. */ + if (strchr(a->node.nam, '=')) { + zwarn("invalid alias '%s' encountered while printing aliases", + a->node.nam); + /* ### TODO: Return an error status to the C caller */ + return; + } + + /* Normal path. */ printf("alias "); if (a->node.flags & ALIAS_SUFFIX) printf("-s "); diff --git a/Src/hashtable.h b/Src/hashtable.h index b6346bb9a..3606e9785 100644 --- a/Src/hashtable.h +++ b/Src/hashtable.h @@ -53,7 +53,7 @@ #define BIN_LOGOUT 19 #define BIN_TEST 20 #define BIN_BRACKET 21 -#define BIN_EXPORT 22 +#define BIN_READONLY 22 #define BIN_ECHO 23 #define BIN_DISABLE 24 #define BIN_ENABLE 25 diff --git a/Src/init.c b/Src/init.c index dcce1d7ce..4097327ee 100644 --- a/Src/init.c +++ b/Src/init.c @@ -240,13 +240,11 @@ loop(int toplevel, int justonce) return LOOP_OK; } -/* Shared among parseargs(), parseopts(), init_io(), and init_misc() */ -static char *cmd; static int restricted; /**/ static void -parseargs(char **argv, char **runscript) +parseargs(char **argv, char **runscript, char **cmdptr) { char **x; LinkList paramlist; @@ -272,7 +270,7 @@ parseargs(char **argv, char **runscript) opts[SHINSTDIN] = 0; opts[SINGLECOMMAND] = 0; - if (parseopts(NULL, &argv, opts, &cmd, NULL)) + if (parseopts(NULL, &argv, opts, cmdptr, NULL)) exit(1); /* @@ -290,7 +288,7 @@ parseargs(char **argv, char **runscript) if (*argv) { if (unset(SHINSTDIN)) { posixzero = *argv; - if (cmd) + if (*cmdptr) argzero = *argv; else *runscript = *argv; @@ -299,7 +297,7 @@ parseargs(char **argv, char **runscript) } while (*argv) zaddlinknode(paramlist, ztrdup(*argv++)); - } else if (!cmd) + } else if (!*cmdptr) opts[SHINSTDIN] = 1; if(isset(SINGLECOMMAND)) opts[INTERACTIVE] &= 1; @@ -497,7 +495,7 @@ printhelp(void) /**/ mod_export void -init_io(void) +init_io(char *cmd) { static char outbuf[BUFSIZ], errbuf[BUFSIZ]; @@ -521,6 +519,8 @@ init_io(void) for (i = 3; i < 10; i++) close(i); } +#else + (void)cmd; #endif if (shout) { @@ -802,7 +802,7 @@ init_term(void) /**/ void -setupvals(void) +setupvals(char *cmd) { #ifdef USE_GETPWUID struct passwd *pswd; @@ -1086,6 +1086,9 @@ setupvals(void) /* Colour sequences for outputting colours in prompts and zle */ set_default_colour_sequences(); + + if (cmd) + setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd)); } /* @@ -1267,7 +1270,7 @@ run_init_scripts(void) /**/ void -init_misc(void) +init_misc(char *cmd) { #ifndef RESTRICTED_R if ( restricted ) @@ -1604,6 +1607,7 @@ mod_export int zsh_main(UNUSED(int argc), char **argv) { char **t, *runscript = NULL; + char *cmd; /* argument to -c */ int t0; #ifdef USE_LOCALE setlocale(LC_ALL, ""); @@ -1652,18 +1656,18 @@ zsh_main(UNUSED(int argc), char **argv) opts[LOGINSHELL] = (**argv == '-'); opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid()); /* sets ZLE, INTERACTIVE, SHINSTDIN and SINGLECOMMAND */ - parseargs(argv, &runscript); + parseargs(argv, &runscript, &cmd); SHTTY = -1; - init_io(); - setupvals(); + init_io(cmd); + setupvals(cmd); init_signals(); init_bltinmods(); init_builtins(); run_init_scripts(); setupshin(runscript); - init_misc(); + init_misc(cmd); for (;;) { /* @@ -35,7 +35,7 @@ /* tokens */ /**/ -mod_export char ztokens[] = "#$^*(())$=|{}[]`<>>?~`,'\"\\\\"; +mod_export char ztokens[] = "#$^*(())$=|{}[]`<>>?~`,-!'\"\\\\"; /* parts of the current token */ @@ -394,8 +394,10 @@ ctxtlex(void) #define LX2_DQUOTE 15 #define LX2_BQUOTE 16 #define LX2_COMMA 17 -#define LX2_OTHER 18 -#define LX2_META 19 +#define LX2_DASH 18 +#define LX2_BANG 19 +#define LX2_OTHER 20 +#define LX2_META 21 static unsigned char lexact1[256], lexact2[256], lextok2[256]; @@ -405,10 +407,10 @@ initlextabs(void) { int t0; static char *lx1 = "\\q\n;!&|(){}[]<>"; - static char *lx2 = ";)|$[]~({}><=\\\'\"`,"; + static char *lx2 = ";)|$[]~({}><=\\\'\"`,-!"; for (t0 = 0; t0 != 256; t0++) { - lexact1[t0] = LX1_OTHER; + lexact1[t0] = LX1_OTHER; lexact2[t0] = LX2_OTHER; lextok2[t0] = t0; } @@ -801,7 +803,7 @@ gettok(void) return INOUTPAR; hungetc(d); lexstop = 0; - if (!(incond == 1 || incmdpos)) + if (!(isset(SHGLOB) || incond == 1 || incmdpos)) break; return INPAR; case LX1_OUTPAR: @@ -919,7 +921,7 @@ gettok(void) static enum lextok gettokstr(int c, int sub) { - int bct = 0, pct = 0, brct = 0, fdpar = 0; + int bct = 0, pct = 0, brct = 0, seen_brct = 0, fdpar = 0; int intpos = 1, in_brace_param = 0; int inquote, unmatched = 0; enum lextok peek; @@ -1033,8 +1035,10 @@ gettokstr(int c, int sub) } break; case LX2_INBRACK: - if (!in_brace_param) + if (!in_brace_param) { brct++; + seen_brct = 1; + } c = Inbrack; break; case LX2_OUTBRACK: @@ -1346,9 +1350,32 @@ gettokstr(int c, int sub) c = Tick; SETPAREND break; - } - add(c); - c = hgetc(); + case LX2_DASH: + /* + * - shouldn't be treated as a special character unless + * we're in a pattern. Howeve,simply counting "[" doesn't + * work as []a-z] is a valid expression and we don't know + * down here what this "[" is for as $foo[stuff] is valid + * in zsh. So just detect an opening [, which is enough + * to turn this into a pattern; the Dash will be harmlessly + * untokenised if not wanted. + */ + if (seen_brct) + c = Dash; + else + c = '-'; + break; + case LX2_BANG: + /* + * Same logic as Dash, for ! to perform negation in range. + */ + if (seen_brct) + c = Bang; + else + c = '!'; + } + add(c); + c = hgetc(); if (intpos) intpos--; if (lexstop) diff --git a/Src/options.c b/Src/options.c index 2678626c7..17c46c311 100644 --- a/Src/options.c +++ b/Src/options.c @@ -256,7 +256,7 @@ static struct optname optns[] = { {{NULL, "unset", OPT_EMULATE|OPT_BSHELL}, UNSET}, {{NULL, "verbose", 0}, VERBOSE}, {{NULL, "vi", 0}, VIMODE}, -{{NULL, "warncreateglobal", 0}, WARNCREATEGLOBAL}, +{{NULL, "warncreateglobal", OPT_EMULATE}, WARNCREATEGLOBAL}, {{NULL, "xtrace", 0}, XTRACE}, {{NULL, "zle", OPT_SPECIAL}, USEZLE}, {{NULL, "braceexpand", OPT_ALIAS}, /* ksh/bash */ -IGNOREBRACES}, diff --git a/Src/params.c b/Src/params.c index d8bf83d0e..b2e889738 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2552,18 +2552,20 @@ setarrvalue(Value v, char **val) v->pm->node.nam); return; } else { - char **old, **new, **p, **q, **r; - int pre_assignment_length; + char **const old = v->pm->gsu.a->getfn(v->pm); + char **new; + char **p, **q, **r; /* index variables */ + const int pre_assignment_length = arrlen(old); int post_assignment_length; int i; + q = old; + if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS)) { if (v->start > 0) v->start--; v->end--; } - q = old = v->pm->gsu.a->getfn(v->pm); - pre_assignment_length = arrlen(old); if (v->start < 0) { v->start += pre_assignment_length; if (v->start < 0) @@ -2703,30 +2705,18 @@ static void check_warn_create(Param pm, const char *pmtype) { Funcstack i; - const char *name; if (pm->level != 0 || (pm->node.flags & PM_SPECIAL)) return; - name = NULL; for (i = funcstack; i; i = i->prev) { if (i->tp == FS_FUNC) { DPUTS(!i->name, "funcstack entry with no name"); - name = i->name; + zwarn("%s parameter %s created globally in function %s", + pmtype, pm->node.nam, i->name); break; } } - - if (name) - { - zwarn("%s parameter %s created globally in function %s", - pmtype, pm->node.nam, name); - } - else - { - zwarn("%s parameter %s created globally in function", - pmtype, pm->node.nam); - } } /**/ @@ -2866,7 +2856,7 @@ mod_export Param setsparam(char *s, char *val) { return assignsparam( - s, val, isset(WARNCREATEGLOBAL) && locallevel > 0 ? + s, val, isset(WARNCREATEGLOBAL) && locallevel > forklevel ? ASSPM_WARN_CREATE : 0); } @@ -2964,7 +2954,7 @@ mod_export Param setaparam(char *s, char **aval) { return assignaparam( - s, aval, isset(WARNCREATEGLOBAL) && locallevel >0 ? + s, aval, isset(WARNCREATEGLOBAL) && locallevel > forklevel ? ASSPM_WARN_CREATE : 0); } @@ -2995,7 +2985,7 @@ sethparam(char *s, char **val) if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) { DPUTS(!v, "BUG: assigning to undeclared associative array"); createparam(t, PM_HASHED); - checkcreate = isset(WARNCREATEGLOBAL) && locallevel > 0; + checkcreate = isset(WARNCREATEGLOBAL) && locallevel > forklevel; } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) && !(v->pm->node.flags & PM_SPECIAL)) { unsetparam(t); @@ -3059,6 +3049,7 @@ setnparam(char *s, mnumber val) if (ss) *ss = '\0'; pm = createparam(t, ss ? PM_ARRAY : + isset(POSIXIDENTIFIERS) ? PM_SCALAR : (val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT); if (!pm) pm = (Param) paramtab->getnode(paramtab, t); @@ -3073,7 +3064,7 @@ setnparam(char *s, mnumber val) /* errflag |= ERRFLAG_ERROR; */ return NULL; } - if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > 0) + if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > forklevel) check_warn_create(v->pm, "numeric"); } setnumvalue(v, val); @@ -3111,7 +3102,8 @@ setiparam_no_convert(char *s, zlong val) convbase(buf, val, 10); return assignsparam( s, ztrdup(buf), - isset(WARNCREATEGLOBAL) && locallevel > 0 ? ASSPM_WARN_CREATE : 0); + isset(WARNCREATEGLOBAL) && locallevel > forklevel ? + ASSPM_WARN_CREATE : 0); } /* Unset a parameter */ @@ -5179,9 +5171,6 @@ printparamvalue(Param p, int printflags) } if (printflags & PRINT_KV_PAIR) putchar(' '); - else if ((printflags & PRINT_TYPESET) && - (PM_TYPE(p->node.flags) == PM_ARRAY || PM_TYPE(p->node.flags) == PM_HASHED)) - printf("%s=", p->node.nam); else putchar('='); @@ -5252,7 +5241,6 @@ mod_export void printparamnode(HashNode hn, int printflags) { Param p = (Param) hn; - int array_typeset; if (p->node.flags & PM_UNSET) { if (isset(POSIXBUILTINS) && (p->node.flags & PM_READONLY) && @@ -5277,28 +5265,8 @@ printparamnode(HashNode hn, int printflags) */ return; } - /* - * Printing the value of array: this needs to be on - * a separate line so more care is required. - */ - array_typeset = (PM_TYPE(p->node.flags) == PM_ARRAY || - PM_TYPE(p->node.flags) == PM_HASHED) && - !(printflags & PRINT_NAMEONLY); - if (array_typeset && (p->node.flags & PM_READONLY)) { - /* - * We need to create the array before making it - * readonly. - */ - printf("typeset -a "); - zputs(p->node.nam, stdout); - putchar('\n'); - printparamvalue(p, printflags); - printflags |= PRINT_NAMEONLY; - } printf("typeset "); } - else - array_typeset = 0; /* Print the attributes of the parameter */ if (printflags & (PRINT_TYPE|PRINT_TYPESET)) { @@ -5346,8 +5314,6 @@ printparamnode(HashNode hn, int printflags) } else { quotedzputs(p->node.nam, stdout); - if (array_typeset) - putchar('\n'); printparamvalue(p, printflags); } } diff --git a/Src/parse.c b/Src/parse.c index 83ba396b0..4829e3a6d 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -2252,6 +2252,8 @@ void (*condlex) _((void)) = zshlex; * cond : cond_1 { SEPER } [ DBAR { SEPER } cond ] */ +#define COND_SEP() (tok == SEPER && condlex != testlex && *zshlextext != ';') + /**/ static int par_cond(void) @@ -2259,11 +2261,11 @@ par_cond(void) int p = ecused, r; r = par_cond_1(); - while (tok == SEPER) + while (COND_SEP()) condlex(); if (tok == DBAR) { condlex(); - while (tok == SEPER) + while (COND_SEP()) condlex(); ecispace(p, 1); par_cond(); @@ -2284,11 +2286,11 @@ par_cond_1(void) int r, p = ecused; r = par_cond_2(); - while (tok == SEPER) + while (COND_SEP()) condlex(); if (tok == DAMPER) { condlex(); - while (tok == SEPER) + while (COND_SEP()) condlex(); ecispace(p, 1); par_cond_1(); @@ -2348,7 +2350,9 @@ par_cond_2(void) * We fall through here on any non-numeric infix operator * or any other time there are at least two arguments. */ - } + } else + while (COND_SEP()) + condlex(); if (tok == BANG) { /* * In "test" compatibility mode, "! -a ..." and "! -o ..." @@ -2366,10 +2370,10 @@ par_cond_2(void) int r; condlex(); - while (tok == SEPER) + while (COND_SEP()) condlex(); r = par_cond(); - while (tok == SEPER) + while (COND_SEP()) condlex(); if (tok != OUTPAR) YYERROR(ecused); @@ -2385,7 +2389,7 @@ par_cond_2(void) /* Check first argument for [[ STRING ]] re-interpretation */ if (s1 /* tok != DOUTBRACK && tok != DAMPER && tok != DBAR */ && tok != LEXERR && (!dble || n_testargs)) { - condlex(); + do condlex(); while (COND_SEP()); return par_cond_double(dupstring("-n"), s1); } else YYERROR(ecused); @@ -2398,14 +2402,16 @@ par_cond_2(void) * checked it does have a string representation). */ tok = STRING; - } + } else + while (COND_SEP()) + condlex(); if (tok == INANG || tok == OUTANG) { enum lextok xtok = tok; - condlex(); + do condlex(); while (COND_SEP()); if (tok != STRING) YYERROR(ecused); s3 = tokstr; - condlex(); + do condlex(); while (COND_SEP()); ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0)); ecstr(s1); ecstr(s3); @@ -2428,11 +2434,11 @@ par_cond_2(void) if (!n_testargs) dble = (s2 && *s2 == '-' && !s2[2]); incond++; /* parentheses do globbing */ - condlex(); + do condlex(); while (COND_SEP()); incond--; /* parentheses do grouping */ if (tok == STRING && !dble) { s3 = tokstr; - condlex(); + do condlex(); while (COND_SEP()); if (tok == STRING) { LinkList l = newlinklist(); @@ -2441,7 +2447,7 @@ par_cond_2(void) while (tok == STRING) { addlinknode(l, tokstr); - condlex(); + do condlex(); while (COND_SEP()); } return par_cond_multi(s1, l); } else diff --git a/Src/pattern.c b/Src/pattern.c index 9e8a80ae1..72c7d97d5 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -247,7 +247,7 @@ typedef unsigned long zrange_t; */ static const char zpc_chars[ZPC_COUNT] = { '/', '\0', Bar, Outpar, Tilde, Inpar, Quest, Star, Inbrack, Inang, - Hat, Pound, Bnullkeep, Quest, Star, '+', '!', '@' + Hat, Pound, Bnullkeep, Quest, Star, '+', Bang, '!', '@' }; /* @@ -257,7 +257,7 @@ static const char zpc_chars[ZPC_COUNT] = { /**/ mod_export const char *zpc_strings[ZPC_COUNT] = { NULL, NULL, "|", NULL, "~", "(", "?", "*", "[", "<", - "^", "#", NULL, "?(", "*(", "+(", "!(", "@(" + "^", "#", NULL, "?(", "*(", "+(", "!(", "\\!(", "@(" }; /* @@ -481,7 +481,7 @@ patcompcharsset(void) */ zpc_special[ZPC_KSH_QUEST] = zpc_special[ZPC_KSH_STAR] = zpc_special[ZPC_KSH_PLUS] = zpc_special[ZPC_KSH_BANG] = - zpc_special[ZPC_KSH_AT] = Marker; + zpc_special[ZPC_KSH_BANG2] = zpc_special[ZPC_KSH_AT] = Marker; } /* * Note that if we are using KSHGLOB, then we test for a following @@ -1268,6 +1268,8 @@ patcomppiece(int *flagp, int paren) kshchar = STOUC('+'); else if (*patparse == zpc_special[ZPC_KSH_BANG]) kshchar = STOUC('!'); + else if (*patparse == zpc_special[ZPC_KSH_BANG2]) + kshchar = STOUC('!'); else if (*patparse == zpc_special[ZPC_KSH_AT]) kshchar = STOUC('@'); else if (*patparse == zpc_special[ZPC_KSH_STAR]) @@ -1424,7 +1426,7 @@ patcomppiece(int *flagp, int paren) DPUTS(zpc_special[ZPC_INBRACK] == Marker, "Treating '[' as pattern character although disabled"); flags |= P_SIMPLE; - if (*patparse == Hat || *patparse == '^' || *patparse == '!') { + if (*patparse == Hat || *patparse == Bang) { patparse++; starter = patnode(P_ANYBUT); } else @@ -1459,7 +1461,7 @@ patcomppiece(int *flagp, int paren) charstart = patparse; METACHARINC(patparse); - if (*patparse == '-' && patparse[1] && + if (*patparse == Dash && patparse[1] && patparse[1] != Outbrack) { patadd(NULL, STOUC(Meta)+PP_RANGE, 1, PA_NOALIGN); if (itok(*charstart)) { @@ -1468,7 +1470,7 @@ patcomppiece(int *flagp, int paren) } else { patadd(charstart, 0, patparse-charstart, PA_NOALIGN); } - charstart = ++patparse; /* skip ASCII '-' */ + charstart = ++patparse; /* skip Dash token */ METACHARINC(patparse); } if (itok(*charstart)) { @@ -4245,7 +4247,8 @@ haswilds(char *str) ((str[-1] == Quest && !zpc_disables[ZPC_KSH_QUEST]) || (str[-1] == Star && !zpc_disables[ZPC_KSH_STAR]) || (str[-1] == '+' && !zpc_disables[ZPC_KSH_PLUS]) || - (str[-1] == '!' && !zpc_disables[ZPC_KSH_BANG]) || + (str[-1] == Bang && !zpc_disables[ZPC_KSH_BANG]) || + (str[-1] == '!' && !zpc_disables[ZPC_KSH_BANG2]) || (str[-1] == '@' && !zpc_disables[ZPC_KSH_AT])))) return 1; break; diff --git a/Src/subst.c b/Src/subst.c index d9c9d24aa..bb1dd8939 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1887,12 +1887,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, if (quotetype == QT_DOLLARS || quotetype == QT_BACKSLASH_PATTERN) goto flagerr; - if (s[1] == '-') { + if (s[1] == '-' || s[1] == '+') { if (quotemod) goto flagerr; s++; quotemod = 1; - quotetype = QT_SINGLE_OPTIONAL; + quotetype = (*s == '-') ? QT_SINGLE_OPTIONAL : + QT_QUOTEDZPUTS; } else { if (quotetype == QT_SINGLE_OPTIONAL) { /* extra q's after '-' not allowed */ @@ -3583,7 +3584,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, ap = aval; if (quotemod > 0) { - if (quotetype > QT_BACKSLASH) { + if (quotetype == QT_QUOTEDZPUTS) { + for (; *ap; ap++) + *ap = quotedzputs(*ap, NULL); + } else if (quotetype > QT_BACKSLASH) { int sl; char *tmp; @@ -3626,7 +3630,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, if (!copied) val = dupstring(val), copied = 1; if (quotemod > 0) { - if (quotetype > QT_BACKSLASH) { + if (quotetype == QT_QUOTEDZPUTS) { + val = quotedzputs(val, NULL); + } else if (quotetype > QT_BACKSLASH) { int sl; char *tmp; tmp = quotestring(val, NULL, quotetype); diff --git a/Src/text.c b/Src/text.c index 9421d70ce..04acd2aac 100644 --- a/Src/text.c +++ b/Src/text.c @@ -40,9 +40,32 @@ /**/ int text_expand_tabs; +/* + * Binary operators in conditions. + * There order is tied to the order of the definitions COND_STREQ + * et seq. in zsh.h. + */ +static const char *cond_binary_ops[] = { + "=", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", + "-ne", "-lt", "-gt", "-le", "-ge", "=~" +}; + static char *tptr, *tbuf, *tlim, *tpending; static int tsiz, tindent, tnewlins, tjob; +/**/ +int +is_cond_binary_op(const char *str) +{ + const char **op; + for (op = cond_binary_ops; *op; op++) + { + if (!strcmp(str, *op)) + return 1; + } + return 0; +} + static void dec_tindent(void) { @@ -120,7 +143,7 @@ taddchr(int c) /**/ static void -taddstr(char *s) +taddstr(const char *s) { int sl = strlen(s); char c; @@ -822,11 +845,6 @@ gettext2(Estate state) break; case WC_COND: { - static char *c1[] = { - "=", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", - "-ne", "-lt", "-gt", "-le", "-ge", "=~" - }; - int ctype; if (!s) { @@ -912,7 +930,7 @@ gettext2(Estate state) /* Binary test: `a = b' etc. */ taddstr(ecgetstr(state, EC_NODUP, NULL)); taddstr(" "); - taddstr(c1[ctype - COND_STREQ]); + taddstr(cond_binary_ops[ctype - COND_STREQ]); taddstr(" "); taddstr(ecgetstr(state, EC_NODUP, NULL)); if (ctype == COND_STREQ || diff --git a/Src/utils.c b/Src/utils.c index 464097034..fd0bab320 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -387,6 +387,7 @@ putshout(int c) return 0; } +#ifdef MULTIBYTE_SUPPORT /* * Turn a character into a visible representation thereof. The visible * string is put together in a static buffer, and this function returns @@ -409,9 +410,76 @@ putshout(int c) /**/ mod_export char * +nicechar_sel(int c, int quotable) +{ + static char buf[10]; + char *s = buf; + c &= 0xff; + if (ZISPRINT(c)) + goto done; + if (c & 0x80) { + if (isset(PRINTEIGHTBIT)) + goto done; + *s++ = '\\'; + *s++ = 'M'; + *s++ = '-'; + c &= 0x7f; + if(ZISPRINT(c)) + goto done; + } + if (c == 0x7f) { + if (quotable) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + } else + *s++ = '^'; + c = '?'; + } else if (c == '\n') { + *s++ = '\\'; + c = 'n'; + } else if (c == '\t') { + *s++ = '\\'; + c = 't'; + } else if (c < 0x20) { + if (quotable) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + } else + *s++ = '^'; + c += 0x40; + } + done: + /* + * The resulting string is still metafied, so check if + * we are returning a character in the range that needs metafication. + * This can't happen if the character is printed "nicely", so + * this results in a maximum of two bytes total (plus the null). + */ + if (imeta(c)) { + *s++ = Meta; + *s++ = c ^ 32; + } else + *s++ = c; + *s = 0; + return buf; +} + +/**/ +mod_export char * nicechar(int c) { - static char buf[6]; + return nicechar_sel(c, 0); +} + +#else /* MULTIBYTE_SUPPORT */ + +/**/ +mod_export char * +nicechar(int c) +{ + static char buf[10]; char *s = buf; c &= 0xff; if (ZISPRINT(c)) @@ -427,7 +495,9 @@ nicechar(int c) goto done; } if (c == 0x7f) { - *s++ = '^'; + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; c = '?'; } else if (c == '\n') { *s++ = '\\'; @@ -436,7 +506,9 @@ nicechar(int c) *s++ = '\\'; c = 't'; } else if (c < 0x20) { - *s++ = '^'; + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; c += 0x40; } done: @@ -455,6 +527,24 @@ nicechar(int c) return buf; } +#endif /* MULTIBYTE_SUPPORT */ + +/* + * Return 1 if nicechar() would reformat this character. + */ + +/**/ +mod_export int +is_nicechar(int c) +{ + c &= 0xff; + if (ZISPRINT(c)) + return 0; + if (c & 0x80) + return !isset(PRINTEIGHTBIT); + return (c == 0x7f || c == '\n' || c == '\t' || c < 0x20); +} + /**/ #ifdef MULTIBYTE_SUPPORT static mbstate_t mb_shiftstate; @@ -507,7 +597,7 @@ mb_charinit(void) /**/ mod_export char * -wcs_nicechar(wchar_t c, size_t *widthp, char **swidep) +wcs_nicechar_sel(wchar_t c, size_t *widthp, char **swidep, int quotable) { static char *buf; static int bufalloc = 0, newalloc; @@ -532,7 +622,12 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep) s = buf; if (!iswprint(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) { if (c == 0x7f) { - *s++ = '^'; + if (quotable) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + } else + *s++ = '^'; c = '?'; } else if (c == L'\n') { *s++ = '\\'; @@ -541,7 +636,12 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep) *s++ = '\\'; c = 't'; } else if (c < 0x20) { - *s++ = '^'; + if (quotable) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + } else + *s++ = '^'; c += 0x40; } else if (c >= 0x80) { ret = -1; @@ -612,6 +712,30 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep) } /**/ +mod_export char * +wcs_nicechar(wchar_t c, size_t *widthp, char **swidep) +{ + return wcs_nicechar_sel(c, widthp, swidep, 0); +} + +/* + * Return 1 if wcs_nicechar() would reformat this character for display. + */ + +/**/ +mod_export int is_wcs_nicechar(wchar_t c) +{ + if (!iswprint(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) { + if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20) + return 1; + if (c >= 0x80) { + return (c >= 0x100); + } + } + return 0; +} + +/**/ mod_export int zwcwidth(wint_t wc) { @@ -1326,6 +1450,9 @@ time_t lastwatch; * If "retval" is not NULL, the return value of the first hook function to * return non-zero is stored in *"retval". The return value is not otherwise * available as the calling context is restored. + * + * Returns 0 if at least one function was called (regardless of that function's + * exit status), and 1 otherwise. */ /**/ @@ -2861,11 +2988,12 @@ spckword(char **s, int hist, int cmd, int ask) if (strncmp(guess, best, preflen)) return; /* replace the temporarily expanded prefix with the original */ - u = (char *) hcalloc(t - *s + strlen(best + preflen) + 1); + u = (char *) zhalloc(t - *s + strlen(best + preflen) + 1); strncpy(u, *s, t - *s); strcpy(u + (t - *s), best + preflen); } else { - u = (char *) hcalloc(strlen(best) + 2); + u = (char *) zhalloc(strlen(best) + 2); + *u = '\0'; strcpy(u + 1, best); } best = u; @@ -3204,7 +3332,7 @@ zjoin(char **arr, int delim, int heap) len += strlen(*s) + 1 + (imeta(delim) ? 1 : 0); if (!len) return heap? "" : ztrdup(""); - ptr = ret = (heap ? (char *) hcalloc(len) : (char *) zshcalloc(len)); + ptr = ret = (char *) (heap ? zhalloc(len) : zalloc(len)); for (s = arr; *s; s++) { strucpy(&ptr, *s); if (imeta(delim)) { @@ -3290,7 +3418,8 @@ spacesplit(char *s, int allownull, int heap, int quote) int l = sizeof(*ret) * (wordcount(s, NULL, -!allownull) + 1); char *(*dup)(const char *) = (heap ? dupstring : ztrdup); - ptr = ret = (heap ? (char **) hcalloc(l) : (char **) zshcalloc(l)); + /* ### TODO: s/calloc/alloc/ */ + ptr = ret = (char **) (heap ? hcalloc(l) : zshcalloc(l)); if (quote) { /* @@ -3320,8 +3449,8 @@ spacesplit(char *s, int allownull, int heap, int quote) t = s; (void)findsep(&s, NULL, quote); if (s > t || allownull) { - *ptr = (heap ? (char *) hcalloc((s - t) + 1) : - (char *) zshcalloc((s - t) + 1)); + *ptr = (char *) (heap ? zhalloc((s - t) + 1) : + zalloc((s - t) + 1)); ztrncpy(*ptr++, t, s - t); } else *ptr++ = dup(nulstring); @@ -3511,7 +3640,7 @@ sepjoin(char **s, char *sep, int heap) } sl = strlen(sep); for (t = s, l = 1 - sl; *t; l += strlen(*t) + sl, t++); - r = p = (heap ? (char *) hcalloc(l) : (char *) zshcalloc(l)); + r = p = (char *) (heap ? zhalloc(l) : zalloc(l)); t = s; while (*t) { strucpy(&p, *t); @@ -3538,14 +3667,14 @@ sepsplit(char *s, char *sep, int allownull, int heap) sl = strlen(sep); n = wordcount(s, sep, 1); - r = p = (heap ? (char **) hcalloc((n + 1) * sizeof(char *)) : - (char **) zshcalloc((n + 1) * sizeof(char *))); + r = p = (char **) (heap ? zhalloc((n + 1) * sizeof(char *)) : + zalloc((n + 1) * sizeof(char *))); for (t = s; n--;) { tt = t; (void)findsep(&t, sep, 0); - *p = (heap ? (char *) hcalloc(t - tt + 1) : - (char *) zshcalloc(t - tt + 1)); + *p = (char *) (heap ? zhalloc(t - tt + 1) : + zalloc(t - tt + 1)); strncpy(*p, tt, t - tt); (*p)[t - tt] = '\0'; p++; @@ -3759,7 +3888,7 @@ inittyptab(void) typtab['\0'] |= IMETA; typtab[STOUC(Meta) ] |= IMETA; typtab[STOUC(Marker)] |= IMETA; - for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Comma); t0++) + for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(LAST_NORMAL_TOK); t0++) typtab[t0] |= ITOK | IMETA; for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++) typtab[t0] |= ITOK | IMETA | INULL; @@ -4832,12 +4961,15 @@ niceztrlen(char const *s) * If outstrp is not NULL, set *outstrp to a zalloc'd version of * the output (still metafied). * - * If "heap" is non-zero, use the heap for *outstrp, else zalloc. + * If flags contains NICEFLAG_HEAP, use the heap for *outstrp, else + * zalloc. + * If flags contsins NICEFLAG_QUOTE, the output is going to be within + * $'...', so quote "'" with a backslash. */ /**/ mod_export size_t -mb_niceformat(const char *s, FILE *stream, char **outstrp, int heap) +mb_niceformat(const char *s, FILE *stream, char **outstrp, int flags) { size_t l = 0, newl; int umlen, outalloc, outleft, eol = 0; @@ -4872,7 +5004,7 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int heap) /* FALL THROUGH */ case MB_INVALID: /* The byte didn't convert, so output it as a \M-... sequence. */ - fmt = nicechar(*ptr); + fmt = nicechar_sel(*ptr, flags & NICEFLAG_QUOTE); newl = strlen(fmt); cnt = 1; /* Get mbs out of its undefined state. */ @@ -4884,7 +5016,10 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int heap) cnt = 1; /* FALL THROUGH */ default: - fmt = wcs_nicechar(c, &newl, NULL); + if (c == L'\'' && (flags & NICEFLAG_QUOTE)) + fmt = "\\'"; + else + fmt = wcs_nicechar_sel(c, &newl, NULL, flags & NICEFLAG_QUOTE); break; } @@ -4918,13 +5053,76 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int heap) if (outstrp) { *outptr = '\0'; /* Use more efficient storage for returned string */ - *outstrp = heap ? dupstring(outstr) : ztrdup(outstr); - free(outstr); + if (flags & NICEFLAG_NODUP) + *outstrp = outstr; + else { + *outstrp = (flags & NICEFLAG_HEAP) ? dupstring(outstr) : + ztrdup(outstr); + free(outstr); + } } return l; } +/* + * Return 1 if mb_niceformat() would reformat this string, else 0. + */ + +/**/ +mod_export int +is_mb_niceformat(const char *s) +{ + int umlen, eol = 0, ret = 0; + wchar_t c; + char *ums, *ptr; + mbstate_t mbs; + + ums = ztrdup(s); + untokenize(ums); + ptr = unmetafy(ums, ¨en); + + memset(&mbs, 0, sizeof mbs); + while (umlen > 0) { + size_t cnt = eol ? MB_INVALID : mbrtowc(&c, ptr, umlen, &mbs); + + switch (cnt) { + case MB_INCOMPLETE: + eol = 1; + /* FALL THROUGH */ + case MB_INVALID: + /* The byte didn't convert, so output it as a \M-... sequence. */ + if (is_nicechar(*ptr)) { + ret = 1; + break; + } + cnt = 1; + /* Get mbs out of its undefined state. */ + memset(&mbs, 0, sizeof mbs); + break; + case 0: + /* Careful: converting '\0' returns 0, but a '\0' is a + * real character for us, so we should consume 1 byte. */ + cnt = 1; + /* FALL THROUGH */ + default: + if (is_wcs_nicechar(c)) + ret = 1; + break; + } + + if (ret) + break; + + umlen -= cnt; + ptr += cnt; + } + + free(ums); + + return ret; +} + /* ztrdup multibyte string with nice formatting */ /**/ @@ -4933,7 +5131,7 @@ nicedup(const char *s, int heap) { char *retstr; - (void)mb_niceformat(s, NULL, &retstr, heap); + (void)mb_niceformat(s, NULL, &retstr, heap ? NICEFLAG_HEAP : 0); return retstr; } @@ -5072,6 +5270,21 @@ mb_metastrlenend(char *ptr, int width, char *eptr) ret = mbrtowc(&wc, &inchar, 1, &mb_shiftstate); if (ret == MB_INCOMPLETE) { + /* + * "num_in_char" is only used for incomplete characters. + * The assumption is that we will output all trailing octets + * that form part of an incomplete character as a single + * character (of single width) if we don't get a complete + * character. This is purely pragmatic --- I'm not aware + * of a standard way of dealing with incomplete characters. + * + * If we do get a complete character, num_in_char + * becomes irrelevant and is set to zero + * + * This is in contrast to "num" which counts the characters + * or widths in complete characters. The two are summed, + * so we don't count characters twice. + */ num_in_char++; } else { if (ret == MB_INVALID) { @@ -5098,8 +5311,8 @@ mb_metastrlenend(char *ptr, int width, char *eptr) } } - /* If incomplete, treat remainder as trailing single bytes */ - return num + num_in_char; + /* If incomplete, treat remainder as trailing single character */ + return num + (num_in_char ? 1 : 0); } /* @@ -5712,25 +5925,76 @@ quotestring(const char *s, char **e, int instring) return v; } -/* Unmetafy and output a string, quoted if it contains special characters. */ +/* + * Unmetafy and output a string, quoted if it contains special + * characters. + * + * If stream is NULL, return the same output with any allocation on the + * heap. + */ /**/ -mod_export int +mod_export char * quotedzputs(char const *s, FILE *stream) { int inquote = 0, c; + char *outstr, *ptr; /* check for empty string */ - if(!*s) - return fputs("''", stream); + if(!*s) { + if (!stream) + return dupstring("''"); + fputs("''", stream); + return NULL; + } - if (!hasspecial(s)) - return zputs(s, stream); +#ifdef MULTIBYTE_SUPPORT + if (is_mb_niceformat(s)) { + if (stream) { + fputs("$'", stream); + mb_niceformat(s, stream, NULL, NICEFLAG_QUOTE); + fputc('\'', stream); + return NULL; + } else { + char *substr; + mb_niceformat(s, NULL, &substr, NICEFLAG_QUOTE|NICEFLAG_NODUP); + outstr = (char *)zhalloc(4 + strlen(substr)); + sprintf(outstr, "$'%s'", substr); + free(substr); + return outstr; + } + } +#endif /* MULTIBYTE_SUPPORT */ + if (!hasspecial(s)) { + if (stream) { + zputs(s, stream); + return NULL; + } else { + return dupstring(s); + } + } + + if (!stream) { + const char *cptr; + int l = strlen(s) + 2; + for (cptr = s; *cptr; cptr++) { + if (*cptr == Meta) + cptr++; + else if (*cptr == '\'') + l += isset(RCQUOTES) ? 1 : 3; + } + ptr = outstr = zhalloc(l + 1); + } else { + ptr = outstr = NULL; + } if (isset(RCQUOTES)) { /* use rc-style quotes-within-quotes for the whole string */ - if(fputc('\'', stream) < 0) - return EOF; + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; while(*s) { if (*s == Meta) c = *++s ^ 32; @@ -5738,53 +6002,98 @@ quotedzputs(char const *s, FILE *stream) c = *s; s++; if (c == '\'') { - if(fputc('\'', stream) < 0) - return EOF; - } else if(c == '\n' && isset(CSHJUNKIEQUOTES)) { - if(fputc('\\', stream) < 0) - return EOF; + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; + } else if (c == '\n' && isset(CSHJUNKIEQUOTES)) { + if (stream) { + if (fputc('\\', stream) < 0) + return NULL; + } else + *ptr++ = '\\'; + } + if (stream) { + if (fputc(c, stream) < 0) + return NULL; + } else { + if (imeta(c)) { + *ptr++ = Meta; + *ptr++ = c ^ 32; + } else + *ptr++ = c; } - if(fputc(c, stream) < 0) - return EOF; } - if(fputc('\'', stream) < 0) - return EOF; + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; } else { /* use Bourne-style quoting, avoiding empty quoted strings */ - while(*s) { + while (*s) { if (*s == Meta) c = *++s ^ 32; else c = *s; s++; if (c == '\'') { - if(inquote) { - if(fputc('\'', stream) < 0) - return EOF; + if (inquote) { + if (stream) { + if (putc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; inquote=0; } - if(fputs("\\'", stream) < 0) - return EOF; + if (stream) { + if (fputs("\\'", stream) < 0) + return NULL; + } else { + *ptr++ = '\\'; + *ptr++ = '\''; + } } else { if (!inquote) { - if(fputc('\'', stream) < 0) - return EOF; + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; inquote=1; } - if(c == '\n' && isset(CSHJUNKIEQUOTES)) { - if(fputc('\\', stream) < 0) - return EOF; + if (c == '\n' && isset(CSHJUNKIEQUOTES)) { + if (stream) { + if (fputc('\\', stream) < 0) + return NULL; + } else + *ptr++ = '\\'; + } + if (stream) { + if (fputc(c, stream) < 0) + return NULL; + } else { + if (imeta(c)) { + *ptr++ = Meta; + *ptr++ = c ^ 32; + } else + *ptr++ = c; } - if(fputc(c, stream) < 0) - return EOF; } } if (inquote) { - if(fputc('\'', stream) < 0) - return EOF; + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; } } - return 0; + if (!stream) + *ptr++ = '\0'; + + return outstr; } /* Double-quote a metafied string. */ @@ -192,24 +192,31 @@ struct mathfunc { #define Tilde ((char) 0x98) #define Qtick ((char) 0x99) #define Comma ((char) 0x9a) +#define Dash ((char) 0x9b) /* Only in patterns */ +#define Bang ((char) 0x9c) /* Only in patterns */ +/* + * Marks the last of the group above. + * Remaining tokens are even more special. + */ +#define LAST_NORMAL_TOK Bang /* * Null arguments: placeholders for single and double quotes * and backslashes. */ -#define Snull ((char) 0x9b) -#define Dnull ((char) 0x9c) -#define Bnull ((char) 0x9d) +#define Snull ((char) 0x9d) +#define Dnull ((char) 0x9e) +#define Bnull ((char) 0x9f) /* * Backslash which will be returned to "\" instead of being stripped * when we turn the string into a printable format. */ -#define Bnullkeep ((char) 0x9e) +#define Bnullkeep ((char) 0xa0) /* * Null argument that does not correspond to any character. * This should be last as it does not appear in ztokens and * is used to initialise the IMETA type in inittyptab(). */ -#define Nularg ((char) 0x9f) +#define Nularg ((char) 0xa1) /* * Take care to update the use of IMETA appropriately when adding @@ -220,7 +227,7 @@ struct mathfunc { * Also used in pattern character arrays as guaranteed not to * mark a character in a string. */ -#define Marker ((char) 0xa0) +#define Marker ((char) 0xa2) /* chars that need to be quoted if meant literally */ @@ -272,7 +279,12 @@ enum { /* * As QT_BACKSLASH, but a NULL string is shown as ''. */ - QT_BACKSLASH_SHOWNULL + QT_BACKSLASH_SHOWNULL, + /* + * Quoting as produced by quotedzputs(), used for human + * readability of parameter values. + */ + QT_QUOTEDZPUTS }; #define QT_IS_SINGLE(x) ((x) == QT_SINGLE || (x) == QT_SINGLE_OPTIONAL) @@ -1538,6 +1550,7 @@ enum zpc_chars { ZPC_KSH_STAR, /* * for *(...) in KSH_GLOB */ ZPC_KSH_PLUS, /* + for +(...) in KSH_GLOB */ ZPC_KSH_BANG, /* ! for !(...) in KSH_GLOB */ + ZPC_KSH_BANG2, /* ! for !(...) in KSH_GLOB, untokenised */ ZPC_KSH_AT, /* @ for @(...) in KSH_GLOB */ ZPC_COUNT /* Number of special chararacters */ }; @@ -3051,6 +3064,13 @@ enum { #define AFTERTRAPHOOK (zshhooks + 2) #ifdef MULTIBYTE_SUPPORT +/* Final argument to mb_niceformat() */ +enum { + NICEFLAG_HEAP = 1, /* Heap allocation where needed */ + NICEFLAG_QUOTE = 2, /* Result will appear in $'...' */ + NICEFLAG_NODUP = 4, /* Leave allocated */ +}; + /* Metafied input */ #define nicezputs(str, outs) (void)mb_niceformat((str), (outs), NULL, 0) #define MB_METACHARINIT() mb_charinit() |