1 /*
2  * params.c - parameters
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1992-1997 Paul Falstad
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Paul Falstad or the Zsh Development Group be liable
16  * to any party for direct, indirect, special, incidental, or consequential
17  * damages arising out of the use of this software and its documentation,
18  * even if Paul Falstad and the Zsh Development Group have been advised of
19  * the possibility of such damage.
20  *
21  * Paul Falstad and the Zsh Development Group specifically disclaim any
22  * warranties, including, but not limited to, the implied warranties of
23  * merchantability and fitness for a particular purpose.  The software
24  * provided hereunder is on an "as is" basis, and Paul Falstad and the
25  * Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 #include "zsh.mdh"
31 #include "params.pro"
32 
33 #include "version.h"
34 #ifdef CUSTOM_PATCHLEVEL
35 #define ZSH_PATCHLEVEL	CUSTOM_PATCHLEVEL
36 #else
37 #include "patchlevel.h"
38 
39 #include <math.h>
40 
41 /* If removed from the ChangeLog for some reason */
42 #ifndef ZSH_PATCHLEVEL
43 #define ZSH_PATCHLEVEL "unknown"
44 #endif
45 #endif
46 
47 /* What level of localness we are at.
48  *
49  * Hand-wavingly, this is incremented at every function call and decremented
50  * at every function return.  See startparamscope().
51  */
52 
53 /**/
54 mod_export int locallevel;
55 
56 /* Variables holding values of special parameters */
57 
58 /**/
59 mod_export
60 char **pparams,		/* $argv        */
61      **cdpath,		/* $cdpath      */
62      **fpath,		/* $fpath       */
63      **mailpath,	/* $mailpath    */
64      **manpath,		/* $manpath     */
65      **psvar,		/* $psvar       */
66      **watch,		/* $watch       */
67      **zsh_eval_context; /* $zsh_eval_context */
68 /**/
69 mod_export
70 char **path,		/* $path        */
71      **fignore;		/* $fignore     */
72 
73 /**/
74 mod_export
75 char *argzero,		/* $0           */
76      *posixzero,	/* $0           */
77      *home,		/* $HOME        */
78      *nullcmd,		/* $NULLCMD     */
79      *oldpwd,		/* $OLDPWD      */
80      *zoptarg,		/* $OPTARG      */
81      *prompt,		/* $PROMPT      */
82      *prompt2,		/* $PROMPT2     */
83      *prompt3,		/* $PROMPT3     */
84      *prompt4,		/* $PROMPT4     */
85      *readnullcmd,	/* $READNULLCMD */
86      *rprompt,		/* $RPROMPT     */
87      *rprompt2,		/* $RPROMPT2    */
88      *sprompt,		/* $SPROMPT     */
89      *wordchars;	/* $WORDCHARS   */
90 /**/
91 mod_export
92 char *ifs,		/* $IFS         */
93      *postedit,		/* $POSTEDIT    */
94      *term,		/* $TERM        */
95      *zsh_terminfo,     /* $TERMINFO    */
96      *zsh_terminfodirs, /* $TERMINFO_DIRS */
97      *ttystrname,	/* $TTY         */
98      *pwd;		/* $PWD         */
99 
100 /**/
101 mod_export
102 zlong lastval,		/* $?           */
103      mypid,		/* $$           */
104      lastpid,		/* $!           */
105      zterm_columns,	/* $COLUMNS     */
106      zterm_lines,	/* $LINES       */
107      rprompt_indent,	/* $ZLE_RPROMPT_INDENT */
108      ppid,		/* $PPID        */
109      zsh_subshell;	/* $ZSH_SUBSHELL */
110 
111 /* $FUNCNEST    */
112 /**/
113 mod_export
114 zlong zsh_funcnest =
115 #ifdef MAX_FUNCTION_DEPTH
116     MAX_FUNCTION_DEPTH
117 #else
118     /* Disabled by default but can be enabled at run time */
119     -1
120 #endif
121     ;
122 
123 /**/
124 zlong lineno,		/* $LINENO      */
125      zoptind,		/* $OPTIND      */
126      shlvl;		/* $SHLVL       */
127 
128 /* $histchars */
129 
130 /**/
131 mod_export unsigned char bangchar;
132 /**/
133 unsigned char hatchar, hashchar;
134 
135 /**/
136 unsigned char keyboardhackchar = '\0';
137 
138 /* $SECONDS = now.tv_sec - shtimer.tv_sec
139  *          + (now.tv_usec - shtimer.tv_usec) / 1000000.0
140  * (rounded to an integer if the parameter is not set to float) */
141 
142 /**/
143 struct timeval shtimer;
144 
145 /* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */
146 
147 /**/
148 mod_export int termflags;
149 
150 /* Forward declaration */
151 
152 static void
153 rprompt_indent_unsetfn(Param pm, int exp);
154 
155 /* Standard methods for get/set/unset pointers in parameters */
156 
157 /**/
158 mod_export const struct gsu_scalar stdscalar_gsu =
159 { strgetfn, strsetfn, stdunsetfn };
160 /**/
161 mod_export const struct gsu_scalar varscalar_gsu =
162 { strvargetfn, strvarsetfn, stdunsetfn };
163 /**/
164 mod_export const struct gsu_scalar nullsetscalar_gsu =
165 { strgetfn, nullstrsetfn, NULL };
166 
167 /**/
168 mod_export const struct gsu_integer stdinteger_gsu =
169 { intgetfn, intsetfn, stdunsetfn };
170 /**/
171 mod_export const struct gsu_integer varinteger_gsu =
172 { intvargetfn, intvarsetfn, stdunsetfn };
173 /**/
174 mod_export const struct gsu_integer nullsetinteger_gsu =
175 { intgetfn, NULL, NULL };
176 
177 /**/
178 mod_export const struct gsu_float stdfloat_gsu =
179 { floatgetfn, floatsetfn, stdunsetfn };
180 
181 /**/
182 mod_export const struct gsu_array stdarray_gsu =
183 { arrgetfn, arrsetfn, stdunsetfn };
184 /**/
185 mod_export const struct gsu_array vararray_gsu =
186 { arrvargetfn, arrvarsetfn, stdunsetfn };
187 
188 /**/
189 mod_export const struct gsu_hash stdhash_gsu =
190 { hashgetfn, hashsetfn, stdunsetfn };
191 /**/
192 mod_export const struct gsu_hash nullsethash_gsu =
193 { hashgetfn, nullsethashfn, nullunsetfn };
194 
195 
196 /* Non standard methods (not exported) */
197 static const struct gsu_integer pound_gsu =
198 { poundgetfn, nullintsetfn, stdunsetfn };
199 static const struct gsu_integer errno_gsu =
200 { errnogetfn, errnosetfn, stdunsetfn };
201 static const struct gsu_integer gid_gsu =
202 { gidgetfn, gidsetfn, stdunsetfn };
203 static const struct gsu_integer egid_gsu =
204 { egidgetfn, egidsetfn, stdunsetfn };
205 static const struct gsu_integer histsize_gsu =
206 { histsizegetfn, histsizesetfn, stdunsetfn };
207 static const struct gsu_integer random_gsu =
208 { randomgetfn, randomsetfn, stdunsetfn };
209 static const struct gsu_integer savehist_gsu =
210 { savehistsizegetfn, savehistsizesetfn, stdunsetfn };
211 static const struct gsu_integer intseconds_gsu =
212 { intsecondsgetfn, intsecondssetfn, stdunsetfn };
213 static const struct gsu_float floatseconds_gsu =
214 { floatsecondsgetfn, floatsecondssetfn, stdunsetfn };
215 static const struct gsu_integer uid_gsu =
216 { uidgetfn, uidsetfn, stdunsetfn };
217 static const struct gsu_integer euid_gsu =
218 { euidgetfn, euidsetfn, stdunsetfn };
219 static const struct gsu_integer ttyidle_gsu =
220 { ttyidlegetfn, nullintsetfn, stdunsetfn };
221 
222 static const struct gsu_scalar argzero_gsu =
223 { argzerogetfn, argzerosetfn, nullunsetfn };
224 static const struct gsu_scalar username_gsu =
225 { usernamegetfn, usernamesetfn, stdunsetfn };
226 static const struct gsu_scalar dash_gsu =
227 { dashgetfn, nullstrsetfn, stdunsetfn };
228 static const struct gsu_scalar histchars_gsu =
229 { histcharsgetfn, histcharssetfn, stdunsetfn };
230 static const struct gsu_scalar home_gsu =
231 { homegetfn, homesetfn, stdunsetfn };
232 static const struct gsu_scalar term_gsu =
233 { termgetfn, termsetfn, stdunsetfn };
234 static const struct gsu_scalar terminfo_gsu =
235 { terminfogetfn, terminfosetfn, stdunsetfn };
236 static const struct gsu_scalar terminfodirs_gsu =
237 { terminfodirsgetfn, terminfodirssetfn, stdunsetfn };
238 static const struct gsu_scalar wordchars_gsu =
239 { wordcharsgetfn, wordcharssetfn, stdunsetfn };
240 static const struct gsu_scalar ifs_gsu =
241 { ifsgetfn, ifssetfn, stdunsetfn };
242 static const struct gsu_scalar underscore_gsu =
243 { underscoregetfn, nullstrsetfn, stdunsetfn };
244 static const struct gsu_scalar keyboard_hack_gsu =
245 { keyboardhackgetfn, keyboardhacksetfn, stdunsetfn };
246 #ifdef USE_LOCALE
247 static const struct gsu_scalar lc_blah_gsu =
248 { strgetfn, lcsetfn, stdunsetfn };
249 static const struct gsu_scalar lang_gsu =
250 { strgetfn, langsetfn, stdunsetfn };
251 static const struct gsu_scalar lc_all_gsu =
252 { strgetfn, lc_allsetfn, stdunsetfn };
253 #endif
254 
255 static const struct gsu_integer varint_readonly_gsu =
256 { intvargetfn, nullintsetfn, stdunsetfn };
257 static const struct gsu_integer zlevar_gsu =
258 { intvargetfn, zlevarsetfn, stdunsetfn };
259 
260 static const struct gsu_scalar colonarr_gsu =
261 { colonarrgetfn, colonarrsetfn, stdunsetfn };
262 
263 static const struct gsu_integer argc_gsu =
264 { poundgetfn, nullintsetfn, stdunsetfn };
265 static const struct gsu_array pipestatus_gsu =
266 { pipestatgetfn, pipestatsetfn, stdunsetfn };
267 
268 static const struct gsu_integer rprompt_indent_gsu =
269 { intvargetfn, zlevarsetfn, rprompt_indent_unsetfn };
270 
271 /* Nodes for special parameters for parameter hash table */
272 
273 #ifdef HAVE_UNION_INIT
274 # define BR(X) {X}
275 typedef struct param initparam;
276 #else
277 # define BR(X) X
278 typedef struct iparam {
279     struct hashnode *next;
280     char *nam;			/* hash data                             */
281     int flags;			/* PM_* flags (defined in zsh.h)         */
282     void *value;
283     void *gsu;			/* get/set/unset methods */
284     int base;			/* output base                           */
285     int width;			/* output field width                    */
286     char *env;			/* location in environment, if exported  */
287     char *ename;		/* name of corresponding environment var */
288     Param old;			/* old struct for use with local         */
289     int level;			/* if (old != NULL), level of localness  */
290 } initparam;
291 #endif
292 
293 static initparam special_params[] ={
294 #define GSU(X) BR((GsuScalar)(void *)(&(X)))
295 #define NULL_GSU BR((GsuScalar)(void *)NULL)
296 #define IPDEF1(A,B,C) {{NULL,A,PM_INTEGER|PM_SPECIAL|C},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
297 IPDEF1("#", pound_gsu, PM_READONLY_SPECIAL),
298 IPDEF1("ERRNO", errno_gsu, PM_UNSET),
299 IPDEF1("GID", gid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
300 IPDEF1("EGID", egid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
301 IPDEF1("HISTSIZE", histsize_gsu, PM_RESTRICTED),
302 IPDEF1("RANDOM", random_gsu, 0),
303 IPDEF1("SAVEHIST", savehist_gsu, PM_RESTRICTED),
304 IPDEF1("SECONDS", intseconds_gsu, 0),
305 IPDEF1("UID", uid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
306 IPDEF1("EUID", euid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
307 IPDEF1("TTYIDLE", ttyidle_gsu, PM_READONLY_SPECIAL),
308 
309 #define IPDEF2(A,B,C) {{NULL,A,PM_SCALAR|PM_SPECIAL|C},BR(NULL),GSU(B),0,0,NULL,NULL,NULL,0}
310 IPDEF2("USERNAME", username_gsu, PM_DONTIMPORT|PM_RESTRICTED),
311 IPDEF2("-", dash_gsu, PM_READONLY_SPECIAL),
312 IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT),
313 IPDEF2("HOME", home_gsu, PM_UNSET),
314 IPDEF2("TERM", term_gsu, PM_UNSET),
315 IPDEF2("TERMINFO", terminfo_gsu, PM_UNSET),
316 IPDEF2("TERMINFO_DIRS", terminfodirs_gsu, PM_UNSET),
317 IPDEF2("WORDCHARS", wordchars_gsu, 0),
318 IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT | PM_RESTRICTED),
319 IPDEF2("_", underscore_gsu, PM_DONTIMPORT),
320 IPDEF2("KEYBOARD_HACK", keyboard_hack_gsu, PM_DONTIMPORT),
321 IPDEF2("0", argzero_gsu, 0),
322 
323 #ifdef USE_LOCALE
324 # define LCIPDEF(name) IPDEF2(name, lc_blah_gsu, PM_UNSET)
325 IPDEF2("LANG", lang_gsu, PM_UNSET),
326 IPDEF2("LC_ALL", lc_all_gsu, PM_UNSET),
327 # ifdef LC_COLLATE
328 LCIPDEF("LC_COLLATE"),
329 # endif
330 # ifdef LC_CTYPE
331 LCIPDEF("LC_CTYPE"),
332 # endif
333 # ifdef LC_MESSAGES
334 LCIPDEF("LC_MESSAGES"),
335 # endif
336 # ifdef LC_NUMERIC
337 LCIPDEF("LC_NUMERIC"),
338 # endif
339 # ifdef LC_TIME
340 LCIPDEF("LC_TIME"),
341 # endif
342 #endif /* USE_LOCALE */
343 
344 #define IPDEF4(A,B) {{NULL,A,PM_INTEGER|PM_READONLY_SPECIAL},BR((void *)B),GSU(varint_readonly_gsu),10,0,NULL,NULL,NULL,0}
345 IPDEF4("!", &lastpid),
346 IPDEF4("$", &mypid),
347 IPDEF4("?", &lastval),
348 IPDEF4("HISTCMD", &curhist),
349 IPDEF4("LINENO", &lineno),
350 IPDEF4("PPID", &ppid),
351 IPDEF4("ZSH_SUBSHELL", &zsh_subshell),
352 
353 #define IPDEF5(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
354 #define IPDEF5U(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
355 IPDEF5("COLUMNS", &zterm_columns, zlevar_gsu),
356 IPDEF5("LINES", &zterm_lines, zlevar_gsu),
357 IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, rprompt_indent_gsu),
358 IPDEF5("SHLVL", &shlvl, varinteger_gsu),
359 IPDEF5("FUNCNEST", &zsh_funcnest, varinteger_gsu),
360 
361 /* Don't import internal integer status variables. */
362 #define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
363 IPDEF6("OPTIND", &zoptind, varinteger_gsu),
364 IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
365 IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu),
366 
367 #define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
368 #define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_SUID},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
369 #define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
370 IPDEF7("OPTARG", &zoptarg),
371 IPDEF7("NULLCMD", &nullcmd),
372 IPDEF7U("POSTEDIT", &postedit),
373 IPDEF7("READNULLCMD", &readnullcmd),
374 IPDEF7("PS1", &prompt),
375 IPDEF7U("RPS1", &rprompt),
376 IPDEF7U("RPROMPT", &rprompt),
377 IPDEF7("PS2", &prompt2),
378 IPDEF7U("RPS2", &rprompt2),
379 IPDEF7U("RPROMPT2", &rprompt2),
380 IPDEF7("PS3", &prompt3),
381 IPDEF7R("PS4", &prompt4),
382 IPDEF7("SPROMPT", &sprompt),
383 
384 #define IPDEF9(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0}
385 IPDEF9("*", &pparams, NULL, PM_ARRAY|PM_READONLY_SPECIAL|PM_DONTIMPORT),
386 IPDEF9("@", &pparams, NULL, PM_ARRAY|PM_READONLY_SPECIAL|PM_DONTIMPORT),
387 
388 /*
389  * This empty row indicates the end of parameters available in
390  * all emulations.
391  */
392 {{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
393 
394 #define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0}
395 IPDEF8("CDPATH", &cdpath, "cdpath", PM_TIED),
396 IPDEF8("FIGNORE", &fignore, "fignore", PM_TIED),
397 IPDEF8("FPATH", &fpath, "fpath", PM_TIED),
398 IPDEF8("MAILPATH", &mailpath, "mailpath", PM_TIED),
399 IPDEF8("WATCH", &watch, "watch", PM_TIED),
400 IPDEF8("PATH", &path, "path", PM_RESTRICTED|PM_TIED),
401 IPDEF8("PSVAR", &psvar, "psvar", PM_TIED),
402 IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY_SPECIAL|PM_TIED),
403 
404 /* MODULE_PATH is not imported for security reasons */
405 IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED|PM_TIED),
406 
407 #define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
408 
409 /*
410  * The following parameters are not available in sh/ksh compatibility *
411  * mode.
412  */
413 
414 /* All of these have sh compatible equivalents.                */
415 IPDEF1("ARGC", argc_gsu, PM_READONLY_SPECIAL),
416 IPDEF2("HISTCHARS", histchars_gsu, PM_DONTIMPORT),
417 IPDEF4("status", &lastval),
418 IPDEF7("prompt", &prompt),
419 IPDEF7("PROMPT", &prompt),
420 IPDEF7("PROMPT2", &prompt2),
421 IPDEF7("PROMPT3", &prompt3),
422 IPDEF7("PROMPT4", &prompt4),
423 IPDEF8("MANPATH", &manpath, "manpath", PM_TIED),
424 IPDEF9("argv", &pparams, NULL, 0),
425 IPDEF9("fignore", &fignore, "FIGNORE", PM_TIED),
426 IPDEF9("cdpath", &cdpath, "CDPATH", PM_TIED),
427 IPDEF9("fpath", &fpath, "FPATH", PM_TIED),
428 IPDEF9("mailpath", &mailpath, "MAILPATH", PM_TIED),
429 IPDEF9("manpath", &manpath, "MANPATH", PM_TIED),
430 IPDEF9("psvar", &psvar, "PSVAR", PM_TIED),
431 IPDEF9("watch", &watch, "WATCH", PM_TIED),
432 
433 IPDEF9("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_TIED|PM_READONLY_SPECIAL),
434 
435 IPDEF9("module_path", &module_path, "MODULE_PATH", PM_TIED|PM_RESTRICTED),
436 IPDEF9("path", &path, "PATH", PM_TIED|PM_RESTRICTED),
437 
438 /* These are known to zsh alone. */
439 
440 IPDEF10("pipestatus", pipestatus_gsu),
441 
442 {{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
443 };
444 
445 /*
446  * Alternative versions of colon-separated path parameters for
447  * sh emulation.  These don't link to the array versions.
448  */
449 static initparam special_params_sh[] = {
450 IPDEF8("CDPATH", &cdpath, NULL, 0),
451 IPDEF8("FIGNORE", &fignore, NULL, 0),
452 IPDEF8("FPATH", &fpath, NULL, 0),
453 IPDEF8("MAILPATH", &mailpath, NULL, 0),
454 IPDEF8("WATCH", &watch, NULL, 0),
455 IPDEF8("PATH", &path, NULL, PM_RESTRICTED),
456 IPDEF8("PSVAR", &psvar, NULL, 0),
457 IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY_SPECIAL),
458 
459 /* MODULE_PATH is not imported for security reasons */
460 IPDEF8("MODULE_PATH", &module_path, NULL, PM_DONTIMPORT|PM_RESTRICTED),
461 
462 {{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
463 };
464 
465 /*
466  * Special way of referring to the positional parameters.  Unlike $*
467  * and $@, this is not readonly.  This parameter is not directly
468  * visible in user space.
469  */
470 static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \
471 				 PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT);
472 
473 #undef BR
474 
475 #define IS_UNSET_VALUE(V) \
476 	((V) && (!(V)->pm || ((V)->pm->node.flags & PM_UNSET) || \
477 		 !(V)->pm->node.nam || !*(V)->pm->node.nam))
478 
479 static Param argvparam;
480 
481 /* "parameter table" - hash table containing the parameters
482  *
483  * realparamtab always points to the shell's global table.  paramtab is sometimes
484  * temporarily changed to point at another table, while dealing with the keys
485  * of an associative array (for example, see makecompparams() which initializes
486  * the associative array ${compstate}).
487  */
488 
489 /**/
490 mod_export HashTable paramtab, realparamtab;
491 
492 /**/
493 mod_export HashTable
newparamtable(int size,char const * name)494 newparamtable(int size, char const *name)
495 {
496     HashTable ht;
497     if (!size)
498 	size = 17;
499     ht = newhashtable(size, name, NULL);
500 
501     ht->hash        = hasher;
502     ht->emptytable  = emptyhashtable;
503     ht->filltable   = NULL;
504     ht->cmpnodes    = strcmp;
505     ht->addnode     = addhashnode;
506     ht->getnode     = getparamnode;
507     ht->getnode2    = gethashnode2;
508     ht->removenode  = removehashnode;
509     ht->disablenode = NULL;
510     ht->enablenode  = NULL;
511     ht->freenode    = freeparamnode;
512     ht->printnode   = printparamnode;
513 
514     return ht;
515 }
516 
517 /**/
518 static HashNode
getparamnode(HashTable ht,const char * nam)519 getparamnode(HashTable ht, const char *nam)
520 {
521     HashNode hn = gethashnode2(ht, nam);
522     Param pm = (Param) hn;
523 
524     if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) {
525 	char *mn = dupstring(pm->u.str);
526 
527 	(void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL :
528 			    nam);
529 	hn = gethashnode2(ht, nam);
530 	if (!hn) {
531 	    /*
532 	     * This used to be a warning, but surely if we allow
533 	     * stuff to go ahead with the autoload stub with
534 	     * no error status we're in for all sorts of mayhem?
535 	     */
536 	    zerr("autoloading module %s failed to define parameter: %s", mn,
537 		 nam);
538 	}
539     }
540     return hn;
541 }
542 
543 /* Copy a parameter hash table */
544 
545 static HashTable outtable;
546 
547 /**/
548 static void
scancopyparams(HashNode hn,UNUSED (int flags))549 scancopyparams(HashNode hn, UNUSED(int flags))
550 {
551     /* Going into a real parameter, so always use permanent storage */
552     Param pm = (Param)hn;
553     Param tpm = (Param) zshcalloc(sizeof *tpm);
554     tpm->node.nam = ztrdup(pm->node.nam);
555     copyparam(tpm, pm, 0);
556     addhashnode(outtable, tpm->node.nam, tpm);
557 }
558 
559 /**/
560 HashTable
copyparamtable(HashTable ht,char * name)561 copyparamtable(HashTable ht, char *name)
562 {
563     HashTable nht = 0;
564     if (ht) {
565 	nht = newparamtable(ht->hsize, name);
566 	outtable = nht;
567 	scanhashtable(ht, 0, 0, 0, scancopyparams, 0);
568 	outtable = NULL;
569     }
570     return nht;
571 }
572 
573 /* Flag to freeparamnode to unset the struct */
574 
575 static int delunset;
576 
577 /* Function to delete a parameter table. */
578 
579 /**/
580 mod_export void
deleteparamtable(HashTable t)581 deleteparamtable(HashTable t)
582 {
583     /* The parameters in the hash table need to be unset *
584      * before being deleted.                             */
585     int odelunset = delunset;
586     delunset = 1;
587     deletehashtable(t);
588     delunset = odelunset;
589 }
590 
591 static unsigned numparamvals;
592 
593 /**/
594 mod_export void
scancountparams(UNUSED (HashNode hn),int flags)595 scancountparams(UNUSED(HashNode hn), int flags)
596 {
597     ++numparamvals;
598     if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS))
599 	++numparamvals;
600 }
601 
602 static Patprog scanprog;
603 static char *scanstr;
604 static char **paramvals;
605 static Param foundparam;
606 
607 /**/
608 static void
scanparamvals(HashNode hn,int flags)609 scanparamvals(HashNode hn, int flags)
610 {
611     struct value v;
612     Patprog prog;
613 
614     if (numparamvals && !(flags & SCANPM_MATCHMANY) &&
615 	(flags & (SCANPM_MATCHVAL|SCANPM_MATCHKEY|SCANPM_KEYMATCH)))
616 	return;
617     v.pm = (Param)hn;
618     if ((flags & SCANPM_KEYMATCH)) {
619 	char *tmp = dupstring(v.pm->node.nam);
620 
621 	tokenize(tmp);
622 	remnulargs(tmp);
623 
624 	if (!(prog = patcompile(tmp, 0, NULL)) || !pattry(prog, scanstr))
625 	    return;
626     } else if ((flags & SCANPM_MATCHKEY) && !pattry(scanprog, v.pm->node.nam)) {
627 	return;
628     }
629     foundparam = v.pm;
630     if (flags & SCANPM_WANTKEYS) {
631 	paramvals[numparamvals++] = v.pm->node.nam;
632 	if (!(flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)))
633 	    return;
634     }
635     v.isarr = (PM_TYPE(v.pm->node.flags) & (PM_ARRAY|PM_HASHED));
636     v.flags = 0;
637     v.start = 0;
638     v.end = -1;
639     paramvals[numparamvals] = getstrvalue(&v);
640     if (flags & SCANPM_MATCHVAL) {
641 	if (pattry(scanprog, paramvals[numparamvals])) {
642 	    numparamvals += ((flags & SCANPM_WANTVALS) ? 1 :
643 			     !(flags & SCANPM_WANTKEYS));
644 	} else if (flags & SCANPM_WANTKEYS)
645 	    --numparamvals;	/* Value didn't match, discard key */
646     } else
647 	++numparamvals;
648     foundparam = NULL;
649 }
650 
651 /**/
652 char **
paramvalarr(HashTable ht,int flags)653 paramvalarr(HashTable ht, int flags)
654 {
655     DPUTS((flags & (SCANPM_MATCHKEY|SCANPM_MATCHVAL)) && !scanprog,
656 	  "BUG: scanning hash without scanprog set");
657     numparamvals = 0;
658     if (ht)
659 	scanhashtable(ht, 0, 0, PM_UNSET, scancountparams, flags);
660     paramvals = (char **) zhalloc((numparamvals + 1) * sizeof(char *));
661     if (ht) {
662 	numparamvals = 0;
663 	scanhashtable(ht, 0, 0, PM_UNSET, scanparamvals, flags);
664     }
665     paramvals[numparamvals] = 0;
666     return paramvals;
667 }
668 
669 /* Return the full array (no indexing) referred to by a Value. *
670  * The array value is cached for the lifetime of the Value.    */
671 
672 /**/
673 static char **
getvaluearr(Value v)674 getvaluearr(Value v)
675 {
676     if (v->arr)
677 	return v->arr;
678     else if (PM_TYPE(v->pm->node.flags) == PM_ARRAY)
679 	return v->arr = v->pm->gsu.a->getfn(v->pm);
680     else if (PM_TYPE(v->pm->node.flags) == PM_HASHED) {
681 	v->arr = paramvalarr(v->pm->gsu.h->getfn(v->pm), v->isarr);
682 	/* Can't take numeric slices of associative arrays */
683 	v->start = 0;
684 	v->end = numparamvals + 1;
685 	return v->arr;
686     } else
687 	return NULL;
688 }
689 
690 /* Return whether the variable is set         *
691  * checks that array slices are within range  *
692  * used for [[ -v ... ]] condition test       */
693 
694 /**/
695 int
issetvar(char * name)696 issetvar(char *name)
697 {
698     struct value vbuf;
699     Value v;
700     int slice;
701     char **arr;
702 
703     if (!(v = getvalue(&vbuf, &name, 1)) || *name)
704 	return 0; /* no value or more chars after the variable name */
705     if (v->isarr & ~SCANPM_ARRONLY)
706 	return v->end > 1; /* for extracted elements, end gives us a count */
707 
708     slice = v->start != 0 || v->end != -1;
709     if (PM_TYPE(v->pm->node.flags) != PM_ARRAY || !slice)
710 	return !slice && !(v->pm->node.flags & PM_UNSET);
711 
712     if (!v->end) /* empty array slice */
713 	return 0;
714     /* get the array and check end is within range */
715     if (!(arr = getvaluearr(v)))
716 	return 0;
717     return arrlen_ge(arr, v->end < 0 ? - v->end : v->end);
718 }
719 
720 /*
721  * Split environment string into (name, value) pair.
722  * this is used to avoid in-place editing of environment table
723  * that results in core dump on some systems
724  */
725 
726 static int
split_env_string(char * env,char ** name,char ** value)727 split_env_string(char *env, char **name, char **value)
728 {
729     char *str, *tenv;
730 
731     if (!env || !name || !value)
732 	return 0;
733 
734     tenv = strcpy(zhalloc(strlen(env) + 1), env);
735     for (str = tenv; *str && *str != '='; str++) {
736 	if (STOUC(*str) >= 128) {
737 	    /*
738 	     * We'll ignore environment variables with names not
739 	     * from the portable character set since we don't
740 	     * know of a good reason to accept them.
741 	     */
742 	    return 0;
743 	}
744     }
745     if (str != tenv && *str == '=') {
746 	*str = '\0';
747 	*name = tenv;
748 	*value = str + 1;
749 	return 1;
750     } else
751 	return 0;
752 }
753 
754 /**
755  * Check parameter flags to see if parameter shouldn't be imported
756  * from environment at start.
757  *
758  * return 1: don't import: 0: ok to import.
759  */
dontimport(int flags)760 static int dontimport(int flags)
761 {
762     /* If explicitly marked as don't export */
763     if (flags & PM_DONTIMPORT)
764 	return 1;
765     /* If value already exported */
766     if (flags & PM_EXPORTED)
767 	return 1;
768     /* If security issue when importing and running with some privilege */
769     if ((flags & PM_DONTIMPORT_SUID) && isset(PRIVILEGED))
770 	return 1;
771     /* OK to import */
772     return 0;
773 }
774 
775 /* Set up parameter hash table.  This will add predefined  *
776  * parameter entries as well as setting up parameter table *
777  * entries for environment variables we inherit.           */
778 
779 /**/
780 void
createparamtable(void)781 createparamtable(void)
782 {
783     Param ip, pm;
784 #if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV)
785     char **new_environ;
786     int  envsize;
787 #endif
788 #ifndef USE_SET_UNSET_ENV
789     char **envp;
790 #endif
791     char **envp2, **sigptr, **t;
792     char buf[50], *str, *iname, *ivalue, *hostnam;
793     int  oae = opts[ALLEXPORT];
794 #ifdef HAVE_UNAME
795     struct utsname unamebuf;
796     char *machinebuf;
797 #endif
798 
799     paramtab = realparamtab = newparamtable(151, "paramtab");
800 
801     /* Add the special parameters to the hash table */
802     for (ip = special_params; ip->node.nam; ip++)
803 	paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
804     if (EMULATION(EMULATE_SH|EMULATE_KSH)) {
805 	for (ip = special_params_sh; ip->node.nam; ip++)
806 	    paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
807     } else {
808 	while ((++ip)->node.nam)
809 	    paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
810     }
811 
812     argvparam = (Param) &argvparam_pm;
813 
814     noerrs = 2;
815 
816     /* Add the standard non-special parameters which have to    *
817      * be initialized before we copy the environment variables. *
818      * We don't want to override whatever values the user has   *
819      * given them in the environment.                           */
820     opts[ALLEXPORT] = 0;
821     setiparam("MAILCHECK", 60);
822     setiparam("LOGCHECK", 60);
823     setiparam("KEYTIMEOUT", 40);
824     setiparam("LISTMAX", 100);
825     /*
826      * We used to get the output baud rate here.  However, that's
827      * pretty irrelevant to a terminal on an X display and can lead
828      * to unnecessary delays if it's wrong (which it probably is).
829      * Furthermore, even if the output is slow it's very likely
830      * to be because of WAN delays, not covered by the output
831      * baud rate.
832      * So allow the user to set it in the special cases where it's
833      * useful.
834      */
835     setsparam("TMPPREFIX", ztrdup_metafy(DEFAULT_TMPPREFIX));
836     setsparam("TIMEFMT", ztrdup_metafy(DEFAULT_TIMEFMT));
837     setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt));
838 
839     hostnam = (char *)zalloc(256);
840     gethostname(hostnam, 256);
841     setsparam("HOST", ztrdup_metafy(hostnam));
842     zfree(hostnam, 256);
843 
844     setsparam("LOGNAME",
845 	      ztrdup_metafy((str = getlogin()) && *str ?
846 			    str : cached_username));
847 
848 #if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV)
849     /* Copy the environment variables we are inheriting to dynamic *
850      * memory, so we can do mallocs and frees on it.               */
851     envsize = sizeof(char *)*(1 + arrlen(environ));
852     new_environ = (char **) zalloc(envsize);
853     memcpy(new_environ, environ, envsize);
854     environ = new_environ;
855 #endif
856 
857     /* Use heap allocation to avoid many small alloc/free calls */
858     pushheap();
859 
860     /* Now incorporate environment variables we are inheriting *
861      * into the parameter hash table. Copy them into dynamic   *
862      * memory so that we can free them if needed               */
863     for (
864 #ifndef USE_SET_UNSET_ENV
865 	envp =
866 #endif
867 	    envp2 = environ; *envp2; envp2++) {
868 	if (split_env_string(*envp2, &iname, &ivalue)) {
869 	    if (!idigit(*iname) && isident(iname) && !strchr(iname, '[')) {
870 		/*
871 		 * Parameters that aren't already in the parameter table
872 		 * aren't special to the shell, so it's always OK to
873 		 * import.  Otherwise, check parameter flags.
874 		 */
875 		if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) ||
876 		     !dontimport(pm->node.flags)) &&
877 		    (pm = assignsparam(iname, metafy(ivalue, -1, META_DUP),
878 				       ASSPM_ENV_IMPORT))) {
879 		    pm->node.flags |= PM_EXPORTED;
880 		    if (pm->node.flags & PM_SPECIAL)
881 			pm->env = mkenvstr (pm->node.nam,
882 					    getsparam(pm->node.nam), pm->node.flags);
883 		    else
884 			pm->env = ztrdup(*envp2);
885 #ifndef USE_SET_UNSET_ENV
886 		    *envp++ = pm->env;
887 #endif
888 		}
889 	    }
890 	}
891     }
892     popheap();
893 #ifndef USE_SET_UNSET_ENV
894     *envp = NULL;
895 #endif
896     opts[ALLEXPORT] = oae;
897 
898     /*
899      * For native emulation we always set the variable home
900      * (see setupvals()).
901      */
902     pm = (Param) paramtab->getnode(paramtab, "HOME");
903     if (EMULATION(EMULATE_ZSH))
904     {
905 	pm->node.flags &= ~PM_UNSET;
906 	if (!(pm->node.flags & PM_EXPORTED))
907 	    addenv(pm, home);
908     } else if (!home)
909 	pm->node.flags |= PM_UNSET;
910     pm = (Param) paramtab->getnode(paramtab, "LOGNAME");
911     if (!(pm->node.flags & PM_EXPORTED))
912 	addenv(pm, pm->u.str);
913     pm = (Param) paramtab->getnode(paramtab, "SHLVL");
914     sprintf(buf, "%d", (int)++shlvl);
915     /* shlvl value in environment needs updating unconditionally */
916     addenv(pm, buf);
917 
918     /* Add the standard non-special parameters */
919     set_pwd_env();
920 #ifdef HAVE_UNAME
921     if(uname(&unamebuf)) setsparam("CPUTYPE", ztrdup("unknown"));
922     else
923     {
924        machinebuf = ztrdup_metafy(unamebuf.machine);
925        setsparam("CPUTYPE", machinebuf);
926     }
927 
928 #else
929     setsparam("CPUTYPE", ztrdup_metafy("unknown"));
930 #endif
931     setsparam("MACHTYPE", ztrdup_metafy(MACHTYPE));
932     setsparam("OSTYPE", ztrdup_metafy(OSTYPE));
933     setsparam("TTY", ztrdup_metafy(ttystrname));
934     setsparam("VENDOR", ztrdup_metafy(VENDOR));
935     setsparam("ZSH_ARGZERO", ztrdup(posixzero));
936     setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION));
937     setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL));
938     setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
939     for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); );
940 
941     noerrs = 0;
942 }
943 
944 /* assign various functions used for non-special parameters */
945 
946 /**/
947 mod_export void
assigngetset(Param pm)948 assigngetset(Param pm)
949 {
950     switch (PM_TYPE(pm->node.flags)) {
951     case PM_SCALAR:
952 	pm->gsu.s = &stdscalar_gsu;
953 	break;
954     case PM_INTEGER:
955 	pm->gsu.i = &stdinteger_gsu;
956 	break;
957     case PM_EFLOAT:
958     case PM_FFLOAT:
959 	pm->gsu.f = &stdfloat_gsu;
960 	break;
961     case PM_ARRAY:
962 	pm->gsu.a = &stdarray_gsu;
963 	break;
964     case PM_HASHED:
965 	pm->gsu.h = &stdhash_gsu;
966 	break;
967     default:
968 	DPUTS(1, "BUG: tried to create param node without valid flag");
969 	break;
970     }
971 }
972 
973 /* Create a parameter, so that it can be assigned to.  Returns NULL if the *
974  * parameter already exists or can't be created, otherwise returns the     *
975  * parameter node.  If a parameter of the same name exists in an outer     *
976  * scope, it is hidden by a newly created parameter.  An already existing  *
977  * parameter node at the current level may be `created' and returned       *
978  * provided it is unset and not special.  If the parameter can't be        *
979  * created because it already exists, the PM_UNSET flag is cleared.        */
980 
981 /**/
982 mod_export Param
createparam(char * name,int flags)983 createparam(char *name, int flags)
984 {
985     Param pm, oldpm;
986 
987     if (paramtab != realparamtab)
988 	flags = (flags & ~PM_EXPORTED) | PM_HASHELEM;
989 
990     if (name != nulstring) {
991 	oldpm = (Param) (paramtab == realparamtab ?
992 			 /* gethashnode2() for direct table read */
993 			 gethashnode2(paramtab, name) :
994 			 paramtab->getnode(paramtab, name));
995 
996 	DPUTS(oldpm && oldpm->level > locallevel,
997 	      "BUG: old local parameter not deleted");
998 	if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) {
999 	    if (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_READONLY)) {
1000 		zerr("read-only variable: %s", name);
1001 		return NULL;
1002 	    }
1003 	    if ((oldpm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
1004 		zerr("%s: restricted", name);
1005 		return NULL;
1006 	    }
1007 	    if (!(oldpm->node.flags & PM_UNSET) ||
1008 		(oldpm->node.flags & PM_SPECIAL) ||
1009 		/* POSIXBUILTINS horror: we need to retain 'export' flags */
1010 		(isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) {
1011 		oldpm->node.flags &= ~PM_UNSET;
1012 		if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) {
1013 		    Param altpm =
1014 			(Param) paramtab->getnode(paramtab, oldpm->ename);
1015 		    if (altpm)
1016 			altpm->node.flags &= ~PM_UNSET;
1017 		}
1018 		return NULL;
1019 	    }
1020 
1021 	    pm = oldpm;
1022 	    pm->base = pm->width = 0;
1023 	    oldpm = pm->old;
1024 	} else {
1025 	    pm = (Param) zshcalloc(sizeof *pm);
1026 	    if ((pm->old = oldpm)) {
1027 		/*
1028 		 * needed to avoid freeing oldpm, but we do take it
1029 		 * out of the environment when it's hidden.
1030 		 */
1031 		if (oldpm->env)
1032 		    delenv(oldpm);
1033 		paramtab->removenode(paramtab, name);
1034 	    }
1035 	    paramtab->addnode(paramtab, ztrdup(name), pm);
1036 	}
1037 
1038 	if (isset(ALLEXPORT) && !(flags & PM_HASHELEM))
1039 	    flags |= PM_EXPORTED;
1040     } else {
1041 	pm = (Param) hcalloc(sizeof *pm);
1042 	pm->node.nam = nulstring;
1043     }
1044     pm->node.flags = flags & ~PM_LOCAL;
1045 
1046     if(!(pm->node.flags & PM_SPECIAL))
1047 	assigngetset(pm);
1048     return pm;
1049 }
1050 
1051 /* Empty dummy function for special hash parameters. */
1052 
1053 /**/
1054 static void
shempty(void)1055 shempty(void)
1056 {
1057 }
1058 
1059 /*
1060  * Create a simple special hash parameter.
1061  *
1062  * This is for hashes added internally --- it's not possible to add
1063  * special hashes from shell commands.  It's currently used
1064  * - by addparamdef() for special parameters in the zsh/parameter
1065  *   module
1066  * - by ztie for special parameters tied to databases.
1067  */
1068 
1069 /**/
1070 mod_export Param
createspecialhash(char * name,GetNodeFunc get,ScanTabFunc scan,int flags)1071 createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan, int flags)
1072 {
1073     Param pm;
1074     HashTable ht;
1075 
1076     if (!(pm = createparam(name, PM_SPECIAL|PM_HASHED|flags)))
1077 	return NULL;
1078 
1079     /*
1080      * If there's an old parameter, we'll put the new one at
1081      * the current locallevel, so that the old parameter is
1082      * exposed again after leaving the function.  Otherwise,
1083      * we'll leave it alone.  Usually this means the parameter
1084      * will stay in place until explicitly unloaded, however
1085      * if the parameter was previously unset within a function
1086      * we'll inherit the level of that function and follow the
1087      * standard convention that the parameter remains local
1088      * even if unset.
1089      *
1090      * These semantics are similar to those of a normal parameter set
1091      * within a function without a local definition.
1092      */
1093     if (pm->old)
1094 	pm->level = locallevel;
1095     pm->gsu.h = (flags & PM_READONLY) ? &stdhash_gsu :
1096 	&nullsethash_gsu;
1097     pm->u.hash = ht = newhashtable(0, name, NULL);
1098 
1099     ht->hash        = hasher;
1100     ht->emptytable  = (TableFunc) shempty;
1101     ht->filltable   = NULL;
1102     ht->addnode     = (AddNodeFunc) shempty;
1103     ht->getnode     = ht->getnode2 = get;
1104     ht->removenode  = (RemoveNodeFunc) shempty;
1105     ht->disablenode = NULL;
1106     ht->enablenode  = NULL;
1107     ht->freenode    = (FreeNodeFunc) shempty;
1108     ht->printnode   = printparamnode;
1109     ht->scantab     = scan;
1110 
1111     return pm;
1112 }
1113 
1114 
1115 /*
1116  * Copy a parameter
1117  *
1118  * If fakecopy is set, we are just saving the details of a special
1119  * parameter.  Otherwise, the result will be used as a real parameter
1120  * and we need to do more work.
1121  */
1122 
1123 /**/
1124 void
copyparam(Param tpm,Param pm,int fakecopy)1125 copyparam(Param tpm, Param pm, int fakecopy)
1126 {
1127     /*
1128      * Note that tpm, into which we're copying, may not be in permanent
1129      * storage.  However, the values themselves are later used directly
1130      * to set the parameter, so must be permanently allocated (in accordance
1131      * with sets.?fn() usage).
1132      */
1133     tpm->node.flags = pm->node.flags;
1134     tpm->base = pm->base;
1135     tpm->width = pm->width;
1136     tpm->level = pm->level;
1137     if (!fakecopy) {
1138 	tpm->old = pm->old;
1139 	tpm->node.flags &= ~PM_SPECIAL;
1140     }
1141     switch (PM_TYPE(pm->node.flags)) {
1142     case PM_SCALAR:
1143 	tpm->u.str = ztrdup(pm->gsu.s->getfn(pm));
1144 	break;
1145     case PM_INTEGER:
1146 	tpm->u.val = pm->gsu.i->getfn(pm);
1147 	break;
1148     case PM_EFLOAT:
1149     case PM_FFLOAT:
1150 	tpm->u.dval = pm->gsu.f->getfn(pm);
1151 	break;
1152     case PM_ARRAY:
1153 	tpm->u.arr = zarrdup(pm->gsu.a->getfn(pm));
1154 	break;
1155     case PM_HASHED:
1156 	tpm->u.hash = copyparamtable(pm->gsu.h->getfn(pm), pm->node.nam);
1157 	break;
1158     }
1159     /*
1160      * If the value is going to be passed as a real parameter (e.g. this is
1161      * called from inside an associative array), we need the gets and sets
1162      * functions to be useful.
1163      *
1164      * In this case we assume the saved parameter is not itself special,
1165      * so we just use the standard functions.  This is also why we switch off
1166      * PM_SPECIAL.
1167      */
1168     if (!fakecopy)
1169 	assigngetset(tpm);
1170 }
1171 
1172 /* Return 1 if the string s is a valid identifier, else return 0. */
1173 
1174 /**/
1175 mod_export int
isident(char * s)1176 isident(char *s)
1177 {
1178     char *ss;
1179 
1180     if (!*s)			/* empty string is definitely not valid */
1181 	return 0;
1182 
1183     if (idigit(*s)) {
1184 	/* If the first character is `s' is a digit, then all must be */
1185 	for (ss = ++s; *ss; ss++)
1186 	    if (!idigit(*ss))
1187 		break;
1188     } else {
1189 	/* Find the first character in `s' not in the iident type table */
1190 	ss = itype_end(s, IIDENT, 0);
1191     }
1192 
1193     /* If the next character is not [, then it is *
1194      * definitely not a valid identifier.         */
1195     if (!*ss)
1196 	return 1;
1197     if (s == ss)
1198 	return 0;
1199     if (*ss != '[')
1200 	return 0;
1201 
1202     /* Require balanced [ ] pairs with something between */
1203     if (!(ss = parse_subscript(++ss, 1, ']')))
1204 	return 0;
1205     untokenize(s);
1206     return !ss[1];
1207 }
1208 
1209 /*
1210  * Parse a single argument to a parameter subscript.
1211  * The subscripts starts at *str; *str is updated (input/output)
1212  *
1213  * *inv is set to indicate if the subscript is reversed (output)
1214  * v is the Value for the parameter being accessed (input; note
1215  *  v->isarr may be modified, and if v is a hash the parameter will
1216  *  be updated to the element of the hash)
1217  * a2 is 1 if this is the second subscript of a range (input)
1218  * *w is only set if we need to find the end of a word (input; should
1219  *  be set to 0 by the caller).
1220  *
1221  * The final two arguments are to support multibyte characters.
1222  * If supplied they are set to the length of the character before
1223  * the index position and the one at the index position.  If
1224  * multibyte characters are not in use they are set to 1 for
1225  * consistency.  Note they aren't fully handled if a2 is non-zero,
1226  * since they aren't needed.
1227  *
1228  * Returns a raw offset into the value from the start or end (i.e.
1229  * after the arithmetic for Meta and possible multibyte characters has
1230  * been taken into account).  This actually gives the offset *after*
1231  * the character in question; subtract *prevcharlen if necessary.
1232  */
1233 
1234 /**/
1235 static zlong
getarg(char ** str,int * inv,Value v,int a2,zlong * w,int * prevcharlen,int * nextcharlen,int flags)1236 getarg(char **str, int *inv, Value v, int a2, zlong *w,
1237        int *prevcharlen, int *nextcharlen, int flags)
1238 {
1239     int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
1240     int keymatch = 0, needtok = 0, arglen, len, inpar = 0;
1241     char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt, c;
1242     zlong num = 1, beg = 0, r = 0, quote_arg = 0;
1243     Patprog pprog = NULL;
1244 
1245     /*
1246      * If in NO_EXEC mode, the parameters won't be set up properly,
1247      * so just pretend everything is a hash for subscript parsing
1248      */
1249 
1250     ishash = (unset(EXECOPT) ||
1251 	      (v->pm && PM_TYPE(v->pm->node.flags) == PM_HASHED));
1252     if (prevcharlen)
1253 	*prevcharlen = 1;
1254     if (nextcharlen)
1255 	*nextcharlen = 1;
1256 
1257     /* first parse any subscription flags */
1258     if (v->pm && (*s == '(' || *s == Inpar)) {
1259 	int escapes = 0;
1260 	int waste;
1261 	for (s++; *s != ')' && *s != Outpar && s != *str; s++) {
1262 	    switch (*s) {
1263 	    case 'r':
1264 		rev = 1;
1265 		keymatch = down = ind = 0;
1266 		break;
1267 	    case 'R':
1268 		rev = down = 1;
1269 		keymatch = ind = 0;
1270 		break;
1271 	    case 'k':
1272 		keymatch = ishash;
1273 		rev = 1;
1274 		down = ind = 0;
1275 		break;
1276 	    case 'K':
1277 		keymatch = ishash;
1278 		rev = down = 1;
1279 		ind = 0;
1280 		break;
1281 	    case 'i':
1282 		rev = ind = 1;
1283 		down = keymatch = 0;
1284 		break;
1285 	    case 'I':
1286 		rev = ind = down = 1;
1287 		keymatch = 0;
1288 		break;
1289 	    case 'w':
1290 		/* If the parameter is a scalar, then make subscription *
1291 		 * work on a per-word basis instead of characters.      */
1292 		word = 1;
1293 		break;
1294 	    case 'f':
1295 		word = 1;
1296 		sep = "\n";
1297 		break;
1298 	    case 'e':
1299 		quote_arg = 1;
1300 		break;
1301 	    case 'n':
1302 		t = get_strarg(++s, &arglen);
1303 		if (!*t)
1304 		    goto flagerr;
1305 		sav = *t;
1306 		*t = '\0';
1307 		num = mathevalarg(s + arglen, &d);
1308 		if (!num)
1309 		    num = 1;
1310 		*t = sav;
1311 		s = t + arglen - 1;
1312 		break;
1313 	    case 'b':
1314 		hasbeg = 1;
1315 		t = get_strarg(++s, &arglen);
1316 		if (!*t)
1317 		    goto flagerr;
1318 		sav = *t;
1319 		*t = '\0';
1320 		if ((beg = mathevalarg(s + arglen, &d)) > 0)
1321 		    beg--;
1322 		*t = sav;
1323 		s = t + arglen - 1;
1324 		break;
1325 	    case 'p':
1326 		escapes = 1;
1327 		break;
1328 	    case 's':
1329 		/* This gives the string that separates words *
1330 		 * (for use with the `w' flag).               */
1331 		t = get_strarg(++s, &arglen);
1332 		if (!*t)
1333 		    goto flagerr;
1334 		sav = *t;
1335 		*t = '\0';
1336 		s += arglen;
1337 		sep = escapes ? getkeystring(s, &waste, GETKEYS_SEP, NULL)
1338 		    : dupstring(s);
1339 		*t = sav;
1340 		s = t + arglen - 1;
1341 		break;
1342 	    default:
1343 	      flagerr:
1344 		num = 1;
1345 		word = rev = ind = down = keymatch = 0;
1346 		sep = NULL;
1347 		s = *str - 1;
1348 	    }
1349 	}
1350 	if (s != *str)
1351 	    s++;
1352     }
1353     if (num < 0) {
1354 	down = !down;
1355 	num = -num;
1356     }
1357     if (v->isarr & SCANPM_WANTKEYS)
1358 	*inv = (ind || !(v->isarr & SCANPM_WANTVALS));
1359     else if (v->isarr & SCANPM_WANTVALS)
1360 	*inv = 0;
1361     else {
1362 	if (v->isarr) {
1363 	    if (ind) {
1364 		v->isarr |= SCANPM_WANTKEYS;
1365 		v->isarr &= ~SCANPM_WANTVALS;
1366 	    } else if (rev)
1367 		v->isarr |= SCANPM_WANTVALS;
1368 	    /*
1369 	     * This catches the case where we are using "k" (rather
1370 	     * than "K") on a hash.
1371 	     */
1372 	    if (!down && keymatch && ishash)
1373 		v->isarr &= ~SCANPM_MATCHMANY;
1374 	}
1375 	*inv = ind;
1376     }
1377 
1378     for (t = s, i = 0;
1379 	 (c = *t) &&
1380 	     ((c != Outbrack && (ishash || c != ',')) || i || inpar);
1381 	 t++) {
1382 	/* Untokenize inull() except before brackets and double-quotes */
1383 	if (inull(c)) {
1384 	    c = t[1];
1385 	    if (c == '[' || c == ']' ||
1386 		c == '(' || c == ')' ||
1387 		c == '{' || c == '}') {
1388 		/* This test handles nested subscripts in hash keys */
1389 		if (ishash && i)
1390 		    *t = ztokens[*t - Pound];
1391 		needtok = 1;
1392 		++t;
1393 	    } else if (c != '"')
1394 		*t = ztokens[*t - Pound];
1395 	    continue;
1396 	}
1397 	/* Inbrack and Outbrack are probably never found here ... */
1398 	if (c == '[' || c == Inbrack)
1399 	    i++;
1400 	else if (c == ']' || c == Outbrack)
1401 	    i--;
1402 	if (c == '(' || c == Inpar)
1403 	    inpar++;
1404 	else if (c == ')' || c == Outpar)
1405 	    inpar--;
1406 	if (ispecial(c))
1407 	    needtok = 1;
1408     }
1409     if (!c)
1410 	return 0;
1411     *str = tt = t;
1412 
1413     /*
1414      * If in NO_EXEC mode, the parameters won't be set up properly,
1415      * so there's no additional sanity checking we can do.
1416      * Just return 0 now.
1417      */
1418     if (unset(EXECOPT))
1419 	return 0;
1420 
1421     s = dupstrpfx(s, t - s);
1422 
1423     /* If we're NOT reverse subscripting, strip the inull()s so brackets *
1424      * are not backslashed after parsestr().  Otherwise leave them alone *
1425      * so that the brackets will be escaped when we patcompile() or when *
1426      * subscript arithmetic is performed (for nested subscripts).        */
1427     if (ishash && (keymatch || !rev))
1428 	remnulargs(s);
1429     if (needtok) {
1430 	s = dupstring(s);
1431 	if (parsestr(&s))
1432 	    return 0;
1433 	singsub(&s);
1434     } else if (rev)
1435 	remnulargs(s);	/* This is probably always a no-op, but ... */
1436     if (!rev) {
1437 	if (ishash) {
1438 	    HashTable ht = v->pm->gsu.h->getfn(v->pm);
1439 	    if (!ht) {
1440 		if (flags & SCANPM_CHECKING)
1441 		    return 0;
1442 		ht = newparamtable(17, v->pm->node.nam);
1443 		v->pm->gsu.h->setfn(v->pm, ht);
1444 	    }
1445 	    untokenize(s);
1446 	    if (!(v->pm = (Param) ht->getnode(ht, s))) {
1447 		HashTable tht = paramtab;
1448 		paramtab = ht;
1449 		v->pm = createparam(s, PM_SCALAR|PM_UNSET);
1450 		paramtab = tht;
1451 	    }
1452 	    v->isarr = (*inv ? SCANPM_WANTINDEX : 0);
1453 	    v->start = 0;
1454 	    *inv = 0;	/* We've already obtained the "index" (key) */
1455 	    *w = v->end = -1;
1456 	    r = isset(KSHARRAYS) ? 1 : 0;
1457 	} else {
1458 	    r = mathevalarg(s, &s);
1459 	    if (isset(KSHARRAYS) && r >= 0)
1460 		r++;
1461 	}
1462 	if (word && !v->isarr) {
1463 	    s = t = getstrvalue(v);
1464 	    i = wordcount(s, sep, 0);
1465 	    if (r < 0)
1466 		r += i + 1;
1467 	    if (r < 1)
1468 		r = 1;
1469 	    if (r > i)
1470 		r = i;
1471 	    if (!s || !*s)
1472 		return 0;
1473 	    while ((d = findword(&s, sep)) && --r);
1474 	    if (!d)
1475 		return 0;
1476 
1477 	    if (!a2 && *tt != ',')
1478 		*w = (zlong)(s - t);
1479 
1480 	    return (a2 ? s : d + 1) - t;
1481 	} else if (!v->isarr && !word) {
1482 	    int lastcharlen = 1;
1483 	    s = getstrvalue(v);
1484 	    /*
1485 	     * Note for the confused (= pws):  the index r we
1486 	     * have so far is that specified by the user.  The value
1487 	     * passed back is an offset from the start or end of
1488 	     * the string.  Hence it needs correcting at least
1489 	     * for Meta characters and maybe for multibyte characters.
1490 	     */
1491 	    if (r > 0) {
1492 		zlong nchars = r;
1493 
1494 		MB_METACHARINIT();
1495 		for (t = s; nchars && *t; nchars--)
1496 		    t += (lastcharlen = MB_METACHARLEN(t));
1497 		/* for consistency, keep any remainder off the end */
1498 		r = (zlong)(t - s) + nchars;
1499 		if (prevcharlen && !nchars /* ignore if off the end */)
1500 		    *prevcharlen = lastcharlen;
1501 		if (nextcharlen && *t)
1502 		    *nextcharlen = MB_METACHARLEN(t);
1503 	    } else if (r == 0) {
1504 		if (prevcharlen)
1505 		    *prevcharlen = 0;
1506 		if (nextcharlen && *s) {
1507 		    MB_METACHARINIT();
1508 		    *nextcharlen = MB_METACHARLEN(s);
1509 		}
1510 	    } else {
1511 		zlong nchars = (zlong)MB_METASTRLEN(s) + r;
1512 
1513 		if (nchars < 0) {
1514 		    /* make sure this isn't valid as a raw pointer */
1515 		    r -= (zlong)strlen(s);
1516 		} else {
1517 		    MB_METACHARINIT();
1518 		    for (t = s; nchars && *t; nchars--)
1519 			t += (lastcharlen = MB_METACHARLEN(t));
1520 		    r = - (zlong)strlen(t); /* keep negative */
1521 		    if (prevcharlen)
1522 			*prevcharlen = lastcharlen;
1523 		    if (nextcharlen && *t)
1524 			*nextcharlen = MB_METACHARLEN(t);
1525 		}
1526 	    }
1527 	}
1528     } else {
1529 	if (!v->isarr && !word && !quote_arg) {
1530 	    l = strlen(s);
1531 	    if (a2) {
1532 		if (!l || *s != '*') {
1533 		    d = (char *) hcalloc(l + 2);
1534 		    *d = '*';
1535 		    strcpy(d + 1, s);
1536 		    s = d;
1537 		}
1538 	    } else {
1539 		if (!l || s[l - 1] != '*' || (l > 1 && s[l - 2] == '\\')) {
1540 		    d = (char *) hcalloc(l + 2);
1541 		    strcpy(d, s);
1542 		    strcat(d, "*");
1543 		    s = d;
1544 		}
1545 	    }
1546 	}
1547 	if (!keymatch) {
1548 	    if (quote_arg) {
1549 		untokenize(s);
1550 		/* Scalar (e) needs implicit asterisk tokens */
1551 		if (!v->isarr && !word) {
1552 		    l = strlen(s);
1553 		    d = (char *) hcalloc(l + 2);
1554 		    if (a2) {
1555 			*d = Star;
1556 			strcpy(d + 1, s);
1557 		    } else {
1558 			strcpy(d, s);
1559 			d[l] = Star;
1560 			d[l + 1] = '\0';
1561 		    }
1562 		    s = d;
1563 		}
1564 	    } else
1565 		tokenize(s);
1566 	    remnulargs(s);
1567 	    pprog = patcompile(s, 0, NULL);
1568 	} else
1569 	    pprog = NULL;
1570 
1571 	if (v->isarr) {
1572 	    if (ishash) {
1573 		scanprog = pprog;
1574 		scanstr = s;
1575 		if (keymatch)
1576 		    v->isarr |= SCANPM_KEYMATCH;
1577 		else {
1578 		    if (!pprog)
1579 			return 1;
1580 		    if (ind)
1581 			v->isarr |= SCANPM_MATCHKEY;
1582 		    else
1583 			v->isarr |= SCANPM_MATCHVAL;
1584 		}
1585 		if (down)
1586 		    v->isarr |= SCANPM_MATCHMANY;
1587 		if ((ta = getvaluearr(v)) &&
1588 		    (*ta || ((v->isarr & SCANPM_MATCHMANY) &&
1589 			     (v->isarr & (SCANPM_MATCHKEY | SCANPM_MATCHVAL |
1590 					  SCANPM_KEYMATCH))))) {
1591 		    *inv = (v->flags & VALFLAG_INV) ? 1 : 0;
1592 		    *w = v->end;
1593 		    scanprog = NULL;
1594 		    return 1;
1595 		}
1596 		scanprog = NULL;
1597 	    } else
1598 		ta = getarrvalue(v);
1599 	    if (!ta || !*ta)
1600 		return !down;
1601 	    len = arrlen(ta);
1602 	    if (beg < 0)
1603 		beg += len;
1604 	    if (down) {
1605 		if (beg < 0)
1606 		    return 0;
1607 	    } else if (beg >= len)
1608 		return len + 1;
1609 	    if (beg >= 0 && beg < len) {
1610 		if (down) {
1611 		    if (!hasbeg)
1612 			beg = len - 1;
1613 		    for (r = 1 + beg, p = ta + beg; p >= ta; r--, p--) {
1614 			if (pprog && pattry(pprog, *p) && !--num)
1615 			    return r;
1616 		    }
1617 		} else
1618 		    for (r = 1 + beg, p = ta + beg; *p; r++, p++)
1619 			if (pprog && pattry(pprog, *p) && !--num)
1620 			    return r;
1621 	    }
1622 	} else if (word) {
1623 	    ta = sepsplit(d = s = getstrvalue(v), sep, 1, 1);
1624 	    len = arrlen(ta);
1625 	    if (beg < 0)
1626 		beg += len;
1627 	    if (down) {
1628 		if (beg < 0)
1629 		    return 0;
1630 	    } else if (beg >= len)
1631 		return len + 1;
1632 	    if (beg >= 0 && beg < len) {
1633 		if (down) {
1634 		    if (!hasbeg)
1635 			beg = len - 1;
1636 		    for (r = 1 + beg, p = ta + beg; p >= ta; p--, r--)
1637 			if (pprog && pattry(pprog, *p) && !--num)
1638 			    break;
1639 		    if (p < ta)
1640 			return 0;
1641 		} else {
1642 		    for (r = 1 + beg, p = ta + beg; *p; r++, p++)
1643 			if (pprog && pattry(pprog, *p) && !--num)
1644 			    break;
1645 		    if (!*p)
1646 			return 0;
1647 		}
1648 	    }
1649 	    if (a2)
1650 		r++;
1651 	    for (i = 0; (t = findword(&d, sep)) && *t; i++)
1652 		if (!--r) {
1653 		    r = (zlong)(t - s + (a2 ? -1 : 1));
1654 		    if (!a2 && *tt != ',')
1655 			*w = r + strlen(ta[i]) - 1;
1656 		    return r;
1657 		}
1658 	    return a2 ? -1 : 0;
1659 	} else {
1660 	    /* Searching characters */
1661 	    int slen;
1662 	    d = getstrvalue(v);
1663 	    if (!d || !*d)
1664 		return 0;
1665 	    /*
1666 	     * beg and len are character counts, not raw offsets.
1667 	     * Remember we need to return a raw offset.
1668 	     */
1669 	    len = MB_METASTRLEN(d);
1670 	    slen = strlen(d);
1671 	    if (beg < 0)
1672 		beg += len;
1673 	    MB_METACHARINIT();
1674 	    if (beg >= 0 && beg < len) {
1675 		char *de = d + slen;
1676 
1677 		if (a2) {
1678 		    /*
1679 		     * Second argument: we don't need to
1680 		     * handle prevcharlen or nextcharlen, but
1681 		     * we do need to handle characters appropriately.
1682 		     */
1683 		    if (down) {
1684 			int nmatches = 0;
1685 			char *lastpos = NULL;
1686 
1687 			if (!hasbeg)
1688 			    beg = len;
1689 
1690 			/*
1691 			 * See below: we have to move forward,
1692 			 * but need to count from the end.
1693 			 */
1694 			for (t = d, r = 0; r <= beg; r++) {
1695 			    sav = *t;
1696 			    *t = '\0';
1697 			    if (pprog && pattry(pprog, d)) {
1698 				nmatches++;
1699 				lastpos = t;
1700 			    }
1701 			    *t = sav;
1702 			    if (t == de)
1703 				break;
1704 			    t += MB_METACHARLEN(t);
1705 			}
1706 
1707 			if (nmatches >= num) {
1708 			    if (num > 1) {
1709 				nmatches -= num;
1710 				MB_METACHARINIT();
1711 				for (t = d, r = 0; ; r++) {
1712 				    sav = *t;
1713 				    *t = '\0';
1714 				    if (pprog && pattry(pprog, d) &&
1715 					nmatches-- == 0) {
1716 					lastpos = t;
1717 					*t = sav;
1718 					break;
1719 				    }
1720 				    *t = sav;
1721 				    t += MB_METACHARLEN(t);
1722 				}
1723 			    }
1724 			    /* else lastpos is already OK */
1725 
1726 			    return lastpos - d;
1727 			}
1728 		    } else {
1729 			/*
1730 			 * This handling of the b flag
1731 			 * gives odd results, but this is the
1732 			 * way it's always worked.
1733 			 */
1734 			for (t = d; beg && t <= de; beg--)
1735 			    t += MB_METACHARLEN(t);
1736 			for (;;) {
1737 			    sav = *t;
1738 			    *t = '\0';
1739 			    if (pprog && pattry(pprog, d) && !--num) {
1740 				*t = sav;
1741 				/*
1742 				 * This time, don't increment
1743 				 * pointer, since it's already
1744 				 * after everything we matched.
1745 				 */
1746 				return t - d;
1747 			    }
1748 			    *t = sav;
1749 			    if (t == de)
1750 				break;
1751 			    t += MB_METACHARLEN(t);
1752 			}
1753 		    }
1754 		} else {
1755 		    /*
1756 		     * First argument: this is the only case
1757 		     * where we need prevcharlen and nextcharlen.
1758 		     */
1759 		    int lastcharlen;
1760 
1761 		    if (down) {
1762 			int nmatches = 0;
1763 			char *lastpos = NULL;
1764 
1765 			if (!hasbeg)
1766 			    beg = len;
1767 
1768 			/*
1769 			 * We can only move forward through
1770 			 * multibyte strings, so record the
1771 			 * matches.
1772 			 * Unfortunately the count num works
1773 			 * from the end, so it's easy to get the
1774 			 * last one but we need to repeat if
1775 			 * we want another one.
1776 			 */
1777 			for (t = d, r = 0; r <= beg; r++) {
1778 			    if (pprog && pattry(pprog, t)) {
1779 				nmatches++;
1780 				lastpos = t;
1781 			    }
1782 			    if (t == de)
1783 				break;
1784 			    t += MB_METACHARLEN(t);
1785 			}
1786 
1787 			if (nmatches >= num) {
1788 			    if (num > 1) {
1789 				/*
1790 				 * Need to start again and repeat
1791 				 * to get the right match.
1792 				 */
1793 				nmatches -= num;
1794 				MB_METACHARINIT();
1795 				for (t = d, r = 0; ; r++) {
1796 				    if (pprog && pattry(pprog, t) &&
1797 					nmatches-- == 0) {
1798 					lastpos = t;
1799 					break;
1800 				    }
1801 				    t += MB_METACHARLEN(t);
1802 				}
1803 			    }
1804 			    /* else lastpos is already OK */
1805 
1806 			    /* return pointer after matched char */
1807 			    lastpos +=
1808 				(lastcharlen = MB_METACHARLEN(lastpos));
1809 			    if (prevcharlen)
1810 				*prevcharlen = lastcharlen;
1811 			    if (nextcharlen)
1812 				*nextcharlen = MB_METACHARLEN(lastpos);
1813 			    return lastpos - d;
1814 			}
1815 
1816 			for (r = beg + 1, t = d + beg; t >= d; r--, t--) {
1817 			    if (pprog && pattry(pprog, t) &&
1818 				!--num)
1819 				return r;
1820 			}
1821 		    } else {
1822 			for (t = d; beg && t <= de; beg--)
1823 			    t += MB_METACHARLEN(t);
1824 			for (;;) {
1825 			    if (pprog && pattry(pprog, t) && !--num) {
1826 				/* return pointer after matched char */
1827 				t += (lastcharlen = MB_METACHARLEN(t));
1828 				if (prevcharlen)
1829 				    *prevcharlen = lastcharlen;
1830 				if (nextcharlen)
1831 				    *nextcharlen = MB_METACHARLEN(t);
1832 				return t - d;
1833 			    }
1834 			    if (t == de)
1835 				break;
1836 			    t += MB_METACHARLEN(t);
1837 			}
1838 		    }
1839 		}
1840 	    }
1841 	    return down ? 0 : slen + 1;
1842 	}
1843     }
1844     return r;
1845 }
1846 
1847 /*
1848  * Parse a subscript.
1849  *
1850  * pptr: In/Out parameter.  On entry, *ptr points to a "[foo]" string.  On exit
1851  * it will point one past the closing bracket.
1852  *
1853  * v: In/Out parameter.  Its .start and .end members (at least) will be updated
1854  * with the parsed indices.
1855  *
1856  * flags: can be either SCANPM_DQUOTED or zero.  Other bits are not used.
1857  */
1858 
1859 /**/
1860 int
getindex(char ** pptr,Value v,int flags)1861 getindex(char **pptr, Value v, int flags)
1862 {
1863     int start, end, inv = 0;
1864     char *s = *pptr, *tbrack;
1865 
1866     *s++ = '[';
1867     /* Error handled after untokenizing */
1868     s = parse_subscript(s, flags & SCANPM_DQUOTED, ']');
1869     /* Now we untokenize everything except inull() markers so we can check *
1870      * for the '*' and '@' special subscripts.  The inull()s are removed  *
1871      * in getarg() after we know whether we're doing reverse indexing.    */
1872     for (tbrack = *pptr + 1; *tbrack && tbrack != s; tbrack++) {
1873 	if (inull(*tbrack) && !*++tbrack)
1874 	    break;
1875 	if (itok(*tbrack))	/* Need to check for Nularg here? */
1876 	    *tbrack = ztokens[*tbrack - Pound];
1877     }
1878     /* If we reached the end of the string (s == NULL) we have an error */
1879     if (*tbrack)
1880 	*tbrack = Outbrack;
1881     else {
1882 	zerr("invalid subscript");
1883 	*pptr = tbrack;
1884 	return 1;
1885     }
1886     s = *pptr + 1;
1887     if ((s[0] == '*' || s[0] == '@') && s + 1 == tbrack) {
1888 	if ((v->isarr || IS_UNSET_VALUE(v)) && s[0] == '@')
1889 	    v->isarr |= SCANPM_ISVAR_AT;
1890 	v->start = 0;
1891 	v->end = -1;
1892 	s += 2;
1893     } else {
1894 	zlong we = 0, dummy;
1895 	int startprevlen, startnextlen;
1896 
1897 	start = getarg(&s, &inv, v, 0, &we, &startprevlen, &startnextlen,
1898 		       flags);
1899 
1900 	if (inv) {
1901 	    if (!v->isarr && start != 0) {
1902 		char *t, *p;
1903 		t = getstrvalue(v);
1904 		/*
1905 		 * Note for the confused (= pws): this is an inverse
1906 		 * offset so at this stage we need to convert from
1907 		 * the immediate offset into the value that we have
1908 		 * into a logical character position.
1909 		 */
1910 		if (start > 0) {
1911 		    int nstart = 0;
1912 		    char *target = t + start - startprevlen;
1913 
1914 		    p = t;
1915 		    MB_METACHARINIT();
1916 		    while (*p) {
1917 			/*
1918 			 * move up characters, counting how many we
1919 			 * found
1920 			 */
1921 			p += MB_METACHARLEN(p);
1922 			if (p < target)
1923 			    nstart++;
1924 			else {
1925 			    if (p == target)
1926 				nstart++;
1927 			    else
1928 				p = target; /* pretend we hit exactly */
1929 			    break;
1930 			}
1931 		    }
1932 		    /* if start was too big, keep the difference */
1933 		    start = nstart + (target - p) + 1;
1934 		} else {
1935 		    zlong startoff = start + strlen(t);
1936 #ifdef DEBUG
1937 		    dputs("BUG: can't have negative inverse offsets???");
1938 #endif
1939 		    if (startoff < 0) {
1940 			/* invalid: keep index but don't dereference */
1941 			start = startoff;
1942 		    } else {
1943 			/* find start in full characters */
1944 			MB_METACHARINIT();
1945 			for (p = t; p < t + startoff;)
1946 			    p += MB_METACHARLEN(p);
1947 			start = - MB_METASTRLEN(p);
1948 		    }
1949 		}
1950 	    }
1951 	    if (start > 0 && (isset(KSHARRAYS) || (v->pm->node.flags & PM_HASHED)))
1952 		start--;
1953 	    if (v->isarr != SCANPM_WANTINDEX) {
1954 		v->flags |= VALFLAG_INV;
1955 		v->isarr = 0;
1956 		v->start = start;
1957 		v->end = start + 1;
1958 	    }
1959 	    if (*s == ',') {
1960 		zerr("invalid subscript");
1961 		*tbrack = ']';
1962 		*pptr = tbrack+1;
1963 		return 1;
1964 	    }
1965 	    if (s == tbrack)
1966 		s++;
1967 	} else {
1968 	    int com;
1969 
1970 	    if ((com = (*s == ','))) {
1971 		s++;
1972 		end = getarg(&s, &inv, v, 1, &dummy, NULL, NULL, flags);
1973 	    } else {
1974 		end = we ? we : start;
1975 	    }
1976 	    if (start != end)
1977 		com = 1;
1978 	    /*
1979 	     * Somehow the logic sometimes forces us to use the previous
1980 	     * or next character to what we would expect, which is
1981 	     * why we had to calculate them in getarg().
1982 	     */
1983 	    if (start > 0)
1984 		start -= startprevlen;
1985 	    else if (start == 0 && end == 0)
1986 	    {
1987 		/*
1988 		 * Strictly, this range is entirely off the
1989 		 * start of the available index range.
1990 		 * This can't happen with KSH_ARRAYS; we already
1991 		 * altered the start index in getarg().
1992 		 * Are we being strict?
1993 		 */
1994 		if (isset(KSHZEROSUBSCRIPT)) {
1995 		    /*
1996 		     * We're not.
1997 		     * Treat this as accessing the first element of the
1998 		     * array.
1999 		     */
2000 		    end = startnextlen;
2001 		} else {
2002 		    /*
2003 		     * We are.  Flag that this range is invalid
2004 		     * for setting elements.  Set the indexes
2005 		     * to a range that returns empty for other accesses.
2006 		     */
2007 		    v->flags |= VALFLAG_EMPTY;
2008 		    start = -1;
2009 		    com = 1;
2010 		}
2011 	    }
2012 	    if (s == tbrack) {
2013 		s++;
2014 		if (v->isarr && !com &&
2015 		    (!(v->isarr & SCANPM_MATCHMANY) ||
2016 		     !(v->isarr & (SCANPM_MATCHKEY | SCANPM_MATCHVAL |
2017 				   SCANPM_KEYMATCH))))
2018 		    v->isarr = 0;
2019 		v->start = start;
2020 		v->end = end;
2021 	    } else
2022 		s = *pptr;
2023 	}
2024     }
2025     *tbrack = ']';
2026     *pptr = s;
2027     return 0;
2028 }
2029 
2030 
2031 /**/
2032 mod_export Value
getvalue(Value v,char ** pptr,int bracks)2033 getvalue(Value v, char **pptr, int bracks)
2034 {
2035   return fetchvalue(v, pptr, bracks, 0);
2036 }
2037 
2038 /**/
2039 mod_export Value
fetchvalue(Value v,char ** pptr,int bracks,int flags)2040 fetchvalue(Value v, char **pptr, int bracks, int flags)
2041 {
2042     char *s, *t, *ie;
2043     char sav, c;
2044     int ppar = 0;
2045 
2046     s = t = *pptr;
2047 
2048     if (idigit(c = *s)) {
2049 	if (bracks >= 0)
2050 	    ppar = zstrtol(s, &s, 10);
2051 	else
2052 	    ppar = *s++ - '0';
2053     }
2054     else if ((ie = itype_end(s, IIDENT, 0)) != s)
2055 	s = ie;
2056     else if (c == Quest)
2057 	*s++ = '?';
2058     else if (c == Pound)
2059 	*s++ = '#';
2060     else if (c == String)
2061 	*s++ = '$';
2062     else if (c == Qstring)
2063 	*s++ = '$';
2064     else if (c == Star)
2065 	*s++ = '*';
2066     else if (IS_DASH(c))
2067 	*s++ = '-';
2068     else if (c == '#' || c == '?' || c == '$' ||
2069 	     c == '!' || c == '@' || c == '*')
2070 	s++;
2071     else
2072 	return NULL;
2073 
2074     if ((sav = *s))
2075 	*s = '\0';
2076     if (ppar) {
2077 	if (v)
2078 	    memset(v, 0, sizeof(*v));
2079 	else
2080 	    v = (Value) hcalloc(sizeof *v);
2081 	v->pm = argvparam;
2082 	v->flags = 0;
2083 	v->start = ppar - 1;
2084 	v->end = ppar;
2085 	if (sav)
2086 	    *s = sav;
2087     } else {
2088 	Param pm;
2089 	int isvarat;
2090 
2091         isvarat = (t[0] == '@' && !t[1]);
2092 	pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t);
2093 	if (sav)
2094 	    *s = sav;
2095 	*pptr = s;
2096 	if (!pm || (pm->node.flags & PM_UNSET))
2097 	    return NULL;
2098 	if (v)
2099 	    memset(v, 0, sizeof(*v));
2100 	else
2101 	    v = (Value) hcalloc(sizeof *v);
2102 	if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) {
2103 	    /* Overload v->isarr as the flag bits for hashed arrays. */
2104 	    v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0);
2105 	    /* If no flags were passed, we need something to represent *
2106 	     * `true' yet differ from an explicit WANTVALS.  Use a     *
2107 	     * special flag for this case.                             */
2108 	    if (!v->isarr)
2109 		v->isarr = SCANPM_ARRONLY;
2110 	}
2111 	v->pm = pm;
2112 	v->flags = 0;
2113 	v->start = 0;
2114 	v->end = -1;
2115 	if (bracks > 0 && (*s == '[' || *s == Inbrack)) {
2116 	    if (getindex(&s, v, flags)) {
2117 		*pptr = s;
2118 		return v;
2119 	    }
2120 	} else if (!(flags & SCANPM_ASSIGNING) && v->isarr &&
2121 		   itype_end(t, IIDENT, 1) != t && isset(KSHARRAYS))
2122 	    v->end = 1, v->isarr = 0;
2123     }
2124     if (!bracks && *s)
2125 	return NULL;
2126     *pptr = s;
2127 #if 0
2128     /*
2129      * Check for large subscripts that might be erroneous.
2130      * This code is too gross in several ways:
2131      * - the limit is completely arbitrary
2132      * - the test vetoes operations on existing arrays
2133      * - it's not at all clear a general test on large arrays of
2134      *   this kind is any use.
2135      *
2136      * Until someone comes up with workable replacement code it's
2137      * therefore commented out.
2138      */
2139     if (v->start > MAX_ARRLEN) {
2140 	zerr("subscript too %s: %d", "big", v->start + !isset(KSHARRAYS));
2141 	return NULL;
2142     }
2143     if (v->start < -MAX_ARRLEN) {
2144 	zerr("subscript too %s: %d", "small", v->start);
2145 	return NULL;
2146     }
2147     if (v->end > MAX_ARRLEN+1) {
2148 	zerr("subscript too %s: %d", "big", v->end - !!isset(KSHARRAYS));
2149 	return NULL;
2150     }
2151     if (v->end < -MAX_ARRLEN) {
2152 	zerr("subscript too %s: %d", "small", v->end);
2153 	return NULL;
2154     }
2155 #endif
2156     return v;
2157 }
2158 
2159 /**/
2160 mod_export char *
getstrvalue(Value v)2161 getstrvalue(Value v)
2162 {
2163     char *s, **ss;
2164     char buf[BDIGBUFSIZE];
2165     int len;
2166 
2167     if (!v)
2168 	return hcalloc(1);
2169 
2170     if ((v->flags & VALFLAG_INV) && !(v->pm->node.flags & PM_HASHED)) {
2171 	sprintf(buf, "%d", v->start);
2172 	s = dupstring(buf);
2173 	return s;
2174     }
2175 
2176     switch(PM_TYPE(v->pm->node.flags)) {
2177     case PM_HASHED:
2178 	/* (!v->isarr) should be impossible unless emulating ksh */
2179 	if (!v->isarr && EMULATION(EMULATE_KSH)) {
2180 	    s = dupstring("[0]");
2181 	    if (getindex(&s, v, 0) == 0)
2182 		s = getstrvalue(v);
2183 	    return s;
2184 	} /* else fall through */
2185     case PM_ARRAY:
2186 	ss = getvaluearr(v);
2187 	if (v->isarr)
2188 	    s = sepjoin(ss, NULL, 1);
2189 	else {
2190 	    if (v->start < 0)
2191 		v->start += arrlen(ss);
2192 	    s = (arrlen_le(ss, v->start) || v->start < 0) ?
2193 		(char *) hcalloc(1) : ss[v->start];
2194 	}
2195 	return s;
2196     case PM_INTEGER:
2197 	convbase(buf, v->pm->gsu.i->getfn(v->pm), v->pm->base);
2198 	s = dupstring(buf);
2199 	break;
2200     case PM_EFLOAT:
2201     case PM_FFLOAT:
2202 	s = convfloat(v->pm->gsu.f->getfn(v->pm),
2203 		      v->pm->base, v->pm->node.flags, NULL);
2204 	break;
2205     case PM_SCALAR:
2206 	s = v->pm->gsu.s->getfn(v->pm);
2207 	break;
2208     default:
2209 	s = "";
2210 	DPUTS(1, "BUG: param node without valid type");
2211 	break;
2212     }
2213 
2214     if (v->flags & VALFLAG_SUBST) {
2215 	if (v->pm->node.flags & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z)) {
2216 	    size_t fwidth = v->pm->width ? (unsigned int)v->pm->width : MB_METASTRLEN(s);
2217 	    switch (v->pm->node.flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) {
2218 		char *t, *tend;
2219 		size_t t0;
2220 
2221 	    case PM_LEFT:
2222 	    case PM_LEFT | PM_RIGHT_Z:
2223 		t = s;
2224 		if (v->pm->node.flags & PM_RIGHT_Z)
2225 		    while (*t == '0')
2226 			t++;
2227 		else
2228 		    while (iblank(*t))
2229 			t++;
2230 		MB_METACHARINIT();
2231 		for (tend = t, t0 = 0; t0 < fwidth && *tend; t0++)
2232 		    tend += MB_METACHARLEN(tend);
2233 		/*
2234 		 * t0 is the number of characters from t used,
2235 		 * hence (fwidth - t0) is the number of padding
2236 		 * characters.  fwidth is a misnomer: we use
2237 		 * character counts, not character widths.
2238 		 *
2239 		 * (tend - t) is the number of bytes we need
2240 		 * to get fwidth characters or the entire string;
2241 		 * the characters may be multiple bytes.
2242 		 */
2243 		fwidth -= t0; /* padding chars remaining */
2244 		t0 = tend - t; /* bytes to copy from string */
2245 		s = (char *) hcalloc(t0 + fwidth + 1);
2246 		memcpy(s, t, t0);
2247 		if (fwidth)
2248 		    memset(s + t0, ' ', fwidth);
2249 		s[t0 + fwidth] = '\0';
2250 		break;
2251 	    case PM_RIGHT_B:
2252 	    case PM_RIGHT_Z:
2253 	    case PM_RIGHT_Z | PM_RIGHT_B:
2254 		{
2255 		    int zero = 1;
2256 		    /* Calculate length in possibly multibyte chars */
2257 		    unsigned int charlen = MB_METASTRLEN(s);
2258 
2259 		    if (charlen < fwidth) {
2260 			char *valprefend = s;
2261 			int preflen;
2262 			if (v->pm->node.flags & PM_RIGHT_Z) {
2263 			    /*
2264 			     * This is a documented feature: when deciding
2265 			     * whether to pad with zeroes, ignore
2266 			     * leading blanks already in the value;
2267 			     * only look for numbers after that.
2268 			     * Not sure how useful this really is.
2269 			     * It's certainly confusing to code around.
2270 			     */
2271 			    for (t = s; iblank(*t); t++)
2272 				;
2273 			    /*
2274 			     * Allow padding after initial minus
2275 			     * for numeric variables.
2276 			     */
2277 			    if ((v->pm->node.flags &
2278 				 (PM_INTEGER|PM_EFLOAT|PM_FFLOAT)) &&
2279 				*t == '-')
2280 				t++;
2281 			    /*
2282 			     * Allow padding after initial 0x or
2283 			     * base# for integer variables.
2284 			     */
2285 			    if (v->pm->node.flags & PM_INTEGER) {
2286 				if (isset(CBASES) &&
2287 				    t[0] == '0' && t[1] == 'x')
2288 				    t += 2;
2289 				else if ((valprefend = strchr(t, '#')))
2290 				    t = valprefend + 1;
2291 			    }
2292 			    valprefend = t;
2293 			    if (!*t)
2294 				zero = 0;
2295 			    else if (v->pm->node.flags &
2296 				     (PM_INTEGER|PM_EFLOAT|PM_FFLOAT)) {
2297 				/* zero always OK */
2298 			    } else if (!idigit(*t))
2299 				zero = 0;
2300 			}
2301 			/* number of characters needed for padding */
2302 			fwidth -= charlen;
2303 			/* bytes from original string */
2304 			t0 = strlen(s);
2305 			t = (char *) hcalloc(fwidth + t0 + 1);
2306 			/* prefix guaranteed to be single byte chars */
2307 			preflen = valprefend - s;
2308 			memset(t + preflen,
2309 			       (((v->pm->node.flags & PM_RIGHT_B)
2310 				 || !zero) ?       ' ' : '0'), fwidth);
2311 			/*
2312 			 * Copy - or 0x or base# before any padding
2313 			 * zeroes.
2314 			 */
2315 			if (preflen)
2316 			    memcpy(t, s, preflen);
2317 			memcpy(t + preflen + fwidth,
2318 			       valprefend, t0 - preflen);
2319 			t[fwidth + t0] = '\0';
2320 			s = t;
2321 		    } else {
2322 			/* Need to skip (charlen - fwidth) chars */
2323 			for (t0 = charlen - fwidth; t0; t0--)
2324 			    s += MB_METACHARLEN(s);
2325 		    }
2326 		}
2327 		break;
2328 	    }
2329 	}
2330 	switch (v->pm->node.flags & (PM_LOWER | PM_UPPER)) {
2331 	case PM_LOWER:
2332 	    s = casemodify(s, CASMOD_LOWER);
2333 	    break;
2334 	case PM_UPPER:
2335 	    s = casemodify(s, CASMOD_UPPER);
2336 	    break;
2337 	}
2338     }
2339     if (v->start == 0 && v->end == -1)
2340 	return s;
2341 
2342     len = strlen(s);
2343     if (v->start < 0) {
2344 	v->start += len;
2345 	if (v->start < 0)
2346 	    v->start = 0;
2347     }
2348     if (v->end < 0) {
2349 	v->end += len;
2350 	if (v->end >= 0) {
2351 	    char *eptr = s + v->end;
2352 	    if (*eptr)
2353 		v->end += MB_METACHARLEN(eptr);
2354 	}
2355     }
2356 
2357     s = (v->start > len) ? dupstring("") :
2358 	dupstring_wlen(s + v->start, len - v->start);
2359 
2360     if (v->end <= v->start)
2361 	s[0] = '\0';
2362     else if (v->end - v->start <= len - v->start)
2363 	s[v->end - v->start] = '\0';
2364 
2365     return s;
2366 }
2367 
2368 static char *nular[] = {"", NULL};
2369 
2370 /**/
2371 mod_export char **
getarrvalue(Value v)2372 getarrvalue(Value v)
2373 {
2374     char **s;
2375 
2376     if (!v)
2377 	return arrdup(nular);
2378     else if (IS_UNSET_VALUE(v))
2379 	return arrdup(&nular[1]);
2380     if (v->flags & VALFLAG_INV) {
2381 	char buf[DIGBUFSIZE];
2382 
2383 	s = arrdup(nular);
2384 	sprintf(buf, "%d", v->start);
2385 	s[0] = dupstring(buf);
2386 	return s;
2387     }
2388     s = getvaluearr(v);
2389     if (v->start == 0 && v->end == -1)
2390 	return s;
2391     if (v->start < 0)
2392 	v->start += arrlen(s);
2393     if (v->end < 0)
2394 	v->end += arrlen(s) + 1;
2395 
2396     /* Null if 1) array too short, 2) index still negative */
2397     if (v->end <= v->start) {
2398 	s = arrdup_max(nular, 0);
2399     }
2400     else if (v->start < 0) {
2401 	s = arrdup_max(nular, 1);
2402     }
2403     else if (arrlen_le(s, v->start)) {
2404 	/* Handle $ary[i,i] consistently for any $i > $#ary
2405 	 * and $ary[i,j] consistently for any $j > $i > $#ary
2406 	 */
2407 	s = arrdup_max(nular, v->end - (v->start + 1));
2408     }
2409     else {
2410         /* Copy to a point before the end of the source array:
2411          * arrdup_max will copy at most v->end - v->start elements,
2412          * starting from v->start element. Original code said:
2413 	 *  s[v->end - v->start] = NULL
2414          * which means that there are exactly the same number of
2415          * elements as the value of the above *0-based* index.
2416          */
2417 	s = arrdup_max(s + v->start, v->end - v->start);
2418     }
2419 
2420     return s;
2421 }
2422 
2423 /**/
2424 mod_export zlong
getintvalue(Value v)2425 getintvalue(Value v)
2426 {
2427     if (!v)
2428 	return 0;
2429     if (v->flags & VALFLAG_INV)
2430 	return v->start;
2431     if (v->isarr) {
2432 	char **arr = getarrvalue(v);
2433 	if (arr) {
2434 	    char *scal = sepjoin(arr, NULL, 1);
2435 	    return mathevali(scal);
2436 	} else
2437 	    return 0;
2438     }
2439     if (PM_TYPE(v->pm->node.flags) == PM_INTEGER)
2440 	return v->pm->gsu.i->getfn(v->pm);
2441     if (v->pm->node.flags & (PM_EFLOAT|PM_FFLOAT))
2442 	return (zlong)v->pm->gsu.f->getfn(v->pm);
2443     return mathevali(getstrvalue(v));
2444 }
2445 
2446 /**/
2447 mnumber
getnumvalue(Value v)2448 getnumvalue(Value v)
2449 {
2450     mnumber mn;
2451     mn.type = MN_INTEGER;
2452 
2453 
2454     if (!v) {
2455 	mn.u.l = 0;
2456     } else if (v->flags & VALFLAG_INV) {
2457 	mn.u.l = v->start;
2458     } else if (v->isarr) {
2459 	char **arr = getarrvalue(v);
2460 	if (arr) {
2461 	    char *scal = sepjoin(arr, NULL, 1);
2462 	    return matheval(scal);
2463 	} else
2464 	    mn.u.l = 0;
2465     } else if (PM_TYPE(v->pm->node.flags) == PM_INTEGER) {
2466 	mn.u.l = v->pm->gsu.i->getfn(v->pm);
2467     } else if (v->pm->node.flags & (PM_EFLOAT|PM_FFLOAT)) {
2468 	mn.type = MN_FLOAT;
2469 	mn.u.d = v->pm->gsu.f->getfn(v->pm);
2470     } else
2471 	return matheval(getstrvalue(v));
2472     return mn;
2473 }
2474 
2475 /**/
2476 void
export_param(Param pm)2477 export_param(Param pm)
2478 {
2479     char buf[BDIGBUFSIZE], *val;
2480 
2481     if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) {
2482 #if 0	/* Requires changes elsewhere in params.c and builtin.c */
2483 	if (EMULATION(EMULATE_KSH) /* isset(KSHARRAYS) */) {
2484 	    struct value v;
2485 	    v.isarr = 1;
2486 	    v.flags = 0;
2487 	    v.start = 0;
2488 	    v.end = -1;
2489 	    val = getstrvalue(&v);
2490 	} else
2491 #endif
2492 	    return;
2493     } else if (PM_TYPE(pm->node.flags) == PM_INTEGER)
2494 	convbase(val = buf, pm->gsu.i->getfn(pm), pm->base);
2495     else if (pm->node.flags & (PM_EFLOAT|PM_FFLOAT))
2496 	val = convfloat(pm->gsu.f->getfn(pm), pm->base,
2497 			pm->node.flags, NULL);
2498     else
2499 	val = pm->gsu.s->getfn(pm);
2500 
2501     addenv(pm, val);
2502 }
2503 
2504 /**/
2505 mod_export void
setstrvalue(Value v,char * val)2506 setstrvalue(Value v, char *val)
2507 {
2508     assignstrvalue(v, val, 0);
2509 }
2510 
2511 /**/
2512 mod_export void
assignstrvalue(Value v,char * val,int flags)2513 assignstrvalue(Value v, char *val, int flags)
2514 {
2515     if (unset(EXECOPT))
2516 	return;
2517     if (v->pm->node.flags & PM_READONLY) {
2518 	zerr("read-only variable: %s", v->pm->node.nam);
2519 	zsfree(val);
2520 	return;
2521     }
2522     if ((v->pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
2523 	zerr("%s: restricted", v->pm->node.nam);
2524 	zsfree(val);
2525 	return;
2526     }
2527     if ((v->pm->node.flags & PM_HASHED) &&
2528 	(v->isarr & (SCANPM_MATCHMANY|SCANPM_ARRONLY))) {
2529 	zerr("%s: attempt to set slice of associative array", v->pm->node.nam);
2530 	zsfree(val);
2531 	return;
2532     }
2533     if (v->flags & VALFLAG_EMPTY) {
2534 	zerr("%s: assignment to invalid subscript range", v->pm->node.nam);
2535 	zsfree(val);
2536 	return;
2537     }
2538     v->pm->node.flags &= ~PM_UNSET;
2539     switch (PM_TYPE(v->pm->node.flags)) {
2540     case PM_SCALAR:
2541 	if (v->start == 0 && v->end == -1) {
2542 	    v->pm->gsu.s->setfn(v->pm, val);
2543 	    if ((v->pm->node.flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) &&
2544 		!v->pm->width)
2545 		v->pm->width = strlen(val);
2546 	} else {
2547 	    char *z, *x;
2548             int zlen, vlen, newsize;
2549 
2550             z = v->pm->gsu.s->getfn(v->pm);
2551             zlen = strlen(z);
2552 
2553 	    if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS))
2554 		v->start--, v->end--;
2555 	    if (v->start < 0) {
2556 		v->start += zlen;
2557 		if (v->start < 0)
2558 		    v->start = 0;
2559 	    }
2560 	    if (v->start > zlen)
2561 		v->start = zlen;
2562 	    if (v->end < 0) {
2563 		v->end += zlen;
2564 		if (v->end < 0) {
2565 		    v->end = 0;
2566 		} else if (v->end >= zlen) {
2567 		    v->end = zlen;
2568 		} else {
2569 #ifdef MULTIBYTE_SUPPORT
2570 		    if (isset(MULTIBYTE)) {
2571 			v->end += MB_METACHARLEN(z + v->end);
2572 		    } else {
2573 			v->end++;
2574 		    }
2575 #else
2576 		    v->end++;
2577 #endif
2578 		}
2579 	    }
2580 	    else if (v->end > zlen)
2581 		v->end = zlen;
2582 
2583             vlen = strlen(val);
2584             /* Characters preceding start index +
2585                characters of what is assigned +
2586                characters following end index */
2587             newsize = v->start + vlen + (zlen - v->end);
2588 
2589             /* Does new size differ? */
2590             if (newsize != zlen || v->pm->gsu.s->setfn != strsetfn) {
2591                 x = (char *) zalloc(newsize + 1);
2592                 strncpy(x, z, v->start);
2593                 strcpy(x + v->start, val);
2594                 strcat(x + v->start, z + v->end);
2595                 v->pm->gsu.s->setfn(v->pm, x);
2596             } else {
2597 		Param pm = v->pm;
2598                 /* Size doesn't change, can limit actions to only
2599                  * overwriting bytes in already allocated string */
2600 		memcpy(z + v->start, val, vlen);
2601 		/* Implement remainder of strsetfn */
2602 		if (!(pm->node.flags & PM_HASHELEM) &&
2603 		    ((pm->node.flags & PM_NAMEDDIR) ||
2604 		     isset(AUTONAMEDIRS))) {
2605 		    pm->node.flags |= PM_NAMEDDIR;
2606 		    adduserdir(pm->node.nam, z, 0, 0);
2607 		}
2608             }
2609             zsfree(val);
2610 	}
2611 	break;
2612     case PM_INTEGER:
2613 	if (val) {
2614 	    zlong ival;
2615 	    if (flags & ASSPM_ENV_IMPORT) {
2616 		char *ptr;
2617 		ival = zstrtol_underscore(val, &ptr, 0, 1);
2618 	    } else
2619 		ival = mathevali(val);
2620 	    v->pm->gsu.i->setfn(v->pm, ival);
2621 	    if ((v->pm->node.flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) &&
2622 		!v->pm->width)
2623 		v->pm->width = strlen(val);
2624 	    zsfree(val);
2625 	}
2626 	if (!v->pm->base && lastbase != -1)
2627 	    v->pm->base = lastbase;
2628 	break;
2629     case PM_EFLOAT:
2630     case PM_FFLOAT:
2631 	if (val) {
2632 	    mnumber mn;
2633 	    if (flags & ASSPM_ENV_IMPORT) {
2634 		char *ptr;
2635 		mn.type = MN_FLOAT;
2636 		mn.u.d = strtod(val, &ptr);
2637 	    } else
2638 		mn = matheval(val);
2639 	    v->pm->gsu.f->setfn(v->pm, (mn.type & MN_FLOAT) ? mn.u.d :
2640 			       (double)mn.u.l);
2641 	    if ((v->pm->node.flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) &&
2642 		!v->pm->width)
2643 		v->pm->width = strlen(val);
2644 	    zsfree(val);
2645 	}
2646 	break;
2647     case PM_ARRAY:
2648 	{
2649 	    char **ss = (char **) zalloc(2 * sizeof(char *));
2650 
2651 	    ss[0] = val;
2652 	    ss[1] = NULL;
2653 	    setarrvalue(v, ss);
2654 	}
2655 	break;
2656     case PM_HASHED:
2657         {
2658 	    if (foundparam == NULL)
2659 	    {
2660 		zerr("%s: attempt to set associative array to scalar",
2661 		     v->pm->node.nam);
2662 		zsfree(val);
2663 		return;
2664 	    }
2665 	    else
2666 		foundparam->gsu.s->setfn(foundparam, val);
2667         }
2668 	break;
2669     }
2670     if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) &&
2671 	 !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) ||
2672 	(v->pm->node.flags & PM_ARRAY) || v->pm->ename)
2673 	return;
2674     export_param(v->pm);
2675 }
2676 
2677 /**/
2678 void
setnumvalue(Value v,mnumber val)2679 setnumvalue(Value v, mnumber val)
2680 {
2681     char buf[BDIGBUFSIZE], *p;
2682 
2683     if (unset(EXECOPT))
2684 	return;
2685     if (v->pm->node.flags & PM_READONLY) {
2686 	zerr("read-only variable: %s", v->pm->node.nam);
2687 	return;
2688     }
2689     if ((v->pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
2690 	zerr("%s: restricted", v->pm->node.nam);
2691 	return;
2692     }
2693     switch (PM_TYPE(v->pm->node.flags)) {
2694     case PM_SCALAR:
2695     case PM_ARRAY:
2696 	if ((val.type & MN_INTEGER) || outputradix) {
2697 	    if (!(val.type & MN_INTEGER))
2698 		val.u.l = (zlong) val.u.d;
2699 	    p = convbase_underscore(buf, val.u.l, outputradix,
2700 				    outputunderscore);
2701 	} else
2702 	    p = convfloat_underscore(val.u.d, outputunderscore);
2703 	setstrvalue(v, ztrdup(p));
2704 	break;
2705     case PM_INTEGER:
2706 	v->pm->gsu.i->setfn(v->pm, (val.type & MN_INTEGER) ? val.u.l :
2707 			    (zlong) val.u.d);
2708 	setstrvalue(v, NULL);
2709 	break;
2710     case PM_EFLOAT:
2711     case PM_FFLOAT:
2712 	v->pm->gsu.f->setfn(v->pm, (val.type & MN_INTEGER) ?
2713 			    (double)val.u.l : val.u.d);
2714 	setstrvalue(v, NULL);
2715 	break;
2716     }
2717 }
2718 
2719 /**/
2720 mod_export void
setarrvalue(Value v,char ** val)2721 setarrvalue(Value v, char **val)
2722 {
2723     if (unset(EXECOPT))
2724 	return;
2725     if (v->pm->node.flags & PM_READONLY) {
2726 	zerr("read-only variable: %s", v->pm->node.nam);
2727 	freearray(val);
2728 	return;
2729     }
2730     if ((v->pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
2731 	zerr("%s: restricted", v->pm->node.nam);
2732 	freearray(val);
2733 	return;
2734     }
2735     if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED))) {
2736 	freearray(val);
2737 	zerr("%s: attempt to assign array value to non-array",
2738 	     v->pm->node.nam);
2739 	return;
2740     }
2741     if (v->flags & VALFLAG_EMPTY) {
2742 	zerr("%s: assignment to invalid subscript range", v->pm->node.nam);
2743 	freearray(val);
2744 	return;
2745     }
2746 
2747     if (v->start == 0 && v->end == -1) {
2748 	if (PM_TYPE(v->pm->node.flags) == PM_HASHED)
2749 	    arrhashsetfn(v->pm, val, 0);
2750 	else
2751 	    v->pm->gsu.a->setfn(v->pm, val);
2752     } else if (v->start == -1 && v->end == 0 &&
2753     	    PM_TYPE(v->pm->node.flags) == PM_HASHED) {
2754     	arrhashsetfn(v->pm, val, ASSPM_AUGMENT);
2755     } else if ((PM_TYPE(v->pm->node.flags) == PM_HASHED)) {
2756 	freearray(val);
2757 	zerr("%s: attempt to set slice of associative array",
2758 	     v->pm->node.nam);
2759 	return;
2760     } else {
2761 	char **const old = v->pm->gsu.a->getfn(v->pm);
2762 	char **new;
2763 	char **p, **q, **r; /* index variables */
2764 	const int pre_assignment_length = arrlen(old);
2765 	int post_assignment_length;
2766 	int i;
2767 
2768 	q = old;
2769 
2770 	if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS)) {
2771 	    if (v->start > 0)
2772 		v->start--;
2773 	    v->end--;
2774 	}
2775 	if (v->start < 0) {
2776 	    v->start += pre_assignment_length;
2777 	    if (v->start < 0)
2778 		v->start = 0;
2779 	}
2780 	if (v->end < 0) {
2781 	    v->end += pre_assignment_length + 1;
2782 	    if (v->end < 0)
2783 		v->end = 0;
2784 	}
2785 	if (v->end < v->start)
2786 	    v->end = v->start;
2787 
2788 	post_assignment_length = v->start + arrlen(val);
2789 	if (v->end < pre_assignment_length) {
2790 	    /*
2791 	     * Allocate room for array elements between the end of the slice `v'
2792 	     * and the original array's end.
2793 	     */
2794 	    post_assignment_length += pre_assignment_length - v->end;
2795 	}
2796 
2797 	if (pre_assignment_length == post_assignment_length
2798 	    && v->pm->gsu.a->setfn == arrsetfn
2799 	    /* ... and isn't something that arrsetfn() treats specially */
2800 	    && 0 == (v->pm->node.flags & (PM_SPECIAL|PM_UNIQUE))
2801 	    && NULL == v->pm->ename)
2802 	{
2803 	    /* v->start is 0-based */
2804 	    p = old + v->start;
2805 	    for (r = val; *r;) {
2806 		/* Free previous string */
2807 		zsfree(*p);
2808 		/* Give away ownership of the string */
2809 		*p++ = *r++;
2810 	    }
2811 	} else {
2812             /* arr+=( ... )
2813              * arr[${#arr}+x,...]=( ... ) */
2814             if (post_assignment_length > pre_assignment_length &&
2815                     pre_assignment_length <= v->start &&
2816                     pre_assignment_length > 0 &&
2817                     v->pm->gsu.a->setfn == arrsetfn)
2818             {
2819                 p = new = (char **) zrealloc(old, sizeof(char *)
2820                                            * (post_assignment_length + 1));
2821 
2822                 p += pre_assignment_length; /* after old elements */
2823 
2824                 /* Consider 1 < 0, case for a=( 1 ); a[1,..] =
2825                  *          1 < 1, case for a=( 1 ); a[2,..] = */
2826                 if (pre_assignment_length < v->start) {
2827                     for (i = pre_assignment_length; i < v->start; i++) {
2828                         *p++ = ztrdup("");
2829                     }
2830                 }
2831 
2832                 for (r = val; *r;) {
2833                     /* Give away ownership of the string */
2834                     *p++ = *r++;
2835                 }
2836 
2837                 /* v->end doesn't matter:
2838                  * a=( 1 2 ); a[4,100]=( a b ); echo "${(q@)a}"
2839                  * 1 2 '' a b */
2840                 *p = NULL;
2841 
2842                 v->pm->u.arr = NULL;
2843                 v->pm->gsu.a->setfn(v->pm, new);
2844             } else {
2845                 p = new = (char **) zalloc(sizeof(char *)
2846                                            * (post_assignment_length + 1));
2847                 for (i = 0; i < v->start; i++)
2848                     *p++ = i < pre_assignment_length ? ztrdup(*q++) : ztrdup("");
2849                 for (r = val; *r;) {
2850                     /* Give away ownership of the string */
2851                     *p++ = *r++;
2852                 }
2853                 if (v->end < pre_assignment_length)
2854                     for (q = old + v->end; *q;)
2855                         *p++ = ztrdup(*q++);
2856                 *p = NULL;
2857 
2858                 v->pm->gsu.a->setfn(v->pm, new);
2859             }
2860 
2861 	    DPUTS2(p - new != post_assignment_length, "setarrvalue: wrong allocation: %d 1= %lu",
2862 		   post_assignment_length, (unsigned long)(p - new));
2863 	}
2864 
2865         /* Ownership of all strings has been
2866          * given away, can plainly free */
2867 	free(val);
2868     }
2869 }
2870 
2871 /* Retrieve an integer parameter */
2872 
2873 /**/
2874 mod_export zlong
getiparam(char * s)2875 getiparam(char *s)
2876 {
2877     struct value vbuf;
2878     Value v;
2879 
2880     if (!(v = getvalue(&vbuf, &s, 1)))
2881 	return 0;
2882     return getintvalue(v);
2883 }
2884 
2885 /* Retrieve a numerical parameter, either integer or floating */
2886 
2887 /**/
2888 mnumber
getnparam(char * s)2889 getnparam(char *s)
2890 {
2891     struct value vbuf;
2892     Value v;
2893 
2894     if (!(v = getvalue(&vbuf, &s, 1))) {
2895 	mnumber mn;
2896 	mn.type = MN_INTEGER;
2897 	mn.u.l = 0;
2898 	return mn;
2899     }
2900     return getnumvalue(v);
2901 }
2902 
2903 /* Retrieve a scalar (string) parameter */
2904 
2905 /**/
2906 mod_export char *
getsparam(char * s)2907 getsparam(char *s)
2908 {
2909     struct value vbuf;
2910     Value v;
2911 
2912     if (!(v = getvalue(&vbuf, &s, 0)))
2913 	return NULL;
2914     return getstrvalue(v);
2915 }
2916 
2917 /**/
2918 mod_export char *
getsparam_u(char * s)2919 getsparam_u(char *s)
2920 {
2921     if ((s = getsparam(s)))
2922 	return unmetafy(s, NULL);
2923     return s;
2924 }
2925 
2926 /* Retrieve an array parameter */
2927 
2928 /**/
2929 mod_export char **
getaparam(char * s)2930 getaparam(char *s)
2931 {
2932     struct value vbuf;
2933     Value v;
2934 
2935     if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) &&
2936 	PM_TYPE(v->pm->node.flags) == PM_ARRAY)
2937 	return v->pm->gsu.a->getfn(v->pm);
2938     return NULL;
2939 }
2940 
2941 /* Retrieve an assoc array parameter as an array */
2942 
2943 /**/
2944 mod_export char **
gethparam(char * s)2945 gethparam(char *s)
2946 {
2947     struct value vbuf;
2948     Value v;
2949 
2950     if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) &&
2951 	PM_TYPE(v->pm->node.flags) == PM_HASHED)
2952 	return paramvalarr(v->pm->gsu.h->getfn(v->pm), SCANPM_WANTVALS);
2953     return NULL;
2954 }
2955 
2956 /* Retrieve the keys of an assoc array parameter as an array */
2957 
2958 /**/
2959 mod_export char **
gethkparam(char * s)2960 gethkparam(char *s)
2961 {
2962     struct value vbuf;
2963     Value v;
2964 
2965     if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) &&
2966 	PM_TYPE(v->pm->node.flags) == PM_HASHED)
2967 	return paramvalarr(v->pm->gsu.h->getfn(v->pm), SCANPM_WANTKEYS);
2968     return NULL;
2969 }
2970 
2971 /*
2972  * Function behind WARNCREATEGLOBAL and WARNNESTEDVAR option.
2973  *
2974  * For WARNNESTEDVAR:
2975  * Called when the variable is created.
2976  * Apply heuristics to see if this variable was just created
2977  * globally but in a local context.
2978  *
2979  * For WARNNESTEDVAR:
2980  * Called when the variable already exists and is set.
2981  * Apply heuristics to see if this variable is setting
2982  * a variable that was created in a less nested function
2983  * or globally.
2984  */
2985 
2986 /**/
2987 static void
check_warn_pm(Param pm,const char * pmtype,int created,int may_warn_about_nested_vars)2988 check_warn_pm(Param pm, const char *pmtype, int created,
2989 	      int may_warn_about_nested_vars)
2990 {
2991     Funcstack i;
2992 
2993     if (!may_warn_about_nested_vars && !created)
2994 	return;
2995 
2996     if (created && isset(WARNCREATEGLOBAL)) {
2997 	if (locallevel <= forklevel || pm->level != 0)
2998 	    return;
2999     } else if (!created && isset(WARNNESTEDVAR)) {
3000 	if (pm->level >= locallevel)
3001 	    return;
3002     } else
3003 	return;
3004 
3005     if (pm->node.flags & PM_SPECIAL)
3006 	return;
3007 
3008     for (i = funcstack; i; i = i->prev) {
3009 	if (i->tp == FS_FUNC) {
3010 	    char *msg;
3011 	    DPUTS(!i->name, "funcstack entry with no name");
3012 	    msg = created ?
3013 		"%s parameter %s created globally in function %s" :
3014 		"%s parameter %s set in enclosing scope in function %s";
3015 	    zwarn(msg, pmtype, pm->node.nam, i->name);
3016 	    break;
3017 	}
3018     }
3019 }
3020 
3021 /**/
3022 mod_export Param
assignsparam(char * s,char * val,int flags)3023 assignsparam(char *s, char *val, int flags)
3024 {
3025     struct value vbuf;
3026     Value v;
3027     char *t = s;
3028     char *ss, *copy, *var;
3029     size_t lvar;
3030     mnumber lhs, rhs;
3031     int sstart, created = 0;
3032 
3033     if (!isident(s)) {
3034 	zerr("not an identifier: %s", s);
3035 	zsfree(val);
3036 	errflag |= ERRFLAG_ERROR;
3037 	return NULL;
3038     }
3039     queue_signals();
3040     if ((ss = strchr(s, '['))) {
3041 	*ss = '\0';
3042 	if (!(v = getvalue(&vbuf, &s, 1))) {
3043 	    createparam(t, PM_ARRAY);
3044 	    created = 1;
3045 	} else {
3046 	    if (v->pm->node.flags & PM_READONLY) {
3047 		zerr("read-only variable: %s", v->pm->node.nam);
3048 		*ss = '[';
3049 		zsfree(val);
3050 		unqueue_signals();
3051 		return NULL;
3052 	    }
3053 	    /*
3054 	     * Parameter defined here is a temporary bogus one.
3055 	     * Don't warn about anything.
3056 	     */
3057 	    flags &= ~ASSPM_WARN;
3058 	}
3059 	*ss = '[';
3060 	v = NULL;
3061     } else {
3062 	if (!(v = getvalue(&vbuf, &s, 1))) {
3063 	    createparam(t, PM_SCALAR);
3064 	    created = 1;
3065 	} else if ((((v->pm->node.flags & PM_ARRAY) && !(flags & ASSPM_AUGMENT)) ||
3066 	    	 (v->pm->node.flags & PM_HASHED)) &&
3067 		 !(v->pm->node.flags & (PM_SPECIAL|PM_TIED)) &&
3068 		 unset(KSHARRAYS)) {
3069 	    unsetparam(t);
3070 	    createparam(t, PM_SCALAR);
3071 	    /* not regarded as a new creation */
3072 	    v = NULL;
3073 	}
3074     }
3075     if (!v && !(v = getvalue(&vbuf, &t, 1))) {
3076 	unqueue_signals();
3077 	zsfree(val);
3078 	/* errflag |= ERRFLAG_ERROR; */
3079 	return NULL;
3080     }
3081     if (flags & ASSPM_WARN)
3082 	check_warn_pm(v->pm, "scalar", created, 1);
3083     if (flags & ASSPM_AUGMENT) {
3084 	if (v->start == 0 && v->end == -1) {
3085 	    switch (PM_TYPE(v->pm->node.flags)) {
3086 	    case PM_SCALAR:
3087 		v->start = INT_MAX;  /* just append to scalar value */
3088 		break;
3089 	    case PM_INTEGER:
3090 	    case PM_EFLOAT:
3091 	    case PM_FFLOAT:
3092 		rhs = matheval(val);
3093 		lhs = getnumvalue(v);
3094 		if (lhs.type == MN_FLOAT) {
3095 		    if ((rhs.type) == MN_FLOAT)
3096         		lhs.u.d = lhs.u.d + rhs.u.d;
3097 		    else
3098 			lhs.u.d = lhs.u.d + (double)rhs.u.l;
3099 		} else {
3100         	    if ((rhs.type) == MN_INTEGER)
3101 			lhs.u.l = lhs.u.l + rhs.u.l;
3102 		    else
3103 			lhs.u.l = lhs.u.l + (zlong)rhs.u.d;
3104 		}
3105 		setnumvalue(v, lhs);
3106     	    	unqueue_signals();
3107 		zsfree(val);
3108 		return v->pm; /* avoid later setstrvalue() call */
3109 	    case PM_ARRAY:
3110 	    	if (unset(KSHARRAYS)) {
3111 		    v->start = arrlen(v->pm->gsu.a->getfn(v->pm));
3112 		    v->end = v->start + 1;
3113 		} else {
3114 		    /* ksh appends scalar to first element */
3115 		    v->end = 1;
3116 		    goto kshappend;
3117 		}
3118 		break;
3119 	    }
3120 	} else {
3121 	    switch (PM_TYPE(v->pm->node.flags)) {
3122 	    case PM_SCALAR:
3123     		if (v->end > 0)
3124 		    v->start = v->end;
3125 		else
3126 		    v->start = v->end = strlen(v->pm->gsu.s->getfn(v->pm)) +
3127 			v->end + 1;
3128 	    	break;
3129 	    case PM_INTEGER:
3130 	    case PM_EFLOAT:
3131 	    case PM_FFLOAT:
3132 		unqueue_signals();
3133 		zerr("attempt to add to slice of a numeric variable");
3134 		zsfree(val);
3135 		return NULL;
3136 	    case PM_ARRAY:
3137 	      kshappend:
3138 		/* treat slice as the end element */
3139 		v->start = sstart = v->end > 0 ? v->end - 1 : v->end;
3140 		v->isarr = 0;
3141 		var = getstrvalue(v);
3142 		v->start = sstart;
3143 		copy = val;
3144 		lvar = strlen(var);
3145 		val = (char *)zalloc(lvar + strlen(val) + 1);
3146 		strcpy(val, var);
3147 		strcpy(val + lvar, copy);
3148 		zsfree(copy);
3149 		break;
3150 	    }
3151 	}
3152     }
3153 
3154     assignstrvalue(v, val, flags);
3155     unqueue_signals();
3156     return v->pm;
3157 }
3158 
3159 /**/
3160 mod_export Param
setsparam(char * s,char * val)3161 setsparam(char *s, char *val)
3162 {
3163     return assignsparam(s, val, ASSPM_WARN);
3164 }
3165 
3166 /**/
3167 mod_export Param
assignaparam(char * s,char ** val,int flags)3168 assignaparam(char *s, char **val, int flags)
3169 {
3170     struct value vbuf;
3171     Value v;
3172     char *t = s;
3173     char *ss;
3174     int created = 0;
3175     int may_warn_about_nested_vars = 1;
3176 
3177     if (!isident(s)) {
3178 	zerr("not an identifier: %s", s);
3179 	freearray(val);
3180 	errflag |= ERRFLAG_ERROR;
3181 	return NULL;
3182     }
3183     queue_signals();
3184     if ((ss = strchr(s, '['))) {
3185 	*ss = '\0';
3186 	if (!(v = getvalue(&vbuf, &s, 1))) {
3187 	    createparam(t, PM_ARRAY);
3188 	    created = 1;
3189 	} else {
3190 	    may_warn_about_nested_vars = 0;
3191 	}
3192 	*ss = '[';
3193 	if (v && PM_TYPE(v->pm->node.flags) == PM_HASHED) {
3194 	    unqueue_signals();
3195 	    zerr("%s: attempt to set slice of associative array",
3196 		 v->pm->node.nam);
3197 	    freearray(val);
3198 	    errflag |= ERRFLAG_ERROR;
3199 	    return NULL;
3200 	}
3201 	v = NULL;
3202     } else {
3203 	if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) {
3204 	    createparam(t, PM_ARRAY);
3205 	    created = 1;
3206 	} else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) &&
3207 		 !(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) {
3208 	    int uniq = v->pm->node.flags & PM_UNIQUE;
3209 	    if (flags & ASSPM_AUGMENT) {
3210 	    	/* insert old value at the beginning of the val array */
3211 		char **new;
3212 		int lv = arrlen(val);
3213 
3214 		new = (char **) zalloc(sizeof(char *) * (lv + 2));
3215 		*new = ztrdup(getstrvalue(v));
3216 		memcpy(new+1, val, sizeof(char *) * (lv + 1));
3217 		free(val);
3218 		val = new;
3219 	    }
3220 	    unsetparam(t);
3221 	    createparam(t, PM_ARRAY | uniq);
3222 	    v = NULL;
3223 	}
3224     }
3225     if (!v)
3226 	if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) {
3227 	    unqueue_signals();
3228 	    freearray(val);
3229 	    /* errflag |= ERRFLAG_ERROR; */
3230 	    return NULL;
3231 	}
3232 
3233     if (flags & ASSPM_WARN)
3234 	check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars);
3235 
3236     /*
3237      * At this point, we may have array entries consisting of
3238      * - a Marker element --- normally allocated array entry but
3239      *   with just Marker char and null
3240      * - an array index element --- as normal for associative array,
3241      *   but non-standard for normal array which we handle now.
3242      * - a value for the indexed element.
3243      * This only applies if the flag ASSPM_KEY_VALUE is passed in,
3244      * indicating prefork() detected this syntax.
3245      *
3246      * For associative arrays we just junk the Marker elements.
3247      */
3248     if (flags & ASSPM_KEY_VALUE) {
3249 	char **aptr;
3250 	if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) {
3251 	    /*
3252 	     * This is an ordinary array with key / value pairs.
3253 	     */
3254 	    int maxlen, origlen, nextind;
3255 	    char **fullval, **origptr;
3256 	    zlong *subscripts = (zlong *)zhalloc(arrlen(val) * sizeof(zlong));
3257 	    zlong *iptr = subscripts;
3258 	    if (flags & ASSPM_AUGMENT) {
3259 		origptr = v->pm->gsu.a->getfn(v->pm);
3260 		maxlen = origlen = arrlen(origptr);
3261 	    } else {
3262 		maxlen = origlen = 0;
3263 		origptr = NULL;
3264 	    }
3265 	    nextind = 0;
3266 	    for (aptr = val; *aptr; ) {
3267 		if (**aptr == Marker) {
3268 		    *iptr = mathevali(*++aptr);
3269 		    if (*iptr < 0 ||
3270 			(!isset(KSHARRAYS) && *iptr == 0)) {
3271 			unqueue_signals();
3272 			zerr("bad subscript for direct array assignment: %s", *aptr);
3273 			freearray(val);
3274 			return NULL;
3275 		    }
3276 		    if (!isset(KSHARRAYS))
3277 			--*iptr;
3278 		    nextind = *iptr + 1;
3279 		    ++iptr;
3280 		    aptr += 2;
3281 		} else {
3282 		    ++nextind;
3283 		    ++aptr;
3284 		}
3285 		if (nextind > maxlen)
3286 		    maxlen = nextind;
3287 	    }
3288 	    fullval = zshcalloc((maxlen+1) * sizeof(char *));
3289 	    if (!fullval) {
3290 		zerr("array too large");
3291 		freearray(val);
3292 		return NULL;
3293 	    }
3294 	    fullval[maxlen] = NULL;
3295 	    if (flags & ASSPM_AUGMENT) {
3296 		char **srcptr = origptr;
3297 		for (aptr = fullval; aptr <= fullval + origlen; aptr++) {
3298 		    *aptr = ztrdup(*srcptr);
3299 		    srcptr++;
3300 		}
3301 	    }
3302 	    iptr = subscripts;
3303 	    nextind = 0;
3304 	    for (aptr = val; *aptr; ++aptr) {
3305 		char *old;
3306 		if (**aptr == Marker) {
3307 		    int augment = ((*aptr)[1] == '+');
3308 		    zsfree(*aptr);
3309 		    zsfree(*++aptr); /* Index, no longer needed */
3310 		    old = fullval[*iptr];
3311 		    if (augment && old) {
3312 			fullval[*iptr] = bicat(old, *++aptr);
3313 			zsfree(*aptr);
3314 		    } else {
3315 			fullval[*iptr] = *++aptr;
3316 		    }
3317 		    nextind = *iptr + 1;
3318 		    ++iptr;
3319 		} else {
3320 		    old = fullval[nextind];
3321 		    fullval[nextind] = *aptr;
3322 		    ++nextind;
3323 		}
3324 		if (old)
3325 		    zsfree(old);
3326 		/* aptr now on value in both cases */
3327 	    }
3328 	    if (*aptr) {		/* Shouldn't be possible */
3329 		DPUTS(1, "Extra element in key / value array");
3330 		zsfree(*aptr);
3331 	    }
3332 	    free(val);
3333 	    for (aptr = fullval; aptr < fullval + maxlen; aptr++) {
3334 		/*
3335 		 * Remember we don't have sparse arrays but and they're null
3336 		 * terminated --- so any value we don't set has to be an
3337 		 * empty string.
3338 		 */
3339 		if (!*aptr)
3340 		    *aptr = ztrdup("");
3341 	    }
3342 	    setarrvalue(v, fullval);
3343 	    unqueue_signals();
3344 	    return v->pm;
3345 	} else if (PM_TYPE(v->pm->node.flags & PM_HASHED)) {
3346 	    /*
3347 	     * We strictly enforce [key]=value syntax for associative
3348 	     * arrays.  Marker can only indicate a Marker / key / value
3349 	     * triad; it cannot be there by accident.
3350 	     *
3351 	     * It's too inefficient to strip Markers here, and they
3352 	     * can't be there in the other form --- so just ignore
3353 	     * them willy nilly lower down.
3354 	     */
3355 	    for (aptr = val; *aptr; aptr += 3) {
3356 		if (**aptr != Marker) {
3357 		    unqueue_signals();
3358 		    freearray(val);
3359 		    zerr("bad [key]=value syntax for associative array");
3360 		    return NULL;
3361 		}
3362 	    }
3363 	} else {
3364 	    unqueue_signals();
3365 	    freearray(val);
3366 	    zerr("invalid use of [key]=value assignment syntax");
3367 	    return NULL;
3368 	}
3369     }
3370 
3371     if (flags & ASSPM_AUGMENT) {
3372     	if (v->start == 0 && v->end == -1) {
3373 	    if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) {
3374 	    	v->start = arrlen(v->pm->gsu.a->getfn(v->pm));
3375 	    	v->end = v->start + 1;
3376 	    } else if (PM_TYPE(v->pm->node.flags) & PM_HASHED)
3377 	    	v->start = -1, v->end = 0;
3378 	} else {
3379 	    if (v->end > 0)
3380 		v->start = v->end--;
3381 	    else if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) {
3382 		v->end = arrlen(v->pm->gsu.a->getfn(v->pm)) + v->end;
3383 		v->start = v->end + 1;
3384 	    }
3385 	}
3386     }
3387 
3388     setarrvalue(v, val);
3389     unqueue_signals();
3390     return v->pm;
3391 }
3392 
3393 
3394 /**/
3395 mod_export Param
setaparam(char * s,char ** aval)3396 setaparam(char *s, char **aval)
3397 {
3398     return assignaparam(s, aval, ASSPM_WARN);
3399 }
3400 
3401 /**/
3402 mod_export Param
sethparam(char * s,char ** val)3403 sethparam(char *s, char **val)
3404 {
3405     struct value vbuf;
3406     Value v;
3407     char *t = s;
3408     int checkcreate = 0;
3409 
3410     if (!isident(s)) {
3411 	zerr("not an identifier: %s", s);
3412 	freearray(val);
3413 	errflag |= ERRFLAG_ERROR;
3414 	return NULL;
3415     }
3416     if (strchr(s, '[')) {
3417 	freearray(val);
3418 	zerr("nested associative arrays not yet supported");
3419 	errflag |= ERRFLAG_ERROR;
3420 	return NULL;
3421     }
3422     if (unset(EXECOPT))
3423 	return NULL;
3424     queue_signals();
3425     if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) {
3426 	createparam(t, PM_HASHED);
3427 	checkcreate = 1;
3428     } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED)) {
3429 	if (!(v->pm->node.flags & PM_SPECIAL)) {
3430 	    unsetparam(t);
3431 	    /* no WARNCREATEGLOBAL check here as parameter already existed */
3432 	    createparam(t, PM_HASHED);
3433 	    v = NULL;
3434 	} else {
3435 	    zerr("%s: can't change type of a special parameter", t);
3436 	    unqueue_signals();
3437 	    return NULL;
3438 	}
3439     }
3440     if (!v)
3441 	if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) {
3442 	    unqueue_signals();
3443 	    /* errflag |= ERRFLAG_ERROR; */
3444 	    return NULL;
3445 	}
3446     check_warn_pm(v->pm, "associative array", checkcreate, 1);
3447     setarrvalue(v, val);
3448     unqueue_signals();
3449     return v->pm;
3450 }
3451 
3452 
3453 /*
3454  * Set a generic shell number, floating point or integer.
3455  * Option to warn on setting.
3456  */
3457 
3458 /**/
3459 mod_export Param
assignnparam(char * s,mnumber val,int flags)3460 assignnparam(char *s, mnumber val, int flags)
3461 {
3462     struct value vbuf;
3463     Value v;
3464     char *t = s, *ss;
3465     Param pm;
3466     int was_unset = 0;
3467 
3468     if (!isident(s)) {
3469 	zerr("not an identifier: %s", s);
3470 	errflag |= ERRFLAG_ERROR;
3471 	return NULL;
3472     }
3473     if (unset(EXECOPT))
3474 	return NULL;
3475     queue_signals();
3476     ss = strchr(s, '[');
3477     v = getvalue(&vbuf, &s, 1);
3478     if (v && (v->pm->node.flags & (PM_ARRAY|PM_HASHED)) &&
3479 	!(v->pm->node.flags & (PM_SPECIAL|PM_TIED)) &&
3480 	/*
3481 	 * not sure what KSHARRAYS has got to do with this...
3482 	 * copied this from assignsparam().
3483 	 */
3484 	unset(KSHARRAYS) && !ss) {
3485 	unsetparam_pm(v->pm, 0, 1);
3486 	was_unset = 1;
3487 	s = t;
3488 	v = NULL;
3489     }
3490     if (!v) {
3491 	/* s has been updated by getvalue, so check again */
3492 	ss = strchr(s, '[');
3493 	if (ss)
3494 	    *ss = '\0';
3495 	pm = createparam(t, ss ? PM_ARRAY :
3496 			 isset(POSIXIDENTIFIERS) ? PM_SCALAR :
3497 			 (val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT);
3498 	if (!pm)
3499 	    pm = (Param) paramtab->getnode(paramtab, t);
3500 	DPUTS(!pm, "BUG: parameter not created");
3501 	if (ss) {
3502 	    *ss = '[';
3503 	} else if (val.type & MN_INTEGER) {
3504 	    pm->base = outputradix;
3505 	}
3506 	if (!(v = getvalue(&vbuf, &t, 1))) {
3507 	    DPUTS(!v, "BUG: value not found for new parameter");
3508 	    /* errflag |= ERRFLAG_ERROR; */
3509 	    unqueue_signals();
3510 	    return NULL;
3511 	}
3512 	if (flags & ASSPM_WARN)
3513 	    check_warn_pm(v->pm, "numeric", !was_unset, 1);
3514     } else {
3515 	if (flags & ASSPM_WARN)
3516 	    check_warn_pm(v->pm, "numeric", 0, 1);
3517     }
3518     setnumvalue(v, val);
3519     unqueue_signals();
3520     return v->pm;
3521 }
3522 
3523 /*
3524  * Set a generic shell number, floating point or integer.
3525  * Warn on setting based on option.
3526  */
3527 
3528 /**/
3529 mod_export Param
setnparam(char * s,mnumber val)3530 setnparam(char *s, mnumber val)
3531 {
3532     return assignnparam(s, val, ASSPM_WARN);
3533 }
3534 
3535 /* Simplified interface to assignnparam */
3536 
3537 /**/
3538 mod_export Param
assigniparam(char * s,zlong val,int flags)3539 assigniparam(char *s, zlong val, int flags)
3540 {
3541     mnumber mnval;
3542     mnval.type = MN_INTEGER;
3543     mnval.u.l = val;
3544     return assignnparam(s, mnval, flags);
3545 }
3546 
3547 /* Simplified interface to setnparam */
3548 
3549 /**/
3550 mod_export Param
setiparam(char * s,zlong val)3551 setiparam(char *s, zlong val)
3552 {
3553     mnumber mnval;
3554     mnval.type = MN_INTEGER;
3555     mnval.u.l = val;
3556     return assignnparam(s, mnval, ASSPM_WARN);
3557 }
3558 
3559 /*
3560  * Set an integer parameter without forcing creation of an integer type.
3561  * This is useful if the integer is going to be set to a parameter which
3562  * would usually be scalar but may not exist.
3563  */
3564 
3565 /**/
3566 mod_export Param
setiparam_no_convert(char * s,zlong val)3567 setiparam_no_convert(char *s, zlong val)
3568 {
3569     /*
3570      * If the target is already an integer, thisgets converted
3571      * back.  Low technology rules.
3572      */
3573     char buf[BDIGBUFSIZE];
3574     convbase(buf, val, 10);
3575     return assignsparam(s, ztrdup(buf), ASSPM_WARN);
3576 }
3577 
3578 /* Unset a parameter */
3579 
3580 /**/
3581 mod_export void
unsetparam(char * s)3582 unsetparam(char *s)
3583 {
3584     Param pm;
3585 
3586     queue_signals();
3587     if ((pm = (Param) (paramtab == realparamtab ?
3588 		       /* getnode2() to avoid autoloading */
3589 		       paramtab->getnode2(paramtab, s) :
3590 		       paramtab->getnode(paramtab, s))))
3591 	unsetparam_pm(pm, 0, 1);
3592     unqueue_signals();
3593 }
3594 
3595 /* Unset a parameter
3596  *
3597  * altflag: if true, don't remove pm->ename from the environment
3598  * exp: See stdunsetfn()
3599  */
3600 
3601 /**/
3602 mod_export int
unsetparam_pm(Param pm,int altflag,int exp)3603 unsetparam_pm(Param pm, int altflag, int exp)
3604 {
3605     Param oldpm, altpm;
3606     char *altremove;
3607 
3608     if ((pm->node.flags & PM_READONLY) && pm->level <= locallevel) {
3609 	zerr("read-only variable: %s", pm->node.nam);
3610 	return 1;
3611     }
3612     if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
3613 	zerr("%s: restricted", pm->node.nam);
3614 	return 1;
3615     }
3616 
3617     if (pm->ename && !altflag)
3618 	altremove = ztrdup(pm->ename);
3619     else
3620 	altremove = NULL;
3621 
3622     if (!(pm->node.flags & PM_UNSET))
3623 	pm->gsu.s->unsetfn(pm, exp);
3624     if (pm->env)
3625 	delenv(pm);
3626 
3627     /* remove it under its alternate name if necessary */
3628     if (altremove) {
3629 	altpm = (Param) paramtab->getnode(paramtab, altremove);
3630 	/* tied parameters are at the same local level as each other */
3631 	oldpm = NULL;
3632 	/*
3633 	 * Look for param under alternate name hidden by a local.
3634 	 * If this parameter is special, however, the visible
3635 	 * parameter is the special and the hidden one is keeping
3636 	 * an old value --- we just mark the visible one as unset.
3637 	 */
3638 	if (altpm && !(altpm->node.flags & PM_SPECIAL))
3639 	{
3640 	    while (altpm && altpm->level > pm->level) {
3641 		oldpm = altpm;
3642 		altpm = altpm->old;
3643 	    }
3644 	}
3645 	if (altpm) {
3646 	    if (oldpm && !altpm->level) {
3647 		oldpm->old = NULL;
3648 		/* fudge things so removenode isn't called */
3649 		altpm->level = 1;
3650 	    }
3651 	    unsetparam_pm(altpm, 1, exp);
3652 	}
3653 
3654 	zsfree(altremove);
3655     }
3656 
3657     /*
3658      * If this was a local variable, we need to keep the old
3659      * struct so that it is resurrected at the right level.
3660      * This is partly because when an array/scalar value is set
3661      * and the parameter used to be the other sort, unsetparam()
3662      * is called.  Beyond that, there is an ambiguity:  should
3663      * foo() { local bar; unset bar; } make the global bar
3664      * available or not?  The following makes the answer "no".
3665      *
3666      * Some specials, such as those used in zle, still need removing
3667      * from the parameter table; they have the PM_REMOVABLE flag.
3668      */
3669     if ((pm->level && locallevel >= pm->level) ||
3670 	(pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL)
3671 	return 0;
3672 
3673     /* remove parameter node from table */
3674     paramtab->removenode(paramtab, pm->node.nam);
3675 
3676     if (pm->old) {
3677 	oldpm = pm->old;
3678 	paramtab->addnode(paramtab, oldpm->node.nam, oldpm);
3679 	if ((PM_TYPE(oldpm->node.flags) == PM_SCALAR) &&
3680 	    !(pm->node.flags & PM_HASHELEM) &&
3681 	    (oldpm->node.flags & PM_NAMEDDIR) &&
3682 	    oldpm->gsu.s == &stdscalar_gsu)
3683 	    adduserdir(oldpm->node.nam, oldpm->u.str, 0, 0);
3684 	if (oldpm->node.flags & PM_EXPORTED) {
3685 	    /*
3686 	     * Re-export the old value which we removed in typeset_single().
3687 	     * I don't think we need to test for ALL_EXPORT here, since if
3688 	     * it was used to export the parameter originally the parameter
3689 	     * should still have the PM_EXPORTED flag.
3690 	     */
3691 	    export_param(oldpm);
3692 	}
3693     }
3694 
3695     paramtab->freenode(&pm->node); /* free parameter node */
3696 
3697     return 0;
3698 }
3699 
3700 /* Standard function to unset a parameter.  This is mostly delegated to *
3701  * the specific set function.
3702  *
3703  * This could usefully be made type-specific, but then we need
3704  * to be more careful when calling the unset method directly.
3705  *
3706  * The "exp"licit parameter should be nonzero for assignments and the
3707  * unset command, and zero for implicit unset (e.g., end of scope).
3708  * Currently this is used only by some modules.
3709  */
3710 
3711 /**/
3712 mod_export void
stdunsetfn(Param pm,UNUSED (int exp))3713 stdunsetfn(Param pm, UNUSED(int exp))
3714 {
3715     switch (PM_TYPE(pm->node.flags)) {
3716 	case PM_SCALAR:
3717 	    if (pm->gsu.s->setfn)
3718 		pm->gsu.s->setfn(pm, NULL);
3719 	    break;
3720 
3721 	case PM_ARRAY:
3722 	    if (pm->gsu.a->setfn)
3723 		pm->gsu.a->setfn(pm, NULL);
3724 	    break;
3725 
3726 	case PM_HASHED:
3727 	    if (pm->gsu.h->setfn)
3728 		pm->gsu.h->setfn(pm, NULL);
3729 	    break;
3730 
3731 	default:
3732 	    if (!(pm->node.flags & PM_SPECIAL))
3733 	    	pm->u.str = NULL;
3734 	    break;
3735     }
3736     if ((pm->node.flags & (PM_SPECIAL|PM_TIED)) == PM_TIED) {
3737 	if (pm->ename) {
3738 	    zsfree(pm->ename);
3739 	    pm->ename = NULL;
3740 	}
3741 	pm->node.flags &= ~PM_TIED;
3742     }
3743     pm->node.flags |= PM_UNSET;
3744 }
3745 
3746 /* Function to get value of an integer parameter */
3747 
3748 /**/
3749 mod_export zlong
intgetfn(Param pm)3750 intgetfn(Param pm)
3751 {
3752     return pm->u.val;
3753 }
3754 
3755 /* Function to set value of an integer parameter */
3756 
3757 /**/
3758 static void
intsetfn(Param pm,zlong x)3759 intsetfn(Param pm, zlong x)
3760 {
3761     pm->u.val = x;
3762 }
3763 
3764 /* Function to get value of a floating point parameter */
3765 
3766 /**/
3767 static double
floatgetfn(Param pm)3768 floatgetfn(Param pm)
3769 {
3770     return pm->u.dval;
3771 }
3772 
3773 /* Function to set value of an integer parameter */
3774 
3775 /**/
3776 static void
floatsetfn(Param pm,double x)3777 floatsetfn(Param pm, double x)
3778 {
3779     pm->u.dval = x;
3780 }
3781 
3782 /* Function to get value of a scalar (string) parameter */
3783 
3784 /**/
3785 mod_export char *
strgetfn(Param pm)3786 strgetfn(Param pm)
3787 {
3788     return pm->u.str ? pm->u.str : (char *) hcalloc(1);
3789 }
3790 
3791 /* Function to set value of a scalar (string) parameter */
3792 
3793 /**/
3794 mod_export void
strsetfn(Param pm,char * x)3795 strsetfn(Param pm, char *x)
3796 {
3797     zsfree(pm->u.str);
3798     pm->u.str = x;
3799     if (!(pm->node.flags & PM_HASHELEM) &&
3800 	((pm->node.flags & PM_NAMEDDIR) || isset(AUTONAMEDIRS))) {
3801 	pm->node.flags |= PM_NAMEDDIR;
3802 	adduserdir(pm->node.nam, x, 0, 0);
3803     }
3804     /* If you update this function, you may need to update the
3805      * `Implement remainder of strsetfn' block in assignstrvalue(). */
3806 }
3807 
3808 /* Function to get value of an array parameter */
3809 
3810 static char *nullarray = NULL;
3811 
3812 /**/
3813 char **
arrgetfn(Param pm)3814 arrgetfn(Param pm)
3815 {
3816     return pm->u.arr ? pm->u.arr : &nullarray;
3817 }
3818 
3819 /* Function to set value of an array parameter */
3820 
3821 /**/
3822 mod_export void
arrsetfn(Param pm,char ** x)3823 arrsetfn(Param pm, char **x)
3824 {
3825     if (pm->u.arr && pm->u.arr != x)
3826 	freearray(pm->u.arr);
3827     if (pm->node.flags & PM_UNIQUE)
3828 	uniqarray(x);
3829     pm->u.arr = x;
3830     /* Arrays tied to colon-arrays may need to fix the environment */
3831     if (pm->ename && x)
3832 	arrfixenv(pm->ename, x);
3833     /* If you extend this function, update the list of conditions in
3834      * setarrvalue(). */
3835 }
3836 
3837 /* Function to get value of an association parameter */
3838 
3839 /**/
3840 mod_export HashTable
hashgetfn(Param pm)3841 hashgetfn(Param pm)
3842 {
3843     return pm->u.hash;
3844 }
3845 
3846 /* Function to set value of an association parameter */
3847 
3848 /**/
3849 mod_export void
hashsetfn(Param pm,HashTable x)3850 hashsetfn(Param pm, HashTable x)
3851 {
3852     if (pm->u.hash && pm->u.hash != x)
3853 	deleteparamtable(pm->u.hash);
3854     pm->u.hash = x;
3855 }
3856 
3857 /* Function to dispose of setting of an unsettable hash */
3858 
3859 /**/
3860 mod_export void
nullsethashfn(UNUSED (Param pm),HashTable x)3861 nullsethashfn(UNUSED(Param pm), HashTable x)
3862 {
3863     deleteparamtable(x);
3864 }
3865 
3866 /* Function to set value of an association parameter using key/value pairs */
3867 
3868 /**/
3869 static void
arrhashsetfn(Param pm,char ** val,int flags)3870 arrhashsetfn(Param pm, char **val, int flags)
3871 {
3872     /* Best not to shortcut this by using the existing hash table,   *
3873      * since that could cause trouble for special hashes.  This way, *
3874      * it's up to pm->gsu.h->setfn() what to do.                     */
3875     int alen = 0;
3876     HashTable opmtab = paramtab, ht = 0;
3877     char **aptr;
3878     Value v = (Value) hcalloc(sizeof *v);
3879     v->end = -1;
3880 
3881     for (aptr = val; *aptr; ++aptr) {
3882 	if (**aptr != Marker)
3883 	    ++alen;
3884     }
3885 
3886     if (alen % 2) {
3887 	freearray(val);
3888 	zerr("bad set of key/value pairs for associative array");
3889 	return;
3890     }
3891     if (flags & ASSPM_AUGMENT) {
3892 	ht = paramtab = pm->gsu.h->getfn(pm);
3893     }
3894     if (alen && (!(flags & ASSPM_AUGMENT) || !paramtab)) {
3895 	ht = paramtab = newparamtable(17, pm->node.nam);
3896     }
3897     for (aptr = val; *aptr; ) {
3898 	int eltflags = 0;
3899 	if (**aptr == Marker) {
3900 	    /* Either all elements have Marker or none. Checked in caller. */
3901 	    if ((*aptr)[1] == '+') {
3902 		/* Actually, assignstrvalue currently doesn't handle this... */
3903 		eltflags = ASSPM_AUGMENT;
3904 		/* ...so we'll use the trick from setsparam(). */
3905 		v->start = INT_MAX;
3906 	    } else {
3907 		v->start = 0;
3908 	    }
3909 	    v->end = -1;
3910 	    zsfree(*aptr++);
3911 	}
3912 	/* The parameter name is ztrdup'd... */
3913 	v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET);
3914 	/*
3915 	 * createparam() doesn't return anything if the parameter
3916 	 * already existed.
3917 	 */
3918 	if (!v->pm)
3919 	    v->pm = (Param) paramtab->getnode(paramtab, *aptr);
3920 	zsfree(*aptr++);
3921 	/* ...but we can use the value without copying. */
3922 	assignstrvalue(v, *aptr++, eltflags);
3923     }
3924     paramtab = opmtab;
3925     pm->gsu.h->setfn(pm, ht);
3926     free(val);		/* not freearray() */
3927 }
3928 
3929 /*
3930  * These functions are used as the set function for special parameters that
3931  * cannot be set by the user.  The set is incomplete as the only such
3932  * parameters are scalar and integer.
3933  */
3934 
3935 /**/
3936 mod_export void
nullstrsetfn(UNUSED (Param pm),char * x)3937 nullstrsetfn(UNUSED(Param pm), char *x)
3938 {
3939     zsfree(x);
3940 }
3941 
3942 /**/
3943 mod_export void
nullintsetfn(UNUSED (Param pm),UNUSED (zlong x))3944 nullintsetfn(UNUSED(Param pm), UNUSED(zlong x))
3945 {}
3946 
3947 /**/
3948 mod_export void
nullunsetfn(UNUSED (Param pm),UNUSED (int exp))3949 nullunsetfn(UNUSED(Param pm), UNUSED(int exp))
3950 {}
3951 
3952 
3953 /* Function to get value of generic special integer *
3954  * parameter.  data is pointer to global variable   *
3955  * containing the integer value.                    */
3956 
3957 /**/
3958 mod_export zlong
intvargetfn(Param pm)3959 intvargetfn(Param pm)
3960 {
3961     return *pm->u.valptr;
3962 }
3963 
3964 /* Function to set value of generic special integer *
3965  * parameter.  data is pointer to global variable   *
3966  * where the value is to be stored.                 */
3967 
3968 /**/
3969 mod_export void
intvarsetfn(Param pm,zlong x)3970 intvarsetfn(Param pm, zlong x)
3971 {
3972     *pm->u.valptr = x;
3973 }
3974 
3975 /* Function to set value of any ZLE-related integer *
3976  * parameter.  data is pointer to global variable   *
3977  * where the value is to be stored.                 */
3978 
3979 /**/
3980 void
zlevarsetfn(Param pm,zlong x)3981 zlevarsetfn(Param pm, zlong x)
3982 {
3983     zlong *p = pm->u.valptr;
3984 
3985     *p = x;
3986     if (p == &zterm_lines || p == &zterm_columns)
3987 	adjustwinsize(2 + (p == &zterm_columns));
3988 }
3989 
3990 
3991 /* Implements gsu_integer.unsetfn for ZLE_RPROMPT_INDENT; see stdunsetfn() */
3992 
3993 static void
rprompt_indent_unsetfn(Param pm,int exp)3994 rprompt_indent_unsetfn(Param pm, int exp)
3995 {
3996     stdunsetfn(pm, exp);
3997     rprompt_indent = 1; /* Keep this in sync with init_term() */
3998 }
3999 
4000 /* Function to set value of generic special scalar    *
4001  * parameter.  data is pointer to a character pointer *
4002  * representing the scalar (string).                  */
4003 
4004 /**/
4005 mod_export void
strvarsetfn(Param pm,char * x)4006 strvarsetfn(Param pm, char *x)
4007 {
4008     char **q = ((char **)pm->u.data);
4009 
4010     zsfree(*q);
4011     *q = x;
4012 }
4013 
4014 /* Function to get value of generic special scalar    *
4015  * parameter.  data is pointer to a character pointer *
4016  * representing the scalar (string).                  */
4017 
4018 /**/
4019 mod_export char *
strvargetfn(Param pm)4020 strvargetfn(Param pm)
4021 {
4022     char *s = *((char **)pm->u.data);
4023 
4024     if (!s)
4025 	return hcalloc(1);
4026     return s;
4027 }
4028 
4029 /* Function to get value of generic special array  *
4030  * parameter.  data is a pointer to the pointer to *
4031  * a pointer (a pointer to a variable length array *
4032  * of pointers).                                   */
4033 
4034 /**/
4035 mod_export char **
arrvargetfn(Param pm)4036 arrvargetfn(Param pm)
4037 {
4038     char **arrptr = *((char ***)pm->u.data);
4039 
4040     return arrptr ? arrptr : &nullarray;
4041 }
4042 
4043 /* Function to set value of generic special array parameter.    *
4044  * data is pointer to a variable length array of pointers which *
4045  * represents this array of scalars (strings).  If pm->ename is *
4046  * non NULL, then it is a colon separated environment variable  *
4047  * version of this array which will need to be updated.         */
4048 
4049 /**/
4050 mod_export void
arrvarsetfn(Param pm,char ** x)4051 arrvarsetfn(Param pm, char **x)
4052 {
4053     char ***dptr = (char ***)pm->u.data;
4054 
4055     if (*dptr != x)
4056 	freearray(*dptr);
4057     if (pm->node.flags & PM_UNIQUE)
4058 	uniqarray(x);
4059     /*
4060      * Special tied arrays point to variables accessible in other
4061      * ways which need to be set to NULL.  We can't do this
4062      * with user tied variables since we can leak memory.
4063      */
4064     if ((pm->node.flags & PM_SPECIAL) && !x)
4065 	*dptr = mkarray(NULL);
4066     else
4067 	*dptr = x;
4068     if (pm->ename) {
4069 	if (x)
4070 	    arrfixenv(pm->ename, x);
4071 	else if (*dptr == path)
4072 	    pathchecked = path;
4073     }
4074 }
4075 
4076 /**/
4077 char *
colonarrgetfn(Param pm)4078 colonarrgetfn(Param pm)
4079 {
4080     char ***dptr = (char ***)pm->u.data;
4081     return *dptr ? zjoin(*dptr, ':', 1) : "";
4082 }
4083 
4084 /**/
4085 void
colonarrsetfn(Param pm,char * x)4086 colonarrsetfn(Param pm, char *x)
4087 {
4088     char ***dptr = (char ***)pm->u.data;
4089     /*
4090      * We have to make sure this is never NULL, since that
4091      * can cause problems.
4092      */
4093     if (*dptr)
4094 	freearray(*dptr);
4095     if (x)
4096 	*dptr = colonsplit(x, pm->node.flags & PM_UNIQUE);
4097     else
4098 	*dptr = mkarray(NULL);
4099     arrfixenv(pm->node.nam, *dptr);
4100     zsfree(x);
4101 }
4102 
4103 /**/
4104 char *
tiedarrgetfn(Param pm)4105 tiedarrgetfn(Param pm)
4106 {
4107     struct tieddata *dptr = (struct tieddata *)pm->u.data;
4108     return *dptr->arrptr ? zjoin(*dptr->arrptr, STOUC(dptr->joinchar), 1) : "";
4109 }
4110 
4111 /**/
4112 void
tiedarrsetfn(Param pm,char * x)4113 tiedarrsetfn(Param pm, char *x)
4114 {
4115     struct tieddata *dptr = (struct tieddata *)pm->u.data;
4116 
4117     if (*dptr->arrptr)
4118 	freearray(*dptr->arrptr);
4119     if (x) {
4120 	char sepbuf[3];
4121 	if (imeta(dptr->joinchar))
4122 	{
4123 	    sepbuf[0] = Meta;
4124 	    sepbuf[1] = dptr->joinchar ^ 32;
4125 	    sepbuf[2] = '\0';
4126 	}
4127 	else
4128 	{
4129 	    sepbuf[0] = dptr->joinchar;
4130 	    sepbuf[1] = '\0';
4131 	}
4132 	*dptr->arrptr = sepsplit(x, sepbuf, 0, 0);
4133 	if (pm->node.flags & PM_UNIQUE)
4134 	    uniqarray(*dptr->arrptr);
4135 	zsfree(x);
4136     } else
4137 	*dptr->arrptr = NULL;
4138     if (pm->ename)
4139 	arrfixenv(pm->node.nam, *dptr->arrptr);
4140 }
4141 
4142 /**/
4143 void
tiedarrunsetfn(Param pm,UNUSED (int exp))4144 tiedarrunsetfn(Param pm, UNUSED(int exp))
4145 {
4146     /*
4147      * Special unset function because we allocated a struct tieddata
4148      * in typeset_single to hold the special data which we now
4149      * need to delete.
4150      */
4151     pm->gsu.s->setfn(pm, NULL);
4152     zfree(pm->u.data, sizeof(struct tieddata));
4153     /* paranoia -- shouldn't need these, but in case we reuse the struct... */
4154     pm->u.data = NULL;
4155     zsfree(pm->ename);
4156     pm->ename = NULL;
4157     pm->node.flags &= ~PM_TIED;
4158     pm->node.flags |= PM_UNSET;
4159 }
4160 
4161 /**/
4162 static void
simple_arrayuniq(char ** x,int freeok)4163 simple_arrayuniq(char **x, int freeok)
4164 {
4165     char **t, **p = x;
4166     char *hole = "";
4167 
4168     /* Find duplicates and replace them with holes */
4169     while (*++p)
4170 	for (t = x; t < p; t++)
4171 	    if (*t != hole && !strcmp(*p, *t)) {
4172 		if (freeok)
4173 		    zsfree(*p);
4174 		*p = hole;
4175 		break;
4176 	    }
4177     /* Swap non-holes into holes in optimal jumps */
4178     for (p = t = x; *t != NULL; t++) {
4179 	if (*t == hole) {
4180 	    while (*p == hole)
4181 		++p;
4182 	    if ((*t = *p) != NULL)
4183 		*p++ = hole;
4184 	} else if (p == t)
4185 	    p++;
4186     }
4187     /* Erase all the remaining holes, just in case */
4188     while (++t < p)
4189 	*t = NULL;
4190 }
4191 
4192 /**/
4193 static void
arrayuniq_freenode(HashNode hn)4194 arrayuniq_freenode(HashNode hn)
4195 {
4196     (void)hn;
4197 }
4198 
4199 /**/
4200 HashTable
newuniqtable(zlong size)4201 newuniqtable(zlong size)
4202 {
4203     HashTable ht = newhashtable((int)size, "arrayuniq", NULL);
4204     /* ??? error checking */
4205 
4206     ht->hash        = hasher;
4207     ht->emptytable  = emptyhashtable;
4208     ht->filltable   = NULL;
4209     ht->cmpnodes    = strcmp;
4210     ht->addnode     = addhashnode;
4211     ht->getnode     = gethashnode2;
4212     ht->getnode2    = gethashnode2;
4213     ht->removenode  = removehashnode;
4214     ht->disablenode = disablehashnode;
4215     ht->enablenode  = enablehashnode;
4216     ht->freenode    = arrayuniq_freenode;
4217     ht->printnode   = NULL;
4218 
4219     return ht;
4220 }
4221 
4222 /**/
4223 static void
arrayuniq(char ** x,int freeok)4224 arrayuniq(char **x, int freeok)
4225 {
4226     char **it, **write_it;
4227     zlong array_size = arrlen(x);
4228     HashTable ht;
4229 
4230     if (array_size == 0)
4231 	return;
4232     if (array_size < 10 || !(ht = newuniqtable(array_size + 1))) {
4233 	/* fallback to simpler routine */
4234 	simple_arrayuniq(x, freeok);
4235 	return;
4236     }
4237 
4238     for (it = x, write_it = x; *it;) {
4239 	if (! gethashnode2(ht, *it)) {
4240 	    HashNode new_node = zhalloc(sizeof(struct hashnode));
4241 	    if (!new_node) {
4242 		/* Oops, out of heap memory, no way to recover */
4243 		zerr("out of memory in arrayuniq");
4244 		break;
4245 	    }
4246 	    (void) addhashnode2(ht, *it, new_node);
4247 	    *write_it = *it;
4248 	    if (it != write_it)
4249 		*it = NULL;
4250 	    ++write_it;
4251 	}
4252 	else {
4253 	    if (freeok)
4254 		zsfree(*it);
4255 	    *it = NULL;
4256 	}
4257 	++it;
4258     }
4259 
4260     deletehashtable(ht);
4261 }
4262 
4263 /**/
4264 void
uniqarray(char ** x)4265 uniqarray(char **x)
4266 {
4267     if (!x || !*x)
4268 	return;
4269     arrayuniq(x, !zheapptr(*x));
4270 }
4271 
4272 /**/
4273 void
zhuniqarray(char ** x)4274 zhuniqarray(char **x)
4275 {
4276     if (!x || !*x)
4277 	return;
4278     arrayuniq(x, 0);
4279 }
4280 
4281 /* Function to get value of special parameter `#' and `ARGC' */
4282 
4283 /**/
4284 zlong
poundgetfn(UNUSED (Param pm))4285 poundgetfn(UNUSED(Param pm))
4286 {
4287     return arrlen(pparams);
4288 }
4289 
4290 /* Function to get value for special parameter `RANDOM' */
4291 
4292 /**/
4293 zlong
randomgetfn(UNUSED (Param pm))4294 randomgetfn(UNUSED(Param pm))
4295 {
4296     return rand() & 0x7fff;
4297 }
4298 
4299 /* Function to set value of special parameter `RANDOM' */
4300 
4301 /**/
4302 void
randomsetfn(UNUSED (Param pm),zlong v)4303 randomsetfn(UNUSED(Param pm), zlong v)
4304 {
4305     srand((unsigned int)v);
4306 }
4307 
4308 /* Function to get value for special parameter `SECONDS' */
4309 
4310 /**/
4311 zlong
intsecondsgetfn(UNUSED (Param pm))4312 intsecondsgetfn(UNUSED(Param pm))
4313 {
4314     struct timeval now;
4315     struct timezone dummy_tz;
4316 
4317     gettimeofday(&now, &dummy_tz);
4318 
4319     return (zlong)(now.tv_sec - shtimer.tv_sec -
4320 		  (now.tv_usec < shtimer.tv_usec ? 1 : 0));
4321 }
4322 
4323 /* Function to set value of special parameter `SECONDS' */
4324 
4325 /**/
4326 void
intsecondssetfn(UNUSED (Param pm),zlong x)4327 intsecondssetfn(UNUSED(Param pm), zlong x)
4328 {
4329     struct timeval now;
4330     struct timezone dummy_tz;
4331     zlong diff;
4332 
4333     gettimeofday(&now, &dummy_tz);
4334     diff = (zlong)now.tv_sec - x;
4335     shtimer.tv_sec = diff;
4336     if ((zlong)shtimer.tv_sec != diff)
4337 	zwarn("SECONDS truncated on assignment");
4338     shtimer.tv_usec = now.tv_usec;
4339 }
4340 
4341 /**/
4342 double
floatsecondsgetfn(UNUSED (Param pm))4343 floatsecondsgetfn(UNUSED(Param pm))
4344 {
4345     struct timeval now;
4346     struct timezone dummy_tz;
4347 
4348     gettimeofday(&now, &dummy_tz);
4349 
4350     return (double)(now.tv_sec - shtimer.tv_sec) +
4351 	(double)(now.tv_usec - shtimer.tv_usec) / 1000000.0;
4352 }
4353 
4354 /**/
4355 void
floatsecondssetfn(UNUSED (Param pm),double x)4356 floatsecondssetfn(UNUSED(Param pm), double x)
4357 {
4358     struct timeval now;
4359     struct timezone dummy_tz;
4360 
4361     gettimeofday(&now, &dummy_tz);
4362     shtimer.tv_sec = now.tv_sec - (zlong)x;
4363     shtimer.tv_usec = now.tv_usec - (zlong)((x - (zlong)x) * 1000000.0);
4364 }
4365 
4366 /**/
4367 double
getrawseconds(void)4368 getrawseconds(void)
4369 {
4370     return (double)shtimer.tv_sec + (double)shtimer.tv_usec / 1000000.0;
4371 }
4372 
4373 /**/
4374 void
setrawseconds(double x)4375 setrawseconds(double x)
4376 {
4377     shtimer.tv_sec = (zlong)x;
4378     shtimer.tv_usec = (zlong)((x - (zlong)x) * 1000000.0);
4379 }
4380 
4381 /**/
4382 int
setsecondstype(Param pm,int on,int off)4383 setsecondstype(Param pm, int on, int off)
4384 {
4385     int newflags = (pm->node.flags | on) & ~off;
4386     int tp = PM_TYPE(newflags);
4387     /* Only one of the numeric types is allowed. */
4388     if (tp == PM_EFLOAT || tp == PM_FFLOAT)
4389     {
4390 	pm->gsu.f = &floatseconds_gsu;
4391     }
4392     else if (tp == PM_INTEGER)
4393     {
4394 	pm->gsu.i = &intseconds_gsu;
4395     }
4396     else
4397 	return 1;
4398     pm->node.flags = newflags;
4399     return 0;
4400 }
4401 
4402 /* Function to get value for special parameter `USERNAME' */
4403 
4404 /**/
4405 char *
usernamegetfn(UNUSED (Param pm))4406 usernamegetfn(UNUSED(Param pm))
4407 {
4408     return get_username();
4409 }
4410 
4411 /* Function to set value of special parameter `USERNAME' */
4412 
4413 /**/
4414 void
usernamesetfn(UNUSED (Param pm),char * x)4415 usernamesetfn(UNUSED(Param pm), char *x)
4416 {
4417 #if defined(HAVE_SETUID) && defined(HAVE_GETPWNAM)
4418     struct passwd *pswd;
4419 
4420     if (x && (pswd = getpwnam(x)) && (pswd->pw_uid != cached_uid)) {
4421 # ifdef USE_INITGROUPS
4422 	initgroups(x, pswd->pw_gid);
4423 # endif
4424 	if (setgid(pswd->pw_gid))
4425 	    zwarn("failed to change group ID: %e", errno);
4426 	else if (setuid(pswd->pw_uid))
4427 	    zwarn("failed to change user ID: %e", errno);
4428 	else {
4429 	    zsfree(cached_username);
4430 	    cached_username = ztrdup(pswd->pw_name);
4431 	    cached_uid = pswd->pw_uid;
4432 	}
4433     }
4434 #endif /* HAVE_SETUID && HAVE_GETPWNAM */
4435     zsfree(x);
4436 }
4437 
4438 /* Function to get value for special parameter `UID' */
4439 
4440 /**/
4441 zlong
uidgetfn(UNUSED (Param pm))4442 uidgetfn(UNUSED(Param pm))
4443 {
4444     return getuid();
4445 }
4446 
4447 /* Function to set value of special parameter `UID' */
4448 
4449 /**/
4450 void
uidsetfn(UNUSED (Param pm),zlong x)4451 uidsetfn(UNUSED(Param pm), zlong x)
4452 {
4453 #ifdef HAVE_SETUID
4454     if (setuid((uid_t)x))
4455 	zerr("failed to change user ID: %e", errno);
4456 #endif
4457 }
4458 
4459 /* Function to get value for special parameter `EUID' */
4460 
4461 /**/
4462 zlong
euidgetfn(UNUSED (Param pm))4463 euidgetfn(UNUSED(Param pm))
4464 {
4465     return geteuid();
4466 }
4467 
4468 /* Function to set value of special parameter `EUID' */
4469 
4470 /**/
4471 void
euidsetfn(UNUSED (Param pm),zlong x)4472 euidsetfn(UNUSED(Param pm), zlong x)
4473 {
4474 #ifdef HAVE_SETEUID
4475     if (seteuid((uid_t)x))
4476 	zerr("failed to change effective user ID: %e", errno);
4477 #endif
4478 }
4479 
4480 /* Function to get value for special parameter `GID' */
4481 
4482 /**/
4483 zlong
gidgetfn(UNUSED (Param pm))4484 gidgetfn(UNUSED(Param pm))
4485 {
4486     return getgid();
4487 }
4488 
4489 /* Function to set value of special parameter `GID' */
4490 
4491 /**/
4492 void
gidsetfn(UNUSED (Param pm),zlong x)4493 gidsetfn(UNUSED(Param pm), zlong x)
4494 {
4495 #ifdef HAVE_SETUID
4496     if (setgid((gid_t)x))
4497 	zerr("failed to change group ID: %e", errno);
4498 #endif
4499 }
4500 
4501 /* Function to get value for special parameter `EGID' */
4502 
4503 /**/
4504 zlong
egidgetfn(UNUSED (Param pm))4505 egidgetfn(UNUSED(Param pm))
4506 {
4507     return getegid();
4508 }
4509 
4510 /* Function to set value of special parameter `EGID' */
4511 
4512 /**/
4513 void
egidsetfn(UNUSED (Param pm),zlong x)4514 egidsetfn(UNUSED(Param pm), zlong x)
4515 {
4516 #ifdef HAVE_SETEUID
4517     if (setegid((gid_t)x))
4518 	zerr("failed to change effective group ID: %e", errno);
4519 #endif
4520 }
4521 
4522 /**/
4523 zlong
ttyidlegetfn(UNUSED (Param pm))4524 ttyidlegetfn(UNUSED(Param pm))
4525 {
4526     struct stat ttystat;
4527 
4528     if (SHTTY == -1 || fstat(SHTTY, &ttystat))
4529 	return -1;
4530     return time(NULL) - ttystat.st_atime;
4531 }
4532 
4533 /* Function to get value for special parameter `IFS' */
4534 
4535 /**/
4536 char *
ifsgetfn(UNUSED (Param pm))4537 ifsgetfn(UNUSED(Param pm))
4538 {
4539     return ifs;
4540 }
4541 
4542 /* Function to set value of special parameter `IFS' */
4543 
4544 /**/
4545 void
ifssetfn(UNUSED (Param pm),char * x)4546 ifssetfn(UNUSED(Param pm), char *x)
4547 {
4548     zsfree(ifs);
4549     ifs = x;
4550     inittyptab();
4551 }
4552 
4553 /* Functions to set value of special parameters `LANG' and `LC_*' */
4554 
4555 #ifdef USE_LOCALE
4556 static struct localename {
4557     char *name;
4558     int category;
4559 } lc_names[] = {
4560 #ifdef LC_COLLATE
4561     {"LC_COLLATE", LC_COLLATE},
4562 #endif
4563 #ifdef LC_CTYPE
4564     {"LC_CTYPE", LC_CTYPE},
4565 #endif
4566 #ifdef LC_MESSAGES
4567     {"LC_MESSAGES", LC_MESSAGES},
4568 #endif
4569 #ifdef LC_NUMERIC
4570     {"LC_NUMERIC", LC_NUMERIC},
4571 #endif
4572 #ifdef LC_TIME
4573     {"LC_TIME", LC_TIME},
4574 #endif
4575     {NULL, 0}
4576 };
4577 
4578 /**/
4579 static void
setlang(char * x)4580 setlang(char *x)
4581 {
4582     struct localename *ln;
4583     char *x2;
4584 
4585     if ((x2 = getsparam_u("LC_ALL")) && *x2)
4586 	return;
4587 
4588     /*
4589      * Set the global locale to the value passed, but override
4590      * this with any non-empty definitions for specific
4591      * categories.
4592      *
4593      * We only use non-empty definitions because empty values aren't
4594      * valid as locales; when passed to setlocale() they mean "use the
4595      * environment variable", but if that's what we're setting the value
4596      * from this is meaningless.  So just all $LANG to show through in
4597      * that case.
4598      */
4599     setlocale(LC_ALL, x ? unmeta(x) : "");
4600     queue_signals();
4601     for (ln = lc_names; ln->name; ln++)
4602 	if ((x = getsparam_u(ln->name)) && *x)
4603 	    setlocale(ln->category, x);
4604     unqueue_signals();
4605 }
4606 
4607 /**/
4608 void
lc_allsetfn(Param pm,char * x)4609 lc_allsetfn(Param pm, char *x)
4610 {
4611     strsetfn(pm, x);
4612     /*
4613      * Treat an empty LC_ALL the same as an unset one,
4614      * namely by using LANG as the default locale but overriding
4615      * that with any LC_* that are set.
4616      */
4617     if (!x || !*x) {
4618 	x = getsparam_u("LANG");
4619 	if (x && *x) {
4620 	    queue_signals();
4621 	    setlang(x);
4622 	    unqueue_signals();
4623 	}
4624     }
4625     else
4626 	setlocale(LC_ALL, unmeta(x));
4627 }
4628 
4629 /**/
4630 void
langsetfn(Param pm,char * x)4631 langsetfn(Param pm, char *x)
4632 {
4633     strsetfn(pm, x);
4634     setlang(unmeta(x));
4635 }
4636 
4637 /**/
4638 void
lcsetfn(Param pm,char * x)4639 lcsetfn(Param pm, char *x)
4640 {
4641     char *x2;
4642     struct localename *ln;
4643 
4644     strsetfn(pm, x);
4645     if ((x2 = getsparam("LC_ALL")) && *x2)
4646 	return;
4647     queue_signals();
4648     /* Treat empty LC_* the same as unset. */
4649     if (!x || !*x)
4650 	x = getsparam("LANG");
4651 
4652     /*
4653      * If we've got no non-empty string at this
4654      * point (after checking $LANG, too),
4655      * we shouldn't bother setting anything.
4656      */
4657     if (x && *x) {
4658 	for (ln = lc_names; ln->name; ln++)
4659 	    if (!strcmp(ln->name, pm->node.nam))
4660 		setlocale(ln->category, unmeta(x));
4661     }
4662     unqueue_signals();
4663 }
4664 #endif /* USE_LOCALE */
4665 
4666 /* Function to set value for special parameter `0' */
4667 
4668 /**/
4669 static void
argzerosetfn(UNUSED (Param pm),char * x)4670 argzerosetfn(UNUSED(Param pm), char *x)
4671 {
4672     if (x) {
4673 	if (isset(POSIXARGZERO))
4674 	    zerr("read-only variable: 0");
4675 	else {
4676 	    zsfree(argzero);
4677 	    argzero = ztrdup(x);
4678 	}
4679 	zsfree(x);
4680     }
4681 }
4682 
4683 /* Function to get value for special parameter `0' */
4684 
4685 /**/
4686 static char *
argzerogetfn(UNUSED (Param pm))4687 argzerogetfn(UNUSED(Param pm))
4688 {
4689     if (isset(POSIXARGZERO))
4690 	return posixzero;
4691     return argzero;
4692 }
4693 
4694 /* Function to get value for special parameter `HISTSIZE' */
4695 
4696 /**/
4697 zlong
histsizegetfn(UNUSED (Param pm))4698 histsizegetfn(UNUSED(Param pm))
4699 {
4700     return histsiz;
4701 }
4702 
4703 /* Function to set value of special parameter `HISTSIZE' */
4704 
4705 /**/
4706 void
histsizesetfn(UNUSED (Param pm),zlong v)4707 histsizesetfn(UNUSED(Param pm), zlong v)
4708 {
4709     if ((histsiz = v) < 1)
4710 	histsiz = 1;
4711     resizehistents();
4712 }
4713 
4714 /* Function to get value for special parameter `SAVEHIST' */
4715 
4716 /**/
4717 zlong
savehistsizegetfn(UNUSED (Param pm))4718 savehistsizegetfn(UNUSED(Param pm))
4719 {
4720     return savehistsiz;
4721 }
4722 
4723 /* Function to set value of special parameter `SAVEHIST' */
4724 
4725 /**/
4726 void
savehistsizesetfn(UNUSED (Param pm),zlong v)4727 savehistsizesetfn(UNUSED(Param pm), zlong v)
4728 {
4729     if ((savehistsiz = v) < 0)
4730 	savehistsiz = 0;
4731 }
4732 
4733 /* Function to set value for special parameter `ERRNO' */
4734 
4735 /**/
4736 void
errnosetfn(UNUSED (Param pm),zlong x)4737 errnosetfn(UNUSED(Param pm), zlong x)
4738 {
4739     errno = (int)x;
4740     if ((zlong)errno != x)
4741 	zwarn("errno truncated on assignment");
4742 }
4743 
4744 /* Function to get value for special parameter `ERRNO' */
4745 
4746 /**/
4747 zlong
errnogetfn(UNUSED (Param pm))4748 errnogetfn(UNUSED(Param pm))
4749 {
4750     return errno;
4751 }
4752 
4753 /* Function to get value for special parameter `KEYBOARD_HACK' */
4754 
4755 /**/
4756 char *
keyboardhackgetfn(UNUSED (Param pm))4757 keyboardhackgetfn(UNUSED(Param pm))
4758 {
4759     static char buf[2];
4760 
4761     buf[0] = keyboardhackchar;
4762     buf[1] = '\0';
4763     return buf;
4764 }
4765 
4766 
4767 /* Function to set value of special parameter `KEYBOARD_HACK' */
4768 
4769 /**/
4770 void
keyboardhacksetfn(UNUSED (Param pm),char * x)4771 keyboardhacksetfn(UNUSED(Param pm), char *x)
4772 {
4773     if (x) {
4774 	int len, i;
4775 
4776 	unmetafy(x, &len);
4777 	if (len > 1) {
4778 	    len = 1;
4779 	    zwarn("Only one KEYBOARD_HACK character can be defined");  /* could be changed if needed */
4780 	}
4781 	for (i = 0; i < len; i++) {
4782 	    if (!isascii(STOUC(x[i]))) {
4783 		zwarn("KEYBOARD_HACK can only contain ASCII characters");
4784 		return;
4785 	    }
4786 	}
4787 	keyboardhackchar = len ? STOUC(x[0]) : '\0';
4788 	free(x);
4789     } else
4790 	keyboardhackchar = '\0';
4791 }
4792 
4793 /* Function to get value for special parameter `histchar' */
4794 
4795 /**/
4796 char *
histcharsgetfn(UNUSED (Param pm))4797 histcharsgetfn(UNUSED(Param pm))
4798 {
4799     static char buf[4];
4800 
4801     buf[0] = bangchar;
4802     buf[1] = hatchar;
4803     buf[2] = hashchar;
4804     buf[3] = '\0';
4805     return buf;
4806 }
4807 
4808 /* Function to set value of special parameter `histchar' */
4809 
4810 /**/
4811 void
histcharssetfn(UNUSED (Param pm),char * x)4812 histcharssetfn(UNUSED(Param pm), char *x)
4813 {
4814     if (x) {
4815 	int len, i;
4816 
4817 	unmetafy(x, &len);
4818 	if (len > 3)
4819 	    len = 3;
4820 	for (i = 0; i < len; i++) {
4821 	    if (!isascii(STOUC(x[i]))) {
4822 		zwarn("HISTCHARS can only contain ASCII characters");
4823 		return;
4824 	    }
4825 	}
4826 	bangchar = len ? STOUC(x[0]) : '\0';
4827 	hatchar =  len > 1 ? STOUC(x[1]) : '\0';
4828 	hashchar = len > 2 ? STOUC(x[2]) : '\0';
4829 	free(x);
4830     } else {
4831 	bangchar = '!';
4832 	hashchar = '#';
4833 	hatchar = '^';
4834     }
4835     inittyptab();
4836 }
4837 
4838 /* Function to get value for special parameter `HOME' */
4839 
4840 /**/
4841 char *
homegetfn(UNUSED (Param pm))4842 homegetfn(UNUSED(Param pm))
4843 {
4844     return home;
4845 }
4846 
4847 /* Function to set value of special parameter `HOME' */
4848 
4849 /**/
4850 void
homesetfn(UNUSED (Param pm),char * x)4851 homesetfn(UNUSED(Param pm), char *x)
4852 {
4853     zsfree(home);
4854     if (x && isset(CHASELINKS) && (home = xsymlink(x, 0)))
4855 	zsfree(x);
4856     else
4857 	home = x ? x : ztrdup("");
4858     finddir(NULL);
4859 }
4860 
4861 /* Function to get value for special parameter `WORDCHARS' */
4862 
4863 /**/
4864 char *
wordcharsgetfn(UNUSED (Param pm))4865 wordcharsgetfn(UNUSED(Param pm))
4866 {
4867     return wordchars;
4868 }
4869 
4870 /* Function to set value of special parameter `WORDCHARS' */
4871 
4872 /**/
4873 void
wordcharssetfn(UNUSED (Param pm),char * x)4874 wordcharssetfn(UNUSED(Param pm), char *x)
4875 {
4876     zsfree(wordchars);
4877     wordchars = x;
4878     inittyptab();
4879 }
4880 
4881 /* Function to get value for special parameter `_' */
4882 
4883 /**/
4884 char *
underscoregetfn(UNUSED (Param pm))4885 underscoregetfn(UNUSED(Param pm))
4886 {
4887     char *u = dupstring(zunderscore);
4888 
4889     untokenize(u);
4890     return u;
4891 }
4892 
4893 /* Function used when we need to reinitialise the terminal */
4894 
4895 static void
term_reinit_from_pm(void)4896 term_reinit_from_pm(void)
4897 {
4898     /* If non-interactive, delay setting up term till we need it. */
4899     if (unset(INTERACTIVE) || !*term)
4900 	termflags |= TERM_UNKNOWN;
4901     else
4902 	init_term();
4903 }
4904 
4905 /* Function to get value for special parameter `TERM' */
4906 
4907 /**/
4908 char *
termgetfn(UNUSED (Param pm))4909 termgetfn(UNUSED(Param pm))
4910 {
4911     return term;
4912 }
4913 
4914 /* Function to set value of special parameter `TERM' */
4915 
4916 /**/
4917 void
termsetfn(UNUSED (Param pm),char * x)4918 termsetfn(UNUSED(Param pm), char *x)
4919 {
4920     zsfree(term);
4921     term = x ? x : ztrdup("");
4922     term_reinit_from_pm();
4923 }
4924 
4925 /* Function to get value of special parameter `TERMINFO' */
4926 
4927 /**/
4928 char *
terminfogetfn(UNUSED (Param pm))4929 terminfogetfn(UNUSED(Param pm))
4930 {
4931     return zsh_terminfo ? zsh_terminfo : dupstring("");
4932 }
4933 
4934 /* Function to set value of special parameter `TERMINFO' */
4935 
4936 /**/
4937 void
terminfosetfn(Param pm,char * x)4938 terminfosetfn(Param pm, char *x)
4939 {
4940     zsfree(zsh_terminfo);
4941     zsh_terminfo = x;
4942 
4943     /*
4944      * terminfo relies on the value being exported before
4945      * we reinitialise the terminal.  This is a bit inefficient.
4946      */
4947     if ((pm->node.flags & PM_EXPORTED) && x)
4948 	addenv(pm, x);
4949 
4950     term_reinit_from_pm();
4951 }
4952 
4953 /* Function to get value of special parameter `TERMINFO_DIRS' */
4954 
4955 /**/
4956 char *
terminfodirsgetfn(UNUSED (Param pm))4957 terminfodirsgetfn(UNUSED(Param pm))
4958 {
4959     return zsh_terminfodirs ? zsh_terminfodirs : dupstring("");
4960 }
4961 
4962 /* Function to set value of special parameter `TERMINFO_DIRS' */
4963 
4964 /**/
4965 void
terminfodirssetfn(Param pm,char * x)4966 terminfodirssetfn(Param pm, char *x)
4967 {
4968     zsfree(zsh_terminfodirs);
4969     zsh_terminfodirs = x;
4970 
4971     /*
4972      * terminfo relies on the value being exported before
4973      * we reinitialise the terminal.  This is a bit inefficient.
4974      */
4975     if ((pm->node.flags & PM_EXPORTED) && x)
4976 	addenv(pm, x);
4977 
4978     term_reinit_from_pm();
4979 }
4980 /* Function to get value for special parameter `pipestatus' */
4981 
4982 /**/
4983 static char **
pipestatgetfn(UNUSED (Param pm))4984 pipestatgetfn(UNUSED(Param pm))
4985 {
4986     char **x = (char **) zhalloc((numpipestats + 1) * sizeof(char *));
4987     char buf[DIGBUFSIZE], **p;
4988     int *q, i;
4989 
4990     for (p = x, q = pipestats, i = numpipestats; i--; p++, q++) {
4991 	sprintf(buf, "%d", *q);
4992 	*p = dupstring(buf);
4993     }
4994     *p = NULL;
4995 
4996     return x;
4997 }
4998 
4999 /* Function to get value for special parameter `pipestatus' */
5000 
5001 /**/
5002 static void
pipestatsetfn(UNUSED (Param pm),char ** x)5003 pipestatsetfn(UNUSED(Param pm), char **x)
5004 {
5005     if (x) {
5006         int i;
5007 
5008         for (i = 0; *x && i < MAX_PIPESTATS; i++, x++)
5009             pipestats[i] = atoi(*x);
5010         numpipestats = i;
5011     }
5012     else
5013         numpipestats = 0;
5014 }
5015 
5016 /**/
5017 void
arrfixenv(char * s,char ** t)5018 arrfixenv(char *s, char **t)
5019 {
5020     Param pm;
5021     int joinchar;
5022 
5023     if (t == path)
5024 	cmdnamtab->emptytable(cmdnamtab);
5025 
5026     pm = (Param) paramtab->getnode(paramtab, s);
5027 
5028     /*
5029      * Only one level of a parameter can be exported.  Unless
5030      * ALLEXPORT is set, this must be global.
5031      */
5032 
5033     if (pm->node.flags & PM_HASHELEM)
5034 	return;
5035 
5036     if (isset(ALLEXPORT))
5037 	pm->node.flags |= PM_EXPORTED;
5038 
5039     /*
5040      * Do not "fix" parameters that were not exported
5041      */
5042 
5043     if (!(pm->node.flags & PM_EXPORTED))
5044 	return;
5045 
5046     if (pm->node.flags & PM_SPECIAL)
5047 	joinchar = ':';
5048     else
5049 	joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar);
5050 
5051     addenv(pm, t ? zjoin(t, joinchar, 1) : "");
5052 }
5053 
5054 
5055 /**/
5056 int
zputenv(char * str)5057 zputenv(char *str)
5058 {
5059     DPUTS(!str, "Attempt to put null string into environment.");
5060 #ifdef USE_SET_UNSET_ENV
5061     /*
5062      * If we are using unsetenv() to remove values from the
5063      * environment, which is the safe thing to do, we
5064      * need to use setenv() to put them there in the first place.
5065      * Unfortunately this is a slightly different interface
5066      * from what zputenv() assumes.
5067      */
5068     char *ptr;
5069     int ret;
5070 
5071     for (ptr = str; *ptr && STOUC(*ptr) < 128 && *ptr != '='; ptr++)
5072 	;
5073     if (STOUC(*ptr) >= 128) {
5074 	/*
5075 	 * Environment variables not in the portable character
5076 	 * set are non-standard and we don't really know of
5077 	 * a use for them.
5078 	 *
5079 	 * We'll disable until someone complains.
5080 	 */
5081 	return 1;
5082     } else if (*ptr) {
5083 	*ptr = '\0';
5084 	ret = setenv(str, ptr+1, 1);
5085 	*ptr = '=';
5086     } else {
5087 	/* safety first */
5088 	DPUTS(1, "bad environment string");
5089 	ret = setenv(str, ptr, 1);
5090     }
5091     return ret;
5092 #else
5093 #ifdef HAVE_PUTENV
5094     return putenv(str);
5095 #else
5096     char **ep;
5097     int num_env;
5098 
5099 
5100     /* First check if there is already an environment *
5101      * variable matching string `name'.               */
5102     if (findenv(str, &num_env)) {
5103 	environ[num_env] = str;
5104     } else {
5105     /* Else we have to make room and add it */
5106 	num_env = arrlen(environ);
5107 	environ = (char **) zrealloc(environ, (sizeof(char *)) * (num_env + 2));
5108 
5109 	/* Now add it at the end */
5110 	ep = environ + num_env;
5111 	*ep = str;
5112 	*(ep + 1) = NULL;
5113     }
5114     return 0;
5115 #endif
5116 #endif
5117 }
5118 
5119 /**/
5120 #ifndef USE_SET_UNSET_ENV
5121 /**/
5122 static int
findenv(char * name,int * pos)5123 findenv(char *name, int *pos)
5124 {
5125     char **ep, *eq;
5126     int  nlen;
5127 
5128 
5129     eq = strchr(name, '=');
5130     nlen = eq ? eq - name : (int)strlen(name);
5131     for (ep = environ; *ep; ep++)
5132 	if (!strncmp (*ep, name, nlen) && *((*ep)+nlen) == '=') {
5133 	    if (pos)
5134 		*pos = ep - environ;
5135 	    return 1;
5136 	}
5137 
5138     return 0;
5139 }
5140 /**/
5141 #endif
5142 
5143 /* Given *name = "foo", it searches the environment for string *
5144  * "foo=bar", and returns a pointer to the beginning of "bar"  */
5145 
5146 /**/
5147 mod_export char *
zgetenv(char * name)5148 zgetenv(char *name)
5149 {
5150 #ifdef HAVE_GETENV
5151     return getenv(name);
5152 #else
5153     char **ep, *s, *t;
5154 
5155     for (ep = environ; *ep; ep++) {
5156        for (s = *ep, t = name; *s && *s == *t; s++, t++);
5157        if (*s == '=' && !*t)
5158            return s + 1;
5159     }
5160     return NULL;
5161 #endif
5162 }
5163 
5164 /**/
5165 static void
copyenvstr(char * s,char * value,int flags)5166 copyenvstr(char *s, char *value, int flags)
5167 {
5168     while (*s++) {
5169 	if ((*s = *value++) == Meta)
5170 	    *s = *value++ ^ 32;
5171 	if (flags & PM_LOWER)
5172 	    *s = tulower(*s);
5173 	else if (flags & PM_UPPER)
5174 	    *s = tuupper(*s);
5175     }
5176 }
5177 
5178 /**/
5179 void
addenv(Param pm,char * value)5180 addenv(Param pm, char *value)
5181 {
5182     char *newenv = 0;
5183 #ifndef USE_SET_UNSET_ENV
5184     char *oldenv = 0, *env = 0;
5185     int pos;
5186 
5187     /*
5188      * First check if there is already an environment
5189      * variable matching string `name'.
5190      */
5191     if (findenv(pm->node.nam, &pos))
5192 	oldenv = environ[pos];
5193 #endif
5194 
5195      newenv = mkenvstr(pm->node.nam, value, pm->node.flags);
5196      if (zputenv(newenv)) {
5197         zsfree(newenv);
5198 	pm->env = NULL;
5199 	return;
5200     }
5201 #ifdef USE_SET_UNSET_ENV
5202      /*
5203       * If we are using setenv/unsetenv to manage the environment,
5204       * we simply store the string we created in pm->env since
5205       * memory management of the environment is handled entirely
5206       * by the system.
5207       *
5208       * TODO: is this good enough to fix problem cases from
5209       * the other branch?  If so, we don't actually need to
5210       * store pm->env at all, just a flag that the value was set.
5211       */
5212      if (pm->env)
5213          zsfree(pm->env);
5214      pm->env = newenv;
5215      pm->node.flags |= PM_EXPORTED;
5216 #else
5217     /*
5218      * Under Cygwin we must use putenv() to maintain consistency.
5219      * Unfortunately, current version (1.1.2) copies argument and may
5220      * silently reuse existing environment string. This tries to
5221      * check for both cases
5222      */
5223     if (findenv(pm->node.nam, &pos)) {
5224 	env = environ[pos];
5225 	if (env != oldenv)
5226 	    zsfree(oldenv);
5227 	if (env != newenv)
5228 	    zsfree(newenv);
5229 	pm->node.flags |= PM_EXPORTED;
5230 	pm->env = env;
5231 	return;
5232     }
5233 
5234     DPUTS(1, "addenv should never reach the end");
5235     pm->env = NULL;
5236 #endif
5237 }
5238 
5239 
5240 /* Given strings *name = "foo", *value = "bar", *
5241  * return a new string *str = "foo=bar".        */
5242 
5243 /**/
5244 static char *
mkenvstr(char * name,char * value,int flags)5245 mkenvstr(char *name, char *value, int flags)
5246 {
5247     char *str, *s = value;
5248     int len_name, len_value = 0;
5249 
5250     len_name = strlen(name);
5251     if (s)
5252 	while (*s && (*s++ != Meta || *s++ != 32))
5253 	    len_value++;
5254     s = str = (char *) zalloc(len_name + len_value + 2);
5255     strcpy(s, name);
5256     s += len_name;
5257     *s = '=';
5258     if (value)
5259 	copyenvstr(s, value, flags);
5260     else
5261 	*++s = '\0';
5262     return str;
5263 }
5264 
5265 /* Given *name = "foo", *value = "bar", add the    *
5266  * string "foo=bar" to the environment.  Return a  *
5267  * pointer to the location of this new environment *
5268  * string.                                         */
5269 
5270 
5271 #ifndef USE_SET_UNSET_ENV
5272 /**/
5273 void
delenvvalue(char * x)5274 delenvvalue(char *x)
5275 {
5276     char **ep;
5277 
5278     for (ep = environ; *ep; ep++) {
5279 	if (*ep == x)
5280 	    break;
5281     }
5282     if (*ep) {
5283 	for (; (ep[0] = ep[1]); ep++);
5284     }
5285     zsfree(x);
5286 }
5287 #endif
5288 
5289 
5290 /* Delete a pointer from the list of pointers to environment *
5291  * variables by shifting all the other pointers up one slot. */
5292 
5293 /**/
5294 void
delenv(Param pm)5295 delenv(Param pm)
5296 {
5297 #ifdef USE_SET_UNSET_ENV
5298     unsetenv(pm->node.nam);
5299     zsfree(pm->env);
5300 #else
5301     delenvvalue(pm->env);
5302 #endif
5303     pm->env = NULL;
5304     /*
5305      * Note we don't remove PM_EXPORT from the flags.  This
5306      * may be asking for trouble but we need to know later
5307      * if we restore this parameter to its old value.
5308      */
5309 }
5310 
5311 /*
5312  * Guts of convbase: this version can return the number of digits
5313  * sans any base discriminator.
5314  */
5315 
5316 /**/
5317 void
convbase_ptr(char * s,zlong v,int base,int * ndigits)5318 convbase_ptr(char *s, zlong v, int base, int *ndigits)
5319 {
5320     int digs = 0;
5321     zulong x;
5322 
5323     if (v < 0)
5324 	*s++ = '-', v = -v;
5325     if (base >= -1 && base <= 1)
5326 	base = -10;
5327 
5328     if (base > 0) {
5329 	if (isset(CBASES) && base == 16)
5330 	    sprintf(s, "0x");
5331 	else if (isset(CBASES) && base == 8 && isset(OCTALZEROES))
5332 	    sprintf(s, "0");
5333 	else if (base != 10)
5334 	    sprintf(s, "%d#", base);
5335 	else
5336 	    *s = 0;
5337 	s += strlen(s);
5338     } else
5339 	base = -base;
5340     for (x = v; x; digs++)
5341 	x /= base;
5342     if (!digs)
5343 	digs = 1;
5344     if (ndigits)
5345 	*ndigits = digs;
5346     s[digs--] = '\0';
5347     x = v;
5348     while (digs >= 0) {
5349 	int dig = x % base;
5350 
5351 	s[digs--] = (dig < 10) ? '0' + dig : dig - 10 + 'A';
5352 	x /= base;
5353     }
5354 }
5355 
5356 /*
5357  * Basic conversion of integer to a string given a base.
5358  * If 0 base is 10.
5359  * If negative no base discriminator is output.
5360  */
5361 
5362 /**/
5363 mod_export void
convbase(char * s,zlong v,int base)5364 convbase(char *s, zlong v, int base)
5365 {
5366     convbase_ptr(s, v, base, NULL);
5367 }
5368 
5369 /*
5370  * Add underscores to converted integer for readability with given spacing.
5371  * s is as for convbase: at least BDIGBUFSIZE.
5372  * If underscores were added, returned value with underscores comes from
5373  * heap, else the returned value is s.
5374  */
5375 
5376 /**/
5377 char *
convbase_underscore(char * s,zlong v,int base,int underscore)5378 convbase_underscore(char *s, zlong v, int base, int underscore)
5379 {
5380     char *retptr, *sptr, *dptr;
5381     int ndigits, nunderscore, mod, len;
5382 
5383     convbase_ptr(s, v, base, &ndigits);
5384 
5385     if (underscore <= 0)
5386 	return s;
5387 
5388     nunderscore = (ndigits - 1) / underscore;
5389     if (!nunderscore)
5390 	return s;
5391     len = strlen(s);
5392     retptr = zhalloc(len + nunderscore + 1);
5393     mod = 0;
5394     memcpy(retptr, s, len - ndigits);
5395     sptr = s + len;
5396     dptr = retptr + len + nunderscore;
5397     /* copy the null */
5398     *dptr-- = *sptr--;
5399     for (;;) {
5400 	*dptr = *sptr;
5401 	if (!--ndigits)
5402 	    break;
5403 	dptr--;
5404 	sptr--;
5405 	if (++mod == underscore) {
5406 	    mod = 0;
5407 	    *dptr-- = '_';
5408 	}
5409     }
5410 
5411     return retptr;
5412 }
5413 
5414 /*
5415  * Convert a floating point value for output.
5416  * Unlike convbase(), this has its own internal storage and returns
5417  * a value from the heap.
5418  */
5419 
5420 /**/
5421 char *
convfloat(double dval,int digits,int flags,FILE * fout)5422 convfloat(double dval, int digits, int flags, FILE *fout)
5423 {
5424     char fmt[] = "%.*e";
5425     char *prev_locale, *ret;
5426 
5427     /*
5428      * The difficulty with the buffer size is that a %f conversion
5429      * prints all digits before the decimal point: with 64 bit doubles,
5430      * that's around 310.  We can't check without doing some quite
5431      * serious floating point operations we'd like to avoid.
5432      * Then we are liable to get all the digits
5433      * we asked for after the decimal point, or we should at least
5434      * bargain for it.  So we just allocate 512 + digits.  This
5435      * should work until somebody decides on 128-bit doubles.
5436      */
5437     if (!(flags & (PM_EFLOAT|PM_FFLOAT))) {
5438 	/*
5439 	 * Conversion from a floating point expression without using
5440 	 * a variable.  The best bet in this case just seems to be
5441 	 * to use the general %g format with something like the maximum
5442 	 * double precision.
5443 	 */
5444 	fmt[3] = 'g';
5445 	if (!digits)
5446 	    digits = 17;
5447     } else {
5448 	if (flags & PM_FFLOAT)
5449 	    fmt[3] = 'f';
5450 	if (digits <= 0)
5451 	    digits = 10;
5452 	if (flags & PM_EFLOAT) {
5453 	    /*
5454 	     * Here, we are given the number of significant figures, but
5455 	     * %e wants the number of decimal places (unlike %g)
5456 	     */
5457 	    digits--;
5458 	}
5459     }
5460 #ifdef USE_LOCALE
5461     prev_locale = dupstring(setlocale(LC_NUMERIC, NULL));
5462     setlocale(LC_NUMERIC, "POSIX");
5463 #endif
5464     if (fout) {
5465 	fprintf(fout, fmt, digits, dval);
5466 	ret = NULL;
5467     } else {
5468 	VARARR(char, buf, 512 + digits);
5469 	if (isinf(dval))
5470 	    ret = dupstring((dval < 0.0) ? "-Inf" : "Inf");
5471 	else if (isnan(dval))
5472 	    ret = dupstring("NaN");
5473 	else {
5474 	    sprintf(buf, fmt, digits, dval);
5475 	    if (!strchr(buf, 'e') && !strchr(buf, '.'))
5476 		strcat(buf, ".");
5477 	    ret = dupstring(buf);
5478 	}
5479     }
5480 #ifdef USE_LOCALE
5481     if (prev_locale) setlocale(LC_NUMERIC, prev_locale);
5482 #endif
5483     return ret;
5484 }
5485 
5486 /*
5487  * convert float to string with basic options but inserting underscores
5488  * for readability.
5489  */
5490 
5491 /**/
convfloat_underscore(double dval,int underscore)5492 char *convfloat_underscore(double dval, int underscore)
5493 {
5494     int ndigits_int = 0, ndigits_frac = 0, nunderscore, len;
5495     char *s, *retptr, *sptr, *dptr;
5496 
5497     s = convfloat(dval, 0, 0, NULL);
5498     if (underscore <= 0)
5499 	return s;
5500 
5501     /*
5502      * Count the number of digits before and after the decimal point, if any.
5503      */
5504     sptr = s;
5505     if (*sptr == '-')
5506 	sptr++;
5507     while (idigit(*sptr)) {
5508 	ndigits_int++;
5509 	sptr++;
5510     }
5511     if (*sptr == '.') {
5512 	sptr++;
5513 	while (idigit(*sptr)) {
5514 	    ndigits_frac++;
5515 	    sptr++;
5516 	}
5517     }
5518 
5519     /*
5520      * Work out how many underscores to insert --- remember we
5521      * put them in integer and fractional parts separately.
5522      */
5523     nunderscore = (ndigits_int-1) / underscore + (ndigits_frac-1) / underscore;
5524     if (!nunderscore)
5525 	return s;
5526     len = strlen(s);
5527     dptr = retptr = zhalloc(len + nunderscore + 1);
5528 
5529     /*
5530      * Insert underscores in integer part.
5531      * Grouping starts from the point in both directions.
5532      */
5533     sptr = s;
5534     if (*sptr == '-')
5535 	*dptr++ = *sptr++;
5536     while (ndigits_int) {
5537 	*dptr++ = *sptr++;
5538 	if (--ndigits_int && !(ndigits_int % underscore))
5539 	    *dptr++ = '_';
5540     }
5541     if (ndigits_frac) {
5542 	/*
5543 	 * Insert underscores in the fractional part.
5544 	 */
5545 	int mod = 0;
5546 	/* decimal point, we already checked */
5547 	*dptr++ = *sptr++;
5548 	while (ndigits_frac) {
5549 	    *dptr++ = *sptr++;
5550 	    mod++;
5551 	    if (--ndigits_frac && mod == underscore) {
5552 		*dptr++ = '_';
5553 		mod = 0;
5554 	    }
5555 	}
5556     }
5557     /* Copy exponent and anything else up to null */
5558     while ((*dptr++ = *sptr++))
5559 	;
5560     return retptr;
5561 }
5562 
5563 /* Start a parameter scope */
5564 
5565 /**/
5566 mod_export void
startparamscope(void)5567 startparamscope(void)
5568 {
5569     locallevel++;
5570 }
5571 
5572 /* End a parameter scope: delete the parameters local to the scope. */
5573 
5574 /**/
5575 mod_export void
endparamscope(void)5576 endparamscope(void)
5577 {
5578     queue_signals();
5579     locallevel--;
5580     /* This pops anything from a higher locallevel */
5581     saveandpophiststack(0, HFILE_USE_OPTIONS);
5582     scanhashtable(paramtab, 0, 0, 0, scanendscope, 0);
5583     unqueue_signals();
5584 }
5585 
5586 /**/
5587 static void
scanendscope(HashNode hn,UNUSED (int flags))5588 scanendscope(HashNode hn, UNUSED(int flags))
5589 {
5590     Param pm = (Param)hn;
5591     if (pm->level > locallevel) {
5592 	if ((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL) {
5593 	    /*
5594 	     * Removable specials are normal in that they can be removed
5595 	     * to reveal an ordinary parameter beneath.  Here we handle
5596 	     * non-removable specials, which were made local by stealth
5597 	     * (see newspecial code in typeset_single()).  In fact the
5598 	     * visible pm is always the same struct; the pm->old is
5599 	     * just a place holder for old data and flags.
5600 	     */
5601 	    Param tpm = pm->old;
5602 
5603 	    if (!strcmp(pm->node.nam, "SECONDS"))
5604 	    {
5605 		setsecondstype(pm, PM_TYPE(tpm->node.flags), PM_TYPE(pm->node.flags));
5606 		/*
5607 		 * We restore SECONDS by restoring its raw internal value
5608 		 * that we cached off into tpm->u.dval.
5609 		 */
5610 		setrawseconds(tpm->u.dval);
5611 		tpm->node.flags |= PM_NORESTORE;
5612 	    }
5613 	    DPUTS(!tpm || PM_TYPE(pm->node.flags) != PM_TYPE(tpm->node.flags) ||
5614 		  !(tpm->node.flags & PM_SPECIAL),
5615 		  "BUG: in restoring scope of special parameter");
5616 	    pm->old = tpm->old;
5617 	    pm->node.flags = (tpm->node.flags & ~PM_NORESTORE);
5618 	    pm->level = tpm->level;
5619 	    pm->base = tpm->base;
5620 	    pm->width = tpm->width;
5621 	    if (pm->env)
5622 		delenv(pm);
5623 
5624 	    if (!(tpm->node.flags & (PM_NORESTORE|PM_READONLY)))
5625 		switch (PM_TYPE(pm->node.flags)) {
5626 		case PM_SCALAR:
5627 		    pm->gsu.s->setfn(pm, tpm->u.str);
5628 		    break;
5629 		case PM_INTEGER:
5630 		    pm->gsu.i->setfn(pm, tpm->u.val);
5631 		    break;
5632 		case PM_EFLOAT:
5633 		case PM_FFLOAT:
5634 		    pm->gsu.f->setfn(pm, tpm->u.dval);
5635 		    break;
5636 		case PM_ARRAY:
5637 		    pm->gsu.a->setfn(pm, tpm->u.arr);
5638 		    break;
5639 		case PM_HASHED:
5640 		    pm->gsu.h->setfn(pm, tpm->u.hash);
5641 		    break;
5642 		}
5643 	    zfree(tpm, sizeof(*tpm));
5644 
5645 	    if (pm->node.flags & PM_EXPORTED)
5646 		export_param(pm);
5647 	} else
5648 	    unsetparam_pm(pm, 0, 0);
5649     }
5650 }
5651 
5652 
5653 /**********************************/
5654 /* Parameter Hash Table Functions */
5655 /**********************************/
5656 
5657 /**/
5658 void
freeparamnode(HashNode hn)5659 freeparamnode(HashNode hn)
5660 {
5661     Param pm = (Param) hn;
5662 
5663     /* The second argument of unsetfn() is used by modules to
5664      * differentiate "exp"licit unset from implicit unset, as when
5665      * a parameter is going out of scope.  It's not clear which
5666      * of these applies here, but passing 1 has always worked.
5667      */
5668     if (delunset)
5669 	pm->gsu.s->unsetfn(pm, 1);
5670     zsfree(pm->node.nam);
5671     /* If this variable was tied by the user, ename was ztrdup'd */
5672     if (!(pm->node.flags & PM_SPECIAL))
5673 	zsfree(pm->ename);
5674     zfree(pm, sizeof(struct param));
5675 }
5676 
5677 /* Print a parameter */
5678 
5679 enum paramtypes_flags {
5680     PMTF_USE_BASE	= (1<<0),
5681     PMTF_USE_WIDTH	= (1<<1),
5682     PMTF_TEST_LEVEL	= (1<<2)
5683 };
5684 
5685 struct paramtypes {
5686     int binflag;	/* The relevant PM_FLAG(S) */
5687     const char *string;	/* String for verbose output */
5688     int typeflag;	/* Flag for typeset -? */
5689     int flags;		/* The enum above */
5690 };
5691 
5692 static const struct paramtypes pmtypes[] = {
5693     { PM_AUTOLOAD, "undefined", 0, 0},
5694     { PM_INTEGER, "integer", 'i', PMTF_USE_BASE},
5695     { PM_EFLOAT, "float", 'E', 0},
5696     { PM_FFLOAT, "float", 'F', 0},
5697     { PM_ARRAY, "array", 'a', 0},
5698     { PM_HASHED, "association", 'A', 0},
5699     { 0, "local", 0, PMTF_TEST_LEVEL},
5700     { PM_LEFT, "left justified", 'L', PMTF_USE_WIDTH},
5701     { PM_RIGHT_B, "right justified", 'R', PMTF_USE_WIDTH},
5702     { PM_RIGHT_Z, "zero filled", 'Z', PMTF_USE_WIDTH},
5703     { PM_LOWER, "lowercase", 'l', 0},
5704     { PM_UPPER, "uppercase", 'u', 0},
5705     { PM_READONLY, "readonly", 'r', 0},
5706     { PM_TAGGED, "tagged", 't', 0},
5707     { PM_EXPORTED, "exported", 'x', 0},
5708     { PM_UNIQUE, "unique", 'U', 0},
5709     { PM_TIED, "tied", 'T', 0}
5710 };
5711 
5712 #define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes)))
5713 
5714 static void
printparamvalue(Param p,int printflags)5715 printparamvalue(Param p, int printflags)
5716 {
5717     char *t, **u;
5718 
5719     if (!(printflags & PRINT_KV_PAIR))
5720 	putchar('=');
5721 
5722     /* How the value is displayed depends *
5723      * on the type of the parameter       */
5724     switch (PM_TYPE(p->node.flags)) {
5725     case PM_SCALAR:
5726 	/* string: simple output */
5727 	if (p->gsu.s->getfn && (t = p->gsu.s->getfn(p)))
5728 	    quotedzputs(t, stdout);
5729 	break;
5730     case PM_INTEGER:
5731 	/* integer */
5732 #ifdef ZSH_64_BIT_TYPE
5733 	fputs(output64(p->gsu.i->getfn(p)), stdout);
5734 #else
5735 	printf("%ld", p->gsu.i->getfn(p));
5736 #endif
5737 	break;
5738     case PM_EFLOAT:
5739     case PM_FFLOAT:
5740 	/* float */
5741 	convfloat(p->gsu.f->getfn(p), p->base, p->node.flags, stdout);
5742 	break;
5743     case PM_ARRAY:
5744 	/* array */
5745 	if (!(printflags & PRINT_KV_PAIR)) {
5746 	    putchar('(');
5747 	    if (!(printflags & PRINT_LINE))
5748 		putchar(' ');
5749 	}
5750 	u = p->gsu.a->getfn(p);
5751 	if(*u) {
5752 	    if (printflags & PRINT_LINE) {
5753 		if (printflags & PRINT_KV_PAIR)
5754 		    printf("  ");
5755 		else
5756 		    printf("\n  ");
5757 	    }
5758 	    quotedzputs(*u++, stdout);
5759 	    while (*u) {
5760 		if (printflags & PRINT_LINE)
5761 		    printf("\n  ");
5762 		else
5763 		    putchar(' ');
5764 		quotedzputs(*u++, stdout);
5765 	    }
5766 	    if ((printflags & (PRINT_LINE|PRINT_KV_PAIR)) == PRINT_LINE)
5767 		putchar('\n');
5768 	}
5769 	if (!(printflags & PRINT_KV_PAIR)) {
5770 	    if (!(printflags & PRINT_LINE))
5771 		putchar(' ');
5772 	    putchar(')');
5773 	}
5774 	break;
5775     case PM_HASHED:
5776 	/* association */
5777 	{
5778 	    HashTable ht;
5779 	    int found = 0;
5780 	    if (!(printflags & PRINT_KV_PAIR)) {
5781 		putchar('(');
5782 		if (!(printflags & PRINT_LINE))
5783 		    putchar(' ');
5784 	    }
5785             ht = p->gsu.h->getfn(p);
5786             if (ht)
5787 		found = scanhashtable(ht, 1, 0, PM_UNSET,
5788 				      ht->printnode, PRINT_KV_PAIR |
5789 				      (printflags & PRINT_LINE));
5790 	    if (!(printflags & PRINT_KV_PAIR)) {
5791 		if (found && (printflags & PRINT_LINE))
5792 		    putchar('\n');
5793 		putchar(')');
5794 	    }
5795 	}
5796 	break;
5797     }
5798 }
5799 
5800 /**/
5801 mod_export void
printparamnode(HashNode hn,int printflags)5802 printparamnode(HashNode hn, int printflags)
5803 {
5804     Param p = (Param) hn;
5805     Param peer = NULL;
5806 
5807     if (p->node.flags & PM_UNSET) {
5808 	if (printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) &&
5809 	    p->node.flags & (PM_READONLY|PM_EXPORTED)) {
5810 	    /*
5811 	     * Special POSIX rules: show the parameter as readonly/exported
5812 	     * even though it's unset, but with no value.
5813 	     */
5814 	    printflags |= PRINT_NAMEONLY;
5815 	}
5816 	else
5817 	    return;
5818     }
5819     if (p->node.flags & PM_AUTOLOAD)
5820 	printflags |= PRINT_NAMEONLY;
5821 
5822     if (printflags & (PRINT_TYPESET|PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT)) {
5823 	if (p->node.flags & (PM_RO_BY_DESIGN|PM_AUTOLOAD)) {
5824 	    /*
5825 	     * It's not possible to restore the state of
5826 	     * these, so don't output.
5827 	     */
5828 	    return;
5829 	}
5830 	/*
5831 	 * The zsh variants of export -p/readonly -p also report other
5832 	 * flags to indicate other attributes or scope. The POSIX variants
5833 	 * don't.
5834 	 */
5835 	if (printflags & PRINT_POSIX_EXPORT) {
5836 	    printf("export ");
5837 	} else if (printflags & PRINT_POSIX_READONLY) {
5838 	    printf("readonly ");
5839 	} else if (locallevel && p->level >= locallevel) {
5840 	    printf("typeset ");	    /* printf("local "); */
5841 	} else if ((p->node.flags & PM_EXPORTED) &&
5842 		   !(p->node.flags & (PM_ARRAY|PM_HASHED))) {
5843 	    printf("export ");
5844 	} else if (locallevel) {
5845 	    printf("typeset -g ");
5846 	} else
5847 	    printf("typeset ");
5848     }
5849 
5850     /* Print the attributes of the parameter */
5851     if (printflags & (PRINT_TYPE|PRINT_TYPESET)) {
5852 	int doneminus = 0, i;
5853 	const struct paramtypes *pmptr;
5854 
5855 	for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) {
5856 	    int doprint = 0;
5857 	    if (pmptr->flags & PMTF_TEST_LEVEL) {
5858 		if (p->level)
5859 		    doprint = 1;
5860 	    } else if ((pmptr->binflag != PM_EXPORTED || p->level ||
5861 			(p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) &&
5862 		       (p->node.flags & pmptr->binflag))
5863 		doprint = 1;
5864 
5865 	    if (doprint) {
5866 		if (printflags & PRINT_TYPESET) {
5867 		    if (pmptr->typeflag) {
5868 			if (!doneminus) {
5869 			    putchar('-');
5870 			    doneminus = 1;
5871 			}
5872 			putchar(pmptr->typeflag);
5873 		    }
5874 		} else
5875 		    printf("%s ", pmptr->string);
5876 		if ((pmptr->flags & PMTF_USE_BASE) && p->base) {
5877 		    printf("%d ", p->base);
5878 		    doneminus = 0;
5879 		}
5880 		if ((pmptr->flags & PMTF_USE_WIDTH) && p->width) {
5881 		    printf("%u ", p->width);
5882 		    doneminus = 0;
5883 		}
5884 	    }
5885 	}
5886 	if (doneminus)
5887 	    putchar(' ');
5888 
5889 	if (p->node.flags & PM_TIED) {
5890 	    /*
5891 	     * For scalars tied to arrays,s
5892 	     *   * typeset +m outputs
5893 	     *      array tied SCALAR array
5894 	     *      tied array SCALAR
5895 	     *   * typeset -p outputs:
5896 	     *      typeset -T SCALAR array  (for hidden values)
5897 	     *      typeset -T SCALAR array=(values)
5898 	     *      for both scalar and array (flags may be different)
5899 	     *
5900 	     * We choose to print the value for the array instead of the scalar
5901 	     * as scalars can't disambiguate between
5902 	     * typeset -T SCALAR array=()
5903 	     * and
5904 	     * typeset -T SCALAR array=('')
5905 	     * (same for (a b:c)...)
5906 	     */
5907 	    Param tmp = (Param) paramtab->getnode(paramtab, p->ename);
5908 
5909 	    /*
5910 	     * Swap param and tied peer for typeset -p output
5911 	     */
5912 	    if (!(printflags & PRINT_TYPESET) || (p->node.flags & PM_ARRAY))
5913 		peer = tmp;
5914 	    else {
5915 		peer = p;
5916 		p = tmp;
5917 	    }
5918 
5919 	    quotedzputs(peer->node.nam, stdout);
5920 	    putchar(' ');
5921 	}
5922     }
5923 
5924     if ((printflags & PRINT_NAMEONLY) ||
5925 	((p->node.flags & PM_HIDEVAL) && !(printflags & PRINT_INCLUDEVALUE)))
5926 	quotedzputs(p->node.nam, stdout);
5927     else {
5928 	if (printflags & PRINT_KV_PAIR) {
5929 	    if (printflags & PRINT_LINE)
5930 		printf("\n  ");
5931 	    putchar('[');
5932 	}
5933 	quotedzputs(p->node.nam, stdout);
5934 	if (printflags & PRINT_KV_PAIR)
5935 	    printf("]=");
5936 
5937 	printparamvalue(p, printflags);
5938     }
5939     if (peer && (printflags & PRINT_TYPESET) && !(p->node.flags & PM_SPECIAL)) {
5940 	/*
5941 	 * append the join char for tied parameters if different from colon
5942 	 * for typeset -p output.
5943 	 */
5944 	unsigned char joinchar = STOUC(((struct tieddata *)peer->u.data)->joinchar);
5945 	if (joinchar != ':') {
5946 	    char buf[2];
5947 	    buf[0] = joinchar;
5948 	    buf[1] = '\0';
5949 	    putchar(' ');
5950 	    quotedzputs(buf, stdout);
5951 	}
5952     }
5953     if ((printflags & (PRINT_KV_PAIR|PRINT_LINE)) == PRINT_KV_PAIR)
5954 	putchar(' ');
5955     else if (!(printflags & PRINT_KV_PAIR))
5956 	putchar('\n');
5957 }
5958