diff options
Diffstat (limited to 'Src/Modules')
-rw-r--r-- | Src/Modules/curses.c | 104 | ||||
-rw-r--r-- | Src/Modules/datetime.c | 8 | ||||
-rw-r--r-- | Src/Modules/db_gdbm.c | 585 | ||||
-rw-r--r-- | Src/Modules/db_gdbm.mdd | 2 | ||||
-rw-r--r-- | Src/Modules/example.c | 3 | ||||
-rw-r--r-- | Src/Modules/parameter.c | 132 | ||||
-rw-r--r-- | Src/Modules/pcre.c | 37 | ||||
-rw-r--r-- | Src/Modules/regex.c | 8 | ||||
-rw-r--r-- | Src/Modules/system.c | 30 | ||||
-rw-r--r-- | Src/Modules/tcp.c | 3 | ||||
-rw-r--r-- | Src/Modules/zftp.c | 2 | ||||
-rw-r--r-- | Src/Modules/zpty.c | 16 | ||||
-rw-r--r-- | Src/Modules/zutil.c | 20 |
13 files changed, 794 insertions, 156 deletions
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index 63c6748f5..a60dfcbf8 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -1082,15 +1082,7 @@ zccmd_input(const char *nam, char **args) #endif /* - * Some documentation for wgetch() says: - - The behavior of getch and friends in the presence of handled signals - is unspecified in the SVr4 and XSI Curses documentation. Under his- - torical curses implementations, it varied depending on whether the - operating system's implementation of handled signal receipt interrupts - a read(2) call in progress or not, and also (in some implementations) - depending on whether an input timeout or non-blocking mode has been - set. + * Linux, OS X, FreeBSD documentation for wgetch() mentions: Programmers concerned about portability should be prepared for either of two cases: (a) signal receipt does not interrupt getch; (b) signal @@ -1098,21 +1090,16 @@ zccmd_input(const char *nam, char **args) EINTR. Under the ncurses implementation, handled signals never inter- rupt getch. - * The observed behavior, however, is different: wgetch() consistently - * returns ERR with EINTR when a signal is handled by the shell "trap" - * command mechanism. Further, it consistently returns ERR twice, the - * second time without even attempting to repeat the interrupted read, - * which has the side-effect of NOT updating errno. A third call will - * then begin reading again. - * - * Therefore, to properly implement signal trapping, we must (1) call - * wgetch() in a loop as long as errno remains EINTR, and (2) clear - * errno only before beginning the loop, not on every pass. + * Some observed behavior: wgetch() returns ERR with EINTR when a signal is + * handled by the shell "trap" command mechanism. Observed that it returns + * ERR twice, the second time without even attempting to repeat the + * interrupted read. Third call will then begin reading again. * - * There remains a potential bug here in that, if the caller has set - * a timeout for the read [see zccmd_timeout()] the countdown is very - * likely restarted on every call to wgetch(), so an interrupted call - * might wait much longer than desired. + * Because of widespread of previous implementation that called wget*ch + * possibly indefinitely many times after ERR/EINTR, and because of the + * above observation, wget_wch call is repeated after each ERR/EINTR, but + * errno is being reset (it wasn't) and the loop to all means should break. + * Problem: the timeout may be waited twice. */ errno = 0; @@ -1120,6 +1107,7 @@ zccmd_input(const char *nam, char **args) while ((ret = wget_wch(w->win, &wi)) == ERR) { if (errno != EINTR || errflag || retflag || breaks || exit_pending) break; + errno = 0; } switch (ret) { case OK: @@ -1146,6 +1134,7 @@ zccmd_input(const char *nam, char **args) while ((ci = wgetch(w->win)) == ERR) { if (errno != EINTR || errflag || retflag || breaks || exit_pending) return 1; + errno = 0; } if (ci >= 256) { keypadnum = ci; @@ -1501,6 +1490,74 @@ zccmd_touch(const char *nam, char **args) return ret; } +static int +zccmd_resize(const char *nam, char **args) +{ +#ifdef HAVE_RESIZE_TERM + int y, x, do_endwin=0, do_save=1; + LinkNode stdscr_win = zcurses_getwindowbyname("stdscr"); + + if (stdscr_win) { + y = atoi(args[0]); + x = atoi(args[1]); + if (args[2]) { + if (0 == strcmp(args[2], "endwin")) { + do_endwin=1; + } else if (0 == strcmp(args[2], "endwin_nosave")) { + do_endwin=1; + do_save=0; + } else if (0 == strcmp(args[2], "nosave")) { + do_save=0; + } else { + zwarnnam(nam, "`resize' expects `endwin', `nosave' or `endwin_nosave' for third argument, if given"); + } + } + + if (y == 0 && x == 0 && args[2] == NULL) { + // Special case to just test that curses has resize_term. #ifdef + // HAVE_RESIZE_TERM will result in return value 2 if resize_term + // is not available. + return 0; + } else { + // Without this call some window moves are innacurate. Tested on + // OS X ncurses 5.4, Homebrew ncursesw 6.0-2, Arch Linux ncursesw + // 6.0, Ubuntu 14.04 ncurses 5.9, FreeBSD ncursesw.so.8 + // + // On the other hand, the whole resize goal can be (from tests) + // accomplished by calling endwin and refresh. But to secure any + // future problems, resize_term is provided, and it is featured + // with endwin, so that users have multiple options. + if (do_endwin) { + endwin(); + } + + if( resize_term( y, x ) == OK ) { + // Things work without this, but we need to get out from + // endwin (i.e. call refresh), and in theory store new + // curses state (the resize might have changed it), which + // should be presented to terminal only after refresh. + if (do_endwin || do_save) { + ZCWin w; + w = (ZCWin)getdata(stdscr_win); + wnoutrefresh(w->win); + doupdate(); + } + + if (do_save) { + gettyinfo(&curses_tty_state); + } + return 0; + } else { + return 1; + } + } + } else { + return 1; + } +#else + return 2; +#endif +} /********************* Main builtin handler @@ -1534,6 +1591,7 @@ bin_zcurses(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) {"mouse", zccmd_mouse, 0, -1}, {"querychar", zccmd_querychar, 1, 2}, {"touch", zccmd_touch, 1, -1}, + {"resize", zccmd_resize, 2, 3}, {NULL, (zccmd_t)0, 0, 0} }; diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c index bb82c542f..6e9047bc5 100644 --- a/Src/Modules/datetime.c +++ b/Src/Modules/datetime.c @@ -133,11 +133,15 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func)) len = 0; for (x=0; x < 4; x++) { - if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0) + if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0 || x==3) break; buffer = zrealloc(buffer, bufsize *= 2); } - DPUTS(len < 0, "bad output from ztrftime"); + if (len < 0) { + zwarnnam(nam, "bad/unsupported format: '%s'", argv[0]); + zfree(buffer, bufsize); + return 1; + } if (scalar) { setsparam(scalar, metafy(buffer, len, META_DUP)); diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index 8dd60fc0d..cf1322459 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -6,6 +6,9 @@ * Copyright (c) 2008 Clint Adams * All rights reserved. * + * Modifications copyright (c) 2017 Sebastian Gniazdowski + * All rights reserved. + * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and to distribute modified versions of this software for any @@ -31,6 +34,18 @@ #include "db_gdbm.mdh" #include "db_gdbm.pro" +#ifndef PM_UPTODATE +#define PM_UPTODATE (1<<19) /* Parameter has up-to-date data (e.g. loaded from DB) */ +#endif + +static Param createhash( char *name, int flags ); +static int append_tied_name( const char *name ); +static int remove_tied_name( const char *name ); +static char *unmetafy_zalloc(const char *to_copy, int *new_len); +static void myfreeparamnode(HashNode hn); + +static int no_database_action = 0; + /* * Make sure we have all the bits I'm using for memory mapping, otherwise * I don't know what I'm doing. @@ -41,8 +56,34 @@ static char *backtype = "db/gdbm"; -static const struct gsu_scalar gdbm_gsu = -{ gdbmgetfn, gdbmsetfn, gdbmunsetfn }; +/* + * Longer GSU structure, to carry GDBM_FILE of owning + * database. Every parameter (hash value) receives GSU + * pointer and thus also receives GDBM_FILE - this way + * parameters can access proper database. + * + * Main HashTable parameter has the same instance of + * the custom GSU struct in u.hash->tmpdata field. + * When database is closed, `dbf` field is set to NULL + * and hash values know to not access database when + * being unset (total purge at zuntie). + * + * When database closing is ended, custom GSU struct + * is freed. Only new ztie creates new custom GSU + * struct instance. + */ + +struct gsu_scalar_ext { + struct gsu_scalar std; /* Size of three pointers */ + GDBM_FILE dbf; + char *dbfile_path; +}; + +/* Source structure - will be copied to allocated one, + * with `dbf` filled. `dbf` allocation <-> gsu allocation. */ +static const struct gsu_scalar_ext gdbm_gsu_ext = +{ { gdbmgetfn, gdbmsetfn, gdbmunsetfn }, 0, 0 }; + /**/ static const struct gsu_hash gdbm_hash_gsu = { hashgetfn, gdbmhashsetfn, gdbmhashunsetfn }; @@ -50,12 +91,24 @@ static const struct gsu_hash gdbm_hash_gsu = static struct builtin bintab[] = { BUILTIN("ztie", 0, bin_ztie, 1, -1, 0, "d:f:r", NULL), BUILTIN("zuntie", 0, bin_zuntie, 1, -1, 0, "u", NULL), + BUILTIN("zgdbmpath", 0, bin_zgdbmpath, 1, -1, 0, "", NULL), +}; + +#define ROARRPARAMDEF(name, var) \ + { name, PM_ARRAY | PM_READONLY, (void *) var, NULL, NULL, NULL, NULL } + +/* Holds names of all tied parameters */ +char **zgdbm_tied; + +static struct paramdef patab[] = { + ROARRPARAMDEF( "zgdbm_tied", &zgdbm_tied ), }; /**/ static int bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) { + struct gsu_scalar_ext *dbf_carrier; char *resource_name, *pmname; GDBM_FILE dbf = NULL; int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE; @@ -77,8 +130,7 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) } /* Here should be a lookup of the backend type against - * a registry. - */ + * a registry, if generam DB mechanism is to be added */ if (strcmp(OPT_ARG(ops, 'd'), backtype) != 0) { zwarnnam(nam, "unsupported backend type `%s'", OPT_ARG(ops, 'd')); return 1; @@ -92,7 +144,8 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) /* * Unset any existing parameter. Note there's no implicit * "local" here, but if the existing parameter is local - * that will be reflected in the new one. + * then new parameter will be also local without following + * unset. * * We need to do this before attempting to open the DB * in case this variable is already tied to a DB. @@ -105,24 +158,40 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } + gdbm_errno=0; dbf = gdbm_open(resource_name, 0, read_write, 0666, 0); - if(dbf) - addmodulefd(gdbm_fdesc(dbf), FDT_INTERNAL); - else { - zwarnnam(nam, "error opening database file %s", resource_name); + if(dbf == NULL) { + zwarnnam(nam, "error opening database file %s (%s)", resource_name, gdbm_strerror(gdbm_errno)); return 1; } - if (!(tied_param = createspecialhash(pmname, &getgdbmnode, &scangdbmkeys, - pmflags))) { + if (!(tied_param = createhash(pmname, pmflags))) { zwarnnam(nam, "cannot create the requested parameter %s", pmname); - fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED; gdbm_close(dbf); return 1; } tied_param->gsu.h = &gdbm_hash_gsu; - tied_param->u.hash->tmpdata = (void *)dbf; + + /* Allocate parameter sub-gsu, fill dbf field. + * dbf allocation is 1 to 1 accompanied by + * gsu_scalar_ext allocation. */ + + dbf_carrier = (struct gsu_scalar_ext *) zalloc(sizeof(struct gsu_scalar_ext)); + dbf_carrier->std = gdbm_gsu_ext.std; + dbf_carrier->dbf = dbf; + tied_param->u.hash->tmpdata = (void *)dbf_carrier; + + /* Fill also file path field */ + if (*resource_name != '/') { + /* Code copied from check_autoload() */ + resource_name = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", resource_name); + resource_name = xsymlink(resource_name, 1); + } + dbf_carrier->dbfile_path = ztrdup(resource_name); + + addmodulefd(gdbm_fdesc(dbf), FDT_INTERNAL); + append_tied_name(pmname); return 0; } @@ -149,8 +218,9 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func)) } queue_signals(); - if (OPT_ISSET(ops,'u')) - gdbmuntie(pm); /* clear read-only-ness */ + if (OPT_ISSET(ops,'u')) { + pm->node.flags &= ~PM_READONLY; + } if (unsetparam_pm(pm, 0, 1)) { /* assume already reported */ ret = 1; @@ -162,25 +232,112 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func)) } /**/ +static int +bin_zgdbmpath(char *nam, char **args, Options ops, UNUSED(int func)) +{ + Param pm; + char *pmname; + + pmname = *args; + + if (!pmname) { + zwarnnam(nam, "parameter name (whose path is to be written to $REPLY) is required"); + return 1; + } + + pm = (Param) paramtab->getnode(paramtab, pmname); + if(!pm) { + zwarnnam(nam, "no such parameter: %s", pmname); + return 1; + } + + if (pm->gsu.h != &gdbm_hash_gsu) { + zwarnnam(nam, "not a tied gdbm parameter: %s", pmname); + return 1; + } + + /* Paranoia, it *will* be always set */ + if (((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbfile_path) { + setsparam("REPLY", ztrdup(((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbfile_path)); + } else { + setsparam("REPLY", ztrdup("")); + } + + return 0; +} + +/* + * The param is actual param in hash – always, because + * getgdbmnode creates every new key seen. However, it + * might be not PM_UPTODATE - which means that database + * wasn't yet queried. + * + * It will be left in this state if database doesn't + * contain such key. That might be a drawback, maybe + * setting to empty value has sense. + */ + +/**/ static char * gdbmgetfn(Param pm) { datum key, content; - int ret; + int ret, umlen; + char *umkey; GDBM_FILE dbf; - key.dptr = pm->node.nam; - key.dsize = strlen(key.dptr) + 1; + /* Key already retrieved? There is no sense of asking the + * database again, because: + * - there can be only multiple readers + * - so, no writer + reader use is allowed + * + * Thus: + * - if we are writers, we for sure have newest copy of data + * - if we are readers, we for sure have newest copy of data + */ + if ( pm->node.flags & PM_UPTODATE ) { + return pm->u.str ? pm->u.str : ""; + } + + /* Unmetafy key. GDBM fits nice into this + * process, as it uses length of data */ + umlen = 0; + umkey = unmetafy_zalloc(pm->node.nam,¨en); + + key.dptr = umkey; + key.dsize = umlen; + + dbf = ((struct gsu_scalar_ext *)pm->gsu.s)->dbf; + + if((ret = gdbm_exists(dbf, key))) { + /* We have data – store it, return it */ + pm->node.flags |= PM_UPTODATE; - dbf = (GDBM_FILE)(pm->u.hash->tmpdata); - ret = gdbm_exists(dbf, key); - if(ret) { content = gdbm_fetch(dbf, key); - } else { - content.dptr = dupstring(""); + + /* Ensure there's no leak */ + if (pm->u.str) { + zsfree(pm->u.str); + pm->u.str = NULL; + } + + /* Metafy returned data. All fits - metafy + * can obtain data length to avoid using \0 */ + pm->u.str = metafy(content.dptr, content.dsize, META_DUP); + /* gdbm allocates with malloc */ + free(content.dptr); + + /* Free key */ + zfree(umkey, umlen+1); + + /* Can return pointer, correctly saved inside hash */ + return pm->u.str; } - return content.dptr; + /* Free key */ + zfree(umkey, umlen+1); + + return ""; } /**/ @@ -190,78 +347,128 @@ gdbmsetfn(Param pm, char *val) datum key, content; GDBM_FILE dbf; - key.dptr = pm->node.nam; - key.dsize = strlen(key.dptr) + 1; - content.dptr = val; - content.dsize = strlen(content.dptr) + 1; + /* Set is done on parameter and on database. + * See the allowed workers / readers comment + * at gdbmgetfn() */ - dbf = (GDBM_FILE)(pm->u.hash->tmpdata); - (void)gdbm_store(dbf, key, content, GDBM_REPLACE); + /* Parameter */ + if (pm->u.str) { + zsfree(pm->u.str); + pm->u.str = NULL; + pm->node.flags &= ~(PM_UPTODATE); + } + + if (val) { + pm->u.str = ztrdup(val); + pm->node.flags |= PM_UPTODATE; + } + + /* Database */ + dbf = ((struct gsu_scalar_ext *)pm->gsu.s)->dbf; + if (dbf && no_database_action == 0) { + int umlen = 0; + char *umkey = unmetafy_zalloc(pm->node.nam,¨en); + + key.dptr = umkey; + key.dsize = umlen; + + if (val) { + /* Unmetafy with exact zalloc size */ + char *umval = unmetafy_zalloc(val,¨en); + + /* Store */ + content.dptr = umval; + content.dsize = umlen; + (void)gdbm_store(dbf, key, content, GDBM_REPLACE); + + /* Free */ + zfree(umval, umlen+1); + } else { + (void)gdbm_delete(dbf, key); + } + + /* Free key */ + zfree(umkey, key.dsize+1); + } } /**/ static void gdbmunsetfn(Param pm, UNUSED(int um)) { - datum key; - GDBM_FILE dbf; - - key.dptr = pm->node.nam; - key.dsize = strlen(key.dptr) + 1; - - dbf = (GDBM_FILE)(pm->u.hash->tmpdata); - (void)gdbm_delete(dbf, key); + /* Set with NULL */ + gdbmsetfn(pm, NULL); } /**/ static HashNode getgdbmnode(HashTable ht, const char *name) { - int len; - char *nameu; - Param pm = NULL; - - nameu = dupstring(name); - unmetafy(nameu, &len); - - pm = (Param) hcalloc(sizeof(struct param)); - pm->node.nam = nameu; - pm->node.flags = PM_SCALAR; - pm->gsu.s = &gdbm_gsu; - pm->u.hash = ht; + HashNode hn = gethashnode2( ht, name ); + Param val_pm = (Param) hn; + + /* Entry for key doesn't exist? Create it now, + * it will be interfacing between the database + * and Zsh - through special gdbm_gsu. So, any + * seen key results in new interfacing parameter. + * + * Previous code was returning heap arena Param + * that wasn't actually added to the hash. It was + * plainly name / database-key holder. Here we + * add the Param to its hash, it is not PM_UPTODATE. + * It will be loaded from database *and filled* + * or left in that state if the database doesn't + * contain it. + * + * No heap arena memory is used, memory usage is + * now limited - by number of distinct keys seen, + * not by number of key *uses*. + * */ + + if ( ! val_pm ) { + val_pm = (Param) zshcalloc( sizeof (*val_pm) ); + val_pm->node.flags = PM_SCALAR | PM_HASHELEM; /* no PM_UPTODATE */ + val_pm->gsu.s = (GsuScalar) ht->tmpdata; + ht->addnode( ht, ztrdup( name ), val_pm ); /* sets pm->node.nam */ + } - return &pm->node; + return (HashNode) val_pm; } /**/ static void scangdbmkeys(HashTable ht, ScanFunc func, int flags) { - Param pm = NULL; - datum key, content; - GDBM_FILE dbf = (GDBM_FILE)(ht->tmpdata); - - pm = (Param) hcalloc(sizeof(struct param)); - - pm->node.flags = PM_SCALAR; - pm->gsu.s = &nullsetscalar_gsu; + datum key, prev_key; + GDBM_FILE dbf = ((struct gsu_scalar_ext *)ht->tmpdata)->dbf; + /* Iterate keys adding them to hash, so + * we have Param to use in `func` */ key = gdbm_firstkey(dbf); while(key.dptr) { - content = gdbm_fetch(dbf, key); - - pm->node.nam = key.dptr; - pm->u.str = content.dptr; - pm->gsu.s = &nullsetscalar_gsu; - - func(&pm->node, flags); - + /* This returns database-interfacing Param, + * it will return u.str or first fetch data + * if not PM_UPTODATE (newly created) */ + char *zkey = metafy(key.dptr, key.dsize, META_DUP); + HashNode hn = getgdbmnode(ht, zkey); + zsfree( zkey ); + + func(hn, flags); + + /* Iterate - no problem as interfacing Param + * will do at most only fetches, not stores */ + prev_key = key; key = gdbm_nextkey(dbf, key); + free(prev_key.dptr); } } +/* + * Replace database with new hash + */ + /**/ static void gdbmhashsetfn(Param pm, HashTable ht) @@ -274,7 +481,7 @@ gdbmhashsetfn(Param pm, HashTable ht) if (!pm->u.hash || pm->u.hash == ht) return; - if (!(dbf = (GDBM_FILE)(pm->u.hash->tmpdata))) + if (!(dbf = ((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbf)) return; key = gdbm_firstkey(dbf); @@ -286,48 +493,78 @@ gdbmhashsetfn(Param pm, HashTable ht) key = gdbm_firstkey(dbf); } - /* just deleted everything, clean up */ - (void)gdbm_reorganize(dbf); + /* Just deleted everything, clean up if no new data. + * User can also reorganize via gdbmtool. */ + if (!ht || ht->hsize == 0) { + (void)gdbm_reorganize(dbf); + } + + no_database_action = 1; + emptyhashtable(pm->u.hash); + no_database_action = 0; if (!ht) return; - for (i = 0; i < ht->hsize; i++) + /* Put new strings into database, waiting + * for their interfacing-Params to be created */ + + for (i = 0; i < ht->hsize; i++) { for (hn = ht->nodes[i]; hn; hn = hn->next) { struct value v; + int umlen = 0; + char *umkey, *umval; v.isarr = v.flags = v.start = 0; v.end = -1; v.arr = NULL; v.pm = (Param) hn; - key.dptr = v.pm->node.nam; - key.dsize = strlen(key.dptr) + 1; + /* Unmetafy key */ + umkey = unmetafy_zalloc(v.pm->node.nam,¨en); + + key.dptr = umkey; + key.dsize = umlen; queue_signals(); - content.dptr = getstrvalue(&v); - content.dsize = strlen(content.dptr) + 1; + /* Unmetafy */ + umval = unmetafy_zalloc(getstrvalue(&v),¨en); + /* Store */ + content.dptr = umval; + content.dsize = umlen; (void)gdbm_store(dbf, key, content, GDBM_REPLACE); + /* Free - unmetafy_zalloc allocates + * exact required space + 1 null byte */ + zfree(umval, content.dsize+1); + zfree(umkey, key.dsize+1); + unqueue_signals(); } + } + /* We reuse our hash, the input is to be deleted */ + deleteparamtable(ht); } /**/ static void gdbmuntie(Param pm) { - GDBM_FILE dbf = (GDBM_FILE)(pm->u.hash->tmpdata); + GDBM_FILE dbf = ((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbf; HashTable ht = pm->u.hash; if (dbf) { /* paranoia */ fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED; - gdbm_close(dbf); - } + gdbm_close(dbf); + + /* Let hash fields know there's no backend */ + ((struct gsu_scalar_ext *)ht->tmpdata)->dbf = NULL; - ht->tmpdata = NULL; + /* Remove from list of tied parameters */ + remove_tied_name(pm->node.nam); + } /* for completeness ... createspecialhash() should have an inverse */ ht->getnode = ht->getnode2 = gethashnode2; @@ -341,21 +578,31 @@ gdbmuntie(Param pm) static void gdbmhashunsetfn(Param pm, UNUSED(int exp)) { + struct gsu_scalar_ext * gsu_ext; + gdbmuntie(pm); - /* hash table is now normal, so proceed normally... */ + + /* Remember custom GSU structure assigned to + * u.hash->tmpdata before hash gets deleted */ + gsu_ext = pm->u.hash->tmpdata; + + /* Uses normal unsetter (because gdbmuntie is called above). + * Will delete all owned field-parameters and also hashtable. */ pm->gsu.h->setfn(pm, NULL); + + /* Don't need custom GSU structure with its + * GDBM_FILE pointer anymore */ + zsfree( gsu_ext->dbfile_path ); + zfree( gsu_ext, sizeof(struct gsu_scalar_ext)); + pm->node.flags |= PM_UNSET; } -#else -# error no gdbm -#endif /* have gdbm */ - static struct features module_features = { bintab, sizeof(bintab)/sizeof(*bintab), NULL, 0, NULL, 0, - NULL, 0, + patab, sizeof(patab)/sizeof(*patab), 0 }; @@ -385,6 +632,7 @@ enables_(Module m, int **enables) int boot_(UNUSED(Module m)) { + zgdbm_tied = zshcalloc((1) * sizeof(char *)); return 0; } @@ -392,6 +640,7 @@ boot_(UNUSED(Module m)) int cleanup_(Module m) { + /* This frees `zgdbm_tied` */ return setfeatureenables(m, &module_features, NULL); } @@ -401,3 +650,169 @@ finish_(UNUSED(Module m)) { return 0; } + +/********************* + * Utility functions * + *********************/ + +static Param createhash( char *name, int flags ) { + Param pm; + HashTable ht; + + pm = createparam(name, flags | PM_SPECIAL | PM_HASHED); + if (!pm) { + return NULL; + } + + if (pm->old) + pm->level = locallevel; + + /* This creates standard hash. */ + ht = pm->u.hash = newparamtable(17, name); + if (!pm->u.hash) { + paramtab->removenode(paramtab, name); + paramtab->freenode(&pm->node); + zwarnnam(name, "out of memory when allocating hash"); + return NULL; + } + + /* Does free Param (unsetfn is called) */ + ht->freenode = myfreeparamnode; + + /* These provide special features */ + ht->getnode = ht->getnode2 = getgdbmnode; + ht->scantab = scangdbmkeys; + + return pm; +} + +/* + * Adds parameter name to `zgdbm_tied` + */ + +static int append_tied_name( const char *name ) { + int old_len = arrlen(zgdbm_tied); + char **new_zgdbm_tied = zshcalloc( (old_len+2) * sizeof(char *)); + + /* Copy */ + char **p = zgdbm_tied; + char **dst = new_zgdbm_tied; + while (*p) { + *dst++ = *p++; + } + + /* Append new one */ + *dst = ztrdup(name); + + /* Substitute, free old one */ + zfree(zgdbm_tied, sizeof(char *) * (old_len + 1)); + zgdbm_tied = new_zgdbm_tied; + + return 0; +} + +/* + * Removes parameter name from `zgdbm_tied` + */ + +static int remove_tied_name( const char *name ) { + int old_len = arrlen(zgdbm_tied); + int new_len; + + /* Two stage, to always have arrlen() == zfree-size - 1. + * Could do allocation and revert when `not found`, but + * what would be better about that. */ + + /* Find one to remove */ + char **p = zgdbm_tied; + while (*p) { + if (0==strcmp(name,*p)) { + break; + } + p++; + } + + /* Copy x+1 to x */ + while (*p) { + *p=*(p+1); + p++; + } + + /* Second stage. Size changed? Only old_size-1 + * change is possible, but.. paranoia way */ + new_len = arrlen(zgdbm_tied); + if (new_len != old_len) { + char **dst; + char **new_zgdbm_tied = zshcalloc((new_len+1) * sizeof(char *)); + + /* Copy */ + p = zgdbm_tied; + dst = new_zgdbm_tied; + while (*p) { + *dst++ = *p++; + } + *dst = NULL; + + /* Substitute, free old one */ + zfree(zgdbm_tied, sizeof(char *) * (old_len + 1)); + zgdbm_tied = new_zgdbm_tied; + } + + return 0; +} + +/* + * Unmetafy that: + * - duplicates bufer to work on it, + * - does zalloc of exact size for the new string, + * - restores work buffer to original content, to restore strlen + */ +static char * +unmetafy_zalloc(const char *to_copy, int *new_len) { + char *work, *to_return; + int my_new_len = 0; + + work = ztrdup(to_copy); + work = unmetafy(work,&my_new_len); + + if (new_len) + *new_len = my_new_len; + + /* This string can be correctly zsfree()-d */ + to_return = (char *) zalloc((my_new_len+1)*sizeof(char)); + memcpy(to_return, work, sizeof(char)*my_new_len); /* memcpy handles $'\0' */ + to_return[my_new_len]='\0'; + + /* Restore original strlen and correctly free */ + strcpy(work, to_copy); + zsfree(work); + + return to_return; +} + +static void +myfreeparamnode(HashNode hn) +{ + Param pm = (Param) hn; + + /* Upstream: The second argument of unsetfn() is used by modules to + * differentiate "exp"licit unset from implicit unset, as when + * a parameter is going out of scope. It's not clear which + * of these applies here, but passing 1 has always worked. + */ + + /* if (delunset) */ + pm->gsu.s->unsetfn(pm, 1); + + zsfree(pm->node.nam); + /* If this variable was tied by the user, ename was ztrdup'd */ + if (pm->node.flags & PM_TIED && pm->ename) { + zsfree(pm->ename); + pm->ename = NULL; + } + zfree(pm, sizeof(struct param)); +} + +#else +# error no gdbm +#endif /* have gdbm */ diff --git a/Src/Modules/db_gdbm.mdd b/Src/Modules/db_gdbm.mdd index ce7926bd9..210c22177 100644 --- a/Src/Modules/db_gdbm.mdd +++ b/Src/Modules/db_gdbm.mdd @@ -7,6 +7,6 @@ fi ' load=no -autofeatures="b:ztie b:zuntie" +autofeatures="b:ztie b:zuntie b:zgdbmpath p:zgdbm_tied" objects="db_gdbm.o" diff --git a/Src/Modules/example.c b/Src/Modules/example.c index 45ca2cffa..c80c9e7b2 100644 --- a/Src/Modules/example.c +++ b/Src/Modules/example.c @@ -69,7 +69,8 @@ bin_example(char *nam, char **args, Options ops, UNUSED(int func)) intparam = i; zsfree(strparam); strparam = ztrdup(*oargs ? *oargs : ""); - freearray(arrparam); + if (arrparam) + freearray(arrparam); arrparam = zarrdup(oargs); return 0; } diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 98bcaba6e..10c47d214 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -167,7 +167,7 @@ unsetpmcommand(Param pm, UNUSED(int exp)) /**/ static void -setpmcommands(UNUSED(Param pm), HashTable ht) +setpmcommands(Param pm, HashTable ht) { int i; HashNode hn; @@ -190,7 +190,15 @@ setpmcommands(UNUSED(Param pm), HashTable ht) cmdnamtab->addnode(cmdnamtab, ztrdup(hn->nam), &cn->node); } - deleteparamtable(ht); + /* + * On full-array assignment ht is a temporary hash with the default + * get/set functions, whereas pm->u.hash has the special $commands + * get/set functions. Do not assign ht to pm, just delete it. + * + * On append, ht and pm->u.hash are the same table, don't delete. + */ + if (ht != pm->u.hash) + deleteparamtable(ht); } static const struct gsu_scalar pmcommand_gsu = @@ -330,7 +338,7 @@ unsetpmfunction(Param pm, UNUSED(int exp)) /**/ static void -setfunctions(UNUSED(Param pm), HashTable ht, int dis) +setfunctions(Param pm, HashTable ht, int dis) { int i; HashNode hn; @@ -349,7 +357,9 @@ setfunctions(UNUSED(Param pm), HashTable ht, int dis) setfunction(hn->nam, ztrdup(getstrvalue(&v)), dis); } - deleteparamtable(ht); + /* See setpmcommands() above */ + if (ht != pm->u.hash) + deleteparamtable(ht); } /**/ @@ -515,6 +525,98 @@ scanpmdisfunctions(HashTable ht, ScanFunc func, int flags) scanfunctions(ht, func, flags, DISABLED); } +/* Functions for the functions_source special parameter. */ + +/* Retrieve the source file for a function by explicit name */ + +/**/ +static HashNode +getfunction_source(UNUSED(HashTable ht), const char *name, int dis) +{ + Shfunc shf; + Param pm = NULL; + + pm = (Param) hcalloc(sizeof(struct param)); + pm->node.nam = dupstring(name); + pm->node.flags = PM_SCALAR|PM_READONLY; + pm->gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu; + + if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) && + (dis ? (shf->node.flags & DISABLED) : !(shf->node.flags & DISABLED))) { + pm->u.str = getshfuncfile(shf); + if (!pm->u.str) + pm->u.str = dupstring(""); + } + return &pm->node; +} + +/* Retrieve the source file for functions by scanning the table */ + +/**/ +static void +scanfunctions_source(UNUSED(HashTable ht), ScanFunc func, int flags, int dis) +{ + struct param pm; + int i; + HashNode hn; + + memset((void *)&pm, 0, sizeof(struct param)); + pm.node.flags = PM_SCALAR|PM_READONLY; + pm.gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu; + + for (i = 0; i < shfunctab->hsize; i++) { + for (hn = shfunctab->nodes[i]; hn; hn = hn->next) { + if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) { + pm.node.nam = hn->nam; + if (func != scancountparams && + ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS))) { + pm.u.str = getshfuncfile((Shfunc)hn); + if (!pm.u.str) + pm.u.str = dupstring(""); + } + func(&pm.node, flags); + } + } + } +} + +/* Param table entry for retrieving functions_source element */ + +/**/ +static HashNode +getpmfunction_source(HashTable ht, const char *name) +{ + return getfunction_source(ht, name, 0); +} + +/* Param table entry for retrieving ds_functions_source element */ + +/**/ +static HashNode +getpmdisfunction_source(HashTable ht, const char *name) +{ + return getfunction_source(ht, name, 1); +} + +/* Param table entry for scanning functions_source table */ + +/**/ +static void +scanpmfunction_source(HashTable ht, ScanFunc func, int flags) +{ + scanfunctions_source(ht, func, flags, 0); +} + +/* Param table entry for scanning dis_functions_source table */ + +/**/ +static void +scanpmdisfunction_source(HashTable ht, ScanFunc func, int flags) +{ + scanfunctions_source(ht, func, flags, 1); +} + /* Functions for the funcstack special parameter. */ /**/ @@ -845,7 +947,7 @@ unsetpmoption(Param pm, UNUSED(int exp)) /**/ static void -setpmoptions(UNUSED(Param pm), HashTable ht) +setpmoptions(Param pm, HashTable ht) { int i; HashNode hn; @@ -870,7 +972,9 @@ setpmoptions(UNUSED(Param pm), HashTable ht) (val && strcmp(val, "off")), 0, opts)) zwarn("can't change option: %s", hn->nam); } - deleteparamtable(ht); + /* See setpmcommands() above */ + if (ht != pm->u.hash) + deleteparamtable(ht); } static const struct gsu_scalar pmoption_gsu = @@ -1409,7 +1513,7 @@ unsetpmnameddir(Param pm, UNUSED(int exp)) /**/ static void -setpmnameddirs(UNUSED(Param pm), HashTable ht) +setpmnameddirs(Param pm, HashTable ht) { int i; HashNode hn, next, hd; @@ -1451,7 +1555,9 @@ setpmnameddirs(UNUSED(Param pm), HashTable ht) i = opts[INTERACTIVE]; opts[INTERACTIVE] = 0; - deleteparamtable(ht); + /* See setpmcommands() above */ + if (ht != pm->u.hash) + deleteparamtable(ht); opts[INTERACTIVE] = i; } @@ -1632,7 +1738,7 @@ unsetpmsalias(Param pm, UNUSED(int exp)) /**/ static void -setaliases(HashTable alht, UNUSED(Param pm), HashTable ht, int flags) +setaliases(HashTable alht, Param pm, HashTable ht, int flags) { int i; HashNode hn, next, hd; @@ -1668,7 +1774,9 @@ setaliases(HashTable alht, UNUSED(Param pm), HashTable ht, int flags) alht->addnode(alht, ztrdup(hn->nam), createaliasnode(ztrdup(val), flags)); } - deleteparamtable(ht); + /* See setpmcommands() above */ + if (ht != pm->u.hash) + deleteparamtable(ht); } /**/ @@ -2095,6 +2203,8 @@ static struct paramdef partab[] = { NULL, getpmdisbuiltin, scanpmdisbuiltins), SPECIALPMDEF("dis_functions", 0, &pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions), + SPECIALPMDEF("dis_functions_source", PM_READONLY, NULL, + getpmdisfunction_source, scanpmdisfunction_source), SPECIALPMDEF("dis_galiases", 0, &pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases), SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY, @@ -2111,6 +2221,8 @@ static struct paramdef partab[] = { &funcstack_gsu, NULL, NULL), SPECIALPMDEF("functions", 0, &pmfunctions_gsu, getpmfunction, scanpmfunctions), + SPECIALPMDEF("functions_source", PM_READONLY, NULL, + getpmfunction_source, scanpmfunction_source), SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY, &functrace_gsu, NULL, NULL), SPECIALPMDEF("galiases", 0, diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index 5fd67963d..27191d709 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -75,7 +75,7 @@ zpcre_utf8_enabled(void) static int bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func)) { - int pcre_opts = 0, pcre_errptr; + int pcre_opts = 0, pcre_errptr, target_len; const char *pcre_error; char *target; @@ -89,15 +89,19 @@ bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func)) pcre_opts |= PCRE_UTF8; pcre_hints = NULL; /* Is this necessary? */ - + if (pcre_pattern) pcre_free(pcre_pattern); target = ztrdup(*args); - unmetafy(target, NULL); + unmetafy(target, &target_len); + + if ((int)strlen(target) != target_len) { + zwarnnam(nam, "embedded NULs in PCRE pattern terminate pattern"); + } pcre_pattern = pcre_compile(target, pcre_opts, &pcre_error, &pcre_errptr, NULL); - + free(target); if (pcre_pattern == NULL) @@ -167,7 +171,12 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar, sprintf(offset_all, "%d %d", ovec[0], ovec[1]); setsparam("ZPCRE_OP", ztrdup(offset_all)); } - match_all = metafy(captures[0], -1, META_DUP); + /* + * Result strings can contain embedded NULs; the length of each is the + * difference between the two values in each paired entry in ovec. + * ovec is length 2*(1+capture_list_length) + */ + match_all = metafy(captures[0], ovec[1] - ovec[0], META_DUP); setsparam(matchvar, match_all); /* * If we're setting match, mbegin, mend we only do @@ -176,13 +185,16 @@ zpcre_get_substrings(char *arg, int *ovec, int ret, char *matchvar, */ if (!want_begin_end || nelem) { char **x, **y; + int vec_off; y = &captures[capture_start]; matches = x = (char **) zalloc(sizeof(char *) * (arrlen(y) + 1)); + vec_off = 2; do { if (*y) - *x++ = metafy(*y, -1, META_DUP); + *x++ = metafy(*y, ovec[vec_off+1]-ovec[vec_off], META_DUP); else *x++ = NULL; + vec_off += 2; } while (*y++); setaparam(substravar, matches); } @@ -318,8 +330,7 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) ovec = zalloc(ovecsize*sizeof(int)); plaintext = ztrdup(*args); - unmetafy(plaintext, NULL); - subject_len = (int)strlen(plaintext); + unmetafy(plaintext, &subject_len); if (offset_start > 0 && offset_start >= subject_len) ret = PCRE_ERROR_NOMATCH; @@ -351,6 +362,7 @@ cond_pcre_match(char **a, int id) const char *pcre_err; char *lhstr, *rhre, *lhstr_plain, *rhre_plain, *avar=NULL; int r = 0, pcre_opts = 0, pcre_errptr, capcnt, *ov, ovsize; + int lhstr_plain_len, rhre_plain_len; int return_value = 0; if (zpcre_utf8_enabled()) @@ -362,8 +374,8 @@ cond_pcre_match(char **a, int id) rhre = cond_str(a,1,0); lhstr_plain = ztrdup(lhstr); rhre_plain = ztrdup(rhre); - unmetafy(lhstr_plain, NULL); - unmetafy(rhre_plain, NULL); + unmetafy(lhstr_plain, &lhstr_plain_len); + unmetafy(rhre_plain, &rhre_plain_len); pcre_pat = NULL; ov = NULL; ovsize = 0; @@ -373,6 +385,9 @@ cond_pcre_match(char **a, int id) switch(id) { case CPCRE_PLAIN: + if ((int)strlen(rhre_plain) != rhre_plain_len) { + zwarn("embedded NULs in PCRE pattern terminate pattern"); + } pcre_pat = pcre_compile(rhre_plain, pcre_opts, &pcre_err, &pcre_errptr, NULL); if (pcre_pat == NULL) { zwarn("failed to compile regexp /%s/: %s", rhre, pcre_err); @@ -381,7 +396,7 @@ cond_pcre_match(char **a, int id) pcre_fullinfo(pcre_pat, NULL, PCRE_INFO_CAPTURECOUNT, &capcnt); ovsize = (capcnt+1)*3; ov = zalloc(ovsize*sizeof(int)); - r = pcre_exec(pcre_pat, NULL, lhstr_plain, strlen(lhstr_plain), 0, 0, ov, ovsize); + r = pcre_exec(pcre_pat, NULL, lhstr_plain, lhstr_plain_len, 0, 0, ov, ovsize); /* r < 0 => error; r==0 match but not enough size in ov * r > 0 => (r-1) substrings found; r==1 => no substrings */ diff --git a/Src/Modules/regex.c b/Src/Modules/regex.c index edb7234d4..d02769ef0 100644 --- a/Src/Modules/regex.c +++ b/Src/Modules/regex.c @@ -111,7 +111,7 @@ zcond_regex_match(char **a, int id) *x = NULL; } if (isset(BASHREMATCH)) { - setaparam("BASH_REMATCH", arr); + assignaparam("BASH_REMATCH", arr, 0); } else { zlong offs; char *ptr; @@ -119,7 +119,7 @@ zcond_regex_match(char **a, int id) m = matches; s = metafy(lhstr + m->rm_so, m->rm_eo - m->rm_so, META_DUP); - setsparam("MATCH", s); + assignsparam("MATCH", s, 0); /* * Count the characters before the match. */ @@ -133,7 +133,7 @@ zcond_regex_match(char **a, int id) ptr += clen; leftlen -= clen; } - setiparam("MBEGIN", offs + !isset(KSHARRAYS)); + assigniparam("MBEGIN", offs + !isset(KSHARRAYS), 0); /* * Add on the characters in the match. */ @@ -144,7 +144,7 @@ zcond_regex_match(char **a, int id) ptr += clen; leftlen -= clen; } - setiparam("MEND", offs + !isset(KSHARRAYS) - 1); + assigniparam("MEND", offs + !isset(KSHARRAYS) - 1, 0); if (nelem) { char **mbegin, **mend, **bptr, **eptr; bptr = mbegin = (char **)zalloc(sizeof(char *)*(nelem+1)); diff --git a/Src/Modules/system.c b/Src/Modules/system.c index afaec262a..3eecd7e95 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -313,7 +313,7 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) int flags = O_NOCTTY | append | ((append || write) ? (read ? O_RDWR : O_WRONLY) : O_RDONLY); char *opt, *ptr, *nextopt, *fdvar; - int o, fd, explicit = -1; + int o, fd, moved_fd, explicit = -1; mode_t perms = 0666; #if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) int fdflags; @@ -376,22 +376,32 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) zwarnnam(nam, "can't open file %s: %e", *args, errno); return 1; } - fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd); - if (fd == -1) { + moved_fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd); + if (moved_fd == -1) { zwarnnam(nam, "can't open file %s", *args); return 1; } -#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) +#ifdef FD_CLOEXEC +#ifdef O_CLOEXEC + /* + * the O_CLOEXEC is a flag attached to the *file descriptor*, not the + * *open file description* so it doesn't survive a dup(). If that flag was + * requested and the fd was moved, we need to reapply it to the moved fd + * even if the original one was open with O_CLOEXEC + */ + if ((flags & O_CLOEXEC) && fd != moved_fd) +#else if (fdflags) - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif +#endif /* O_CLOEXEC */ + fcntl(moved_fd, F_SETFD, FD_CLOEXEC); +#endif /* FD_CLOEXEC */ if (explicit == -1) { - fdtable[fd] = FDT_EXTERNAL; - setiparam(fdvar, fd); - /* if setting the variable failed, close fd to avoid leak */ + fdtable[moved_fd] = FDT_EXTERNAL; + setiparam(fdvar, moved_fd); + /* if setting the variable failed, close moved_fd to avoid leak */ if (errflag) - zclose(fd); + zclose(moved_fd); } return 0; diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c index dec12142b..0bbce5d49 100644 --- a/Src/Modules/tcp.c +++ b/Src/Modules/tcp.c @@ -343,7 +343,8 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) { int herrno, err=1, destport, force=0, verbose=0, test=0, targetfd=0; ZSOCKLEN_T len; - char **addrp, *desthost, *localname, *remotename; + char **addrp, *desthost; + const char *localname, *remotename; struct hostent *zthost = NULL, *ztpeer = NULL; struct servent *srv; Tcp_session sess = NULL; diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index deed35e2f..24f4b4200 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -3177,7 +3177,7 @@ static struct features module_features = { int setup_(UNUSED(Module m)) { - return (require_module("zsh/net/tcp", NULL) == 1); + return (require_module("zsh/net/tcp", NULL, 0) == 1); } /**/ diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 0ef753915..3c1bef58f 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -331,6 +331,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) /* This code copied from the clone module, except for getting * * the descriptor from get_pty() and duplicating it to 0/1/2. */ + deletehookfunc("exit", ptyhook); clearjobtab(0); ppid = getppid(); mypid = getpid(); @@ -544,7 +545,8 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch) p = dupstring(args[1]); tokenize(p); remnulargs(p); - if (!(prog = patcompile(p, PAT_STATIC, NULL))) { + /* Signals handlers might stomp PAT_STATIC */ + if (!(prog = patcompile(p, PAT_ZDUP, NULL))) { zwarnnam(nam, "bad pattern: %s", args[1]); return 1; } @@ -682,9 +684,14 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch) write_loop(1, buf, used); } - if (seen && (!prog || matchok || !mustmatch)) - return 0; - return cmd->fin + 1; + { + int ret = cmd->fin + 1; + if (seen && (!prog || matchok || !mustmatch)) + ret = 0; + if (prog) + freepatprog(prog); + return ret; + } } static int @@ -846,6 +853,7 @@ bin_zpty(char *nam, char **args, Options ops, UNUSED(int func)) } } +/**/ static int ptyhook(UNUSED(Hookdef d), UNUSED(void *dummy)) { diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index d95c0c254..19a8306b5 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -510,25 +510,33 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) zwarnnam(nam, "too many arguments"); return 1; } + + queue_signals(); /* Protect PAT_STATIC */ + if (context) { tokenize(context); zstyle_contprog = patcompile(context, PAT_STATIC, NULL); - if (!zstyle_contprog) + if (!zstyle_contprog) { + unqueue_signals(); return 1; + } } else zstyle_contprog = NULL; if (stylename) { s = (Style)zstyletab->getnode2(zstyletab, stylename); - if (!s) + if (!s) { + unqueue_signals(); return 1; + } zstyletab->printnode(&s->node, list); } else { scanhashtable(zstyletab, 1, 0, 0, zstyletab->printnode, list); } + unqueue_signals(); return 0; } switch (args[0][1]) { @@ -675,14 +683,20 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) char **vals; Patprog prog; + queue_signals(); /* Protect PAT_STATIC */ + tokenize(args[3]); if ((vals = lookupstyle(args[1], args[2])) && (prog = patcompile(args[3], PAT_STATIC, NULL))) { while (*vals) - if (pattry(prog, *vals++)) + if (pattry(prog, *vals++)) { + unqueue_signals(); return 0; + } } + + unqueue_signals(); return 1; } break; |