diff options
Diffstat (limited to 'Src')
66 files changed, 2357 insertions, 1556 deletions
diff --git a/Src/Builtins/rlimits.awk b/Src/Builtins/rlimits.awk deleted file mode 100644 index e9c576c66..000000000 --- a/Src/Builtins/rlimits.awk +++ /dev/null @@ -1,116 +0,0 @@ -# -# rlimits.awk: {g,n}awk script to generate rlimits.h -# -# NB: On SunOS 4.1.3 - user-functions don't work properly, also \" problems -# Without 0 + hacks some nawks compare numbers as strings -# -BEGIN {limidx = 0} - -/^[\t ]*(#[\t ]*define[\t _]*RLIMIT_[A-Z_]*[\t ]*[0-9][0-9]*|RLIMIT_[A-Z_]*,[\t ]*|_*RLIMIT_[A-Z_]*[\t ]*=[\t ]*[0-9][0-9]*,[\t ]*)/ { - limindex = index($0, "RLIMIT_") - limtail = substr($0, limindex, 80) - split(limtail, tmp) - limnam = substr(tmp[1], 8, 20) - limnum = tmp[2] - # in this case I assume GNU libc resourcebits.h - if (limnum == "") { - limnum = limidx++ - limindex = index($0, ",") - limnam = substr(limnam, 1, limindex-1) - } - if (limnum == "=") { - if (tmp[3] ~ /^[0-9]/) { - limnum = tmp[3] + 0 - } else { - limnum = limidx++ - } - limindex = index($0, ",") - limnam = substr(limnam, 1, limindex-1) - } - limrev[limnam] = limnum - if (lim[limnum] == "") { - lim[limnum] = limnam - if (limnum ~ /^[0-9]*$/) { - if (limnam == "AIO_MEM") { msg[limnum] = "Maiomemorylocked" } - if (limnam == "AIO_OPS") { msg[limnum] = "Naiooperations" } - if (limnam == "AS") { msg[limnum] = "Maddressspace" } - if (limnam == "CORE") { msg[limnum] = "Mcoredumpsize" } - if (limnam == "CPU") { msg[limnum] = "Tcputime" } - if (limnam == "DATA") { msg[limnum] = "Mdatasize" } - if (limnam == "FSIZE") { msg[limnum] = "Mfilesize" } - if (limnam == "LOCKS") { msg[limnum] = "Nmaxfilelocks" } - if (limnam == "MEMLOCK") { msg[limnum] = "Mmemorylocked" } - if (limnam == "NOFILE") { msg[limnum] = "Ndescriptors" } - if (limnam == "NPROC") { msg[limnum] = "Nmaxproc" } - if (limnam == "NTHR") { msg[limnum] = "Nmaxpthreads" } - if (limnam == "OFILE") { msg[limnum] = "Ndescriptors" } - if (limnam == "PTHREAD") { msg[limnum] = "Nmaxpthreads" } - if (limnam == "RSS") { msg[limnum] = "Mresident" } - if (limnam == "SBSIZE") { msg[limnum] = "Msockbufsize" } - if (limnam == "STACK") { msg[limnum] = "Mstacksize" } - if (limnam == "TCACHE") { msg[limnum] = "Ncachedthreads" } - if (limnam == "VMEM") { msg[limnum] = "Mvmemorysize" } - if (limnam == "SIGPENDING") { msg[limnum] = "Nsigpending" } - if (limnam == "MSGQUEUE") { msg[limnum] = "Nmsgqueue" } - if (limnam == "NICE") { msg[limnum] = "Nnice" } - if (limnam == "RTPRIO") { msg[limnum] = "Nrt_priority" } - if (limnam == "RTTIME") { msg[limnum] = "Urt_time" } - if (limnam == "POSIXLOCKS") { msg[limnum] = "Nposixlocks" } - if (limnam == "NPTS") { msg[limnum] = "Npseudoterminals" } - if (limnam == "SWAP") { msg[limnum] = "Mswapsize" } - if (limnam == "KQUEUES") { msg[limnum] = "Nkqueues" } - if (limnam == "UMTXP") { msg[limnum] = "Numtxp" } - } - } -} -/^[\t ]*#[\t ]*define[\t _]*RLIM_NLIMITS[\t ]*[0-9][0-9]*/ { - limindex = index($0, "RLIM_") - limtail = substr($0, limindex, 80) - split(limtail, tmp) - nlimits = tmp[2] -} -# in case of GNU libc -/^[\t ]*RLIM_NLIMITS[\t ]*=[\t ]*RLIMIT_NLIMITS/ { - if(!nlimits) { nlimits = limidx } -} -/^[\t _]*RLIM(IT)?_NLIMITS[\t ]*=[\t ]*[0-9][0-9]*/ { - limindex = index($0, "=") - limtail = substr($0, limindex, 80) - split(limtail, tmp) - nlimits = tmp[2] -} - -END { - if (limrev["MEMLOCK"] != "") { - irss = limrev["RSS"] - msg[irss] = "Mmemoryuse" - } - ps = "%s" - - printf("%s\n%s\n\n", "/** rlimits.h **/", "/** architecture-customized limits for zsh **/") - printf("#define ZSH_NLIMITS %d\n\nstatic char const *recs[ZSH_NLIMITS] = {\n", 0 + nlimits) - - for (i = 0; i < 0 + nlimits; i++) - if (msg[i] == "") - printf("\t%c%s%c,\n", 34, lim[i], 34) - else - printf("\t%c%s%c,\n", 34, substr(msg[i], 2, 30), 34) - print "};" - print "" - print "static int limtype[ZSH_NLIMITS] = {" - for (i = 0; i < 0 + nlimits; i++) { - if (msg[i] == "") - limtype = "UNKNOWN" - else { - limtype = substr(msg[i], 1, 1) - if(limtype == "M") { limtype = "MEMORY" } - if(limtype == "N") { limtype = "NUMBER" } - if(limtype == "T") { limtype = "TIME" } - if(limtype == "U") { limtype = "MICROSECONDS" } - } - printf("\tZLIMTYPE_%s,\n", limtype) - } - print "};" - - exit(0) -} diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index 6b552f3a9..5f9c84b0f 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -32,20 +32,7 @@ #if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY) -#if defined(HAVE_RLIMIT_POSIXLOCKS) && !defined(HAVE_RLIMIT_LOCKS) -# define RLIMIT_LOCKS RLIMIT_POSIXLOCKS -# define HAVE_RLIMIT_LOCKS 1 -#endif - -#if defined(HAVE_RLIMIT_NTHR) && !defined(HAVE_RLIMIT_PTHREAD) -# define RLIMIT_PTHREAD RLIMIT_NTHR -# define HAVE_RLIMIT_PTHREAD 1 -# define THREAD_FMT "-T: threads " -#else -# define THREAD_FMT "-T: threads per process " -#endif - -enum { +enum zlimtype { ZLIMTYPE_MEMORY, ZLIMTYPE_NUMBER, ZLIMTYPE_TIME, @@ -53,11 +40,222 @@ enum { ZLIMTYPE_UNKNOWN }; -/* Generated rec array containing limits required for the limit builtin. * - * They must appear in this array in numerical order of the RLIMIT_* macros. */ +typedef struct resinfo_T { + int res; /* RLIMIT_XXX */ + char* name; /* used by limit builtin */ + enum zlimtype type; + int unit; /* 1, 512, or 1024 */ + char opt; /* option character */ + char* descr; /* used by ulimit builtin */ +} resinfo_T; + +/* table of known resources */ +/* + * How to add a new resource: + * 1. Add zsh_LIMIT_PRESENT(RLIMIT_XXX) in configure.ac. + * 2. Add an entry for RLIMIT_XXX to known_resources[]. + * Make sure the option letter (resinto_T.opt) is unique. + * 3. Build zsh and run the test B12rlimit.ztst. + */ +static const resinfo_T known_resources[] = { + {RLIMIT_CPU, "cputime", ZLIMTYPE_TIME, 1, + 't', "cpu time (seconds)"}, + {RLIMIT_FSIZE, "filesize", ZLIMTYPE_MEMORY, 512, + 'f', "file size (blocks)"}, + {RLIMIT_DATA, "datasize", ZLIMTYPE_MEMORY, 1024, + 'd', "data seg size (kbytes)"}, + {RLIMIT_STACK, "stacksize", ZLIMTYPE_MEMORY, 1024, + 's', "stack size (kbytes)"}, + {RLIMIT_CORE, "coredumpsize", ZLIMTYPE_MEMORY, 512, + 'c', "core file size (blocks)"}, +# ifdef HAVE_RLIMIT_NOFILE + {RLIMIT_NOFILE, "descriptors", ZLIMTYPE_NUMBER, 1, + 'n', "file descriptors"}, +# endif +# if defined(HAVE_RLIMIT_AS) && !defined(RLIMIT_VMEM_IS_AS) + {RLIMIT_AS, "addressspace", ZLIMTYPE_MEMORY, 1024, + 'v', "address space (kbytes)"}, +# endif +# if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS) && !defined(RLIMIT_RSS_IS_AS) + {RLIMIT_RSS, "resident", ZLIMTYPE_MEMORY, 1024, + 'm', "resident set size (kbytes)"}, +# endif +# if defined(HAVE_RLIMIT_VMEM) + {RLIMIT_VMEM, +# if defined(RLIMIT_VMEM_IS_RSS) + "resident", ZLIMTYPE_MEMORY, 1024, + 'm', "memory size (kbytes)" +# else + "vmemorysize", ZLIMTYPE_MEMORY, 1024, + 'v', "virtual memory size (kbytes)" +# endif + }, +# endif +# ifdef HAVE_RLIMIT_NPROC + {RLIMIT_NPROC, "maxproc", ZLIMTYPE_NUMBER, 1, + 'u', "processes"}, +# endif +# ifdef HAVE_RLIMIT_MEMLOCK + {RLIMIT_MEMLOCK, "memorylocked", ZLIMTYPE_MEMORY, 1024, + 'l', "locked-in-memory size (kbytes)"}, +# endif + /* Linux */ +# ifdef HAVE_RLIMIT_LOCKS + {RLIMIT_LOCKS, "maxfilelocks", ZLIMTYPE_NUMBER, 1, + 'x', "file locks"}, +# endif +# ifdef HAVE_RLIMIT_SIGPENDING + {RLIMIT_SIGPENDING, "sigpending", ZLIMTYPE_NUMBER, 1, + 'i', "pending signals"}, +# endif +# ifdef HAVE_RLIMIT_MSGQUEUE + {RLIMIT_MSGQUEUE, "msgqueue", ZLIMTYPE_NUMBER, 1, + 'q', "bytes in POSIX msg queues"}, +# endif +# ifdef HAVE_RLIMIT_NICE + {RLIMIT_NICE, "nice", ZLIMTYPE_NUMBER, 1, + 'e', "max nice"}, +# endif +# ifdef HAVE_RLIMIT_RTPRIO + {RLIMIT_RTPRIO, "rt_priority", ZLIMTYPE_NUMBER, 1, + 'r', "max rt priority"}, +# endif +# ifdef HAVE_RLIMIT_RTTIME + {RLIMIT_RTTIME, "rt_time", ZLIMTYPE_MICROSECONDS, 1, + 'N', "rt cpu time (microseconds)"}, +# endif + /* BSD */ +# ifdef HAVE_RLIMIT_SBSIZE + {RLIMIT_SBSIZE, "sockbufsize", ZLIMTYPE_MEMORY, 1, + 'b', "socket buffer size (bytes)"}, +# endif +# ifdef HAVE_RLIMIT_KQUEUES /* FreeBSD */ + {RLIMIT_KQUEUES, "kqueues", ZLIMTYPE_NUMBER, 1, + 'k', "kqueues"}, +# endif +# ifdef HAVE_RLIMIT_NPTS /* FreeBSD */ + {RLIMIT_NPTS, "pseudoterminals", ZLIMTYPE_NUMBER, 1, + 'p', "pseudo-terminals"}, +# endif +# ifdef HAVE_RLIMIT_SWAP /* FreeBSD */ + {RLIMIT_SWAP, "swapsize", ZLIMTYPE_MEMORY, 1024, + 'w', "swap size (kbytes)"}, +# endif +# ifdef HAVE_RLIMIT_UMTXP /* FreeBSD */ + {RLIMIT_UMTXP, "umtxp", ZLIMTYPE_NUMBER, 1, + 'o', "umtx shared locks"}, +# endif + +# ifdef HAVE_RLIMIT_POSIXLOCKS /* DragonFly */ + {RLIMIT_POSIXLOCKS, "posixlocks", ZLIMTYPE_NUMBER, 1, + 'x', "number of POSIX locks"}, +# endif +# if defined(HAVE_RLIMIT_NTHR) && !defined(HAVE_RLIMIT_RTPRIO) /* Net/OpenBSD */ + {RLIMIT_NTHR, "maxpthreads", ZLIMTYPE_NUMBER, 1, + 'r', "threads"}, +# endif + /* others */ +# if defined(HAVE_RLIMIT_PTHREAD) && !defined(HAVE_RLIMIT_NTHR) /* IRIX ? */ + {RLIMIT_PTHREAD, "maxpthreads", ZLIMTYPE_NUMBER, 1, + 'T', "threads per process"}, +# endif +# ifdef HAVE_RLIMIT_AIO_MEM /* HP-UX ? */ + {RLIMIT_AIO_MEM, "aiomemorylocked", ZLIMTYPE_MEMORY, 1024, + 'N', "AIO locked-in-memory (kbytes)"}, +# endif +# ifdef HAVE_RLIMIT_AIO_OPS /* HP-UX ? */ + {RLIMIT_AIO_OPS, "aiooperations", ZLIMTYPE_NUMBER, 1, + 'N', "AIO operations"}, +# endif +# ifdef HAVE_RLIMIT_TCACHE /* HP-UX ? */ + {RLIMIT_TCACHE, "cachedthreads", ZLIMTYPE_NUMBER, 1, + 'N', "cached threads"}, +# endif +}; + +/* resinfo[RLIMIT_XXX] points to the corresponding entry + * in known_resources[] */ +static const resinfo_T **resinfo; -# include "rlimits.h" +/**/ +static void +set_resinfo(void) +{ + int i; + + resinfo = (const resinfo_T **)zshcalloc(RLIM_NLIMITS*sizeof(resinfo_T *)); + + for (i=0; i<sizeof(known_resources)/sizeof(resinfo_T); ++i) { + resinfo[known_resources[i].res] = &known_resources[i]; + } + for (i=0; i<RLIM_NLIMITS; ++i) { + if (!resinfo[i]) { + /* unknown resource */ + resinfo_T *info = (resinfo_T *)zshcalloc(sizeof(resinfo_T)); + char *buf = (char *)zalloc(12); + snprintf(buf, 12, "UNKNOWN-%d", i); + info->res = - 1; /* negative value indicates "unknown" */ + info->name = buf; + info->type = ZLIMTYPE_UNKNOWN; + info->unit = 1; + info->opt = 'N'; + info->descr = buf; + resinfo[i] = info; + } + } +} +/**/ +static void +free_resinfo(void) +{ + int i; + for (i=0; i<RLIM_NLIMITS; ++i) { + if (resinfo[i]->res < 0) { /* unknown resource */ + free(resinfo[i]->name); + free((void*)resinfo[i]); + } + } + free(resinfo); + resinfo = NULL; +} + +/* Find resource by its option character */ + +/**/ +static int +find_resource(char c) +{ + int i; + for (i=0; i<RLIM_NLIMITS; ++i) { + if (resinfo[i]->opt == c) + return i; + } + return -1; +} + +/* Print a value of type rlim_t */ + +/**/ +static void +printrlim(rlim_t val, const char *unit) +{ +# ifdef RLIM_T_IS_QUAD_T + printf("%qd%s", val, unit); +# else +# ifdef RLIM_T_IS_LONG_LONG + printf("%lld%s", val, unit); +# else +# ifdef RLIM_T_IS_UNSIGNED + printf("%lu%s", (unsigned long)val, unit); +# else + printf("%ld%s", (long)val, unit); +# endif /* RLIM_T_IS_UNSIGNED */ +# endif /* RLIM_T_IS_LONG_LONG */ +# endif /* RLIM_T_IS_QUAD_T */ +} + +/**/ static rlim_t zstrtorlimt(const char *s, char **t, int base) { @@ -97,8 +295,8 @@ static void showlimitvalue(int lim, rlim_t val) { /* display limit for resource number lim */ - if (lim < ZSH_NLIMITS) - printf("%-16s", recs[lim]); + if (lim < RLIM_NLIMITS) + printf("%-16s", resinfo[lim]->name); else { /* Unknown limit, hence unknown units. */ @@ -106,81 +304,25 @@ showlimitvalue(int lim, rlim_t val) } if (val == RLIM_INFINITY) printf("unlimited\n"); - else if (lim >= ZSH_NLIMITS) - { -# ifdef RLIM_T_IS_QUAD_T - printf("%qd\n", val); -# else -# ifdef RLIM_T_IS_LONG_LONG - printf("%lld\n", val); -# else -# ifdef RLIM_T_IS_UNSIGNED - printf("%lu\n", (unsigned long)val); -# else - printf("%ld\n", (long)val); -# endif /* RLIM_T_IS_UNSIGNED */ -# endif /* RLIM_T_IS_LONG_LONG */ -# endif /* RLIM_T_IS_QUAD_T */ - } - else if (limtype[lim] == ZLIMTYPE_TIME) { + else if (lim >= RLIM_NLIMITS) + printrlim(val, "\n"); + else if (resinfo[lim]->type == ZLIMTYPE_TIME) { /* time-type resource -- display as hours, minutes and seconds. */ printf("%d:%02d:%02d\n", (int)(val / 3600), (int)(val / 60) % 60, (int)(val % 60)); - } else if (limtype[lim] == ZLIMTYPE_MICROSECONDS) { - /* microseconds */ -# ifdef RLIM_T_IS_QUAD_T - printf("%qdus\n", val); -# else -# ifdef RLIM_T_IS_LONG_LONG - printf("%lldus\n", val); -# else -# ifdef RLIM_T_IS_UNSIGNED - printf("%luus\n", (unsigned long)val); -# else - printf("%ldus\n", (long)val); -# endif /* RLIM_T_IS_UNSIGNED */ -# endif /* RLIM_T_IS_LONG_LONG */ -# endif /* RLIM_T_IS_QUAD_T */ - } else if (limtype[lim] == ZLIMTYPE_NUMBER || - limtype[lim] == ZLIMTYPE_UNKNOWN) { - /* pure numeric resource */ -# ifdef RLIM_T_IS_QUAD_T - printf("%qd\n", val); -# else -# ifdef RLIM_T_IS_LONG_LONG - printf("%lld\n", val); -# else -# ifdef RLIM_T_IS_UNSIGNED - printf("%lu\n", (unsigned long)val); -# else - printf("%ld\n", (long)val); -# endif /* RLIM_T_IS_UNSIGNED */ -# endif /* RLIM_T_IS_LONG_LONG */ -# endif /* RLIM_T_IS_QUAD_T */ - } else if (val >= 1024L * 1024L) - /* memory resource -- display with `K' or `M' modifier */ -# ifdef RLIM_T_IS_QUAD_T - printf("%qdMB\n", val / (1024L * 1024L)); - else - printf("%qdkB\n", val / 1024L); -# else -# ifdef RLIM_T_IS_LONG_LONG - printf("%lldMB\n", val / (1024L * 1024L)); - else - printf("%lldkB\n", val / 1024L); -# else -# ifdef RLIM_T_IS_UNSIGNED - printf("%luMB\n", (unsigned long)(val / (1024L * 1024L))); - else - printf("%lukB\n", (unsigned long)(val / 1024L)); -# else - printf("%ldMB\n", (long)val / (1024L * 1024L)); - else - printf("%ldkB\n", (long)val / 1024L); -# endif /* RLIM_T_IS_UNSIGNED */ -# endif /* RLIM_T_IS_LONG_LONG */ -# endif /* RLIM_T_IS_QUAD_T */ + } else if (resinfo[lim]->type == ZLIMTYPE_MICROSECONDS) + printrlim(val, "us\n"); /* microseconds */ + else if (resinfo[lim]->type == ZLIMTYPE_NUMBER || + resinfo[lim]->type == ZLIMTYPE_UNKNOWN) + printrlim(val, "\n"); /* pure numeric resource */ + else { + /* memory resource -- display with `k' or `M' modifier */ + if (val >= 1024L * 1024L) + printrlim(val/(1024L * 1024L), "MB\n"); + else + printrlim(val/1024L, "kB\n"); + } } /* Display resource limits. hard indicates whether `hard' or `soft' * @@ -193,7 +335,7 @@ showlimits(char *nam, int hard, int lim) { int rt; - if (lim >= ZSH_NLIMITS) + if (lim >= RLIM_NLIMITS) { /* * Not configured into the shell. Ask the OS @@ -215,7 +357,7 @@ showlimits(char *nam, int hard, int lim) else { /* main loop over resource types */ - for (rt = 0; rt != ZSH_NLIMITS; rt++) + for (rt = 0; rt != RLIM_NLIMITS; rt++) showlimitvalue(rt, (hard) ? limits[rt].rlim_max : limits[rt].rlim_cur); } @@ -234,7 +376,7 @@ printulimit(char *nam, int lim, int hard, int head) rlim_t limit; /* get the limit in question */ - if (lim >= ZSH_NLIMITS) + if (lim >= RLIM_NLIMITS) { struct rlimit vals; @@ -248,199 +390,25 @@ printulimit(char *nam, int lim, int hard, int head) else limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur; /* display the appropriate heading */ - switch (lim) { - case RLIMIT_CORE: - if (head) - printf("-c: core file size (blocks) "); - if (limit != RLIM_INFINITY) - limit /= 512; - break; - case RLIMIT_DATA: - if (head) - printf("-d: data seg size (kbytes) "); - if (limit != RLIM_INFINITY) - limit /= 1024; - break; - case RLIMIT_FSIZE: - if (head) - printf("-f: file size (blocks) "); - if (limit != RLIM_INFINITY) - limit /= 512; - break; -# ifdef HAVE_RLIMIT_SIGPENDING - case RLIMIT_SIGPENDING: - if (head) - printf("-i: pending signals "); - break; -# endif -# ifdef HAVE_RLIMIT_MEMLOCK - case RLIMIT_MEMLOCK: - if (head) - printf("-l: locked-in-memory size (kbytes) "); - if (limit != RLIM_INFINITY) - limit /= 1024; - break; -# endif /* HAVE_RLIMIT_MEMLOCK */ -/* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid * - * duplicate case statement. Observed on QNX Neutrino 6.1.0. */ -# if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS) && !defined(RLIMIT_RSS_IS_AS) - case RLIMIT_RSS: - if (head) - printf("-m: resident set size (kbytes) "); - if (limit != RLIM_INFINITY) - limit /= 1024; - break; -# endif /* HAVE_RLIMIT_RSS */ -# if defined(HAVE_RLIMIT_VMEM) && defined(HAVE_RLIMIT_RSS) && defined(RLIMIT_VMEM_IS_RSS) - case RLIMIT_VMEM: - if (head) - printf("-m: memory size (kbytes) "); - if (limit != RLIM_INFINITY) - limit /= 1024; - break; -# endif /* HAVE_RLIMIT_VMEM */ -# ifdef HAVE_RLIMIT_NOFILE - case RLIMIT_NOFILE: - if (head) - printf("-n: file descriptors "); - break; -# endif /* HAVE_RLIMIT_NOFILE */ -# ifdef HAVE_RLIMIT_MSGQUEUE - case RLIMIT_MSGQUEUE: - if (head) - printf("-q: bytes in POSIX msg queues "); - break; -# endif - case RLIMIT_STACK: - if (head) - printf("-s: stack size (kbytes) "); - if (limit != RLIM_INFINITY) - limit /= 1024; - break; - case RLIMIT_CPU: - if (head) - printf("-t: cpu time (seconds) "); - break; -# ifdef HAVE_RLIMIT_NPROC - case RLIMIT_NPROC: - if (head) - printf("-u: processes "); - break; -# endif /* HAVE_RLIMIT_NPROC */ -# if defined(HAVE_RLIMIT_VMEM) && (!defined(HAVE_RLIMIT_RSS) || !defined(RLIMIT_VMEM_IS_RSS)) - case RLIMIT_VMEM: - if (head) - printf("-v: virtual memory size (kbytes) "); - if (limit != RLIM_INFINITY) - limit /= 1024; - break; -# endif /* HAVE_RLIMIT_VMEM */ -# if defined HAVE_RLIMIT_AS && !defined(RLIMIT_VMEM_IS_AS) - case RLIMIT_AS: - if (head) - printf("-v: address space (kbytes) "); - if (limit != RLIM_INFINITY) - limit /= 1024; - break; -# endif /* HAVE_RLIMIT_AS */ -# ifdef HAVE_RLIMIT_LOCKS - case RLIMIT_LOCKS: - if (head) - printf("-x: file locks "); - break; -# endif /* HAVE_RLIMIT_LOCKS */ -# ifdef HAVE_RLIMIT_AIO_MEM - case RLIMIT_AIO_MEM: - if (head) - printf("-N %2d: AIO locked-in-memory (kbytes)", RLIMIT_AIO_MEM); - if (limit != RLIM_INFINITY) - limit /= 1024; - break; -# endif /* HAVE_RLIMIT_AIO_MEM */ -# ifdef HAVE_RLIMIT_AIO_OPS - case RLIMIT_AIO_OPS: - if (head) - printf("-N %2d: AIO operations ", RLIMIT_AIO_OPS); - break; -# endif /* HAVE_RLIMIT_AIO_OPS */ -# ifdef HAVE_RLIMIT_TCACHE - case RLIMIT_TCACHE: - if (head) - printf("-N %2d: cached threads ", RLIMIT_TCACHE); - break; -# endif /* HAVE_RLIMIT_TCACHE */ -# ifdef HAVE_RLIMIT_SBSIZE - case RLIMIT_SBSIZE: - if (head) - printf("-b: socket buffer size (bytes) "); - break; -# endif /* HAVE_RLIMIT_SBSIZE */ -# ifdef HAVE_RLIMIT_PTHREAD - case RLIMIT_PTHREAD: - if (head) - printf("%s", THREAD_FMT); - break; -# endif /* HAVE_RLIMIT_PTHREAD */ -# ifdef HAVE_RLIMIT_NICE - case RLIMIT_NICE: - if (head) - printf("-e: max nice "); - break; -# endif /* HAVE_RLIMIT_NICE */ -# ifdef HAVE_RLIMIT_RTPRIO - case RLIMIT_RTPRIO: - if (head) - printf("-r: max rt priority "); - break; -# endif /* HAVE_RLIMIT_RTPRIO */ -# ifdef HAVE_RLIMIT_NPTS - case RLIMIT_NPTS: - if (head) - printf("-p: pseudo-terminals "); - break; -# endif /* HAVE_RLIMIT_NPTS */ -# ifdef HAVE_RLIMIT_SWAP - case RLIMIT_SWAP: - if (head) - printf("-w: swap size (kbytes) "); - if (limit != RLIM_INFINITY) - limit /= 1024; - break; -# endif /* HAVE_RLIMIT_SWAP */ -# ifdef HAVE_RLIMIT_KQUEUES - case RLIMIT_KQUEUES: - if (head) - printf("-k: kqueues "); - break; -# endif /* HAVE_RLIMIT_KQUEUES */ -# ifdef HAVE_RLIMIT_UMTXP - case RLIMIT_UMTXP: - if (head) - printf("-o: umtx shared locks "); - break; -# endif /* HAVE_RLIMIT_UMTXP */ - default: - if (head) - printf("-N %2d: ", lim); - break; + if (head) { + if (lim < RLIM_NLIMITS) { + const resinfo_T *info = resinfo[lim]; + if (info->opt == 'N') + printf("-N %2d: %-29s", lim, info->descr); + else + printf("-%c: %-32s", info->opt, info->descr); + } + else + printf("-N %2d: %-29s", lim, ""); } /* display the limit */ if (limit == RLIM_INFINITY) printf("unlimited\n"); else { -# ifdef RLIM_T_IS_QUAD_T - printf("%qd\n", limit); -# else -# ifdef RLIM_T_IS_LONG_LONG - printf("%lld\n", limit); -# else -# ifdef RLIM_T_IS_UNSIGNED - printf("%lu\n", (unsigned long)limit); -# else - printf("%ld\n", (long)limit); -# endif /* RLIM_T_IS_UNSIGNED */ -# endif /* RLIM_T_IS_LONG_LONG */ -# endif /* RLIM_T_IS_QUAD_T */ + if (lim < RLIM_NLIMITS) + printrlim(limit/resinfo[lim]->unit, "\n"); + else + printrlim(limit, "\n"); } return 0; @@ -450,7 +418,7 @@ printulimit(char *nam, int lim, int hard, int head) static int do_limit(char *nam, int lim, rlim_t val, int hard, int soft, int set) { - if (lim >= ZSH_NLIMITS) { + if (lim >= RLIM_NLIMITS) { struct rlimit vals; if (getrlimit(lim, &vals) < 0) { @@ -558,8 +526,8 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func)) lim = (int)zstrtol(s, NULL, 10); } else - for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++) - if (!strncmp(recs[limnum], s, strlen(s))) { + for (lim = -1, limnum = 0; limnum < RLIM_NLIMITS; limnum++) + if (!strncmp(resinfo[limnum]->name, s, strlen(s))) { if (lim != -1) lim = -2; else @@ -576,7 +544,7 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func)) /* without value for limit, display the current limit */ if (!(s = *argv++)) return showlimits(nam, hard, lim); - if (lim >= ZSH_NLIMITS) + if (lim >= RLIM_NLIMITS) { val = zstrtorlimt(s, &s, 10); if (*s) @@ -586,7 +554,7 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func)) return 1; } } - else if (limtype[lim] == ZLIMTYPE_TIME) { + else if (resinfo[lim]->type == ZLIMTYPE_TIME) { /* time-type resource -- may be specified as seconds, or minutes or * * hours with the `m' and `h' modifiers, and `:' may be used to add * * together more than one of these. It's easier to understand from * @@ -604,9 +572,9 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func)) return 1; } } - } else if (limtype[lim] == ZLIMTYPE_NUMBER || - limtype[lim] == ZLIMTYPE_UNKNOWN || - limtype[lim] == ZLIMTYPE_MICROSECONDS) { + } else if (resinfo[lim]->type == ZLIMTYPE_NUMBER || + resinfo[lim]->type == ZLIMTYPE_UNKNOWN || + resinfo[lim]->type == ZLIMTYPE_MICROSECONDS) { /* pure numeric resource -- only a straight decimal number is permitted. */ char *t = s; @@ -642,7 +610,7 @@ static int do_unlimit(char *nam, int lim, int hard, int soft, int set, int euid) { /* remove specified limit */ - if (lim >= ZSH_NLIMITS) { + if (lim >= RLIM_NLIMITS) { struct rlimit vals; if (getrlimit(lim, &vals) < 0) { @@ -718,8 +686,8 @@ bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func)) if (idigit(**argv)) { lim = (int)zstrtol(*argv, NULL, 10); } else { - for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++) - if (!strncmp(recs[limnum], *argv, strlen(*argv))) { + for (lim = -1, limnum = 0; limnum < RLIM_NLIMITS; limnum++) + if (!strncmp(resinfo[limnum]->name, *argv, strlen(*argv))) { if (lim != -1) lim = -2; else @@ -800,116 +768,14 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) resmask = (1 << RLIM_NLIMITS) - 1; nres = RLIM_NLIMITS; continue; - case 't': - res = RLIMIT_CPU; - break; - case 'f': - res = RLIMIT_FSIZE; - break; - case 'd': - res = RLIMIT_DATA; - break; - case 's': - res = RLIMIT_STACK; - break; - case 'c': - res = RLIMIT_CORE; - break; -# ifdef HAVE_RLIMIT_SBSIZE - case 'b': - res = RLIMIT_SBSIZE; - break; -# endif /* HAVE_RLIMIT_SBSIZE */ -# ifdef HAVE_RLIMIT_MEMLOCK - case 'l': - res = RLIMIT_MEMLOCK; - break; -# endif /* HAVE_RLIMIT_MEMLOCK */ -# ifdef HAVE_RLIMIT_RSS - case 'm': - res = RLIMIT_RSS; - break; -# endif /* HAVE_RLIMIT_RSS */ -# ifdef HAVE_RLIMIT_NOFILE - case 'n': - res = RLIMIT_NOFILE; - break; -# endif /* HAVE_RLIMIT_NOFILE */ -# ifdef HAVE_RLIMIT_NPROC - case 'u': - res = RLIMIT_NPROC; - break; -# endif /* HAVE_RLIMIT_NPROC */ -# if defined(HAVE_RLIMIT_VMEM) || defined(HAVE_RLIMIT_AS) - case 'v': -# ifdef HAVE_RLIMIT_VMEM - res = RLIMIT_VMEM; -# else - res = RLIMIT_AS; -# endif - break; -# endif /* HAVE_RLIMIT_VMEM */ -# ifdef HAVE_RLIMIT_LOCKS - case 'x': - res = RLIMIT_LOCKS; - break; -# endif -# ifdef HAVE_RLIMIT_SIGPENDING - case 'i': - res = RLIMIT_SIGPENDING; - break; -# endif -# ifdef HAVE_RLIMIT_MSGQUEUE - case 'q': - res = RLIMIT_MSGQUEUE; - break; -# endif -# ifdef HAVE_RLIMIT_NICE - case 'e': - res = RLIMIT_NICE; - break; -# endif -# ifdef HAVE_RLIMIT_RTPRIO - case 'r': - res = RLIMIT_RTPRIO; - break; -# else -# ifdef HAVE_RLIMIT_NTHR - /* For compatibility with sh on NetBSD */ - case 'r': - res = RLIMIT_NTHR; - break; -# endif /* HAVE_RLIMIT_NTHR */ -# endif -# ifdef HAVE_RLIMIT_NPTS - case 'p': - res = RLIMIT_NPTS; - break; -# endif -# ifdef HAVE_RLIMIT_SWAP - case 'w': - res = RLIMIT_SWAP; - break; -# endif -# ifdef HAVE_RLIMIT_KQUEUES - case 'k': - res = RLIMIT_KQUEUES; - break; -# endif -# ifdef HAVE_RLIMIT_PTHREAD - case 'T': - res = RLIMIT_PTHREAD; - break; -# endif -# ifdef HAVE_RLIMIT_UMTXP - case 'o': - res = RLIMIT_UMTXP; - break; -# endif default: - /* unrecognised limit */ - zwarnnam(name, "bad option: -%c", *options); - return 1; + res = find_resource(*options); + if (res < 0) { + /* unrecognised limit */ + zwarnnam(name, "bad option: -%c", *options); + return 1; + } + break; } if (options[1]) { resmask |= 1 << res; @@ -961,34 +827,8 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) return 1; } /* scale appropriately */ - switch (res) { - case RLIMIT_FSIZE: - case RLIMIT_CORE: - limit *= 512; - break; - case RLIMIT_DATA: - case RLIMIT_STACK: -# ifdef HAVE_RLIMIT_RSS - case RLIMIT_RSS: -# endif /* HAVE_RLIMIT_RSS */ -# ifdef HAVE_RLIMIT_MEMLOCK - case RLIMIT_MEMLOCK: -# endif /* HAVE_RLIMIT_MEMLOCK */ -/* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid * - * duplicate case statement. Observed on QNX Neutrino 6.1.0. */ -# if defined(HAVE_RLIMIT_VMEM) && !defined(RLIMIT_VMEM_IS_RSS) - case RLIMIT_VMEM: -# endif /* HAVE_RLIMIT_VMEM */ -/* ditto RLIMIT_VMEM and RLIMIT_AS */ -# if defined(HAVE_RLIMIT_AS) && !defined(RLIMIT_VMEM_IS_AS) && !defined(RLIMIT_RSS_IS_AS) - case RLIMIT_AS: -# endif /* HAVE_RLIMIT_AS */ -# ifdef HAVE_RLIMIT_AIO_MEM - case RLIMIT_AIO_MEM: -# endif /* HAVE_RLIMIT_AIO_MEM */ - limit *= 1024; - break; - } + if (res < RLIM_NLIMITS) + limit *= resinfo[res]->unit; } if (do_limit(name, res, limit, hard, soft, 1)) ret++; @@ -1052,6 +892,7 @@ enables_(Module m, int **enables) int boot_(UNUSED(Module m)) { + set_resinfo(); return 0; } @@ -1059,6 +900,7 @@ boot_(UNUSED(Module m)) int cleanup_(Module m) { + free_resinfo(); return setfeatureenables(m, &module_features, NULL); } diff --git a/Src/Builtins/rlimits.mdd b/Src/Builtins/rlimits.mdd index 9e6e9e598..06c9e9c7f 100644 --- a/Src/Builtins/rlimits.mdd +++ b/Src/Builtins/rlimits.mdd @@ -6,18 +6,3 @@ autofeatures="b:limit b:ulimit b:unlimit" autofeatures_emu="b:ulimit" objects="rlimits.o" - -:<<\Make -rlimits.o rlimits..o: rlimits.h - -# this file will not be made if limits are unavailable -rlimits.h: rlimits.awk @RLIMITS_INC_H@ - $(AWK) -f $(sdir)/rlimits.awk @RLIMITS_INC_H@ /dev/null > rlimits.h - @if grep ZLIMTYPE_UNKNOWN rlimits.h >/dev/null; then \ - echo >&2 WARNING: unknown limits: mail Src/Builtins/rlimits.h to developers; \ - else :; fi - -clean-here: clean.rlimits -clean.rlimits: - rm -f rlimits.h -Make diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index 19f285e34..e46903916 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -1212,7 +1212,7 @@ zccmd_input(const char *nam, char **args) addlinknode(margs, "CTRL"); if (mevent.bstate & BUTTON_ALT) addlinknode(margs, "ALT"); - if (!setaparam(args[3], zlinklist2array(margs))) + if (!setaparam(args[3], zlinklist2array(margs, 1))) return 1; } else { #endif @@ -1464,7 +1464,7 @@ zccmd_querychar(const char *nam, char **args) } /* Turn this into an array and store it. */ - return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist)); + return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist, 1)); } diff --git a/Src/Modules/curses_keys.awk b/Src/Modules/curses_keys.awk index ffb182c35..25bd63c7e 100644 --- a/Src/Modules/curses_keys.awk +++ b/Src/Modules/curses_keys.awk @@ -12,8 +12,13 @@ BEGIN {nkeydefs = 0} END { printf("static const struct zcurses_namenumberpair keypad_names[] = {\n") - for (i = 0; i < 0 + nkeydefs; i++) + for (i = 0; i < 0 + nkeydefs; i++) { + if (name[i] == "EVENT") + printf("#ifdef KEY_EVENT\n") printf(" {\"%s\", KEY_%s},\n", name[i], name[i]) + if (name[i] == "EVENT") + printf("#endif\n") + } printf(" {NULL, 0}\n") printf("};\n") } diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c index 521c15a5b..085e4cc26 100644 --- a/Src/Modules/datetime.c +++ b/Src/Modules/datetime.c @@ -175,7 +175,8 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func)) setsparam(scalar, metafy(buffer, len, META_DUP)); } else { fwrite(buffer, 1, len, stdout); - putchar('\n'); + if (!OPT_ISSET(ops,'n')) + putchar('\n'); } zfree(buffer, bufsize); @@ -235,7 +236,7 @@ getcurrenttime(UNUSED(Param pm)) } static struct builtin bintab[] = { - BUILTIN("strftime", 0, bin_strftime, 1, 3, 0, "qrs:", NULL), + BUILTIN("strftime", 0, bin_strftime, 1, 3, 0, "nqrs:", NULL), }; static const struct gsu_integer epochseconds_gsu = diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index b8e7c76c6..7e11ec939 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -233,7 +233,7 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func)) /**/ static int -bin_zgdbmpath(char *nam, char **args, Options ops, UNUSED(int func)) +bin_zgdbmpath(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { Param pm; char *pmname; @@ -332,6 +332,8 @@ gdbmgetfn(Param pm) /* Can return pointer, correctly saved inside hash */ return pm->u.str; + } else { + pm->node.flags |= PM_DEFAULTED; } /* Free key */ diff --git a/Src/Modules/files.c b/Src/Modules/files.c index 6d20e38a8..bf0e8f8a8 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -32,12 +32,6 @@ typedef int (*MoveFunc) _((char const *, char const *)); typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *)); -#ifndef STDC_HEADERS -extern int link _((const char *, const char *)); -extern int symlink _((const char *, const char *)); -extern int rename _((const char *, const char *)); -#endif - struct recursivecmd; #include "files.pro" @@ -122,19 +116,29 @@ domkdir(char *nam, char *path, mode_t mode, int p) { int err; mode_t oumask; + struct stat st; + int n = 8; char const *rpath = unmeta(path); - if(p) { - struct stat st; - - if(!stat(rpath, &st) && S_ISDIR(st.st_mode)) + while(n-- > 0) { + oumask = umask(0); + err = mkdir(rpath, mode) ? errno : 0; + umask(oumask); + if (!err) return 0; + if(!p || err != EEXIST) + break; + if(stat(rpath, &st)) { + if(errno == ENOENT) + continue; + err = errno; + break; + } + if(S_ISDIR(st.st_mode)) + return 0; + break; } - oumask = umask(0); - err = mkdir(rpath, mode) ? errno : 0; - umask(oumask); - if(!err) - return 0; + zwarnnam(nam, "cannot make directory `%s': %e", path, err); return 1; } @@ -342,7 +346,13 @@ domove(char *nam, MoveFunc movefn, char *p, char *q, int flags) unlink(qbuf); } if(movefn(pbuf, qbuf)) { - zwarnnam(nam, "%s: %e", p, errno); + int ferrno = errno; + char *errfile = p; + if (ferrno == ENOENT && !lstat(pbuf, &st)) { + /* p *does* exist, so error is in q */ + errfile = q; + } + zwarnnam(nam, "`%s': %e", errfile, ferrno); zsfree(pbuf); return 1; } @@ -642,7 +652,7 @@ chmod_dochmod(char *arg, char *rp, UNUSED(struct stat const *sp), void *magic) /**/ static int -bin_chmod(char *nam, char **args, Options ops, int func) +bin_chmod(char *nam, char **args, Options ops, UNUSED(int func)) { struct chmodmagic chm; char *str = args[0], *ptr; diff --git a/Src/Modules/nearcolor.c b/Src/Modules/nearcolor.c index b49ee9afb..d50a6bb44 100644 --- a/Src/Modules/nearcolor.c +++ b/Src/Modules/nearcolor.c @@ -188,7 +188,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { addhookfunc("get_color_attr", (Hookfn) getnearestcolor); return 0; diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 86416c5c5..c53839152 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -125,7 +125,7 @@ makeprivate(HashNode hn, UNUSED(int flags)) break; } /* PM_HIDE so new parameters in deeper scopes do not shadow */ - pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE); + pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE|PM_RO_BY_DESIGN); pm->level -= 1; } } @@ -171,6 +171,7 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func) { int from_typeset = 1; int ofake = fakelevel; /* paranoia in case of recursive call */ + int hasargs = /* *args != NULL || */ (assigns && firstnode(assigns)); makeprivate_error = 0; if (!OPT_ISSET(ops, 'P')) { @@ -189,7 +190,12 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func) return bin_typeset("private", args, assigns, ops, func); } - ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */ + if (!(OPT_ISSET(ops, 'm') || OPT_ISSET(ops, '+'))) + ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */ + if (OPT_ISSET(ops, 'p') || OPT_ISSET(ops, 'm') || + (!hasargs && OPT_ISSET(ops, '+'))) { + return bin_typeset("private", args, assigns, ops, func); + } queue_signals(); fakelevel = locallevel; @@ -555,7 +561,7 @@ printprivatenode(HashNode hn, int printflags) static struct builtin bintab[] = { /* Copied from BUILTIN("local"), "P" added */ - BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lprtux", "P") + BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lmprtux", "P") }; static struct features module_features = { diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index ef9148d7b..dbb61e474 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -592,7 +592,7 @@ getpmfunction_source(HashTable ht, const char *name) return getfunction_source(ht, name, 0); } -/* Param table entry for retrieving ds_functions_source element */ +/* Param table entry for retrieving dis_functions_source element */ /**/ static HashNode @@ -1244,19 +1244,19 @@ histwgetfn(UNUSED(Param pm)) /**/ static char * -pmjobtext(int job) +pmjobtext(Job jtab, int job) { Process pn; int len = 1; char *ret; - for (pn = jobtab[job].procs; pn; pn = pn->next) + for (pn = jtab[job].procs; pn; pn = pn->next) len += strlen(pn->text) + 3; ret = (char *) zhalloc(len); ret[0] = '\0'; - for (pn = jobtab[job].procs; pn; pn = pn->next) { + for (pn = jtab[job].procs; pn; pn = pn->next) { strcat(ret, pn->text); if (pn->next) strcat(ret, " | "); @@ -1269,22 +1269,25 @@ static HashNode getpmjobtext(UNUSED(HashTable ht), const char *name) { Param pm = NULL; - int job; + int job, jmax; char *pend; + Job jtab; pm = (Param) hcalloc(sizeof(struct param)); pm->node.nam = dupstring(name); pm->node.flags = PM_SCALAR | PM_READONLY; pm->gsu.s = &nullsetscalar_gsu; + selectjobtab(&jtab, &jmax); + job = strtod(name, &pend); /* Non-numeric keys are looked up by job name */ if (*pend) job = getjob(name, NULL); - if (job >= 1 && job <= maxjob && - jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) - pm->u.str = pmjobtext(job); + if (job >= 1 && job <= jmax && + jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobtext(jtab, job); else { pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); @@ -1297,22 +1300,25 @@ static void scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; - int job; + int job, jmax; char buf[40]; + Job jtab; memset((void *)&pm, 0, sizeof(struct param)); pm.node.flags = PM_SCALAR | PM_READONLY; pm.gsu.s = &nullsetscalar_gsu; - for (job = 1; job <= maxjob; job++) { - if (jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) { + selectjobtab(&jtab, &jmax); + + for (job = 1; job <= jmax; job++) { + if (jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) { if (func != scancountparams) { sprintf(buf, "%d", job); pm.node.nam = dupstring(buf); if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || !(flags & SCANPM_WANTKEYS)) - pm.u.str = pmjobtext(job); + pm.u.str = pmjobtext(jtab, job); } func(&pm.node, flags); } @@ -1323,7 +1329,7 @@ scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags) /**/ static char * -pmjobstate(int job) +pmjobstate(Job jtab, int job) { Process pn; char buf[256], buf2[128], *ret, *state, *cp; @@ -1335,14 +1341,14 @@ pmjobstate(int job) else cp = ":"; - if (jobtab[job].stat & STAT_DONE) + if (jtab[job].stat & STAT_DONE) ret = dyncat("done", cp); - else if (jobtab[job].stat & STAT_STOPPED) + else if (jtab[job].stat & STAT_STOPPED) ret = dyncat("suspended", cp); else ret = dyncat("running", cp); - for (pn = jobtab[job].procs; pn; pn = pn->next) { + for (pn = jtab[job].procs; pn; pn = pn->next) { if (pn->status == SP_RUNNING) state = "running"; @@ -1371,21 +1377,24 @@ static HashNode getpmjobstate(UNUSED(HashTable ht), const char *name) { Param pm = NULL; - int job; + int job, jmax; char *pend; + Job jtab; pm = (Param) hcalloc(sizeof(struct param)); pm->node.nam = dupstring(name); pm->node.flags = PM_SCALAR | PM_READONLY; pm->gsu.s = &nullsetscalar_gsu; + selectjobtab(&jtab, &jmax); + job = strtod(name, &pend); if (*pend) job = getjob(name, NULL); - if (job >= 1 && job <= maxjob && - jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) - pm->u.str = pmjobstate(job); + if (job >= 1 && job <= jmax && + jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobstate(jtab, job); else { pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); @@ -1398,22 +1407,25 @@ static void scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; - int job; + int job, jmax; + Job jtab; char buf[40]; + selectjobtab(&jtab, &jmax); + memset((void *)&pm, 0, sizeof(struct param)); pm.node.flags = PM_SCALAR | PM_READONLY; pm.gsu.s = &nullsetscalar_gsu; - for (job = 1; job <= maxjob; job++) { - if (jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) { + for (job = 1; job <= jmax; job++) { + if (jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) { if (func != scancountparams) { sprintf(buf, "%d", job); pm.node.nam = dupstring(buf); if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || !(flags & SCANPM_WANTKEYS)) - pm.u.str = pmjobstate(job); + pm.u.str = pmjobstate(jtab, job); } func(&pm.node, flags); } @@ -1424,11 +1436,11 @@ scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags) /**/ static char * -pmjobdir(int job) +pmjobdir(Job jtab, int job) { char *ret; - ret = dupstring(jobtab[job].pwd ? jobtab[job].pwd : pwd); + ret = dupstring(jtab[job].pwd ? jtab[job].pwd : pwd); return ret; } @@ -1437,21 +1449,24 @@ static HashNode getpmjobdir(UNUSED(HashTable ht), const char *name) { Param pm = NULL; - int job; + int job, jmax; char *pend; + Job jtab; pm = (Param) hcalloc(sizeof(struct param)); pm->node.nam = dupstring(name); pm->node.flags = PM_SCALAR | PM_READONLY; pm->gsu.s = &nullsetscalar_gsu; + selectjobtab(&jtab, &jmax); + job = strtod(name, &pend); if (*pend) job = getjob(name, NULL); - if (job >= 1 && job <= maxjob && - jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) - pm->u.str = pmjobdir(job); + if (job >= 1 && job <= jmax && + jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobdir(jtab, job); else { pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); @@ -1464,22 +1479,25 @@ static void scanpmjobdirs(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; - int job; + int job, jmax; char buf[40]; + Job jtab; memset((void *)&pm, 0, sizeof(struct param)); pm.node.flags = PM_SCALAR | PM_READONLY; pm.gsu.s = &nullsetscalar_gsu; - for (job = 1; job <= maxjob; job++) { - if (jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) { + selectjobtab(&jtab, &jmax); + + for (job = 1; job <= jmax; job++) { + if (jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) { if (func != scancountparams) { sprintf(buf, "%d", job); pm.node.nam = dupstring(buf); if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || !(flags & SCANPM_WANTKEYS)) - pm.u.str = pmjobdir(job); + pm.u.str = pmjobdir(jtab, job); } func(&pm.node, flags); } @@ -2011,6 +2029,9 @@ scanpmdissaliases(HashTable ht, ScanFunc func, int flags) /**/ static Groupset get_all_groups(void) { +#ifdef DISABLE_DYNAMIC_NSS + return NULL; +#else Groupset gs = zhalloc(sizeof(*gs)); Groupmap gaptr; gid_t *list, *lptr, egid; @@ -2063,6 +2084,7 @@ static Groupset get_all_groups(void) } return gs; +#endif /* DISABLE_DYNAMIC_NSS */ } /* Standard hash element lookup. */ @@ -2081,7 +2103,11 @@ getpmusergroups(UNUSED(HashTable ht), const char *name) pm->gsu.s = &nullsetscalar_gsu; if (!gs) { +#ifdef DISABLE_DYNAMIC_NSS + zerr("parameter 'usergroups' not available: NSS is disabled"); +#else zerr("failed to retrieve groups for user: %e", errno); +#endif pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); return &pm->node; @@ -2113,7 +2139,11 @@ scanpmusergroups(UNUSED(HashTable ht), ScanFunc func, int flags) Groupmap gaptr; if (!gs) { +#ifdef DISABLE_DYNAMIC_NSS + zerr("parameter 'usergroups' not available: NSS is disabled"); +#else zerr("failed to retrieve groups for user: %e", errno); +#endif return; } diff --git a/Src/Modules/system.c b/Src/Modules/system.c index fb3d80773..71745548f 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -29,6 +29,7 @@ #include "system.mdh" #include "system.pro" +#include <math.h> #ifdef HAVE_POLL_H # include <poll.h> @@ -279,7 +280,7 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func)) } -static struct { char *name; int oflag; } openopts[] = { +static struct { const char *name; int oflag; } openopts[] = { #ifdef O_CLOEXEC { "cloexec", O_CLOEXEC }, #else @@ -296,6 +297,9 @@ static struct { char *name; int oflag; } openopts[] = { #ifdef O_NOATIME { "noatime", O_NOATIME }, #endif +#ifdef O_NONBLOCK + { "nonblock", O_NONBLOCK}, +#endif { "excl", O_EXCL | O_CREAT }, { "creat", O_CREAT }, { "create", O_CREAT }, @@ -531,7 +535,9 @@ static int bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { int cloexec = 1, unlock = 0, readlock = 0; - zlong timeout = -1; + double timeout = -1; + long timeout_interval = 1e6; + mnumber timeout_param; char *fdvar = NULL; #ifdef HAVE_FCNTL_H struct flock lck; @@ -583,7 +589,51 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } else { optarg = *args++; } - timeout = mathevali(optarg); + timeout_param = matheval(optarg); + timeout = (timeout_param.type & MN_FLOAT) ? + timeout_param.u.d : (double)timeout_param.u.l; + + /* + * timeout must not overflow time_t, but little is known + * about this type's limits. Conservatively limit to 2^30-1 + * (34 years). Then it can only overflow if time_t is only + * a 32-bit int and CLOCK_MONOTONIC is not supported, in which + * case there is a Y2038 problem anyway. + */ + if (timeout > 1073741823.) { + zwarnnam(nam, "flock: invalid timeout value: '%s'", + optarg); + return 1; + } + break; + + case 'i': + /* retry interval in seconds */ + if (optptr[1]) { + optarg = optptr + 1; + optptr += strlen(optarg) - 1; + } else if (!*args) { + zwarnnam(nam, + "flock: option %c requires " + "a numeric retry interval", + opt); + return 1; + } else { + optarg = *args++; + } + timeout_param = matheval(optarg); + if (!(timeout_param.type & MN_FLOAT)) { + timeout_param.type = MN_FLOAT; + timeout_param.u.d = (double)timeout_param.u.l; + } + timeout_param.u.d = ceil(timeout_param.u.d * 1e6); + if (timeout_param.u.d < 1 + || timeout_param.u.d > 0.999 * LONG_MAX) { + zwarnnam(nam, "flock: invalid interval value: '%s'", + optarg); + return 1; + } + timeout_interval = (long)timeout_param.u.d; break; case 'u': @@ -647,7 +697,24 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) lck.l_len = 0; /* lock the whole file */ if (timeout > 0) { - time_t end = time(NULL) + (time_t)timeout; + /* + * Get current time, calculate timeout time. + * No need to check for overflow, already checked above. + */ + struct timespec now, end; + double timeout_s; + long remaining_us; + zgettime_monotonic_if_available(&now); + end.tv_sec = now.tv_sec; + end.tv_nsec = now.tv_nsec; + end.tv_nsec += ceil(modf(timeout, &timeout_s) * 1000000000L); + end.tv_sec += timeout_s; + if (end.tv_nsec >= 1000000000L) { + end.tv_nsec -= 1000000000L; + end.tv_sec += 1; + } + + /* Try acquiring lock, loop until timeout. */ while (fcntl(flock_fd, F_SETLK, &lck) < 0) { if (errflag) { zclose(flock_fd); @@ -658,11 +725,16 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) zwarnnam(nam, "failed to lock file %s: %e", args[0], errno); return 1; } - if (time(NULL) >= end) { + zgettime_monotonic_if_available(&now); + remaining_us = timespec_diff_us(&now, &end); + if (remaining_us <= 0) { zclose(flock_fd); return 2; } - sleep(1); + if (remaining_us <= timeout_interval) { + timeout_interval = remaining_us; + } + zsleep(timeout_interval); } } else { while (fcntl(flock_fd, timeout == 0 ? F_SETLK : F_SETLKW, &lck) < 0) { diff --git a/Src/watch.c b/Src/Modules/watch.c index 93b3cb134..d45c3cf3d 100644 --- a/Src/watch.c +++ b/Src/Modules/watch.c @@ -27,7 +27,7 @@ * */ -#include "zsh.mdh" +#include "watch.mdh" /* Headers for utmp/utmpx structures */ #ifdef HAVE_UTMP_H @@ -139,9 +139,6 @@ # define DEFAULT_WATCHFMT "%n has %a %l." #endif /* !WATCH_UTMP_UT_HOST */ -/**/ -char const * const default_watchfmt = DEFAULT_WATCHFMT; - #ifdef WATCH_STRUCT_UTMP # include "watch.pro" @@ -152,11 +149,14 @@ char const * const default_watchfmt = DEFAULT_WATCHFMT; static int wtabsz = 0; static WATCH_STRUCT_UTMP *wtab = NULL; + +/* the last time we checked the people in the WATCH variable */ +static time_t lastwatch; + static time_t lastutmpcheck = 0; /* get the time of login/logout for WATCH */ -/**/ static time_t getlogtime(WATCH_STRUCT_UTMP *u, int inout) { @@ -169,9 +169,9 @@ getlogtime(WATCH_STRUCT_UTMP *u, int inout) return u->ut_time; if (!(in = fopen(WATCH_WTMP_FILE, "r"))) return time(NULL); - fseek(in, 0, 2); + fseek(in, 0, SEEK_END); do { - if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), 1)) { + if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), SEEK_CUR)) { fclose(in); return time(NULL); } @@ -202,7 +202,6 @@ getlogtime(WATCH_STRUCT_UTMP *u, int inout) # define BEGIN3 '(' # define END3 ')' -/**/ static char * watch3ary(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt) { @@ -247,6 +246,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) struct tm *tm; char *fm2; int len; + zattr atr; # ifdef WATCH_UTMP_UT_HOST char *p; int i; @@ -348,6 +348,40 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) case '%': putchar('%'); break; + case 'F': + if (*fmt == '{') { + fmt++; + atr = match_colour((const char**)&fmt, 1, 0); + if (*fmt == '}') + fmt++; + if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) { + txtunset(TXT_ATTR_FG_COL_MASK); + txtset(atr & TXT_ATTR_FG_ON_MASK); + set_colour_attribute(atr, COL_SEQ_FG, TSC_RAW); + } + } + break; + case 'f': + txtunset(TXT_ATTR_FG_ON_MASK); + set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_RAW); + break; + case 'K': + if (*fmt == '{') { + fmt++; + atr = match_colour((const char**)&fmt, 0, 0); + if (*fmt == '}') + fmt++; + if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) { + txtunset(TXT_ATTR_BG_COL_MASK); + txtset(atr & TXT_ATTR_BG_ON_MASK); + set_colour_attribute(atr, COL_SEQ_BG, TSC_RAW); + } + } + break; + case 'k': + txtunset(TXT_ATTR_BG_ON_MASK); + set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_RAW); + break; case 'S': txtset(TXTSTANDOUT); tsetcap(TCSTANDOUTBEG, TSC_RAW); @@ -407,7 +441,6 @@ watchlog_match(char *teststr, char *actual, int len) /* check the List for login/logouts */ -/**/ static void watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt) { @@ -470,7 +503,6 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt) /* compare 2 utmp entries */ -/**/ static int ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v) { @@ -481,7 +513,6 @@ ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v) /* initialize the user List */ -/**/ static int readwtab(WATCH_STRUCT_UTMP **head, int initial_sz) { @@ -592,10 +623,19 @@ dowatch(void) wtab = utab; wtabsz = utabsz; fflush(stdout); + lastwatch = time(NULL); +} + +static void +checksched(void) +{ + /* Do nothing if WATCH is not set, or LOGCHECK has not elapsed */ + if (watch && (int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) + dowatch(); } /**/ -int +static int bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func)) { if (!watch) @@ -611,16 +651,109 @@ bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int #else /* !WATCH_STRUCT_UTMP */ -/**/ -void dowatch(void) +static void +checksched(void) { } /**/ -int +static int bin_log(char *nam, char **argv, Options ops, int func) { return bin_notavail(nam, argv, ops, func); } #endif /* !WATCH_STRUCT_UTMP */ + +/**/ +static char **watch; /* $watch */ + +/* module setup */ + +static struct builtin bintab[] = { + BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), +}; + +static struct paramdef partab[] = { + PARAMDEF("WATCH", PM_SCALAR|PM_SPECIAL, &watch, NULL), + PARAMDEF("watch", PM_ARRAY|PM_SPECIAL, &watch, NULL), +}; + +static struct features module_features = { + bintab, sizeof(bintab)/sizeof(*bintab), + NULL, 0, + NULL, 0, + partab, sizeof(partab)/sizeof(*partab), + 0 +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + /* On Cygwin, colonarr_gsu exists in libzsh.dll and we can't + * use &colonarr_gsu in the initialization of partab[] above */ + partab[0].gsu = (void *)&colonarr_gsu; + partab[1].gsu = (void *)&vararray_gsu; + return 0; +} + +/**/ +int +features_(Module m, char ***features) +{ + *features = featuresarray(m, &module_features); + return 0; +} + +/**/ +int +enables_(Module m, int **enables) +{ + return handlefeatures(m, &module_features, enables); +} + +/**/ +int +boot_(UNUSED(Module m)) +{ + static char const * const default_watchfmt = DEFAULT_WATCHFMT; + + Param pma = (Param) paramtab->getnode(paramtab, "watch"); + Param pms = (Param) paramtab->getnode(paramtab, "WATCH"); + if (pma && pms && pma->u.arr == watch && pms->u.arr == watch) { + /* only tie the two parameters if both were added */ + pma->ename = "WATCH"; + pms->ename = "watch"; + pma->node.flags |= PM_TIED; + pms->node.flags |= PM_TIED; + } + watch = mkarray(NULL); + + /* These two parameters are only set to defaults if not set. + * So setting them in .zshrc will not be enough to load the + * module. It's useless until the watch array is set anyway. */ + if (!paramtab->getnode(paramtab, "WATCHFMT")) + setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt)); + if (!paramtab->getnode(paramtab, "LOGCHECK")) + setiparam("LOGCHECK", 60); + + addprepromptfn(&checksched); + + return 0; +} + +/**/ +int +cleanup_(Module m) +{ + delprepromptfn(&checksched); + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} diff --git a/Src/Modules/watch.mdd b/Src/Modules/watch.mdd new file mode 100644 index 000000000..7e8454ede --- /dev/null +++ b/Src/Modules/watch.mdd @@ -0,0 +1,7 @@ +name=zsh/watch +link=dynamic +load=yes + +autofeatures="b:log p:WATCH p:watch" + +objects="watch.o" diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c index bc97771c0..56cdab888 100644 --- a/Src/Modules/zprof.c +++ b/Src/Modules/zprof.c @@ -213,7 +213,25 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func)) return 0; } -/**/ +static char * +name_for_anonymous_function(char *name) +{ + char lineno[DIGBUFSIZE]; + char *parts[7]; + + convbase(lineno, funcstack[0].flineno, 10); + + parts[0] = name; + parts[1] = " ["; + parts[2] = funcstack[0].filename ? funcstack[0].filename : ""; + parts[3] = ":"; + parts[4] = lineno; + parts[5] = "]"; + parts[6] = NULL; + + return sepjoin(parts, "", 1); +} + static int zprof_wrapper(Eprog prog, FuncWrap w, char *name) { @@ -224,12 +242,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name) struct timeval tv; struct timezone dummy; double prev = 0, now; + char *name_for_lookups; + + if (is_anonymous_function_name(name)) { + name_for_lookups = name_for_anonymous_function(name); + } else { + name_for_lookups = name; + } if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) { active = 1; - if (!(f = findpfunc(name))) { + if (!(f = findpfunc(name_for_lookups))) { f = (Pfunc) zalloc(sizeof(*f)); - f->name = ztrdup(name); + f->name = ztrdup(name_for_lookups); f->calls = 0; f->time = f->self = 0.0; f->next = calls; diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 45fd15ee0..dfd2a2a7a 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -30,6 +30,13 @@ #include "zpty.mdh" #include "zpty.pro" +#ifdef __CYGWIN__ +#include <cygwin/version.h> +#if defined(CYGWIN_VERSION_DLL_MAJOR) && CYGWIN_VERSION_DLL_MAJOR<3002 +#define USE_CYGWIN_FIX 1 +#endif +#endif + /* The number of bytes we normally read when given no pattern and the * upper bound on the number of bytes we read (even if we are give a * pattern). */ @@ -428,6 +435,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) mypid = 0; /* trick to ensure we _exit() */ zexit(lastval, ZEXIT_NORMAL); } +#ifndef USE_CYGWIN_FIX master = movefd(master); if (master == -1) { zerrnam(nam, "cannot duplicate fd %d: %e", master, errno); @@ -435,6 +443,9 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) ineval = oineval; return 1; } +#else + addmodulefd(master, FDT_INTERNAL); +#endif p = (Ptycmd) zalloc(sizeof(*p)); diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 24659cb16..2f17c03f1 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -96,7 +96,7 @@ struct stypat { Stypat next; char *pat; /* pattern string */ Patprog prog; /* compiled pattern */ - int weight; /* how specific is the pattern? */ + zulong weight; /* how specific is the pattern? */ Eprog eval; /* eval-on-retrieve? */ char **vals; }; @@ -200,7 +200,8 @@ printstylenode(HashNode hn, int printflags) else { printf("zstyle %s", (p->eval ? "-e " : "")); quotedzputs(p->pat, stdout); - printf(" %s", s->node.nam); + putchar(' '); + quotedzputs(s->node.nam, stdout); } for (v = p->vals; *v; v++) { putchar(' '); @@ -293,7 +294,9 @@ newzstyletable(int size, char const *name) static int setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) { - int weight, tmp, first; + zulong weight; + int tmp; + int first; char *str; Stypat p, q, qq; Eprog eprog = NULL; @@ -348,6 +351,12 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) * - A component that's a literal string scores 2 points. * - The score of a pattern is the sum of the score of its components. * + * The result of this calculation is stored in the low bits of 'weight'. + * The high bits of 'weight' are used to store the number of ':'-separated + * components. This provides a lexicographic comparison: first compare + * the number of components, and if that's equal, compare the specificity + * of the components. + * * This corresponds to the notion of 'more specific' in the zshmodules(1) * documentation of zstyle. */ @@ -367,6 +376,7 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) if (*str == ':') { /* Yet another component. */ + weight += ZLONG_CONST(1) << (CHAR_BIT * sizeof(weight) / 2); first = 1; weight += tmp; @@ -766,10 +776,12 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) * ousedp (*outp)[*ousedp] is where to write next * olenp *olenp is the size allocated for *outp * endchar Terminator character in addition to `\0' (may be '\0') + * presence -F: Ternary expressions test emptyness instead * skip If 1, don't output, just parse. */ static char *zformat_substring(char* instr, char **specs, char **outp, - int *ousedp, int *olenp, int endchar, int skip) + int *ousedp, int *olenp, int endchar, + int presence, int skip) { char *s; @@ -797,27 +809,35 @@ static char *zformat_substring(char* instr, char **specs, char **outp, if ((*s == '.' || testit) && idigit(s[1])) { for (max = 0, s++; idigit(*s); s++) max = (max * 10) + (int) STOUC(*s) - '0'; - } - else if (testit) + } else if (*s == '.' || testit) s++; if (testit && STOUC(*s)) { int actval, testval, endcharl; - /* - * One one number is useful for ternary expressions. - * Remember to put the sign back. - */ + /* Only one number is useful for ternary expressions. */ testval = (min >= 0) ? min : (max >= 0) ? max : 0; - if (right) - testval *= -1; - if (specs[STOUC(*s)]) - actval = (int)mathevali(specs[STOUC(*s)]); - else - actval = 0; - /* zero means values are equal, i.e. true */ - actval -= testval; + if (specs[STOUC(*s)] && *specs[STOUC(*s)]) { + if (presence) { + if (testval) +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) + actval = MB_METASTRWIDTH(specs[STOUC(*s)]); + else +#endif + actval = strlen(specs[STOUC(*s)]); + else + actval = 1; + actval = right ? (testval < actval) : (testval >= actval); + } else { + if (right) /* put the sign back */ + testval *= -1; + /* zero means values are equal, i.e. true */ + actval = (int)mathevali(specs[STOUC(*s)]) - testval; + } + } else + actval = presence ? !right : testval; /* careful about premature end of string */ if (!(endcharl = *++s)) @@ -828,10 +848,10 @@ static char *zformat_substring(char* instr, char **specs, char **outp, * vice versa... unless we are already skipping. */ if (!(s = zformat_substring(s+1, specs, outp, ousedp, - olenp, endcharl, skip || actval))) + olenp, endcharl, presence, skip || actval))) return NULL; if (!(s = zformat_substring(s+1, specs, outp, ousedp, - olenp, ')', skip || !actval))) + olenp, ')', presence, skip || !actval))) return NULL; } else if (skip) { continue; @@ -903,6 +923,7 @@ static int bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { char opt; + int presence = 0; if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) { zwarnnam(nam, "invalid argument: %s", args[0]); @@ -911,15 +932,18 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) args++; switch (opt) { + case 'F': + presence = 1; + /* fall-through */ case 'f': { - char **ap, *specs[256], *out; + char **ap, *specs[256] = {0}, *out; int olen, oused = 0; - memset(specs, 0, 256 * sizeof(char *)); - specs['%'] = "%"; specs[')'] = ")"; + + /* Parse the specs in argv. */ for (ap = args + 2; *ap; ap++) { if (!ap[0][0] || ap[0][0] == '-' || ap[0][0] == '.' || idigit(ap[0][0]) || ap[0][1] != ':') { @@ -930,7 +954,8 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } out = (char *) zhalloc(olen = 128); - zformat_substring(args[1], specs, &out, &oused, &olen, '\0', 0); + zformat_substring(args[1], specs, &out, &oused, &olen, '\0', + presence, 0); out[oused] = '\0'; setsparam(args[0], ztrdup(out)); @@ -1865,7 +1890,10 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) while (*++o) { if (!(d = sopts[STOUC(*o)])) { if (fail) { - zwarnnam(nam, "bad option: %c", *o); + if (*o != '-') + zwarnnam(nam, "bad option: -%c", *o); + else + zwarnnam(nam, "bad option: -%s", o); return 1; } o = NULL; @@ -1878,7 +1906,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } else if (!(d->flags & ZOF_OPT) || (pp[1] && pp[1][0] != '-')) { if (!pp[1]) { - zwarnnam(nam, "missing argument for option: %s", + zwarnnam(nam, "missing argument for option: -%s", d->name); return 1; } @@ -1905,7 +1933,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) else if (!(d->flags & ZOF_OPT) || (pp[1] && pp[1][0] != '-')) { if (!pp[1]) { - zwarnnam(nam, "missing argument for option: %s", + zwarnnam(nam, "missing argument for option: -%s", d->name); return 1; } diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 2e3249b52..2ca779fe5 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -85,8 +85,8 @@ struct cmgroup { #define CGF_NOSORT 1 /* don't sort this group */ #define CGF_LINES 2 /* these are to be printed on different lines */ #define CGF_HASDL 4 /* has display strings printed on separate lines */ -#define CGF_UNIQALL 8 /* remove all duplicates */ -#define CGF_UNIQCON 16 /* remove consecutive duplicates */ +#define CGF_UNIQALL 8 /* remove consecutive duplicates (if neither are set, */ +#define CGF_UNIQCON 16 /* don't deduplicate */ /* remove all dupes) */ #define CGF_PACKED 32 /* LIST_PACKED for this group */ #define CGF_ROWS 64 /* LIST_ROWS_FIRST for this group */ #define CGF_FILES 128 /* contains file names */ @@ -140,6 +140,7 @@ struct cmatch { #define CMF_ALL (1<<13) /* a match representing all other matches */ #define CMF_DUMMY (1<<14) /* unselectable dummy match */ #define CMF_MORDER (1<<15) /* order by matches, not display strings */ +#define CMF_DELETE (1<<16) /* used for deduplication of unsorted matches, don't set */ /* Stuff for completion matcher control. */ @@ -299,7 +300,7 @@ struct menuinfo { #define CAF_NOSORT 2 /* compadd -V: don't sort */ #define CAF_MATCH 4 /* compadd without -U: do matching */ #define CAF_UNIQCON 8 /* compadd -2: don't deduplicate */ -#define CAF_UNIQALL 16 /* compadd -1: deduplicate */ +#define CAF_UNIQALL 16 /* compadd -1: deduplicate consecutive only */ #define CAF_ARRAYS 32 /* compadd -a or -k: array/assoc parameter names */ #define CAF_KEYS 64 /* compadd -k: assoc parameter names */ #define CAF_ALL 128 /* compadd -C: _all_matches */ @@ -329,7 +330,7 @@ struct cadata { char *exp; /* explanation (-X) */ char *apar; /* array to store matches in (-A) */ char *opar; /* array to store originals in (-O) */ - char *dpar; /* array to delete non-matches in (-D) */ + char **dpar; /* arrays to delete non-matches in (-D) */ char *disp; /* array with display lists (-d) */ char *mesg; /* message to show unconditionally (-x) */ int dummies; /* add that many dummy matches */ diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 7e3badc57..fe3ea10fc 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -648,7 +648,7 @@ callcompfunc(char *s, char *fn) if (compredirs) freearray(compredirs); if (rdstrs) - compredirs = zlinklist2array(rdstrs); + compredirs = zlinklist2array(rdstrs, 1); else compredirs = (char **) zshcalloc(sizeof(char *)); @@ -821,6 +821,7 @@ callcompfunc(char *s, char *fn) sfcontext = SFC_CWIDGET; NEWHEAPS(compheap) { LinkList largs = NULL; + int oxt = isset(XTRACE); if (*cfargs) { char **p = cfargs; @@ -830,7 +831,9 @@ callcompfunc(char *s, char *fn) while (*p) addlinknode(largs, dupstring(*p++)); } + opts[XTRACE] = 0; cfret = doshfunc(shfunc, largs, 1); + opts[XTRACE] = oxt; } OLDHEAPS; sfcontext = osc; endparamscope(); @@ -1122,6 +1125,18 @@ check_param(char *s, int set, int test) * * TODO: passing s as a parameter while we get some mysterious * offset "offs" into it via a global sucks badly. + * + * From ../lex.c we know: + * wb is the beginning position of the current word in the line + * we is the position of the end of the current word in the line + * From zle_tricky.c we know: + * offs is position within the word where we are completing + * + * So wb + offs is the current cursor position if COMPLETE_IN_WORD + * is set, otherwise it is the end of the word (same as we). + * + * Note below we are thus stepping backward from our completion + * position to find a '$' in the current word (if any). */ for (p = s + offs; ; p--) { if (*p == String || *p == Qstring) { @@ -1168,13 +1183,13 @@ check_param(char *s, int set, int test) char *tb = b; /* If this is a ${...}, see if we are before the '}'. */ - if (!skipparens(Inbrace, Outbrace, &tb)) + if (!skipparens(Inbrace, Outbrace, &tb) && tb - s <= offs) return NULL; /* Ignore the possible (...) flags. */ b++, br++; if ((qstring ? skipparens('(', ')', &b) : - skipparens(Inpar, Outpar, &b)) > 0) { + skipparens(Inpar, Outpar, &b)) > 0 || b - s > offs) { /* * We are still within the parameter flags. There's no * point trying to do anything clever here with @@ -1922,7 +1937,7 @@ set_comp_sep(void) mod_export void set_list_array(char *name, LinkList l) { - setaparam(name, zlinklist2array(l)); + setaparam(name, zlinklist2array(l, 1)); } /* Get the words from a variable or a (list of words). */ @@ -2058,10 +2073,10 @@ addmatches(Cadata dat, char **argv) /* ms: "match string" - string to use as completion. * Overloaded at one place as a temporary. */ char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL; - char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre; + char **aign = NULL, ***dparr = NULL, *oaq = autoq, *oppre = dat->ppre; char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL; char **arrays = NULL; - int lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0; + int dind, lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0; int ppl = 0, psl = 0, ilen = 0; int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern; int isexact, doadd, ois = instring, oib = inbackt; @@ -2069,7 +2084,7 @@ addmatches(Cadata dat, char **argv) struct cmlist mst; Cmlist oms = mstack; Patprog cp = NULL, *pign = NULL; - LinkList aparl = NULL, oparl = NULL, dparl = NULL; + LinkList aparl = NULL, oparl = NULL, *dparl = NULL; Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl; Heap oldheap; @@ -2097,7 +2112,7 @@ addmatches(Cadata dat, char **argv) curexpl->always = !!dat->mesg; curexpl->count = curexpl->fcount = 0; curexpl->str = dupstring(dat->mesg ? dat->mesg : dat->exp); - if (dat->mesg) + if (dat->mesg && !dat->dpar && !dat->opar && !dat->apar) addexpl(1); } else curexpl = NULL; @@ -2163,11 +2178,24 @@ addmatches(Cadata dat, char **argv) if (dat->opar) oparl = newlinklist(); if (dat->dpar) { - if (*(dat->dpar) == '(') - dparr = NULL; - else if ((dparr = get_user_var(dat->dpar)) && !*dparr) - dparr = NULL; - dparl = newlinklist(); + int darr = 0, dparlen = arrlen(dat->dpar); + char **tail = dat->dpar + dparlen; + + dparr = (char ***)hcalloc((1 + dparlen) * sizeof(char **)); + dparl = (LinkList *)hcalloc((1 + dparlen) * sizeof(LinkList)); + queue_signals(); + while (darr < dparlen) { + if ((dparr[darr] = getaparam(dat->dpar[darr])) && *dparr[darr]) { + dparr[darr] = arrdup(dparr[darr]); + dparl[darr++] = newlinklist(); + } else { + /* swap in the last -D argument if we didn't get a non-empty array */ + dat->dpar[darr] = *--tail; + *tail = NULL; + --dparlen; + } + } + unqueue_signals(); } /* Store the matcher in our stack of matchers. */ if (dat->match) { @@ -2484,8 +2512,10 @@ addmatches(Cadata dat, char **argv) } if (!addit) { compignored++; - if (dparr && !*++dparr) - dparr = NULL; + for (dind = 0; dparl && dparl[dind]; dind++) { + if (dparr[dind] && !*++dparr[dind]) + dparr[dind] = NULL; + } goto next_array; } } @@ -2502,8 +2532,10 @@ addmatches(Cadata dat, char **argv) !(dat->flags & CMF_FILE) ? 1 : 2) : 0), &bpl, bcp, &bsl, bcs, &isexact))) { - if (dparr && !*++dparr) - dparr = NULL; + for (dind = 0; dparl && dparl[dind]; dind++) { + if (dparr[dind] && !*++dparr[dind]) + dparr[dind] = NULL; + } goto next_array; } if (doadd) { @@ -2530,10 +2562,14 @@ addmatches(Cadata dat, char **argv) addlinknode(aparl, ms); if (dat->opar) addlinknode(oparl, s); - if (dat->dpar && dparr) { - addlinknode(dparl, *dparr); - if (!*++dparr) - dparr = NULL; + if (dat->dpar) { + for (dind = 0; dparl[dind]; dind++) { + if (dparr[dind]) { + addlinknode(dparl[dind], *dparr[dind]); + if (!*++dparr[dind]) + dparr[dind] = NULL; + } + } } free_cline(lc); } @@ -2561,8 +2597,10 @@ addmatches(Cadata dat, char **argv) set_list_array(dat->apar, aparl); if (dat->opar) set_list_array(dat->opar, oparl); - if (dat->dpar) - set_list_array(dat->dpar, dparl); + if (dat->dpar) { + for (dind = 0; dparl[dind]; dind++) + set_list_array(dat->dpar[dind], dparl[dind]); + } if (dat->exp) addexpl(0); if (!hasallmatch && (dat->aflags & CAF_ALL)) { @@ -3209,17 +3247,20 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) } *cp = NULL; } - } else { + } else if (n > 0) { if (!(flags & CGF_NOSORT)) { /* Now sort the array (it contains matches). */ matchorder = flags; qsort((void *) rp, n, sizeof(Cmatch), (int (*) _((const void *, const void *)))matchcmp); + /* since the matches are sorted and the default is to remove + * all duplicates, -1 (remove only consecutive dupes) is a no-op, + * so this condition only checks for -2 */ if (!(flags & CGF_UNIQCON)) { int dup; - /* And delete the ones that occur more than once. */ + /* we did not pass -2 so go ahead and remove those dupes */ for (ap = cp = rp; *ap; ap++) { *cp++ = *ap; for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--); @@ -3241,30 +3282,47 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) if ((*ap)->flags & (CMF_NOLIST | CMF_MULT)) nl++; } + /* used -O nosort or -V, don't sort */ } else { + /* didn't use -1 or -2, so remove all duplicates (efficient) */ if (!(flags & CGF_UNIQALL) && !(flags & CGF_UNIQCON)) { - int dup; - - for (ap = rp; *ap; ap++) { - for (bp = cp = ap + 1; *bp; bp++) { - if (!matcheq(*ap, *bp)) - *cp++ = *bp; - else + int dup, del = 0; + + /* To avoid O(n^2) here, sort a copy of the list, then remove marked elements */ + matchorder = flags; + Cmatch *sp, *asp; + sp = (Cmatch *) zhalloc((n + 1) * sizeof(Cmatch)); + memcpy(sp, rp, (n + 1) * sizeof(Cmatch)); + qsort((void *) sp, n, sizeof(Cmatch), + (int (*) _((const void *, const void *)))matchcmp); + for (asp = sp + 1; *asp; asp++) { + Cmatch *ap = asp - 1, *bp = asp; + if (matcheq(*ap, *bp)) { + bp[0]->flags = CMF_DELETE; + del = 1; + } else if (!ap[0]->disp) { + /* Mark those, that would show the same string in the list. */ + for (dup = 0; bp[0] && !(bp[0])->disp && + !strcmp((*ap)->str, (bp[0])->str); bp = ++sp) { + (bp[0])->flags |= CMF_MULT; + dup = 1; + } + if (dup) + (*ap)->flags |= CMF_FMULT; + } + } + if (del) { + int n_orig = n; + for (bp = rp, ap = rp; bp < rp + n_orig; ap++, bp++) { + while (bp[0]->flags & CMF_DELETE) { + bp++; n--; + } + *ap = *bp; } - *cp = NULL; - if (!(*ap)->disp) { - for (dup = 0, bp = ap + 1; *bp; bp++) - if (!(*bp)->disp && - !((*bp)->flags & CMF_MULT) && - !strcmp((*ap)->str, (*bp)->str)) { - (*bp)->flags |= CMF_MULT; - dup = 1; - } - if (dup) - (*ap)->flags |= CMF_FMULT; - } + *ap = NULL; } + /* passed -1 but not -2, so remove consecutive duplicates (efficient) */ } else if (!(flags & CGF_UNIQCON)) { int dup; diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 7beb6d847..67a60963e 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -607,6 +607,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) char *oarg = NULL; /* argument of -o option */ int added; /* return value */ Cmatcher match = NULL; + size_t dparlen = 0, dparsize = 0; /* no. of -D options and array size */ if (incompfunc != 1) { zwarnnam(name, "can only be called from completion function"); @@ -614,7 +615,8 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) } dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.mesg = dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = - dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL; + dat.ign = dat.exp = dat.apar = dat.opar = NULL; + dat.dpar = NULL; dat.match = NULL; dat.flags = 0; dat.aflags = CAF_MATCH; @@ -741,7 +743,12 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) e = "parameter name expected after -%c"; break; case 'D': - sp = &(dat.dpar); + if (dparsize <= dparlen + 1) { + dparsize = (dparsize + 1) * 2; + dat.dpar = (char **)zrealloc(dat.dpar, sizeof(char *) * dparsize); + } + sp = dat.dpar + dparlen++; + *sp = dat.dpar[dparlen] = NULL; e = "parameter name expected after -%c"; break; case 'd': @@ -768,11 +775,13 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) } else { zwarnnam(name, "number expected after -%c", *p); zsfree(mstr); + zfree(dat.dpar, dparsize); return 1; } if (dat.dummies < 0) { zwarnnam(name, "invalid number: %d", dat.dummies); zsfree(mstr); + zfree(dat.dpar, dparsize); return 1; } break; @@ -782,6 +791,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) default: zwarnnam(name, "bad option: -%c", *p); zsfree(mstr); + zfree(dat.dpar, dparsize); return 1; } if (sp) { @@ -802,6 +812,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) /* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */ zwarnnam(name, e, *p); zsfree(mstr); + zfree(dat.dpar, dparsize); return 1; } if (m) { @@ -820,17 +831,21 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) { zsfree(mstr); + zfree(dat.dpar, dparsize); return 1; } zsfree(mstr); if (!*argv && !dat.group && !dat.mesg && - !(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON|CAF_ALL))) + !(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON|CAF_ALL))) { + zfree(dat.dpar, dparsize); return 1; + } dat.match = match = cpcmatcher(match); added = addmatches(&dat, argv); freecmatcher(match); + zfree(dat.dpar, dparsize); return added; } @@ -1343,7 +1358,7 @@ get_compstate(Param pm) /**/ static void -set_compstate(UNUSED(Param pm), HashTable ht) +set_compstate(Param pm, HashTable ht) { struct compparam *cp; Param *pp; diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 429c8159f..0dc64db6a 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -603,6 +603,16 @@ zcoff(void) zcputs(NULL, COL_NO); } +/* Clear to end of line, if possible and necessary. */ +static void +cleareol() +{ + if (mlbeg >= 0 && tccan(TCCLEAREOL)) { + if (*last_cap) + zcoff(); /* If we used colors, prevent them from bleeding. */ + tcout(TCCLEAREOL); + } +} static void initiscol(void) @@ -670,8 +680,7 @@ clprintfmt(char *p, int ml) doiscol(i++); cc++; if (*p == '\n') { - if (mlbeg >= 0 && tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); + cleareol(); cc = 0; } if (ml == mlend - 1 && (cc % zterm_columns) == zterm_columns - 1) @@ -693,8 +702,7 @@ clprintfmt(char *p, int ml) !--mrestlines && (ask = asklistscroll(ml))) return ask; } - if (mlbeg >= 0 && tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); + cleareol(); return 0; } @@ -1047,8 +1055,7 @@ compprintnl(int ml) { int ask; - if (mlbeg >= 0 && tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); + cleareol(); putc('\n', shout); if (mscroll && !--mrestlines && (ask = asklistscroll(ml))) @@ -1263,8 +1270,8 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) if ((cc >= zterm_columns - 2 || cchar == ZWC('\n')) && stat) dopr = 2; if (cchar == ZWC('\n')) { - if (dopr == 1 && mlbeg >= 0 && tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); + if (dopr == 1) + cleareol(); l += 1 + ((cc - 1) / zterm_columns); cc = 0; } @@ -1306,8 +1313,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) if (dopr) { if (!(cc % zterm_columns)) fputs(" \010", shout); - if (mlbeg >= 0 && tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); + cleareol(); } if (stat && n) mfirstl = -1; @@ -1338,8 +1344,8 @@ compzputs(char const *s, int ml) c = *s; s++; putc(c, shout); - if (c == '\n' && mlbeg >= 0 && tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); + if (c == '\n') + cleareol(); if (mscroll && (++col == zterm_columns || c == '\n')) { ml++; if (!--mrestlines && (ask = asklistscroll(ml))) @@ -1692,8 +1698,7 @@ compprintlist(int showall) lastlistlen = listdat.nlines; } else if ((nl = listdat.nlines + nlnct - 1) < zterm_lines) { - if (mlbeg >= 0 && tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); + cleareol(); tcmultout(TCUP, TCMULTUP, nl); showinglist = -1; @@ -3277,9 +3282,8 @@ domenuselect(Hookdef dummy, Chdata dat) !strcmp(cmd->nam, "reverse-menu-complete")) { mode = 0; comprecursive = 1; - unmetafy_line(); - reversemenucomplete(zlenoargs); - metafy_line(); + zmult = -zmult; + do_menucmp(0); mselect = (*(minfo.cur))->gnum; setwish = 1; mline = -1; diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index cc4c3eca9..bb8359f1d 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -693,8 +693,9 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, alen = mp->ralen; aol = mp->lalen; } /* Give up if we don't have enough characters for the - * line-string and the anchor. */ - if (ll < llen + alen || lw < alen) + * line-string and the anchor, or for both anchors in + * the case of the trial completion word. */ + if (ll < llen + alen || lw < alen + aol) continue; if (mp->flags & CMF_LEFT) { @@ -1318,7 +1319,7 @@ pattern_match_equivalence(Cpattern lp, convchar_t wind, int wmtp, convchar_t lchr; int lmtp; - if (!PATMATCHINDEX(lp->u.str, wind-1, &lchr, &lmtp)) { + if (!PATMATCHINDEX(lp->u.str, wind, &lchr, &lmtp)) { /* * No equivalent. No possible match; give up. */ @@ -1437,7 +1438,7 @@ pattern_match_restrict(Cpattern p, Cpattern wp, convchar_t *wsc, int wsclen, /* * If either is "?", they match each other; no further tests. - * Apply this even if the character wasn't convertable; + * Apply this even if the character wasn't convertible; * there's no point trying to be clever in that case. */ if (p->tp != CPAT_ANY || wp->tp != CPAT_ANY) @@ -1495,7 +1496,7 @@ pattern_match_restrict(Cpattern p, Cpattern wp, convchar_t *wsc, int wsclen, * characters. We're matching two patterns against * one another to generate a character to insert. * This is a bit too psychedelic, so I'm going to - * bale out now. See you on the ground. + * bail out now. See you on the ground. */ return 0; } @@ -1563,7 +1564,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws) c = unmeta_one(s, &len); /* * If either is "?", they match each other; no further tests. - * Apply this even if the character wasn't convertable; + * Apply this even if the character wasn't convertible; * there's no point trying to be clever in that case. */ if (p->tp != CPAT_ANY || wp->tp != CPAT_ANY) @@ -1933,7 +1934,7 @@ bld_line(Cmatcher mp, ZLE_STRING_T line, char *mword, char *word, * This is the nightmare case: we have line and * and word matchers and some pattern which restricts * the value on the line without us knowing exactly - * what it is. Despatch to the special function + * what it is. Dispatch to the special function * for that. */ if (mp && !mp->flags && mp->wlen <= wlen && diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 30fc60b78..57789c0f3 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -612,9 +612,10 @@ instmatch(Cmatch m, int *scs) int pcs = zlemetacs; l = 0; - for (bp = brbeg, brpos = m->brpl, - bradd = (m->pre ? strlen(m->pre) : 0); - bp; bp = bp->next, brpos++) { + bradd = (m->pre ? strlen(m->pre) : 0); + for (bp = brbeg, brpos = m->brpl; + bp && brpos; + bp = bp->next, brpos++) { zlemetacs = a + *brpos + bradd; pcs = zlemetacs; l = strlen(bp->str); @@ -1583,7 +1584,7 @@ calclist(int showall) nlines += 1 + printfmt(m->disp, 0, 0, 0); g->flags |= CGF_HASDL; } else { - l = ZMB_nicewidth(m->disp); + l = ZMB_nicewidth(m->disp) + !!m->modec; ndisp++; if (l > glong) glong = l; @@ -2247,15 +2248,13 @@ iprintm(Cmgroup g, Cmatch *mp, UNUSED(int mc), UNUSED(int ml), int lastc, int wi #ifdef MULTIBYTE_SUPPORT len = mb_niceformat(m->disp, shout, NULL, 0); #else - nicezputs(m->disp, shout); - len = niceztrlen(m->disp); + len = sb_niceformat(m->disp, shout, NULL, 0); #endif } else { #ifdef MULTIBYTE_SUPPORT len = mb_niceformat(m->str, shout, NULL, 0); #else - nicezputs(m->str, shout); - len = niceztrlen(m->str); + len = sb_niceformat(m->str, shout, NULL, 0); #endif if ((g->flags & CGF_FILES) && m->modec) { diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 90db8b4b8..59abb4cc4 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -1035,7 +1035,7 @@ freecadef(Cadef d) freecaargs(d->rest); zsfree(d->nonarg); if (d->single) - zfree(d->single, 256 * sizeof(Caopt)); + zfree(d->single, 188 * sizeof(Caopt)); zfree(d, sizeof(*d)); d = s; } @@ -1079,6 +1079,21 @@ bslashcolon(char *s) return r; } +/* Get an index into the single array used in struct cadef + * opt is the option letter and pre is either - or + + * we only keep an array for the 94 ASCII characters from ! to ~ so + * with - and + prefixes, the array is double that at 188 elements + * returns -1 if opt is out-of-range + */ +static int +single_index(char pre, char opt) +{ + if (opt <= 0x20 || opt > 0x7e) + return -1; + + return opt + (pre == '-' ? -0x21 : 94 - 0x21); +} + /* Parse an argument definition. */ static Caarg @@ -1151,8 +1166,8 @@ alloc_cadef(char **args, int single, char *match, char *nonarg, int flags) ret->lastt = time(0); ret->set = NULL; if (single) { - ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); - memset(ret->single, 0, 256 * sizeof(Caopt)); + ret->single = (Caopt *) zalloc(188 * sizeof(Caopt)); + memset(ret->single, 0, 188 * sizeof(Caopt)); } else ret->single = NULL; ret->match = ztrdup(match); @@ -1558,9 +1573,10 @@ parse_cadef(char *nam, char **args) * pointer for the definition in the array for fast lookup. * But don't treat '--' as a single option called '-' */ - - if (single && name[1] && !name[2] && name[1] != '-') - ret->single[STOUC(name[1])] = opt; + if (single && name[1] && !name[2] && name[1] != '-') { + int sidx = single_index(*name, name[1]); + if (sidx >= 0) ret->single[sidx] = opt; + } if (again == 1) { /* Do it all again for `*-...'. */ @@ -1738,11 +1754,12 @@ ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp) Caopt p, pp = NULL; char pre = *line++; LinkList l = NULL; + int sidx; *lp = NULL; for (p = NULL; *line; line++) { - if ((p = d->single[STOUC(*line)]) && p->active && - p->args && p->name[0] == pre) { + if ((sidx = single_index(pre, *line)) != -1 && + (p = d->single[sidx]) && p->active && p->args) { if (p->type == CAO_NEXT) { if (!l) *lp = l = newlinklist(); @@ -1813,7 +1830,7 @@ ca_get_arg(Cadef d, int n) /* Mark options as inactive. * d: option definitions for a set * pass either: - * xor: a list if exclusions + * xor: a list of exclusions * opts: if set, all options excluded leaving only nornal/rest arguments */ static void @@ -2031,9 +2048,9 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) state.def = state.ddef = NULL; state.curopt = state.dopt = NULL; state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.actopts = - state.nth = state.inopt = state.inarg = state.opt = state.arg = 1; + state.nth = state.inarg = state.opt = state.arg = 1; state.argend = argend = arrlen(compwords) - 1; - state.singles = state.oopt = 0; + state.inopt = state.singles = state.oopt = 0; state.curpos = compcurrent; state.args = znewlinklist(); state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); @@ -2080,9 +2097,14 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) remnulargs(line); untokenize(line); - ca_inactive(d, argxor, cur - 1, 0); - if ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--")) { + if (argxor) { + ca_inactive(d, argxor, cur - 1, 0); + argxor = NULL; + } + if ((d->flags & CDF_SEP) && cur != compcurrent && state.actopts && + !strcmp(line, "--")) { ca_inactive(d, NULL, cur, 1); + state.actopts = 0; continue; } @@ -2136,7 +2158,8 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) /* See if it's an option. */ - if (state.opt == 2 && (state.curopt = ca_get_opt(d, line, 0, &pe)) && + if (state.opt == 2 && (*line == '-' || *line == '+') && + (state.curopt = ca_get_opt(d, line, 0, &pe)) && (state.curopt->type == CAO_OEQUAL ? (compwords[cur] || pe[-1] == '=') : (state.curopt->type == CAO_EQUAL ? @@ -2184,12 +2207,14 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) state.curopt = NULL; } } else if (state.opt == 2 && d->single && + (*line == '-' || *line == '+') && ((state.curopt = ca_get_sopt(d, line, &pe, &sopts)) || (cur != compcurrent && sopts && nonempty(sopts)))) { /* Or maybe it's a single-letter option? */ char *p; Caopt tmpopt; + int sidx; if (cur != compcurrent && sopts && nonempty(sopts)) state.curopt = (Caopt) uremnode(sopts, firstnode(sopts)); @@ -2207,7 +2232,8 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) state.singles = (!pe || !*pe); for (p = line + 1; p < pe; p++) { - if ((tmpopt = d->single[STOUC(*p)])) { + if ((sidx = single_index(*line, *p)) != -1 && + (tmpopt = d->single[sidx])) { if (!state.oargs[tmpopt->num]) state.oargs[tmpopt->num] = znewlinklist(); @@ -2235,11 +2261,17 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) } else if (multi && (*line == '-' || *line == '+') && cur != compcurrent && (ca_foreign_opt(d, all, line))) return 1; - else if (state.arg && - (!napat || cur <= compcurrent || !pattry(napat, line))) { + else if (state.arg && cur <= compcurrent) { /* Otherwise it's a normal argument. */ - if (napat && cur <= compcurrent) + + /* test pattern passed to -A. if it matches treat this as an unknown + * option and continue to the next word */ + if (napat && cur < compcurrent && state.actopts) { + if (pattry(napat, line)) + continue; ca_inactive(d, NULL, cur + 1, 1); + state.actopts = 0; + } arglast = 1; /* if this is the first normal arg after an option, may have been @@ -2293,7 +2325,7 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) if (adef) state.oopt = adef->num - state.nth; - if (state.def) + if (state.def && cur != compcurrent) argxor = state.def->xor; if (state.def && state.def->type != CAA_NORMAL && @@ -2375,6 +2407,23 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) return 0; } +/* Build a NUL-separated from a list. + * + * This is only used to populate values of $opt_args. + */ + +static char * +ca_nullist(LinkList l) +{ + if (l) { + char **array = zlinklist2array(l, 0 /* don't dup elements */); + char *ret = zjoin(array, '\0', 0 /* permanent allocation */); + free(array); /* the elements are owned by the list */ + return ret; + } else + return ztrdup(""); +} + /* Build a colon-list from a list. * * This is only used to populate values of $opt_args. @@ -2563,7 +2612,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) case 's': min = 1; max = 1; break; case 'M': min = 1; max = 1; break; case 'a': min = 0; max = 0; break; - case 'W': min = 2; max = 2; break; + case 'W': min = 3; max = 3; break; case 'n': min = 1; max = 1; break; default: zwarnnam(nam, "invalid option: %s", args[0]); @@ -2795,11 +2844,20 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) return 0; return 1; case 'W': - /* This gets two parameter names as arguments. The first is set to - * the current word sans any option prefixes handled by comparguments. + /* This gets two parameter names and one integer as arguments. + * + * The first parameter is set to the current word sans any option + * prefixes handled by comparguments. + * * The second parameter is set to an array containing the options on * the line and their arguments. I.e. the stuff _arguments returns - * to its caller in the `line' and `opt_args' parameters. */ + * to its caller in the `line' and `opt_args' parameters. + * + * The integer is one if the second parameter (which is just $opt_args, + * you know) should encode multiple values by joining them with NULs + * and zero if it should encode multiple values by joining them with + * colons after backslash-escaping colons and backslashes. + */ { Castate s; char **ret, **p; @@ -2807,6 +2865,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) LinkList *a; Caopt o; int num; + int opt_args_use_NUL_separators = (args[3][0] != '0'); for (num = 0, s = lstate; s; s = s->snext) num += countlinknodes(s->args); @@ -2832,7 +2891,10 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (*a) { *p++ = (o->gsname ? tricat(o->gsname, o->name, "") : ztrdup(o->name)); - *p++ = ca_colonlist(*a); + if (opt_args_use_NUL_separators) + *p++ = ca_nullist(*a); + else + *p++ = ca_colonlist(*a); } *p = NULL; @@ -3591,7 +3653,7 @@ bin_compvalues(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (cv_laststate.vals) { char **ret; - ret = zlinklist2array(cv_laststate.vals); + ret = zlinklist2array(cv_laststate.vals, 1); sethparam(args[1], ret); return 0; @@ -4016,7 +4078,7 @@ bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) set = (Ctset) zalloc(sizeof(*set)); - set->tags = zlinklist2array(list); + set->tags = zlinklist2array(list, 1); set->next = NULL; set->ptr = NULL; set->tag = NULL; diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 609493f8c..391586c4a 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -447,6 +447,10 @@ struct region_highlight { * Any of the flags defined above. */ int flags; + /* + * User-settable "memo" key. Metafied. + */ + const char *memo; }; /* diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index 581ca4979..cfaa70dae 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -683,8 +683,13 @@ insertlastword(char **args) } nwords = countlinknodes(l); } else { - /* Some stored line. */ - if (!(he = quietgethist(evhist)) || !he->nwords) { + /* Some stored line. By default, search for a non-empty line. */ + while ((he = quietgethist(evhist)) && histstep == -1 && !*args) { + if (he->nwords) + break; + evhist = addhistnum(evhist, histstep, HIST_FOREIGN); + } + if (!he || !he->nwords) { unmetafy_line(); return 1; } diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 49b2a26ad..d90838f03 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -424,7 +424,7 @@ scankeys(HashNode hn, UNUSED(int flags)) /**************************/ /**/ -Keymap +mod_export Keymap openkeymap(char *name) { KeymapName n = (KeymapName) keymapnamtab->getnode(keymapnamtab, name); diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index be68f4722..9edf30e01 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -906,6 +906,8 @@ getbyte(long do_keytmout, int *timeout, int full) continue; stopmsg = 1; zexit(1, ZEXIT_NORMAL); + /* If called from an exit hook, zexit() returns, so: */ + break; } icnt = 0; if (errno == EINTR) { @@ -929,6 +931,8 @@ getbyte(long do_keytmout, int *timeout, int full) zerr("error on TTY read: %e", errno); stopmsg = 1; zexit(1, ZEXIT_NORMAL); + /* If called from an exit hook, zexit() returns, so: */ + break; } } if (cc == '\r') /* undo the exchange of \n and \r determined by */ @@ -1056,7 +1060,7 @@ getrestchar(int inchar, char *outstr, int *outcount) #endif /**/ -void +mod_export void redrawhook(void) { Thingy initthingy; @@ -1065,6 +1069,7 @@ redrawhook(void) int saverrflag = errflag, savretflag = retflag; int lastcmd_prev = lastcmd; int old_incompfunc = incompfunc; + int old_viinrepeat = viinrepeat; char *args[2]; Thingy lbindk_save = lbindk, bindk_save = bindk; @@ -1079,6 +1084,7 @@ redrawhook(void) incompfunc = 0; execzlefunc(initthingy, args, 1, 0); incompfunc = old_incompfunc; + viinrepeat = old_viinrepeat; /* Restore errflag and retflag as zlecallhook() does */ errflag = saverrflag | (errflag & ERRFLAG_INT); diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c index 612ac2138..eba28d1ec 100644 --- a/Src/Zle/zle_misc.c +++ b/Src/Zle/zle_misc.c @@ -1508,9 +1508,6 @@ static struct suffixset *suffixlist; /**/ static char *suffixfunc; -/* Length associated with the suffix function */ -static int suffixfunclen; - /* Whether to remove suffix on uninsertable characters */ /**/ int suffixnoinsrem; @@ -1611,7 +1608,7 @@ makesuffixstr(char *f, char *s, int n) if (f) { zsfree(suffixfunc); suffixfunc = ztrdup(f); - suffixfunclen = n; + suffixlen = n; } else if (s) { int inv, i, z = 0; ZLE_STRING_T ws, lasts, wptr; @@ -1685,7 +1682,7 @@ iremovesuffix(ZLE_INT_T c, int keep) unmetafy_line(); } - sprintf(buf, "%d", suffixfunclen); + sprintf(buf, "%d", suffixlen); addlinknode(args, suffixfunc); addlinknode(args, buf); @@ -1800,5 +1797,5 @@ fixsuffix(void) suffixlist = next; } - suffixfunclen = suffixnoinsrem = suffixlen = 0; + suffixnoinsrem = suffixlen = 0; } diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c index 155fda80d..3bafff3f1 100644 --- a/Src/Zle/zle_move.c +++ b/Src/Zle/zle_move.c @@ -166,7 +166,7 @@ decpos(int *pos) */ /**/ -char * +mod_export char * backwardmetafiedchar(char *start, char *endptr, convchar_t *retchr) { #ifdef MULTIBYTE_SUPPORT diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 7b8593dec..30b5d4447 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -212,9 +212,9 @@ static zattr default_atr_on, special_atr_on; /* * Array of region highlights, no special termination. - * The first element (0) always describes the region between - * point and mark. Any other elements are set by the user - * via the parameter region_highlight. + * The first N_SPECIAL_HIGHLIGHTS elements describe special uses of + * highlighting, documented under N_SPECIAL_HIGHLIGHTS. + * Any other elements are set by the user via the parameter region_highlight. */ /**/ @@ -255,7 +255,9 @@ int cost; #endif static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), 0 }; +#ifdef MULTIBYTE_SUPPORT static const REFRESH_ELEMENT zr_dt = { ZWC('.'), 0 }; +#endif static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), 0 }; static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 }; static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 }; @@ -414,16 +416,19 @@ get_region_highlight(UNUSED(Param pm)) arrsize--; rhp++, arrp++) { char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE]; - int atrlen = 0, alloclen; + int atrlen, alloclen; + const char memo_equals[] = "memo="; sprintf(digbuf1, "%d", rhp->start); sprintf(digbuf2, "%d", rhp->end); atrlen = output_highlight(rhp->atr, NULL); alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) + - 3; /* 2 spaces, 1 0 */ + 3; /* 2 spaces, 1 terminating NUL */ if (rhp->flags & ZRH_PREDISPLAY) alloclen += 2; /* "P " */ + if (rhp->memo) + alloclen += 1 /* space */ + strlen(memo_equals) + strlen(rhp->memo); *arrp = (char *)zhalloc(alloclen * sizeof(char)); /* * On input we allow a space after the flags. @@ -436,6 +441,12 @@ get_region_highlight(UNUSED(Param pm)) (rhp->flags & ZRH_PREDISPLAY) ? "P" : "", digbuf1, digbuf2); (void)output_highlight(rhp->atr, *arrp + strlen(*arrp)); + + if (rhp->memo) { + strcat(*arrp, " "); + strcat(*arrp, memo_equals); + strcat(*arrp, rhp->memo); + } } *arrp = NULL; return retarr; @@ -460,6 +471,8 @@ set_region_highlight(UNUSED(Param pm), char **aval) /* no null termination, but include special highlighting at start */ int newsize = len + N_SPECIAL_HIGHLIGHTS; int diffsize = newsize - n_region_highlights; + + free_region_highlights_memos(); region_highlights = (struct region_highlight *) zrealloc(region_highlights, sizeof(struct region_highlight) * newsize); @@ -476,6 +489,7 @@ set_region_highlight(UNUSED(Param pm), char **aval) *aval; rhp++, aval++) { char *strp, *oldstrp; + const char memo_equals[] = "memo="; oldstrp = *aval; if (*oldstrp == 'P') { @@ -502,7 +516,44 @@ set_region_highlight(UNUSED(Param pm), char **aval) while (inblank(*strp)) strp++; - match_highlight(strp, &rhp->atr); + strp = (char*) match_highlight(strp, &rhp->atr); + + while (inblank(*strp)) + strp++; + + if (strpfx(memo_equals, strp)) { + const char *memo_start = strp + strlen(memo_equals); + const char *i, *memo_end; + + /* + * Forward compatibility: end parsing at a comma or whitespace to + * allow the following extensions: + * + * - A fifth field: "0 20 bold memo=foo bar". + * + * - Additional attributes in the fourth field: "0 20 bold memo=foo,bar" + * and "0 20 bold memo=foo\0bar". + * + * For similar reasons, we don't flag an error if the fourth field + * doesn't start with "memo=" as we expect. + */ + i = memo_start; + + /* ### TODO: Consider optimizing the common case that memo_start to + * end-of-string is entirely ASCII */ + while (1) { + int nbytes; + convchar_t c = unmeta_one(i, &nbytes); + + if (c == '\0' || c == ',' || inblank(c)) { + memo_end = i; + break; + } else + i += nbytes; + } + rhp->memo = ztrduppfx(memo_start, memo_end - memo_start); + } else + rhp->memo = NULL; } freearray(av); @@ -2797,6 +2848,7 @@ zle_refresh_finish(void) if (region_highlights) { + free_region_highlights_memos(); zfree(region_highlights, sizeof(struct region_highlight) * n_region_highlights); region_highlights = NULL; diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index ce61db27b..cd3f2c356 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -678,6 +678,7 @@ bin_zle_flags(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) else if (!strcmp(*flag, "keepsuffix")) w->flags |= ZLE_KEEPSUFFIX; */ + /* If you add magic strings here, be consistent with bin_zle_call() */ else if (!strcmp(*flag, "vichange")) { if (invicmdmode()) { startvichange(-1); @@ -703,7 +704,7 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) { Thingy t; struct modifier modsave = zmod; - int ret, saveflag = 0, setbindk = 0, setlbindk, remetafy; + int ret, saveflag = 0, setbindk = 0, setlbindk = 0, remetafy; char *wname = *args++, *keymap_restore = NULL, *keymap_tmp; if (!wname) @@ -727,12 +728,26 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) while (*args && **args == '-') { char skip_this_arg[2] = "x"; char *num; + char *flag; if (!args[0][1] || args[0][1] == '-') { args++; break; } while (*++(*args)) { switch (**args) { + case 'f': + flag = args[0][1] ? args[0]+1 : args[1]; + if (flag == NULL || strcmp(flag, "nolast")) { + zwarnnam(name, "%s", "'nolast' expected after -f"); + if (remetafy) + metafy_line(); + return 1; + } + if (!args[0][1]) + *++args = skip_this_arg; + /* If you add magic strings here, be consistent with bin_zle_flags() */ + setlbindk = 1; + break; case 'n': num = args[0][1] ? args[0]+1 : args[1]; if (!num) { @@ -787,7 +802,7 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func)) * a vi range to detect a repeated key */ setbindk = setbindk || (t->widget && (t->widget->flags & (WIDGET_INT | ZLE_VIOPER)) == WIDGET_INT); - setlbindk = t->widget && (t->widget->flags & ZLE_NOLAST) == ZLE_NOLAST; + setlbindk |= t->widget && (t->widget->flags & ZLE_NOLAST) == ZLE_NOLAST; ret = execzlefunc(t, args, setbindk, setlbindk); unrefthingy(t); if (saveflag) diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 2b306fdcd..526216fa7 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -557,6 +557,22 @@ zlegetline(int *ll, int *cs) } +/* + * free() the 'memo' elements of region_highlights. + */ + +/**/ +void +free_region_highlights_memos(void) +{ + struct region_highlight *rhp; + for (rhp = region_highlights; + rhp < region_highlights + n_region_highlights; + rhp++) { + zfree((char*) rhp->memo, 0); + } +} + /* Forward reference */ struct zle_region; @@ -568,6 +584,7 @@ struct zle_region { int start; int end; int flags; + const char *memo; }; /* Forward reference */ @@ -632,6 +649,7 @@ zle_save_positions(void) newrhp->next = NULL; newrhp->atr = rhp->atr; newrhp->flags = rhp->flags; + newrhp->memo = ztrdup(rhp->memo); if (zlemetaline) { newrhp->start = rhp->start_meta; newrhp->end = rhp->end_meta; @@ -682,6 +700,7 @@ zle_restore_positions(void) nreg++, oldrhp = oldrhp->next) ; if (nreg + N_SPECIAL_HIGHLIGHTS != n_region_highlights) { + free_region_highlights_memos(); n_region_highlights = nreg + N_SPECIAL_HIGHLIGHTS; region_highlights = (struct region_highlight *) zrealloc(region_highlights, @@ -694,6 +713,7 @@ zle_restore_positions(void) rhp->atr = oldrhp->atr; rhp->flags = oldrhp->flags; + rhp->memo = oldrhp->memo; /* transferring ownership of the permanently-allocated memory */ if (zlemetaline) { rhp->start_meta = oldrhp->start; rhp->end_meta = oldrhp->end; @@ -707,6 +727,7 @@ zle_restore_positions(void) rhp++; } } else if (region_highlights) { + free_region_highlights_memos(); zfree(region_highlights, sizeof(struct region_highlight) * n_region_highlights); region_highlights = NULL; @@ -1509,7 +1530,7 @@ mkundoent(void) struct change *ch; UNMETACHECK(); - if(lastll == zlell && !ZS_memcmp(lastline, zleline, zlell)) { + if(lastll == zlell && lastlinesz >= zlell && !ZS_memcmp(lastline, zleline, zlell)) { lastcs = zlecs; return; } diff --git a/Src/builtin.c b/Src/builtin.c index aa5767cf1..8ef678b22 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -71,7 +71,7 @@ static struct builtin builtins[] = * But that's actually not useful, so it's more consistent to * cause an error. */ - BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL), + BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRst:W", NULL), BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL), BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlp:%rtux", "E"), BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "ckmMstTuUWx:z", NULL), @@ -89,7 +89,6 @@ static struct builtin builtins[] = 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 | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL), - BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), #if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG) @@ -841,7 +840,6 @@ int bin_cd(char *nam, char **argv, Options ops, int func) { LinkNode dir; - struct stat st1, st2; if (isset(RESTRICTED)) { zwarnnam(nam, "restricted"); @@ -860,23 +858,6 @@ bin_cd(char *nam, char **argv, Options ops, int func) } cd_new_pwd(func, dir, OPT_ISSET(ops, 'q')); - if (stat(unmeta(pwd), &st1) < 0) { - setjobpwd(); - zsfree(pwd); - pwd = NULL; - pwd = metafy(zgetcwd(), -1, META_DUP); - } else if (stat(".", &st2) < 0) { - if (chdir(unmeta(pwd)) < 0) - zwarn("unable to chdir(%s): %e", pwd, errno); - } else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) { - if (chasinglinks) { - setjobpwd(); - zsfree(pwd); - pwd = NULL; - pwd = metafy(zgetcwd(), -1, META_DUP); - } else if (chdir(unmeta(pwd)) < 0) - zwarn("unable to chdir(%s): %e", pwd, errno); - } unqueue_signals(); return 0; } @@ -1210,6 +1191,7 @@ static void cd_new_pwd(int func, LinkNode dir, int quiet) { char *new_pwd, *s; + struct stat st1, st2; int dirstacksize; if (func == BIN_PUSHD) @@ -1239,6 +1221,22 @@ cd_new_pwd(int func, LinkNode dir, int quiet) } } + if (stat(unmeta(new_pwd), &st1) < 0) { + zsfree(new_pwd); + new_pwd = NULL; + new_pwd = metafy(zgetcwd(), -1, META_DUP); + } else if (stat(".", &st2) < 0) { + if (chdir(unmeta(new_pwd)) < 0) + zwarn("unable to chdir(%s): %e", new_pwd, errno); + } else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) { + if (chasinglinks) { + zsfree(new_pwd); + new_pwd = NULL; + new_pwd = metafy(zgetcwd(), -1, META_DUP); + } else if (chdir(unmeta(new_pwd)) < 0) + zwarn("unable to chdir(%s): %e", new_pwd, errno); + } + /* shift around the pwd variables, to make oldpwd and pwd relate to the current (i.e. new) pwd */ zsfree(oldpwd); @@ -1600,8 +1598,9 @@ bin_fc(char *nam, char **argv, Options ops, int func) * command line to avoid giving the user a nasty turn * if some helpful soul ran "print -s 'rm -rf /'". */ - first = OPT_ISSET(ops,'l')? addhistnum(curhist,-16,0) - : addhistnum(curline.histnum,-1,0); + int xflags = OPT_ISSET(ops,'L') ? HIST_FOREIGN : 0; + first = OPT_ISSET(ops,'l')? addhistnum(curhist,-16,xflags) + : addhistnum(curline.histnum,-1,xflags); if (first < 1) first = 1; if (last < first) @@ -1644,7 +1643,7 @@ bin_fc(char *nam, char **argv, Options ops, int func) if (!fclist(out, ops, first, last, asgf, pprog, 1)) { char *editor; - if (func == BIN_R) + if (func == BIN_R || OPT_ISSET(ops, 's')) editor = "-"; else if (OPT_HASARG(ops, 'e')) editor = OPT_ARG(ops, 'e'); @@ -2024,7 +2023,7 @@ 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), +typeset_single(char *cname, char *pname, Param pm, int func, int on, int off, int roff, Asgment asg, Param altpm, Options ops, int joinchar) { @@ -2280,7 +2279,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } else if (asg->flags & ASG_ARRAY) { int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!(pm = assignaparam(pname, asg->value.array ? - zlinklist2array(asg->value.array) : + zlinklist2array(asg->value.array, 1) : mkarray(NULL), flags))) return NULL; } @@ -2442,7 +2441,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) { int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!(pm = assignaparam(pname, asg->value.array ? - zlinklist2array(asg->value.array) : + zlinklist2array(asg->value.array, 1) : mkarray(NULL), flags))) return NULL; dont_set = 1; @@ -2480,13 +2479,19 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return NULL; } if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) { - if (typeset_setwidth(cname, pm, ops, on, 0)) + if (typeset_setwidth(cname, pm, ops, on, 0)) { + unsetparam_pm(pm, 0, 1); return NULL; + } } if (on & (PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) { - if (typeset_setbase(cname, pm, ops, on, 0)) + if (typeset_setbase(cname, pm, ops, on, 0)) { + unsetparam_pm(pm, 0, 1); return NULL; + } } + if (isset(TYPESETTOUNSET)) + pm->node.flags |= PM_DEFAULTED; } else { if (idigit(*pname)) zerrnam(cname, "not an identifier: %s", pname); @@ -2503,8 +2508,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), */ struct tieddata *tdp = (struct tieddata *) zalloc(sizeof(struct tieddata)); - if (!tdp) + if (!tdp) { + unsetparam_pm(pm, 0, 1); return NULL; + } tdp->joinchar = joinchar; tdp->arrptr = &altpm->u.arr; @@ -2536,7 +2543,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), arrayval = mkarray(NULL); } } else if (asg->value.array) - arrayval = zlinklist2array(asg->value.array); + arrayval = zlinklist2array(asg->value.array, 1); else arrayval = mkarray(NULL); if (!(pm=assignaparam(pname, arrayval, flags))) @@ -2597,7 +2604,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), */ /**/ -int +mod_export int bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) { Param pm; @@ -2607,7 +2614,12 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) int on = 0, off = 0, roff, bit = PM_ARRAY; int i; int returnval = 0, printflags = 0; - int hasargs; + int hasargs = *argv != NULL || (assigns && firstnode(assigns)); + + /* POSIXBUILTINS is set for bash/ksh and both ignore -p with args */ + if ((func == BIN_READONLY || func == BIN_EXPORT) && + isset(POSIXBUILTINS) && hasargs) + ops->ind['p'] = 0; /* hash -f is really the builtin `functions' */ if (OPT_ISSET(ops,'f')) @@ -2687,7 +2699,6 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* -p0 treated as -p for consistency */ } } - hasargs = *argv != NULL || (assigns && firstnode(assigns)); if (!hasargs) { int exclude = 0; if (!OPT_ISSET(ops,'p')) { @@ -2830,7 +2841,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) unqueue_signals(); return 1; } else if (pm) { - if (!(pm->node.flags & PM_UNSET) + if ((!(pm->node.flags & PM_UNSET) || pm->node.flags & PM_DECLARED) && (locallevel == pm->level || !(on & PM_LOCAL))) { if (pm->node.flags & PM_TIED) { if (PM_TYPE(pm->node.flags) != PM_SCALAR) { @@ -2883,6 +2894,8 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) * * Don't attempt to set it yet, it's too early * to be exported properly. + * + * This may create the array with PM_DEFAULTED. */ asg2.name = asg->name; asg2.flags = 0; @@ -2923,9 +2936,13 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) apm->ename = ztrdup(asg0.name); if (asg->value.array) { int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; - assignaparam(asg->name, zlinklist2array(asg->value.array), flags); - } else if (oldval) - assignsparam(asg0.name, oldval, 0); + assignaparam(asg->name, zlinklist2array(asg->value.array, 1), flags); + } else if (asg0.value.scalar || oldval) { + /* We have to undo what we did wrong with asg2 */ + apm->node.flags &= ~PM_DEFAULTED; + if (oldval) + assignsparam(asg0.name, oldval, 0); + } unqueue_signals(); return 0; @@ -3706,14 +3723,12 @@ bin_unset(char *name, char **argv, Options ops, int func) while ((s = *argv++)) { char *ss = strchr(s, '['), *subscript = 0; if (ss) { - char *sse; + char *sse = ss + strlen(ss)-1; *ss = 0; - if ((sse = parse_subscript(ss+1, 1, ']'))) { + if (*sse == ']') { *sse = 0; subscript = dupstring(ss+1); *sse = ']'; - remnulargs(subscript); - untokenize(subscript); } } if ((ss && !subscript) || !isident(s)) { @@ -3901,7 +3916,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) } unqueue_signals(); if (all) { - allmatched = argv = zlinklist2array(matchednodes); + allmatched = argv = zlinklist2array(matchednodes, 1); matchednodes = NULL; popheap(); } else @@ -4817,7 +4832,7 @@ bin_print(char *name, char **args, Options ops, int func) { fwrite(args[n], len[n], 1, fout); l = widths[n]; - if (n < argc) + if (n < argc && ic < nc - 1) for (; l < sc; l++) fputc(' ', fout); } @@ -4857,7 +4872,7 @@ bin_print(char *name, char **args, Options ops, int func) /* normal output */ if (!fmt) { - if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') || + if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')) { /* * We don't want the arguments unmetafied after all. @@ -5550,6 +5565,11 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun /* check for legality */ if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) { p = "?"; + /* Keep OPTIND correct if the user doesn't return after the error */ + if (isset(POSIXBUILTINS)) { + optcind = 0; + zoptind++; + } zsfree(zoptarg); setsparam(var, ztrdup(p)); if(quiet) { @@ -5566,6 +5586,11 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun if(p[1] == ':') { if(optcind == lenstr) { if(!args[zoptind]) { + /* Fix OPTIND as above */ + if (isset(POSIXBUILTINS)) { + optcind = 0; + zoptind++; + } zsfree(zoptarg); if(quiet) { setsparam(var, ztrdup(":")); @@ -5607,13 +5632,16 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun */ /**/ -mod_export int -exit_pending; +mod_export volatile int exit_pending; /* Shell level at which we exit if exit_pending */ /**/ -mod_export int -exit_level; +mod_export volatile int exit_level; + +/* we have printed a 'you have stopped (running) jobs.' message */ + +/**/ +mod_export volatile int stopmsg; /* break, bye, continue, exit, logout, return -- most of these take * * one numeric argument, and the other (logout) is related to return. * @@ -5705,11 +5733,6 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func) return 0; } -/* we have printed a 'you have stopped (running) jobs.' message */ - -/**/ -mod_export int stopmsg; - /* check to see if user has jobs running/stopped */ /**/ @@ -5800,8 +5823,11 @@ zexit(int val, enum zexit_t from_where) * a later value always overrides an earlier. */ exit_val = val; - if (shell_exiting == -1) + if (shell_exiting == -1) { + retflag = 1; + breaks = loops; return; + } if (isset(MONITOR) && !stopmsg && from_where != ZEXIT_SIGNAL) { scanjobs(); /* check if jobs need printing */ @@ -6119,7 +6145,7 @@ bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func)) savehackchar = keyboardhackchar; emulate(shname, opt_R, &new_emulation, new_opts); optlist = newlinklist(); - if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0)) { + if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0, NULL)) { ret = 1; goto restore; } diff --git a/Src/compat.c b/Src/compat.c index 8ab335aa1..817bb4aaf 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -126,6 +126,32 @@ zgettime(struct timespec *ts) return ret; } +/* Likewise with CLOCK_MONOTONIC if available. */ + +/**/ +mod_export int +zgettime_monotonic_if_available(struct timespec *ts) +{ + int ret = -1; + +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + struct timespec dts; + if (clock_gettime(CLOCK_MONOTONIC, &dts) < 0) { + zwarn("unable to retrieve CLOCK_MONOTONIC time: %e", errno); + ret--; + } else { + ret++; + ts->tv_sec = (time_t) dts.tv_sec; + ts->tv_nsec = (long) dts.tv_nsec; + } +#endif + + if (ret) { + ret = zgettime(ts); + } + return ret; +} + /* compute the difference between two calendar times */ @@ -496,7 +522,7 @@ zgetdir(struct dirsav *d) */ /**/ -char * +mod_export char * zgetcwd(void) { char *ret = zgetdir(NULL); diff --git a/Src/exec.c b/Src/exec.c index 50027654a..27d49e005 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -84,7 +84,7 @@ int nohistsave; /* error flag: bits from enum errflag_bits */ /**/ -mod_export int errflag; +mod_export volatile int errflag; /* * State of trap return value. Value is from enum trap_state. @@ -122,7 +122,7 @@ int subsh; /* != 0 if we have a return pending */ /**/ -mod_export int retflag; +mod_export volatile int retflag; /**/ long lastval2; @@ -547,10 +547,29 @@ zexecve(char *pth, char **argv, char **newenvp) } } } else if (eno == ENOEXEC) { - for (t0 = 0; t0 != ct; t0++) - if (!execvebuf[t0]) - break; - if (t0 == ct) { + /* Perform binary safety check on classic shell * + * scripts (shebang wasn't introduced until UNIX * + * Seventh Edition). POSIX says we shall allow * + * execution of scripts with concatenated binary * + * and suggests checking a line exists before the * + * first NUL character with a lowercase letter or * + * expansion. This is consistent with FreeBSD sh. */ + int isbinary, hasletter; + if (!(ptr2 = memchr(execvebuf, '\0', ct))) { + isbinary = 0; + } else { + isbinary = 1; + hasletter = 0; + for (ptr = execvebuf; ptr < ptr2; ptr++) { + if (islower(*ptr) || *ptr == '$' || *ptr == '`') + hasletter = 1; + if (hasletter && *ptr == '\n') { + isbinary = 0; + break; + } + } + } + if (!isbinary) { argv[-1] = "sh"; winch_unblock(); execve("/bin/sh", argv - 1, newenvp); @@ -665,8 +684,10 @@ execute(LinkList args, int flags, int defpath) /* If the parameter STTY is set in the command's environment, * * we first run the stty command with the value of this * - * parameter as it arguments. */ - if ((s = STTYval) && isatty(0) && (GETPGRP() == getpid())) { + * parameter as it arguments. If the parameter is empty, we * + * do nothing, but this causes the terminal settings to be * + * restored later which can be useful. */ + if ((s = STTYval) && *s && isatty(0) && (GETPGRP() == getpid())) { char *t = tricat("stty", " ", s); STTYval = 0; /* this prevents infinite recursion */ @@ -1014,7 +1035,8 @@ entersubsh(int flags, struct entersubsh_ret *retp) if (!(flags & ESUB_KEEPTRAP)) for (sig = 0; sig < SIGCOUNT; sig++) - if (!(sigtrapped[sig] & ZSIG_FUNC)) + if (!(sigtrapped[sig] & ZSIG_FUNC) && + !(isset(POSIXTRAPS) && (sigtrapped[sig] & ZSIG_IGNORED))) unsettrap(sig); monitor = isset(MONITOR); job_control_ok = monitor && (flags & ESUB_JOB_CONTROL) && isset(POSIXJOBS); @@ -1036,7 +1058,8 @@ entersubsh(int flags, struct entersubsh_ret *retp) } else if (thisjob != -1 && (flags & ESUB_PGRP)) { if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) { if (setpgrp(0L, jobtab[list_pipe_job].gleader) == -1 || - killpg(jobtab[list_pipe_job].gleader, 0) == -1) { + (killpg(jobtab[list_pipe_job].gleader, 0) == -1 && + errno == ESRCH)) { jobtab[list_pipe_job].gleader = jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid()); setpgrp(0L, jobtab[list_pipe_job].gleader); @@ -1248,7 +1271,9 @@ execsimple(Estate state) } else { int q = queue_signal_level(); dont_queue_signals(); - if (code == WC_FUNCDEF) + if (errflag) + lv = errflag; + else if (code == WC_FUNCDEF) lv = execfuncdef(state, NULL); else lv = (execfuncs[code - WC_CURSH])(state, 0); @@ -1664,6 +1689,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) execpline2(state, code, how, opipe[0], ipipe[1], last1); pline_level--; if (how & Z_ASYNC) { + clearoldjobtab(); lastwj = newjob; if (thisjob == list_pipe_job) @@ -2142,14 +2168,15 @@ clobber_open(struct redir *f) { struct stat buf; int fd, oerrno; + char *ufname = unmeta(f->name); /* If clobbering, just open. */ if (isset(CLOBBER) || IS_CLOBBER_REDIR(f->type)) - return open(unmeta(f->name), + return open(ufname, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0666); /* If not clobbering, attempt to create file exclusively. */ - if ((fd = open(unmeta(f->name), + if ((fd = open(ufname, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0666)) >= 0) return fd; @@ -2157,11 +2184,27 @@ clobber_open(struct redir *f) * Try opening, and if it's a regular file then close it again * * because we weren't supposed to open it. */ oerrno = errno; - if ((fd = open(unmeta(f->name), O_WRONLY | O_NOCTTY)) != -1) { - if(!fstat(fd, &buf) && !S_ISREG(buf.st_mode)) - return fd; + if ((fd = open(ufname, O_WRONLY | O_NOCTTY)) != -1) { + if(!fstat(fd, &buf)) { + if (!S_ISREG(buf.st_mode)) + return fd; + /* + * If CLOBBER_EMPTY is in effect and the file is empty, + * we are allowed to re-use it. + * + * Note: there is an intrinsic race here because another + * process can write to this file at any time. The only fix + * would be file locking, which we wish to avoid in basic + * file operations at this level. This would not be + * fixed. just additionally complicated, by re-opening the + * file and truncating. + */ + if (isset(CLOBBEREMPTY) && buf.st_size == 0) + return fd; + } close(fd); } + errno = oerrno; return -1; } @@ -2771,8 +2814,7 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc, /* Check if we should run background jobs at a lower priority. */ if ((how & Z_ASYNC) && isset(BGNICE)) { errno = 0; - nice(5); - if (errno) + if (nice(5) == -1 && errno) zwarn("nice(5) failed: %e", errno); } #endif /* HAVE_NICE */ @@ -2865,11 +2907,13 @@ execcmd_exec(Estate state, Execcmd_params eparams, pushnode(args, dupstring("fg")); } - if ((how & Z_ASYNC) || output) { + if ((how & Z_ASYNC) || output || + (last1 == 2 && input && EMULATION(EMULATE_SH))) { /* - * If running in the background, or not the last command in a - * pipeline, we don't need any of the rest of this function to - * affect the state in the main shell, so fork immediately. + * If running in the background, not the last command in a + * pipeline, or the last command in a multi-stage pipeline + * in sh mode, we don't need any of the rest of this function + * to affect the state in the main shell, so fork immediately. * * In other cases we may need to process the command line * a bit further before we make the decision. @@ -3383,7 +3427,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, int rmall; s[l - 2] = 0; - rmall = checkrmall(s); + rmall = checkrmall(l == 2 ? "/" : s); s[l - 2] = t; if (!rmall) { @@ -3913,7 +3957,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, if (type == WC_AUTOFN) { /* * We pre-loaded this to get any redirs. - * So we execuate a simplified function here. + * So we execute a simplified function here. */ lastval = execautofn_basic(state, do_exec); } else @@ -3968,8 +4012,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, if (is_shfunc) { /* It's a shell function */ - pipecleanfilelist(filelist, 0); execshfunc((Shfunc) hn, args); + pipecleanfilelist(filelist, 0); } else { /* It's a builtin */ LinkList assigns = (LinkList)0; @@ -4581,7 +4625,7 @@ getoutput(char *cmd, int qt) char *s; int onc = nocomments; - nocomments = (interact && unset(INTERACTIVECOMMENTS)); + nocomments = (interact && !sourcelevel && unset(INTERACTIVECOMMENTS)); prog = parse_string(cmd, 0); nocomments = onc; @@ -4793,8 +4837,10 @@ getoutputfile(char *cmd, char **eptr) singsub(&s); if (errflag) s = NULL; - else + else { untokenize(s); + s = dyncat(s, "\n"); + } } if (!s) /* Unclear why we need to do this before open() */ @@ -5146,33 +5192,51 @@ exectime(Estate state, UNUSED(int do_exec)) return lastval; } -/* Define a shell function */ - +/* The string displayed in lieu of the name of an anonymous function (in PS4, + * zprof output, etc) + */ static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)"; +/* + * Take a function name argument and return true iff it is equal to the string + * used for the names of anonymous functions, "(anon)". + * + * Note that it's possible to define a named function literally called "(anon)" + * (though I doubt anyone would ever do that). + */ +/**/ +int is_anonymous_function_name(const char *name) +{ + return !strcmp(name, ANONYMOUS_FUNCTION_NAME); +} + +/* Define a shell function */ + /**/ static int execfuncdef(Estate state, Eprog redir_prog) { Shfunc shf; char *s = NULL; - int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0, ret = 0; + int signum, nprg, sbeg, nstrs, npats, do_tracing, len, plen, i, htok = 0, ret = 0; int anon_func = 0; Wordcode beg = state->pc, end; Eprog prog; Patprog *pp; LinkList names; + int tracing_flags; end = beg + WC_FUNCDEF_SKIP(state->pc[-1]); names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); - nprg = end - beg; sbeg = *state->pc++; nstrs = *state->pc++; npats = *state->pc++; + do_tracing = *state->pc++; nprg = (end - state->pc); plen = nprg * sizeof(wordcode); len = plen + (npats * sizeof(Patprog)) + nstrs; + tracing_flags = do_tracing ? PM_TAGGED_LOCAL : 0; if (htok && names) { execsubst(names); @@ -5222,7 +5286,7 @@ execfuncdef(Estate state, Eprog redir_prog) shf = (Shfunc) zalloc(sizeof(*shf)); shf->funcdef = prog; - shf->node.flags = 0; + shf->node.flags = tracing_flags; /* No dircache here, not a directory */ shf->filename = ztrdup(scriptfilename); shf->lineno = @@ -5317,6 +5381,12 @@ execfuncdef(Estate state, Eprog redir_prog) */ removetrapnode(signum); } + /* Is this function traced and redefining itself? */ + if (funcstack && funcstack->tp == FS_FUNC && + !strcmp(s, funcstack->name)) { + Shfunc old = ((Shfunc)shfunctab->getnode(shfunctab, s)); + shf->node.flags |= old->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL); + } shfunctab->addnode(shfunctab, ztrdup(s), shf); } } @@ -6132,7 +6202,7 @@ stripkshdef(Eprog prog, char *name) int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i; Patprog *pp; - pc += 5; + pc += 6; nprg = end - pc; plen = nprg * sizeof(wordcode); diff --git a/Src/glob.c b/Src/glob.c index f67a376b9..400be12d5 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -279,11 +279,11 @@ addpath(char *s, int l) * foo/ can be used to reference a non-directory foo. Returns nonzero if * * the file does not exists. */ -/**/ static int statfullpath(const char *s, struct stat *st, int l) { char buf[PATH_MAX+1]; + int check_for_being_a_directory = 0; DPUTS(strlen(s) + !*s + pathpos - pathbufcwd >= PATH_MAX, "BUG: statfullpath(): pathname too long"); @@ -294,16 +294,44 @@ statfullpath(const char *s, struct stat *st, int l) * Don't add the '.' if the path so far is empty, since * then we get bogus empty strings inserted as files. */ - buf[pathpos - pathbufcwd] = '.'; - buf[pathpos - pathbufcwd + 1] = '\0'; - l = 0; + if (st) { + buf[pathpos - pathbufcwd] = '.'; + buf[pathpos - pathbufcwd + 1] = '\0'; + l = 0; + } + else { + check_for_being_a_directory = 1; + } } unmetafy(buf, NULL); - if (!st) { + if (st) { + return l ? lstat(buf, st) : stat(buf, st); + } + else if (check_for_being_a_directory) { + struct stat tmp; + if (stat(buf, &tmp)) + return -1; + + return S_ISDIR(tmp.st_mode) ? 0 : -1; + } + else { char lbuf[1]; - return access(buf, F_OK) && (!l || readlink(buf, lbuf, 1) < 0); + + /* If it exists, signal success. */ + if (access(buf, F_OK) == 0) + return 0; + + /* Would a dangling symlink be good enough? */ + if (l == 0) + return -1; + + /* Is it a dangling symlink? */ + if (readlink(buf, lbuf, 1) >= 0) + return 0; + + /* Guess it doesn't exist, then. */ + return -1; } - return l ? lstat(buf, st) : stat(buf, st); } /* This may be set by qualifier functions to an array of strings to insert @@ -327,11 +355,13 @@ insert(char *s, int checked) if (gf_listtypes || gf_markdirs) { /* Add the type marker to the end of the filename */ mode_t mode; - checked = statted = 1; if (statfullpath(s, &buf, 1)) { unqueue_signals(); return; } + else { + checked = statted = 1; + } mode = buf.st_mode; if (gf_follow) { if (!S_ISLNK(mode) || statfullpath(s, &buf2, 0)) @@ -387,11 +417,10 @@ insert(char *s, int checked) qn = qn->next; } } else if (!checked) { - if (statfullpath(s, &buf, 1)) { + if (statfullpath(s, NULL, 1)) { unqueue_signals(); return; } - statted = 1; news = dyncat(pathbuf, news); } else news = dyncat(pathbuf, news); @@ -2191,7 +2220,7 @@ bracechardots(char *str, convchar_t *c1p, convchar_t *c2p) #ifdef MULTIBYTE_SUPPORT cstart == WEOF || #else - !cstart || + !*pconv || #endif pnext[0] != '.' || pnext[1] != '.') return 0; @@ -2212,7 +2241,7 @@ bracechardots(char *str, convchar_t *c1p, convchar_t *c2p) #ifdef MULTIBYTE_SUPPORT cend == WEOF || #else - !cend || + !*pconv || #endif *pnext != Outbrace) return 0; @@ -2276,22 +2305,19 @@ xpandbraces(LinkList list, LinkNode *np) strp = str - str3; lenalloc = strp + strlen(str2+1) + 1; do { -#ifdef MULTIBYTE_SUPPORT char *ncptr; int nclen; +#ifdef MULTIBYTE_SUPPORT mb_charinit(); ncptr = wcs_nicechar(cend, NULL, NULL); +#else + ncptr = nicechar(cend); +#endif nclen = strlen(ncptr); p = zhalloc(lenalloc + nclen); memcpy(p, str3, strp); memcpy(p + strp, ncptr, nclen); strcpy(p + strp + nclen, str2 + 1); -#else - p = zhalloc(lenalloc + 1); - memcpy(p, str3, strp); - sprintf(p + strp, "%c", cend); - strcat(p + strp, str2 + 1); -#endif insertlinknode(list, last, p); if (rev) /* decreasing: add in reverse order. */ last = nextnode(last); @@ -2539,7 +2565,8 @@ get_match_ret(Imatchdata imd, int b, int e) addlinknode(imd->repllist, rd); return imd->mstr; } - ll += strlen(replstr); + if (replstr) + ll += strlen(replstr); } if (fl & SUB_MATCH) /* matched portion */ ll += 1 + (e - b); @@ -2565,6 +2592,9 @@ get_match_ret(Imatchdata imd, int b, int e) if (bl) buf[bl - 1] = '\0'; + if (ll == 0) + return NULL; + rr = r = (char *) hcalloc(ll); if (fl & SUB_MATCH) { @@ -3253,6 +3283,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, return 1; } if (matched) { + /* Default is to match at the start; see comment in MULTIBYTE above */ switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) { case 0: case SUB_LONG: @@ -3300,7 +3331,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. */ - for (ioff = 0, t = s, umlen = uml; t < send; + for (ioff = 0, t = s, umlen = uml; t <= send; ioff++, t++, umlen--) { set_pat_start(p, t-s); if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) { @@ -3322,7 +3353,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, /* longest or smallest at start with substrings */ t = s; if (fl & SUB_GLOBAL) { - imd.repllist = newlinklist(); + imd.repllist = (fl & SUB_LIST) ? znewlinklist() : newlinklist(); if (repllistp) *repllistp = imd.repllist; } @@ -3331,7 +3362,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, do { /* loop over all matches for global substitution */ matched = 0; - for (; t < send; t++, ioff++, umlen--) { + for (; t <= send; t++, ioff++, umlen--) { /* Find the longest match from this position. */ set_pat_start(p, t-s); if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) { @@ -3372,6 +3403,8 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * which is already marked for replacement. */ matched = 1; + if (t == send) + break; while (t < mpos) { ioff++; umlen--; @@ -3379,8 +3412,10 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, } break; } + if (t == send) + break; } - } while (matched); + } while (matched && t < send); /* * check if we can match a blank string, if so do it * at the start. Goodness knows if this is a good idea @@ -3452,6 +3487,8 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * Results from get_match_ret in repllist are all metafied. */ s = *sp; + if (fl & SUB_LIST) + return 1; i = 0; /* start of last chunk we got from *sp */ for (nd = firstnode(imd.repllist); nd; incnode(nd)) { rd = (Repldata) getdata(nd); @@ -3481,7 +3518,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, imd.replstr = NULL; imd.repllist = NULL; *sp = get_match_ret(&imd, 0, 0); - return 1; + return (fl & SUB_RETFAIL) ? 0 : 1; } /**/ diff --git a/Src/hashnameddir.c b/Src/hashnameddir.c index bed43d025..bab7b64b2 100644 --- a/Src/hashnameddir.c +++ b/Src/hashnameddir.c @@ -42,17 +42,6 @@ /* Named Directory Hash Table Functions */ /****************************************/ -#ifdef HAVE_NIS_PLUS -# include <rpcsvc/nis.h> -#else -# ifdef HAVE_NIS -# include <rpc/types.h> -# include <rpc/rpc.h> -# include <rpcsvc/ypclnt.h> -# include <rpcsvc/yp_prot.h> -# endif -#endif - /* hash table containing named directories */ /**/ @@ -102,122 +91,11 @@ emptynameddirtable(HashTable ht) /* Add all the usernames in the password file/database * * to the named directories table. */ -#ifdef HAVE_NIS_PLUS -static int -add_userdir(nis_name table, nis_object *object, void *userdata) -{ - if (object->zo_data.objdata_u.en_data.en_cols.en_cols_len >= 6) { - static char name[40], dir[PATH_MAX + 1]; - register entry_col *ec = - object->zo_data.objdata_u.en_data.en_cols.en_cols_val; - register int nl = minimum(ec[0].ec_value.ec_value_len, 39); - register int dl = minimum(ec[5].ec_value.ec_value_len, PATH_MAX); - - memcpy(name, ec[0].ec_value.ec_value_val, nl); - name[nl] = '\0'; - memcpy(dir, ec[5].ec_value.ec_value_val, dl); - dir[dl] = '\0'; - - adduserdir(name, dir, ND_USERNAME, 1); - } - return 0; -} -#else -# ifdef HAVE_NIS -static int -add_userdir(int status, char *key, int keylen, char *val, int vallen, char *dummy) -{ - char *p, *d, *de; - - if (status != YP_TRUE) - return 1; - - if (vallen > keylen && *(p = val + keylen) == ':') { - *p++ = '\0'; - for (de = val + vallen - 1; *de != ':' && de > val; de--); - if (de > val) { - *de = '\0'; - if ((d = strrchr(p, ':'))) { - if (*++d && val[0]) - adduserdir(val, d, ND_USERNAME, 1); - } - } - } - return 0; -} -# endif /* HAVE_NIS */ -#endif /* HAVE_NIS_PLUS */ - /**/ static void fillnameddirtable(UNUSED(HashTable ht)) { if (!allusersadded) { -#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS) - FILE *pwf; - char buf[BUFSIZ], *p, *d, *de; - int skipping, oldct = nameddirtab->ct, usepwf = 1; - -# ifndef HAVE_NIS_PLUS - char domain[YPMAXDOMAIN]; - struct ypall_callback cb; - - /* Get potential matches from NIS and cull those without local accounts */ - if (getdomainname(domain, YPMAXDOMAIN) == 0) { - cb.foreach = (int (*)()) add_userdir; - cb.data = NULL; - yp_all(domain, PASSWD_MAP, &cb); - } -# else /* HAVE_NIS_PLUS */ - /* Maybe we should turn this string into a #define'd constant...? */ - - nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH, - add_userdir, 0); -# endif - if (nameddirtab->ct == oldct) { - /* Using NIS or NIS+ didn't add any user directories. This seems - * fishy, so we fall back to using getpwent(). If we don't have - * that, we only use the passwd file. */ -#ifdef HAVE_GETPWENT - struct passwd *pw; - - setpwent(); - - /* loop through the password file/database * - * and add all entries returned. */ - while ((pw = getpwent()) && !errflag) - adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1); - - endpwent(); - usepwf = 0; -#endif /* HAVE_GETPWENT */ - } - if (usepwf) { - /* Don't forget the non-NIS matches from the flat passwd file */ - if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) { - skipping = 0; - while (fgets(buf, BUFSIZ, pwf) != NULL) { - if (strchr(buf, '\n') != NULL) { - if (!skipping) { - if ((p = strchr(buf, ':')) != NULL) { - *p++ = '\0'; - if ((de = strrchr(p, ':'))) { - *de = '\0'; - if ((d = strrchr(p, ':'))) { - if (*++d && buf[0]) - adduserdir(buf, d, ND_USERNAME, 1); - } - } - } - } else - skipping = 0; - } else - skipping = 1; - } - fclose(pwf); - } - } -#else /* no NIS or NIS_PLUS */ #ifdef USE_GETPWENT struct passwd *pw; @@ -229,8 +107,7 @@ fillnameddirtable(UNUSED(HashTable ht)) adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1); endpwent(); -#endif /* HAVE_GETPWENT */ -#endif +#endif /* USE_GETPWENT */ allusersadded = 1; } } diff --git a/Src/hashtable.c b/Src/hashtable.c index e210ddeca..bb165505e 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -28,25 +28,29 @@ */ #include "../config.h" +#include "zsh.mdh" +#include "hashtable.pro" + +typedef struct scanstatus *ScanStatus; +typedef struct hashtableimpl* HashTableImpl; + +struct hashtableimpl { + /* Public part of hash table, accessible from outside of hashtable.c. * + * Must be the first field to allow casting HashTable to HashTableImpl. */ + struct hashtable pub; + + /* HASHTABLE INTERNAL MEMBERS */ + ScanStatus scan; /* status of a scan over this hashtable */ #ifdef ZSH_HASH_DEBUG -# define HASHTABLE_DEBUG_MEMBERS \ - /* Members of struct hashtable used for debugging hash tables */ \ - HashTable next, last; /* linked list of all hash tables */ \ - char *tablename; /* string containing name of the hash table */ \ + /* HASHTABLE DEBUG MEMBERS */ + HashTableImpl next, last; /* linked list of all hash tables */ + char *tablename; /* string containing name of the hash table */ PrintTableStats printinfo; /* pointer to function to print table stats */ -#else /* !ZSH_HASH_DEBUG */ -# define HASHTABLE_DEBUG_MEMBERS #endif /* !ZSH_HASH_DEBUG */ +}; -#define HASHTABLE_INTERNAL_MEMBERS \ - ScanStatus scan; /* status of a scan over this hashtable */ \ - HASHTABLE_DEBUG_MEMBERS - -typedef struct scanstatus *ScanStatus; - -#include "zsh.mdh" -#include "hashtable.pro" +static inline HashTableImpl impl(HashTable ht) { return (HashTableImpl)ht; } /* Structure for recording status of a hashtable scan in progress. When a * * scan starts, the .scan member of the hashtable structure points to one * @@ -71,7 +75,8 @@ struct scanstatus { /********************************/ #ifdef ZSH_HASH_DEBUG -static HashTable firstht, lastht; +static void printhashtabinfo(HashTable ht); +static HashTableImpl firstht, lastht; #endif /* ZSH_HASH_DEBUG */ /* Generic hash function */ @@ -94,9 +99,9 @@ hasher(const char *str) mod_export HashTable newhashtable(int size, UNUSED(char const *name), UNUSED(PrintTableStats printinfo)) { - HashTable ht; + HashTableImpl ht; - ht = (HashTable) zshcalloc(sizeof *ht); + ht = (HashTableImpl) zshcalloc(sizeof *ht); #ifdef ZSH_HASH_DEBUG ht->next = NULL; if(!firstht) @@ -108,12 +113,12 @@ newhashtable(int size, UNUSED(char const *name), UNUSED(PrintTableStats printinf ht->printinfo = printinfo ? printinfo : printhashtabinfo; ht->tablename = ztrdup(name); #endif /* ZSH_HASH_DEBUG */ - ht->nodes = (HashNode *) zshcalloc(size * sizeof(HashNode)); - ht->hsize = size; - ht->ct = 0; + ht->pub.nodes = (HashNode *) zshcalloc(size * sizeof(HashNode)); + ht->pub.hsize = size; + ht->pub.ct = 0; ht->scan = NULL; - ht->scantab = NULL; - return ht; + ht->pub.scantab = NULL; + return &ht->pub; } /* Delete a hash table. After this function has been used, any * @@ -125,18 +130,18 @@ deletehashtable(HashTable ht) { ht->emptytable(ht); #ifdef ZSH_HASH_DEBUG - if(ht->next) - ht->next->last = ht->last; + if(impl(ht)->next) + impl(ht)->next->last = impl(ht)->last; else - lastht = ht->last; - if(ht->last) - ht->last->next = ht->next; + lastht = impl(ht)->last; + if(impl(ht)->last) + impl(ht)->last->next = impl(ht)->next; else - firstht = ht->next; - zsfree(ht->tablename); + firstht = impl(ht)->next; + zsfree(impl(ht)->tablename); #endif /* ZSH_HASH_DEBUG */ zfree(ht->nodes, ht->hsize * sizeof(HashNode)); - zfree(ht, sizeof(*ht)); + zfree(ht, sizeof(struct hashtableimpl)); } /* Add a node to a hash table. * @@ -175,7 +180,7 @@ addhashnode2(HashTable ht, char *nam, void *nodeptr) if (!hp) { hn->next = NULL; ht->nodes[hashval] = hn; - if (++ht->ct >= ht->hsize * 2 && !ht->scan) + if (++ht->ct >= ht->hsize * 2 && !impl(ht)->scan) expandhashtable(ht); return NULL; } @@ -185,15 +190,15 @@ addhashnode2(HashTable ht, char *nam, void *nodeptr) ht->nodes[hashval] = hn; replacing: hn->next = hp->next; - if(ht->scan) { - if(ht->scan->sorted) { - HashNode *hashtab = ht->scan->u.s.hashtab; + if(impl(ht)->scan) { + if(impl(ht)->scan->sorted) { + HashNode *hashtab = impl(ht)->scan->u.s.hashtab; int i; - for(i = ht->scan->u.s.ct; i--; ) + for(i = impl(ht)->scan->u.s.ct; i--; ) if(hashtab[i] == hp) hashtab[i] = hn; - } else if(ht->scan->u.u == hp) - ht->scan->u.u = hn; + } else if(impl(ht)->scan->u.u == hp) + impl(ht)->scan->u.u = hn; } return hp; } @@ -211,7 +216,7 @@ addhashnode2(HashTable ht, char *nam, void *nodeptr) /* else just add it at the front of the list */ hn->next = ht->nodes[hashval]; ht->nodes[hashval] = hn; - if (++ht->ct >= ht->hsize * 2 && !ht->scan) + if (++ht->ct >= ht->hsize * 2 && !impl(ht)->scan) expandhashtable(ht); return NULL; } @@ -284,15 +289,15 @@ removehashnode(HashTable ht, const char *nam) ht->nodes[hashval] = hp->next; gotit: ht->ct--; - if(ht->scan) { - if(ht->scan->sorted) { - HashNode *hashtab = ht->scan->u.s.hashtab; + if(impl(ht)->scan) { + if(impl(ht)->scan->sorted) { + HashNode *hashtab = impl(ht)->scan->u.s.hashtab; int i; - for(i = ht->scan->u.s.ct; i--; ) + for(i = impl(ht)->scan->u.s.ct; i--; ) if(hashtab[i] == hp) hashtab[i] = NULL; - } else if(ht->scan->u.u == hp) - ht->scan->u.u = hp->next; + } else if(impl(ht)->scan->u.u == hp) + impl(ht)->scan->u.u = hp->next; } return hp; } @@ -399,7 +404,7 @@ scanmatchtable(HashTable ht, Patprog pprog, int sorted, st.sorted = 1; st.u.s.hashtab = hnsorttab; st.u.s.ct = ct; - ht->scan = &st; + impl(ht)->scan = &st; for (htp = hnsorttab, i = 0; i < ct; i++, htp++) { if ((!flags1 || ((*htp)->flags & flags1)) && @@ -410,13 +415,13 @@ scanmatchtable(HashTable ht, Patprog pprog, int sorted, } } - ht->scan = NULL; + impl(ht)->scan = NULL; } else { int i, hsize = ht->hsize; HashNode *nodes = ht->nodes; st.sorted = 0; - ht->scan = &st; + impl(ht)->scan = &st; for (i = 0; i < hsize; i++) for (st.u.u = nodes[i]; st.u.u; ) { @@ -429,7 +434,7 @@ scanmatchtable(HashTable ht, Patprog pprog, int sorted, } } - ht->scan = NULL; + impl(ht)->scan = NULL; } return match; @@ -531,7 +536,7 @@ printhashtabinfo(HashTable ht) int chainlen[MAXDEPTH + 1]; int i, tmpcount, total; - printf("name of table : %s\n", ht->tablename); + printf("name of table : %s\n", impl(ht)->tablename); printf("size of nodes[] : %d\n", ht->hsize); printf("number of nodes : %d\n\n", ht->ct); @@ -560,12 +565,12 @@ printhashtabinfo(HashTable ht) int bin_hashinfo(UNUSED(char *nam), UNUSED(char **args), UNUSED(Options ops), UNUSED(int func)) { - HashTable ht; + HashTableImpl ht; printf("----------------------------------------------------\n"); queue_signals(); for(ht = firstht; ht; ht = ht->next) { - ht->printinfo(ht); + ht->printinfo(&ht->pub); printf("----------------------------------------------------\n"); } unqueue_signals(); diff --git a/Src/hist.c b/Src/hist.c index 5281e8718..f9440dba7 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -339,6 +339,13 @@ hist_in_word(int yesno) histactive &= ~HA_INWORD; } +/**/ +int +hist_is_in_word(void) +{ + return (histactive & HA_INWORD) ? 1 : 0; +} + /* add a character to the current history word */ static void @@ -842,7 +849,7 @@ histsubchar(int c) break; case 'A': - if (!chrealpath(&sline)) { + if (!chrealpath(&sline, 'A', 1)) { herrflush(); zerr("modifier failed: A"); return -1; @@ -1854,7 +1861,11 @@ chabspath(char **junkptr) return 1; if (**junkptr != '/') { - *junkptr = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", *junkptr); + char *here = zgetcwd(); + if (here[strlen(here)-1] != '/') + *junkptr = zhtricat(metafy(here, -1, META_HEAPDUP), "/", *junkptr); + else + *junkptr = dyncat(here, *junkptr); } current = *junkptr; @@ -1922,9 +1933,20 @@ chabspath(char **junkptr) return 1; } +/* + * Resolve symlinks in junkptr. + * + * If mode is 'A', resolve dot-dot before symlinks. Else, mode should be 'P'. + * Refer to the documentation of the :A and :P modifiers for details. + * + * use_heap is 1 if the result is to be allocated on the heap, 0 otherwise. + * + * Return 0 for error, non-zero for success. + */ + /**/ int -chrealpath(char **junkptr) +chrealpath(char **junkptr, char mode, int use_heap) { char *str; #ifdef HAVE_REALPATH @@ -1936,12 +1958,14 @@ chrealpath(char **junkptr) # endif #endif + DPUTS1(mode != 'A' && mode != 'P', "chrealpath: mode='%c' is invalid", mode); + if (!**junkptr) return 1; - /* Notice that this means ..'s are applied before symlinks are resolved! */ - if (!chabspath(junkptr)) - return 0; + if (mode == 'A') + if (!chabspath(junkptr)) + return 0; #ifndef HAVE_REALPATH return 1; @@ -1989,14 +2013,15 @@ chrealpath(char **junkptr) str++; } + use_heap = (use_heap ? META_HEAPDUP : META_DUP); if (real) { - *junkptr = metafy(str = bicat(real, nonreal), -1, META_HEAPDUP); + *junkptr = metafy(str = bicat(real, nonreal), -1, use_heap); zsfree(str); #ifdef REALPATH_ACCEPTS_NULL free(real); #endif } else { - *junkptr = metafy(nonreal, lastpos - nonreal + 1, META_HEAPDUP); + *junkptr = metafy(nonreal, lastpos - nonreal + 1, use_heap); } #endif @@ -2227,6 +2252,7 @@ casemodify(char *str, int how) #endif while (*str) { int c; + int mod = 0; if (*str == Meta) { c = str[1] ^ 32; str += 2; @@ -2234,13 +2260,17 @@ casemodify(char *str, int how) c = *str++; switch (how) { case CASMOD_LOWER: - if (isupper(c)) + if (isupper(c)) { c = tolower(c); + mod = 1; + } break; case CASMOD_UPPER: - if (islower(c)) + if (islower(c)) { c = toupper(c); + mod = 1; + } break; case CASMOD_CAPS: @@ -2248,14 +2278,18 @@ casemodify(char *str, int how) if (!ialnum(c)) nextupper = 1; else if (nextupper) { - if (islower(c)) + if (islower(c)) { c = toupper(c); + mod = 1; + } nextupper = 0; - } else if (isupper(c)) + } else if (isupper(c)) { c = tolower(c); + mod = 1; + } break; } - if (imeta(c)) { + if (mod && imeta(c)) { *ptr2++ = Meta; *ptr2++ = c ^ 32; } else @@ -2575,11 +2609,13 @@ resizehistents(void) } static int -readhistline(int start, char **bufp, int *bufsiz, FILE *in) +readhistline(int start, char **bufp, int *bufsiz, FILE *in, int *readbytes) { char *buf = *bufp; if (fgets(buf + start, *bufsiz - start, in)) { - int len = start + strlen(buf + start); + int len = strlen(buf + start); + *readbytes += len; + len += start; if (len == start) return -1; if (buf[len - 1] != '\n') { @@ -2588,16 +2624,23 @@ readhistline(int start, char **bufp, int *bufsiz, FILE *in) return -1; *bufp = zrealloc(buf, 2 * (*bufsiz)); *bufsiz = 2 * (*bufsiz); - return readhistline(len, bufp, bufsiz, in); + return readhistline(len, bufp, bufsiz, in, readbytes); } } else { + int spc; buf[len - 1] = '\0'; if (len > 1 && buf[len - 2] == '\\') { buf[--len - 1] = '\n'; if (!feof(in)) - return readhistline(len, bufp, bufsiz, in); + return readhistline(len, bufp, bufsiz, in, readbytes); } + + spc = len - 2; + while (spc >= 0 && buf[spc] == ' ') + spc--; + if (spc != len - 2 && buf[spc] == '\\') + buf[--len - 1] = '\0'; } return len; } @@ -2616,7 +2659,7 @@ readhistfile(char *fn, int err, int readflags) short *words; struct stat sb; int nwordpos, nwords, bufsiz; - int searching, newflags, l, ret, uselex; + int searching, newflags, l, ret, uselex, readbytes; if (!fn && !(fn = getsparam("HISTFILE"))) return; @@ -2648,7 +2691,7 @@ readhistfile(char *fn, int err, int readflags) pushheap(); if (readflags & HFILE_FAST && lasthist.text) { if (lasthist.fpos < lasthist.fsiz) { - fseek(in, lasthist.fpos, 0); + fseek(in, lasthist.fpos, SEEK_SET); searching = 1; } else { @@ -2658,13 +2701,15 @@ readhistfile(char *fn, int err, int readflags) } else searching = 0; + fpos = ftell(in); + readbytes = 0; newflags = HIST_OLD | HIST_READ; if (readflags & HFILE_FAST) newflags |= HIST_FOREIGN; if (readflags & HFILE_SKIPOLD || (hist_ignore_all_dups && newflags & hist_skip_flags)) newflags |= HIST_MAKEUNIQUE; - while (fpos = ftell(in), (l = readhistline(0, &buf, &bufsiz, in))) { + while (fpos += readbytes, readbytes = 0, (l = readhistline(0, &buf, &bufsiz, in, &readbytes))) { char *pt; int remeta = 0; @@ -2723,7 +2768,7 @@ readhistfile(char *fn, int err, int readflags) && histstrcmp(pt, lasthist.text) == 0) searching = 0; else { - fseek(in, 0, 0); + fseek(in, 0, SEEK_SET); histfile_linect = 0; searching = -1; } @@ -2959,7 +3004,7 @@ savehistfile(char *fn, int err, int writeflags) ret = 0; for (; he && he->histnum <= xcurhist; he = down_histent(he)) { - int count_backslashes = 0; + int end_backslashes = 0; if ((writeflags & HFILE_SKIPDUPS && he->node.flags & HIST_DUP) || (writeflags & HFILE_SKIPFOREIGN && he->node.flags & HIST_FOREIGN) @@ -2992,18 +3037,14 @@ savehistfile(char *fn, int err, int writeflags) if (*t == '\n') if ((ret = fputc('\\', out)) < 0) break; - if (*t == '\\') - count_backslashes++; - else - count_backslashes = 0; + end_backslashes = (*t == '\\' || (end_backslashes && *t == ' ')); if ((ret = fputc(*t, out)) < 0) break; } if (ret < 0) break; - if (count_backslashes && (count_backslashes % 2 == 0)) - if ((ret = fputc(' ', out)) < 0) - break; + if (end_backslashes) + ret = fputc(' ', out); if (ret < 0 || (ret = fputc('\n', out)) < 0) break; } diff --git a/Src/init.c b/Src/init.c index 99ccc16cf..871d46b12 100644 --- a/Src/init.c +++ b/Src/init.c @@ -248,7 +248,8 @@ static int restricted; /**/ static void -parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr) +parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr, + int *needkeymap) { char **x; LinkList paramlist; @@ -265,7 +266,7 @@ parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr) * matched by code at the end of the present function. */ - if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags)) + if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags, needkeymap)) exit(1); /* @@ -376,7 +377,7 @@ static void parseopts_setemulate(char *nam, int flags) /**/ mod_export int parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp, - LinkList optlist, int flags) + LinkList optlist, int flags, int *needkeymap) { int optionbreak = 0; int action, optno; @@ -482,8 +483,14 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp, return 1; } else if (optno == RESTRICTED && toplevel) { restricted = action; - } else if ((optno == EMACSMODE || optno == VIMODE) && !toplevel) { - WARN_OPTION("can't change option: %s", *argv); + } else if ((optno == EMACSMODE || optno == VIMODE) + && (!toplevel || needkeymap)){ + if (!toplevel) { + WARN_OPTION("can't change option: %s", *argv); + } else { + /* Need to wait for modules to be loadable */ + *needkeymap = optno; + } } else { if (dosetopt(optno, action, toplevel, new_opts) && !toplevel) { @@ -1035,7 +1042,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #endif /* FPATH_NEEDS_INIT */ mailpath = mkarray(NULL); - watch = mkarray(NULL); psvar = mkarray(NULL); module_path = mkarray(ztrdup(MODULE_DIR)); modulestab = newmoduletable(17, "modules"); @@ -1705,7 +1711,7 @@ zsh_main(UNUSED(int argc), char **argv) { char **t, *runscript = NULL, *zsh_name; char *cmd; /* argument to -c */ - int t0; + int t0, needkeymap = 0; #ifdef USE_LOCALE setlocale(LC_ALL, ""); #endif @@ -1751,7 +1757,7 @@ zsh_main(UNUSED(int argc), char **argv) createoptiontable(); /* sets emulation, LOGINSHELL, PRIVILEGED, ZLE, INTERACTIVE, * SHINSTDIN and SINGLECOMMAND */ - parseargs(zsh_name, argv, &runscript, &cmd); + parseargs(zsh_name, argv, &runscript, &cmd, &needkeymap); SHTTY = -1; init_io(cmd); @@ -1760,6 +1766,15 @@ zsh_main(UNUSED(int argc), char **argv) init_signals(); init_bltinmods(); init_builtins(); + + if (needkeymap) + { + /* Saved for after module system initialisation */ + zleentry(ZLE_CMD_SET_KEYMAP, needkeymap); + opts[needkeymap] = 1; + opts[needkeymap == EMACSMODE ? VIMODE : EMACSMODE] = 0; + } + run_init_scripts(); setupshin(runscript); init_misc(cmd, zsh_name); diff --git a/Src/input.c b/Src/input.c index 18228b37d..c59232681 100644 --- a/Src/input.c +++ b/Src/input.c @@ -223,13 +223,20 @@ shingetchar(void) return STOUC(*shinbufptr++); shinbufreset(); - do { - errno = 0; - nread = read(SHIN, shinbuffer, SHINBUFSIZE); - } while (nread < 0 && errno == EINTR); - if (nread <= 0) - return -1; - shinbufendptr = shinbuffer + nread; + for (;;) { + errno = 0; + nread = read(SHIN, shinbufendptr, 1); + if (nread > 0) { + /* Use line buffering (POSIX requirement) */ + if (*shinbufendptr++ == '\n') + break; + if (shinbufendptr == shinbuffer + SHINBUFSIZE) + break; + } else if (nread == 0 || errno != EINTR) + break; + } + if (shinbufendptr == shinbuffer) + return -1; return STOUC(*shinbufptr++); } @@ -595,9 +602,9 @@ stuff(char *fn) zerr("can't open %s", fn); return 1; } - fseek(in, 0, 2); + fseek(in, 0, SEEK_END); len = ftell(in); - fseek(in, 0, 0); + fseek(in, 0, SEEK_SET); buf = (char *)zalloc(len + 1); if (!(fread(buf, len, 1, in))) { zerr("read error on %s", fn); diff --git a/Src/jobs.c b/Src/jobs.c index e7438251e..af0a1233d 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -98,10 +98,12 @@ mod_export int jobtabsize; mod_export int maxjob; /* If we have entered a subshell, the original shell's job table. */ -static struct job *oldjobtab; +/**/ +mod_export struct job *oldjobtab; /* The size of that. */ -static int oldmaxjob; +/**/ +mod_export int oldmaxjob; /* shell timings */ @@ -283,7 +285,8 @@ handle_sub(int job, int fg) if ((cp = ((WIFEXITED(jn->procs->status) || WIFSIGNALED(jn->procs->status)) && - killpg(jn->gleader, 0) == -1))) { + (killpg(jn->gleader, 0) == -1 && + errno == ESRCH)))) { Process p; for (p = jn->procs; p->next; p = p->next); jn->gleader = p->pid; @@ -441,7 +444,7 @@ update_job(Job jn) Process pn; int job; int val = 0, status = 0; - int somestopped = 0, inforeground = 0; + int somestopped = 0, inforeground = 0, signalled = 0; for (pn = jn->auxprocs; pn; pn = pn->next) { #ifdef WIFCONTINUED @@ -463,12 +466,15 @@ update_job(Job jn) return; /* so no need to update job table entry */ if (WIFSTOPPED(pn->status)) /* some processes are stopped */ somestopped = 1; /* so job is not done, but entry needs updating */ - if (!pn->next) /* last job in pipeline determines exit status */ + if (!pn->next) { + /* last job in pipeline determines exit status */ val = (WIFSIGNALED(pn->status) ? 0200 | WTERMSIG(pn->status) : (WIFSTOPPED(pn->status) ? 0200 | WEXITSTATUS(pn->status) : WEXITSTATUS(pn->status))); + signalled = WIFSIGNALED(pn->status); + } if (pn->pid == jn->gleader) /* if this process is process group leader */ status = pn->status; } @@ -541,9 +547,13 @@ update_job(Job jn) /* is this job in the foreground of an interactive shell? */ if (mypgrp != pgrp && inforeground && - (jn->gleader == pgrp || (pgrp > 1 && kill(-pgrp, 0) == -1))) { + (jn->gleader == pgrp || + (pgrp > 1 && + (kill(-pgrp, 0) == -1 && errno == ESRCH)))) { if (list_pipe) { - if (somestopped || (pgrp > 1 && kill(-pgrp, 0) == -1)) { + if (somestopped || (pgrp > 1 && + kill(-pgrp, 0) == -1 && + errno == ESRCH)) { attachtty(mypgrp); /* check window size and adjust if necessary */ adjustwinsize(0); @@ -559,7 +569,7 @@ update_job(Job jn) } /* If we have `foo|while true; (( x++ )); done', and hit * ^C, we have to stop the loop, too. */ - if ((val & 0200) && inforeground == 1 && + if (signalled && inforeground == 1 && ((val & ~0200) == SIGINT || (val & ~0200) == SIGQUIT)) { if (!errbrk_saved) { errbrk_saved = 1; @@ -576,7 +586,7 @@ update_job(Job jn) adjustwinsize(0); } } - } else if (list_pipe && (val & 0200) && inforeground == 1 && + } else if (list_pipe && signalled && inforeground == 1 && ((val & ~0200) == SIGINT || (val & ~0200) == SIGQUIT)) { if (!errbrk_saved) { errbrk_saved = 1; @@ -1708,8 +1718,15 @@ clearjobtab(int monitor) /* Don't report any job we're part of */ if (thisjob != -1 && thisjob < oldmaxjob) memset(oldjobtab+thisjob, 0, sizeof(struct job)); + + /* oldmaxjob is now the size of the table, but outside + * this function, it's used as a job number, which must + * be the largest index available in the table. + */ + --oldmaxjob; } + memset(jobtab, 0, jobtabsize * sizeof(struct job)); /* zero out table */ maxjob = 0; @@ -1723,6 +1740,18 @@ clearjobtab(int monitor) thisjob = initjob(); } +/* In a subshell, decide we want our own job table after all. */ + +/**/ +mod_export void +clearoldjobtab(void) +{ + if (oldjobtab) + free(oldjobtab); + oldjobtab = NULL; + oldmaxjob = 0; +} + static int initnewjob(int i) { jobtab[i].stat = STAT_INUSE; @@ -1854,13 +1883,14 @@ scanjobs(void) /* This simple function indicates whether or not s may represent * * a number. It returns true iff s consists purely of digits and * - * minuses. Note that minus may appear more than once, and the empty * - * string will produce a `true' response. */ + * minuses. Note that minus may appear more than once. */ /**/ static int isanum(char *s) { + if (*s == '\0') + return 0; while (*s == '-' || idigit(*s)) s++; return *s == '\0'; @@ -1885,6 +1915,26 @@ setcurjob(void) } } +/* Find the job table for reporting jobs */ + +/**/ +mod_export void +selectjobtab(Job *jtabp, int *jmaxp) +{ + if (oldjobtab) + { + /* In subshell --- use saved job table to report */ + *jtabp = oldjobtab; + *jmaxp = oldmaxjob; + } + else + { + /* Use main job table */ + *jtabp = jobtab; + *jmaxp = maxjob; + } +} + /* Convert a job specifier ("%%", "%1", "%foo", "%?bar?", etc.) * * to a job number. */ @@ -1895,13 +1945,7 @@ getjob(const char *s, const char *prog) int jobnum, returnval, mymaxjob; Job myjobtab; - if (oldjobtab) { - myjobtab = oldjobtab; - mymaxjob = oldmaxjob; - } else { - myjobtab= jobtab; - mymaxjob = maxjob; - } + selectjobtab(&myjobtab, &mymaxjob); /* if there is no %, treat as a name */ if (*s != '%') @@ -2261,6 +2305,13 @@ bin_fg(char *name, char **argv, Options ops, int func) memcpy(hackzero, *argv, len); memset(hackzero + len, 0, hackspace - len); #endif + +#ifdef HAVE_PRCTL + /* try to change /proc/$$/comm which will * + * be used when checking with "ps -e" */ +#include <sys/prctl.h> + prctl(PR_SET_NAME, *argv); +#endif unqueue_signals(); return 0; } @@ -2417,6 +2468,7 @@ bin_fg(char *name, char **argv, Options ops, int func) case BIN_BG: case BIN_WAIT: if (func == BIN_BG) { + clearoldjobtab(); jobtab[job].stat |= STAT_NOSTTY; jobtab[job].stat &= ~STAT_CURSH; } @@ -2469,7 +2521,8 @@ bin_fg(char *name, char **argv, Options ops, int func) if ((jobtab[job].stat & STAT_SUPERJOB) && ((!jobtab[job].procs->next || (jobtab[job].stat & STAT_SUBLEADER) || - killpg(jobtab[job].gleader, 0) == -1)) && + (killpg(jobtab[job].gleader, 0) == -1 && + errno == ESRCH))) && jobtab[jobtab[job].other].gleader) attachtty(jobtab[jobtab[job].other].gleader); else @@ -270,7 +270,7 @@ zshlex(void) do { if (inrepeat_) ++inrepeat_; - if (inrepeat_ == 3 && isset(SHORTLOOPS)) + if (inrepeat_ == 3 && (isset(SHORTLOOPS) || isset(SHORTREPEAT))) incmdpos = 1; tok = gettok(); } while (tok != ENDINPUT && exalias()); @@ -541,6 +541,17 @@ cmd_or_math_sub(void) { int c = hgetc(), ret; + if (c == '\\') { + c = hgetc(); + if (c != '\n') { + hungetc(c); + hungetc('\\'); + lexstop = 0; + return skipcomm() ? CMD_OR_MATH_ERR : CMD_OR_MATH_CMD; + } + c = hgetc(); + } + if (c == '(') { int lexpos = (int)(lexbuf.ptr - tokstr); add(Inpar); @@ -998,6 +1009,16 @@ gettokstr(int c, int sub) break; case LX2_STRING: e = hgetc(); + if (e == '\\') { + e = hgetc(); + if (e != '\n') { + hungetc(e); + hungetc('\\'); + lexstop = 0; + break; + } + e = hgetc(); + } if (e == '[') { cmdpush(CS_MATHSUBST); add(String); @@ -1868,6 +1889,7 @@ exalias(void) hwend(); if (interact && isset(SHINSTDIN) && !strin && incasepat <= 0 && tok == STRING && !nocorrect && !(inbufflags & INP_ALIAS) && + !hist_is_in_word() && (isset(CORRECTALL) || (isset(CORRECT) && incmdpos))) spckword(&tokstr, 1, incmdpos, 1); diff --git a/Src/linklist.c b/Src/linklist.c index 85d9bb367..f64685d9e 100644 --- a/Src/linklist.c +++ b/Src/linklist.c @@ -438,22 +438,27 @@ hlinklist2array(LinkList list, int copy) /* * Convert a linked list whose data elements are strings to - * an array. The result is a permanently allocated, freearrayable - * array. + * a permanently-allocated array. The elements of the array are the same + * elements as the linked list data if copy is 0, else they are duplicated + * into permanent memory so the result is a permanently allocated, + * freearrayable array that's a deep copy of the linked list. */ /**/ mod_export char ** -zlinklist2array(LinkList list) +zlinklist2array(LinkList list, int copy) { int l = countlinknodes(list); char **ret = (char **) zalloc((l + 1) * sizeof(char *)), **p; LinkNode n; for (n = firstnode(list), p = ret; n; incnode(n), p++) { - *p = ztrdup((char *) getdata(n)); + *p = (char *) getdata(n); + if (copy) + *p = ztrdup(*p); } *p = NULL; return ret; } + diff --git a/Src/loop.c b/Src/loop.c index 01abc6cc9..db5b3e097 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -43,7 +43,7 @@ mod_export int contflag; /* # of break levels */ /**/ -mod_export int breaks; +mod_export volatile int breaks; /**/ int @@ -497,13 +497,15 @@ execrepeat(Estate state, UNUSED(int do_exec)) end = state->pc + WC_REPEAT_SKIP(code); - lastval = 0; tmp = ecgetstr(state, EC_DUPTOK, &htok); - if (htok) + if (htok) { singsub(&tmp); + untokenize(tmp); + } count = mathevali(tmp); if (errflag) return 1; + lastval = 0; /* used when the repeat count is zero */ pushheap(); cmdpush(CS_REPEAT); loops++; @@ -581,7 +583,7 @@ execif(Estate state, int do_exec) cmdpop(); } else { noerrexit = olderrexit; - if (!retflag) + if (!retflag && !errflag) lastval = 0; } state->pc = end; @@ -742,7 +744,7 @@ exectry(Estate state, int do_exec) /* The :try clause */ ++try_tryflag; - execlist(state, 1, do_exec); + execlist(state, 1, 0); --try_tryflag; /* Don't record errflag here, may be reset. However, */ diff --git a/Src/makepro.awk b/Src/makepro.awk index 226d3f96b..f69660531 100644 --- a/Src/makepro.awk +++ b/Src/makepro.awk @@ -79,7 +79,7 @@ BEGIN { break } sub(/^ */, "", line) - match(line, /^((const|enum|mod_export|static|struct|union) +)*([_0-9A-Za-z]+ +|((char|double|float|int|long|short|unsigned|void) +)+)((const|static) +)*/) + match(line, /^((const|enum|mod_export|static|struct|union|volatile) +)*([_0-9A-Za-z]+ +|((char|double|float|int|long|short|unsigned|void) +)+)((const|static) +)*/) dtype = substr(line, 1, RLENGTH) sub(/ *$/, "", dtype) if(" " dtype " " ~ / static /) diff --git a/Src/math.c b/Src/math.c index 905b910ec..777ad9c31 100644 --- a/Src/math.c +++ b/Src/math.c @@ -162,7 +162,7 @@ static int unary = 1; #define TOKCOUNT 53 /* - * Opeator recedences: in reverse order, i.e. lower number, high precedence. + * Operator precedences: in reverse order, i.e. lower number, high precedence. * These are the C precedences. * * 0 Non-operators: NUM (numeric constant), ID (identifier), @@ -219,7 +219,7 @@ static int c_prec[TOKCOUNT] = }; /* - * Opeator recedences: in reverse order, i.e. lower number, high precedence. + * Operator precedences: in reverse order, i.e. lower number, high precedence. * These are the default zsh precedences. * * 0 Non-operators: NUM (numeric constant), ID (identifier), @@ -831,6 +831,8 @@ zzlex(void) case ' ': /* Fall through! */ case '\t': case '\n': + case '"': /* POSIX says ignore these */ + case Dnull: break; default: if (idigit(*--ptr) || *ptr == '.') @@ -838,13 +840,18 @@ zzlex(void) if (*ptr == '#') { if (*++ptr == '\\' || *ptr == '#') { int v; + char *optr = ptr; ptr++; if (!*ptr) { zerr("bad math expression: character missing after ##"); return EOI; } - ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v); + if(!(ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v))) { + zerr("bad math expression: bad character after ##"); + ptr = optr; + return EOI; + } yyval.u.l = v; return NUM; } @@ -856,14 +863,18 @@ zzlex(void) p = ptr; ptr = ie; - if (ie - p == 3) { - if (strncasecmp(p, "NaN", 3) == 0) { + if (ie - p == 3 && !EMULATION(EMULATE_SH)) { + if ((p[0] == 'N' || p[0] == 'n') && + (p[1] == 'A' || p[1] == 'a') && + (p[2] == 'N' || p[2] == 'n')) { yyval.type = MN_FLOAT; yyval.u.d = 0.0; yyval.u.d /= yyval.u.d; return NUM; } - else if (strncasecmp(p, "Inf", 3) == 0) { + else if ((p[0] == 'I' || p[0] == 'i') && + (p[1] == 'N' || p[1] == 'n') && + (p[2] == 'F' || p[2] == 'f')) { yyval.type = MN_FLOAT; yyval.u.d = 0.0; yyval.u.d = 1.0 / yyval.u.d; @@ -1072,19 +1072,6 @@ zrealloc(void *ptr, size_t size) # endif #endif -#if defined(_BSD) && !defined(STDC_HEADERS) -# define FREE_RET_T int -# define FREE_ARG_T char * -# define FREE_DO_RET -# define MALLOC_RET_T char * -# define MALLOC_ARG_T size_t -#else -# define FREE_RET_T void -# define FREE_ARG_T void * -# define MALLOC_RET_T void * -# define MALLOC_ARG_T size_t -#endif - /* structure for building free list in blocks holding small blocks */ struct m_shdr { @@ -1198,8 +1185,8 @@ static struct m_hdr *m_l; #endif /* ZSH_MEM_DEBUG */ -MALLOC_RET_T -malloc(MALLOC_ARG_T size) +void * +malloc(size_t size) { struct m_hdr *m, *mp, *mt; long n, s, os = 0; @@ -1226,7 +1213,7 @@ malloc(MALLOC_ARG_T size) #if 1 size = 1; #else - return (MALLOC_RET_T) m_high; + return (void *) m_high; #endif queue_signals(); /* just queue signals rather than handling them */ @@ -1284,7 +1271,7 @@ malloc(MALLOC_ARG_T size) #endif unqueue_signals(); - return (MALLOC_RET_T) sh; + return (void *) sh; } /* we still want a small block but there were no block with a free small block of the requested size; so we use the real allocation @@ -1426,14 +1413,14 @@ malloc(MALLOC_ARG_T size) #endif unqueue_signals(); - return (MALLOC_RET_T) (((char *)m) + sizeof(struct m_hdr)); + return (void *) (((char *)m) + sizeof(struct m_hdr)); } #ifdef ZSH_MEM_DEBUG m_m[m->len < (1024 * M_ISIZE) ? (m->len / M_ISIZE) : 1024]++; #endif unqueue_signals(); - return (MALLOC_RET_T) & m->next; + return (void *) & m->next; } /* this is an internal free(); the second argument may, but need not hold @@ -1640,14 +1627,10 @@ zfree(void *p, int sz) unqueue_signals(); } -FREE_RET_T -free(FREE_ARG_T p) +void +free(void *p) { zfree(p, 0); /* 0 means: size is unknown */ - -#ifdef FREE_DO_RET - return 0; -#endif } /* this one is for strings (and only strings, real strings, real C strings, @@ -1661,8 +1644,8 @@ zsfree(char *p) zfree(p, strlen(p) + 1); } -MALLOC_RET_T -realloc(MALLOC_RET_T p, MALLOC_ARG_T size) +void * +realloc(void *p, size_t size) { struct m_hdr *m = (struct m_hdr *)(((char *)p) - M_ISIZE), *mt; char *r; @@ -1673,12 +1656,12 @@ realloc(MALLOC_RET_T p, MALLOC_ARG_T size) queue_signals(); r = malloc(size); unqueue_signals(); - return (MALLOC_RET_T) r; + return (void *) r; } /* and some systems even do this... */ if (!p || !size) - return (MALLOC_RET_T) p; + return p; queue_signals(); /* just queue signals caught rather than handling them */ @@ -1707,17 +1690,17 @@ realloc(MALLOC_RET_T p, MALLOC_ARG_T size) free(p); unqueue_signals(); - return (MALLOC_RET_T) r; + return (void *) r; } -MALLOC_RET_T -calloc(MALLOC_ARG_T n, MALLOC_ARG_T size) +void * +calloc(size_t n, size_t size) { long l; char *r; if (!(l = n * size)) - return (MALLOC_RET_T) m_high; + return (void *) m_high; /* * use realloc() (with a NULL `p` argument it behaves exactly the same @@ -1729,7 +1712,7 @@ calloc(MALLOC_ARG_T n, MALLOC_ARG_T size) memset(r, 0, l); - return (MALLOC_RET_T) r; + return (void *) r; } #ifdef ZSH_MEM_DEBUG diff --git a/Src/module.c b/Src/module.c index f41b82f25..bab4d8d73 100644 --- a/Src/module.c +++ b/Src/module.c @@ -1039,7 +1039,7 @@ checkaddparam(const char *nam, int opt_i) * non-autoloadable parameter already there. This * is consistent with the way add_auto* functions work. */ - if (!opt_i || !pm->level) { + if (!opt_i || pm->level) { zwarn("Can't add module parameter `%s': %s", nam, pm->level ? "local parameter exists" : diff --git a/Src/openssh_bsd_setres_id.c b/Src/openssh_bsd_setres_id.c index 65e91a40c..217a6d074 100644 --- a/Src/openssh_bsd_setres_id.c +++ b/Src/openssh_bsd_setres_id.c @@ -46,6 +46,8 @@ * */ +#include "zsh.mdh" +#include "openssh_bsd_setres_id.pro" #include <sys/types.h> @@ -53,8 +55,6 @@ #include <unistd.h> #include <string.h> -#include "zsh.mdh" - #if defined(ZSH_IMPLEMENT_SETRESGID) || defined(BROKEN_SETRESGID) int setresgid(gid_t rgid, gid_t egid, gid_t sgid) diff --git a/Src/options.c b/Src/options.c index 08ba71917..a1fe918fc 100644 --- a/Src/options.c +++ b/Src/options.c @@ -105,6 +105,7 @@ static struct optname optns[] = { {{NULL, "bsdecho", OPT_EMULATE|OPT_SH}, BSDECHO}, {{NULL, "caseglob", OPT_ALL}, CASEGLOB}, {{NULL, "casematch", OPT_ALL}, CASEMATCH}, +{{NULL, "casepaths", 0}, CASEPATHS}, {{NULL, "cbases", 0}, CBASES}, {{NULL, "cprecedences", OPT_EMULATE|OPT_NONZSH}, CPRECEDENCES}, {{NULL, "cdablevars", OPT_EMULATE}, CDABLEVARS}, @@ -114,6 +115,7 @@ static struct optname optns[] = { {{NULL, "checkjobs", OPT_EMULATE|OPT_ZSH}, CHECKJOBS}, {{NULL, "checkrunningjobs", OPT_EMULATE|OPT_ZSH}, CHECKRUNNINGJOBS}, {{NULL, "clobber", OPT_EMULATE|OPT_ALL}, CLOBBER}, +{{NULL, "clobberempty", 0}, CLOBBEREMPTY}, {{NULL, "combiningchars", 0}, COMBININGCHARS}, {{NULL, "completealiases", 0}, COMPLETEALIASES}, {{NULL, "completeinword", 0}, COMPLETEINWORD}, @@ -248,6 +250,7 @@ static struct optname optns[] = { {{NULL, "shnullcmd", OPT_EMULATE|OPT_BOURNE}, SHNULLCMD}, {{NULL, "shoptionletters", OPT_EMULATE|OPT_BOURNE}, SHOPTIONLETTERS}, {{NULL, "shortloops", OPT_EMULATE|OPT_NONBOURNE},SHORTLOOPS}, +{{NULL, "shortrepeat", OPT_EMULATE}, SHORTREPEAT}, {{NULL, "shwordsplit", OPT_EMULATE|OPT_BOURNE}, SHWORDSPLIT}, {{NULL, "singlecommand", OPT_SPECIAL}, SINGLECOMMAND}, {{NULL, "singlelinezle", OPT_KSH}, SINGLELINEZLE}, @@ -256,6 +259,7 @@ static struct optname optns[] = { {{NULL, "transientrprompt", 0}, TRANSIENTRPROMPT}, {{NULL, "trapsasync", 0}, TRAPSASYNC}, {{NULL, "typesetsilent", OPT_EMULATE|OPT_BOURNE}, TYPESETSILENT}, +{{NULL, "typesettounset", OPT_EMULATE|OPT_BOURNE}, TYPESETTOUNSET}, {{NULL, "unset", OPT_EMULATE|OPT_BSHELL}, UNSET}, {{NULL, "verbose", 0}, VERBOSE}, {{NULL, "vi", 0}, VIMODE}, @@ -807,7 +811,7 @@ dosetopt(int optno, int value, int force, char *new_opts) return -1; } -# ifdef HAVE_INITGROUPS +# ifdef USE_INITGROUPS /* Set the supplementary groups list. * * Note that on macOS, FreeBSD, and possibly some other platforms, diff --git a/Src/params.c b/Src/params.c index 863b32600..970a207e4 100644 --- a/Src/params.c +++ b/Src/params.c @@ -63,7 +63,6 @@ char **pparams, /* $argv */ **mailpath, /* $mailpath */ **manpath, /* $manpath */ **psvar, /* $psvar */ - **watch, /* $watch */ **zsh_eval_context; /* $zsh_eval_context */ /**/ mod_export @@ -98,8 +97,10 @@ char *ifs, /* $IFS */ *pwd; /* $PWD */ /**/ -mod_export -zlong lastval, /* $? */ +mod_export volatile zlong + lastval; /* $? */ +/**/ +mod_export zlong mypid, /* $$ */ lastpid, /* $! */ zterm_columns, /* $COLUMNS */ @@ -192,6 +193,10 @@ mod_export const struct gsu_hash stdhash_gsu = mod_export const struct gsu_hash nullsethash_gsu = { hashgetfn, nullsethashfn, nullunsetfn }; +/**/ +mod_export const struct gsu_scalar colonarr_gsu = +{ colonarrgetfn, colonarrsetfn, stdunsetfn }; + /* Non standard methods (not exported) */ static const struct gsu_integer pound_gsu = @@ -257,9 +262,6 @@ static const struct gsu_integer varint_readonly_gsu = static const struct gsu_integer zlevar_gsu = { intvargetfn, zlevarsetfn, stdunsetfn }; -static const struct gsu_scalar colonarr_gsu = -{ colonarrgetfn, colonarrsetfn, stdunsetfn }; - static const struct gsu_integer argc_gsu = { poundgetfn, nullintsetfn, stdunsetfn }; static const struct gsu_array pipestatus_gsu = @@ -396,7 +398,6 @@ IPDEF8("CDPATH", &cdpath, "cdpath", PM_TIED), IPDEF8("FIGNORE", &fignore, "fignore", PM_TIED), IPDEF8("FPATH", &fpath, "fpath", PM_TIED), IPDEF8("MAILPATH", &mailpath, "mailpath", PM_TIED), -IPDEF8("WATCH", &watch, "watch", PM_TIED), IPDEF8("PATH", &path, "path", PM_RESTRICTED|PM_TIED), IPDEF8("PSVAR", &psvar, "psvar", PM_TIED), IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY_SPECIAL|PM_TIED), @@ -428,7 +429,6 @@ IPDEF9("fpath", &fpath, "FPATH", PM_TIED), IPDEF9("mailpath", &mailpath, "MAILPATH", PM_TIED), IPDEF9("manpath", &manpath, "MANPATH", PM_TIED), IPDEF9("psvar", &psvar, "PSVAR", PM_TIED), -IPDEF9("watch", &watch, "WATCH", PM_TIED), IPDEF9("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_TIED|PM_READONLY_SPECIAL), @@ -451,7 +451,6 @@ IPDEF8("CDPATH", &cdpath, NULL, 0), IPDEF8("FIGNORE", &fignore, NULL, 0), IPDEF8("FPATH", &fpath, NULL, 0), IPDEF8("MAILPATH", &mailpath, NULL, 0), -IPDEF8("WATCH", &watch, NULL, 0), IPDEF8("PATH", &path, NULL, PM_RESTRICTED), IPDEF8("PSVAR", &psvar, NULL, 0), IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY_SPECIAL), @@ -759,7 +758,7 @@ split_env_string(char *env, char **name, char **value) */ static int dontimport(int flags) { - /* If explicitly marked as don't export */ + /* If explicitly marked as don't import */ if (flags & PM_DONTIMPORT) return 1; /* If value already exported */ @@ -819,7 +818,6 @@ createparamtable(void) * given them in the environment. */ opts[ALLEXPORT] = 0; setiparam("MAILCHECK", 60); - setiparam("LOGCHECK", 60); setiparam("KEYTIMEOUT", 40); setiparam("LISTMAX", 100); /* @@ -834,16 +832,18 @@ createparamtable(void) */ setsparam("TMPPREFIX", ztrdup_metafy(DEFAULT_TMPPREFIX)); setsparam("TIMEFMT", ztrdup_metafy(DEFAULT_TIMEFMT)); - setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt)); hostnam = (char *)zalloc(256); gethostname(hostnam, 256); setsparam("HOST", ztrdup_metafy(hostnam)); zfree(hostnam, 256); - setsparam("LOGNAME", - ztrdup_metafy((str = getlogin()) && *str ? - str : cached_username)); + setsparam("LOGNAME", ztrdup_metafy( +#ifndef DISABLE_DYNAMIC_NSS + (str = getlogin()) && *str ? str : +#endif + cached_username + )); #if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV) /* Copy the environment variables we are inheriting to dynamic * @@ -1008,6 +1008,11 @@ createparam(char *name, int flags) (oldpm->node.flags & PM_SPECIAL) || /* POSIXBUILTINS horror: we need to retain 'export' flags */ (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) { + if (oldpm->node.flags & PM_RO_BY_DESIGN) { + zerr("%s: can't change parameter attribute", + name); + return NULL; + } oldpm->node.flags &= ~PM_UNSET; if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) { Param altpm = @@ -2093,7 +2098,8 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) if (sav) *s = sav; *pptr = s; - if (!pm || (pm->node.flags & PM_UNSET)) + if (!pm || ((pm->node.flags & PM_UNSET) && + !(pm->node.flags & PM_DECLARED))) return NULL; if (v) memset(v, 0, sizeof(*v)); @@ -3055,6 +3061,7 @@ assignsparam(char *s, char *val, int flags) * Don't warn about anything. */ flags &= ~ASSPM_WARN; + v->pm->node.flags &= ~PM_DEFAULTED; } *ss = '['; v = NULL; @@ -3080,6 +3087,7 @@ assignsparam(char *s, char *val, int flags) } if (flags & ASSPM_WARN) check_warn_pm(v->pm, "scalar", created, 1); + v->pm->node.flags &= ~PM_DEFAULTED; if (flags & ASSPM_AUGMENT) { if (v->start == 0 && v->end == -1) { switch (PM_TYPE(v->pm->node.flags)) { @@ -3232,6 +3240,7 @@ assignaparam(char *s, char **val, int flags) if (flags & ASSPM_WARN) check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars); + v->pm->node.flags &= ~PM_DEFAULTED; /* * At this point, we may have array entries consisting of @@ -3444,6 +3453,7 @@ sethparam(char *s, char **val) return NULL; } check_warn_pm(v->pm, "associative array", checkcreate, 1); + v->pm->node.flags &= ~PM_DEFAULTED; setarrvalue(v, val); unqueue_signals(); return v->pm; @@ -3515,6 +3525,7 @@ assignnparam(char *s, mnumber val, int flags) if (flags & ASSPM_WARN) check_warn_pm(v->pm, "numeric", 0, 1); } + v->pm->node.flags &= ~PM_DEFAULTED; setnumvalue(v, val); unqueue_signals(); return v->pm; @@ -3619,6 +3630,7 @@ unsetparam_pm(Param pm, int altflag, int exp) else altremove = NULL; + pm->node.flags &= ~PM_DECLARED; /* like ksh, not like bash */ if (!(pm->node.flags & PM_UNSET)) pm->gsu.s->unsetfn(pm, exp); if (pm->env) @@ -3652,6 +3664,8 @@ unsetparam_pm(Param pm, int altflag, int exp) } zsfree(altremove); + if (!(pm->node.flags & PM_SPECIAL)) + pm->gsu.s = &stdscalar_gsu; } /* @@ -4074,7 +4088,7 @@ arrvarsetfn(Param pm, char **x) } /**/ -char * +mod_export char * colonarrgetfn(Param pm) { char ***dptr = (char ***)pm->u.data; @@ -4082,7 +4096,7 @@ colonarrgetfn(Param pm) } /**/ -void +mod_export void colonarrsetfn(Param pm, char *x) { char ***dptr = (char ***)pm->u.data; @@ -4116,6 +4130,11 @@ tiedarrsetfn(Param pm, char *x) if (*dptr->arrptr) freearray(*dptr->arrptr); + else if (pm->ename) { + Param altpm = (Param) paramtab->getnode(paramtab, pm->ename); + if (altpm) + altpm->node.flags &= ~PM_DEFAULTED; + } if (x) { char sepbuf[3]; if (imeta(dptr->joinchar)) @@ -4414,7 +4433,7 @@ usernamegetfn(UNUSED(Param pm)) void usernamesetfn(UNUSED(Param pm), char *x) { -#if defined(HAVE_SETUID) && defined(HAVE_GETPWNAM) +#if defined(HAVE_SETUID) && defined(USE_GETPWNAM) struct passwd *pswd; if (x && (pswd = getpwnam(x)) && (pswd->pw_uid != cached_uid)) { @@ -4431,7 +4450,7 @@ usernamesetfn(UNUSED(Param pm), char *x) cached_uid = pswd->pw_uid; } } -#endif /* HAVE_SETUID && HAVE_GETPWNAM */ +#endif /* HAVE_SETUID && USE_GETPWNAM */ zsfree(x); } @@ -5035,6 +5054,7 @@ arrfixenv(char *s, char **t) if (isset(ALLEXPORT)) pm->node.flags |= PM_EXPORTED; + pm->node.flags &= ~PM_DEFAULTED; /* * Do not "fix" parameters that were not exported @@ -5569,6 +5589,14 @@ startparamscope(void) locallevel++; } +#ifdef USE_LOCALE +/* + * Flag that one of the special LC_ functions or LANG changed on scope + * end + */ +static int lc_update_needed; +#endif /* USE_LOCALE */ + /* End a parameter scope: delete the parameters local to the scope. */ /**/ @@ -5579,7 +5607,28 @@ endparamscope(void) locallevel--; /* This pops anything from a higher locallevel */ saveandpophiststack(0, HFILE_USE_OPTIONS); +#ifdef USE_LOCALE + lc_update_needed = 0; +#endif scanhashtable(paramtab, 0, 0, 0, scanendscope, 0); +#ifdef USE_LOCALE + if (lc_update_needed) + { + /* Locale changed --- ensure it is restored. */ + char *val; + if ((val = getsparam_u("LC_ALL")) && *val) { + setlocale(LC_ALL, val); + } else { + struct localename *ln; + if ((val = getsparam_u("LANG")) && *val) + setlang(val); + for (ln = lc_names; ln->name; ln++) { + if ((val = getsparam_u(ln->name)) && *val) + setlocale(ln->category, val); + } + } + } +#endif /* USE_LOCALE */ unqueue_signals(); } @@ -5600,6 +5649,11 @@ scanendscope(HashNode hn, UNUSED(int flags)) */ Param tpm = pm->old; +#ifdef USE_LOCALE + if (!strncmp(pm->node.nam, "LC_", 3) || + !strcmp(pm->node.nam, "LANG")) + lc_update_needed = 1; +#endif if (!strcmp(pm->node.nam, "SECONDS")) { setsecondstype(pm, PM_TYPE(tpm->node.flags), PM_TYPE(pm->node.flags)); @@ -5805,8 +5859,9 @@ printparamnode(HashNode hn, int printflags) Param peer = NULL; if (p->node.flags & PM_UNSET) { - if (printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) && - p->node.flags & (PM_READONLY|PM_EXPORTED)) { + if ((printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) && + p->node.flags & (PM_READONLY|PM_EXPORTED)) || + (p->node.flags & PM_DEFAULTED) == PM_DEFAULTED) { /* * Special POSIX rules: show the parameter as readonly/exported * even though it's unset, but with no value. @@ -5833,8 +5888,12 @@ printparamnode(HashNode hn, int printflags) * don't. */ if (printflags & PRINT_POSIX_EXPORT) { + if (!(p->node.flags & PM_EXPORTED)) + return; printf("export "); } else if (printflags & PRINT_POSIX_READONLY) { + if (!(p->node.flags & PM_READONLY)) + return; printf("readonly "); } else if (locallevel && p->level >= locallevel) { printf("typeset "); /* printf("local "); */ diff --git a/Src/parse.c b/Src/parse.c index de1b27967..d612b7e17 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -102,6 +102,13 @@ struct heredocs *hdocs; * The parser now produces word code, reducing memory consumption compared * to the nested structs we had before. * + * Word codes are represented by the "wordcode" type. + * + * Each wordcode variable consists of a "code", in the least-significant bits + * of the value, and "data" in the other bits. The macros wc_code() and wc_data() + * access the "code" and "data" parts of a wordcode. The macros wc_bdata() and + * wc_bld() build wordcodes from code and data. + * * Word code layout: * * WC_END @@ -166,8 +173,13 @@ struct heredocs *hdocs; * - followed by offset to first string * - followed by length of string table * - followed by number of patterns for body + * - followed by an integer indicating tracing status * - followed by codes for body * - followed by strings for body + * - if number of names is 0, followed by: + * - the offset to the end of the funcdef + * - the number of arguments to the function + * - the arguments to the function * * WC_FOR * - data contains type (list, ...) and offset to after body @@ -253,14 +265,24 @@ struct heredocs *hdocs; * to avoid a lot of string parsing and some more string duplication. */ -/**/ -int eclen, ecused, ecnpats; -/**/ -Wordcode ecbuf; -/**/ -Eccstr ecstrs; -/**/ -int ecsoffs, ecssub, ecnfunc; +/* Number of wordcodes allocated. */ +static int eclen; +/* Number of wordcodes populated. */ +static int ecused; +/* Number of patterns... */ +static int ecnpats; + +static Wordcode ecbuf; + +static Eccstr ecstrs; + +static int ecsoffs, ecssub; + +/* + * ### The number of starts and ends of function definitions up to this point. + * Never decremented. + */ +static int ecnfunc; #define EC_INIT_SIZE 256 #define EC_DOUBLE_THRESHOLD 32768 @@ -364,7 +386,11 @@ ecispace(int p, int n) ecadjusthere(p, n); } -/* Add one wordcode. */ +/* + * Add one wordcode. + * + * Return the index of the added wordcode. + */ static int ecadd(wordcode c) @@ -403,6 +429,7 @@ ecstrcode(char *s) unsigned val = hasher(s); if ((l = strlen(s) + 1) && l <= 4) { + /* Short string. */ t = has_token(s); wordcode c = (t ? 3 : 2); switch (l) { @@ -413,11 +440,13 @@ ecstrcode(char *s) } return c; } else { + /* Long string. */ Eccstr p, *pp; long cmp; for (pp = &ecstrs; (p = *pp); ) { if (!(cmp = p->nfunc - ecnfunc) && !(cmp = (((long)p->hashval) - ((long)val))) && !(cmp = strcmp(p->str, s))) { + /* Re-use the existing string. */ return p->offs; } pp = (cmp < 0 ? &(p->left) : &(p->right)); @@ -494,7 +523,12 @@ init_parse(void) /* Build eprog. */ -/* careful: copy_ecstr is from arg1 to arg2, unlike memcpy */ +/* + * Copy the strings of s and all its descendants in the binary tree to the + * memory block p. + * + * careful: copy_ecstr is from arg1 to arg2, unlike memcpy + */ static void copy_ecstr(Eccstr s, char *p) @@ -1559,7 +1593,7 @@ par_repeat(int *cmplx) if (tok != ZEND) YYERRORV(oecused); zshlex(); - } else if (unset(SHORTLOOPS)) { + } else if (unset(SHORTLOOPS) && unset(SHORTREPEAT)) { YYERRORV(oecused); } else par_save_list1(cmplx); @@ -1636,6 +1670,7 @@ par_funcdef(int *cmplx) int oecused = ecused, num = 0, onp, p, c = 0; int so, oecssub = ecssub; zlong oldlineno = lineno; + int do_tracing = 0; lineno = 0; nocorrect = 1; @@ -1643,7 +1678,21 @@ par_funcdef(int *cmplx) zshlex(); p = ecadd(0); - ecadd(0); + ecadd(0); /* p + 1 */ + + /* Consume an initial (-T), (--), or (-T --). + * Anything else is a literal function name. + */ + if (tok == STRING && tokstr[0] == Dash) { + if (tokstr[1] == 'T' && !tokstr[2]) { + ++do_tracing; + zshlex(); + } + if (tok == STRING && tokstr[0] == Dash && + tokstr[1] == Dash && !tokstr[2]) { + zshlex(); + } + } while (tok == STRING) { if ((*tokstr == Inbrace || *tokstr == '{') && @@ -1655,9 +1704,10 @@ par_funcdef(int *cmplx) num++; zshlex(); } - ecadd(0); - ecadd(0); - ecadd(0); + ecadd(0); /* p + num + 2 */ + ecadd(0); /* p + num + 3 */ + ecadd(0); /* p + num + 4 */ + ecadd(0); /* p + num + 5 */ nocorrect = 0; incmdpos = 1; @@ -1695,18 +1745,20 @@ par_funcdef(int *cmplx) ecadd(WCB_END()); ecbuf[p + num + 2] = so - oecssub; - ecbuf[p + num + 3] = ecsoffs - so; - ecbuf[p + num + 4] = ecnpats; - ecbuf[p + 1] = num; + ecbuf[p + num + 3] = ecsoffs - so; /* "length of string table" */ + ecbuf[p + num + 4] = ecnpats; /* "number of patterns for body" */ + ecbuf[p + num + 5] = do_tracing; + ecbuf[p + 1] = num; /* "number of names" */ ecnpats = onp; ecssub = oecssub; ecnfunc++; - ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); /* "offset to after body" */ + /* If it's an anonymous function... */ if (num == 0) { - /* Unnamed function */ + /* ... look for arguments to it. */ int parg = ecadd(0); ecadd(0); while (tok == STRING) { @@ -2000,7 +2052,7 @@ par_simple(int *cmplx, int nr) /* Error if preceding assignments */ if (assignments || postassigns) YYERROR(oecused); - if (hasalias && !isset(ALIASFUNCDEF) && argc && + if (isset(EXECOPT) && hasalias && !isset(ALIASFUNCDEF) && argc && hasalias != input_hasalias()) { zwarn("defining function based on alias `%s'", hasalias); YYERROR(oecused); @@ -2019,6 +2071,7 @@ par_simple(int *cmplx, int nr) ecadd(0); ecadd(0); ecadd(0); + ecadd(0); ecnfunc++; ecssub = so = ecsoffs; @@ -2074,6 +2127,7 @@ par_simple(int *cmplx, int nr) ecbuf[p + argc + 2] = so - oecssub; ecbuf[p + argc + 3] = ecsoffs - so; ecbuf[p + argc + 4] = ecnpats; + ecbuf[p + argc + 5] = 0; ecnpats = onp; ecssub = oecssub; @@ -2081,8 +2135,9 @@ par_simple(int *cmplx, int nr) ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + /* If it's an anonymous function... */ if (argc == 0) { - /* Unnamed function */ + /* ... look for arguments to it. */ int parg = ecadd(0); ecadd(0); while (tok == STRING || IS_REDIROP(tok)) { @@ -2454,7 +2509,7 @@ par_cond_2(void) * In "test" compatibility mode, "! -a ..." and "! -o ..." * are treated as "[string] [and] ..." and "[string] [or] ...". */ - if (!(n_testargs > 1 && (check_cond(*testargs, "a") || + if (!(n_testargs > 2 && (check_cond(*testargs, "a") || check_cond(*testargs, "o")))) { condlex(); @@ -2756,10 +2811,6 @@ freeeprog(Eprog p) DPUTS(p->nref > 0 && (p->flags & EF_HEAP), "Heap EPROG has nref > 0"); DPUTS(p->nref < 0 && !(p->flags & EF_HEAP), "Real EPROG has nref < 0"); DPUTS(p->nref < -1, "Uninitialised EPROG nref"); -#ifdef MAX_FUNCTION_DEPTH - DPUTS(zsh_funcnest >=0 && p->nref > zsh_funcnest + 10, - "Overlarge EPROG nref"); -#endif if (p->nref > 0 && !--p->nref) { for (i = p->npats, pp = p->pats; i--; pp++) freepatprog(*pp); @@ -2773,6 +2824,13 @@ freeeprog(Eprog p) } } +/* + * dup is of type 'enum ec_dup_t'. + * + * If tokflag is not NULL, *tokflag will be set to 1 if the string contains + * tokens and to 0 otherwise. + */ + /**/ char * ecgetstr(Estate s, int dup, int *tokflag) diff --git a/Src/patchlevel.h.release b/Src/patchlevel.h.release index e4475aae7..05403a9e4 100644 --- a/Src/patchlevel.h.release +++ b/Src/patchlevel.h.release @@ -1 +1 @@ -#define ZSH_PATCHLEVEL "zsh-5.8.1-0-g1a490c7" +#define ZSH_PATCHLEVEL "zsh-5.8.1.2-test-0-g6e55c92" diff --git a/Src/pattern.c b/Src/pattern.c index c7c2c8bea..c0e31b78e 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -509,7 +509,7 @@ void patcompstart(void) { patcompcharsset(); - if (isset(CASEGLOB)) + if (isset(CASEGLOB) || isset(CASEPATHS)) patglobflags = 0; else patglobflags = GF_IGNCASE; @@ -632,6 +632,13 @@ patcompile(char *exp, int inflags, char **endexp) p->patmlen = len; p->patnpar = patnpar-1; +#ifndef __CYGWIN__ /* The filesystem itself is case-insensitive on Cygwin */ + if ((patflags & PAT_FILE) && !isset(CASEGLOB) && !(patflags & PAT_PURES)) { + p->globflags |= GF_IGNCASE; + p->globend |= GF_IGNCASE; + } +#endif + if (!strp) { pscan = (Upat)(patout + startoff); @@ -1250,7 +1257,7 @@ patcomppiece(int *flagp, int paren) int hash, count; union upat up; char *nptr, *str0, *ptr, *patprev; - zrange_t from, to; + zrange_t from = 0, to; char *charstart; flags = 0; diff --git a/Src/prompt.c b/Src/prompt.c index 91e21c8e9..738c7fc7a 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -268,7 +268,7 @@ parsecolorchar(zattr arg, int is_fg) bv->fm--; } } else - arg = match_colour(NULL, 1, arg); + arg = match_colour(NULL, is_fg, arg); return arg; } @@ -1055,9 +1055,9 @@ tsetcap(int cap, int flags) if (txtisset(TXTUNDERLINE)) tsetcap(TCUNDERLINEBEG, flags); if (txtisset(TXTFGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_FG, TSC_PROMPT); + set_colour_attribute(txtattrmask, COL_SEQ_FG, flags); if (txtisset(TXTBGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_BG, TSC_PROMPT); + set_colour_attribute(txtattrmask, COL_SEQ_BG, flags); } } } @@ -1706,38 +1706,22 @@ match_colour(const char **teststrp, int is_fg, int colour) return TXT_ERROR; } } - /* - * Try termcap for numbered characters if possible. - * Don't for named characters, since our best bet - * of getting the names right is with ANSI sequences. - */ - if (!named && tccan(tc)) { - if (tccolours >= 0 && colour >= tccolours) { - /* - * Out of range of termcap colours. - * Can we assume ANSI colours work? - */ - if (colour > 7) - return TXT_ERROR; /* No. */ - } else { - /* - * We can handle termcap colours and the number - * is in range, so use termcap. - */ - on |= is_fg ? TXT_ATTR_FG_TERMCAP : - TXT_ATTR_BG_TERMCAP; - } - } + + /* Out of range of termcap colours and basic ANSI set. */ + if (tccan(tc) && colour > 7 && colour >= tccolours) + return TXT_ERROR; + return on | (zattr)colour << shft; } /* * Match a set of highlights in the given teststr. * Set *on_var to reflect the values found. + * Return a pointer to the first character not consumed. */ /**/ -mod_export void +mod_export const char * match_highlight(const char *teststr, zattr *on_var) { int found = 1; @@ -1755,7 +1739,7 @@ match_highlight(const char *teststr, zattr *on_var) atr = match_colour(&teststr, is_fg, 0); if (*teststr == ',') teststr++; - else if (*teststr) + else if (*teststr && *teststr != ' ') break; found = 1; /* skip out of range colours but keep scanning attributes */ @@ -1768,7 +1752,7 @@ match_highlight(const char *teststr, zattr *on_var) if (*val == ',') val++; - else if (*val) + else if (*val && *val != ' ') break; *on_var |= hl->mask_on; @@ -1779,6 +1763,8 @@ match_highlight(const char *teststr, zattr *on_var) } } } + + return teststr; } /* @@ -1788,7 +1774,7 @@ match_highlight(const char *teststr, zattr *on_var) */ static int -output_colour(int colour, int fg_bg, int use_tc, int truecol, char *buf) +output_colour(int colour, int fg_bg, int truecol, char *buf) { int atrlen = 3, len; char *ptr = buf; @@ -1806,7 +1792,7 @@ output_colour(int colour, int fg_bg, int use_tc, int truecol, char *buf) * used instead of termcap even for colour > 7. Here this just emits the * color number, so it works fine for both zle_highlight and tercap cases */ - } else if (use_tc || colour > 7) { + } else if (colour > 7) { char digbuf[DIGBUFSIZE]; sprintf(digbuf, "%d", colour); len = strlen(digbuf); @@ -1843,7 +1829,6 @@ output_highlight(zattr atr, char *buf) if (atr & TXTFGCOLOUR) { len = output_colour(txtchangeget(atr, TXT_ATTR_FG_COL), COL_SEQ_FG, - (atr & TXT_ATTR_FG_TERMCAP), (atr & TXT_ATTR_FG_24BIT), ptr); atrlen += len; @@ -1860,7 +1845,6 @@ output_highlight(zattr atr, char *buf) } len = output_colour(txtchangeget(atr, TXT_ATTR_BG_COL), COL_SEQ_BG, - (atr & TXT_ATTR_BG_TERMCAP), (atr & TXT_ATTR_BG_24BIT), ptr); atrlen += len; @@ -2025,7 +2009,6 @@ free_colour_buffer(void) * fg_bg indicates if we're changing the foreground or background. * tc indicates the termcap code to use, if appropriate. * def indicates if we're resetting the default colour. - * use_termcap indicates if we should use termcap to output colours. * flags is either 0 or TSC_PROMPT. */ @@ -2035,7 +2018,7 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) { char *ptr; int do_free, is_prompt = (flags & TSC_PROMPT) ? 1 : 0; - int colour, tc, def, use_termcap, use_truecolor; + int colour, tc, def, use_truecolor; int is_default_zle_highlight = 1; if (fg_bg == COL_SEQ_FG) { @@ -2043,13 +2026,11 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) tc = TCFGCOLOUR; def = txtchangeisset(atr, TXTNOFGCOLOUR); use_truecolor = txtchangeisset(atr, TXT_ATTR_FG_24BIT); - use_termcap = txtchangeisset(atr, TXT_ATTR_FG_TERMCAP); } else { colour = txtchangeget(atr, TXT_ATTR_BG_COL); tc = TCBGCOLOUR; def = txtchangeisset(atr, TXTNOBGCOLOUR); use_truecolor = txtchangeisset(atr, TXT_ATTR_BG_24BIT); - use_termcap = txtchangeisset(atr, TXT_ATTR_BG_TERMCAP); } /* Test if current zle_highlight settings are customized, or @@ -2064,17 +2045,14 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) } /* - * If we're not restoring the default, and either have a - * colour value that is too large for ANSI, or have been told - * to use the termcap sequence, try to use the termcap sequence. - * True color is not covered by termcap. + * If we're not restoring the default or applying true color, + * try to use the termcap sequence. * * We have already sanitised the values we allow from the * highlighting variables, so much of this shouldn't be * necessary at this point, but we might as well be safe. */ - if (!def && !use_truecolor && - (is_default_zle_highlight && (colour > 7 || use_termcap))) + if (!def && !use_truecolor && is_default_zle_highlight) { /* * We can if it's available, and either we couldn't get @@ -2094,7 +2072,8 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) *bv->bp++ = Outpar; } } else { - tputs(tgoto(tcstr[tc], colour, colour), 1, putshout); + tputs(tgoto(tcstr[tc], colour, colour), 1, + (flags & TSC_RAW) ? putraw : putshout); } /* That worked. */ return; @@ -2153,7 +2132,7 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) *bv->bp++ = Outpar; } } else - tputs(colseq_buf, 1, putshout); + tputs(colseq_buf, 1, (flags & TSC_RAW) ? putraw : putshout); if (do_free) free_colour_buffer(); diff --git a/Src/signals.c b/Src/signals.c index 96ff9e9b3..5c787e2a8 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -53,7 +53,7 @@ mod_export Eprog siglists[VSIGCOUNT]; /* Total count of trapped signals */ /**/ -mod_export int nsigtrapped; +mod_export volatile int nsigtrapped; /* Running an exit trap? */ @@ -72,20 +72,20 @@ static int exit_trap_posix; /* Variables used by signal queueing */ /**/ -mod_export int queueing_enabled, queue_front, queue_rear; +mod_export volatile int queueing_enabled, queue_front, queue_rear; /**/ mod_export int signal_queue[MAX_QUEUE_SIZE]; /**/ mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE]; #ifdef DEBUG /**/ -mod_export int queue_in; +mod_export volatile int queue_in; #endif /* Variables used by trap queueing */ /**/ -mod_export int trap_queueing_enabled, trap_queue_front, trap_queue_rear; +mod_export volatile int trap_queueing_enabled, trap_queue_front, trap_queue_rear; /**/ mod_export int trap_queue[MAX_QUEUE_SIZE]; @@ -539,7 +539,8 @@ wait_for_processes(void) #endif if (WIFEXITED(status) && pn->pid == jn->gleader && - killpg(pn->pid, 0) == -1) { + killpg(pn->pid, 0) == -1 && + errno == ESRCH) { if (last_attached_pgrp == jn->gleader && !(jn->stat & STAT_NOSTTY)) { /* @@ -671,9 +672,9 @@ zhandler(int sig) if ((isset(PRIVILEGED) || isset(RESTRICTED)) && isset(INTERACTIVE) && (noerrexit & NOERREXIT_SIGNAL)) zexit(SIGINT, ZEXIT_SIGNAL); + errflag |= ERRFLAG_INT; if (list_pipe || chline || simple_pline) { breaks = loops; - errflag |= ERRFLAG_INT; inerrflush(); check_cursh_sig(SIGINT); } @@ -809,6 +810,7 @@ killjb(Job jn, int sig) err = killpg(jn->gleader, sig); if (sig == SIGCONT && err != -1) makerunning(jn); + return err; } } for (pn = jn->procs; pn; pn = pn->next) { @@ -1265,19 +1267,19 @@ unqueue_traps(void) /* Are we already executing a trap? */ /**/ -int intrap; +volatile int intrap; /* Is the current trap a function? */ /**/ -int trapisfunc; +volatile int trapisfunc; /* * If the current trap is not a function, at what function depth * did the trap get called? */ /**/ -int traplocallevel; +volatile int traplocallevel; /* * sig is the signal number. diff --git a/Src/sort.c b/Src/sort.c index 8faf9349c..26949ad9c 100644 --- a/Src/sort.c +++ b/Src/sort.c @@ -135,12 +135,23 @@ eltpcmp(const void *a, const void *b) #endif if (sortnumeric) { + int mul = 0; for (; *as == *bs && *as; as++, bs++); #ifndef HAVE_STRCOLL cmp = (int)STOUC(*as) - (int)STOUC(*bs); #endif - if (idigit(*as) || idigit(*bs)) { + if (sortnumeric < 0) { + if (*as == '-' && idigit(as[1]) && idigit(*bs)) { + cmp = -1; + mul = 1; + } else if (*bs == '-' && idigit(bs[1]) && idigit(*as)) { + cmp = 1; + mul = 1; + } + } + if (!mul && (idigit(*as) || idigit(*bs))) { for (; as > ao && idigit(as[-1]); as--, bs--); + mul = (sortnumeric < 0 && as > ao && as[-1] == '-') ? -1 : 1; if (idigit(*as) && idigit(*bs)) { while (*as == '0') as++; @@ -148,13 +159,13 @@ eltpcmp(const void *a, const void *b) bs++; for (; idigit(*as) && *as == *bs; as++, bs++); if (idigit(*as) || idigit(*bs)) { - cmp = (int)STOUC(*as) - (int)STOUC(*bs); + cmp = mul * ((int)STOUC(*as) - (int)STOUC(*bs)); while (idigit(*as) && idigit(*bs)) as++, bs++; if (idigit(*as) && !idigit(*bs)) - return sortdir; + return mul * sortdir; if (idigit(*bs) && !idigit(*as)) - return -sortdir; + return -mul * sortdir; } } } @@ -195,7 +206,8 @@ zstrcmp(const char *as, const char *bs, int sortflags) sortdir = 1; sortnobslash = (sortflags & SORTIT_IGNORING_BACKSLASHES) ? 1 : 0; - sortnumeric = (sortflags & SORTIT_NUMERICALLY) ? 1 : 0; + sortnumeric = (sortflags & SORTIT_NUMERICALLY_SIGNED) ? -1 : + (sortflags & SORTIT_NUMERICALLY) ? 1 : 0; ret = eltpcmp(&aeptr, &beptr); @@ -389,7 +401,8 @@ strmetasort(char **array, int sortwhat, int *unmetalenp) oldsortnumeric = sortnumeric; sortdir = (sortwhat & SORTIT_BACKWARDS) ? -1 : 1; - sortnumeric = (sortwhat & SORTIT_NUMERICALLY) ? 1 : 0; + sortnumeric = (sortwhat & SORTIT_NUMERICALLY_SIGNED) ? -1 : + (sortwhat & SORTIT_NUMERICALLY) ? 1 : 0; qsort(sortptrarr, nsort, sizeof(SortElt), eltpcmp); diff --git a/Src/string.c b/Src/string.c index 9e14ef949..5f439926e 100644 --- a/Src/string.c +++ b/Src/string.c @@ -103,7 +103,10 @@ wcs_ztrdup(const wchar_t *s) #endif /* MULTIBYTE_SUPPORT */ -/* concatenate s1, s2, and s3 in dynamically allocated buffer */ +/* Concatenate s1, s2, and s3 into dynamically allocated buffer. + * + * To concatenate four or more strings, see zjoin(). + */ /**/ mod_export char * diff --git a/Src/subst.c b/Src/subst.c index 79efc9ad2..0f98e6ea3 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -765,7 +765,7 @@ filesubstr(char **namptr, int assign) *namptr = dyncat(res, ptr2+1); return 1; } - if (isset(NOMATCH)) + if (isset(NOMATCH) && isset(EXECOPT)) zerr("no directory expansion: ~[%s]", tmp); return 0; } else if (!inblank(str[1]) && isend(*ptr) && @@ -796,7 +796,7 @@ filesubstr(char **namptr, int assign) *namptr = dyncat(hom, ptr); return 1; } - } else if (*str == Equals && isset(EQUALS) && str[1]) { /* =foo */ + } else if (*str == Equals && isset(EQUALS) && str[1] && str[1] != Inpar) { /* =foo */ char *expn = equalsubstr(str+1, assign, isset(NOMATCH)); if (expn) { *namptr = expn; @@ -1565,6 +1565,11 @@ check_colon_subscript(char *str, char **endp) if (!*str || ialpha(*str) || *str == '&') return NULL; + if (*str == ':') { + *endp = str; + return dupstring("0"); + } + *endp = parse_subscript(str, 0, ':'); if (!*endp) { /* No trailing colon? */ @@ -1575,8 +1580,10 @@ check_colon_subscript(char *str, char **endp) sav = **endp; **endp = '\0'; str = dupstring(str); - if (parsestr(&str)) + if (parsestr(&str)) { + **endp = sav; return NULL; + } singsub(&str); remnulargs(str); untokenize(str); @@ -1701,7 +1708,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, /* * This expressive name refers to the set of flags which * is applied to matching for #, %, / and their doubled variants: - * (M), (R), (B), (E), (N), (S). + * (M), (R), (B), (E), (N), (S), (*). */ int flags = 0; /* Value from (I) flag, used for ditto. */ @@ -1847,6 +1854,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * nested (P) flags. */ int fetch_needed; + /* + * If an array parameter is quoted but has :offset:length (as in + * "${array:off:len}"), we apply :off:len as array index before + * joining the array into a string (for compatibility with ksh/bash). + */ + int quoted_array_with_offset = 0; *s++ = '\0'; /* @@ -1923,6 +1936,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, case '@': nojoin = 2; /* nojoin = 2 means force */ break; + case '*': + case Star: + flags |= SUB_EGLOB; + break; case 'M': flags |= SUB_MATCH; break; @@ -1972,6 +1989,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, case 'n': sortit |= SORTIT_NUMERICALLY; break; + case '-': + case Dash: + sortit |= SORTIT_NUMERICALLY_SIGNED; + break; case 'a': sortit |= SORTIT_SOMEHOW; indord = 1; @@ -2244,9 +2265,31 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, break; default: - flagerr: - zerr("error in flags"); - return NULL; + flagerr: + { + /* + * We're trying to output the string that failed to + * parse and the offset of the parse error within that. + * + * The string is *str. It hasn't been changed since + * entry to this function, I think, except that the + * first non-variable-declaration line in this function + * (currently the 238th line in this function) + * writes a NUL to the first place in *str, so we'll + * compensate by outputting the dollar sign manually. + */ + char *str_copy_for_output = dupstring(*str + 1); + + /* + * Convert to a 1-based offset, because the shell + * language is 1-based by default. + */ + zlong offset = s - *str + 1; + + untokenize(str_copy_for_output); + zerr("error in flags near position %z in '$%s'", offset, str_copy_for_output); + return NULL; + } } } s++; @@ -2526,7 +2569,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Handle the (t) flag: value now becomes the type * information for the parameter. */ - if (v && v->pm && !(v->pm->node.flags & PM_UNSET)) { + if (v && v->pm && ((v->pm->node.flags & PM_DECLARED) || + !(v->pm->node.flags & PM_UNSET))) { int f = v->pm->node.flags; switch (PM_TYPE(f)) { @@ -2781,7 +2825,6 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } if (bct) { - noclosebrace: zerr("closing brace expected"); return NULL; } @@ -2800,7 +2843,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, c == '#' || c == Pound || c == '?' || c == Quest || c == '/')) { - + int eglob = isset(EXTENDEDGLOB); /* * Default index is 1 if no (I) or (I) gave zero. But * why don't we set the default explicitly at the start @@ -2822,9 +2865,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, char *ptr; /* * previous flags are irrelevant, except for (S) which - * indicates shortest substring; else look for longest. + * indicates shortest substring; else look for longest, + * and (*) which temporarily enables extended globbing. */ - flags = (flags & SUB_SUBSTR) ? 0 : SUB_LONG; + flags = ((flags & SUB_SUBSTR) ? 0 : SUB_LONG)|(flags & SUB_EGLOB); if ((c = *s) == '/') { /* doubled, so replace all occurrences */ flags |= SUB_GLOBAL; @@ -2937,9 +2981,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } break; case ':': - /* this must be `::=', unconditional assignment */ - if (*s != '=' && *s != Equals) - goto noclosebrace; + /* this could be either `::=', unconditional assignment + * or a ${name:offset:length} with an empty offset */ + if (*s != '=' && *s != Equals) { + s -= 1; + goto colonsubscript; + } vunset = 1; s++; /* Fall through */ @@ -3123,7 +3170,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, for (ap = aval; *ap; ap++) { untokenize(*ap); } + if (flags & SUB_EGLOB) + opts[EXTENDEDGLOB] = 1; getmatcharr(&aval, s, flags, flnum, replstr); + opts[EXTENDEDGLOB] = eglob; } else { if (vunset) { if (vunset > 0 && unset(UNSET)) { @@ -3138,7 +3188,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, copied = 1; untokenize(val); } + if (flags & SUB_EGLOB) + opts[EXTENDEDGLOB] = 1; getmatch(&val, s, flags, flnum, replstr); + opts[EXTENDEDGLOB] = eglob; } break; } @@ -3282,6 +3335,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * if there isn't a trailing modifier? Why don't we do this * e.g. when we handle the ${(t)...} flag? */ +colonsubscript: if (chkset) { val = dupstring(vunset ? "0" : "1"); isarr = 0; @@ -3314,20 +3368,31 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, return NULL; } if (*check_offset2) { + char *nextp; check_offset = check_colon_subscript(check_offset2 + 1, - &check_offset2); - if (*check_offset2 && *check_offset2 != ':') { - zerr("invalid length: %s", check_offset); - return NULL; - } + &nextp); if (check_offset) { + check_offset2 = nextp; + if (*check_offset2 && *check_offset2 != ':') { + zerr("invalid length: %s", check_offset); + return NULL; + } length = mathevali(check_offset); length_set = 1; if (errflag) return NULL; } } - if (isarr) { + /* + * We've got :OFFSET (and :LENGTH). + * If aval is non-NULL but isarr is 0, PARAM is (probably) + * an array but quoted like "${PARAM:OFFSET}". We apply + * :OFFSET as array index (as if it is not quoted). We will + * join them later (search for quoted_array_with_offset). + */ + if (aval && !isarr) + quoted_array_with_offset = 1; + if (isarr || quoted_array_with_offset) { int alen, count; char **srcptr, **dstptr, **newarr; @@ -3572,9 +3637,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * exception is that ${name:-word} and ${name:+word} will have already * done any requested splitting of the word value with quoting preserved. */ - if (ssub || spbreak || spsep || sep) { + if (ssub || spbreak || spsep || sep || quoted_array_with_offset) { int force_split = !ssub && (spbreak || spsep); - if (isarr) { + if (isarr || quoted_array_with_offset) { /* sep non-null here means F or j flag, force join */ if (nojoin == 0 || sep) { val = sepjoin(aval, sep, 1); @@ -4399,7 +4464,7 @@ modify(char **str, char **ptr, int inbrace) chabspath(©); break; case 'A': - chrealpath(©); + chrealpath(©, 'A', 1); break; case 'c': { @@ -4485,7 +4550,7 @@ modify(char **str, char **ptr, int inbrace) chabspath(str); break; case 'A': - chrealpath(str); + chrealpath(str, 'A', 1); break; case 'c': { diff --git a/Src/text.c b/Src/text.c index 69530ae79..5cd7685fd 100644 --- a/Src/text.c +++ b/Src/text.c @@ -335,6 +335,8 @@ getjobtext(Eprog prog, Wordcode c) tlim = tptr + JOBTEXTSIZE - 1; tjob = 1; gettext2(&s); + if (tptr[-1] == Meta) + --tptr; *tptr = '\0'; freeeprog(prog); /* mark as unused */ untokenize(jbuf); @@ -600,7 +602,7 @@ gettext2(Estate state) n->u._funcdef.end = end; n->u._funcdef.nargs = nargs; state->strs += *state->pc; - state->pc += 3; + state->pc += 4; } } else { state->strs = s->u._funcdef.strs; diff --git a/Src/utils.c b/Src/utils.c index f5667f389..62bd3e602 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -130,6 +130,7 @@ set_widearray(char *mb_array, Widechar_array wca) %l const char *, int C string of given length (null not required) %L long decimal value %d int decimal value + %z zlong decimal value %% (none) literal '%' %c int character at that codepoint %e int strerror() message (argument is typically 'errno') @@ -331,6 +332,14 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) num = va_arg(ap, int); fprintf(file, "%d", num); break; + case 'z': + { + zlong znum = va_arg(ap, zlong); + char buf[DIGBUFSIZE]; + convbase(buf, znum, 10); + fputs(buf, file); + break; + } case '%': putc('%', file); break; @@ -429,7 +438,6 @@ putshout(int c) return 0; } -#ifdef MULTIBYTE_SUPPORT /* * Turn a character into a visible representation thereof. The visible * string is put together in a static buffer, and this function returns @@ -515,62 +523,6 @@ nicechar(int c) return nicechar_sel(c, 0); } -#else /* MULTIBYTE_SUPPORT */ - -/**/ -mod_export char * -nicechar(int c) -{ - static char buf[10]; - char *s = buf; - c &= 0xff; - if (ZISPRINT(c)) - goto done; - if (c & 0x80) { - if (isset(PRINTEIGHTBIT)) - goto done; - *s++ = '\\'; - *s++ = 'M'; - *s++ = '-'; - c &= 0x7f; - if(ZISPRINT(c)) - goto done; - } - if (c == 0x7f) { - *s++ = '\\'; - *s++ = 'C'; - *s++ = '-'; - c = '?'; - } else if (c == '\n') { - *s++ = '\\'; - c = 'n'; - } else if (c == '\t') { - *s++ = '\\'; - c = 't'; - } else if (c < 0x20) { - *s++ = '\\'; - *s++ = 'C'; - *s++ = '-'; - c += 0x40; - } - done: - /* - * The resulting string is still metafied, so check if - * we are returning a character in the range that needs metafication. - * This can't happen if the character is printed "nicely", so - * this results in a maximum of two bytes total (plus the null). - */ - if (imeta(c)) { - *s++ = Meta; - *s++ = c ^ 32; - } else - *s++ = c; - *s = 0; - return buf; -} - -#endif /* MULTIBYTE_SUPPORT */ - /* * Return 1 if nicechar() would reformat this character. */ @@ -711,7 +663,7 @@ wcs_nicechar_sel(wchar_t c, size_t *widthp, char **swidep, int quotable) if (widthp) *widthp = 6; } else { - strcpy(buf, nicechar((int)c)); + strcpy(buf, nicechar_sel((int)c, quotable)); /* * There may be metafied characters from nicechar(), * so compute width and end position independently. @@ -771,7 +723,7 @@ mod_export int is_wcs_nicechar(wchar_t c) if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20) return 1; if (c >= 0x80) { - return (c >= 0x100); + return (c >= 0x100 || is_nicechar((int)c)); } } return 0; @@ -910,11 +862,14 @@ slashsplit(char *s) return r; } -/* expands symlinks and .. or . expressions */ +/* expands .. or . expressions and one level of symlinks + * + * Puts the result in the global "xbuf" + */ /**/ static int -xsymlinks(char *s, int full) +xsymlinks(char *s) { char **pp, **opp; char xbuf2[PATH_MAX*3+1], xbuf3[PATH_MAX*2+1]; @@ -963,7 +918,7 @@ xsymlinks(char *s, int full) } else { ret = 1; metafy(xbuf3, t0, META_NOALLOC); - if (!full) { + { /* * If only one expansion requested, ensure the * full path is in xbuf. @@ -998,17 +953,6 @@ xsymlinks(char *s, int full) */ break; } - if (*xbuf3 == '/') { - strcpy(xbuf, ""); - if (xsymlinks(xbuf3 + 1, 1) < 0) - ret = -1; - else - xbuflen = strlen(xbuf); - } else - if (xsymlinks(xbuf3, 1) < 0) - ret = -1; - else - xbuflen = strlen(xbuf); } } freearray(opp); @@ -1023,17 +967,17 @@ xsymlinks(char *s, int full) */ /**/ -char * +mod_export char * xsymlink(char *s, int heap) { if (*s != '/') return NULL; *xbuf = '\0'; - if (xsymlinks(s + 1, 1) < 0) + if (!chrealpath(&s, 'P', heap)) { zwarn("path expansion failed, using root directory"); - if (!*xbuf) return heap ? dupstring("/") : ztrdup("/"); - return heap ? dupstring(xbuf) : ztrdup(xbuf); + } + return s; } /**/ @@ -1041,12 +985,12 @@ void print_if_link(char *s, int all) { if (*s == '/') { - *xbuf = '\0'; if (all) { char *start = s + 1; char xbuflink[PATH_MAX+1]; + *xbuf = '\0'; for (;;) { - if (xsymlinks(start, 0) > 0) { + if (xsymlinks(start) > 0) { printf(" -> "); zputs(*xbuf ? xbuf : "/", stdout); if (!*xbuf) @@ -1059,8 +1003,11 @@ print_if_link(char *s, int all) } } } else { - if (xsymlinks(s + 1, 1) > 0) - printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout); + if (chrealpath(&s, 'P', 0)) { + printf(" -> "); + zputs(*s ? s : "/", stdout); + zsfree(s); + } } } } @@ -1104,7 +1051,7 @@ substnamedir(char *s) /* Returns the current username. It caches the username * * and uid to try to avoid requerying the password files * - * or NIS/NIS+ database. */ + * or other source. */ /**/ uid_t cached_uid; @@ -1115,7 +1062,7 @@ char *cached_username; char * get_username(void) { -#ifdef HAVE_GETPWUID +#ifdef USE_GETPWUID struct passwd *pswd; uid_t current_uid; @@ -1128,9 +1075,9 @@ get_username(void) else cached_username = ztrdup(""); } -#else /* !HAVE_GETPWUID */ +#else /* !USE_GETPWUID */ cached_uid = getuid(); -#endif /* !HAVE_GETPWUID */ +#endif /* !USE_GETPWUID */ return cached_username; } @@ -1306,7 +1253,7 @@ getnameddir(char *name) return str; } -#ifdef HAVE_GETPWNAM +#ifdef USE_GETPWNAM { /* Retrieve an entry from the password table/database for this user. */ struct passwd *pw; @@ -1322,7 +1269,7 @@ getnameddir(char *name) return dupstring(pw->pw_dir); } } -#endif /* HAVE_GETPWNAM */ +#endif /* USE_GETPWNAM */ /* There are no more possible sources of directory names, so give up. */ return NULL; @@ -1374,6 +1321,9 @@ delprepromptfn(voidvoidfnptr_t func) { LinkNode ln; + if (!prepromptfns) + return; + for (ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) { Prepromptfn ppdat = (Prepromptfn)getdata(ln); if (ppdat->func == func) { @@ -1487,14 +1437,9 @@ deltimedfn(voidvoidfnptr_t func) /**/ time_t lastmailcheck; -/* the last time we checked the people in the WATCH variable */ - -/**/ -time_t lastwatch; - /* * Call a function given by "name" with optional arguments - * "lnklist". If these are present the first argument is the function name. + * "lnklst". If these are present the first argument is the function name. * * If "arrayp" is not zero, we also look through * the array "name"_functions and execute functions found there. @@ -1523,6 +1468,10 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval) incompfunc = 0; if ((shfunc = getshfunc(name))) { + if (!lnklst) { + lnklst = newlinklist(); + addlinknode(lnklst, name); + } ret = doshfunc(shfunc, lnklst, 1); stat = 0; } @@ -1535,10 +1484,16 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval) memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN); if ((arrptr = getaparam(arrnam))) { + char **argarr = lnklst ? hlinklist2array(lnklst, 0) : NULL; arrptr = arrdup(arrptr); for (; *arrptr; arrptr++) { if ((shfunc = getshfunc(*arrptr))) { - int newret = doshfunc(shfunc, lnklst, 1); + int newret, i = 1; + LinkList arg0 = newlinklist(); + addlinknode(arg0, *arrptr); + while (argarr && argarr[i]) + addlinknode(arg0, argarr[i++]); + newret = doshfunc(shfunc, arg0, 1); if (!ret) ret = newret; stat = 0; @@ -1620,17 +1575,6 @@ preprompt(void) if (errflag) return; - /* If WATCH is set, then check for the * - * specified login/logout events. */ - if (watch) { - if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) { - dowatch(); - lastwatch = time(NULL); - } - } - if (errflag) - return; - /* Check mail */ currentmailcheck = time(NULL); if (mailcheck && @@ -2745,6 +2689,42 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds) } /* + * Return the difference between 2 times, given as struct timespec*, + * expressed in microseconds, as a long. If the difference doesn't fit + * into a long, return LONG_MIN or LONG_MAX so that the times can still + * be compared. + * + * Note: returns a long rather than a zlong because zsleep() below + * takes a long. + */ + +/**/ +long +timespec_diff_us(const struct timespec *t1, const struct timespec *t2) +{ + int reverse = (t1->tv_sec > t2->tv_sec); + time_t diff_sec; + long diff_usec, max_margin, res; + + /* Don't just subtract t2-t1 because time_t might be unsigned. */ + diff_sec = (reverse ? t1->tv_sec - t2->tv_sec : t2->tv_sec - t1->tv_sec); + if (diff_sec > LONG_MAX / 1000000L) { + goto overflow; + } + res = diff_sec * 1000000L; + max_margin = LONG_MAX - res; + diff_usec = (reverse ? + t1->tv_nsec - t2->tv_nsec : t2->tv_nsec - t1->tv_nsec + ) / 1000; + if (diff_usec <= max_margin) { + res += diff_usec; + return (reverse ? -res : res); + } + overflow: + return (reverse ? LONG_MIN : LONG_MAX); +} + +/* * Sleep for the given number of microseconds --- must be within * range of a long at the moment, but this is only used for * limited internal purposes. @@ -3088,11 +3068,13 @@ spckword(char **s, int hist, int cmd, int ask) int preflen = 0; int autocd = cmd && isset(AUTOCD) && strcmp(*s, ".") && strcmp(*s, ".."); - if ((histdone & HISTFLAG_NOEXEC) || **s == '-' || **s == '%') + if (!(*s)[0] || !(*s)[1]) return; - if (!strcmp(*s, "in")) + if ((histdone & HISTFLAG_NOEXEC) || + /* Leading % is a job, else leading hyphen is an option */ + (cmd ? **s == '%' : (**s == '-' || **s == Dash))) return; - if (!(*s)[0] || !(*s)[1]) + if (!strcmp(*s, "in")) return; if (cmd) { if (shfunctab->getnode(shfunctab, *s) || @@ -3111,8 +3093,12 @@ spckword(char **s, int hist, int cmd, int ask) if (*t == Tilde || *t == Equals || *t == String) t++; for (; *t; t++) - if (itok(*t)) - return; + if (itok(*t)) { + if (*t == Dash) + *t = '-'; + else + return; + } best = NULL; for (t = *s; *t; t++) if (*t == '/') @@ -3556,6 +3542,17 @@ strftimehandling: return buf - origbuf; } +/* + * Return a string consisting of the elements of 'arr' joined by the character + * 'delim', which will be metafied if necessary. The string will be allocated + * on the heap iff 'heap'. + * + * Comparable to: + * + * char metafied_delim[] = { Meta, delim ^ 32, '\0' }; + * sepjoin(arr, metafied_delim, heap) + */ + /**/ mod_export char * zjoin(char **arr, int delim, int heap) @@ -3854,10 +3851,12 @@ wordcount(char *s, char *sep, int mul) /* * 's' is a NULL-terminated array of strings. - * 'sep' is a string. + * 'sep' is a string, or NULL to split on ${IFS[1]}. * * Return a string consisting of the elements of 's' joined by 'sep', * allocated on the heap iff 'heap'. + * + * See also zjoin(). */ /**/ @@ -4268,7 +4267,7 @@ wcsitype(wchar_t c, int itype) } else { switch (itype) { case IIDENT: - if (!isset(POSIXIDENTIFIERS)) + if (isset(POSIXIDENTIFIERS)) return 0; return iswalnum(c); @@ -5197,26 +5196,11 @@ zputs(char const *s, FILE *stream) mod_export char * nicedup(char const *s, int heap) { - int c, len = strlen(s) * 5 + 1; - VARARR(char, buf, len); - char *p = buf, *n; + char *retstr; - while ((c = *s++)) { - if (itok(c)) { - if (c <= Comma) - c = ztokens[c - Pound]; - else - continue; - } - if (c == Meta) - c = *s++ ^ 32; - /* The result here is metafied */ - n = nicechar(c); - while(*n) - *p++ = *n++; - } - *p = '\0'; - return heap ? dupstring(buf) : ztrdup(buf); + (void)sb_niceformat(s, NULL, &retstr, heap ? NICEFLAG_HEAP : 0); + + return retstr; } #endif @@ -5235,20 +5219,7 @@ nicedupstring(char const *s) mod_export int nicezputs(char const *s, FILE *stream) { - int c; - - while ((c = *s++)) { - if (itok(c)) { - if (c <= Comma) - c = ztokens[c - Pound]; - else - continue; - } - if (c == Meta) - c = *s++ ^ 32; - if(zputs(nicechar(c), stream) < 0) - return EOF; - } + sb_niceformat(s, stream, NULL, 0); return 0; } @@ -5737,7 +5708,7 @@ mb_charlenconv(const char *s, int slen, wint_t *wcp) } /**/ -#else +#else /* MULTIBYTE_SUPPORT */ /* Simple replacement for mb_metacharlenconv */ @@ -5777,6 +5748,121 @@ charlenconv(const char *x, int len, int *c) return 1; } +/* + * Non-multibyte version of mb_niceformat() above. Same basic interface. + */ + +/**/ +mod_export size_t +sb_niceformat(const char *s, FILE *stream, char **outstrp, int flags) +{ + size_t l = 0, newl; + int umlen, outalloc, outleft; + char *ums, *ptr, *eptr, *fmt, *outstr, *outptr; + + if (outstrp) { + outleft = outalloc = 2 * strlen(s); + outptr = outstr = zalloc(outalloc); + } else { + outleft = outalloc = 0; + outptr = outstr = NULL; + } + + ums = ztrdup(s); + /* + * is this necessary at this point? niceztrlen does this + * but it's used in lots of places. however, one day this may + * be, too. + */ + untokenize(ums); + ptr = unmetafy(ums, ¨en); + eptr = ptr + umlen; + + while (ptr < eptr) { + int c = STOUC(*ptr); + if (c == '\'' && (flags & NICEFLAG_QUOTE)) { + fmt = "\\'"; + newl = 2; + } + else if (c == '\\' && (flags & NICEFLAG_QUOTE)) { + fmt = "\\\\"; + newl = 2; + } + else { + fmt = nicechar_sel(c, flags & NICEFLAG_QUOTE); + newl = 1; + } + + ++ptr; + l += newl; + + if (stream) + zputs(fmt, stream); + if (outstr) { + /* Append to output string */ + int outlen = strlen(fmt); + if (outlen >= outleft) { + /* Reallocate to twice the length */ + int outoffset = outptr - outstr; + + outleft += outalloc; + outalloc *= 2; + outstr = zrealloc(outstr, outalloc); + outptr = outstr + outoffset; + } + memcpy(outptr, fmt, outlen); + /* Update start position */ + outptr += outlen; + /* Update available bytes */ + outleft -= outlen; + } + } + + free(ums); + if (outstrp) { + *outptr = '\0'; + /* Use more efficient storage for returned string */ + if (flags & NICEFLAG_NODUP) + *outstrp = outstr; + else { + *outstrp = (flags & NICEFLAG_HEAP) ? dupstring(outstr) : + ztrdup(outstr); + free(outstr); + } + } + + return l; +} + +/* + * Return 1 if sb_niceformat() would reformat this string, else 0. + */ + +/**/ +mod_export int +is_sb_niceformat(const char *s) +{ + int umlen, ret = 0; + char *ums, *ptr, *eptr; + + ums = ztrdup(s); + untokenize(ums); + ptr = unmetafy(ums, ¨en); + eptr = ptr + umlen; + + while (ptr < eptr) { + if (is_nicechar(*ptr)) { + ret = 1; + break; + } + ++ptr; + } + + free(ums); + + return ret; +} + /**/ #endif /* MULTIBYTE_SUPPORT */ @@ -5853,8 +5939,11 @@ zexpandtabs(const char *s, int len, int width, int startpos, FILE *fout, memset(&mbs, 0, sizeof(mbs)); s++; len--; - } else if (ret == MB_INCOMPLETE) { + } else if (ret == MB_INCOMPLETE || /* incomplete at end --- assume likewise, best we've got */ + ret == 0) { + /* NUL character returns 0, which would loop infinitely, so advance + * one byte in this case too */ s++; len--; } else { @@ -6307,6 +6396,22 @@ quotedzputs(char const *s, FILE *stream) return outstr; } } +#else + if (is_sb_niceformat(s)){ + if (stream) { + fputs("$'", stream); + sb_niceformat(s, stream, NULL, NICEFLAG_QUOTE); + fputc('\'', stream); + return NULL; + } else { + char *substr; + sb_niceformat(s, NULL, &substr, NICEFLAG_QUOTE|NICEFLAG_NODUP); + outstr = (char *)zhalloc(4 + strlen(substr)); + sprintf(outstr, "$'%s'", substr); + free(substr); + return outstr; + } + } #endif /* MULTIBYTE_SUPPORT */ if (!hasspecial(s)) { @@ -6635,13 +6740,21 @@ ucs4toutf8(char *dest, unsigned int wval) * * The return value is unmetafied unless GETKEY_DOLLAR_QUOTE is * in use. + * + * If GETKEY_SINGLE_CHAR is set in how, a next character in the given + * string is parsed, and the character code for it is returned in misc. + * The return value of the function is a pointer to the byte in the + * given string from where the next parsing should start. If the next + * character can't be found then NULL is returned. + * CAUTION: Currently, GETKEY_SINGLE_CHAR can be used only via + * GETKEYS_MATH. Other use of it may cause trouble. */ /**/ mod_export char * getkeystring(char *s, int *len, int how, int *misc) { - char *buf, tmp[1]; + char *buf = NULL, tmp[1]; char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL; char svchar = '\0'; int meta = 0, control = 0, ignoring = 0; @@ -6667,9 +6780,11 @@ getkeystring(char *s, int *len, int how, int *misc) DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR)) == (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR), "BUG: incompatible options in getkeystring"); + DPUTS((how & GETKEY_SINGLE_CHAR) && (how != GETKEYS_MATH), + "BUG: unsupported options in getkeystring"); if (how & GETKEY_SINGLE_CHAR) - t = buf = tmp; + t = tmp; else { /* Length including terminating NULL */ int maxlen = 1; @@ -7103,13 +7218,20 @@ getkeystring(char *s, int *len, int how, int *misc) */ DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_UPDATE_OFFSET)) == GETKEY_DOLLAR_QUOTE, "BUG: unterminated $' substitution"); - *t = '\0'; - if (how & GETKEY_DOLLAR_QUOTE) - *tdest = '\0'; - if (how & GETKEY_SINGLE_CHAR) + + if (how & GETKEY_SINGLE_CHAR) { + /* couldn't find a character */ *misc = 0; - else - *len = ((how & GETKEY_DOLLAR_QUOTE) ? tdest : t) - buf; + return NULL; + } + if (how & GETKEY_DOLLAR_QUOTE) { + *tdest = '\0'; + *len = tdest - buf; + } + else { + *t = '\0'; + *len = t - buf; + } return buf; } @@ -832,22 +832,59 @@ struct estate { char *strs; /* strings from prog */ }; +/* + * A binary tree of strings. + * + * Refer to the "Word code." comment at the top of Src/parse.c for details. + */ typedef struct eccstr *Eccstr; - struct eccstr { + /* Child pointers. */ Eccstr left, right; + + /* String; pointer into to estate::strs. */ char *str; - wordcode offs, aoffs; + + /* Wordcode of a long string, as described in the Src/parse.c comment. */ + wordcode offs; + + /* Raw memory offset of str in estate::strs. */ + wordcode aoffs; + + /* + * ### The number of starts and ends of function definitions up to this point. + * + * String reuse may only happen between strings that have the same "nfunc" value. + */ int nfunc; + + /* Hash of str. */ int hashval; }; -#define EC_NODUP 0 -#define EC_DUP 1 -#define EC_DUPTOK 2 +/* + * Values for the "dup" parameter to ecgetstr(). + */ +enum ec_dup_t { + /* + * Make no promises about how the return value is allocated, except that + * the caller does not need to explicitly free it. It might be heap allocated, + * a static string, or anything in between. + */ + EC_NODUP = 0, -#define WC_CODEBITS 5 + /* Allocate the return value from the heap. */ + EC_DUP = 1, + + /* + * If the string contains tokens (as indicated by the least significant bit + * of the wordcode), behave as EC_DUP; otherwise, as EC_NODUP. + */ + EC_DUPTOK = 2 +}; +/* See comment at the top of Src/parse.c for details. */ +#define WC_CODEBITS 5 #define wc_code(C) ((C) & ((wordcode) ((1 << WC_CODEBITS) - 1))) #define wc_data(C) ((C) >> WC_CODEBITS) #define wc_bdata(D) ((D) << WC_CODEBITS) @@ -876,7 +913,11 @@ struct eccstr { #define WC_AUTOFN 20 #define WC_TRY 21 -/* increment as necessary */ +/* + * Increment as necessary. + * + * If this exceeds 31, increment WC_CODEBITS. + */ #define WC_COUNT 22 #define WCB_END() wc_bld(WC_END, 0) @@ -1156,7 +1197,10 @@ typedef void (*ScanTabFunc) _((HashTable, ScanFunc, int)); typedef void (*PrintTableStats) _((HashTable)); -/* hash table for standard open hashing */ +/* Hash table for standard open hashing. Instances of struct hashtable can be * + * created only by newhashtable(). In fact, this function creates an instance * + * of struct hashtableimpl, which is made of struct hashtable (public part) * + * and additional data members that are only accessible from hashtable.c. */ struct hashtable { /* HASHTABLE DATA */ @@ -1180,10 +1224,6 @@ struct hashtable { FreeNodeFunc freenode; /* pointer to function to free a node */ ScanFunc printnode; /* pointer to function to print a node */ ScanTabFunc scantab; /* pointer to function to scan table */ - -#ifdef HASHTABLE_INTERNAL_MEMBERS - HASHTABLE_INTERNAL_MEMBERS /* internal use in hashtable.c */ -#endif }; /* generic hash table node */ @@ -1408,7 +1448,7 @@ struct builtin { int minargs; /* minimum number of arguments */ int maxargs; /* maximum number of arguments, or -1 for no limit */ int funcid; /* xbins (see above) for overloaded handlerfuncs */ - char *optstr; /* string of legal options */ + char *optstr; /* string of legal options (see execbuiltin()) */ char *defopts; /* options set by default for overloaded handlerfuncs */ }; @@ -1889,8 +1929,10 @@ struct tieddata { made read-only by the user */ #define PM_READONLY_SPECIAL (PM_SPECIAL|PM_READONLY|PM_RO_BY_DESIGN) #define PM_DONTIMPORT (1<<22) /* do not import this variable */ +#define PM_DECLARED (1<<22) /* explicitly named with typeset */ #define PM_RESTRICTED (1<<23) /* cannot be changed in restricted mode */ #define PM_UNSET (1<<24) /* has null value */ +#define PM_DEFAULTED (PM_DECLARED|PM_UNSET) #define PM_REMOVABLE (1<<25) /* special can be removed from paramtab */ #define PM_AUTOLOAD (1<<26) /* autoloaded from module */ #define PM_NORESTORE (1<<27) /* do not restore value of local special */ @@ -1951,6 +1993,7 @@ struct tieddata { #define SUB_START 0x1000 /* force match at start with SUB_END * and no SUB_SUBSTR */ #define SUB_LIST 0x2000 /* no substitution, return list of matches */ +#define SUB_EGLOB 0x4000 /* use extended globbing in patterns */ /* * Structure recording multiple matches inside a test string. @@ -2346,6 +2389,7 @@ enum { BSDECHO, CASEGLOB, CASEMATCH, + CASEPATHS, CBASES, CDABLEVARS, CDSILENT, @@ -2354,6 +2398,7 @@ enum { CHECKJOBS, CHECKRUNNINGJOBS, CLOBBER, + CLOBBEREMPTY, APPENDCREATE, COMBININGCHARS, COMPLETEALIASES, @@ -2484,6 +2529,7 @@ enum { SHNULLCMD, SHOPTIONLETTERS, SHORTLOOPS, + SHORTREPEAT, SHWORDSPLIT, SINGLECOMMAND, SINGLELINEZLE, @@ -2492,6 +2538,7 @@ enum { TRANSIENTRPROMPT, TRAPSASYNC, TYPESETSILENT, + TYPESETTOUNSET, UNSET, VERBOSE, VIMODE, @@ -2683,11 +2730,6 @@ struct ttyinfo { /* Bits to shift the background colour */ #define TXT_ATTR_BG_COL_SHIFT (40) -/* Flag to use termcap AF sequence to set colour, if available */ -#define TXT_ATTR_FG_TERMCAP 0x1000 -/* Flag to use termcap AB sequence to set colour, if available */ -#define TXT_ATTR_BG_TERMCAP 0x2000 - /* Flag to indicate that foreground is a 24-bit colour */ #define TXT_ATTR_FG_24BIT 0x4000 /* Flag to indicate that background is a 24-bit colour */ @@ -2696,16 +2738,15 @@ struct ttyinfo { /* Things to turn on, including values for the colour elements */ #define TXT_ATTR_ON_VALUES_MASK \ (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\ - TXT_ATTR_FG_TERMCAP|TXT_ATTR_BG_TERMCAP|\ TXT_ATTR_FG_24BIT|TXT_ATTR_BG_24BIT) /* Mask out everything to do with setting a foreground colour */ #define TXT_ATTR_FG_ON_MASK \ - (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_TERMCAP|TXT_ATTR_FG_24BIT) + (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_24BIT) /* Mask out everything to do with setting a background colour */ #define TXT_ATTR_BG_ON_MASK \ - (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_TERMCAP|TXT_ATTR_BG_24BIT) + (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_24BIT) /* Mask out everything to do with activating colours */ #define TXT_ATTR_COLOUR_ON_MASK \ @@ -2971,17 +3012,18 @@ enum { SORTIT_ANYOLDHOW = 0, /* Defaults */ SORTIT_IGNORING_CASE = 1, SORTIT_NUMERICALLY = 2, - SORTIT_BACKWARDS = 4, + SORTIT_NUMERICALLY_SIGNED = 4, + SORTIT_BACKWARDS = 8, /* * Ignore backslashes that quote another character---which may * be another backslash; the second backslash is active. */ - SORTIT_IGNORING_BACKSLASHES = 8, + SORTIT_IGNORING_BACKSLASHES = 16, /* * Ignored by strmetasort(); used by paramsubst() to indicate * there is some sorting to do. */ - SORTIT_SOMEHOW = 16, + SORTIT_SOMEHOW = 32, }; /* @@ -3235,14 +3277,15 @@ enum zexit_t { #define AFTERTRAPHOOK (zshhooks + 2) #define GETCOLORATTR (zshhooks + 3) -#ifdef MULTIBYTE_SUPPORT -/* Final argument to mb_niceformat() */ +/* Final argument to [ms]b_niceformat() */ enum { NICEFLAG_HEAP = 1, /* Heap allocation where needed */ NICEFLAG_QUOTE = 2, /* Result will appear in $'...' */ NICEFLAG_NODUP = 4, /* Leave allocated */ }; +#ifdef MULTIBYTE_SUPPORT + /* Metafied input */ #define nicezputs(str, outs) (void)mb_niceformat((str), (outs), NULL, 0) #define MB_METACHARINIT() mb_charinit() diff --git a/Src/zsh.mdd b/Src/zsh.mdd index 9bcaccae5..da8d58322 100644 --- a/Src/zsh.mdd +++ b/Src/zsh.mdd @@ -13,7 +13,7 @@ objects="builtin.o compat.o cond.o context.o \ exec.o glob.o hashtable.o hashnameddir.o \ hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \ mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \ -signames.o sort.o string.o subst.o text.o utils.o watch.o \ +signames.o sort.o string.o subst.o text.o utils.o \ openssh_bsd_setres_id.o" headers="../config.h zsh_system.h zsh.h sigcount.h signals.h \ diff --git a/Src/zsh_system.h b/Src/zsh_system.h index 161b073b4..6f4efce96 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -235,16 +235,8 @@ char *alloca _((size_t)); # include <errno.h> #endif -#ifdef TIME_WITH_SYS_TIME -# include <sys/time.h> -# include <time.h> -#else -# ifdef HAVE_SYS_TIME_H -# include <sys/time.h> -# else -# include <time.h> -# endif -#endif +#include <sys/time.h> +#include <time.h> /* This is needed by some old SCO unices */ #if !defined(HAVE_STRUCT_TIMEZONE) && !defined(ZSH_OOT_MODULE) @@ -279,16 +271,7 @@ struct timespec { # include <sys/times.h> #endif -#if STDC_HEADERS || HAVE_STRING_H # include <string.h> -/* An ANSI string.h and pre-ANSI memory.h might conflict. */ -# if !STDC_HEADERS && HAVE_MEMORY_H -# include <memory.h> -# endif /* not STDC_HEADERS and HAVE_MEMORY_H */ -#else /* not STDC_HEADERS and not HAVE_STRING_H */ -# include <strings.h> -/* memory.h and strings.h conflict on some systems. */ -#endif /* not STDC_HEADERS and not HAVE_STRING_H */ #ifdef HAVE_LOCALE_H # include <locale.h> |