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