diff options
Diffstat (limited to 'Src')
55 files changed, 2161 insertions, 594 deletions
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c index bcf7661f4..5d5dac6b7 100644 --- a/Src/Builtins/sched.c +++ b/Src/Builtins/sched.c @@ -220,7 +220,8 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) endstr = "-- "; else endstr = ""; - printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr, sch->cmd); + printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr, + unmeta(sch->cmd)); } return 0; } else if (!argptr[1]) { diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index 41ad2c6e4..62dbd55ea 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -765,7 +765,7 @@ zccmd_string(const char *nam, char **args) w = (ZCWin)getdata(node); #ifdef HAVE_WADDWSTR - mb_metacharinit(); + mb_charinit(); wptr = wstr = zhalloc((strlen(str)+1) * sizeof(wchar_t)); while (*str && (clen = mb_metacharlenconv(str, &wc))) { diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c index 63a04dc89..86c61cf1c 100644 --- a/Src/Modules/datetime.c +++ b/Src/Modules/datetime.c @@ -53,10 +53,12 @@ reverse_strftime(char *nam, char **argv, char *scalar, int quiet) * to use the current timezone. This is probably the best guess; * it's the one that will cause dates and times output by strftime * without the -r option and without an explicit timezone to be - * converted back correctly. + * converted back correctly. Additionally, tm_mday is set to 1 + * as that and not 0 corresponds to the first of the month. */ (void)memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; + tm.tm_mday = 1; endp = strptime(argv[1], argv[0], &tm); if (!endp) { @@ -96,7 +98,7 @@ reverse_strftime(char *nam, char **argv, char *scalar, int quiet) static int output_strftime(char *nam, char **argv, Options ops, UNUSED(int func)) { - int bufsize, x; + int bufsize, x, len; char *endptr = NULL, *scalar = NULL, *buffer; time_t secs; struct tm *t; @@ -129,16 +131,19 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func)) bufsize = strlen(argv[0]) * 8; buffer = zalloc(bufsize); + len = 0; for (x=0; x < 4; x++) { - if (ztrftime(buffer, bufsize, argv[0], t, 0L) >= 0) + if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0) break; buffer = zrealloc(buffer, bufsize *= 2); } + DPUTS(len < 0, "bad output from ztrftime"); if (scalar) { - setsparam(scalar, metafy(buffer, -1, META_DUP)); + setsparam(scalar, metafy(buffer, len, META_DUP)); } else { - printf("%s\n", buffer); + fwrite(buffer, 1, len, stdout); + putchar('\n'); } zfree(buffer, bufsize); diff --git a/Src/Modules/files.c b/Src/Modules/files.c index f86b9c1e9..dbcff6307 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -131,7 +131,7 @@ domkdir(char *nam, char *path, mode_t mode, int p) return 0; } oumask = umask(0); - err = mkdir(path, mode) ? errno : 0; + err = mkdir(rpath, mode) ? errno : 0; umask(oumask); if(!err) return 0; diff --git a/Src/Modules/newuser.c b/Src/Modules/newuser.c index 71902da7d..efdb2abba 100644 --- a/Src/Modules/newuser.c +++ b/Src/Modules/newuser.c @@ -67,7 +67,7 @@ check_dotfile(const char *dotdir, const char *fname) int boot_(UNUSED(Module m)) { - const char *dotdir = getsparam("ZDOTDIR"); + const char *dotdir = getsparam_u("ZDOTDIR"); const char *spaths[] = { #ifdef SITESCRIPT_DIR SITESCRIPT_DIR, diff --git a/Src/Modules/regex.c b/Src/Modules/regex.c index ce57de986..16cc77f30 100644 --- a/Src/Modules/regex.c +++ b/Src/Modules/regex.c @@ -115,6 +115,7 @@ zcond_regex_match(char **a, int id) } else { zlong offs; char *ptr; + int clen, leftlen; m = matches; s = metafy(lhstr + m->rm_so, m->rm_eo - m->rm_so, META_DUP); @@ -123,19 +124,25 @@ zcond_regex_match(char **a, int id) * Count the characters before the match. */ ptr = lhstr; + leftlen = m->rm_so; offs = 0; - MB_METACHARINIT(); - while (ptr < lhstr + m->rm_so) { + MB_CHARINIT(); + 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 < lhstr + m->rm_eo) { + leftlen = m->rm_eo - m->rm_so; + while (leftlen) { offs++; - ptr += MB_METACHARLEN(ptr); + clen = MB_CHARLEN(ptr, leftlen); + ptr += clen; + leftlen -= clen; } setiparam("MEND", offs + !isset(KSHARRAYS) - 1); if (nelem) { @@ -148,20 +155,31 @@ zcond_regex_match(char **a, int id) ++n, ++m, ++bptr, ++eptr) { char buf[DIGBUFSIZE]; + if (m->rm_so < 0 || m->rm_eo < 0) { + *bptr = ztrdup("-1"); + *eptr = ztrdup("-1"); + continue; + } ptr = lhstr; + leftlen = m->rm_so; offs = 0; /* Find the start offset */ - MB_METACHARINIT(); - while (ptr < lhstr + m->rm_so) { + MB_CHARINIT(); + 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 < lhstr + m->rm_eo) { + leftlen = m->rm_eo - m->rm_so; + 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/socket.c b/Src/Modules/socket.c index 6c70d3166..65b87d7dd 100644 --- a/Src/Modules/socket.c +++ b/Src/Modules/socket.c @@ -175,7 +175,7 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) tv.tv_sec = 0; tv.tv_usec = 0; - if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv))) return 1; + if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv)) == 0) return 1; else if (ret == -1) { zwarnnam(nam, "select error: %e", errno); @@ -191,8 +191,11 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) } len = sizeof(soun); - if ((rfd = accept(lfd, (struct sockaddr *)&soun, &len)) == -1) - { + do { + rfd = accept(lfd, (struct sockaddr *)&soun, &len); + } while (rfd < 0 && errno == EINTR && !errflag); + + if (rfd == -1) { zwarnnam(nam, "could not accept connection: %e", errno); return 1; } diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index 6fc53894c..396177149 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -197,8 +197,11 @@ stattimeprint(time_t tim, char *outbuf, int flags) } if (flags & STF_STRING) { char *oend = outbuf + strlen(outbuf); - ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) : - localtime(&tim), 0L); + /* Where the heck does "40" come from? */ + int len = ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) : + localtime(&tim), 0L); + if (len > 0) + metafy(oend, len, META_NOALLOC); if (flags & STF_RAW) strcat(oend, ")"); } diff --git a/Src/Modules/system.c b/Src/Modules/system.c index f6a21d160..1ab1fb17e 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -279,6 +279,182 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func)) } +static struct { char *name; int oflag; } openopts[] = { +#ifdef O_CLOEXEC + { "cloexec", O_CLOEXEC }, +#else +# ifdef FD_CLOEXEC + { "cloexec", 0 }, /* this needs to be first in the table */ +# endif +#endif +#ifdef O_NOFOLLOW + { "nofollow", O_NOFOLLOW }, +#endif +#ifdef O_SYNC + { "sync", O_SYNC }, +#endif +#ifdef O_NOATIME + { "noatime", O_NOATIME }, +#endif + { "excl", O_EXCL | O_CREAT }, + { "creat", O_CREAT }, + { "create", O_CREAT }, + { "truncate", O_TRUNC }, + { "trunc", O_TRUNC } +}; + +/**/ +static int +bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) +{ + int read = OPT_ISSET(ops, 'r'); + int write = OPT_ISSET(ops, 'w'); + int append = OPT_ISSET(ops, 'a') ? O_APPEND : 0; + int flags = O_NOCTTY | append | ((append || write) ? + (read ? O_RDWR : O_WRONLY) : O_RDONLY); + char *opt, *ptr, *nextopt, *fdvar; + int o, fd, explicit = -1; + mode_t perms = 0666; +#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) + int fdflags; +#endif + + if (!OPT_ISSET(ops, 'u')) { + zwarnnam(nam, "file descriptor not specified"); + return 1; + } + + /* file descriptor, either 0-9 or a variable name */ + fdvar = OPT_ARG(ops, 'u'); + if (idigit(*fdvar) && !fdvar[1]) { + explicit = atoi(fdvar); + } else if (!isident(fdvar)) { + zwarnnam(nam, "not an identifier: %s", fdvar); + return 1; + } + + /* open options */ + if (OPT_ISSET(ops, 'o')) { + opt = OPT_ARG(ops, 'o'); + while (opt) { + if (!strncasecmp(opt, "O_", 2)) /* ignore initial O_ */ + opt += 2; + if ((nextopt = strchr(opt, ','))) + *nextopt++ = '\0'; + for (o = sizeof(openopts)/sizeof(*openopts) - 1; o >= 0 && + strcasecmp(openopts[o].name, opt); o--) {} + if (o < 0) { + zwarnnam(nam, "unsupported option: %s\n", opt); + return 1; + } +#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) + if (!openopts[o].oflag) + fdflags = FD_CLOEXEC; +#endif + flags |= openopts[o].oflag; + opt = nextopt; + } + } + + /* -m: permissions or mode for created files */ + if (OPT_ISSET(ops, 'm')) { + ptr = opt = OPT_ARG(ops, 'm'); + while (*ptr >= '0' && *ptr <= '7') ptr++; + if (*ptr || ptr - opt < 3) { + zwarnnam(nam, "invalid mode %s", opt); + return 1; + } + perms = zstrtol(opt, 0, 8); /* octal number */ + } + + if (flags & O_CREAT) + fd = open(*args, flags, perms); + else + fd = open(*args, flags); + + if (fd == -1) { + zwarnnam(nam, "can't open file %s: %e", *args, errno); + return 1; + } + fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd); + if (fd == -1) { + zwarnnam(nam, "can't open file %s", *args); + return 1; + } + +#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) + if (fdflags) + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + if (explicit == -1) { + fdtable[fd] = FDT_EXTERNAL; + setiparam(fdvar, fd); + /* if setting the variable failed, close fd to avoid leak */ + if (errflag) + zclose(fd); + } + + return 0; +} + + +/* + * Return values of bin_sysseek: + * 0 Success + * 1 Error in parameters to command + * 2 Error on seek, ERRNO set by system + */ + +/**/ +static int +bin_sysseek(char *nam, char **args, Options ops, UNUSED(int func)) +{ + int w = SEEK_SET, fd = 0; + char *whence; + off_t pos; + + /* -u: file descriptor if not stdin */ + if (OPT_ISSET(ops, 'u')) { + fd = getposint(OPT_ARG(ops, 'u'), nam); + if (fd < 0) + return 1; + } + + /* -w: whence - starting point of seek */ + if (OPT_ISSET(ops, 'w')) { + whence = OPT_ARG(ops, 'w'); + if (!(strcasecmp(whence, "current") && strcmp(whence, "1"))) + w = SEEK_CUR; + else if (!(strcasecmp(whence, "end") && strcmp(whence, "2"))) + w = SEEK_END; + else if (strcasecmp(whence, "start") && strcmp(whence, "0")) { + zwarnnam(nam, "unknown argument to -w: %s", whence); + return 1; + } + } + + pos = (off_t)mathevali(*args); + return (lseek(fd, pos, w) == -1) ? 2 : 0; +} + +/**/ +static mnumber +math_systell(UNUSED(char *name), int argc, mnumber *argv, UNUSED(int id)) +{ + int fd = (argv->type == MN_INTEGER) ? argv->u.l : (int)argv->u.d; + mnumber ret; + ret.type = MN_INTEGER; + ret.u.l = 0; + + if (fd < 0) { + zerr("file descriptor out of range"); + return ret; + } + ret.u.l = lseek(fd, 0, SEEK_CUR); + return ret; +} + + /* * Return values of bin_syserror: * 0 Successfully processed error @@ -542,6 +718,8 @@ static struct builtin bintab[] = { BUILTIN("syserror", 0, bin_syserror, 0, 1, 0, "e:p:", NULL), BUILTIN("sysread", 0, bin_sysread, 0, 1, 0, "c:i:o:s:t:", NULL), BUILTIN("syswrite", 0, bin_syswrite, 1, 1, 0, "c:o:", NULL), + BUILTIN("sysopen", 0, bin_sysopen, 1, 1, 0, "rwau:o:m:", NULL), + BUILTIN("sysseek", 0, bin_sysseek, 1, 1, 0, "u:w:", NULL), BUILTIN("zsystem", 0, bin_zsystem, 1, -1, 0, NULL, NULL) }; @@ -611,6 +789,9 @@ scanpmsysparams(UNUSED(HashTable ht), ScanFunc func, int flags) func(&spm.node, flags); } +static struct mathfunc mftab[] = { + NUMMATHFUNC("systell", math_systell, 1, 1, 0) +}; static struct paramdef partab[] = { SPECIALPMDEF("errnos", PM_ARRAY|PM_READONLY, @@ -622,7 +803,7 @@ static struct paramdef partab[] = { static struct features module_features = { bintab, sizeof(bintab)/sizeof(*bintab), NULL, 0, - NULL, 0, + mftab, sizeof(mftab)/sizeof(*mftab), partab, sizeof(partab)/sizeof(*partab), 0 }; diff --git a/Src/Modules/system.mdd b/Src/Modules/system.mdd index 46f02d166..eed0c1b9d 100644 --- a/Src/Modules/system.mdd +++ b/Src/Modules/system.mdd @@ -2,7 +2,7 @@ name=zsh/system link=dynamic load=no -autofeatures="b:sysread b:syswrite b:syserror p:errnos" +autofeatures="b:sysread b:syswrite b:sysopen b:sysseek b:syserror p:errnos f:systell" objects="system.o errnames.o" diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c index 0d9522047..bc1765da1 100644 --- a/Src/Modules/tcp.c +++ b/Src/Modules/tcp.c @@ -519,7 +519,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) tv.tv_sec = 0; tv.tv_usec = 0; - if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv))) return 1; + if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv)) == 0) return 1; else if (ret == -1) { zwarnnam(nam, "select error: %e", errno); @@ -536,8 +536,11 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) sess = zts_alloc(ZTCP_INBOUND); len = sizeof(sess->peer.in); - if ((rfd = accept(lfd, (struct sockaddr *)&sess->peer.in, &len)) == -1) - { + do { + rfd = accept(lfd, (struct sockaddr *)&sess->peer.in, &len); + } while (rfd < 0 && errno == EINTR && !errflag); + + if (rfd == -1) { zwarnnam(nam, "could not accept connection: %e", errno); tcp_close(sess); return 1; diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index 09d4bd703..bd51512f9 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -731,7 +731,7 @@ zfgetmsg(void) stopit = (*ptr++ != '-'); queue_signals(); - if (!(verbose = getsparam("ZFTP_VERBOSE"))) + if (!(verbose = getsparam_u("ZFTP_VERBOSE"))) verbose = ""; if (strchr(verbose, lastcodestr[0])) { /* print the whole thing verbatim */ @@ -1785,7 +1785,7 @@ zftp_open(char *name, char **args, int flags) char *hname; alarm(0); queue_signals(); - if ((hname = getsparam("ZFTP_HOST")) && *hname) + if ((hname = getsparam_u("ZFTP_HOST")) && *hname) zwarnnam(name, "timeout connecting to %s", hname); else zwarnnam(name, "timeout on host name lookup"); @@ -3077,7 +3077,7 @@ bin_zftp(char *name, char **args, UNUSED(Options ops), UNUSED(int func)) } queue_signals(); - if ((prefs = getsparam("ZFTP_PREFS"))) { + if ((prefs = getsparam_u("ZFTP_PREFS"))) { zfprefs = 0; for (ptr = prefs; *ptr; ptr++) { switch (toupper(STOUC(*ptr))) { @@ -3149,9 +3149,11 @@ zftp_cleanup(void) zfclose(zfsess != cursess); } zsfree(lastmsg); + lastmsg = NULL; zfunsetparam("ZFTP_SESSION"); freelinklist(zfsessions, (FreeFunc) freesession); zfree(zfstatusp, sizeof(int)*zfsesscnt); + zfstatusp = NULL; } static int diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 7b6130c6f..12e42b5bd 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -463,6 +463,8 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) #endif errno == EINTR)); + setiparam("REPLY", master); + return 0; } diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index d4051bda0..ae3a64074 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -327,9 +327,7 @@ do_completion(UNUSED(Hookdef dummy), Compldat dat) haspattern = 0; complistmax = getiparam("LISTMAX"); zsfree(complastprompt); - complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) || - (unset(ALWAYSLASTPROMPT) && zmult != 1)) ? - "yes" : ""); + complastprompt = ztrdup(isset(ALWAYSLASTPROMPT) ? "yes" : ""); dolastprompt = 1; zsfree(complist); complist = ztrdup(isset(LISTROWSFIRST) ? @@ -975,7 +973,7 @@ makecomplist(char *s, int incmd, int lst) mnum = 0; unambig_mnum = -1; isuf = NULL; - insmnum = 1; + insmnum = zmult; #if 0 /* group-numbers in compstate[insert] */ insgnum = 1; @@ -2051,7 +2049,7 @@ addmatches(Cadata dat, char **argv) Heap oldheap; SWITCHHEAPS(oldheap, compheap) { - if (dat->dummies) + if (dat->dummies >= 0) dat->aflags = ((dat->aflags | CAF_NOSORT | CAF_UNIQCON) & ~CAF_UNIQALL); @@ -2536,7 +2534,7 @@ addmatches(Cadata dat, char **argv) addmatch("<all>", dat->flags | CMF_ALL, &disp, 1); hasallmatch = 1; } - while (dat->dummies--) + while (dat->dummies-- > 0) addmatch("", dat->flags | CMF_DUMMY, &disp, 0); } SWITCHBACKHEAPS(oldheap); diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 189582d22..bac533e7e 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1803,7 +1803,7 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat) mnum = 0; unambig_mnum = -1; isuf = NULL; - insmnum = 1; + insmnum = zmult; #if 0 /* group-numbers in compstate[insert] */ insgnum = 1; diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index ea5e41f5f..ee4e5b0a5 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -267,8 +267,12 @@ parse_cmatcher(char *name, char *s) s++; if (!*s || !*++s) { - if (name) - zwarnnam(name, "missing line pattern"); + if (name) { + if (both) + zwarnnam(name, "missing right anchor"); + else + zwarnnam(name, "missing line pattern"); + } return pcm_err; } } else @@ -288,6 +292,7 @@ parse_cmatcher(char *name, char *s) if ((fl & CMF_RIGHT) && !fl2 && (!*s || !*++s)) { if (name) zwarnnam(name, "missing right anchor"); + return pcm_err; } else if (!(fl & CMF_RIGHT) || fl2) { if (!*s) { if (name) @@ -313,8 +318,7 @@ parse_cmatcher(char *name, char *s) return pcm_err; } s++; - } else - right = NULL; + } if (*s == '*') { if (!(fl & (CMF_LEFT | CMF_RIGHT))) { @@ -537,7 +541,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) dat.match = NULL; dat.flags = 0; dat.aflags = CAF_MATCH; - dat.dummies = 0; + dat.dummies = -1; for (; *argv && **argv == '-'; argv++) { if (!(*argv)[1]) { @@ -1625,7 +1629,6 @@ boot_(Module m) addhookfunc("before_complete", (Hookfn) before_complete); addhookfunc("after_complete", (Hookfn) after_complete); addhookfunc("accept_completion", (Hookfn) accept_last); - addhookfunc("reverse_menu", (Hookfn) reverse_menu); addhookfunc("list_matches", (Hookfn) list_matches); addhookfunc("invalidate_list", (Hookfn) invalidate_list); (void)addhookdefs(m, comphooks, sizeof(comphooks)/sizeof(*comphooks)); @@ -1640,7 +1643,6 @@ cleanup_(Module m) deletehookfunc("before_complete", (Hookfn) before_complete); deletehookfunc("after_complete", (Hookfn) after_complete); deletehookfunc("accept_completion", (Hookfn) accept_last); - deletehookfunc("reverse_menu", (Hookfn) reverse_menu); deletehookfunc("list_matches", (Hookfn) list_matches); deletehookfunc("invalidate_list", (Hookfn) invalidate_list); (void)deletehookdefs(m, comphooks, diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index f54206619..fd90ccb31 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -507,8 +507,8 @@ getcols() max_caplen = lr_caplen = 0; mcolors.flags = 0; queue_signals(); - if (!(s = getsparam("ZLS_COLORS")) && - !(s = getsparam("ZLS_COLOURS"))) { + if (!(s = getsparam_u("ZLS_COLORS")) && + !(s = getsparam_u("ZLS_COLOURS"))) { for (i = 0; i < NUM_COLS; i++) mcolors.files[i] = filecol(""); mcolors.pats = NULL; @@ -728,7 +728,7 @@ clnicezputs(int do_colors, char *s, int ml) if (do_colors) initiscol(); - mb_metacharinit(); + mb_charinit(); while (umleft > 0) { size_t cnt = eol ? MB_INVALID : mbrtowc(&cc, uptr, umleft, &mbs); @@ -2071,6 +2071,7 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat) memset(mgtab, 0, i * sizeof(Cmgroup)); mlastcols = mcols = zterm_columns; mlastlines = mlines = listdat.nlines; + mmtabp = 0; } last_cap = (char *) zhalloc(max_caplen + 1); *last_cap = '\0'; @@ -2269,41 +2270,16 @@ msearchpop(int *backp) } static Cmatch ** -msearch(Cmatch **ptr, int ins, int back, int rep, int *wrapp) +msearch(Cmatch **ptr, char *ins, int back, int rep, int *wrapp) { -#ifdef MULTIBYTE_SUPPORT - /* MB_CUR_MAX may not be constant */ - VARARR(char, s, MB_CUR_MAX+1); -#else - char s[2]; -#endif Cmatch **p, *l = NULL, m; int x = mcol, y = mline; int ex, ey, wrap = 0, owrap = (msearchstate & MS_WRAPPED); msearchpush(ptr, back); - if (ins) { -#ifdef MULTIBYTE_SUPPORT - if (lastchar_wide_valid) - { - mbstate_t mbs; - int len; - - memset(&mbs, 0, sizeof(mbs)); - len = wcrtomb(s, lastchar_wide, &mbs); - if (len < 0) - len = 0; - s[len] = '\0'; - } else -#endif - { - s[0] = lastchar; - s[1] = '\0'; - } - - msearchstr = dyncat(msearchstr, s); - } + if (ins) + msearchstr = dyncat(msearchstr, ins); if (back) { ex = mcols - 1; ey = -1; @@ -2587,6 +2563,8 @@ domenuselect(Hookdef dummy, Chdata dat) } p = mmtabp; pg = mgtabp; + if (!p) /* selected match not in display, find line */ + continue; minfo.cur = *p; minfo.group = *pg; if (setwish) @@ -2603,6 +2581,7 @@ domenuselect(Hookdef dummy, Chdata dat) getk: if (!do_last_key) { + zmult = 1; cmd = getkeycmd(); if (mtab_been_reallocated) { do_last_key = 1; @@ -2680,7 +2659,7 @@ domenuselect(Hookdef dummy, Chdata dat) s->nbrbeg = nbrbeg; s->nbrend = nbrend; s->nmatches = nmatches; - s->origline = origline; + s->origline = dupstring(origline); s->origcs = origcs; s->origll = origll; s->status = dupstring(status); @@ -2811,7 +2790,7 @@ domenuselect(Hookdef dummy, Chdata dat) s->nbrbeg = nbrbeg; s->nbrend = nbrend; s->nmatches = nmatches; - s->origline = origline; + s->origline = dupstring(origline); s->origcs = origcs; s->origll = origll; s->status = dupstring(status); @@ -3273,38 +3252,74 @@ domenuselect(Hookdef dummy, Chdata dat) cmd == Th(z_historyincrementalsearchbackward) || ((mode == MM_FSEARCH || mode == MM_BSEARCH) && (cmd == Th(z_selfinsert) || - cmd == Th(z_selfinsertunmeta)))) { + cmd == Th(z_selfinsertunmeta) || + cmd == Th(z_bracketedpaste)))) { Cmatch **np, **op = p; int was = (mode == MM_FSEARCH || mode == MM_BSEARCH); - int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta)); + int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta) || + cmd == Th(z_bracketedpaste)); int back = (cmd == Th(z_historyincrementalsearchbackward)); int wrap; do { + char *toins = NULL; +#ifdef MULTIBYTE_SUPPORT + /* MB_CUR_MAX may not be constant */ + VARARR(char, insert, MB_CUR_MAX+1); +#else + char insert[2]; +#endif if (was) { p += wishcol - mcol; mcol = wishcol; } if (!ins) { if (was) { - if (!*msearchstr && lastsearch) { + if (!*msearchstr && lastsearch && + back == (mode == MM_BSEARCH)) { msearchstr = dupstring(lastsearch); mode = 0; } } else { msearchstr = ""; msearchstack = NULL; + msearchstate = MS_OK; } - } - if (cmd == Th(z_selfinsertunmeta)) { - fixunmeta(); - } + } else { + if (cmd == Th(z_selfinsertunmeta)) { + fixunmeta(); + } + if (cmd == Th(z_bracketedpaste)) { + toins = bracketedstring(); + } else { + toins = insert; +#ifdef MULTIBYTE_SUPPORT + if (lastchar_wide_valid) + { + mbstate_t mbs; + int len; + + memset(&mbs, 0, sizeof(mbs)); + len = wcrtomb(s, lastchar_wide, &mbs); + if (len < 0) + len = 0; + insert[len] = '\0'; + } else +#endif + { + insert[0] = lastchar; + insert[1] = '\0'; + } + } + } wrap = 0; - np = msearch(p, ins, (ins ? (mode == MM_BSEARCH) : back), + np = msearch(p, toins, (ins ? (mode == MM_BSEARCH) : back), (was && !ins), &wrap); if (!ins) mode = (back ? MM_BSEARCH : MM_FSEARCH); + else if (cmd == Th(z_bracketedpaste)) + free(toins); if (*msearchstr) { zsfree(lastsearch); diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 9f383f4b8..7fec7c804 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -1220,25 +1220,39 @@ do_menucmp(int lst) was_meta = 1; /* Otherwise go to the next match in the array... */ - do { - if (!*++(minfo.cur)) { - do { - if (!(minfo.group = (minfo.group)->next)) { - minfo.group = amatches; + while (zmult) { + do { + if (zmult > 0) { + if (!*++(minfo.cur)) { + do { + if (!(minfo.group = (minfo.group)->next)) { + minfo.group = amatches; #ifdef ZSH_HEAP_DEBUG - if (memory_validate(minfo.group->heap_id)) { - HEAP_ERROR(minfo.group->heap_id); - } + if (memory_validate(minfo.group->heap_id)) { + HEAP_ERROR(minfo.group->heap_id); + } #endif + } + } while (!(minfo.group)->mcount); + minfo.cur = minfo.group->matches; } - } while (!(minfo.group)->mcount); - minfo.cur = minfo.group->matches; - } - } while ((menuacc && - !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || - ((*minfo.cur)->flags & CMF_DUMMY) || - (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && - (!(*minfo.cur)->str || !*(*minfo.cur)->str))); + } else { + if (minfo.cur == (minfo.group)->matches) { + do { + if (!(minfo.group = (minfo.group)->prev)) + minfo.group = lmatches; + } while (!(minfo.group)->mcount); + minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1; + } else + minfo.cur--; + } + } while ((menuacc && + !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || + ((*minfo.cur)->flags & CMF_DUMMY) || + (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && + (!(*minfo.cur)->str || !*(*minfo.cur)->str))); + zmult -= (0 < zmult) - (zmult < 0); + } /* ... and insert it into the command line. */ do_single(*minfo.cur); @@ -1246,43 +1260,6 @@ do_menucmp(int lst) unmetafy_line(); } -/**/ -int -reverse_menu(UNUSED(Hookdef dummy), UNUSED(void *dummy2)) -{ - int was_meta; - - if (minfo.cur == NULL) - return 1; - - do { - if (minfo.cur == (minfo.group)->matches) { - do { - if (!(minfo.group = (minfo.group)->prev)) - minfo.group = lmatches; - } while (!(minfo.group)->mcount); - minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1; - } else - minfo.cur--; - } while ((menuacc && - !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || - ((*minfo.cur)->flags & CMF_DUMMY) || - (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && - (!(*minfo.cur)->str || !*(*minfo.cur)->str))); - /* May already be metafied if called from within a selection */ - if (zlemetaline == NULL) { - metafy_line(); - was_meta = 0; - } - else - was_meta = 1; - do_single(*(minfo.cur)); - if (!was_meta) - unmetafy_line(); - - return 0; -} - /* Accepts the current completion and starts a new arg, * * with the next completions. This gives you a way to * * accept several selections from the list of matches. */ diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 27938c17f..e5db0867b 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -4196,7 +4196,7 @@ cfp_matcher_range(Cmatcher *ms, char *add) addlen = MB_METACHARLENCONV(add, &addc); #ifdef MULTIBYTE_SUPPORT if (addc == WEOF) - addc = (wchar_t)(*p == Meta ? p[1] ^ 32 : *p); + addc = (wchar_t)(*add == Meta ? add[1] ^ 32 : *add); #endif if (!(m = *mp)) { diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index b41661a7d..2b2654c5d 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -28,12 +28,14 @@ "beginning-of-history", beginningofhistory, 0 "beginning-of-line", beginningofline, 0 "beginning-of-line-hist", beginningoflinehist, 0 +"bracketed-paste", bracketedpaste, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_YANKBEFORE "capitalize-word", capitalizeword, 0 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "copy-prev-word", copyprevword, ZLE_KEEPSUFFIX "copy-prev-shell-word", copyprevshellword, ZLE_KEEPSUFFIX "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX +"deactivate-region", deactivateregion, 0 "delete-char", deletechar, ZLE_KEEPSUFFIX "delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "delete-word", deleteword, ZLE_KEEPSUFFIX @@ -93,7 +95,7 @@ "quoted-insert", quotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX "quote-line", quoteline, 0 "quote-region", quoteregion, 0 -"read-command", readcommand, 0 +"read-command", readcommand, ZLE_NOTCOMMAND "recursive-edit", recursiveedit, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "redo", redo, ZLE_KEEPSUFFIX diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 3c652909e..59f459185 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -352,8 +352,7 @@ struct brinfo { #define BEFORECOMPLETEHOOK (zlehooks + 2) #define AFTERCOMPLETEHOOK (zlehooks + 3) #define ACCEPTCOMPHOOK (zlehooks + 4) -#define REVERSEMENUHOOK (zlehooks + 5) -#define INVALIDATELISTHOOK (zlehooks + 6) +#define INVALIDATELISTHOOK (zlehooks + 5) /* complete hook data struct */ @@ -430,8 +429,9 @@ struct region_highlight { * 0: region between point and mark * 1: isearch region * 2: suffix + * 3: pasted text */ -#define N_SPECIAL_HIGHLIGHTS (3) +#define N_SPECIAL_HIGHLIGHTS (4) #ifdef MULTIBYTE_SUPPORT diff --git a/Src/Zle/zle_bindings.c b/Src/Zle/zle_bindings.c index 2ae8c8764..55863db1b 100644 --- a/Src/Zle/zle_bindings.c +++ b/Src/Zle/zle_bindings.c @@ -317,7 +317,7 @@ int vicmdbind[128] = { /* ^X */ z_undefinedkey, /* ^Y */ z_undefinedkey, /* ^Z */ z_undefinedkey, - /* ^[ */ z_undefinedkey, + /* ^[ */ z_beep, /* ^\ */ z_undefinedkey, /* ^] */ z_undefinedkey, /* ^^ */ z_undefinedkey, diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index cc66f99ae..c61b4ef0e 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -894,10 +894,8 @@ zgetline(UNUSED(char **args)) free(s); free(lineadd); clearlist = 1; - if (stackhist != -1) { - histline = stackhist; - stackhist = -1; - } + /* not restoring stackhist as we're inserting into current line */ + stackhist = -1; } return 0; } @@ -1598,7 +1596,7 @@ doisearch(char **args, int dir, int pattern) dir = odir; skip_pos = 1; rpt: - if (!sbptr && previous_search_len) { + if (!sbptr && previous_search_len && dir == odir) { if (previous_search_len > sibuf - FIRST_SEARCH_CHAR - 2) { ibuf = hrealloc((char *)ibuf, sibuf, (sibuf + previous_search_len)); @@ -1620,6 +1618,21 @@ doisearch(char **args, int dir, int pattern) feep = 1; else goto ins; + } else if (cmd == Th(z_bracketedpaste)) { + char *paste = bracketedstring(); + set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos, + zlemetacs, sbptr, dir, nomatch); + size_t pastelen = strlen(paste); + if (sbptr + pastelen >= sibuf - FIRST_SEARCH_CHAR - 2) { + int oldsize = sibuf; + sibuf += (pastelen >= sibuf) ? pastelen + 1 : sibuf; + ibuf = hrealloc(ibuf, oldsize, sibuf); + sbuf = ibuf + FIRST_SEARCH_CHAR; + } + strcpy(sbuf + sbptr, paste); + sbptr += pastelen; + patprog = NULL; + free(paste); } else if (cmd == Th(z_acceptsearch)) { break; } else { diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index c6fae251d..5b4189faa 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -540,7 +540,7 @@ reselectkeymap(void) /**/ mod_export int -bindkey(Keymap km, char *seq, Thingy bind, char *str) +bindkey(Keymap km, const char *seq, Thingy bind, char *str) { Key k; int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]); @@ -1363,6 +1363,7 @@ default_bindings(void) } /* escape in operator pending cancels the operation */ bindkey(oppmap, "\33", refthingy(t_vicmdmode), NULL); + bindkey(vismap, "\33", refthingy(t_deactivateregion), NULL); bindkey(vismap, "o", refthingy(t_exchangepointandmark), NULL); bindkey(vismap, "p", refthingy(t_putreplaceselection), NULL); bindkey(vismap, "x", refthingy(t_videlete), NULL); @@ -1400,6 +1401,11 @@ default_bindings(void) bindkey(emap, "\30\30", refthingy(t_exchangepointandmark), NULL); bindkey(emap, "\30=", refthingy(t_whatcursorposition), NULL); + /* bracketed paste applicable to all keymaps */ + bindkey(emap, "\33[200~", refthingy(t_bracketedpaste), NULL); + bindkey(vmap, "\33[200~", refthingy(t_bracketedpaste), NULL); + bindkey(amap, "\33[200~", refthingy(t_bracketedpaste), NULL); + /* emacs mode: ESC sequences, all taken from the meta binding table */ buf[0] = '\33'; buf[2] = 0; diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index cec44c0ed..e610ae1f3 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -1119,7 +1119,7 @@ zlecore(void) char * zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) { - char *s; + char *s, **bracket; int old_errno = errno; int tmout = getiparam("TMOUT"); @@ -1206,6 +1206,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) histline = stackhist; stackhist = -1; } + handleundo(); } /* * If main is linked to the viins keymap, we need to register @@ -1248,6 +1249,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) zlecallhook(init, NULL); + if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2) + fputs(*bracket, shout); + zrefresh(); zlecore(); @@ -1257,6 +1261,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) "ZLE_VARED_ABORTED" : "ZLE_LINE_ABORTED", zlegetline(NULL, NULL)); + if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2) + fputs(bracket[1], shout); + if (done && !exit_pending && !errflag) zlecallhook(finish, NULL); @@ -1858,7 +1865,7 @@ trashzle(void) clearflag = listshown = 0; } if (postedit) - fprintf(shout, "%s", postedit); + fprintf(shout, "%s", unmeta(postedit)); fflush(shout); resetneeded = 1; if (!(zlereadflags & ZLRF_NOSETTY)) @@ -1986,8 +1993,6 @@ mod_export struct hookdef zlehooks[] = { HOOKDEF("after_complete", NULL, 0), /* ACCEPTCOMPHOOK */ HOOKDEF("accept_completion", NULL, 0), - /* REVERSEMENUHOOK */ - HOOKDEF("reverse_menu", NULL, 0), /* INVALIDATELISTHOOK */ HOOKDEF("invalidate_list", NULL, 0), }; @@ -2004,6 +2009,8 @@ static struct features module_features = { int setup_(UNUSED(Module m)) { + char **bpaste; + /* Set up editor entry points */ zle_entry_ptr = zle_main_entry; zle_load_state = 1; @@ -2028,6 +2035,11 @@ setup_(UNUSED(Module m)) clwords = (char **) zshcalloc((clwsize = 16) * sizeof(char *)); + bpaste = zshcalloc(3*sizeof(char *)); + bpaste[0] = ztrdup("\033[?2004h"); + bpaste[1] = ztrdup("\033[?2004l"); + setaparam("zle_bracketed_paste", bpaste); + return 0; } diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c index 4669ef2ad..2d1862813 100644 --- a/Src/Zle/zle_misc.c +++ b/Src/Zle/zle_misc.c @@ -517,10 +517,12 @@ copyregionaskill(char **args) /* * kct: index into kill ring, or -1 for original cutbuffer of yank. - * yankb, yanke: mark the start and end of last yank in editing buffer. * yankcs marks the cursor position preceding the last yank */ -static int kct, yankb, yanke, yankcs; +static int kct, yankcs; + +/**/ +int yankb, yanke; /* mark the start and end of last yank in editing buffer. */ /* The original cutbuffer, either cutbuf or one of the vi buffers. */ static Cutbuffer kctbuf; @@ -736,6 +738,71 @@ yankpop(UNUSED(char **args)) } /**/ +mod_export char * +bracketedstring(void) +{ + static const char endesc[] = "\033[201~"; + int endpos = 0; + size_t psize = 64; + char *pbuf = zalloc(psize); + size_t current = 0; + int next, timeout; + + while (endesc[endpos]) { + if (current + 1 >= psize) + pbuf = zrealloc(pbuf, psize *= 2); + if ((next = getbyte(1L, &timeout)) == EOF) + break; + if (!endpos || next != endesc[endpos++]) + endpos = (next == *endesc); + if (imeta(next)) { + pbuf[current++] = Meta; + pbuf[current++] = next ^ 32; + } else if (next == '\r') + pbuf[current++] = '\n'; + else + pbuf[current++] = next; + } + pbuf[current-endpos] = '\0'; + return pbuf; +} + +/**/ +int +bracketedpaste(char **args) +{ + char *pbuf = bracketedstring(); + + if (*args) { + setsparam(*args, pbuf); + } else { + int n; + ZLE_STRING_T wpaste; + wpaste = stringaszleline((zmult == 1) ? pbuf : + quotestring(pbuf, NULL, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL); + cuttext(wpaste, n, CUT_REPLACE); + if (!(zmod.flags & MOD_VIBUF)) { + kct = -1; + kctbuf = &cutbuf; + zmult = 1; + if (region_active) + killregion(zlenoargs); + /* Chop a final newline if its insertion would be hard to + * distinguish by the user from the line being accepted. */ + else if (n > 1 && zlecontext != ZLCON_VARED && + (zlecs + (insmode ? 0 : n - 1)) >= zlell && + wpaste[n-1] == ZWC('\n')) + n--; + yankcs = yankb = zlecs; + doinsert(wpaste, n); + yanke = zlecs; + } + free(pbuf); free(wpaste); + } + return 0; +} + +/**/ int overwritemode(UNUSED(char **args)) { @@ -1264,6 +1331,22 @@ executenamedcommand(char *prmt) if (listed) clearlist = listshown = 1; curlist = 0; + } else if (cmd == Th(z_bracketedpaste)) { + char *insert = bracketedstring(); + size_t inslen = strlen(insert); + if (len + inslen > NAMLEN) + feep = 1; + else { + strcpy(ptr, insert); + len += inslen; + ptr += inslen; + if (listed) { + clearlist = listshown = 1; + listed = 0; + } else + curlist = 0; + } + free(insert); } else { if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) { Thingy r; @@ -1484,13 +1567,13 @@ makesuffix(int n) { char *suffixchars; - if (!(suffixchars = getsparam("ZLE_REMOVE_SUFFIX_CHARS"))) + if (!(suffixchars = getsparam_u("ZLE_REMOVE_SUFFIX_CHARS"))) suffixchars = " \t\n;&|"; addsuffixstring(SUFTYP_POSSTR, 0, suffixchars, n); /* Do this second so it takes precedence */ - if ((suffixchars = getsparam("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars) + if ((suffixchars = getsparam_u("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars) addsuffixstring(SUFTYP_POSSTR, SUFFLAGS_SPACE, suffixchars, n); suffixlen = n; diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c index d751c4333..f49df8647 100644 --- a/Src/Zle/zle_move.c +++ b/Src/Zle/zle_move.c @@ -555,6 +555,13 @@ visuallinemode(UNUSED(char **args)) return 0; } +/**/ +int +deactivateregion(UNUSED(char **args)) +{ + region_active = 0; + return 0; +} /**/ int @@ -876,24 +883,36 @@ int vigotomark(UNUSED(char **args)) { ZLE_INT_T ch; + int *markcs, *markhist = 0; int oldcs = zlecs; int oldline = histline; + int tmpcs, tmphist; ch = getfullchar(0); - if (ch == ZWC('\'') || ch == ZWC('`')) - ch = 26; - else { - if (ch < ZWC('a') || ch > ZWC('z')) - return 1; - ch -= ZWC('a'); - } - if (!vimarkline[ch]) - return 1; - if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) { - vimarkline[ch] = 0; + if (ch == ZWC('\'') || ch == ZWC('`')) { + markhist = vimarkline + 26; + markcs = vimarkcs + 26; + } else if (ch == ZWC('.') && curchange->prev) { + /* position cursor where it was after the last change. not exactly + * what vim does but close enough */ + tmpcs = curchange->prev->new_cs; + tmphist = curchange->prev->hist; + markcs = &tmpcs; + markhist = &tmphist; + } else if (ch >= ZWC('a') && ch <= ZWC('z')) { + markhist = vimarkline + (ch - ZWC('a')); + markcs = vimarkcs + (ch - ZWC('a')); + } else return 1; + if (markhist) { + if (!*markhist) + return 1; + if (histline != *markhist && !zle_goto_hist(*markhist, 0, 0)) { + *markhist = 0; + return 1; + } } - zlecs = vimarkcs[ch]; + zlecs = *markcs; vimarkcs[26] = oldcs; vimarkline[26] = oldline; if (zlecs > zlell) diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c index ce4b0724d..b84e72088 100644 --- a/Src/Zle/zle_params.c +++ b/Src/Zle/zle_params.c @@ -95,6 +95,8 @@ static const struct gsu_integer region_active_gsu = { get_region_active, set_region_active, zleunsetfn }; static const struct gsu_integer undo_change_no_gsu = { get_undo_current_change, NULL, zleunsetfn }; +static const struct gsu_integer undo_limit_no_gsu = +{ get_undo_limit_change, set_undo_limit_change, zleunsetfn }; static const struct gsu_array killring_gsu = { get_killring, set_killring, unset_killring }; @@ -137,6 +139,7 @@ static struct zleparam { { "region_highlight", PM_ARRAY, GSU(region_highlight_gsu), NULL }, { "UNDO_CHANGE_NO", PM_INTEGER | PM_READONLY, GSU(undo_change_no_gsu), NULL }, + { "UNDO_LIMIT_NO", PM_INTEGER, GSU(undo_limit_no_gsu), NULL }, { "WIDGET", PM_SCALAR | PM_READONLY, GSU(widget_gsu), NULL }, { "WIDGETFUNC", PM_SCALAR | PM_READONLY, GSU(widgetfunc_gsu), NULL }, { "WIDGETSTYLE", PM_SCALAR | PM_READONLY, GSU(widgetstyle_gsu), NULL }, diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index fe337993f..78046fb7b 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -318,6 +318,7 @@ zle_set_highlight(void) int region_atr_on_set = 0; int isearch_atr_on_set = 0; int suffix_atr_on_set = 0; + int paste_atr_on_set = 0; struct region_highlight *rhp; special_atr_on = default_atr_on = 0; @@ -337,7 +338,8 @@ zle_set_highlight(void) for (; *atrs; atrs++) { if (!strcmp(*atrs, "none")) { /* reset attributes for consistency... usually unnecessary */ - special_atr_on = default_atr_on = 0; + special_atr_on = default_atr_on = + paste_atr_on_set = 0; special_atr_on_set = region_atr_on_set = isearch_atr_on_set = suffix_atr_on_set = 1; } else if (strpfx("default:", *atrs)) { @@ -354,6 +356,9 @@ zle_set_highlight(void) } else if (strpfx("suffix:", *atrs)) { match_highlight(*atrs + 7, &(region_highlights[2].atr)); suffix_atr_on_set = 1; + } else if (strpfx("paste:", *atrs)) { + match_highlight(*atrs + 6, &(region_highlights[3].atr)); + paste_atr_on_set = 1; } } } @@ -367,6 +372,7 @@ zle_set_highlight(void) region_highlights[1].atr = TXTUNDERLINE; if (!suffix_atr_on_set) region_highlights[2].atr = TXTBOLDFACE; + /* paste defaults to 0 */ allocate_colour_buffer(); } @@ -1073,6 +1079,13 @@ zrefresh(void) region_highlights[2].start = region_highlights[2].end = -1; } + if (lastcmd & ZLE_YANK) { + region_highlights[3].start = yankb; + region_highlights[3].end = yanke; + } else { + region_highlights[3].start = region_highlights[3].end = -1; + } + if (clearlist && listshown > 0) { if (tccan(TCCLEAREOD)) { int ovln = vln, ovcs = vcs; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index f18ad170e..b87b99b00 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -345,17 +345,8 @@ mod_export int reversemenucomplete(char **args) { wouldinstab = 0; - if (!menucmp) { - menucomplete(args); - /* - * Drop through, since we are now on the first item instead of - * the last. We've already updated the display, so this is a - * bit inefficient, but it's simple and it works. - */ - } - - runhookdef(REVERSEMENUHOOK, NULL); - return 0; + zmult = -zmult; + return menucomplete(args); } /**/ @@ -730,11 +721,12 @@ docomplete(int lst) } } } - if (lst == COMP_EXPAND_COMPLETE) + if (lst == COMP_EXPAND_COMPLETE) { do { /* Check if there is a parameter expression. */ for (; *q && *q != String; q++); - if (*q == String && q[1] != Inpar && q[1] != Inbrack) { + if (*q == String && q[1] != Inpar && q[1] != Inparmath && + q[1] != Inbrack) { if (*++q == Inbrace) { if (! skipparens(Inbrace, Outbrace, &q) && q == s + zlemetacs - wb) @@ -778,6 +770,7 @@ docomplete(int lst) } else break; } while (q < s + zlemetacs - wb); + } if (lst == COMP_EXPAND_COMPLETE) { /* If it is still not clear if we should use expansion or * * completion and there is a `$' or a backtick in the word, * @@ -1182,14 +1175,30 @@ get_comp_string(void) do { qsub = noword = 0; - lincmd = ((incmdpos && !ins && !incond) || - (oins == 2 && wordpos == 2) || - (ins == 3 && wordpos == 1)); + /* + * pws: added cmdtok == NULLTOK test as fallback for detecting + * we haven't had a command yet. This is a cop out: it's needed + * after SEPER because of bizarre and incomprehensible dance + * that we otherwise do involving the "ins" flag when you might + * have thought we'd just reset everything because we're now + * considering a new command. Consequently, although this looks + * relatively harmless by itself, it's probably incomplete. + */ + lincmd = (incmdpos && !ins && !incond) || + (oins == 2 && wordpos == 2) || + (ins == 3 && wordpos == 1) || + (cmdtok == NULLTOK && !incond); linredir = (inredir && !ins); oins = ins; /* Get the next token. */ if (linarr) incmdpos = 0; + /* + * Arrange to parse assignments after typeset etc... + * but not if we're already in an array. + */ + if (cmdtok == TYPESET) + intypeset = !linarr; ctxtlex(); if (tok == LEXERR) { @@ -1272,10 +1281,11 @@ get_comp_string(void) tt0 = NULLTOK; } if (lincmd && (tok == STRING || tok == FOR || tok == FOREACH || - tok == SELECT || tok == REPEAT || tok == CASE)) { + tok == SELECT || tok == REPEAT || tok == CASE || + tok == TYPESET)) { /* The lexer says, this token is in command position, so * * store the token string (to find the right compctl). */ - ins = (tok == REPEAT ? 2 : (tok != STRING)); + ins = (tok == REPEAT ? 2 : (tok != STRING && tok != TYPESET)); zsfree(cmdstr); cmdstr = ztrdup(tokstr); cmdtok = tok; @@ -1290,7 +1300,7 @@ get_comp_string(void) * handle completing multiple SEPER-ated command positions on * the same command line, e.g., pipelines. */ - ins = (cmdtok != STRING); + ins = (cmdtok != STRING && cmdtok != TYPESET); } if (!lexflags && tt0 == NULLTOK) { /* This is done when the lexer reached the word the cursor is on. */ @@ -1436,7 +1446,7 @@ get_comp_string(void) we = wb = zlemetacs; clwpos = clwnum; t0 = STRING; - } else if (t0 == STRING) { + } else if (t0 == STRING || t0 == TYPESET) { /* We found a simple string. */ s = ztrdup(clwords[clwpos]); } else if (t0 == ENVSTRING) { @@ -1492,7 +1502,7 @@ get_comp_string(void) zlemetaline = tmp; zlemetall = strlen(zlemetaline); } - if (t0 != STRING && inwhat != IN_MATH) { + if (t0 != STRING && t0 != TYPESET && inwhat != IN_MATH) { if (tmp) { tmp = NULL; linptr = zlemetaline; diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index e4ab97a54..d1d320613 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -1288,7 +1288,7 @@ showmsg(char const *msg) p = unmetafy(umsg, &ulen); memset(&mbs, 0, sizeof mbs); - mb_metacharinit(); + mb_charinit(); while (ulen > 0) { char const *n; if (*p == '\n') { @@ -1405,9 +1405,9 @@ static struct change *nextchanges, *endnextchanges; /**/ zlong undo_changeno; -/* If non-zero, the last increment to undo_changeno was for the variable */ +/* If positive, don't undo beyond this point */ -static int undo_set_by_variable; +zlong undo_limitno; /**/ void @@ -1418,8 +1418,7 @@ initundo(void) curchange->prev = curchange->next = NULL; curchange->del = curchange->ins = NULL; curchange->dell = curchange->insl = 0; - curchange->changeno = undo_changeno = 0; - undo_set_by_variable = 0; + curchange->changeno = undo_changeno = undo_limitno = 0; lastline = zalloc((lastlinesz = linesz) * ZLE_CHAR_SIZE); ZS_memcpy(lastline, zleline, (lastll = zlell)); lastcs = zlecs; @@ -1545,7 +1544,6 @@ mkundoent(void) ch->prev = NULL; } ch->changeno = ++undo_changeno; - undo_set_by_variable = 0; endnextchanges = ch; } @@ -1580,12 +1578,13 @@ undo(char **args) struct change *prev = curchange->prev; if(!prev) return 1; - if (prev->changeno < last_change) - break; - if (unapplychange(prev)) - curchange = prev; - else + if (prev->changeno <= last_change) break; + if (prev->changeno <= undo_limitno && !*args) + return 1; + if (!unapplychange(prev) && last_change >= 0) + unapplychange(prev); + curchange = prev; } while (last_change >= (zlong)0 || (curchange->flags & CH_PREV)); setlastline(); return 0; @@ -1735,16 +1734,22 @@ zlecallhook(char *name, char *arg) zlong get_undo_current_change(UNUSED(Param pm)) { - if (undo_set_by_variable) { - /* We were the last to increment this, doesn't need another one. */ - return undo_changeno; - } - undo_set_by_variable = 1; - /* - * Increment the number in case a change is in progress; - * we don't want to back off what's already been done when - * we return to this change number. This eliminates any - * problem about the point where a change is numbered. - */ - return ++undo_changeno; + /* add entry for any pending changes */ + mkundoent(); + setlastline(); + return undo_changeno; +} + +/**/ +zlong +get_undo_limit_change(UNUSED(Param pm)) +{ + return undo_limitno; +} + +/**/ +void +set_undo_limit_change(UNUSED(Param pm), zlong value) +{ + undo_limitno = value; } diff --git a/Src/builtin.c b/Src/builtin.c index 9358e8b1f..3d34aa74c 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -53,7 +53,7 @@ static struct builtin builtins[] = BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL), - BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL), + BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL), BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL), BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), @@ -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, 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, BIN_EXPORT, "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. @@ -71,8 +71,8 @@ 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, bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"), - BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUz", NULL), + BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"), + BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx: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), @@ -82,11 +82,11 @@ static struct builtin builtins[] = #endif BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"), - BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"), + BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"), BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL), BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL), BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL), - BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL), + BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL), BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), @@ -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:z-", 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("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, "nrlL", 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, 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, 0, "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), @@ -120,7 +120,7 @@ static struct builtin builtins[] = BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL), BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL), BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"), - BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL), + BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL), BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL), BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL), BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"), @@ -128,9 +128,9 @@ static struct builtin builtins[] = BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, 0, "fmv", NULL), BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL), BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL), - BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSw", NULL), - BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsSw", "ca"), - BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsSw", "c"), + BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL), + BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsSwx:", "ca"), + BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsSwx:", "c"), BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "AFRILP:abcfdilmpue", NULL), BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL), }; @@ -246,7 +246,7 @@ new_optarg(Options ops) /**/ int -execbuiltin(LinkList args, Builtin bn) +execbuiltin(LinkList args, LinkList assigns, Builtin bn) { char *pp, *name, *optstr; int flags, sense, argc, execop, xtr = isset(XTRACE); @@ -443,11 +443,46 @@ execbuiltin(LinkList args, Builtin bn) fputc(' ', xtrerr); quotedzputs(*fullargv++, xtrerr); } + if (assigns) { + LinkNode node; + for (node = firstnode(assigns); node; incnode(node)) { + Asgment asg = (Asgment)node; + fputc(' ', xtrerr); + quotedzputs(asg->name, xtrerr); + if (asg->is_array) { + LinkNode arrnode; + fprintf(xtrerr, "=("); + if (asg->value.array) { + for (arrnode = firstnode(asg->value.array); + arrnode; + incnode(arrnode)) { + fputc(' ', xtrerr); + quotedzputs((char *)getdata(arrnode), xtrerr); + } + } + fprintf(xtrerr, " )"); + } else if (asg->value.scalar) { + fputc('=', xtrerr); + quotedzputs(asg->value.scalar, xtrerr); + } + } + } fputc('\n', xtrerr); fflush(xtrerr); } /* call the handler function, and return its return value */ - return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid); + if (flags & BINF_ASSIGN) + { + /* + * Takes two sets of arguments. + */ + HandlerFuncAssign assignfunc = (HandlerFuncAssign)bn->handlerfunc; + return (*(assignfunc)) (name, argv, assigns, &ops, bn->funcid); + } + else + { + return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid); + } } } @@ -1128,7 +1163,7 @@ cd_try_chdir(char *pfix, char *dest, int hard) * or a parent directory is renamed in the interim. */ if (lchdir(buf, NULL, hard) && - (pfix || *dest == '/' || lchdir(dest, NULL, hard))) { + (pfix || *dest == '/' || lchdir(unmeta(dest), NULL, hard))) { free(buf); return NULL; } @@ -1435,10 +1470,6 @@ bin_fc(char *nam, char **argv, Options ops, int func) unqueue_signals(); return 0; } - if (OPT_ISSET(ops,'I')) { - zwarnnam(nam, "-I requires one of -R/-W/-A"); - return 1; - } if (zleactive) { zwarnnam(nam, "no interactive history within ZLE"); @@ -1456,12 +1487,13 @@ bin_fc(char *nam, char **argv, Options ops, int func) if (!asgf) asgf = asgl = a; else { - asgl->next = a; + asgl->node.next = &a->node; asgl = a; } a->name = *argv; - a->value = s; - a->next = NULL; + a->is_array = 0; + a->value.scalar = s; + a->node.next = a->node.prev = NULL; argv++; } /* interpret and check first history line specifier */ @@ -1635,8 +1667,8 @@ fcsubs(char **sp, struct asgment *sub) /* loop through the linked list */ while (sub) { oldstr = sub->name; - newstr = sub->value; - sub = sub->next; + newstr = sub->value.scalar; + sub = (Asgment)sub->node.next; oldpos = s; /* loop over occurences of oldstr in s, replacing them with newstr */ while ((newpos = (char *)strstr(oldpos, oldstr))) { @@ -1672,7 +1704,7 @@ static int fclist(FILE *f, Options ops, zlong first, zlong last, struct asgment *subs, Patprog pprog, int is_command) { - int fclistdone = 0; + int fclistdone = 0, xflags = 0; zlong tmp; char *s, *tdfmt, *timebuf; Histent ent; @@ -1722,11 +1754,19 @@ fclist(FILE *f, Options ops, zlong first, zlong last, tdfmt = timebuf = NULL; } + /* xflags exclude events */ + if (OPT_ISSET(ops,'L')) { + xflags |= HIST_FOREIGN; + } + if (OPT_ISSET(ops,'I')) { + xflags |= HIST_READ; + } + for (;;) { - if (!OPT_ISSET(ops,'L') || !(ent->node.flags & HIST_FOREIGN)) - s = dupstring(ent->node.nam); - else + if (ent->node.flags & xflags) s = NULL; + else + s = dupstring(ent->node.nam); /* this if does the pattern matching, if required */ if (s && (!pprog || pattry(pprog, s))) { /* perform substitution */ @@ -1743,9 +1783,12 @@ fclist(FILE *f, Options ops, zlong first, zlong last, command, if required */ if (tdfmt != NULL) { struct tm *ltm; + int len; ltm = localtime(&ent->stim); - if (ztrftime(timebuf, 256, tdfmt, ltm, 0L)) - fprintf(f, "%s ", timebuf); + if ((len = ztrftime(timebuf, 256, tdfmt, ltm, 0L)) >= 0) { + fwrite(timebuf, 1, len, f); + fprintf(f, " "); + } } /* display the time taken by the command, if required */ if (OPT_ISSET(ops,'D')) { @@ -1782,7 +1825,7 @@ fclist(FILE *f, Options ops, zlong first, zlong last, if (!fclistdone) { if (subs) zwarnnam("fc", "no substitutions performed"); - else if (OPT_ISSET(ops,'L') || pprog) + else if (xflags || pprog) zwarnnam("fc", "no matching events found"); return 1; } @@ -1816,13 +1859,22 @@ fcedit(char *ename, char *fn) /**/ static Asgment -getasg(char *s) +getasg(char ***argvp, LinkList assigns) { + char *s = **argvp; static struct asgment asg; /* sanity check for valid argument */ - if (!s) + if (!s) { + if (assigns) { + Asgment asgp = (Asgment)firstnode(assigns); + if (!asgp) + return NULL; + (void)uremnode(assigns, &asgp->node); + return asgp; + } return NULL; + } /* check if name is empty */ if (*s == '=') { @@ -1830,6 +1882,7 @@ getasg(char *s) return NULL; } asg.name = s; + asg.is_array = 0; /* search for `=' */ for (; *s && *s != '='; s++); @@ -1837,11 +1890,12 @@ getasg(char *s) /* found `=', so return with a value */ if (*s) { *s = '\0'; - asg.value = s + 1; + asg.value.scalar = s + 1; } else { /* didn't find `=', so we only have a name */ - asg.value = NULL; + asg.value.scalar = NULL; } + (*argvp)++; return &asg; } @@ -1923,10 +1977,10 @@ typeset_setwidth(const char * name, Param pm, Options ops, int on, int always) /**/ static Param typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), - int on, int off, int roff, char *value, Param altpm, + int on, int off, int roff, Asgment asg, Param altpm, Options ops, int joinchar) { - int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly; + int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0; char *subscript; /* @@ -1971,7 +2025,24 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), /* attempting a type conversion, or making a tied colonarray? */ tc = 0; - if (usepm || newspecial != NS_NONE) { + if (ASG_ARRAYP(asg) && PM_TYPE(on) == PM_SCALAR && + !(usepm && (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))) + on |= PM_ARRAY; + if (usepm && ASG_ARRAYP(asg) && newspecial == NS_NONE && + PM_TYPE(pm->node.flags) != PM_ARRAY && + PM_TYPE(pm->node.flags) != PM_HASHED) { + if (on & (PM_EFLOAT|PM_FFLOAT|PM_INTEGER)) { + zerrnam(cname, "%s: can't assign array value to non-array", pname); + return NULL; + } + if (pm->node.flags & PM_SPECIAL) { + zerrnam(cname, "%s: can't assign array value to non-array special", pname); + return NULL; + } + tc = 1; + usepm = 0; + } + else if (usepm || newspecial != NS_NONE) { int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) & (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED| PM_ARRAY|PM_TIED|PM_AUTOLOAD); @@ -2019,7 +2090,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), tc = 0; /* but don't do a normal conversion */ } } else if (!setsecondstype(pm, on, off)) { - if (value && !(pm = setsparam(pname, ztrdup(value)))) + if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar)))) return NULL; usepm = 1; err = 0; @@ -2045,7 +2116,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * Stricter rules about retaining readonly attribute in this case. */ if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) && - !value) + !ASG_VALUEP(asg)) on |= PM_UNSET; else if (usepm && (pm->node.flags & PM_READONLY) && !(on & PM_READONLY)) { @@ -2064,8 +2135,15 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * ii. we are creating a new local parameter */ if (usepm) { + if (asg->is_array ? + !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) : + (asg->value.scalar && (PM_TYPE(pm->node.flags & + (PM_ARRAY|PM_HASHED))))) { + zerrnam(cname, "%s: inconsistent type for assignment", pname); + return NULL; + } on &= ~PM_LOCAL; - if (!on && !roff && !value) { + if (!on && !roff && !ASG_VALUEP(asg)) { if (OPT_ISSET(ops,'p')) paramtab->printnode(&pm->node, PRINT_TYPESET); else if (!OPT_ISSET(ops,'g') && @@ -2119,15 +2197,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) { if (pm->node.flags & PM_EXPORTED) { - if (!(pm->node.flags & PM_UNSET) && !pm->env && !value) + if (!(pm->node.flags & PM_UNSET) && !pm->env && !ASG_VALUEP(asg)) addenv(pm, getsparam(pname)); } else if (pm->env && !(pm->node.flags & PM_HASHELEM)) delenv(pm); - if (value && !(pm = setsparam(pname, ztrdup(value)))) + DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected"); + if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + return NULL; + } else if (asg->is_array) { + if (!(pm = setaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL)))) return NULL; - } else if (value) { - zwarnnam(cname, "can't assign new value for array %s", pname); - return NULL; } pm->node.flags |= (on & PM_READONLY); if (OPT_ISSET(ops,'p')) @@ -2135,6 +2216,13 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return pm; } + if (asg->is_array ? + !(on & (PM_ARRAY|PM_HASHED)) : + (asg->value.scalar && (on & (PM_ARRAY|PM_HASHED)))) { + zerrnam(cname, "%s: inconsistent type for assignment", pname); + return NULL; + } + /* * We're here either because we're creating a new parameter, * or we're adding a parameter at a different local level, @@ -2154,9 +2242,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), /* * Try to carry over a value, but not when changing from, * to, or between non-scalar types. + * + * (We can do better now, but it does have user-visible + * implications.) */ - if (!value && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) - value = dupstring(getsparam(pname)); + if (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) { + asg->value.scalar = dupstring(getsparam(pname)); + asg->is_array = 0; + } /* pname may point to pm->nam which is about to disappear */ pname = dupstring(pname); unsetparam_pm(pm, 0, 1); @@ -2235,7 +2328,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), zerrnam(cname, "%s: can't create readonly array elements", pname); return NULL; - } else if (on & PM_LOCAL) { + } else if ((on & PM_LOCAL) && locallevel) { *subscript = 0; pm = (Param) (paramtab == realparamtab ? gethashnode2(paramtab, pname) : @@ -2247,21 +2340,30 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return NULL; } } - if (PM_TYPE(on) == PM_SCALAR) { + if (PM_TYPE(on) == PM_SCALAR && !ASG_ARRAYP(asg)) { /* * This will either complain about bad identifiers, or will set * a hash element or array slice. This once worked by accident, * creating a stray parameter along the way via createparam(), * now called below in the isident() branch. */ - if (!(pm = setsparam(pname, ztrdup(value ? value : "")))) + if (!(pm = setsparam(pname, ztrdup(asg->value.scalar ? asg->value.scalar : "")))) return NULL; - value = NULL; + dont_set = 1; + asg->is_array = 0; + keeplocal = 0; + on = pm->node.flags; + } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) { + if (!(pm = setaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL)))) + return NULL; + dont_set = 1; keeplocal = 0; on = pm->node.flags; } else { zerrnam(cname, - "%s: array elements must be scalar", pname); + "%s: inconsistent array element or slice assignment", pname); return NULL; } } @@ -2327,10 +2429,19 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), pm->level = keeplocal; else if (on & PM_LOCAL) pm->level = locallevel; - if (value && !(pm->node.flags & (PM_ARRAY|PM_HASHED))) { + if (ASG_VALUEP(asg) && !dont_set) { Param ipm = pm; - if (!(pm = setsparam(pname, ztrdup(value)))) - return NULL; + if (pm->node.flags & (PM_ARRAY|PM_HASHED)) { + DPUTS(!ASG_ARRAYP(asg), "BUG: inconsistent scalar value for array"); + if (!(pm=setaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL)))) + return NULL; + } else { + DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar"); + if (!(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + return NULL; + } if (pm != ipm) { DPUTS(ipm->node.flags != pm->node.flags, "BUG: parameter recreated with wrong flags"); @@ -2367,12 +2478,6 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } } pm->node.flags |= (on & PM_READONLY); - if (value && (pm->node.flags & (PM_ARRAY|PM_HASHED))) { - zerrnam(cname, "%s: can't assign initial value for array", pname); - /* the only safe thing to do here seems to be unset the param */ - unsetparam_pm(pm, 0, 1); - return NULL; - } if (OPT_ISSET(ops,'p')) paramtab->printnode(&pm->node, PRINT_TYPESET); @@ -2380,11 +2485,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return pm; } -/* declare, export, integer, local, readonly, typeset */ +/* + * declare, export, float, integer, local, readonly, typeset + * + * Note the difference in interface from most builtins, covered by the + * BINF_ASSIGN builtin flag. This is only made use of by builtins + * called by reserved word, which only covers declare, local, readonly + * and typeset. Otherwise assigns is NULL. + */ /**/ int -bin_typeset(char *name, char **argv, Options ops, int func) +bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) { Param pm; Asgment asg; @@ -2393,6 +2505,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) int on = 0, off = 0, roff, bit = PM_ARRAY; int i; int returnval = 0, printflags = 0; + int hasargs; /* hash -f is really the builtin `functions' */ if (OPT_ISSET(ops,'f')) @@ -2445,7 +2558,8 @@ bin_typeset(char *name, char **argv, Options ops, int func) /* Given no arguments, list whatever the options specify. */ if (OPT_ISSET(ops,'p')) printflags |= PRINT_TYPESET; - if (!*argv) { + hasargs = *argv != NULL || (assigns && firstnode(assigns)); + if (!hasargs) { if (!OPT_ISSET(ops,'p')) { if (!(on|roff)) printflags |= PRINT_TYPE; @@ -2464,9 +2578,9 @@ bin_typeset(char *name, char **argv, Options ops, int func) if (on & PM_TIED) { Param apm; - struct asgment asg0; - char *oldval = NULL; - int joinchar; + struct asgment asg0, asg2; + char *oldval = NULL, *joinstr; + int joinchar, nargs; if (OPT_ISSET(ops,'m')) { zwarnnam(name, "incompatible options for -T"); @@ -2474,34 +2588,41 @@ bin_typeset(char *name, char **argv, Options ops, int func) return 1; } on &= ~off; - if (!argv[1] || (argv[2] && argv[3])) { + nargs = arrlen(argv) + (assigns ? countlinknodes(assigns) : 0); + if (nargs < 2) { zwarnnam(name, "-T requires names of scalar and array"); unqueue_signals(); return 1; } + if (nargs > 3) { + zwarnnam(name, "too many arguments for -T"); + unqueue_signals(); + return 1; + } - /* - * Third argument, if given, is character used to join - * the elements of the array in the scalar. - */ - if (!argv[2]) - joinchar = ':'; - else if (!*argv[2]) - joinchar = 0; - else if (*argv[2] == Meta) - joinchar = argv[2][1] ^ 32; - else - joinchar = *argv[2]; - - if (!(asg = getasg(argv[0]))) { + if (!(asg = getasg(&argv, assigns))) { unqueue_signals(); return 1; } asg0 = *asg; - if (!(asg = getasg(argv[1]))) { + if (ASG_ARRAYP(&asg0)) { + unqueue_signals(); + zwarnnam(name, "first argument of tie must be scalar: %s", + asg0.name); + return 1; + } + + if (!(asg = getasg(&argv, assigns))) { + unqueue_signals(); + return 1; + } + if (!ASG_ARRAYP(asg) && asg->value.scalar) { unqueue_signals(); + zwarnnam(name, "second argument of tie must be array: %s", + asg->name); return 1; } + if (!strcmp(asg0.name, asg->name)) { unqueue_signals(); zerrnam(name, "can't tie a variable to itself: %s", asg0.name); @@ -2512,6 +2633,36 @@ bin_typeset(char *name, char **argv, Options ops, int func) zerrnam(name, "can't tie array elements: %s", asg0.name); return 1; } + if (ASG_VALUEP(asg) && ASG_VALUEP(&asg0)) { + unqueue_signals(); + zerrnam(name, "only one tied parameter can have value: %s", asg0.name); + return 1; + } + + /* + * Third argument, if given, is character used to join + * the elements of the array in the scalar. + */ + if (*argv) + joinstr = *argv; + else if (assigns && firstnode(assigns)) { + Asgment nextasg = (Asgment)firstnode(assigns); + if (ASG_ARRAYP(nextasg) || ASG_VALUEP(nextasg)) { + zwarnnam(name, "third argument of tie must be join character"); + unqueue_signals(); + return 1; + } + joinstr = nextasg->name; + } else + joinstr = NULL; + if (!joinstr) + joinchar = ':'; + else if (!*joinstr) + joinchar = 0; + else if (*joinstr == Meta) + joinchar = joinstr[1] ^ 32; + else + joinchar = *joinstr; /* * Keep the old value of the scalar. We need to do this * here as if it is already tied to the same array it @@ -2526,15 +2677,19 @@ bin_typeset(char *name, char **argv, Options ops, int func) && (locallevel == pm->level || !(on & PM_LOCAL))) { if (pm->node.flags & PM_TIED) { unqueue_signals(); - if (!strcmp(asg->name, pm->ename)) { + if (PM_TYPE(pm->node.flags) != PM_SCALAR) { + zwarnnam(name, "already tied as non-scalar: %s", asg0.name); + } else if (!strcmp(asg->name, pm->ename)) { /* * Already tied in the fashion requested. */ struct tieddata *tdp = (struct tieddata*)pm->u.data; /* Update join character */ tdp->joinchar = joinchar; - if (asg0.value) - setsparam(asg0.name, ztrdup(asg0.value)); + if (asg0.value.scalar) + setsparam(asg0.name, ztrdup(asg0.value.scalar)); + else if (asg->value.array) + setaparam(asg->name, zlinklist2array(asg->value.array)); return 0; } else { zwarnnam(name, "can't tie already tied scalar: %s", @@ -2542,7 +2697,8 @@ bin_typeset(char *name, char **argv, Options ops, int func) } return 1; } - if (!asg0.value && !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))) + if (!asg0.value.scalar && !asg->value.array && + !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))) oldval = ztrdup(getsparam(asg0.name)); on |= (pm->node.flags & PM_EXPORTED); } @@ -2550,12 +2706,18 @@ bin_typeset(char *name, char **argv, Options ops, int func) * Create the tied array; this is normal except that * it has the PM_TIED flag set. Do it first because * we need the address. + * + * Don't attempt to set it yet, it's too early + * to be exported properly. */ + asg2.name = asg->name; + asg2.is_array = 0; + asg2.value.array = (LinkList)0; if (!(apm=typeset_single(name, asg->name, (Param)paramtab->getnode(paramtab, asg->name), func, (on | PM_ARRAY) & ~PM_EXPORTED, - off, roff, asg->value, NULL, ops, 0))) { + off, roff, &asg2, NULL, ops, 0))) { if (oldval) zsfree(oldval); unqueue_signals(); @@ -2568,7 +2730,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) if (!(pm=typeset_single(name, asg0.name, (Param)paramtab->getnode(paramtab, asg0.name), - func, on, off, roff, asg0.value, apm, + func, on, off, roff, &asg0, apm, ops, joinchar))) { if (oldval) zsfree(oldval); @@ -2587,7 +2749,9 @@ bin_typeset(char *name, char **argv, Options ops, int func) if (apm->ename) zsfree(apm->ename); apm->ename = ztrdup(asg0.name); - if (oldval) + if (asg->value.array) + setaparam(asg->name, zlinklist2array(asg->value.array)); + else if (oldval) setsparam(asg0.name, oldval); unqueue_signals(); @@ -2607,18 +2771,18 @@ bin_typeset(char *name, char **argv, Options ops, int func) printflags |= PRINT_NAMEONLY; } - while ((asg = getasg(*argv++))) { + while ((asg = getasg(&argv, assigns))) { LinkList pmlist = newlinklist(); LinkNode pmnode; tokenize(asg->name); /* expand argument */ if (!(pprog = patcompile(asg->name, 0, NULL))) { untokenize(asg->name); - zwarnnam(name, "bad pattern : %s", argv[-1]); + zwarnnam(name, "bad pattern : %s", asg->name); returnval = 1; continue; } - if (OPT_PLUS(ops,'m') && !asg->value) { + if (OPT_PLUS(ops,'m') && !ASG_VALUEP(asg)) { scanmatchtable(paramtab, pprog, 1, on|roff, 0, paramtab->printnode, printflags); continue; @@ -2644,7 +2808,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) { pm = (Param) getdata(pmnode); if (!typeset_single(name, pm->node.nam, pm, func, on, off, roff, - asg->value, NULL, ops, 0)) + asg, NULL, ops, 0)) returnval = 1; } } @@ -2653,7 +2817,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) } /* Take arguments literally. Don't glob */ - while ((asg = getasg(*argv++))) { + while ((asg = getasg(&argv, assigns))) { HashNode hn = (paramtab == realparamtab ? gethashnode2(paramtab, asg->name) : paramtab->getnode(paramtab, asg->name)); @@ -2667,7 +2831,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) continue; } if (!typeset_single(name, asg->name, (Param)hn, - func, on, off, roff, asg->value, NULL, + func, on, off, roff, asg, NULL, ops, 0)) returnval = 1; } @@ -2749,7 +2913,7 @@ bin_functions(char *name, char **argv, Options ops, int func) Patprog pprog; Shfunc shf; int i, returnval = 0; - int on = 0, off = 0, pflags = 0, roff; + int on = 0, off = 0, pflags = 0, roff, expand = 0; /* Do we have any flags defined? */ if (OPT_PLUS(ops,'u')) @@ -2785,11 +2949,23 @@ 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') || *argv || !scriptname))) { zwarnnam(name, "invalid option(s)"); return 1; } + if (OPT_ISSET(ops,'x')) { + char *eptr; + expand = (int)zstrtol(OPT_ARG(ops,'x'), &eptr, 10); + if (*eptr) { + zwarnnam(name, "number expected after -x"); + return 1; + } + if (expand == 0) /* no indentation at all */ + expand = -1; + } + if (OPT_PLUS(ops,'f') || roff || OPT_ISSET(ops,'+')) pflags |= PRINT_NAMEONLY; @@ -2948,8 +3124,8 @@ bin_functions(char *name, char **argv, Options ops, int func) } else { if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u')) on &= ~PM_UNDEFINED; - scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode, - pflags); + scanshfunc(1, on|off, DISABLED, shfunctab->printnode, + pflags, expand); } unqueue_signals(); return ret; @@ -2965,8 +3141,8 @@ bin_functions(char *name, char **argv, Options ops, int func) /* with no options, just print all functions matching the glob pattern */ queue_signals(); if (!(on|off) && !OPT_ISSET(ops,'X')) { - scanmatchtable(shfunctab, pprog, 1, 0, DISABLED, - shfunctab->printnode, pflags); + scanmatchshfunc(pprog, 1, 0, DISABLED, + shfunctab->printnode, pflags, expand); } else { /* apply the options to all functions matching the glob pattern */ for (i = 0; i < shfunctab->hsize; i++) { @@ -3008,7 +3184,7 @@ bin_functions(char *name, char **argv, Options ops, int func) returnval = 1; } else /* no flags, so just print */ - shfunctab->printnode(&shf->node, pflags); + printshfuncexpand(&shf->node, pflags, expand); } else if (on & PM_UNDEFINED) { int signum = -1, ok = 1; @@ -3222,6 +3398,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) int aliasflags; int csh, all, v, wd; int informed = 0; + int expand = 0; char *cnam, **allmatched = 0; /* Check some option information */ @@ -3230,6 +3407,17 @@ bin_whence(char *nam, char **argv, Options ops, int func) all = OPT_ISSET(ops,'a'); wd = OPT_ISSET(ops,'w'); + if (OPT_ISSET(ops,'x')) { + char *eptr; + expand = (int)zstrtol(OPT_ARG(ops,'x'), &eptr, 10); + if (*eptr) { + zwarnnam(nam, "number expected after -x"); + return 1; + } + if (expand == 0) /* no indentation at all */ + expand = -1; + } + if (OPT_ISSET(ops,'w')) printflags |= PRINT_WHENCE_WORD; else if (OPT_ISSET(ops,'c')) @@ -3286,8 +3474,8 @@ bin_whence(char *nam, char **argv, Options ops, int func) /* and shell functions... */ informed += - scanmatchtable(shfunctab, pprog, 1, 0, DISABLED, - shfunctab->printnode, printflags); + scanmatchshfunc(pprog, 1, 0, DISABLED, + shfunctab->printnode, printflags, expand); /* and builtins. */ informed += @@ -3342,7 +3530,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) } /* Look for shell function */ if ((hn = shfunctab->getnode(shfunctab, *argv))) { - shfunctab->printnode(hn, printflags); + printshfuncexpand(hn, printflags, expand); informed = 1; if (!all) continue; @@ -3486,7 +3674,7 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func)) } queue_signals(); - for (;*argv;++argv) { + while (*argv) { void *hn; if (OPT_ISSET(ops,'m')) { /* with the -m option, treat the argument as a glob pattern */ @@ -3501,12 +3689,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func)) } continue; } - if (!(asg = getasg(*argv))) { + if (!(asg = getasg(&argv, NULL))) { zwarnnam(name, "bad assignment"); returnval = 1; - } else if (asg->value) { + } else if (ASG_VALUEP(asg)) { if(isset(RESTRICTED)) { - zwarnnam(name, "restricted: %s", asg->value); + zwarnnam(name, "restricted: %s", asg->value.scalar); returnval = 1; } else { /* The argument is of the form foo=bar, * @@ -3522,12 +3710,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func)) } else { Nameddir nd = hn = zshcalloc(sizeof *nd); nd->node.flags = 0; - nd->dir = ztrdup(asg->value); + nd->dir = ztrdup(asg->value.scalar); } } else { Cmdnam cn = hn = zshcalloc(sizeof *cn); cn->node.flags = HASHED; - cn->u.cmd = ztrdup(asg->value); + cn->u.cmd = ztrdup(asg->value.scalar); } ht->addnode(ht, ztrdup(asg->name), hn); if(OPT_ISSET(ops,'v')) @@ -3733,12 +3921,12 @@ bin_alias(char *name, char **argv, Options ops, UNUSED(int func)) /* Take arguments literally. Don't glob */ queue_signals(); - while ((asg = getasg(*argv++))) { - if (asg->value && !OPT_ISSET(ops,'L')) { + while ((asg = getasg(&argv, NULL))) { + if (asg->value.scalar && !OPT_ISSET(ops,'L')) { /* The argument is of the form foo=bar and we are not * * forcing a listing with -L, so define an alias */ ht->addnode(ht, ztrdup(asg->name), - createaliasnode(ztrdup(asg->value), flags1)); + createaliasnode(ztrdup(asg->value.scalar), flags1)); } else if ((a = (Alias) ht->getnode(ht, asg->name))) { /* display alias if appropriate */ if (!type_opts || ht == sufaliastab || @@ -4208,11 +4396,40 @@ bin_print(char *name, char **args, Options ops, int func) return 0; } - for (; *args; args++, len++) { - fwrite(*args, *len, 1, fout); - if (args[1]) - fputc(OPT_ISSET(ops,'l') ? '\n' : - OPT_ISSET(ops,'N') ? '\0' : ' ', fout); + if (OPT_HASARG(ops, 'x') || OPT_HASARG(ops, 'X')) { + char *eptr; + int expand, startpos = 0; + int all = OPT_HASARG(ops, 'X'); + char *xarg = all ? OPT_ARG(ops, 'X') : OPT_ARG(ops, 'x'); + + expand = (int)zstrtol(xarg, &eptr, 10); + if (*eptr || expand <= 0) { + zwarnnam(name, "positive integer expected after -%c: %s", 'x', + xarg); + return 1; + } + for (; *args; args++, len++) { + startpos = zexpandtabs(*args, *len, expand, startpos, fout, + all); + if (args[1]) { + if (OPT_ISSET(ops, 'l')) { + fputc('\n', fout); + startpos = 0; + } else if (OPT_ISSET(ops,'N')) { + fputc('\0', fout); + } else { + fputc(' ', fout); + startpos++; + } + } + } + } else { + for (; *args; args++, len++) { + fwrite(*args, *len, 1, fout); + if (args[1]) + fputc(OPT_ISSET(ops,'l') ? '\n' : + OPT_ISSET(ops,'N') ? '\0' : ' ', fout); + } } if (!(OPT_ISSET(ops,'n') || nnl)) fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout); @@ -4461,7 +4678,7 @@ bin_print(char *name, char **args, Options ops, int func) lleft -= chars; ptr += chars; } - if (width > 0 && flags[2]) width = -width; + if (width > 0 && flags[3]) width = -width; if (width > 0 && lchars < width) count += fprintf(fout, "%*c", width - lchars, ' '); count += fwrite(b, 1, lbytes, fout); @@ -4525,7 +4742,7 @@ bin_print(char *name, char **args, Options ops, int func) convchar_t cc; #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE)) { - mb_metacharinit(); + mb_charinit(); (void)mb_metacharlenconv(metafy(curarg+1, curlen-1, META_USEHEAP), &cc); } @@ -5500,7 +5717,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) wint_t wi; if (isset(MULTIBYTE)) { - mb_metacharinit(); + mb_charinit(); (void)mb_metacharlenconv(delimstr, &wi); } else @@ -6355,8 +6572,14 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) zwarnnam(name, "undefined signal: %s", *argv); break; } - if (!strcmp(sigs[sig], *argv)) + if (idigit(**argv) || + !strcmp(sigs[sig], *argv) || + (!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) { + /* The signal was specified by number or by canonical name (with + * or without SIG prefix). + */ flags = 0; + } else { /* * Record that the signal is used under an assumed name. diff --git a/Src/compat.c b/Src/compat.c index b3a8b063c..db468529a 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -454,8 +454,13 @@ zgetcwd(void) return ret; } -/* chdir with arbitrary long pathname. Returns 0 on success, -1 on normal * - * failure and -2 when chdir failed and the current directory is lost. */ +/* + * chdir with arbitrary long pathname. Returns 0 on success, -1 on normal * + * failure and -2 when chdir failed and the current directory is lost. + * + * This is to be treated as if at system level, so dir is unmetafied but + * terminated by a NULL. + */ /**/ mod_export int diff --git a/Src/context.c b/Src/context.c index 1b8741f46..2dc8d3b89 100644 --- a/Src/context.c +++ b/Src/context.c @@ -53,6 +53,8 @@ zcontext_save_partial(int parts) { struct context_stack *cs; + queue_signals(); + cs = (struct context_stack *)malloc(sizeof(struct context_stack)); if (parts & ZCONTEXT_HIST) { @@ -67,6 +69,8 @@ zcontext_save_partial(int parts) cs->next = cstack; cstack = cs; + + unqueue_signals(); } /* save context in full */ diff --git a/Src/exec.c b/Src/exec.c index 9f163a627..45f1c66f0 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1115,15 +1115,20 @@ execsimple(Estate state) if (code == WC_ASSIGN) { cmdoutval = 0; addvars(state, state->pc - 1, 0); + setunderscore(""); if (isset(XTRACE)) { fputc('\n', xtrerr); fflush(xtrerr); } lv = (errflag ? errflag : cmdoutval); - } else if (code == WC_FUNCDEF) { - lv = execfuncdef(state, NULL); } else { - lv = (execfuncs[code - WC_CURSH])(state, 0); + int q = queue_signal_level(); + dont_queue_signals(); + if (code == WC_FUNCDEF) + lv = execfuncdef(state, NULL); + else + lv = (execfuncs[code - WC_CURSH])(state, 0); + restore_queue_signals(q); } thisjob = otj; @@ -1157,6 +1162,8 @@ execlist(Estate state, int dont_change_job, int exiting) */ int oldnoerrexit = noerrexit; + queue_signals(); + cj = thisjob; old_pline_level = pline_level; old_list_pipe = list_pipe; @@ -1350,7 +1357,13 @@ execlist(Estate state, int dont_change_job, int exiting) state->pc--; sublist_done: - noerrexit = oldnoerrexit; + /* + * See hairy code near the end of execif() for the + * following. "noerrexit == 2" only applies until + * we hit execcmd on the way down. We're now + * on the way back up, so don't restore it. + */ + noerrexit = (oldnoerrexit == 2) ? 0 : oldnoerrexit; if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) { /* @@ -1421,6 +1434,8 @@ sublist_done: /* Make sure this doesn't get executed again. */ sigtrapped[SIGEXIT] = 0; } + + unqueue_signals(); } /* Execute a pipeline. * @@ -1449,6 +1464,14 @@ execpline(Estate state, wordcode slcode, int how, int last1) else if (slflags & WC_SUBLIST_NOT) last1 = 0; + /* If trap handlers are allowed to run here, they may start another + * external job in the middle of us starting this one, which can + * result in jobs being reaped before their job table entries have + * been initialized, which in turn leads to waiting forever for + * jobs that no longer exist. So don't do that. + */ + queue_signals(); + pj = thisjob; ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0; child_block(); @@ -1461,6 +1484,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) */ if ((thisjob = newjob = initjob()) == -1) { child_unblock(); + unqueue_signals(); return 1; } if (how & Z_TIMED) @@ -1516,6 +1540,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) else spawnjob(); child_unblock(); + unqueue_signals(); /* Executing background code resets shell status */ return lastval = 0; } else { @@ -1573,15 +1598,18 @@ execpline(Estate state, wordcode slcode, int how, int last1) } if (!(jn->stat & STAT_LOCKED)) { updated = hasprocs(thisjob); - waitjobs(); + waitjobs(); /* deals with signal queue */ child_block(); } else updated = 0; if (!updated && list_pipe_job && hasprocs(list_pipe_job) && !(jobtab[list_pipe_job].stat & STAT_STOPPED)) { + int q = queue_signal_level(); child_unblock(); child_block(); + dont_queue_signals(); + restore_queue_signals(q); } if (list_pipe_child && jn->stat & STAT_DONE && @@ -1665,6 +1693,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) break; } child_unblock(); + unqueue_signals(); if (list_pipe && (lastval & 0200) && pj >= 0 && (!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) { @@ -2264,14 +2293,14 @@ addvars(Estate state, Wordcode pc, int addflags) state->pc = opc; return; } - if (!isstr || (isset(GLOBASSIGN) && + if (!isstr || (isset(GLOBASSIGN) && isstr && haswilds((char *)getdata(firstnode(vl))))) { globlist(vl, 0); /* Unset the parameter to force it to be recreated * as either scalar or array depending on how many * matches were found for the glob. */ - if (isset(GLOBASSIGN)) + if (isset(GLOBASSIGN) && isstr) unsetparam(name); } if (errflag) { @@ -2436,13 +2465,13 @@ execcmd(Estate state, int input, int output, int how, int last1) char *text; int save[10]; int fil, dfil, is_cursh, type, do_exec = 0, redir_err = 0, i, htok = 0; - int nullexec = 0, assign = 0, forked = 0; + int nullexec = 0, assign = 0, forked = 0, postassigns = 0; int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0; /* Various flags to the command. */ int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1; LinkList redir; wordcode code; - Wordcode beg = state->pc, varspc; + Wordcode beg = state->pc, varspc, assignspc = (Wordcode)0; FILE *oxtrerr = xtrerr, *newxtrerr = NULL; doneps4 = 0; @@ -2463,8 +2492,28 @@ execcmd(Estate state, int input, int output, int how, int last1) /* It would be nice if we could use EC_DUPTOK instead of EC_DUP here. * But for that we would need to check/change all builtins so that * they don't modify their argument strings. */ - args = (type == WC_SIMPLE ? - ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL); + switch (type) { + case WC_SIMPLE: + args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok); + break; + + case WC_TYPESET: + args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP, &htok); + postassigns = *state->pc++; + assignspc = state->pc; + for (i = 0; i < postassigns; i++) { + code = *state->pc; + DPUTS(wc_code(code) != WC_ASSIGN, + "BUG: miscounted typeset assignments"); + state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(code) + 2); + } + break; + + default: + args = NULL; + } + /* * If assignment but no command get the status from variable * assignment. @@ -2487,7 +2536,7 @@ execcmd(Estate state, int input, int output, int how, int last1) /* If the command begins with `%', then assume it is a * * reference to a job in the job table. */ - if (type == WC_SIMPLE && args && nonempty(args) && + if ((type == WC_SIMPLE || type == WC_TYPESET) && args && nonempty(args) && *(char *)peekfirst(args) == '%') { if (how & Z_DISOWN) { oautocont = opts[AUTOCONTINUE]; @@ -2516,20 +2565,32 @@ execcmd(Estate state, int input, int output, int how, int last1) * command if it contains some tokens (e.g. x=ex; ${x}port), so this * * only works in simple cases. has_token() is called to make sure * * this really is a simple case. */ - if (type == WC_SIMPLE) { + if (type == WC_SIMPLE || type == WC_TYPESET) { while (args && nonempty(args)) { char *cmdarg = (char *) peekfirst(args); checked = !has_token(cmdarg); if (!checked) break; - if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) && - (hn = shfunctab->getnode(shfunctab, cmdarg))) { - is_shfunc = 1; - break; - } - if (!(hn = builtintab->getnode(builtintab, cmdarg))) { - checked = !(cflags & BINF_BUILTIN); - break; + if (type == WC_TYPESET && + (hn = builtintab->getnode2(builtintab, cmdarg))) { + /* + * If reserved word for typeset command found (and so + * enabled), use regardless of whether builtin is + * enabled as we share the implementation. + * + * Reserved words take precedence over shell functions. + */ + checked = 1; + } else { + if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) && + (hn = shfunctab->getnode(shfunctab, cmdarg))) { + is_shfunc = 1; + break; + } + if (!(hn = builtintab->getnode(builtintab, cmdarg))) { + checked = !(cflags & BINF_BUILTIN); + break; + } } orig_cflags |= cflags; cflags &= ~BINF_BUILTIN & ~BINF_COMMAND; @@ -2660,7 +2721,7 @@ execcmd(Estate state, int input, int output, int how, int last1) if (args && htok) prefork(args, esprefork); - if (type == WC_SIMPLE) { + if (type == WC_SIMPLE || type == WC_TYPESET) { int unglobbed = 0; for (;;) { @@ -2896,7 +2957,7 @@ execcmd(Estate state, int input, int output, int how, int last1) return; } - if (type == WC_SIMPLE && !nullexec) { + if ((type == WC_SIMPLE || type == WC_TYPESET) && !nullexec) { char *s; char trycd = (isset(AUTOCD) && isset(SHINSTDIN) && (!redir || empty(redir)) && args && !empty(args) && @@ -3008,6 +3069,7 @@ execcmd(Estate state, int input, int output, int how, int last1) addproc(pid, text, 0, &bgtime); if (oautocont >= 0) opts[AUTOCONTINUE] = oautocont; + pipecleanfilelist(jobtab[thisjob].filelist, 1); return; } /* pid == 0 */ @@ -3282,7 +3344,8 @@ execcmd(Estate state, int input, int output, int how, int last1) fil = -1; else if (IS_APPEND_REDIR(fn->type)) fil = open(unmeta(fn->name), - (unset(CLOBBER) && !IS_CLOBBER_REDIR(fn->type)) ? + ((unset(CLOBBER) && unset(APPENDCREATE)) && + !IS_CLOBBER_REDIR(fn->type)) ? O_WRONLY | O_APPEND | O_NOCTTY : O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0666); else @@ -3350,6 +3413,7 @@ execcmd(Estate state, int input, int output, int how, int last1) fflush(xtrerr); } } else if (isset(EXECOPT) && !errflag) { + int q = queue_signal_level(); /* * We delay the entersubsh() to here when we are exec'ing * the current shell (including a fake exec to run a builtin then @@ -3390,11 +3454,14 @@ execcmd(Estate state, int input, int output, int how, int last1) } else redir_prog = NULL; + dont_queue_signals(); lastval = execfuncdef(state, redir_prog); + restore_queue_signals(q); } else if (type >= WC_CURSH) { if (last1 == 1) do_exec = 1; + dont_queue_signals(); if (type == WC_AUTOFN) { /* * We pre-loaded this to get any redirs. @@ -3403,6 +3470,7 @@ execcmd(Estate state, int input, int output, int how, int last1) lastval = execautofn_basic(state, do_exec); } else lastval = (execfuncs[type - WC_CURSH])(state, do_exec); + restore_queue_signals(q); } else if (is_builtin || is_shfunc) { LinkList restorelist = 0, removelist = 0; /* builtin or shell function */ @@ -3452,13 +3520,126 @@ execcmd(Estate state, int input, int output, int how, int last1) if (is_shfunc) { /* It's a shell function */ - pipecleanfilelist(filelist); + pipecleanfilelist(filelist, 0); execshfunc((Shfunc) hn, args); } else { /* It's a builtin */ + LinkList assigns = (LinkList)0; if (forked) closem(FDT_INTERNAL); - lastval = execbuiltin(args, (Builtin) hn); + if (postassigns) { + Wordcode opc = state->pc; + state->pc = assignspc; + assigns = newlinklist(); + while (postassigns--) { + wordcode ac = *state->pc++; + char *name = ecgetstr(state, EC_DUPTOK, &htok); + Asgment asg; + local_list1(svl); + + DPUTS(wc_code(ac) != WC_ASSIGN, + "BUG: bad assignment list for typeset"); + if (htok) { + init_list1(svl, name); + if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR && + WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) { + char *data; + /* + * Special case: this is a name only, so + * it's not required to be a single + * expansion. Furthermore, for + * consistency with the builtin + * interface, it may expand into + * scalar assignments: + * ass=(one=two three=four) + * typeset a=b $ass + */ + /* Unused dummy value for name */ + (void)ecgetstr(state, EC_DUPTOK, &htok); + prefork(&svl, PREFORK_TYPESET); + if (errflag) { + state->pc = opc; + break; + } + globlist(&svl, 0); + if (errflag) { + state->pc = opc; + break; + } + while ((data = ugetnode(&svl))) { + char *ptr; + asg = (Asgment)zhalloc(sizeof(struct asgment)); + asg->is_array = 0; + if ((ptr = strchr(data, '='))) { + *ptr++ = '\0'; + asg->name = data; + asg->value.scalar = ptr; + } else { + asg->name = data; + asg->value.scalar = NULL; + } + uaddlinknode(assigns, &asg->node); + } + continue; + } + prefork(&svl, PREFORK_SINGLE); + name = empty(&svl) ? "" : + (char *)getdata(firstnode(&svl)); + } + untokenize(name); + asg = (Asgment)zhalloc(sizeof(struct asgment)); + asg->name = name; + if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR) { + char *val = ecgetstr(state, EC_DUPTOK, &htok); + asg->is_array = 0; + if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) { + /* Fake assignment, no value */ + asg->value.scalar = NULL; + } else { + if (htok) { + init_list1(svl, val); + prefork(&svl, PREFORK_SINGLE|PREFORK_ASSIGN); + if (errflag) { + state->pc = opc; + break; + } + /* + * No globassign for typeset + * arguments, thank you + */ + val = empty(&svl) ? "" : + (char *)getdata(firstnode(&svl)); + } + untokenize(val); + asg->value.scalar = val; + } + } else { + asg->is_array = 1; + asg->value.array = + ecgetlist(state, WC_ASSIGN_NUM(ac), + EC_DUPTOK, &htok); + if (asg->value.array) + { + prefork(asg->value.array, PREFORK_ASSIGN); + if (errflag) { + state->pc = opc; + break; + } + globlist(asg->value.array, 0); + if (errflag) { + state->pc = opc; + break; + } + } + } + + uaddlinknode(assigns, &asg->node); + } + state->pc = opc; + } + dont_queue_signals(); + lastval = execbuiltin(args, assigns, (Builtin) hn); + restore_queue_signals(q); fflush(stdout); if (save[1] == -2) { if (ferror(stdout)) { @@ -3500,7 +3681,7 @@ execcmd(Estate state, int input, int output, int how, int last1) if (!subsh && isset(RCS) && interact && !nohistsave) savehistfile(NULL, 1, HFILE_USE_OPTIONS); } - if (type == WC_SIMPLE) { + if (type == WC_SIMPLE || type == WC_TYPESET) { if (varspc) { int addflags = ADDVAR_EXPORT|ADDVAR_RESTRICT; if (forked) @@ -3531,7 +3712,7 @@ execcmd(Estate state, int input, int output, int how, int last1) DPUTS(varspc, "BUG: assignment before complex command"); list_pipe = 0; - pipecleanfilelist(filelist); + pipecleanfilelist(filelist, 0); /* If we're forked (and we should be), no need to return */ DPUTS(last1 != 1 && !forked, "BUG: not exiting?"); DPUTS(type != WC_SUBSH, "Not sure what we're doing."); @@ -4133,6 +4314,10 @@ namedpipe(void) { char *tnam = gettempname(NULL, 1); + if (!tnam) { + zerr("failed to create named pipe: %e", errno); + return NULL; + } # ifdef HAVE_MKFIFO if (mkfifo(tnam, 0600) < 0){ # else @@ -4664,11 +4849,9 @@ execshfunc(Shfunc shf, LinkList args) if ((osfc = sfcontext) == SFC_NONE) sfcontext = SFC_DIRECT; xtrerr = stderr; - unqueue_signals(); doshfunc(shf, args, 0); - queue_signals(); sfcontext = osfc; free(cmdstack); cmdstack = ocs; @@ -4883,6 +5066,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) static int funcdepth; #endif + queue_signals(); /* Lots of memory and global state changes coming */ + pushheap(); oargv0 = NULL; @@ -5105,6 +5290,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) } popheap(); + unqueue_signals(); + /* * Exit with a tidy up. * Only leave if we're at the end of the appropriate function --- @@ -5143,6 +5330,8 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name) int cont, ouu; char *ou; + queue_signals(); + ou = zalloc(ouu = underscoreused); if (ou) memcpy(ou, zunderscore, underscoreused); @@ -5164,12 +5353,14 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name) wrap = wrap->next; } startparamscope(); - execode(prog, 1, 0, "shfunc"); + execode(prog, 1, 0, "shfunc"); /* handles signal unqueueing */ if (ou) { setunderscore(ou); zfree(ou, ouu); } endparamscope(); + + unqueue_signals(); } /* Search fpath for an undefined function. Finds the file, and returns the * diff --git a/Src/glob.c b/Src/glob.c index 057d44a17..dea1bf50e 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -102,8 +102,14 @@ int badcshglob; /**/ int pathpos; /* position in pathbuf (needed by pattern code) */ +/* + * pathname buffer (needed by pattern code). + * It is currently believed the string in here is stored metafied and is + * unmetafied temporarily as needed by system calls. + */ + /**/ -char *pathbuf; /* pathname buffer (needed by pattern code) */ +char *pathbuf; typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figure. */ @@ -216,22 +222,26 @@ static struct globdata curglobdata; #define save_globstate(N) \ do { \ + queue_signals(); \ memcpy(&(N), &curglobdata, sizeof(struct globdata)); \ (N).gd_pathpos = pathpos; \ (N).gd_pathbuf = pathbuf; \ (N).gd_glob_pre = glob_pre; \ (N).gd_glob_suf = glob_suf; \ pathbuf = NULL; \ + unqueue_signals(); \ } while (0) #define restore_globstate(N) \ do { \ + queue_signals(); \ zfree(pathbuf, pathbufsz); \ memcpy(&curglobdata, &(N), sizeof(struct globdata)); \ pathpos = (N).gd_pathpos; \ pathbuf = (N).gd_pathbuf; \ glob_pre = (N).gd_glob_pre; \ glob_suf = (N).gd_glob_suf; \ + unqueue_signals(); \ } while (0) /* pathname component in filename patterns */ @@ -253,7 +263,7 @@ addpath(char *s, int l) { DPUTS(!pathbuf, "BUG: pathbuf not initialised"); while (pathpos + l + 1 >= pathbufsz) - pathbuf = realloc(pathbuf, pathbufsz *= 2); + pathbuf = zrealloc(pathbuf, pathbufsz *= 2); while (l--) pathbuf[pathpos++] = *s++; pathbuf[pathpos++] = '/'; @@ -491,7 +501,7 @@ scanner(Complist q, int shortcircuit) if (l >= PATH_MAX) return; - err = lchdir(pathbuf + pathbufcwd, &ds, 0); + err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0); if (err == -1) return; if (err) { @@ -513,7 +523,7 @@ scanner(Complist q, int shortcircuit) else if (!strcmp(str, "..")) { struct stat sc, sr; - add = (stat("/", &sr) || stat(pathbuf, &sc) || + add = (stat("/", &sr) || stat(unmeta(pathbuf), &sc) || sr.st_ino != sc.st_ino || sr.st_dev != sc.st_dev); } @@ -560,7 +570,7 @@ scanner(Complist q, int shortcircuit) DPUTS(pathpos == pathbufcwd, "BUG: filename longer than PATH_MAX"); - err = lchdir(pathbuf + pathbufcwd, &ds, 0); + err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0); if (err == -1) break; if (err) { @@ -2237,7 +2247,7 @@ xpandbraces(LinkList list, LinkNode *np) #ifdef MULTIBYTE_SUPPORT char *ncptr; int nclen; - mb_metacharinit(); + mb_charinit(); ncptr = wcs_nicechar(cend, NULL, NULL); nclen = strlen(ncptr); p = zhalloc(lenalloc + nclen); @@ -2805,7 +2815,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * ... now we know whether it's worth looking for the * shortest, which we do by brute force. */ - mb_metacharinit(); + mb_charinit(); for (t = s, umlen = 0; t < s + mlen; ) { set_pat_end(p, *t); if (pattrylen(p, s, t - s, umlen, 0)) { @@ -2831,7 +2841,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * so that match, mbegin, mend and MATCH, MBEGIN, MEND are * correct. */ - mb_metacharinit(); + mb_charinit(); tmatch = NULL; for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { set_pat_start(p, t-s); @@ -2855,7 +2865,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, /* Largest possible match at tail of string: * * move forward along string until we get a match. * * Again there's no optimisation. */ - mb_metacharinit(); + mb_charinit(); for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { set_pat_start(p, t-s); if (pattrylen(p, t, s + l - t, umlen, ioff)) { @@ -2889,7 +2899,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, } ioff = 0; /* offset into string */ umlen = umltot; - mb_metacharinit(); + mb_charinit(); do { /* loop over all matches for global substitution */ matched = 0; @@ -2986,7 +2996,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, */ nmatches = 0; tmatch = NULL; - mb_metacharinit(); + mb_charinit(); for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { set_pat_start(p, t-s); if (pattrylen(p, t, s + l - t, umlen, ioff)) { @@ -3002,7 +3012,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * We need to find the n'th last match. */ n = nmatches - n; - mb_metacharinit(); + mb_charinit(); for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { set_pat_start(p, t-s); if (pattrylen(p, t, s + l - t, umlen, ioff) && diff --git a/Src/hashtable.c b/Src/hashtable.c index ab381cc6a..90739a882 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -937,13 +937,17 @@ printshfuncnode(HashNode hn, int printflags) quotedzputs(f->node.nam, stdout); if (f->funcdef || f->node.flags & PM_UNDEFINED) { - printf(" () {\n\t"); - if (f->node.flags & PM_UNDEFINED) - printf("%c undefined\n\t", hashchar); - else + printf(" () {\n"); + zoutputtab(stdout); + if (f->node.flags & PM_UNDEFINED) { + printf("%c undefined\n", hashchar); + zoutputtab(stdout); + } else t = getpermtext(f->funcdef, NULL, 1); - if (f->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL)) - printf("%c traced\n\t", hashchar); + if (f->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL)) { + printf("%c traced\n", hashchar); + zoutputtab(stdout); + } if (!t) { char *fopt = "UtTkz"; int flgs[] = { @@ -959,11 +963,12 @@ printshfuncnode(HashNode hn, int printflags) zputs(t, stdout); zsfree(t); if (f->funcdef->flags & EF_RUN) { - printf("\n\t"); + printf("\n"); + zoutputtab(stdout); quotedzputs(f->node.nam, stdout); printf(" \"$@\""); } - } + } printf("\n}"); } else { printf(" () { }"); @@ -979,6 +984,59 @@ printshfuncnode(HashNode hn, int printflags) putchar('\n'); } +/* + * Wrap scanmatchtable for shell functions with optional + * expansion of leading tabs. + * expand = 0 is standard: use hard tabs. + * expand > 0 uses that many spaces. + * expand < 0 uses no identation. + * + * Note this function and the following two are called with + * interrupts queued, so saving and restoring text_expand_tabs + * is safe. + */ + +/**/ +mod_export int +scanmatchshfunc(Patprog pprog, int sorted, int flags1, int flags2, + ScanFunc scanfunc, int scanflags, int expand) +{ + int ret, save_expand; + + save_expand = text_expand_tabs; + text_expand_tabs = expand; + ret = scanmatchtable(shfunctab, pprog, sorted, flags1, flags2, + scanfunc, scanflags); + text_expand_tabs = save_expand; + + return ret; +} + +/* Wrap scanhashtable to expand tabs for shell functions */ + +/**/ +mod_export int +scanshfunc(int sorted, int flags1, int flags2, + ScanFunc scanfunc, int scanflags, int expand) +{ + return scanmatchshfunc(NULL, sorted, flags1, flags2, + scanfunc, scanflags, expand); +} + +/* Wrap shfunctab->printnode to expand tabs */ + +/**/ +mod_export void +printshfuncexpand(HashNode hn, int printflags, int expand) +{ + int save_expand; + + save_expand = text_expand_tabs; + text_expand_tabs = expand; + shfunctab->printnode(hn, printflags); + text_expand_tabs = save_expand; +} + /**************************************/ /* Reserved Word Hash Table Functions */ /**************************************/ @@ -992,22 +1050,29 @@ static struct reswd reswds[] = { {{NULL, "}", 0}, OUTBRACE}, {{NULL, "case", 0}, CASE}, {{NULL, "coproc", 0}, COPROC}, + {{NULL, "declare", 0}, TYPESET}, {{NULL, "do", 0}, DOLOOP}, {{NULL, "done", 0}, DONE}, {{NULL, "elif", 0}, ELIF}, {{NULL, "else", 0}, ELSE}, {{NULL, "end", 0}, ZEND}, {{NULL, "esac", 0}, ESAC}, + {{NULL, "export", 0}, TYPESET}, {{NULL, "fi", 0}, FI}, + {{NULL, "float", 0}, TYPESET}, {{NULL, "for", 0}, FOR}, {{NULL, "foreach", 0}, FOREACH}, {{NULL, "function", 0}, FUNC}, {{NULL, "if", 0}, IF}, + {{NULL, "integer", 0}, TYPESET}, + {{NULL, "local", 0}, TYPESET}, {{NULL, "nocorrect", 0}, NOCORRECT}, + {{NULL, "readonly", 0}, TYPESET}, {{NULL, "repeat", 0}, REPEAT}, {{NULL, "select", 0}, SELECT}, {{NULL, "then", 0}, THEN}, {{NULL, "time", 0}, TIME}, + {{NULL, "typeset", 0}, TYPESET}, {{NULL, "until", 0}, UNTIL}, {{NULL, "while", 0}, WHILE}, {{NULL, NULL, 0}, 0} diff --git a/Src/hist.c b/Src/hist.c index bd03c4f11..75e809c48 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -136,6 +136,7 @@ mod_export int hist_skip_flags; #define HA_NOINC (1<<1) /* Don't store, curhist not incremented */ #define HA_INWORD (1<<2) /* We're inside a word, don't add start and end markers */ +#define HA_UNGET (1<<3) /* Recursively ungetting */ /* Array of word beginnings and endings in current history line. */ @@ -904,8 +905,14 @@ ihungetc(int c) while (!lexstop && !errflag) { if (hptr[-1] != (char) c && stophist < 4 && - hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\') - hungetc('\n'), hungetc('\\'); + hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\' && + !(histactive & HA_UNGET) && + (inbufflags & (INP_ALIAS|INP_HIST)) != INP_ALIAS) { + histactive |= HA_UNGET; + hungetc('\n'); + hungetc('\\'); + histactive &= ~HA_UNGET; + } if (expanding) { zlemetacs--; @@ -2000,7 +2007,7 @@ casemodify(char *str, int how) VARARR(char, mbstr, MB_CUR_MAX); mbstate_t ps; - mb_metacharinit(); + mb_charinit(); memset(&ps, 0, sizeof(ps)); while (*str) { wint_t wc; diff --git a/Src/init.c b/Src/init.c index 102276a64..22db4b3b2 100644 --- a/Src/init.c +++ b/Src/init.c @@ -105,6 +105,7 @@ loop(int toplevel, int justonce) Eprog prog; int err, non_empty = 0; + queue_signals(); pushheap(); if (!toplevel) zcontext_save(); @@ -218,6 +219,7 @@ loop(int toplevel, int justonce) if (((!interact || sourcelevel) && errflag) || retflag) break; if (isset(SINGLECOMMAND) && toplevel) { + dont_queue_signals(); if (sigtrapped[SIGEXIT]) dotrap(SIGEXIT); exit(lastval); @@ -229,6 +231,7 @@ loop(int toplevel, int justonce) if (!toplevel) zcontext_restore(); popheap(); + unqueue_signals(); if (err) return LOOP_ERROR; @@ -1117,8 +1120,9 @@ setupshin(char *runscript) exit(127); } scriptfilename = sfname; - zsfree(argzero); /* ztrdup'd in parseargs */ - argzero = runscript; + sfname = argzero; /* copy to avoid race condition */ + argzero = ztrdup(runscript); + zsfree(sfname); /* argzero ztrdup'd in parseargs */ } /* * We only initialise line numbering once there is a script to @@ -1425,7 +1429,7 @@ sourcehome(char *s) char *h; queue_signals(); - if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam("ZDOTDIR"))) { + if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam_u("ZDOTDIR"))) { h = home; if (!h) return; diff --git a/Src/input.c b/Src/input.c index 4a5bf89c6..eb968ea72 100644 --- a/Src/input.c +++ b/Src/input.c @@ -141,16 +141,19 @@ shingetline(void) int c; char buf[BUFSIZ]; char *p; + int q = queue_signal_level(); p = buf; - winch_unblock(); for (;;) { + winch_unblock(); + dont_queue_signals(); do { errno = 0; c = fgetc(bshin); } while (c < 0 && errno == EINTR); if (c < 0 || c == '\n') { winch_block(); + restore_queue_signals(q); if (c == '\n') *p++ = '\n'; if (p > buf) { @@ -167,12 +170,13 @@ shingetline(void) *p++ = c; if (p >= buf + BUFSIZ - 1) { winch_block(); + queue_signals(); line = zrealloc(line, ll + (p - buf) + 1); memcpy(line + ll, buf, p - buf); ll += p - buf; line[ll] = '\0'; p = buf; - winch_unblock(); + unqueue_signals(); } } } @@ -222,7 +226,8 @@ ingetc(void) if (inputline()) break; } - zshlex_raw_add(lastc); + if (!lexstop) + zshlex_raw_add(lastc); return lastc; } @@ -376,6 +381,8 @@ inputline(void) static void inputsetline(char *str, int flags) { + queue_signals(); + if ((inbufflags & INP_FREE) && inbuf) { free(inbuf); } @@ -393,6 +400,8 @@ inputsetline(char *str, int flags) else inbufct = inbufleft; inbufflags = flags; + + unqueue_signals(); } /* @@ -591,7 +600,7 @@ inpoptop(void) * history is before, but they're both pushed onto * the input stack. */ - if ((inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS) + if ((inbufflags & (INP_ALIAS|INP_HIST|INP_RAW_KEEP)) == INP_ALIAS) zshlex_raw_back(); } } diff --git a/Src/jobs.c b/Src/jobs.c index 948f61b01..b47ba8c60 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1179,7 +1179,7 @@ addfilelist(const char *name, int fd) /**/ void -pipecleanfilelist(LinkList filelist) +pipecleanfilelist(LinkList filelist, int proc_subst_only) { LinkNode node; @@ -1188,7 +1188,12 @@ pipecleanfilelist(LinkList filelist) node = firstnode(filelist); while (node) { Jobfile jf = (Jobfile)getdata(node); - if (jf->is_fd) { + if (jf->is_fd && + (!proc_subst_only +#ifdef FDT_PROC_SUBST + || fdtable[jf->u.fd] == FDT_PROC_SUBST +#endif + )) { LinkNode next = nextnode(node); zclose(jf->u.fd); (void)remnode(filelist, node); @@ -1379,10 +1384,17 @@ waitforpid(pid_t pid, int wait_cmd) dont_queue_signals(); child_block(); /* unblocked in signal_suspend() */ queue_traps(wait_cmd); + + /* This function should never be called with a pid that is not a + * child of the current shell. Consequently, if kill(0, pid) + * fails here with ESRCH, the child has already been reaped. In + * the loop body, we expect this to happen in signal_suspend() + * via zhandler(), after which this test terminates the loop. + */ while (!errflag && (kill(pid, 0) >= 0 || errno != ESRCH)) { if (first) first = 0; - else + else if (!wait_cmd) kill(pid, SIGCONT); last_signal = -1; @@ -1415,9 +1427,9 @@ zwaitjob(int job, int wait_cmd) int q = queue_signal_level(); Job jn = jobtab + job; - dont_queue_signals(); child_block(); /* unblocked during signal_suspend() */ queue_traps(wait_cmd); + dont_queue_signals(); if (jn->procs || jn->auxprocs) { /* if any forks were done */ jn->stat |= STAT_LOCKED; if (jn->stat & STAT_CHANGED) @@ -1433,7 +1445,7 @@ zwaitjob(int job, int wait_cmd) * we can't deadlock on the fact that those still exist, so * that's not a problem. */ - pipecleanfilelist(jn->filelist); + pipecleanfilelist(jn->filelist, 0); } while (!errflag && jn->stat && !(jn->stat & STAT_DONE) && @@ -1473,9 +1485,9 @@ zwaitjob(int job, int wait_cmd) pipestats[0] = lastval; numpipestats = 1; } + restore_queue_signals(q); unqueue_traps(); child_unblock(); - restore_queue_signals(q); return 0; } @@ -1623,7 +1635,7 @@ spawnjob(void) deletejob(jobtab + thisjob, 0); else { jobtab[thisjob].stat |= STAT_LOCKED; - pipecleanfilelist(jobtab[thisjob].filelist); + pipecleanfilelist(jobtab[thisjob].filelist, 0); } thisjob = -1; } @@ -339,6 +339,7 @@ ctxtlex(void) incmdpos = 1; break; case STRING: + case TYPESET: /* case ENVSTRING: */ case ENVARRAY: case OUTPAR: @@ -1182,7 +1183,7 @@ gettokstr(int c, int sub) c = Outpar; } } else if (peek != ENVSTRING && - incmdpos && !bct && !brct) { + (incmdpos || intypeset) && !bct && !brct) { char *t = tokstr; if (idigit(*t)) while (++t < lexbuf.ptr && idigit(*t)); @@ -1200,7 +1201,7 @@ gettokstr(int c, int sub) t++; if (t == lexbuf.ptr) { e = hgetc(); - if (e == '(' && incmdpos) { + if (e == '(') { *lexbuf.ptr = '\0'; return ENVARRAY; } @@ -1387,7 +1388,7 @@ dquote_parse(char endchar, int sub) { int pct = 0, brct = 0, bct = 0, intick = 0, err = 0; int c; - int math = endchar == ')' || endchar == ']'; + int math = endchar == ')' || endchar == ']' || infor; int zlemath = math && zlemetacs > zlemetall + addedx - inbufct; while (((c = hgetc()) != endchar || bct || @@ -1994,8 +1995,10 @@ skipcomm(void) #else char *new_tokstr; int new_lexstop, new_lex_add_raw; + int save_infor = infor; struct lexbufstate new_lexbuf; + infor = 0; cmdpush(CS_CMDSUBST); SETPARBEGIN add(Inpar); @@ -2020,6 +2023,18 @@ skipcomm(void) new_tokstr = tokstr; new_lexbuf = lexbuf; + /* + * If we're expanding an alias at this point, we need the whole + * remaining text as part of the string for the command in + * parentheses, so don't backtrack. This is different from the + * usual case where the alias is fully within the command, where + * we want the unexpanded text so that it will be expanded + * again when the command in the parentheses is executed. + * + * I never wanted to be a software engineer, you know. + */ + if (inbufflags & INP_ALIAS) + inbufflags |= INP_RAW_KEEP; zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); hist_in_word(1); } else { @@ -2052,6 +2067,7 @@ skipcomm(void) * the recursive parsing. */ lexflags &= ~LEXFLAGS_ZLE; + dbparens = 0; /* restored by zcontext_restore_partial() */ if (!parse_event(OUTPAR) || tok != OUTPAR) lexstop = 1; @@ -2098,6 +2114,7 @@ skipcomm(void) if (!lexstop) SETPAREND cmdpop(); + infor = save_infor; return lexstop; #endif diff --git a/Src/loop.c b/Src/loop.c index e4e8e2df8..4def9b652 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -56,6 +56,10 @@ execfor(Estate state, int do_exec) char *name, *str, *cond = NULL, *advance = NULL; zlong val = 0; LinkList vars = NULL, args = NULL; + int old_simple_pline = simple_pline; + + /* See comments in execwhile() */ + simple_pline = 1; end = state->pc + WC_FOR_SKIP(code); @@ -69,10 +73,12 @@ execfor(Estate state, int do_exec) fprintf(xtrerr, "%s\n", str2); fflush(xtrerr); } - if (!errflag) + if (!errflag) { matheval(str); + } if (errflag) { state->pc = end; + simple_pline = old_simple_pline; return 1; } cond = ecgetstr(state, EC_NODUP, &ctok); @@ -85,12 +91,14 @@ execfor(Estate state, int do_exec) if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) { state->pc = end; + simple_pline = old_simple_pline; return 0; } if (htok) { execsubst(args); if (errflag) { state->pc = end; + simple_pline = old_simple_pline; return 1; } } @@ -198,6 +206,7 @@ execfor(Estate state, int do_exec) popheap(); cmdpop(); loops--; + simple_pline = old_simple_pline; state->pc = end; return lastval; } @@ -214,6 +223,10 @@ execselect(Estate state, UNUSED(int do_exec)) FILE *inp; size_t more; LinkList args; + int old_simple_pline = simple_pline; + + /* See comments in execwhile() */ + simple_pline = 1; end = state->pc + WC_FOR_SKIP(code); name = ecgetstr(state, EC_NODUP, NULL); @@ -229,18 +242,21 @@ execselect(Estate state, UNUSED(int do_exec)) if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) { state->pc = end; + simple_pline = old_simple_pline; return 0; } if (htok) { execsubst(args); if (errflag) { state->pc = end; + simple_pline = old_simple_pline; return 1; } } } if (!args || empty(args)) { state->pc = end; + simple_pline = old_simple_pline; return 0; } loops++; @@ -315,6 +331,7 @@ execselect(Estate state, UNUSED(int do_exec)) popheap(); fclose(inp); loops--; + simple_pline = old_simple_pline; state->pc = end; return lastval; } @@ -382,6 +399,7 @@ execwhile(Estate state, UNUSED(int do_exec)) Wordcode end, loop; wordcode code = state->pc[-1]; int olderrexit, oldval, isuntil = (WC_WHILE_TYPE(code) == WC_WHILE_UNTIL); + int old_simple_pline = simple_pline; end = state->pc + WC_WHILE_SKIP(code); olderrexit = noerrexit; @@ -396,8 +414,6 @@ execwhile(Estate state, UNUSED(int do_exec)) /* This is an empty loop. Make sure the signal handler sets the * flags and then just wait for someone hitting ^C. */ - int old_simple_pline = simple_pline; - simple_pline = 1; while (!breaks) @@ -409,7 +425,14 @@ execwhile(Estate state, UNUSED(int do_exec)) for (;;) { state->pc = loop; noerrexit = 1; + + /* In case the test condition is a functional no-op, + * make sure signal handlers recognize ^C to end the loop. */ + simple_pline = 1; + execlist(state, 1, 0); + + simple_pline = old_simple_pline; noerrexit = olderrexit; if (!((lastval == 0) ^ isuntil)) { if (breaks) @@ -421,7 +444,14 @@ execwhile(Estate state, UNUSED(int do_exec)) lastval = oldval; break; } + + /* In case the loop body is also a functional no-op, + * make sure signal handlers recognize ^C as above. */ + simple_pline = 1; + execlist(state, 1, 0); + + simple_pline = old_simple_pline; if (breaks) { breaks--; if (breaks || !contflag) @@ -452,6 +482,10 @@ execrepeat(Estate state, UNUSED(int do_exec)) wordcode code = state->pc[-1]; int count, htok = 0; char *tmp; + int old_simple_pline = simple_pline; + + /* See comments in execwhile() */ + simple_pline = 1; end = state->pc + WC_REPEAT_SKIP(code); @@ -484,6 +518,7 @@ execrepeat(Estate state, UNUSED(int do_exec)) cmdpop(); popheap(); loops--; + simple_pline = old_simple_pline; state->pc = end; return lastval; } diff --git a/Src/math.c b/Src/math.c index 97a97b32b..977e92345 100644 --- a/Src/math.c +++ b/Src/math.c @@ -407,6 +407,13 @@ mathevall(char *s, enum prec_type prec_tp, char **ep) stack[0].val.type = MN_INTEGER; stack[0].val.u.l = 0; mathparse(prec_tp == MPREC_TOP ? TOPPREC : ARGPREC); + /* + * Internally, we parse the contents of parentheses at top + * precedence... so we can return a parenthesis here if + * there are too many at the end. + */ + if (mtok == M_OUTPAR && !errflag) + zerr("bad math expression: unexpected ')'"); *ep = ptr; DPUTS(!errflag && sp > 0, "BUG: math: wallabies roaming too freely in outback"); @@ -791,7 +798,7 @@ zzlex(void) ptr++; if (!*ptr) { - zerr("character missing after ##"); + zerr("bad math expression: character missing after ##"); return EOI; } ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v); @@ -914,7 +921,7 @@ setmathvar(struct mathvalue *mvp, mnumber v) mvp->pval = NULL; } if (!mvp->lval) { - zerr("lvalue required"); + zerr("bad math expression: lvalue required"); v.type = MN_INTEGER; v.u.l = 0; return v; @@ -1256,7 +1263,7 @@ op(int what) /* Error if (-num ** b) and b is not an integer */ double tst = (double)(zlong)b.u.d; if (tst != b.u.d) { - zerr("imaginary power"); + zerr("bad math expression: imaginary power"); return; } } @@ -1338,7 +1345,7 @@ op(int what) push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, NULL, 0); break; case COLON: - zerr("':' without '?'"); + zerr("bad math expression: ':' without '?'"); break; case PREPLUS: if (spval->type & MN_FLOAT) @@ -1355,7 +1362,7 @@ op(int what) setmathvar(stack + sp, *spval); break; default: - zerr("out of integers"); + zerr("bad math expression: out of integers"); return; } } @@ -1525,7 +1532,7 @@ mathparse(int pc) mathparse(TOPPREC); if (mtok != M_OUTPAR) { if (!errflag) - zerr("')' expected"); + zerr("bad math expression: ')' expected"); return; } break; @@ -1543,7 +1550,7 @@ mathparse(int pc) noeval--; if (mtok != COLON) { if (!errflag) - zerr("':' expected"); + zerr("bad math expression: ':' expected"); return; } if (q) diff --git a/Src/options.c b/Src/options.c index 3e3e07474..1fb102f1d 100644 --- a/Src/options.c +++ b/Src/options.c @@ -81,6 +81,7 @@ static struct optname optns[] = { {{NULL, "allexport", OPT_EMULATE}, ALLEXPORT}, {{NULL, "alwayslastprompt", OPT_ALL}, ALWAYSLASTPROMPT}, {{NULL, "alwaystoend", 0}, ALWAYSTOEND}, +{{NULL, "appendcreate", OPT_EMULATE|OPT_BOURNE}, APPENDCREATE}, {{NULL, "appendhistory", OPT_ALL}, APPENDHISTORY}, {{NULL, "autocd", OPT_EMULATE}, AUTOCD}, {{NULL, "autocontinue", 0}, AUTOCONTINUE}, @@ -172,7 +173,7 @@ static struct optname optns[] = { {{NULL, "kshautoload", OPT_EMULATE|OPT_BOURNE}, KSHAUTOLOAD}, {{NULL, "kshglob", OPT_EMULATE|OPT_KSH}, KSHGLOB}, {{NULL, "kshoptionprint", OPT_EMULATE|OPT_KSH}, KSHOPTIONPRINT}, -{{NULL, "kshtypeset", OPT_EMULATE|OPT_KSH}, KSHTYPESET}, +{{NULL, "kshtypeset", 0}, KSHTYPESET}, {{NULL, "kshzerosubscript", 0}, KSHZEROSUBSCRIPT}, {{NULL, "listambiguous", OPT_ALL}, LISTAMBIGUOUS}, {{NULL, "listbeep", OPT_ALL}, LISTBEEP}, @@ -192,7 +193,7 @@ static struct optname optns[] = { {{NULL, "monitor", OPT_SPECIAL}, MONITOR}, {{NULL, "multibyte", #ifdef MULTIBYTE_SUPPORT - OPT_EMULATE|OPT_ZSH|OPT_CSH|OPT_KSH + OPT_ALL #else 0 #endif diff --git a/Src/params.c b/Src/params.c index 98541a6da..00f43e47d 100644 --- a/Src/params.c +++ b/Src/params.c @@ -196,7 +196,7 @@ static const struct gsu_integer ttyidle_gsu = { ttyidlegetfn, nullintsetfn, stdunsetfn }; static const struct gsu_scalar argzero_gsu = -{ argzerogetfn, nullstrsetfn, nullunsetfn }; +{ argzerogetfn, argzerosetfn, nullunsetfn }; static const struct gsu_scalar username_gsu = { usernamegetfn, usernamesetfn, stdunsetfn }; static const struct gsu_scalar dash_gsu = @@ -1116,14 +1116,12 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, Patprog pprog = NULL; /* - * If in NO_EXEC mode, the parameters won't be set up - * properly, so there's no point even doing any sanity checking. - * Just return 0 now. + * If in NO_EXEC mode, the parameters won't be set up properly, + * so just pretend everything is a hash for subscript parsing */ - if (unset(EXECOPT)) - return 0; - ishash = (v->pm && PM_TYPE(v->pm->node.flags) == PM_HASHED); + ishash = (unset(EXECOPT) || + (v->pm && PM_TYPE(v->pm->node.flags) == PM_HASHED)); if (prevcharlen) *prevcharlen = 1; if (nextcharlen) @@ -1278,8 +1276,18 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, } if (!c) return 0; - s = dupstrpfx(s, t - s); *str = tt = t; + + /* + * If in NO_EXEC mode, the parameters won't be set up properly, + * so there's no additional sanity checking we can do. + * Just return 0 now. + */ + if (unset(EXECOPT)) + return 0; + + s = dupstrpfx(s, t - s); + /* If we're NOT reverse subscripting, strip the inull()s so brackets * * are not backslashed after parsestr(). Otherwise leave them alone * * so that the brackets will be escaped when we patcompile() or when * @@ -2631,6 +2639,15 @@ getsparam(char *s) return getstrvalue(v); } +/**/ +mod_export char * +getsparam_u(char *s) +{ + if ((s = getsparam(s))) + return unmetafy(s, NULL); + return s; +} + /* Retrieve an array parameter */ /**/ @@ -3963,7 +3980,7 @@ setlang(char *x) struct localename *ln; char *x2; - if ((x2 = getsparam("LC_ALL")) && *x2) + if ((x2 = getsparam_u("LC_ALL")) && *x2) return; /* @@ -3977,10 +3994,10 @@ setlang(char *x) * from this is meaningless. So just all $LANG to show through in * that case. */ - setlocale(LC_ALL, x ? x : ""); + setlocale(LC_ALL, x ? unmeta(x) : ""); queue_signals(); for (ln = lc_names; ln->name; ln++) - if ((x = getsparam(ln->name)) && *x) + if ((x = getsparam_u(ln->name)) && *x) setlocale(ln->category, x); unqueue_signals(); } @@ -3996,7 +4013,7 @@ lc_allsetfn(Param pm, char *x) * that with any LC_* that are set. */ if (!x || !*x) { - x = getsparam("LANG"); + x = getsparam_u("LANG"); if (x && *x) { queue_signals(); setlang(x); @@ -4004,7 +4021,7 @@ lc_allsetfn(Param pm, char *x) } } else - setlocale(LC_ALL, x); + setlocale(LC_ALL, unmeta(x)); } /**/ @@ -4012,7 +4029,7 @@ void langsetfn(Param pm, char *x) { strsetfn(pm, x); - setlang(x); + setlang(unmeta(x)); } /**/ @@ -4038,12 +4055,27 @@ lcsetfn(Param pm, char *x) if (x && *x) { for (ln = lc_names; ln->name; ln++) if (!strcmp(ln->name, pm->node.nam)) - setlocale(ln->category, x); + setlocale(ln->category, unmeta(x)); } unqueue_signals(); } #endif /* USE_LOCALE */ +/* Function to set value for special parameter `0' */ + +/**/ +static void +argzerosetfn(UNUSED(Param pm), char *x) +{ + if (x) { + if (!isset(POSIXARGZERO)) { + zsfree(argzero); + argzero = ztrdup(x); + } + zsfree(x); + } +} + /* Function to get value for special parameter `0' */ /**/ @@ -5076,8 +5108,10 @@ printparamvalue(Param p, int printflags) break; case PM_ARRAY: /* array */ - if (!(printflags & PRINT_KV_PAIR)) + if (!(printflags & PRINT_KV_PAIR)) { putchar('('); + putchar(' '); + } u = p->gsu.a->getfn(p); if(*u) { quotedzputs(*u++, stdout); @@ -5086,13 +5120,17 @@ printparamvalue(Param p, int printflags) quotedzputs(*u++, stdout); } } - if (!(printflags & PRINT_KV_PAIR)) + if (!(printflags & PRINT_KV_PAIR)) { + putchar(' '); putchar(')'); + } break; case PM_HASHED: /* association */ - if (!(printflags & PRINT_KV_PAIR)) + if (!(printflags & PRINT_KV_PAIR)) { putchar('('); + putchar(' '); + } { HashTable ht = p->gsu.h->getfn(p); if (ht) diff --git a/Src/parse.c b/Src/parse.c index c932851d9..7c2d20250 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -63,6 +63,11 @@ int isnewlin; /**/ int infor; +/* != 0 if parsing arguments of typeset etc. */ + +/**/ +mod_export int intypeset; + /* list of here-documents */ /**/ @@ -118,11 +123,20 @@ struct heredocs *hdocs; * WC_ASSIGN * - data contains type (scalar, array) and number of array-elements * - followed by name and value + * Note variant for WC_TYPESET assignments: WC_ASSIGN_INC indicates + * a name with no equals, not an =+ which isn't valid here. * * WC_SIMPLE * - data contains the number of arguments (plus command) * - followed by strings * + * WC_TYPESET + * Variant of WC_SIMPLE used when TYPESET reserved word found. + * - data contains the number of string arguments (plus command) + * - followed by strings + * - followed by number of assignments + * - followed by assignments if non-zero number. + * * WC_SUBSH * - data unused * - followed by list @@ -257,6 +271,7 @@ parse_context_save(struct parse_stack *ps, int toplevel) ps->incasepat = incasepat; ps->isnewlin = isnewlin; ps->infor = infor; + ps->intypeset = intypeset; ps->hdocs = hdocs; ps->eclen = eclen; @@ -290,6 +305,7 @@ parse_context_restore(const struct parse_stack *ps, int toplevel) incasepat = ps->incasepat; isnewlin = ps->isnewlin; infor = ps->infor; + intypeset = ps->intypeset; hdocs = ps->hdocs; eclen = ps->eclen; @@ -430,7 +446,7 @@ init_parse_status(void) * between the two it's a bit ambiguous. We clear them when * using the lexical analyser for strings as well as here. */ - incasepat = incond = inredir = infor = 0; + incasepat = incond = inredir = infor = intypeset = 0; incmdpos = 1; } @@ -440,6 +456,8 @@ init_parse_status(void) void init_parse(void) { + queue_signals(); + if (ecbuf) zfree(ecbuf, eclen); ecbuf = (Wordcode) zalloc((eclen = EC_INIT_SIZE) * sizeof(wordcode)); @@ -450,6 +468,8 @@ init_parse(void) ecnfunc = 0; init_parse_status(); + + unqueue_signals(); } /* Build eprog. */ @@ -472,6 +492,8 @@ bld_eprog(int heap) Eprog ret; int l; + queue_signals(); + ecadd(WCB_END()); ret = heap ? (Eprog) zhalloc(sizeof(*ret)) : (Eprog) zalloc(sizeof(*ret)); @@ -495,6 +517,8 @@ bld_eprog(int heap) zfree(ecbuf, eclen); ecbuf = NULL; + unqueue_signals(); + return ret; } @@ -992,6 +1016,7 @@ par_cmd(int *cmplx, int zsh_construct) incmdpos = 1; incasepat = 0; incond = 0; + intypeset = 0; return 1; } @@ -1575,9 +1600,9 @@ par_funcdef(int *cmplx) p = ecadd(0); ecadd(0); - incmdpos = 1; while (tok == STRING) { - if (*tokstr == Inbrace && !tokstr[1]) { + if ((*tokstr == Inbrace || *tokstr == '{') && + !tokstr[1]) { tok = INBRACE; break; } @@ -1590,6 +1615,7 @@ par_funcdef(int *cmplx) ecadd(0); nocorrect = 0; + incmdpos = 1; if (tok == INOUTPAR) zshlex(); while (tok == SEPER) @@ -1709,7 +1735,8 @@ static int par_simple(int *cmplx, int nr) { int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0; - int c = *cmplx, nrediradd, assignments = 0; + int c = *cmplx, nrediradd, assignments = 0, ppost = 0, is_typeset = 0; + wordcode postassigns = 0; r = ecused; for (;;) { @@ -1717,31 +1744,32 @@ par_simple(int *cmplx, int nr) *cmplx = c = 1; nocorrect = 1; } else if (tok == ENVSTRING) { - char *p, *name, *str; + char *ptr, *name, *str; name = tokstr; - for (p = tokstr; *p && *p != Inbrack && *p != '=' && *p != '+'; - p++); - if (*p == Inbrack) skipparens(Inbrack, Outbrack, &p); - if (*p == '+') { - *p++ = '\0'; + for (ptr = tokstr; + *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+'; + ptr++); + if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr); + if (*ptr == '+') { + *ptr++ = '\0'; ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0)); } else ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0)); - - if (*p == '=') { - *p = '\0'; - str = p + 1; + + if (*ptr == '=') { + *ptr = '\0'; + str = ptr + 1; } else equalsplit(tokstr, &str); - for (p = str; *p; p++) { + for (ptr = str; *ptr; ptr++) { /* * We can't treat this as "simple" if it contains * expansions that require process subsitution, since then * we need process handling. */ - if (p[1] == Inpar && - (*p == Equals || *p == Inang || *p == OutangProc)) { + if (ptr[1] == Inpar && + (*ptr == Equals || *ptr == Inang || *ptr == OutangProc)) { *cmplx = 1; break; } @@ -1786,14 +1814,18 @@ par_simple(int *cmplx, int nr) p = ecadd(WCB_SIMPLE(0)); for (;;) { - if (tok == STRING) { + if (tok == STRING || tok == TYPESET) { int redir_var = 0; *cmplx = 1; incmdpos = 0; + if (tok == TYPESET) + intypeset = is_typeset = 1; + if (!isset(IGNOREBRACES) && *tokstr == Inbrace) { + /* Look for redirs of the form {var}>file etc. */ char *eptr = tokstr + strlen(tokstr) - 1; char *ptr = eptr; @@ -1824,15 +1856,74 @@ par_simple(int *cmplx, int nr) if (!redir_var) { - ecstr(tokstr); - argc++; + if (postassigns) { + /* + * We're in the variable part of a typeset, + * but this doesn't have an assignment. + * We'll parse it as if it does, but mark + * it specially with WC_ASSIGN_INC. + */ + postassigns++; + ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0)); + ecstr(tokstr); + ecstr(""); /* TBD can possibly optimise out */ + } else { + ecstr(tokstr); + argc++; + } zshlex(); } } else if (IS_REDIROP(tok)) { *cmplx = c = 1; nrediradd = par_redir(&r, NULL); p += nrediradd; + if (ppost) + ppost += nrediradd; sr += nrediradd; + } else if (tok == ENVSTRING) { + char *ptr, *name, *str; + + if (!postassigns++) + ppost = ecadd(0); + + name = tokstr; + for (ptr = tokstr; *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+'; + ptr++); + if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr); + ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0)); + + if (*ptr == '=') { + *ptr = '\0'; + str = ptr + 1; + } else + equalsplit(tokstr, &str); + ecstr(name); + ecstr(str); + zshlex(); + } else if (tok == ENVARRAY) { + int n, parr; + + if (!postassigns++) + ppost = ecadd(0); + + parr = ecadd(0); + ecstr(tokstr); + cmdpush(CS_ARRAY); + /* + * Careful here: this must be the typeset case, + * but we need to tell the lexer not to look + * for assignments until we've finished the + * present one. + */ + intypeset = 0; + zshlex(); + n = par_nl_wordlist(); + ecbuf[parr] = WCB_ASSIGN(WC_ASSIGN_ARRAY, WC_ASSIGN_NEW, n); + cmdpop(); + intypeset = 1; + if (tok != OUTPAR) + YYERROR(oecused); + zshlex(); } else if (tok == INOUTPAR) { zlong oldlineno = lineno; int onp, so, oecssub = ecssub; @@ -1841,7 +1932,7 @@ par_simple(int *cmplx, int nr) if (!isset(MULTIFUNCDEF) && argc > 1) YYERROR(oecused); /* Error if preceding assignments */ - if (assignments) + if (assignments || postassigns) YYERROR(oecused); *cmplx = c; @@ -1947,9 +2038,18 @@ par_simple(int *cmplx, int nr) return 0; } incmdpos = 1; + intypeset = 0; - if (!isfunc) - ecbuf[p] = WCB_SIMPLE(argc); + if (!isfunc) { + if (is_typeset) { + ecbuf[p] = WCB_TYPESET(argc); + if (postassigns) + ecbuf[ppost] = postassigns; + else + ecadd(0); + } else + ecbuf[p] = WCB_SIMPLE(argc); + } return sr + 1; } diff --git a/Src/pattern.c b/Src/pattern.c index 4e5e8a110..7d38988a0 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -520,6 +520,8 @@ patcompile(char *exp, int inflags, char **endexp) char *lng, *strp = NULL; Patprog p; + queue_signals(); + startoff = sizeof(struct patprog); /* Ensure alignment of start of program string */ startoff = (startoff + sizeof(union upat) - 1) & ~(sizeof(union upat) - 1); @@ -582,8 +584,10 @@ patcompile(char *exp, int inflags, char **endexp) if (!strp || (*strp && *strp != '/')) { /* No, do normal compilation. */ strp = NULL; - if (patcompswitch(0, &flags) == 0) + if (patcompswitch(0, &flags) == 0) { + unqueue_signals(); return NULL; + } } else { /* * Yes, copy the string, and skip compilation altogether. @@ -715,6 +719,8 @@ patcompile(char *exp, int inflags, char **endexp) if (endexp) *endexp = patparse; + + unqueue_signals(); return p; } @@ -1113,8 +1119,8 @@ range_type(char *start, int len) const char **csp; for (csp = colon_stuffs; *csp; csp++) { - if (!strncmp(start, *csp, len)) - return (csp - colon_stuffs) + PP_FIRST; + if (strlen(*csp) == len && !strncmp(start, *csp, len)) + return (csp - colon_stuffs) + PP_FIRST; } return PP_UNKWN; @@ -2202,20 +2208,15 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, if ((patglobflags & GF_MATCHREF) && !(patflags & PAT_FILE)) { char *str = ztrduppfx(patinstart, patinlen); - char *ptr = patinstart; - int mlen = 0; + int mlen; /* * Count the characters. We're not using CHARSUB() - * because the string is still metafied. We're - * not using mb_metastrlen() because that expects - * the string to be null terminated. + * because the string is still metafied. */ MB_METACHARINIT(); - while (ptr < patinstart + patinlen) { - mlen++; - ptr += MB_METACHARLEN(ptr); - } + mlen = MB_METASTRLEN2END(patinstart, 0, + patinstart + patinlen); setsparam("MATCH", str); setiparam("MBEGIN", diff --git a/Src/prompt.c b/Src/prompt.c index ffc1d0df2..be067ee7e 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -271,7 +271,7 @@ static int putpromptchar(int doprint, int endchar, unsigned int *txtchangep) { char *ss, *hostnam; - int t0, arg, test, sep, j, numjobs; + int t0, arg, test, sep, j, numjobs, len; struct tm *tm; struct timezone dummy_tz; struct timeval tv; @@ -673,12 +673,14 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) */ for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) { addbufspc(t0); - if (ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec) >= 0) + if ((len = ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec)) + >= 0) break; } /* There is enough room for this because addbufspc(t0) * allocates room for t0 * 2 bytes. */ - metafy(bv->bp, -1, META_NOALLOC); + if (len >= 0) + metafy(bv->bp, len, META_NOALLOC); bv->bp += strlen(bv->bp); zsfree(tmbuf); break; @@ -964,7 +966,7 @@ stradd(char *d) /* FALL THROUGH */ default: /* Take full wide character in one go */ - mb_metacharinit(); + mb_charinit(); pc = wcs_nicechar(cc, NULL, NULL); break; } diff --git a/Src/signals.c b/Src/signals.c index 3950ad1a2..f45c1860c 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -487,6 +487,12 @@ wait_for_processes(void) break; } + /* This is necessary to be sure queueing_enabled > 0 when + * we enter printjob() from update_job(), so that we don't + * decrement to zero in should_report_time() and improperly + * run other handlers in the middle of processing this one */ + queue_signals(); + /* * Find the process and job containing this pid and * update it. @@ -528,8 +534,16 @@ wait_for_processes(void) * and is not equal to the current foreground job. */ if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) && - jn - jobtab != thisjob) - addbgstatus(pid, (int)lastval2); + jn - jobtab != thisjob) { + int val = (WIFSIGNALED(status) ? + 0200 | WTERMSIG(status) : + (WIFSTOPPED(status) ? + 0200 | WEXITSTATUS(status) : + WEXITSTATUS(status))); + addbgstatus(pid, val); + } + + unqueue_signals(); } } @@ -1207,6 +1221,8 @@ dotrapargs(int sig, int *sigtr, void *sigfn) } } + queue_signals(); /* Any time we manage memory or global state */ + intrap++; *sigtr |= ZSIG_IGNORED; @@ -1244,7 +1260,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn) sfcontext = SFC_SIGNAL; incompfunc = 0; - doshfunc((Shfunc)sigfn, args, 1); + doshfunc((Shfunc)sigfn, args, 1); /* manages signal queueing */ sfcontext = osc; incompfunc= old_incompfunc; freelinklist(args, (FreeFunc) NULL); @@ -1254,7 +1270,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn) trap_state = TRAP_STATE_PRIMED; trapisfunc = isfunc = 0; - execode((Eprog)sigfn, 1, 0, "trap"); + execode((Eprog)sigfn, 1, 0, "trap"); /* manages signal queueing */ } runhookdef(AFTERTRAPHOOK, NULL); @@ -1321,6 +1337,8 @@ dotrapargs(int sig, int *sigtr, void *sigfn) if (*sigtr != ZSIG_IGNORED) *sigtr &= ~ZSIG_IGNORED; intrap--; + + unqueue_signals(); } /* Standard call to execute a trap for a given signal. */ diff --git a/Src/subst.c b/Src/subst.c index 81d34d28a..021d23444 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3834,8 +3834,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) y = dupstring(nulstring); insertlinknode(l, n, (void *) y), incnode(n); } - if (eval) - n = on; + /* This used to omit restoring of *str and instead test + * if (eval) + * n = on; + * but that causes strange behavior of history modifiers when + * applied across all values of an array. What is magic about + * eval here that *str seemed not to need restoring? + */ + *str = getdata(n = on); } else { /* * Scalar value. Handle last minute transformations diff --git a/Src/text.c b/Src/text.c index 958303c68..3978a26a9 100644 --- a/Src/text.c +++ b/Src/text.c @@ -30,6 +30,16 @@ #include "zsh.mdh" #include "text.pro" +/* + * If non-zero, expand syntactically significant leading tabs in text + * to this number of spaces. + * + * If negative, don't output leading whitespace at all. + */ + +/**/ +int text_expand_tabs; + static char *tptr, *tbuf, *tlim, *tpending; static int tsiz, tindent, tnewlins, tjob; @@ -67,7 +77,7 @@ taddpending(char *str1, char *str2) */ if (tpending) { int oldlen = strlen(tpending); - tpending = realloc(tpending, len + oldlen); + tpending = zrealloc(tpending, len + oldlen); sprintf(tpending + oldlen, "%s%s", str1, str2); } else { tpending = (char *)zalloc(len); @@ -100,7 +110,7 @@ taddchr(int c) tptr--; return; } - tbuf = realloc(tbuf, tsiz *= 2); + tbuf = zrealloc(tbuf, tsiz *= 2); tlim = tbuf + tsiz; tptr = tbuf + tsiz / 2; } @@ -120,7 +130,7 @@ taddstr(char *s) if (!tbuf) return; - tbuf = realloc(tbuf, tsiz *= 2); + tbuf = zrealloc(tbuf, tsiz *= 2); tlim = tbuf + tsiz; tptr = tbuf + x; } @@ -145,6 +155,48 @@ taddlist(Estate state, int num) } } +/* add an assignment */ + +static void +taddassign(wordcode code, Estate state, int typeset) +{ + /* name */ + taddstr(ecgetstr(state, EC_NODUP, NULL)); + /* value... maybe */ + if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) { + if (typeset) { + /* dummy assignment --- just var name */ + (void)ecgetstr(state, EC_NODUP, NULL); + taddchr(' '); + return; + } + taddchr('+'); + } + taddchr('='); + if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) { + taddchr('('); + taddlist(state, WC_ASSIGN_NUM(code)); + taddstr(") "); + } else { + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddchr(' '); + } +} + +/* add a number of assignments from typeset */ + +/**/ +static void +taddassignlist(Estate state, wordcode count) +{ + if (count) + taddchr(' '); + while (count--) { + wordcode code = *state->pc++; + taddassign(code, state, 1); + } +} + /* add a newline, or something equivalent, to the text buffer */ /**/ @@ -156,8 +208,16 @@ taddnl(int no_semicolon) if (tnewlins) { tdopending(); taddchr('\n'); - for (t0 = 0; t0 != tindent; t0++) - taddchr('\t'); + for (t0 = 0; t0 != tindent; t0++) { + if (text_expand_tabs >= 0) { + if (text_expand_tabs) { + int t1; + for (t1 = 0; t1 < text_expand_tabs; t1++) + taddchr(' '); + } else + taddchr('\t'); + } + } } else if (no_semicolon) { taddstr(" "); } else { @@ -165,6 +225,30 @@ taddnl(int no_semicolon) } } +/* + * Output a tab that may be expanded as part of a leading set. + * Note this is not part of the text framework; it's for + * code that needs to output its own tabs that are to be + * consistent with those from getpermtext(). + * + * Note these tabs are only expected to be useful at the + * start of the line, so we make no attempt to count columns. + */ + +/**/ +void +zoutputtab(FILE *outf) +{ + if (text_expand_tabs < 0) + return; + if (text_expand_tabs) { + int i; + for (i = 0; i < text_expand_tabs; i++) + fputc(' ', outf); + } else + fputc('\t', outf); +} + /* get a permanent textual representation of n */ /**/ @@ -397,22 +481,17 @@ gettext2(Estate state) } break; case WC_ASSIGN: - taddstr(ecgetstr(state, EC_NODUP, NULL)); - if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) taddchr('+'); - taddchr('='); - if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) { - taddchr('('); - taddlist(state, WC_ASSIGN_NUM(code)); - taddstr(") "); - } else { - taddstr(ecgetstr(state, EC_NODUP, NULL)); - taddchr(' '); - } + taddassign(code, state, 0); break; case WC_SIMPLE: taddlist(state, WC_SIMPLE_ARGC(code)); stack = 1; break; + case WC_TYPESET: + taddlist(state, WC_TYPESET_ARGC(code)); + taddassignlist(state, *state->pc++); + stack = 1; + break; case WC_SUBSH: if (!s) { taddstr("("); @@ -602,7 +681,7 @@ gettext2(Estate state) case WC_CASE: if (!s) { Wordcode end = state->pc + WC_CASE_SKIP(code); - wordcode nalts; + wordcode ialts; taddstr("case "); taddstr(ecgetstr(state, EC_NODUP, NULL)); @@ -616,6 +695,7 @@ gettext2(Estate state) taddstr("esac"); stack = 1; } else { + Wordcode prev_pc; tindent++; if (tnewlins) taddnl(0); @@ -623,21 +703,23 @@ gettext2(Estate state) taddchr(' '); taddstr("("); code = *state->pc++; - nalts = *state->pc++; - while (nalts--) { + prev_pc = state->pc++; + ialts = *prev_pc; + while (ialts--) { taddstr(ecgetstr(state, EC_NODUP, NULL)); state->pc++; - if (nalts) + if (ialts) taddstr(" | "); } taddstr(") "); tindent++; n = tpush(code, 0); n->u._case.end = end; - n->pop = (state->pc - 2 + WC_CASE_SKIP(code) >= end); + n->pop = (prev_pc + WC_CASE_SKIP(code) >= end); } } else if (state->pc < s->u._case.end) { - wordcode nalts; + Wordcode prev_pc; + wordcode ialts; dec_tindent(); switch (WC_CASE_TYPE(code)) { case WC_CASE_OR: @@ -658,17 +740,18 @@ gettext2(Estate state) taddchr(' '); taddstr("("); code = *state->pc++; - nalts = *state->pc++; - while (nalts--) { + prev_pc = state->pc++; + ialts = *prev_pc; + while (ialts--) { taddstr(ecgetstr(state, EC_NODUP, NULL)); state->pc++; - if (nalts) + if (ialts) taddstr(" | "); } taddstr(") "); tindent++; s->code = code; - s->pop = ((state->pc - 2 + WC_CASE_SKIP(code)) >= + s->pop = (prev_pc + WC_CASE_SKIP(code) >= s->u._case.end); } else { dec_tindent(); diff --git a/Src/utils.c b/Src/utils.c index 271c800fd..4c4dc55cd 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -82,7 +82,7 @@ set_widearray(char *mb_array, Widechar_array wca) wchar_t *wcptr = tmpwcs; wint_t wci; - mb_metacharinit(); + mb_charinit(); while (*mb_array) { int mblen = mb_metacharlenconv(mb_array, &wci); @@ -248,7 +248,7 @@ VA_DCL VA_START(ap, message); VA_GET_ARG(ap, message, const char *); - if ((filename = getsparam("ZSH_DEBUG_LOG")) != NULL && + if ((filename = getsparam_u("ZSH_DEBUG_LOG")) != NULL && (file = fopen(filename, "a")) != NULL) { zerrmsg(file, message, ap); fclose(file); @@ -332,7 +332,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) case 'c': num = va_arg(ap, int); #ifdef MULTIBYTE_SUPPORT - mb_metacharinit(); + mb_charinit(); zputs(wcs_nicechar(num, NULL, NULL), file); #else zputs(nicechar(num), file); @@ -461,12 +461,13 @@ static mbstate_t mb_shiftstate; /* * Initialise multibyte state: called before a sequence of - * wcs_nicechar() or mb_metacharlenconv(). + * wcs_nicechar(), mb_metacharlenconv(), or + * mb_charlenconv(). */ /**/ mod_export void -mb_metacharinit(void) +mb_charinit(void) { memset(&mb_shiftstate, 0, sizeof(mb_shiftstate)); } @@ -500,7 +501,7 @@ mb_metacharinit(void) * (but not both). (Note the complication that the wide character * part may contain metafied characters.) * - * The caller needs to call mb_metacharinit() before the first call, to + * The caller needs to call mb_charinit() before the first call, to * set up the multibyte shift state for a range of characters. */ @@ -1948,7 +1949,8 @@ extern char *_mktemp(char *); /* Get a unique filename for use as a temporary file. If "prefix" is * NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the * unique suffix includes a prefixed '.' for improved readability. If - * "use_heap" is true, we allocate the returned name on the heap. */ + * "use_heap" is true, we allocate the returned name on the heap. + * The string passed as "prefix" is expected to be metafied. */ /**/ mod_export char * @@ -1975,6 +1977,9 @@ gettempname(const char *prefix, int use_heap) return ret; } +/* The gettempfile() "prefix" is expected to be metafied, see hist.c + * and gettempname(). */ + /**/ mod_export int gettempfile(const char *prefix, int use_heap, char **tempname) @@ -2873,6 +2878,10 @@ ztrftimebuf(int *bufsizeptr, int decr) * not enough memory --- and return -1. Not guaranteed to be portable, * since the strftime() interface doesn't make any guarantees about * the state of the buffer if it returns zero. + * + * fmt is metafied, but we need to unmetafy it on the fly to + * pass into strftime / combine with the output from strftime. + * The return value in buf is not metafied. */ /**/ @@ -2882,7 +2891,7 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) int hr12; #ifdef HAVE_STRFTIME int decr; - char tmp[4]; + char *fmtstart; #else static char *astr[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; @@ -2893,12 +2902,22 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) char *origbuf = buf; - while (*fmt) - if (*fmt == '%') { + while (*fmt) { + if (*fmt == Meta) { + int chr = fmt[1] ^ 32; + if (ztrftimebuf(&bufsize, 1)) + return -1; + *buf++ = chr; + fmt += 2; + } else if (*fmt == '%') { int strip; int digs = 3; +#ifdef HAVE_STRFTIME + fmtstart = +#endif fmt++; + if (*fmt == '-') { strip = 1; fmt++; @@ -2923,6 +2942,21 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) */ if (ztrftimebuf(&bufsize, 2)) return -1; +#ifdef HAVE_STRFTIME + /* Our internal handling doesn't handle padding and other gnu extensions, + * so here we detect them and pass over to strftime(). We don't want + * to do this unconditionally though, as we have some extensions that + * strftime() doesn't have (%., %f, %L and %K) */ +morefmt: + if (!((fmt - fmtstart == 1) || (fmt - fmtstart == 2 && strip) || *fmt == '.')) { + while (*fmt && strchr("OE^#_-0123456789", *fmt)) + fmt++; + if (*fmt) { + fmt++; + goto strftimehandling; + } + } +#endif switch (*fmt++) { case '.': if (ztrftimebuf(&bufsize, digs)) @@ -2938,10 +2972,10 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) sprintf(buf, "%0*ld", digs, usec); buf += digs; break; - case 'd': - if (tm->tm_mday > 9 || !strip) - *buf++ = '0' + tm->tm_mday / 10; - *buf++ = '0' + tm->tm_mday % 10; + case '\0': + /* Guard against premature end of string */ + *buf++ = '%'; + fmt--; break; case 'f': strip = 1; @@ -2982,6 +3016,11 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) *buf++ = '0' + (hr12 % 10); break; + case 'd': + if (tm->tm_mday > 9 || !strip) + *buf++ = '0' + tm->tm_mday / 10; + *buf++ = '0' + tm->tm_mday % 10; + break; case 'm': if (tm->tm_mon > 8 || !strip) *buf++ = '0' + (tm->tm_mon + 1) / 10; @@ -3002,18 +3041,9 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) *buf++ = '0' + (tm->tm_year / 10) % 10; *buf++ = '0' + tm->tm_year % 10; break; - case '\0': - /* Guard against premature end of string */ - *buf++ = '%'; - fmt--; - break; #ifndef HAVE_STRFTIME case 'Y': { - /* - * Not worth handling this natively if - * strftime has it. - */ int year, digits, testyear; year = tm->tm_year + 1900; digits = 1; @@ -3047,24 +3077,51 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) if (fmt[-1] != '%') *buf++ = fmt[-1]; #else + case 'E': + case 'O': + case '^': + case '#': + case '_': + case '-': + case '0' ... '9': + goto morefmt; +strftimehandling: default: /* * Remember we've already allowed for two characters * in the accounting in bufsize (but nowhere else). */ - *buf = '\1'; - sprintf(tmp, strip ? "%%-%c" : "%%%c", fmt[-1]); - if (!strftime(buf, bufsize + 2, tmp, tm)) { - if (*buf) { - buf[0] = '\0'; - return -1; + int size = fmt - fmtstart; + char *tmp, *last; + tmp = zhalloc(size + 1); + strncpy(tmp, fmtstart, size); + last = fmt-1; + if (*last == Meta) { + /* + * This is for consistency in counting: + * a metafiable character isn't actually + * a valid strftime descriptor. + * + * Previous characters were explicitly checked, + * so can't be metafied. + */ + *last = *++fmt ^ 32; + } + tmp[size] = '\0'; + *buf = '\1'; + if (!strftime(buf, bufsize + 2, tmp, tm)) + { + if (*buf) { + buf[0] = '\0'; + return -1; + } + return 0; } - return 0; + decr = strlen(buf); + buf += decr; + bufsize -= decr - 2; } - decr = strlen(buf); - buf += decr; - bufsize -= decr - 2; #endif break; } @@ -3073,6 +3130,7 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) return -1; *buf++ = *fmt++; } + } *buf = '\0'; return buf - origbuf; } @@ -3555,7 +3613,7 @@ zbeep(void) { char *vb; queue_signals(); - if ((vb = getsparam("ZBEEP"))) { + if ((vb = getsparam_u("ZBEEP"))) { int len; vb = getkeystring(vb, &len, GETKEYS_BINDKEY, NULL); write_loop(SHTTY, vb, len); @@ -3832,7 +3890,7 @@ itype_end(const char *ptr, int itype, int once) #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE) && (itype != IIDENT || !isset(POSIXIDENTIFIERS))) { - mb_metacharinit(); + mb_charinit(); while (*ptr) { wint_t wc; int len = mb_metacharlenconv(ptr, &wc); @@ -4367,7 +4425,10 @@ unmeta(const char *file_name) char *p; const char *t; int newsz, meta; - + + if (!file_name) + return NULL; + meta = 0; for (t = file_name; *t; t++) { if (*t == Meta) @@ -4471,9 +4532,37 @@ ztrlen(char const *s) for (l = 0; *s; l++) { if (*s++ == Meta) { #ifdef DEBUG - if (! *s) + if (! *s) { fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n"); - else + break; + } else +#endif + s++; + } + } + return l; +} + +#ifndef MULTIBYTE_SUPPORT +/* + * ztrlen() but with explicit end point for non-null-terminated + * segments. eptr may not be NULL. + */ + +/**/ +mod_export int +ztrlenend(char const *s, char const *eptr) +{ + int l; + + for (l = 0; s < eptr; l++) { + if (*s++ == Meta) { +#ifdef DEBUG + if (! *s) { + fprintf(stderr, + "BUG: unexpected end of string in ztrlenend()\n"); + break; + } else #endif s++; } @@ -4481,6 +4570,8 @@ ztrlen(char const *s) return l; } +#endif /* MULTIBYTE_SUPPORT */ + /* Subtract two pointers in a metafied string. */ /**/ @@ -4879,11 +4970,16 @@ mb_metacharlenconv(const char *s, wint_t *wcp) * If width is 1, return total character width rather than number. * If width is greater than 1, return 1 if character has non-zero width, * else 0. + * + * Ends if either *ptr is '\0', the normal case (eptr may be NULL for + * this), or ptr is eptr (i.e. *eptr is where the null would be if null + * terminated) for strings not delimited by nulls --- note these are + * still metafied. */ /**/ mod_export int -mb_metastrlen(char *ptr, int width) +mb_metastrlenend(char *ptr, int width, char *eptr) { char inchar, *laststart; size_t ret; @@ -4898,7 +4994,7 @@ mb_metastrlen(char *ptr, int width) num = num_in_char = 0; memset(&mb_shiftstate, 0, sizeof(mb_shiftstate)); - while (*ptr) { + while (*ptr && !(eptr && ptr >= eptr)) { if (*ptr == Meta) inchar = *++ptr ^ 32; else @@ -4937,6 +5033,65 @@ mb_metastrlen(char *ptr, int width) return num + num_in_char; } +/* + * The equivalent of mb_metacharlenconv_r() for + * strings that aren't metafied and hence have + * explicit lengths. + */ + +/**/ +mod_export int +mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp) +{ + size_t ret = MB_INVALID; + char inchar; + const char *ptr; + wchar_t wc; + + for (ptr = s; slen; ) { + inchar = *ptr; + ptr++; + slen--; + ret = mbrtowc(&wc, &inchar, 1, mbsp); + + if (ret == MB_INVALID) + break; + if (ret == MB_INCOMPLETE) + continue; + if (wcp) + *wcp = wc; + return ptr - s; + } + + if (wcp) + *wcp = WEOF; + /* No valid multibyte sequence */ + memset(mbsp, 0, sizeof(*mbsp)); + if (ptr > s) { + return 1; /* Treat as single byte character */ + } else + return 0; /* Probably shouldn't happen */ +} + +/* + * The equivalent of mb_metacharlenconv() for + * strings that aren't metafied and hence have + * explicit lengths; + */ + +/**/ +mod_export int +mb_charlenconv(const char *s, int slen, wint_t *wcp) +{ + if (!isset(MULTIBYTE)) { + if (wcp) + *wcp = (wint_t)*s; + return 1; + } + + return mb_charlenconv_r(s, slen, wcp, &mb_shiftstate); +} + /**/ #else @@ -4961,8 +5116,127 @@ metacharlenconv(const char *x, int *c) return 1; } +/* Simple replacement for mb_charlenconv */ + /**/ +mod_export int +charlenconv(const char *x, int len, int *c) +{ + if (!len) { + if (c) + *c = '\0'; + return 0; + } + + if (c) + *c = (char)*x; + return 1; +} + +/**/ +#endif /* MULTIBYTE_SUPPORT */ + +/* + * Expand tabs to given width, with given starting position on line. + * len is length of unmetafied string in bytes. + * Output to fout. + * Return the end position on the line, i.e. if this is 0 modulo width + * the next character is aligned with a tab stop. + * + * If all is set, all tabs are expanded, else only leading tabs. + */ + +/**/ +mod_export int +zexpandtabs(const char *s, int len, int width, int startpos, FILE *fout, + int all) +{ + int at_start = 1; + +#ifdef MULTIBYTE_SUPPORT + mbstate_t mbs; + size_t ret; + wchar_t wc; + + memset(&mbs, 0, sizeof(mbs)); +#endif + + while (len) { + if (*s == '\t') { + if (all || at_start) { + s++; + len--; + if (width <= 0 || !(startpos % width)) { + /* always output at least one space */ + fputc(' ', fout); + startpos++; + } + if (width <= 0) + continue; /* paranoia */ + while (startpos % width) { + fputc(' ', fout); + startpos++; + } + } else { + /* + * Leave tab alone. + * Guess width to apply... we might get this wrong. + * This is only needed if there's a following string + * that needs tabs expanding, which is unusual. + */ + startpos += width - startpos % width; + s++; + len--; + fputc('\t', fout); + } + continue; + } else if (*s == '\n' || *s == '\r') { + fputc(*s, fout); + s++; + len--; + startpos = 0; + at_start = 1; + continue; + } + + at_start = 0; +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + const char *sstart = s; + ret = mbrtowc(&wc, s, len, &mbs); + if (ret == MB_INVALID) { + /* Assume single character per character */ + memset(&mbs, 0, sizeof(mbs)); + s++; + len--; + } else if (ret == MB_INCOMPLETE) { + /* incomplete at end --- assume likewise, best we've got */ + s++; + len--; + } else { + s += ret; + len -= (int)ret; + } + if (ret == MB_INVALID || ret == MB_INCOMPLETE) { + startpos++; + } else { + int wcw = WCWIDTH(wc); + if (wcw > 0) /* paranoia */ + startpos += wcw; + } + fwrite(sstart, s - sstart, 1, fout); + + continue; + } #endif /* MULTIBYTE_SUPPORT */ + fputc(*s, fout); + s++; + len--; + startpos++; + } + + return startpos; +} /* check for special characters in the string */ @@ -5301,7 +5575,25 @@ quotestring(const char *s, char **e, int instring) /* Needs to be passed straight through. */ if (dobackslash) *v++ = '\\'; - *v++ = *u++; + if (*u == Inparmath) { + /* + * Already syntactically quoted: don't + * add more. + */ + int inmath = 1; + *v++ = *u++; + for (;;) { + char uc = *u; + *v++ = *u++; + if (uc == '\0') + break; + else if (uc == Outparmath && !--inmath) + break; + else if (uc == Inparmath) + ++inmath; + } + } else + *v++ = *u++; continue; } @@ -6148,10 +6440,15 @@ init_dirsav(Dirsav d) d->dirfd = d->level = -1; } -/* Change directory, without following symlinks. Returns 0 on success, -1 * - * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If * - * fchdir() fails, or the current directory is unreadable, we might end up * - * in an unwanted directory in case of failure. */ +/* + * Change directory, without following symlinks. Returns 0 on success, -1 + * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If + * fchdir() fails, or the current directory is unreadable, we might end up + * in an unwanted directory in case of failure. + * + * path is an unmetafied but null-terminated string, as needed by system + * calls. + */ /**/ mod_export int diff --git a/Src/watch.c b/Src/watch.c index fe409f91a..c804913ad 100644 --- a/Src/watch.c +++ b/Src/watch.c @@ -237,6 +237,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) time_t timet; struct tm *tm; char *fm2; + int len; # ifdef WATCH_UTMP_UT_HOST char *p; int i; @@ -330,7 +331,9 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) } timet = getlogtime(u, inout); tm = localtime(&timet); - ztrftime(buf, 40, fm2, tm, 0L); + len = ztrftime(buf, 40, fm2, tm, 0L); + if (len > 0) + metafy(buf, len, META_NOALLOC); printf("%s", (*buf == ' ') ? buf + 1 : buf); break; case '%': @@ -566,7 +569,7 @@ dowatch(void) return; } queue_signals(); - if (!(fmt = getsparam("WATCHFMT"))) + if (!(fmt = getsparam_u("WATCHFMT"))) fmt = DEFAULT_WATCHFMT; while ((uct || wct) && !errflag) if (!uct || (wct && ucmp(uptr, wptr) > 0)) @@ -336,7 +336,8 @@ enum lextok { THEN, /* then */ TIME, /* time */ /* 60 */ UNTIL, /* until */ - WHILE /* while */ + WHILE, /* while */ + TYPESET /* typeset or similar */ }; /* Redirection types. If you modify this, you may also have to modify * @@ -424,6 +425,7 @@ enum { #define INP_HISTCONT (1<<5) /* stack is continued from history expn. */ #define INP_LINENO (1<<6) /* update line number */ #define INP_APPEND (1<<7) /* Append new lines to allow backup */ +#define INP_RAW_KEEP (1<<8) /* Input needed in raw mode even if alias */ /* Flags for metafy */ #define META_REALLOC 0 @@ -671,14 +673,6 @@ struct multio { int fds[MULTIOUNIT]; /* list of src/dests redirected to/from this fd */ }; -/* structure for foo=bar assignments */ - -struct asgment { - struct asgment *next; - char *name; - char *value; -}; - /* lvalue for variable assignment/expansion */ struct value { @@ -789,23 +783,24 @@ struct eccstr { #define WC_REDIR 4 #define WC_ASSIGN 5 #define WC_SIMPLE 6 -#define WC_SUBSH 7 -#define WC_CURSH 8 -#define WC_TIMED 9 -#define WC_FUNCDEF 10 -#define WC_FOR 11 -#define WC_SELECT 12 -#define WC_WHILE 13 -#define WC_REPEAT 14 -#define WC_CASE 15 -#define WC_IF 16 -#define WC_COND 17 -#define WC_ARITH 18 -#define WC_AUTOFN 19 -#define WC_TRY 20 +#define WC_TYPESET 7 +#define WC_SUBSH 8 +#define WC_CURSH 9 +#define WC_TIMED 10 +#define WC_FUNCDEF 11 +#define WC_FOR 12 +#define WC_SELECT 13 +#define WC_WHILE 14 +#define WC_REPEAT 15 +#define WC_CASE 16 +#define WC_IF 17 +#define WC_COND 18 +#define WC_ARITH 19 +#define WC_AUTOFN 20 +#define WC_TRY 21 /* increment as necessary */ -#define WC_COUNT 21 +#define WC_COUNT 22 #define WCB_END() wc_bld(WC_END, 0) @@ -849,6 +844,12 @@ struct eccstr { #define WC_ASSIGN_SCALAR 0 #define WC_ASSIGN_ARRAY 1 #define WC_ASSIGN_NEW 0 +/* + * In normal assignment, this indicate += to append. + * In assignment following a typeset, where that's not allowed, + * we overload this to indicate a variable without an + * assignment. + */ #define WC_ASSIGN_INC 1 #define WC_ASSIGN_NUM(C) (wc_data(C) >> 2) #define WCB_ASSIGN(T,A,N) wc_bld(WC_ASSIGN, ((T) | ((A) << 1) | ((N) << 2))) @@ -856,6 +857,9 @@ struct eccstr { #define WC_SIMPLE_ARGC(C) wc_data(C) #define WCB_SIMPLE(N) wc_bld(WC_SIMPLE, (N)) +#define WC_TYPESET_ARGC(C) wc_data(C) +#define WCB_TYPESET(N) wc_bld(WC_TYPESET, (N)) + #define WC_SUBSH_SKIP(C) wc_data(C) #define WCB_SUBSH(O) wc_bld(WC_SUBSH, (O)) @@ -1140,6 +1144,32 @@ struct alias { /* is this an alias for suffix handling? */ #define ALIAS_SUFFIX (1<<2) +/* structure for foo=bar assignments */ + +struct asgment { + struct linknode node; + char *name; + int is_array; + union { + char *scalar; + LinkList array; + } value; +}; + +/* + * Assignment is array? + */ +#define ASG_ARRAYP(asg) ((asg)->is_array) + +/* + * 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. + */ + +#define ASG_VALUEP(asg) (ASG_ARRAYP(asg) || \ + ((asg)->value.scalar != (char *)0)) + /* node in command path hash table (cmdnamtab) */ struct cmdnam { @@ -1268,6 +1298,7 @@ struct options { */ typedef int (*HandlerFunc) _((char *, char **, Options, int)); +typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int)); #define NULLBINCMD ((HandlerFunc) 0) struct builtin { @@ -1311,6 +1342,12 @@ struct builtin { * does not terminate options. */ #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 + * assignment structures. + */ +#define BINF_ASSIGN (1<<19) struct module { struct hashnode node; @@ -1715,9 +1752,10 @@ struct tieddata { * necessarily want to match multiple * elements */ -#define SCANPM_ISVAR_AT ((-1)<<15) /* "$foo[@]"-style substitution - * Only sign bit is significant - */ +/* "$foo[@]"-style substitution + * Only sign bit is significant + */ +#define SCANPM_ISVAR_AT ((int)(((unsigned int)-1)<<15)) /* * Flags for doing matches inside parameter substitutions, i.e. @@ -2084,6 +2122,7 @@ enum { CHASELINKS, CHECKJOBS, CLOBBER, + APPENDCREATE, COMBININGCHARS, COMPLETEALIASES, COMPLETEINWORD, @@ -2779,6 +2818,7 @@ struct parse_stack { int incasepat; int isnewlin; int infor; + int intypeset; int eclen, ecused, ecnpats; Wordcode ecbuf; @@ -2921,14 +2961,22 @@ enum { #define AFTERTRAPHOOK (zshhooks + 2) #ifdef MULTIBYTE_SUPPORT +/* Metafied input */ #define nicezputs(str, outs) (void)mb_niceformat((str), (outs), NULL, 0) -#define MB_METACHARINIT() mb_metacharinit() +#define MB_METACHARINIT() mb_charinit() typedef wint_t convchar_t; #define MB_METACHARLENCONV(str, cp) mb_metacharlenconv((str), (cp)) #define MB_METACHARLEN(str) mb_metacharlenconv(str, NULL) -#define MB_METASTRLEN(str) mb_metastrlen(str, 0) -#define MB_METASTRWIDTH(str) mb_metastrlen(str, 1) -#define MB_METASTRLEN2(str, widthp) mb_metastrlen(str, widthp) +#define MB_METASTRLEN(str) mb_metastrlenend(str, 0, NULL) +#define MB_METASTRWIDTH(str) mb_metastrlenend(str, 1, NULL) +#define MB_METASTRLEN2(str, widthp) mb_metastrlenend(str, widthp, NULL) +#define MB_METASTRLEN2END(str, widthp, eptr) \ + mb_metastrlenend(str, widthp, eptr) + +/* Unmetafined input */ +#define MB_CHARINIT() mb_charinit() +#define MB_CHARLENCONV(str, len, cp) mb_charlenconv((str), (len), (cp)) +#define MB_CHARLEN(str, len) mb_charlenconv((str), (len), NULL) /* * We replace broken implementations with one that uses Unicode @@ -3011,6 +3059,11 @@ typedef int convchar_t; #define MB_METASTRLEN(str) ztrlen(str) #define MB_METASTRWIDTH(str) ztrlen(str) #define MB_METASTRLEN2(str, widthp) ztrlen(str) +#define MB_METASTRLEN2END(str, widthp, eptr) ztrlenend(str, eptr) + +#define MB_CHARINIT() +#define MB_CHARLENCONV(str, len, cp) charlenconv((str), (len), (cp)) +#define MB_CHARLEN(str, len) ((len) ? 1 : 0) #define WCWIDTH_WINT(c) (1) |