From 6ff0628182ac74bbf22dc02edcc9a148f12767db Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Sat, 12 Sep 2015 09:53:39 -0700 Subject: 36509: allocate hash table nodes with zshcalloc() to avoid garbage flag values --- Src/Modules/curses.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Src/Modules') diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index 0054aef52..64329f643 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -370,7 +370,7 @@ zcurses_colorget(const char *nam, char *colorpair) return NULL; } - cpn = (Colorpairnode)zalloc(sizeof(struct colorpairnode)); + cpn = (Colorpairnode)zshcalloc(sizeof(struct colorpairnode)); if (!cpn) { zsfree(cp); @@ -462,7 +462,7 @@ zccmd_init(const char *nam, char **args) use_default_colors(); #endif /* Initialise the default color pair, always 0 */ - cpn = (Colorpairnode)zalloc(sizeof(struct colorpairnode)); + cpn = (Colorpairnode)zshcalloc(sizeof(struct colorpairnode)); if (cpn) { cpn->colorpair = 0; addhashnode(zcurses_colorpairs, -- cgit v1.2.3 From c2254cfe2c9b4bace38035ec95e4bad4ef46c9f5 Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Sun, 27 Sep 2015 12:43:03 -0700 Subject: 36661: replace obsolete "autobins" setting with "autofeatures" --- ChangeLog | 5 +++++ Src/Modules/curses.mdd | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 05c163014..13a62fdfa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2015-09-27 Barton E. Schaefer + + * 36661: Src/Modules/curses.mdd: replace obsolete "autobins" + setting with "autofeatures" + 2015-09-28 Jun-ichi Takimoto * 36631: Completion/Unix/Command/_sh, diff --git a/Src/Modules/curses.mdd b/Src/Modules/curses.mdd index 669c4f5c7..80c8f867b 100644 --- a/Src/Modules/curses.mdd +++ b/Src/Modules/curses.mdd @@ -2,7 +2,7 @@ name=zsh/curses link='if test "x$ac_cv_func_initscr" = xyes && test "x$zsh_cv_path_curses_header" != x; then echo dynamic; else echo no; fi' load=no -autobins="zcurses" +autofeatures="b:zcurses" objects="curses.o" -- cgit v1.2.3 From 1f6dcf9bcdade41886e36826196e79502a9afa26 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sat, 24 Oct 2015 20:24:35 +0100 Subject: 36941: Mark file descripors in ztcp as used. Allow such file descriptors to be either internal and closed on exec or external and so managed explicitly by module. --- ChangeLog | 6 ++++++ Src/Modules/tcp.c | 7 ++++++- Src/utils.c | 26 ++++++++++++++++++++++++++ Src/zsh.h | 15 +++++++++++---- 4 files changed, 49 insertions(+), 5 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 84f1b9856..356e92319 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2015-10-24 Peter Stephenson + + * 36941: Src/utils.c, Src/zsh.h, Src/Modules/tcp.c: ensure shell + knows about file descriptiors used by ztcp and allow such file + descriptors to be marked as internal if needed (not yet used). + 2015-10-22 Daniel Shahaf * unposted: Doc/Zsh/contrib.yo: vcs_info docs: Typo fix. diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c index bc1765da1..274f01ffc 100644 --- a/Src/Modules/tcp.c +++ b/Src/Modules/tcp.c @@ -236,6 +236,8 @@ tcp_socket(int domain, int type, int protocol, int ztflags) if (!sess) return NULL; sess->fd = socket(domain, type, protocol); + /* We'll check failure and tidy up in caller */ + addmodulefd(sess->fd, FALSE); return sess; } @@ -298,7 +300,7 @@ tcp_close(Tcp_session sess) { if (sess->fd != -1) { - err = close(sess->fd); + err = zclose(sess->fd); if (err) zwarn("connection close failed: %e", errno); } @@ -546,6 +548,9 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } + /* redup expects fd is already registered */ + addmodulefd(rfd, FALSE); + if (targetfd) { sess->fd = redup(rfd, targetfd); if (sess->fd < 0) { diff --git a/Src/utils.c b/Src/utils.c index 61cbe84aa..4c69d75a1 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1891,6 +1891,32 @@ redup(int x, int y) return ret; } +/* + * Add an fd opened ithin a module. + * + * If is_internal is FALSE, the fd can be used within the shell for + * normal I/O but it will not be closed automatically or by normal shell + * syntax; it can only be closed by the module (which should included + * zclose() as part of the sequence). + * + * If is_internal is TRUE the fd is treated like others created by the + * shell for internall use; it can be closed and will be closed by the + * shell if it exec's or performs an exec with a fork optimised out. + *. + * Safe if fd is -1 to indicate failure. + */ +/**/ +mod_export void +addmodulefd(int fd, bool is_internal) +{ + if (fd >= 0) { + check_fd_table(fd); + fdtable[fd] = is_internal ? FDT_INTERNAL : FDT_MODULE; + } +} + +/**/ + /* * Indicate that an fd has a file lock; if cloexec is 1 it will be closed * on exec. diff --git a/Src/zsh.h b/Src/zsh.h index 15fa5e417..f819249c3 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -405,26 +405,33 @@ enum { * the {varid}> file syntax. */ #define FDT_EXTERNAL 2 +/* + * Entry visible to other processes but controlled by a module. + * The difference from FDT_EXTERNAL is that closing this using + * standard fd syntax will fail as there is some tidying up that + * needs to be done by the module's own mechanism. + */ +#define FDT_MODULE 3 /* * Entry used by output from the XTRACE option. */ -#define FDT_XTRACE 3 +#define FDT_XTRACE 4 /* * Entry used for file locking. */ -#define FDT_FLOCK 4 +#define FDT_FLOCK 5 /* * As above, but the fd is not marked for closing on exec, * so the shell can still exec the last process. */ -#define FDT_FLOCK_EXEC 5 +#define FDT_FLOCK_EXEC 6 #ifdef PATH_DEV_FD /* * Entry used by a process substition. * This marker is not tested internally as we associated the file * descriptor with a job for closing. */ -#define FDT_PROC_SUBST 6 +#define FDT_PROC_SUBST 7 #endif /* Flags for input stack */ -- cgit v1.2.3 From 15490398d7553605c422d8ab0e59e69e25111df4 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sat, 24 Oct 2015 20:48:47 +0100 Subject: 36944: extend fd management to zsocket --- ChangeLog | 4 ++++ Doc/Zsh/mod_socket.yo | 5 +++++ Src/Modules/socket.c | 17 +++++++++++++++-- Src/Modules/tcp.c | 4 ++-- Src/utils.c | 25 +++++++++++++++---------- 5 files changed, 41 insertions(+), 14 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 356e92319..71f15bb0d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2015-10-24 Peter Stephenson + * 36944: Src/utils.c, Src/Modules/tcp.c, Src/Modules/socket.c: + extend the previous to zsocket, although this needs to allow the + fd to be closed explicitly. + * 36941: Src/utils.c, Src/zsh.h, Src/Modules/tcp.c: ensure shell knows about file descriptiors used by ztcp and allow such file descriptors to be marked as internal if needed (not yet used). diff --git a/Doc/Zsh/mod_socket.yo b/Doc/Zsh/mod_socket.yo index 332c551fe..867f6081f 100644 --- a/Doc/Zsh/mod_socket.yo +++ b/Doc/Zsh/mod_socket.yo @@ -28,6 +28,11 @@ will be taken as the target file descriptor for the connection. In order to elicit more verbose output, use tt(-v). + +File descriptors can be closed with normal shell syntax when no longer +needed, for example: + +example(exec {REPLY}>&-) ) enditem() diff --git a/Src/Modules/socket.c b/Src/Modules/socket.c index 65b87d7dd..f683496df 100644 --- a/Src/Modules/socket.c +++ b/Src/Modules/socket.c @@ -115,6 +115,8 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } + addmodulefd(sfd, FDT_EXTERNAL); + if (targetfd) { sfd = redup(sfd, targetfd); } @@ -127,6 +129,9 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } + /* allow to be closed explicitly */ + fdtable[sfd] = FDT_EXTERNAL; + setiparam("REPLY", sfd); if (verbose) @@ -200,12 +205,16 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } + addmodulefd(rfd, FDT_EXTERNAL); + if (targetfd) { sfd = redup(rfd, targetfd); if (sfd < 0) { zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno); + zclose(rfd); return 1; } + fdtable[sfd] = FDT_EXTERNAL; } else { sfd = rfd; @@ -240,12 +249,16 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) } else { + addmodulefd(sfd, FDT_EXTERNAL); + if (targetfd) { - sfd = redup(sfd, targetfd); - if (sfd < 0) { + if (redup(sfd, targetfd) < 0) { zerrnam(nam, "could not duplicate socket fd to %d: %e", targetfd, errno); + zclose(sfd); return 1; } + sfd = targetfd; + fdtable[sfd] = FDT_EXTERNAL; } setiparam("REPLY", sfd); diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c index 274f01ffc..7b0dcc74a 100644 --- a/Src/Modules/tcp.c +++ b/Src/Modules/tcp.c @@ -237,7 +237,7 @@ tcp_socket(int domain, int type, int protocol, int ztflags) sess->fd = socket(domain, type, protocol); /* We'll check failure and tidy up in caller */ - addmodulefd(sess->fd, FALSE); + addmodulefd(sess->fd, FDT_MODULE); return sess; } @@ -549,7 +549,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) } /* redup expects fd is already registered */ - addmodulefd(rfd, FALSE); + addmodulefd(rfd, FDT_MODULE); if (targetfd) { sess->fd = redup(rfd, targetfd); diff --git a/Src/utils.c b/Src/utils.c index 4c69d75a1..37a02c593 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1894,24 +1894,29 @@ redup(int x, int y) /* * Add an fd opened ithin a module. * - * If is_internal is FALSE, the fd can be used within the shell for - * normal I/O but it will not be closed automatically or by normal shell - * syntax; it can only be closed by the module (which should included - * zclose() as part of the sequence). + * fdt is the type of the fd; see the FDT_ definitions in zsh.h. + * The most likely falures are: + * + * FDT_EXTERNAL: the fd can be used within the shell for normal I/O but + * it will not be closed automatically or by normal shell syntax. + * + * FDT_MODULE: as FDT_EXTERNAL, but it can only be closed by the module + * (which should included zclose() as part of the sequence, not by + * the standard shell syntax for closing file descriptors. + * + * FDT_INTERNAL: fd is treated like others created by the shell for + * internall use; it can be closed and will be closed by the shell if it + * exec's or performs an exec with a fork optimised out. * - * If is_internal is TRUE the fd is treated like others created by the - * shell for internall use; it can be closed and will be closed by the - * shell if it exec's or performs an exec with a fork optimised out. - *. * Safe if fd is -1 to indicate failure. */ /**/ mod_export void -addmodulefd(int fd, bool is_internal) +addmodulefd(int fd, int fdt) { if (fd >= 0) { check_fd_table(fd); - fdtable[fd] = is_internal ? FDT_INTERNAL : FDT_MODULE; + fdtable[fd] = fdt; } } -- cgit v1.2.3 From 609273875d3fb911c20806aeb7c4d558e698a7f2 Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Mon, 26 Oct 2015 07:25:51 -0700 Subject: 36968: use addmodulefd() to tell the shell about the descriptor of the dbm file --- ChangeLog | 3 +++ Src/Modules/db_gdbm.c | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index a55439df4..df70cbd45 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2015-10-26 Barton E. Schaefer + * 36968: Src/Modules/db_gdbm.c: use addmodulefd() to tell the + shell about the descriptor of the dbm file + * 36956: Src/mem.c: revert 34451, mmap() is too slow on MacOS 2015-10-26 Peter Stephenson diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index 76d4751bf..032963262 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -106,7 +106,9 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) } dbf = gdbm_open(resource_name, 0, read_write, 0666, 0); - if(!dbf) { + if(dbf) + addmodulefd(gdbm_fdesc(dbf), FDT_INTERNAL); + else { zwarnnam(nam, "error opening database file %s", resource_name); return 1; } @@ -114,6 +116,7 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) if (!(tied_param = createspecialhash(pmname, &getgdbmnode, &scangdbmkeys, pmflags))) { zwarnnam(nam, "cannot create the requested parameter %s", pmname); + fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED; gdbm_close(dbf); return 1; } @@ -319,8 +322,10 @@ gdbmuntie(Param pm) GDBM_FILE dbf = (GDBM_FILE)(pm->u.hash->tmpdata); HashTable ht = pm->u.hash; - if (dbf) /* paranoia */ + if (dbf) { /* paranoia */ + fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED; gdbm_close(dbf); + } ht->tmpdata = NULL; -- cgit v1.2.3 From d45a68c5463224f43721371fb7bd4cbc24e62874 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 27 Oct 2015 09:28:36 +0000 Subject: 36974: fix some functions with empty argument lists --- ChangeLog | 6 ++++++ Src/Modules/zftp.c | 8 ++++---- Src/Zle/complist.c | 6 +++--- Src/Zle/computil.c | 4 ++-- Src/Zle/zle_thingy.c | 2 +- Src/parse.c | 2 +- 6 files changed, 17 insertions(+), 11 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index bdd27871d..846b57ef7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2015-10-27 Peter Stephenson + + * 36974: Src/Modules/zftp.c, Src/Zle/complist.c, + Src/Zle/computil.c, Src/Zle/zle_thingy.c, Src/parse.c: fix empty + argument lists. + 2015-10-26 Barton E. Schaefer * 36906: Kamil Dudka : Src/mem.c: diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index bd51512f9..b4081df5f 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -409,7 +409,7 @@ zfalarm(int tmout) /**/ static void -zfpipe() +zfpipe(void) { /* Just ignore SIGPIPE and rely on getting EPIPE from the write. */ signal(SIGPIPE, SIG_IGN); @@ -450,7 +450,7 @@ zfunalarm(void) /**/ static void -zfunpipe() +zfunpipe(void) { if (sigtrapped[SIGPIPE]) { if (siglists[SIGPIPE] || (sigtrapped[SIGPIPE] & ZSIG_FUNC)) @@ -1298,7 +1298,7 @@ zfstarttrans(char *nam, int recv, off_t sz) /**/ static void -zfendtrans() +zfendtrans(void) { zfunsetparam("ZFTP_SIZE"); zfunsetparam("ZFTP_FILE"); @@ -2834,7 +2834,7 @@ newsession(char *nm) /* Save the existing session: this just means saving the parameters. */ static void -savesession() +savesession(void) { char **ps, **pd, *val; diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 986ad31ea..29aaee82a 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -499,7 +499,7 @@ filecol(char *col) */ static void -getcols() +getcols(void) { char *s; int i, l; @@ -602,7 +602,7 @@ zcoff(void) static void -initiscol() +initiscol(void) { int i; @@ -1909,7 +1909,7 @@ singlecalc(int *cp, int l, int *lcp) } static void -singledraw() +singledraw(void) { Cmgroup g; int mc1, mc2, ml1, ml2, md1, md2, mcc1, mcc2, lc1, lc2, t1, t2; diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index db8db88a6..60fb096be 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -185,7 +185,7 @@ cd_group(int maxg) * descriptions. */ static void -cd_calc() +cd_calc(void) { Cdset set; Cdstr str; @@ -236,7 +236,7 @@ cd_sort(const void *a, const void *b) } static int -cd_prep() +cd_prep(void) { Cdrun run, *runp; Cdset set; diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index c6ef8e6a0..271fd8efc 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -626,7 +626,7 @@ bin_zle_complete(char *name, char **args, UNUSED(Options ops), UNUSED(char func) /**/ static int -zle_usable() +zle_usable(void) { return zleactive && !incompctlfunc && !incompfunc #if 0 diff --git a/Src/parse.c b/Src/parse.c index a26df6f0a..83ba396b0 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -530,7 +530,7 @@ empty_eprog(Eprog p) } static void -clear_hdocs() +clear_hdocs(void) { struct heredocs *p, *n; -- cgit v1.2.3 From 0628802baf5c9245138db82dd058cad023a7d0ae Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 29 Oct 2015 15:01:07 +0000 Subject: 37014: Improved internal parameter setting. Enhance WARNCREATEGLOBAL to work in many more cases. Don't create REPLY as an integer if it didn't previously exist as one, even if the value to be set is integral, as this is likely to mess up later uses of REPLY. --- ChangeLog | 11 +++++++++ Functions/MIME/zsh-mime-setup | 2 +- Functions/Misc/add-zsh-hook | 2 ++ Src/Modules/socket.c | 6 ++--- Src/Modules/tcp.c | 6 ++--- Src/Modules/zpty.c | 2 +- Src/builtin.c | 38 ++++++++++++++++++------------- Src/params.c | 53 +++++++++++++++++++++++++++++++++++++++++-- Src/zsh.h | 3 --- 9 files changed, 94 insertions(+), 29 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 0c38b2697..9454013cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2015-10-29 Peter Stephenson + + * 37014: Functions/MIME/zsh-mime-setup, + Functions/Misc/add-zsh-hook, Src/Modules/socket.c, + Src/Modules/tcp.c, Src/Modules/zpty.c, Src/builtin.c, + Src/params.c,Src/zsh.h: improved internal parameter setting. + Enhance WARNCREATEGLOBAL to work in many more cases. Don't + create REPLY as an integer if it didn't previously exist as one, + even if the value to be set is integral, as this is likely to + mess up later uses of REPLY. + 2015-10-29 Jun-ichi Takimoto * 36983 (with fix from 36990): Completion/Unix/Command/_head, diff --git a/Functions/MIME/zsh-mime-setup b/Functions/MIME/zsh-mime-setup index 23e44fdc0..35f6e6b6b 100644 --- a/Functions/MIME/zsh-mime-setup +++ b/Functions/MIME/zsh-mime-setup @@ -1,7 +1,7 @@ emulate -L zsh setopt extendedglob cbases -local opt o_verbose o_list +local opt o_verbose o_list i autoload -Uz zsh-mime-handler diff --git a/Functions/Misc/add-zsh-hook b/Functions/Misc/add-zsh-hook index ee37d674d..fc39659ae 100644 --- a/Functions/Misc/add-zsh-hook +++ b/Functions/Misc/add-zsh-hook @@ -82,9 +82,11 @@ if (( del )); then else if (( ${(P)+hook} )); then if (( ${${(P)hook}[(I)$fn]} == 0 )); then + typeset -ga $hook set -A $hook ${(P)hook} $fn fi else + typeset -ga $hook set -A $hook $fn fi autoload $autoopts -- $fn diff --git a/Src/Modules/socket.c b/Src/Modules/socket.c index f683496df..7c3fb5ebe 100644 --- a/Src/Modules/socket.c +++ b/Src/Modules/socket.c @@ -132,7 +132,7 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) /* allow to be closed explicitly */ fdtable[sfd] = FDT_EXTERNAL; - setiparam("REPLY", sfd); + setiparam_no_convert("REPLY", (zlong)sfd); if (verbose) printf("%s listener is on fd %d\n", soun.sun_path, sfd); @@ -220,7 +220,7 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) sfd = rfd; } - setiparam("REPLY", sfd); + setiparam_no_convert("REPLY", (zlong)sfd); if (verbose) printf("new connection from %s is on fd %d\n", soun.sun_path, sfd); @@ -261,7 +261,7 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) fdtable[sfd] = FDT_EXTERNAL; } - setiparam("REPLY", sfd); + setiparam_no_convert("REPLY", (zlong)sfd); if (verbose) printf("%s is now on fd %d\n", soun.sun_path, sfd); diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c index 7b0dcc74a..9fc1b29a2 100644 --- a/Src/Modules/tcp.c +++ b/Src/Modules/tcp.c @@ -461,7 +461,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } - setiparam("REPLY", sess->fd); + setiparam_no_convert("REPLY", (zlong)sess->fd); if (verbose) printf("%d listener is on fd %d\n", ntohs(sess->sock.in.sin_port), sess->fd); @@ -562,7 +562,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) sess->fd = rfd; } - setiparam("REPLY", sess->fd); + setiparam_no_convert("REPLY", (zlong)sess->fd); if (verbose) printf("%d is on fd %d\n", ntohs(sess->peer.in.sin_port), sess->fd); @@ -681,7 +681,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) } } - setiparam("REPLY", sess->fd); + setiparam_no_convert("REPLY", (zlong)sess->fd); if (verbose) printf("%s:%d is now on fd %d\n", diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 9741ee287..3b8366076 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -464,7 +464,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) #endif errno == EINTR)); - setiparam("REPLY", master); + setiparam_no_convert("REPLY", (zlong)master); return 0; } diff --git a/Src/builtin.c b/Src/builtin.c index 97022addf..8045bc8f7 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2090,7 +2090,9 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), tc = 0; /* but don't do a normal conversion */ } } else if (!setsecondstype(pm, on, off)) { - if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + if (asg->value.scalar && + !(pm = assignsparam( + pname, ztrdup(asg->value.scalar), 0))) return NULL; usepm = 1; err = 0; @@ -2202,12 +2204,13 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } else if (pm->env && !(pm->node.flags & PM_HASHELEM)) delenv(pm); DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected"); - if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + if (asg->value.scalar && + !(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0))) return NULL; } else if (asg->is_array) { - if (!(pm = setaparam(pname, asg->value.array ? + if (!(pm = assignaparam(pname, asg->value.array ? zlinklist2array(asg->value.array) : - mkarray(NULL)))) + mkarray(NULL), 0))) return NULL; } pm->node.flags |= (on & PM_READONLY); @@ -2347,16 +2350,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * creating a stray parameter along the way via createparam(), * now called below in the isident() branch. */ - if (!(pm = setsparam(pname, ztrdup(asg->value.scalar ? asg->value.scalar : "")))) + if (!(pm = assignsparam( + pname, + ztrdup(asg->value.scalar ? asg->value.scalar : ""), 0))) return NULL; dont_set = 1; asg->is_array = 0; keeplocal = 0; on = pm->node.flags; } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) { - if (!(pm = setaparam(pname, asg->value.array ? - zlinklist2array(asg->value.array) : - mkarray(NULL)))) + if (!(pm = assignaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL), 0))) return NULL; dont_set = 1; keeplocal = 0; @@ -2433,13 +2438,13 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), Param ipm = pm; if (pm->node.flags & (PM_ARRAY|PM_HASHED)) { DPUTS(!ASG_ARRAYP(asg), "BUG: inconsistent scalar value for array"); - if (!(pm=setaparam(pname, asg->value.array ? - zlinklist2array(asg->value.array) : - mkarray(NULL)))) + if (!(pm=assignaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL), 0))) return NULL; } else { DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar"); - if (!(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + if (!(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0))) return NULL; } if (pm != ipm) { @@ -2687,9 +2692,10 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* Update join character */ tdp->joinchar = joinchar; if (asg0.value.scalar) - setsparam(asg0.name, ztrdup(asg0.value.scalar)); + assignsparam(asg0.name, ztrdup(asg0.value.scalar), 0); else if (asg->value.array) - setaparam(asg->name, zlinklist2array(asg->value.array)); + assignaparam( + asg->name, zlinklist2array(asg->value.array), 0); return 0; } else { zwarnnam(name, "can't tie already tied scalar: %s", @@ -2750,9 +2756,9 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) zsfree(apm->ename); apm->ename = ztrdup(asg0.name); if (asg->value.array) - setaparam(asg->name, zlinklist2array(asg->value.array)); + assignaparam(asg->name, zlinklist2array(asg->value.array), 0); else if (oldval) - setsparam(asg0.name, oldval); + assignsparam(asg0.name, oldval, 0); unqueue_signals(); return 0; diff --git a/Src/params.c b/Src/params.c index a8abb289e..4d33660fb 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2826,6 +2826,15 @@ assignsparam(char *s, char *val, int flags) return v->pm; } +/**/ +mod_export Param +setsparam(char *s, char *val) +{ + return assignsparam( + s, val, isset(WARNCREATEGLOBAL) && locallevel > 0 ? + ASSPM_WARN_CREATE : 0); +} + /**/ mod_export Param assignaparam(char *s, char **val, int flags) @@ -2914,6 +2923,16 @@ assignaparam(char *s, char **val, int flags) return v->pm; } + +/**/ +mod_export Param +setaparam(char *s, char **aval) +{ + return assignaparam( + s, aval, isset(WARNCREATEGLOBAL) && locallevel >0 ? + ASSPM_WARN_CREATE : 0); +} + /**/ mod_export Param sethparam(char *s, char **val) @@ -2937,11 +2956,15 @@ sethparam(char *s, char **val) if (unset(EXECOPT)) return NULL; queue_signals(); - if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) + if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) { createparam(t, PM_HASHED); - else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) && + if (isset(WARNCREATEGLOBAL) && locallevel > 0 && v->pm->level == 0) + zwarn("associative array parameter %s created globally in function", + v->pm->node.nam); + } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) && !(v->pm->node.flags & PM_SPECIAL)) { unsetparam(t); + /* no WARNCREATEGLOBAL check here as parameter already existed */ createparam(t, PM_HASHED); v = NULL; } @@ -2968,6 +2991,7 @@ setnparam(char *s, mnumber val) Value v; char *t = s, *ss; Param pm; + int was_unset = 0; if (!isident(s)) { zerr("not an identifier: %s", s); @@ -2987,6 +3011,7 @@ setnparam(char *s, mnumber val) */ unset(KSHARRAYS) && !ss) { unsetparam_pm(v->pm, 0, 1); + was_unset = 1; s = t; v = NULL; } @@ -3007,6 +3032,10 @@ setnparam(char *s, mnumber val) } v = getvalue(&vbuf, &t, 1); DPUTS(!v, "BUG: value not found for new parameter"); + if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > 0 && + v->pm->level == 0) + zwarn("numeric parameter %s created globally in function", + v->pm->node.nam); } setnumvalue(v, val); unqueue_signals(); @@ -3025,6 +3054,26 @@ setiparam(char *s, zlong val) return setnparam(s, mnval); } +/* + * Set an integer parameter without forcing creation of an integer type. + * This is useful if the integer is going to be set to a parmaeter which + * would usually be scalar but may not exist. + */ + +/**/ +mod_export Param +setiparam_no_convert(char *s, zlong val) +{ + /* + * If the target is already an integer, thisgets converted + * back. Low technology rules. + */ + char buf[BDIGBUFSIZE]; + convbase(buf, val, 10); + return assignsparam( + s, ztrdup(buf), + isset(WARNCREATEGLOBAL) && locallevel > 0 ? ASSPM_WARN_CREATE : 0); +} /* Unset a parameter */ diff --git a/Src/zsh.h b/Src/zsh.h index f819249c3..d03d171e4 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1939,9 +1939,6 @@ struct paramdef { { name, flags | PM_SPECIAL | PM_HIDE | PM_HIDEVAL, \ NULL, gsufn, getfn, scanfn, NULL } -#define setsparam(S,V) assignsparam(S,V,0) -#define setaparam(S,V) assignaparam(S,V,0) - /* * Flags for assignsparam and assignaparam. */ -- cgit v1.2.3 From d3d5325d27db3a56b7caf5313617d5e84aae8fe7 Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Sun, 8 Nov 2015 16:19:37 -0800 Subject: 37081: new module zsh/param/private for private-scoped parameters in functions --- ChangeLog | 5 + Doc/Makefile.in | 4 +- Doc/Zsh/mod_private.yo | 89 +++++++ Src/Modules/param_private.c | 587 ++++++++++++++++++++++++++++++++++++++++++ Src/Modules/param_private.mdd | 7 + Test/V10private.ztst | 265 +++++++++++++++++++ 6 files changed, 955 insertions(+), 2 deletions(-) create mode 100644 Doc/Zsh/mod_private.yo create mode 100644 Src/Modules/param_private.c create mode 100644 Src/Modules/param_private.mdd create mode 100644 Test/V10private.ztst (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 110f10743..622b0dbd8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2015-11-08 Barton E. Schaefer + * 37081: Doc/Makefile.in, Doc/Zsh/mod_private.yo, + Src/Modules/param_private.c, Src/Modules/param_private.mdd, + Test/V10private.ztst: new module zsh/param/private for + private-scoped parameters in functions + * 37080: Src/builtin.c, Src/params.c: use paramtab abstraction more consistently, add explanatory comments diff --git a/Doc/Makefile.in b/Doc/Makefile.in index 7645f42f7..d5899917e 100644 --- a/Doc/Makefile.in +++ b/Doc/Makefile.in @@ -62,8 +62,8 @@ Zsh/mod_computil.yo Zsh/mod_curses.yo \ Zsh/mod_datetime.yo Zsh/mod_db_gdbm.yo Zsh/mod_deltochar.yo \ Zsh/mod_example.yo Zsh/mod_files.yo Zsh/mod_langinfo.yo \ Zsh/mod_mapfile.yo Zsh/mod_mathfunc.yo Zsh/mod_newuser.yo \ -Zsh/mod_parameter.yo Zsh/mod_pcre.yo Zsh/mod_regex.yo \ -Zsh/mod_sched.yo Zsh/mod_socket.yo \ +Zsh/mod_parameter.yo Zsh/mod_pcre.yo Zsh/mod_private.yo \ +Zsh/mod_regex.yo Zsh/mod_sched.yo Zsh/mod_socket.yo \ Zsh/mod_stat.yo Zsh/mod_system.yo Zsh/mod_tcp.yo \ Zsh/mod_termcap.yo Zsh/mod_terminfo.yo \ Zsh/mod_zftp.yo Zsh/mod_zle.yo Zsh/mod_zleparameter.yo \ diff --git a/Doc/Zsh/mod_private.yo b/Doc/Zsh/mod_private.yo new file mode 100644 index 000000000..c08da6791 --- /dev/null +++ b/Doc/Zsh/mod_private.yo @@ -0,0 +1,89 @@ +COMMENT(!MOD!zsh/param/private +Builtins for managing private-scoped parameters in function context. +!MOD!) +The tt(zsh/param/private) module is used to create parameters whose scope +is limited to the current function body, and em(not) to other functions +called by the current function. + +This module provides a single autoloaded builtin: +ifnzman() +startitem() +findex(private) +cindex(private parameter, creating) +item(tt(private) [ {tt(PLUS())|tt(-)}tt(AHUahlprtux) ] \ +[ {tt(PLUS())|tt(-)}tt(EFLRZi) [ var(n) ] ] [ var(name)[tt(=)var(value)] ... ])( +The tt(private) builtin accepts all the same options and arguments as tt(local) +(ifzman(zmanref(zshbuiltins))ifnzman(noderef(Shell Builtin Commands))) except +for the `tt(-)tt(T)' option. Tied parameters may not be made private. + +If used at the top level (outside a function scope), tt(private) creates a +normal parameter in the same manner as tt(declare) or tt(typeset). A +warning about this is printed if tt(WARN_CREATE_GLOBAL) is set +(ifzman(zmanref(zshoptions))ifnzman(noderef(Options))). Used inside a +function scope, tt(private) creates a local parameter similar to one +declared with tt(local), except having special properties noted below. + +Special parameters which expose or manipulate internal shell state, such +as tt(ARGC), tt(argv), tt(COLUMNS), tt(LINES), tt(UID), tt(EUID), tt(IFS), +tt(PROMPT), tt(RANDOM), tt(SECONDS), etc., cannot be made private unless +the `tt(-)tt(h)' option is used to hide the special meaning of the +parameter. This may change in the future. +) +enditem() + +As with other tt(typeset) equivalents, tt(private) is both a builtin and a +reserved word, so arrays may be assigned with parenthesized word list +var(name)tt(=LPAR())var(value)...tt(RPAR()) syntax. However, the reserved +word `tt(private)' is not available until tt(zsh/param/private) is loaded, +so care must be taken with order of execution and parsing for function +definitions which use tt(private). To compensate for this, the module +also adds the option `tt(-P)' to the `tt(local)' builtin to declare private +parameters. + +For example, this construction fails if tt(zsh/param/private) has not yet +been loaded when `tt(failing)' is defined: +example(bad_declaration+LPAR()RPAR() { + zmodload zsh/param/private + private array=LPAR() one two three RPAR() +}) + +This construction works because tt(local) is already a keyword, and the +module is loaded before the statement is executed: +example(good_declaration+LPAR()RPAR() { + zmodload zsh/param/private + local -P array=LPAR() one two three RPAR() +}) + +The following is usable in scripts but may have trouble with tt(autoload): +example(zmodload zsh/param/private +iffy_declaration+LPAR()RPAR() { + private array=LPAR() one two three RPAR() +}) + +The tt(private) builtin may always be used with scalar assignments and +for declarations without assignments. + +Parameters declared with tt(private) have the following properties: +ifnzman() +startitemize() +itemiz(Within the function body where it is declared, the parameter +behaves as a local, except as noted above for tied or special parameters.) +itemiz(The type of a parameter declared private cannot be changed in the +scope where it was declared, even if the parameter is unset. Thus an +array cannot be assigned to a private scalar, etc.) +itemiz(Within any other function called by the declaring function, the +private parameter does em(NOT) hide other parameters of the same name, so +for example a global parameter of the same name is visible and may be +assigned or unset. This includes calls to anonymous functions, although +that may also change in the future.) +itemiz(An exported private remains in the environment of inner scopes but +appears unset for the current shell in those scopes. Generally, exporting +private parameters should be avoided.) +enditemize() + +Note that this differs from the static scope defined by compiled languages +derived from C, in that the a new call to the same function creates a new +scope, i.e., the parameter is still associated with the call stack rather +than with the function definition. It differs from ksh `tt(typeset -S)' +because the syntax used to define the function has no bearing on whether +the parameter scope is respected. diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c new file mode 100644 index 000000000..7f9aa7921 --- /dev/null +++ b/Src/Modules/param_private.c @@ -0,0 +1,587 @@ +/* + * param_private.c - bindings for private parameter scopes + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2015 Barton E. Schaefer + * 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 + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Barton E. Schaefer or the Zsh Development + * Group be liable to any party for direct, indirect, special, incidental, or + * consequential damages arising out of the use of this software and its + * documentation, even if Barton E. Schaefer and the Zsh + * Development Group have been advised of the possibility of such damage. + * + * Barton E. Schaefer and the Zsh Development Group + * specifically disclaim any warranties, including, but not limited to, the + * implied warranties of merchantability and fitness for a particular purpose. + * The software provided hereunder is on an "as is" basis, and + * Barton E. Schaefer and the Zsh Development Group have no + * obligation to provide maintenance, support, updates, enhancements, or + * modifications. + * + */ + +#include "param_private.mdh" +#include "param_private.pro" + +struct gsu_closure { + union { + struct gsu_scalar s; + struct gsu_integer i; + struct gsu_float f; + struct gsu_array a; + struct gsu_hash h; + } u; + void *g; +}; + +const struct gsu_scalar scalar_private_gsu = +{ pps_getfn, pps_setfn, pps_unsetfn }; + +const struct gsu_integer integer_private_gsu = +{ ppi_getfn, ppi_setfn, ppi_unsetfn }; + +const struct gsu_float float_private_gsu = +{ ppf_getfn, ppf_setfn, ppf_unsetfn }; + +const struct gsu_array array_private_gsu = +{ ppa_getfn, ppa_setfn, ppa_unsetfn }; + +const struct gsu_hash hash_private_gsu = +{ pph_getfn, pph_setfn, pph_unsetfn }; + +/* + * The trick here is: + * + * bin_private() opens a new parameter scope, then calls bin_typeset(). + * + * bin_typeset() handles the usual parameter creation and error checks. + * + * makeprivate() then finds all parameters created in the new scope and + * rejects them if they can't be "promoted" to the surrounding scope. + * Otherwise it swaps out their GSU structure and promotes them so they + * will be removed when the surrounding scope ends. + * + * bin_private() then ends the current scope, which discards any of the + * parameters rejected by makeprivate(). + * + */ + +static int makeprivate_error = 0; + +static void +makeprivate(HashNode hn, UNUSED(int flags)) +{ + Param pm = (Param)hn; + if (pm->level == locallevel) { + if (pm->ename || (pm->node.flags & PM_NORESTORE) || + (pm->old && + (pm->old->level == locallevel - 1 || + ((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL && + /* typeset_single() line 2300 discards PM_REMOVABLE -- why? */ + !is_private(pm->old))))) { + zwarnnam("private", "can't change scope of existing param: %s", + pm->node.nam); + makeprivate_error = 1; + return; + } + struct gsu_closure *gsu = zhalloc(sizeof(struct gsu_closure)); + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + gsu->g = (void *)(pm->gsu.s); + gsu->u.s = scalar_private_gsu; + pm->gsu.s = (GsuScalar)gsu; + break; + case PM_INTEGER: + gsu->g = (void *)(pm->gsu.i); + gsu->u.i = integer_private_gsu; + pm->gsu.i = (GsuInteger)gsu; + break; + case PM_EFLOAT: + case PM_FFLOAT: + gsu->g = (void *)(pm->gsu.f); + gsu->u.f = float_private_gsu; + pm->gsu.f = (GsuFloat)gsu; + break; + case PM_ARRAY: + gsu->g = (void *)(pm->gsu.a); + gsu->u.a = array_private_gsu; + pm->gsu.a = (GsuArray)gsu; + break; + case PM_HASHED: + gsu->g = (void *)(pm->gsu.h); + gsu->u.h = hash_private_gsu; + pm->gsu.h = (GsuHash)gsu; + break; + default: + makeprivate_error = 1; + break; + } + /* PM_HIDE so new parameters in deeper scopes do not shadow */ + pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE); + pm->level -= 1; + } +} + +/**/ +static int +is_private(Param pm) +{ + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + if (!pm->gsu.s || pm->gsu.s->unsetfn != pps_unsetfn) + return 0; + break; + case PM_INTEGER: + if (!pm->gsu.i || pm->gsu.i->unsetfn != ppi_unsetfn) + return 0; + break; + case PM_EFLOAT: + case PM_FFLOAT: + if (!pm->gsu.f || pm->gsu.f->unsetfn != ppf_unsetfn) + return 0; + break; + case PM_ARRAY: + if (!pm->gsu.a || pm->gsu.a->unsetfn != ppa_unsetfn) + return 0; + break; + case PM_HASHED: + if (!pm->gsu.h || pm->gsu.h->unsetfn != pph_unsetfn) + return 0; + break; + default: + /* error */ + return 0; + } + return 1; +} + +static int fakelevel; + +/**/ +static int +bin_private(char *nam, char **args, LinkList assigns, Options ops, int func) +{ + int from_typeset = 1; + makeprivate_error = 0; + + if (!OPT_ISSET(ops, 'P')) + return bin_typeset(nam, args, assigns, ops, func); + else if (OPT_ISSET(ops, 'T')) { + zwarn("bad option: -T"); + return 1; + } + + if (locallevel == 0) { + if (isset(WARNCREATEGLOBAL)) + zwarnnam(nam, "invalid local scope, using globals"); + return bin_typeset("private", args, assigns, ops, func); + } + + ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */ + + queue_signals(); + fakelevel = locallevel; + startparamscope(); + from_typeset = bin_typeset("private", args, assigns, ops, func); + scanhashtable(paramtab, 0, 0, 0, makeprivate, 0); + endparamscope(); + fakelevel = 0; + unqueue_signals(); + + return makeprivate_error | from_typeset; +} + +static void +setfn_error(Param pm) +{ + pm->node.flags |= PM_UNSET; + zerr("%s: attempt to assign private in nested scope", pm->node.nam); +} + +/* + * How the GSU functions work: + * + * The getfn and setfn family compare to locallevel and then call through + * to the original getfn or setfn. This means you can't assign at a + * deeper scope to any parameter declared private unless you first declare + * it local again at the new scope. Testing locallevel in getfn is most + * likely unnecessary given the scopeprivate() wrapper installed below. + * + * The unsetfn family compare locallevel and restore the old GSU before + * calling the original unsetfn. This assures that if the old unsetfn + * wants to use its getfn or setfn, they're unconditionally present. + * + */ + +/**/ +static char * +pps_getfn(Param pm) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s); + GsuScalar gsu = (GsuScalar)(c->g); + + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return (char *) hcalloc(1); +} + +/**/ +static void +pps_setfn(Param pm, char *x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s); + GsuScalar gsu = (GsuScalar)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +pps_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s); + GsuScalar gsu = (GsuScalar)(c->g); + pm->gsu.s = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +/**/ +static zlong +ppi_getfn(Param pm) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i); + GsuInteger gsu = (GsuInteger)(c->g); + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return 0; +} + +/**/ +static void +ppi_setfn(Param pm, zlong x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i); + GsuInteger gsu = (GsuInteger)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +ppi_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i); + GsuInteger gsu = (GsuInteger)(c->g); + pm->gsu.i = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +/**/ +static double +ppf_getfn(Param pm) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f); + GsuFloat gsu = (GsuFloat)(c->g); + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return 0; +} + +/**/ +static void +ppf_setfn(Param pm, double x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f); + GsuFloat gsu = (GsuFloat)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +ppf_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f); + GsuFloat gsu = (GsuFloat)(c->g); + pm->gsu.f = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +/**/ +static char ** +ppa_getfn(Param pm) +{ + static char *nullarray = NULL; + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a); + GsuArray gsu = (GsuArray)(c->g); + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return &nullarray; +} + +/**/ +static void +ppa_setfn(Param pm, char **x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a); + GsuArray gsu = (GsuArray)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +ppa_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a); + GsuArray gsu = (GsuArray)(c->g); + pm->gsu.a = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +static HashTable emptytable; + +/**/ +static HashTable +pph_getfn(Param pm) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h); + GsuHash gsu = (GsuHash)(c->g); + if (locallevel >= pm->level) + return gsu->getfn(pm); + else + return emptytable; +} + +/**/ +static void +pph_setfn(Param pm, HashTable x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h); + GsuHash gsu = (GsuHash)(c->g); + if (locallevel == pm->level) + gsu->setfn(pm, x); + else + setfn_error(pm); +} + +/**/ +static void +pph_unsetfn(Param pm, int x) +{ + struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h); + GsuHash gsu = (GsuHash)(c->g); + pm->gsu.h = gsu; + if (locallevel <= pm->level) + gsu->unsetfn(pm, x); +} + +/* + * How visibility works: + * + * Upon entering a new function scope, we find all the private parameters + * at this locallevel. Any that we find are marked PM_UNSET. If they are + * already unset, they are also marked as PM_NORESTORE. + * + * On exit from the scope, we find the same parameters again and remove + * the PM_UNSET and PM_NORESTORE flags as appropriate. We're guaraneed + * by makeprivate() that PM_NORESTORE won't conflict with anything here. + * + */ + +static void +scopeprivate(HashNode hn, int onoff) +{ + Param pm = (Param)hn; + if (pm->level == locallevel) { + if (!is_private(pm)) + return; + if (onoff == PM_UNSET) + if (pm->node.flags & PM_UNSET) + pm->node.flags |= PM_NORESTORE; + else + pm->node.flags |= PM_UNSET; + else if (!(pm->node.flags & PM_NORESTORE)) + pm->node.flags &= ~PM_UNSET; + pm->node.flags &= ~PM_NORESTORE; + } +} + +static struct funcwrap wrapper[] = { + WRAPDEF(wrap_private) +}; + +/**/ +static int +wrap_private(Eprog prog, FuncWrap w, char *name) +{ + static int wraplevel = 0; + + if (wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) { + int owl = wraplevel; + wraplevel = locallevel; + scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET); + runshfunc(prog, w, name); + scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0); + wraplevel = owl; + return 0; + } + return 1; +} + +static HashNode (*getparamnode) _((HashTable, const char *)); + +/**/ +static HashNode +getprivatenode(HashTable ht, const char *nam) +{ + HashNode hn = getparamnode(ht, nam); + Param pm = (Param) hn; + + while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) + pm = pm->old; + return (HashNode)pm; +} + +/**/ +static HashNode +getprivatenode2(HashTable ht, const char *nam) +{ + /* getparamnode() would follow autoloads, we must not do that here */ + HashNode hn = gethashnode2(ht, nam); + Param pm = (Param) hn; + + while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) + pm = pm->old; + return (HashNode)pm; +} + +/**/ +static void +printprivatenode(HashNode hn, int printflags) +{ + Param pm = (Param) hn; + while (pm && (!fakelevel || + (fakelevel > pm->level && (pm->node.flags & PM_UNSET))) && + locallevel > pm->level && is_private(pm)) + pm = pm->old; + /* Ideally, we'd print the word "private" here instead of "typeset" + * when the parameter is in fact a private, but that would require + * re-implementing the entirety of printparamnode(). */ + if (pm) + printparamnode((HashNode)pm, printflags); +} + +/* + * Standard module configuration/linkage + */ + +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") +}; + +static struct features module_features = { + bintab, sizeof(bintab)/sizeof(*bintab), + NULL, 0, + NULL, 0, + NULL, 0, + 0 +}; + +static struct builtin save_local; +static struct reswd reswd_private = {{NULL, "private", 0}, TYPESET}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + HashNode hn = builtintab->getnode(builtintab, "local"); + + /* Horrible, horrible hack */ + getparamnode = realparamtab->getnode; + realparamtab->getnode = getprivatenode; + realparamtab->getnode2 = getprivatenode2; + realparamtab->printnode = printprivatenode; + + /* Even more horrible hack */ + save_local = *(Builtin)hn; + ((Builtin)hn)->handlerfunc = bintab[0].handlerfunc; + ((Builtin)hn)->optstr = bintab[0].optstr; + + reswdtab->addnode(reswdtab, reswd_private.node.nam, &reswd_private); + + 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_(Module m) +{ + emptytable = newparamtable(1, "private"); + return addwrapper(m, wrapper); +} + +/**/ +int +cleanup_(Module m) +{ + HashNode hn = builtintab->getnode(builtintab, "local"); + *(Builtin)hn = save_local; + + removehashnode(reswdtab, "private"); + + realparamtab->getnode = getparamnode; + realparamtab->getnode2 = gethashnode2; + realparamtab->printnode = printparamnode; + + deletewrapper(m, wrapper); + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + deletehashtable(emptytable); + return 0; +} diff --git a/Src/Modules/param_private.mdd b/Src/Modules/param_private.mdd new file mode 100644 index 000000000..e6eb3228f --- /dev/null +++ b/Src/Modules/param_private.mdd @@ -0,0 +1,7 @@ +name=zsh/param/private +link=dynamic +load=yes + +autofeatures="b:private" + +objects="param_private.o" diff --git a/Test/V10private.ztst b/Test/V10private.ztst new file mode 100644 index 000000000..444b5b4ea --- /dev/null +++ b/Test/V10private.ztst @@ -0,0 +1,265 @@ +# Tests for the zsh/param/private module + +%prep + + zmodload zsh/param/private + +%test + + (zmodload -u zsh/param/private && zmodload zsh/param/private) +0:unload and reload the module without crashing + + typeset scalar_test=toplevel + () { + print $scalar_test + private scalar_test + print $+scalar_test + unset scalar_test + print $+scalar_test + } + print $scalar_test +0:basic scope hiding +>toplevel +>1 +>0 +>toplevel + + typeset scalar_test=toplevel + print $scalar_test + () { + private scalar_test=function + print $scalar_test + } + print $scalar_test +0:enter and exit a scope +>toplevel +>function +>toplevel + + print $+unset_test + () { + private unset_test + print $+unset_test + unset_test=setme + print $unset_test + } + print $+unset_test +0:variable defined only in scope +>0 +>1 +>setme +>0 + + # Depends on zsh-5.0.9 typeset keyword + typeset -a array_test=(top level) + () { + local -Pa array_test=(in function) + () { + private array_test + print $+array_test + } + print $array_test + } + print $array_test +0:nested scope with different type, correctly restored +>1 +>in function +>top level + + typeset -a array_test=(top level) + () { + private array_test + array_test=(in function) + } +1:type of private may not be changed by assignment +?(anon):2: array_test: attempt to assign array value to non-array + + typeset -A hash_test=(top level) + () { + setopt localoptions noglob + private hash_test[top] + } +1:associative array fields may not be private +?(anon):private:2: hash_test[top]: can't create local array elements + + () { + private path + } +1:tied params may not be private, part 1 +?(anon):private:1: can't change scope of existing param: path + + () { + private PATH + } +1:tied params may not be private, part 2 +?(anon):private:1: can't change scope of existing param: PATH + + () { + private -h path + print X$path + } +0:privates may hide tied paramters +>X + + # Deliberate type mismatch here + typeset -a hash_test=(top level) + typeset -p hash_test + inner () { + private -p hash_test + print ${(t)hash_test} ${(kv)hash_test} + } + outer () { + local -PA hash_test=(in function) + typeset -p hash_test + inner + } + outer + print ${(kv)hash_test} +0:private hides value from surrounding scope in nested scope +>typeset -a hash_test +>hash_test=( top level ) +>typeset -A hash_test +>hash_test=( in function ) +>typeset -a hash_test +>hash_test=( top level ) +>array-local top level +>top level +F:note "typeset" rather than "private" in output from outer + + () { + private -a array_test + local array_test=scalar + } +1:private cannot be re-declared as local +?(anon):local:2: array_test: inconsistent type for assignment + + () { + local hash_test=scalar + private -A hash_test + } +1:local cannot be re-declared as private +?(anon):private:2: can't change scope of existing param: hash_test + + inner () { + print $+scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + } + () { + private -x scalar_test=whaat + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + inner + print Y $scalar_test + } +0:exported private behaves like a local, part 1 +>X whaat +>0 +>X whaat +>Y whaat + + inner () { + typeset -p array_test + $ZTST_testdir/../Src/zsh -fc 'print X $array_test' + } + () { + local -Pax array_test=(whaat) + print Y $array_test + $ZTST_testdir/../Src/zsh -fc 'print X $array_test' + inner + } +0:exported private behaves like a local, part 2 (arrays do not export) +?inner:typeset:1: no such variable: array_test +>Y whaat +>X +>X + + inner () { + print $+scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + } + () { + private scalar_test=whaat + export scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + inner + () { + print $+scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + } + print Y $scalar_test + } +0:exported private behaves like a local, part 3 (export does not change scope) +>X whaat +>0 +>X whaat +>0 +>X whaat +>Y whaat + + typeset -A hash_test=(top level) + () { + local -PA hash_test=(in function) + () { + print X ${(kv)hash_test} + } + print Y ${(kv)hash_test} + } + print ${(kv)hash_test} +0:privates are not visible in anonymous functions, part 1 +>X top level +>Y in function +>top level + + typeset -A hash_test=(top level) + () { + local -PA hash_test=(in function) + () { + print X ${(kv)hash_test} + hash_test[in]=deeper + } + print Y ${(kv)hash_test} + } + print ${(okv)hash_test} +0:privates are not visible in anonymous functions, part 2 +>X top level +>Y in function +>deeper in level top + + typeset -A hash_test=(top level) + () { + local -Pa array_test=(in function) + local -PA hash_test=($array_test) + () { + print X ${(kv)hash_test} + hash_test=(even deeper) + array_test+=(${(kv)hash_test}) + } + print Y ${(kv)hash_test} Z $array_test + } + print ${(kv)hash_test} +0:privates are not visible in anonymous functions, part 3 +>X top level +>Y in function Z in function +>even deeper + + typeset -A hash_test=(top level) + () { + local -PA hash_test=(in function) + () { + print X ${(kv)hash_test} + unset hash_test + } + print Y ${(kv)hash_test} + } + print ${(t)hash_test} ${(kv)hash_test} +0:privates are not visible in anonymous functions, part 4 +>X top level +>Y in function +> + + # Subshell because otherwise this silently dumps core when broken + ( () { private SECONDS } ) +1:special parameters cannot be made private +?(anon):private: can't change scope of existing param: SECONDS + + () { private -h SECONDS } +0:private parameter may hide a special parameter -- cgit v1.2.3