params.c 122 KB
Newer Older
Tanaka Akira's avatar
Tanaka Akira committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * params.c - parameters
 *
 * This file is part of zsh, the Z shell.
 *
 * Copyright (c) 1992-1997 Paul Falstad
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and to distribute modified versions of this software for any
 * purpose, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * In no event shall Paul Falstad or the Zsh Development Group be liable
 * to any party for direct, indirect, special, incidental, or consequential
 * damages arising out of the use of this software and its documentation,
 * even if Paul Falstad and the Zsh Development Group have been advised of
 * the possibility of such damage.
 *
 * Paul Falstad and the Zsh Development Group specifically disclaim any
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose.  The software
 * provided hereunder is on an "as is" basis, and Paul Falstad and the
 * Zsh Development Group have no obligation to provide maintenance,
 * support, updates, enhancements, or modifications.
 *
 */

#include "zsh.mdh"
#include "params.pro"

#include "version.h"
34 35 36
#ifdef CUSTOM_PATCHLEVEL
#define ZSH_PATCHLEVEL	CUSTOM_PATCHLEVEL
#else
37
#include "patchlevel.h"
38 39 40 41 42

/* If removed from the ChangeLog for some reason */
#ifndef ZSH_PATCHLEVEL
#define ZSH_PATCHLEVEL "unknown"
#endif
43
#endif
Tanaka Akira's avatar
Tanaka Akira committed
44 45 46 47

/* what level of localness we are at */
 
/**/
48
mod_export int locallevel;
49

Tanaka Akira's avatar
Tanaka Akira committed
50 51 52
/* Variables holding values of special parameters */
 
/**/
53
mod_export
Tanaka Akira's avatar
Tanaka Akira committed
54 55 56 57 58 59
char **pparams,		/* $argv        */
     **cdpath,		/* $cdpath      */
     **fpath,		/* $fpath       */
     **mailpath,	/* $mailpath    */
     **manpath,		/* $manpath     */
     **psvar,		/* $psvar       */
60 61
     **watch,		/* $watch       */
     **zsh_eval_context; /* $zsh_eval_context */
62 63 64 65
/**/
mod_export
char **path,		/* $path        */
     **fignore;		/* $fignore     */
Tanaka Akira's avatar
Tanaka Akira committed
66 67
 
/**/
68
mod_export
Tanaka Akira's avatar
Tanaka Akira committed
69
char *argzero,		/* $0           */
70
     *posixzero,	/* $0           */
Tanaka Akira's avatar
Tanaka Akira committed
71 72 73 74 75 76 77 78 79 80
     *home,		/* $HOME        */
     *nullcmd,		/* $NULLCMD     */
     *oldpwd,		/* $OLDPWD      */
     *zoptarg,		/* $OPTARG      */
     *prompt,		/* $PROMPT      */
     *prompt2,		/* $PROMPT2     */
     *prompt3,		/* $PROMPT3     */
     *prompt4,		/* $PROMPT4     */
     *readnullcmd,	/* $READNULLCMD */
     *rprompt,		/* $RPROMPT     */
81
     *rprompt2,		/* $RPROMPT2    */
Tanaka Akira's avatar
Tanaka Akira committed
82 83 84
     *sprompt,		/* $SPROMPT     */
     *wordchars,	/* $WORDCHARS   */
     *zsh_name;		/* $ZSH_NAME    */
85 86 87 88
/**/
mod_export
char *ifs,		/* $IFS         */
     *postedit,		/* $POSTEDIT    */
Oliver Kiddle's avatar
Oliver Kiddle committed
89
     *term,		/* $TERM        */
90
     *zsh_terminfo,     /* $TERMINFO    */
91 92
     *ttystrname,	/* $TTY         */
     *pwd;		/* $PWD         */
Tanaka Akira's avatar
Tanaka Akira committed
93 94

/**/
95 96
mod_export
zlong lastval,		/* $?           */
Tanaka Akira's avatar
Tanaka Akira committed
97 98
     mypid,		/* $$           */
     lastpid,		/* $!           */
99 100
     zterm_columns,	/* $COLUMNS     */
     zterm_lines,	/* $LINES       */
101
     rprompt_indent,	/* $ZLE_RPROMPT_INDENT */
102 103
     ppid,		/* $PPID        */
     zsh_subshell;	/* $ZSH_SUBSHELL */
104 105
/**/
zlong lineno,		/* $LINENO      */
Tanaka Akira's avatar
Tanaka Akira committed
106 107 108 109 110 111
     zoptind,		/* $OPTIND      */
     shlvl;		/* $SHLVL       */

/* $histchars */
 
/**/
112 113 114
mod_export unsigned char bangchar;
/**/
unsigned char hatchar, hashchar;
115 116 117

/**/
unsigned char keyboardhackchar = '\0';
Tanaka Akira's avatar
Tanaka Akira committed
118
 
119 120 121
/* $SECONDS = now.tv_sec - shtimer.tv_sec
 *          + (now.tv_usec - shtimer.tv_usec) / 1000000.0
 * (rounded to an integer if the parameter is not set to float) */
Tanaka Akira's avatar
Tanaka Akira committed
122 123 124 125 126 127 128
 
/**/
struct timeval shtimer;
 
/* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */

/**/
129
mod_export int termflags;
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

/* Standard methods for get/set/unset pointers in parameters */

/**/
mod_export const struct gsu_scalar stdscalar_gsu =
{ strgetfn, strsetfn, stdunsetfn };
/**/
mod_export const struct gsu_scalar varscalar_gsu =
{ strvargetfn, strvarsetfn, stdunsetfn };
/**/
mod_export const struct gsu_scalar nullsetscalar_gsu =
{ strgetfn, nullstrsetfn, NULL };

/**/
mod_export const struct gsu_integer stdinteger_gsu =
{ intgetfn, intsetfn, stdunsetfn };
/**/
mod_export const struct gsu_integer varinteger_gsu =
{ intvargetfn, intvarsetfn, stdunsetfn };
/**/
mod_export const struct gsu_integer nullsetinteger_gsu =
{ intgetfn, NULL, NULL };

/**/
mod_export const struct gsu_float stdfloat_gsu =
{ floatgetfn, floatsetfn, stdunsetfn };

/**/
mod_export const struct gsu_array stdarray_gsu =
{ arrgetfn, arrsetfn, stdunsetfn };
/**/
mod_export const struct gsu_array vararray_gsu =
{ arrvargetfn, arrvarsetfn, stdunsetfn };

/**/
mod_export const struct gsu_hash stdhash_gsu =
{ hashgetfn, hashsetfn, stdunsetfn };
/**/
mod_export const struct gsu_hash nullsethash_gsu =
169
{ hashgetfn, nullsethashfn, nullunsetfn };
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197


/* Non standard methods (not exported) */
static const struct gsu_integer pound_gsu =
{ poundgetfn, nullintsetfn, stdunsetfn };
static const struct gsu_integer errno_gsu =
{ errnogetfn, errnosetfn, stdunsetfn };
static const struct gsu_integer gid_gsu =
{ gidgetfn, gidsetfn, stdunsetfn };
static const struct gsu_integer egid_gsu =
{ egidgetfn, egidsetfn, stdunsetfn };
static const struct gsu_integer histsize_gsu =
{ histsizegetfn, histsizesetfn, stdunsetfn };
static const struct gsu_integer random_gsu =
{ randomgetfn, randomsetfn, stdunsetfn };
static const struct gsu_integer savehist_gsu =
{ savehistsizegetfn, savehistsizesetfn, stdunsetfn };
static const struct gsu_integer intseconds_gsu =
{ intsecondsgetfn, intsecondssetfn, stdunsetfn };
static const struct gsu_float floatseconds_gsu =
{ floatsecondsgetfn, floatsecondssetfn, stdunsetfn };
static const struct gsu_integer uid_gsu =
{ uidgetfn, uidsetfn, stdunsetfn };
static const struct gsu_integer euid_gsu =
{ euidgetfn, euidsetfn, stdunsetfn };
static const struct gsu_integer ttyidle_gsu =
{ ttyidlegetfn, nullintsetfn, stdunsetfn };

198
static const struct gsu_scalar argzero_gsu =
199
{ argzerogetfn, argzerosetfn, nullunsetfn };
200 201 202 203 204 205 206 207 208 209
static const struct gsu_scalar username_gsu =
{ usernamegetfn, usernamesetfn, stdunsetfn };
static const struct gsu_scalar dash_gsu =
{ dashgetfn, nullstrsetfn, stdunsetfn };
static const struct gsu_scalar histchars_gsu =
{ histcharsgetfn, histcharssetfn, stdunsetfn };
static const struct gsu_scalar home_gsu =
{ homegetfn, homesetfn, stdunsetfn };
static const struct gsu_scalar term_gsu =
{ termgetfn, termsetfn, stdunsetfn };
210 211
static const struct gsu_scalar terminfo_gsu =
{ terminfogetfn, terminfosetfn, stdunsetfn };
212 213 214 215 216 217
static const struct gsu_scalar wordchars_gsu =
{ wordcharsgetfn, wordcharssetfn, stdunsetfn };
static const struct gsu_scalar ifs_gsu =
{ ifsgetfn, ifssetfn, stdunsetfn };
static const struct gsu_scalar underscore_gsu =
{ underscoregetfn, nullstrsetfn, stdunsetfn };
218 219
static const struct gsu_scalar keyboard_hack_gsu =
{ keyboardhackgetfn, keyboardhacksetfn, stdunsetfn };
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
#ifdef USE_LOCALE
static const struct gsu_scalar lc_blah_gsu =
{ strgetfn, lcsetfn, stdunsetfn };
static const struct gsu_scalar lang_gsu =
{ strgetfn, langsetfn, stdunsetfn };
static const struct gsu_scalar lc_all_gsu =
{ strgetfn, lc_allsetfn, stdunsetfn };
#endif

static const struct gsu_integer varint_readonly_gsu =
{ intvargetfn, nullintsetfn, stdunsetfn };
static const struct gsu_integer zlevar_gsu =
{ intvargetfn, zlevarsetfn, stdunsetfn };

static const struct gsu_scalar colonarr_gsu =
{ colonarrgetfn, colonarrsetfn, stdunsetfn };

static const struct gsu_integer argc_gsu =
{ poundgetfn, nullintsetfn, stdunsetfn };
static const struct gsu_array pipestatus_gsu =
{ pipestatgetfn, pipestatsetfn, stdunsetfn };

Tanaka Akira's avatar
Tanaka Akira committed
242 243 244 245
/* Nodes for special parameters for parameter hash table */

#ifdef HAVE_UNION_INIT
# define BR(X) {X}
246
typedef struct param initparam;
Tanaka Akira's avatar
Tanaka Akira committed
247 248
#else
# define BR(X) X
249
typedef struct iparam {
Tanaka Akira's avatar
Tanaka Akira committed
250 251 252 253
    struct hashnode *next;
    char *nam;			/* hash data                             */
    int flags;			/* PM_* flags (defined in zsh.h)         */
    void *value;
254 255 256
    void *gsu;			/* get/set/unset methods */
    int base;			/* output base                           */
    int width;			/* output field width                    */
Tanaka Akira's avatar
Tanaka Akira committed
257 258 259 260
    char *env;			/* location in environment, if exported  */
    char *ename;		/* name of corresponding environment var */
    Param old;			/* old struct for use with local         */
    int level;			/* if (old != NULL), level of localness  */
261
} initparam;
Tanaka Akira's avatar
Tanaka Akira committed
262
#endif
263 264

static initparam special_params[] ={
265 266
#define GSU(X) BR((GsuScalar)(void *)(&(X)))
#define NULL_GSU BR((GsuScalar)(void *)NULL)
267
#define IPDEF1(A,B,C) {{NULL,A,PM_INTEGER|PM_SPECIAL|C},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
268
IPDEF1("#", pound_gsu, PM_READONLY),
269
IPDEF1("ERRNO", errno_gsu, PM_UNSET),
270 271 272 273 274 275 276 277 278 279
IPDEF1("GID", gid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
IPDEF1("EGID", egid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
IPDEF1("HISTSIZE", histsize_gsu, PM_RESTRICTED),
IPDEF1("RANDOM", random_gsu, 0),
IPDEF1("SAVEHIST", savehist_gsu, PM_RESTRICTED),
IPDEF1("SECONDS", intseconds_gsu, 0),
IPDEF1("UID", uid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
IPDEF1("EUID", euid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
IPDEF1("TTYIDLE", ttyidle_gsu, PM_READONLY),

280
#define IPDEF2(A,B,C) {{NULL,A,PM_SCALAR|PM_SPECIAL|C},BR(NULL),GSU(B),0,0,NULL,NULL,NULL,0}
281 282 283
IPDEF2("USERNAME", username_gsu, PM_DONTIMPORT|PM_RESTRICTED),
IPDEF2("-", dash_gsu, PM_READONLY),
IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT),
284
IPDEF2("HOME", home_gsu, PM_UNSET),
285
IPDEF2("TERM", term_gsu, PM_UNSET),
286
IPDEF2("TERMINFO", terminfo_gsu, PM_UNSET),
287 288
IPDEF2("WORDCHARS", wordchars_gsu, 0),
IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT),
289
IPDEF2("_", underscore_gsu, PM_DONTIMPORT),
290
IPDEF2("KEYBOARD_HACK", keyboard_hack_gsu, PM_DONTIMPORT),
291
IPDEF2("0", argzero_gsu, 0),
Tanaka Akira's avatar
Tanaka Akira committed
292

293
#ifdef USE_LOCALE
294 295 296
# define LCIPDEF(name) IPDEF2(name, lc_blah_gsu, PM_UNSET)
IPDEF2("LANG", lang_gsu, PM_UNSET),
IPDEF2("LC_ALL", lc_all_gsu, PM_UNSET),
Tanaka Akira's avatar
Tanaka Akira committed
297 298 299 300 301 302 303 304 305
# ifdef LC_COLLATE
LCIPDEF("LC_COLLATE"),
# endif
# ifdef LC_CTYPE
LCIPDEF("LC_CTYPE"),
# endif
# ifdef LC_MESSAGES
LCIPDEF("LC_MESSAGES"),
# endif
306 307 308
# ifdef LC_NUMERIC
LCIPDEF("LC_NUMERIC"),
# endif
Tanaka Akira's avatar
Tanaka Akira committed
309 310 311
# ifdef LC_TIME
LCIPDEF("LC_TIME"),
# endif
312
#endif /* USE_LOCALE */
Tanaka Akira's avatar
Tanaka Akira committed
313

314
#define IPDEF4(A,B) {{NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL},BR((void *)B),GSU(varint_readonly_gsu),10,0,NULL,NULL,NULL,0}
Tanaka Akira's avatar
Tanaka Akira committed
315 316 317
IPDEF4("!", &lastpid),
IPDEF4("$", &mypid),
IPDEF4("?", &lastval),
318
IPDEF4("HISTCMD", &curhist),
Tanaka Akira's avatar
Tanaka Akira committed
319 320
IPDEF4("LINENO", &lineno),
IPDEF4("PPID", &ppid),
321
IPDEF4("ZSH_SUBSHELL", &zsh_subshell),
Tanaka Akira's avatar
Tanaka Akira committed
322

323
#define IPDEF5(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
324
#define IPDEF5U(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
325 326
IPDEF5("COLUMNS", &zterm_columns, zlevar_gsu),
IPDEF5("LINES", &zterm_lines, zlevar_gsu),
327
IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, zlevar_gsu),
328
IPDEF5("SHLVL", &shlvl, varinteger_gsu),
329 330 331 332 333

/* Don't import internal integer status variables. */
#define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0}
IPDEF6("OPTIND", &zoptind, varinteger_gsu),
IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
334
IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu),
Tanaka Akira's avatar
Tanaka Akira committed
335

336
#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
337
#define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
Tanaka Akira's avatar
Tanaka Akira committed
338 339
IPDEF7("OPTARG", &zoptarg),
IPDEF7("NULLCMD", &nullcmd),
340
IPDEF7U("POSTEDIT", &postedit),
Tanaka Akira's avatar
Tanaka Akira committed
341 342
IPDEF7("READNULLCMD", &readnullcmd),
IPDEF7("PS1", &prompt),
343 344
IPDEF7U("RPS1", &rprompt),
IPDEF7U("RPROMPT", &rprompt),
Tanaka Akira's avatar
Tanaka Akira committed
345
IPDEF7("PS2", &prompt2),
346 347
IPDEF7U("RPS2", &rprompt2),
IPDEF7U("RPROMPT2", &rprompt2),
Tanaka Akira's avatar
Tanaka Akira committed
348 349 350 351
IPDEF7("PS3", &prompt3),
IPDEF7("PS4", &prompt4),
IPDEF7("SPROMPT", &sprompt),

352
#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}
Tanaka Akira's avatar
Tanaka Akira committed
353 354 355 356 357 358 359
IPDEF8("CDPATH", &cdpath, "cdpath", 0),
IPDEF8("FIGNORE", &fignore, "fignore", 0),
IPDEF8("FPATH", &fpath, "fpath", 0),
IPDEF8("MAILPATH", &mailpath, "mailpath", 0),
IPDEF8("WATCH", &watch, "watch", 0),
IPDEF8("PATH", &path, "path", PM_RESTRICTED),
IPDEF8("PSVAR", &psvar, "psvar", 0),
360
IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY),
Tanaka Akira's avatar
Tanaka Akira committed
361 362 363 364

/* MODULE_PATH is not imported for security reasons */
IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED),

365
#define IPDEF9F(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}
Tanaka Akira's avatar
Tanaka Akira committed
366
#define IPDEF9(A,B,C) IPDEF9F(A,B,C,0)
Peter Stephenson's avatar
Peter Stephenson committed
367 368
IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
369 370 371 372 373

/*
 * This empty row indicates the end of parameters available in
 * all emulations.
 */
374
{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
375

376
#define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
Tanaka Akira's avatar
Tanaka Akira committed
377

378 379 380 381 382 383
/*
 * The following parameters are not available in sh/ksh compatibility *
 * mode.
 */

/* All of these have sh compatible equivalents.                */
384 385
IPDEF1("ARGC", argc_gsu, PM_READONLY),
IPDEF2("HISTCHARS", histchars_gsu, PM_DONTIMPORT),
Tanaka Akira's avatar
Tanaka Akira committed
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
IPDEF4("status", &lastval),
IPDEF7("prompt", &prompt),
IPDEF7("PROMPT", &prompt),
IPDEF7("PROMPT2", &prompt2),
IPDEF7("PROMPT3", &prompt3),
IPDEF7("PROMPT4", &prompt4),
IPDEF8("MANPATH", &manpath, "manpath", 0),
IPDEF9("argv", &pparams, NULL),
IPDEF9("fignore", &fignore, "FIGNORE"),
IPDEF9("cdpath", &cdpath, "CDPATH"),
IPDEF9("fpath", &fpath, "FPATH"),
IPDEF9("mailpath", &mailpath, "MAILPATH"),
IPDEF9("manpath", &manpath, "MANPATH"),
IPDEF9("psvar", &psvar, "PSVAR"),
IPDEF9("watch", &watch, "WATCH"),

402 403
IPDEF9F("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_READONLY),

Tanaka Akira's avatar
Tanaka Akira committed
404 405 406
IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED),
IPDEF9F("path", &path, "PATH", PM_RESTRICTED),

407 408
/* These are known to zsh alone. */

409
IPDEF10("pipestatus", pipestatus_gsu),
410

411
{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
Tanaka Akira's avatar
Tanaka Akira committed
412
};
413 414 415 416 417 418 419 420 421

/*
 * Special way of referring to the positional parameters.  Unlike $*
 * and $@, this is not readonly.  This parameter is not directly
 * visible in user space.
 */
initparam argvparam_pm = IPDEF9F("", &pparams, NULL, \
				 PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT);

Tanaka Akira's avatar
Tanaka Akira committed
422 423
#undef BR

424
#define IS_UNSET_VALUE(V) \
425 426
	((V) && (!(V)->pm || ((V)->pm->node.flags & PM_UNSET) || \
		 !(V)->pm->node.nam || !*(V)->pm->node.nam))
427

Tanaka Akira's avatar
Tanaka Akira committed
428 429 430 431 432
static Param argvparam;

/* hash table containing the parameters */
 
/**/
433 434 435 436 437 438
mod_export HashTable paramtab, realparamtab;

/**/
mod_export HashTable
newparamtable(int size, char const *name)
{
439 440 441 442
    HashTable ht;
    if (!size)
	size = 17;
    ht = newhashtable(size, name, NULL);
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461

    ht->hash        = hasher;
    ht->emptytable  = emptyhashtable;
    ht->filltable   = NULL;
    ht->cmpnodes    = strcmp;
    ht->addnode     = addhashnode;
    ht->getnode     = getparamnode;
    ht->getnode2    = getparamnode;
    ht->removenode  = removehashnode;
    ht->disablenode = NULL;
    ht->enablenode  = NULL;
    ht->freenode    = freeparamnode;
    ht->printnode   = printparamnode;

    return ht;
}

/**/
static HashNode
462
getparamnode(HashTable ht, const char *nam)
463 464 465 466
{
    HashNode hn = gethashnode2(ht, nam);
    Param pm = (Param) hn;

467
    if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) {
468 469
	char *mn = dupstring(pm->u.str);

470 471
	(void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL :
			    nam);
472
	hn = gethashnode2(ht, nam);
473 474 475 476 477 478
	if (!hn) {
	    /*
	     * This used to be a warning, but surely if we allow
	     * stuff to go ahead with the autoload stub with
	     * no error status we're in for all sorts of mayhem?
	     */
479 480
	    zerr("autoloading module %s failed to define parameter: %s", mn,
		 nam);
481 482 483 484 485 486 487 488 489 490 491
	}
    }
    return hn;
}

/* Copy a parameter hash table */

static HashTable outtable;

/**/
static void
492
scancopyparams(HashNode hn, UNUSED(int flags))
493 494 495
{
    /* Going into a real parameter, so always use permanent storage */
    Param pm = (Param)hn;
496
    Param tpm = (Param) zshcalloc(sizeof *tpm);
497
    tpm->node.nam = ztrdup(pm->node.nam);
498
    copyparam(tpm, pm, 0);
499
    addhashnode(outtable, tpm->node.nam, tpm);
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
}

/**/
HashTable
copyparamtable(HashTable ht, char *name)
{
    HashTable nht = newparamtable(ht->hsize, name);
    outtable = nht;
    scanhashtable(ht, 0, 0, 0, scancopyparams, 0);
    outtable = NULL;
    return nht;
}

/* Flag to freeparamnode to unset the struct */

static int delunset;

/* Function to delete a parameter table. */

/**/
mod_export void
deleteparamtable(HashTable t)
{
    /* The parameters in the hash table need to be unset *
     * before being deleted.                             */
    int odelunset = delunset;
    delunset = 1;
    deletehashtable(t);
    delunset = odelunset;
}

static unsigned numparamvals;

/**/
mod_export void
535
scancountparams(UNUSED(HashNode hn), int flags)
536 537 538 539 540 541 542 543 544
{
    ++numparamvals;
    if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS))
	++numparamvals;
}

static Patprog scanprog;
static char *scanstr;
static char **paramvals;
545
static Param foundparam;
546 547

/**/
548
static void
549 550 551 552 553 554 555 556 557 558
scanparamvals(HashNode hn, int flags)
{
    struct value v;
    Patprog prog;

    if (numparamvals && !(flags & SCANPM_MATCHMANY) &&
	(flags & (SCANPM_MATCHVAL|SCANPM_MATCHKEY|SCANPM_KEYMATCH)))
	return;
    v.pm = (Param)hn;
    if ((flags & SCANPM_KEYMATCH)) {
559
	char *tmp = dupstring(v.pm->node.nam);
560 561 562 563 564 565

	tokenize(tmp);
	remnulargs(tmp);

	if (!(prog = patcompile(tmp, 0, NULL)) || !pattry(prog, scanstr))
	    return;
566
    } else if ((flags & SCANPM_MATCHKEY) && !pattry(scanprog, v.pm->node.nam)) {
567 568
	return;
    }
569
    foundparam = v.pm;
570
    if (flags & SCANPM_WANTKEYS) {
571
	paramvals[numparamvals++] = v.pm->node.nam;
572 573 574
	if (!(flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)))
	    return;
    }
575
    v.isarr = (PM_TYPE(v.pm->node.flags) & (PM_ARRAY|PM_HASHED));
576
    v.flags = 0;
577
    v.start = 0;
578
    v.end = -1;
579 580 581 582 583 584 585 586 587
    paramvals[numparamvals] = getstrvalue(&v);
    if (flags & SCANPM_MATCHVAL) {
	if (pattry(scanprog, paramvals[numparamvals])) {
	    numparamvals += ((flags & SCANPM_WANTVALS) ? 1 :
			     !(flags & SCANPM_WANTKEYS));
	} else if (flags & SCANPM_WANTKEYS)
	    --numparamvals;	/* Value didn't match, discard key */
    } else
	++numparamvals;
588
    foundparam = NULL;
589 590 591 592 593 594
}

/**/
char **
paramvalarr(HashTable ht, int flags)
{
595 596
    DPUTS((flags & (SCANPM_MATCHKEY|SCANPM_MATCHVAL)) && !scanprog,
	  "BUG: scanning hash without scanprog set");
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
    numparamvals = 0;
    if (ht)
	scanhashtable(ht, 0, 0, PM_UNSET, scancountparams, flags);
    paramvals = (char **) zhalloc((numparamvals + 1) * sizeof(char *));
    if (ht) {
	numparamvals = 0;
	scanhashtable(ht, 0, 0, PM_UNSET, scanparamvals, flags);
    }
    paramvals[numparamvals] = 0;
    return paramvals;
}

/* Return the full array (no indexing) referred to by a Value. *
 * The array value is cached for the lifetime of the Value.    */

/**/
static char **
getvaluearr(Value v)
{
    if (v->arr)
	return v->arr;
618
    else if (PM_TYPE(v->pm->node.flags) == PM_ARRAY)
619
	return v->arr = v->pm->gsu.a->getfn(v->pm);
620
    else if (PM_TYPE(v->pm->node.flags) == PM_HASHED) {
621
	v->arr = paramvalarr(v->pm->gsu.h->getfn(v->pm), v->isarr);
622
	/* Can't take numeric slices of associative arrays */
623
	v->start = 0;
624
	v->end = numparamvals + 1;
625 626 627 628 629
	return v->arr;
    } else
	return NULL;
}

630
/*
Oliver Kiddle's avatar
Oliver Kiddle committed
631
 * Split environment string into (name, value) pair.
632 633 634 635 636 637 638 639 640 641 642 643
 * this is used to avoid in-place editing of environment table
 * that results in core dump on some systems
 */

static int
split_env_string(char *env, char **name, char **value)
{
    char *str, *tenv;

    if (!env || !name || !value)
	return 0;

644 645 646 647 648 649 650 651 652 653 654
    tenv = strcpy(zhalloc(strlen(env) + 1), env);
    for (str = tenv; *str && *str != '='; str++) {
	if (STOUC(*str) >= 128) {
	    /*
	     * We'll ignore environment variables with names not
	     * from the portable character set since we don't
	     * know of a good reason to accept them.
	     */
	    return 0;
	}
    }
655 656 657 658 659 660 661 662 663
    if (str != tenv && *str == '=') {
	*str = '\0';
	*name = tenv;
	*value = str + 1;
	return 1;
    } else
	return 0;
}
    
Tanaka Akira's avatar
Tanaka Akira committed
664 665 666 667 668 669 670 671 672
/* Set up parameter hash table.  This will add predefined  *
 * parameter entries as well as setting up parameter table *
 * entries for environment variables we inherit.           */

/**/
void
createparamtable(void)
{
    Param ip, pm;
673
#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV)
674 675 676
    char **new_environ;
    int  envsize;
#endif
677 678 679 680
#ifndef USE_SET_UNSET_ENV
    char **envp;
#endif
    char **envp2, **sigptr, **t;
681
    char buf[50], *str, *iname, *ivalue, *hostnam;
682
    int  oae = opts[ALLEXPORT];
683 684 685 686
#ifdef HAVE_UNAME
    struct utsname unamebuf;
    char *machinebuf;
#endif
Tanaka Akira's avatar
Tanaka Akira committed
687

688
    paramtab = realparamtab = newparamtable(151, "paramtab");
Tanaka Akira's avatar
Tanaka Akira committed
689 690

    /* Add the special parameters to the hash table */
691 692
    for (ip = special_params; ip->node.nam; ip++)
	paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
693
    if (!EMULATION(EMULATE_SH|EMULATE_KSH))
694 695
	while ((++ip)->node.nam)
	    paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
Tanaka Akira's avatar
Tanaka Akira committed
696

697
    argvparam = (Param) &argvparam_pm;
Tanaka Akira's avatar
Tanaka Akira committed
698

699 700 701 702
    noerrs = 2;

    /* Add the standard non-special parameters which have to    *
     * be initialized before we copy the environment variables. *
Oliver Kiddle's avatar
Oliver Kiddle committed
703
     * We don't want to override whatever values the user has   *
704 705 706 707 708
     * given them in the environment.                           */
    opts[ALLEXPORT] = 0;
    setiparam("MAILCHECK", 60);
    setiparam("LOGCHECK", 60);
    setiparam("KEYTIMEOUT", 40);
709
    setiparam("LISTMAX", 100);
710 711 712 713 714 715 716 717 718 719
    /*
     * We used to get the output baud rate here.  However, that's
     * pretty irrelevant to a terminal on an X display and can lead
     * to unnecessary delays if it's wrong (which it probably is).
     * Furthermore, even if the output is slow it's very likely
     * to be because of WAN delays, not covered by the output
     * baud rate.
     * So allow the user to set it in the special cases where it's
     * useful.
     */
720 721 722
    setsparam("TMPPREFIX", ztrdup_metafy(DEFAULT_TMPPREFIX));
    setsparam("TIMEFMT", ztrdup_metafy(DEFAULT_TIMEFMT));
    setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt));
723 724 725

    hostnam = (char *)zalloc(256);
    gethostname(hostnam, 256);
726
    setsparam("HOST", ztrdup_metafy(hostnam));
727 728
    zfree(hostnam, 256);

729 730 731
    setsparam("LOGNAME",
	      ztrdup_metafy((str = getlogin()) && *str ?
			    str : cached_username));
732

733
#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV)
734 735
    /* Copy the environment variables we are inheriting to dynamic *
     * memory, so we can do mallocs and frees on it.               */
736 737
    envsize = sizeof(char *)*(1 + arrlen(environ));
    new_environ = (char **) zalloc(envsize);
738
    memcpy(new_environ, environ, envsize);
739 740
    environ = new_environ;
#endif
741

742 743 744
    /* Use heap allocation to avoid many small alloc/free calls */
    pushheap();

745
    /* Now incorporate environment variables we are inheriting *
746 747
     * into the parameter hash table. Copy them into dynamic   *
     * memory so that we can free them if needed               */
748 749 750 751 752
    for (
#ifndef USE_SET_UNSET_ENV
	envp = 
#endif
	    envp2 = environ; *envp2; envp2++) {
753 754
	if (split_env_string(*envp2, &iname, &ivalue)) {
	    if (!idigit(*iname) && isident(iname) && !strchr(iname, '[')) {
755
		if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) ||
756
		     !(pm->node.flags & PM_DONTIMPORT || pm->node.flags & PM_EXPORTED)) &&
757 758
		    (pm = assignsparam(iname, metafy(ivalue, -1, META_DUP),
				       ASSPM_ENV_IMPORT))) {
759 760 761 762
		    pm->node.flags |= PM_EXPORTED;
		    if (pm->node.flags & PM_SPECIAL)
			pm->env = mkenvstr (pm->node.nam,
					    getsparam(pm->node.nam), pm->node.flags);
763 764
		    else
			pm->env = ztrdup(*envp2);
765
#ifndef USE_SET_UNSET_ENV
766
		    *envp++ = pm->env;
767
#endif
Tanaka Akira's avatar
Tanaka Akira committed
768 769 770
		}
	    }
	}
771
    }
772
    popheap();
773
#ifndef USE_SET_UNSET_ENV
774
    *envp = '\0';
775
#endif
776
    opts[ALLEXPORT] = oae;
Tanaka Akira's avatar
Tanaka Akira committed
777

778
    if (EMULATION(EMULATE_ZSH))
779 780 781 782 783 784
    {
	/*
	 * For native emulation we always set the variable home
	 * (see setupvals()).
	 */
	pm = (Param) paramtab->getnode(paramtab, "HOME");
785 786
	pm->node.flags &= ~PM_UNSET;
	if (!(pm->node.flags & PM_EXPORTED))
787 788
	    addenv(pm, home);
    }
789
    pm = (Param) paramtab->getnode(paramtab, "LOGNAME");
790
    if (!(pm->node.flags & PM_EXPORTED))
791 792
	addenv(pm, pm->u.str);
    pm = (Param) paramtab->getnode(paramtab, "SHLVL");
793
    sprintf(buf, "%d", (int)++shlvl);
794 795
    /* shlvl value in environment needs updating unconditionally */
    addenv(pm, buf);
796 797 798 799 800 801 802

    /* Add the standard non-special parameters */
    set_pwd_env();
#ifdef HAVE_UNAME
    if(uname(&unamebuf)) setsparam("CPUTYPE", ztrdup("unknown"));
    else
    {
803
       machinebuf = ztrdup_metafy(unamebuf.machine);
804 805
       setsparam("CPUTYPE", machinebuf);
    }
806

807
#else
808
    setsparam("CPUTYPE", ztrdup_metafy("unknown"));
809
#endif
810 811 812 813 814 815 816
    setsparam("MACHTYPE", ztrdup_metafy(MACHTYPE));
    setsparam("OSTYPE", ztrdup_metafy(OSTYPE));
    setsparam("TTY", ztrdup_metafy(ttystrname));
    setsparam("VENDOR", ztrdup_metafy(VENDOR));
    setsparam("ZSH_NAME", ztrdup_metafy(zsh_name));
    setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION));
    setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL));
817
    setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
818
    for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); );
Tanaka Akira's avatar
Tanaka Akira committed
819 820 821 822

    noerrs = 0;
}

823 824 825
/* assign various functions used for non-special parameters */

/**/
826
mod_export void
827 828
assigngetset(Param pm)
{
829
    switch (PM_TYPE(pm->node.flags)) {
830
    case PM_SCALAR:
831
	pm->gsu.s = &stdscalar_gsu;
832 833
	break;
    case PM_INTEGER:
834
	pm->gsu.i = &stdinteger_gsu;
835 836 837
	break;
    case PM_EFLOAT:
    case PM_FFLOAT:
838
	pm->gsu.f = &stdfloat_gsu;
839 840
	break;
    case PM_ARRAY:
841
	pm->gsu.a = &stdarray_gsu;
842 843
	break;
    case PM_HASHED:
844
	pm->gsu.h = &stdhash_gsu;
845 846 847 848 849 850 851
	break;
    default:
	DPUTS(1, "BUG: tried to create param node without valid flag");
	break;
    }
}

Tanaka Akira's avatar
Tanaka Akira committed
852 853 854 855 856 857 858 859 860
/* Create a parameter, so that it can be assigned to.  Returns NULL if the *
 * parameter already exists or can't be created, otherwise returns the     *
 * parameter node.  If a parameter of the same name exists in an outer     *
 * scope, it is hidden by a newly created parameter.  An already existing  *
 * parameter node at the current level may be `created' and returned       *
 * provided it is unset and not special.  If the parameter can't be        *
 * created because it already exists, the PM_UNSET flag is cleared.        */

/**/
861
mod_export Param
Tanaka Akira's avatar
Tanaka Akira committed
862 863 864 865
createparam(char *name, int flags)
{
    Param pm, oldpm;

866 867 868
    if (paramtab != realparamtab)
	flags = (flags & ~PM_EXPORTED) | PM_HASHELEM;

Tanaka Akira's avatar
Tanaka Akira committed
869
    if (name != nulstring) {
870 871 872
	oldpm = (Param) (paramtab == realparamtab ?
			 gethashnode2(paramtab, name) :
			 paramtab->getnode(paramtab, name));
Tanaka Akira's avatar
Tanaka Akira committed
873

874
	DPUTS(oldpm && oldpm->level > locallevel,
875
	      "BUG: old local parameter not deleted");
876
	if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) {
877 878 879 880
	    if (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_READONLY)) {
		zerr("read-only variable: %s", name);
		return NULL;
	    }
881 882 883
	    if (!(oldpm->node.flags & PM_UNSET) || (oldpm->node.flags & PM_SPECIAL)) {
		oldpm->node.flags &= ~PM_UNSET;
		if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) {
884
		    Param altpm =
885 886
			(Param) paramtab->getnode(paramtab, oldpm->ename);
		    if (altpm)
887
			altpm->node.flags &= ~PM_UNSET;
888
		}
Tanaka Akira's avatar
Tanaka Akira committed
889 890
		return NULL;
	    }
891
	    if ((oldpm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
892
		zerr("%s: restricted", name);
Tanaka Akira's avatar
Tanaka Akira committed
893 894 895 896
		return NULL;
	    }

	    pm = oldpm;
897
	    pm->base = pm->width = 0;
Tanaka Akira's avatar
Tanaka Akira committed
898 899
	    oldpm = pm->old;
	} else {
900
	    pm = (Param) zshcalloc(sizeof *pm);
Tanaka Akira's avatar
Tanaka Akira committed
901
	    if ((pm->old = oldpm)) {
902 903 904 905
		/*
		 * needed to avoid freeing oldpm, but we do take it
		 * out of the environment when it's hidden.
		 */
906 907
		if (oldpm->env)
		    delenv(oldpm);
Tanaka Akira's avatar
Tanaka Akira committed
908 909 910 911 912
		paramtab->removenode(paramtab, name);
	    }
	    paramtab->addnode(paramtab, ztrdup(name), pm);
	}

913
	if (isset(ALLEXPORT) && !(flags & PM_HASHELEM))
Tanaka Akira's avatar
Tanaka Akira committed
914
	    flags |= PM_EXPORTED;
915
    } else {
916
	pm = (Param) hcalloc(sizeof *pm);
917
	pm->node.nam = nulstring;
Tanaka Akira's avatar
Tanaka Akira committed
918
    }
919
    pm->node.flags = flags & ~PM_LOCAL;
920

921
    if(!(pm->node.flags & PM_SPECIAL))
922
	assigngetset(pm);
Tanaka Akira's avatar
Tanaka Akira committed
923 924 925
    return pm;
}

926 927 928 929 930 931 932 933
/* Empty dummy function for special hash parameters. */

/**/
static void
shempty(void)
{
}

934 935 936 937 938 939 940 941 942
/*
 * Create a simple special hash parameter.
 *
 * This is for hashes added internally --- it's not possible to add
 * special hashes from shell commands.  It's currently used
 * - by addparamdef() for special parameters in the zsh/parameter
 *   module
 * - by ztie for special parameters tied to databases.
 */
943 944 945 946 947 948 949 950 951 952 953

/**/
mod_export Param
createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan, int flags)
{
    Param pm;
    HashTable ht;

    if (!(pm = createparam(name, PM_SPECIAL|PM_HASHED|flags)))
	return NULL;

954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
    /*
     * If there's an old parameter, we'll put the new one at
     * the current locallevel, so that the old parameter is
     * exposed again after leaving the function.  Otherwise,
     * we'll leave it alone.  Usually this means the parameter
     * will stay in place until explicitly unloaded, however
     * if the parameter was previously unset within a function
     * we'll inherit the level of that function and follow the
     * standard convention that the parameter remains local
     * even if unset.
     *
     * These semantics are similar to those of a normal parameter set
     * within a function without a local definition.
     */
    if (pm->old)
	pm->level = locallevel;
970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
    pm->gsu.h = (flags & PM_READONLY) ? &stdhash_gsu :
	&nullsethash_gsu;
    pm->u.hash = ht = newhashtable(0, name, NULL);

    ht->hash        = hasher;
    ht->emptytable  = (TableFunc) shempty;
    ht->filltable   = NULL;
    ht->addnode     = (AddNodeFunc) shempty;
    ht->getnode     = ht->getnode2 = get;
    ht->removenode  = (RemoveNodeFunc) shempty;
    ht->disablenode = NULL;
    ht->enablenode  = NULL;
    ht->freenode    = (FreeNodeFunc) shempty;
    ht->printnode   = printparamnode;
    ht->scantab     = scan;

    return pm;
}


990 991 992 993 994 995 996
/*
 * Copy a parameter
 *
 * If fakecopy is set, we are just saving the details of a special
 * parameter.  Otherwise, the result will be used as a real parameter
 * and we need to do more work.
 */
997 998 999

/**/
void
1000
copyparam(Param tpm, Param pm, int fakecopy)
1001 1002 1003 1004 1005 1006 1007
{
    /*
     * Note that tpm, into which we're copying, may not be in permanent
     * storage.  However, the values themselves are later used directly
     * to set the parameter, so must be permanently allocated (in accordance
     * with sets.?fn() usage).
     */
1008
    tpm->node.flags = pm->node.flags;
1009 1010
    tpm->base = pm->base;
    tpm->width = pm->width;
1011 1012
    tpm->level = pm->level;
    if (!fakecopy)
1013 1014
	tpm->node.flags &= ~PM_SPECIAL;
    switch (PM_TYPE(pm->node.flags)) {
1015
    case PM_SCALAR:
1016
	tpm->u.str = ztrdup(pm->gsu.s->getfn(pm));
1017 1018
	break;
    case PM_INTEGER:
1019
	tpm->u.val = pm->gsu.i->getfn(pm);
1020 1021 1022
	break;
    case PM_EFLOAT:
    case PM_FFLOAT:
1023
	tpm->u.dval = pm->gsu.f->getfn(pm);
1024 1025
	break;
    case PM_ARRAY:
1026
	tpm->u.arr = zarrdup(pm->gsu.a->getfn(pm));
1027 1028
	break;
    case PM_HASHED:
1029
	tpm->u.hash = copyparamtable(pm->gsu.h->getfn(pm), pm->node.nam);
1030 1031 1032
	break;
    }
    /*
1033 1034 1035 1036
     * If the value is going to be passed as a real parameter (e.g. this is
     * called from inside an associative array), we need the gets and sets
     * functions to be useful.
     *
1037
     * In this case we assume the saved parameter is not itself special,
1038 1039
     * so we just use the standard functions.  This is also why we switch off
     * PM_SPECIAL.
1040
     */
1041
    if (!fakecopy)
1042 1043 1044
	assigngetset(tpm);
}

Tanaka Akira's avatar
Tanaka Akira committed
1045 1046 1047
/* Return 1 if the string s is a valid identifier, else return 0. */

/**/
1048
mod_export int
Tanaka Akira's avatar
Tanaka Akira committed
1049 1050 1051 1052 1053 1054 1055
isident(char *s)
{
    char *ss;

    if (!*s)			/* empty string is definitely not valid */
	return 0;

1056 1057 1058 1059 1060 1061 1062
    if (idigit(*s)) {
	/* If the first character is `s' is a digit, then all must be */
	for (ss = ++s; *ss; ss++)
	    if (!idigit(*ss))
		break;
    } else {
	/* Find the first character in `s' not in the iident type table */
1063
	ss = itype_end(s, IIDENT, 0);
1064
    }
Tanaka Akira's avatar
Tanaka Akira committed
1065

1066
    /* If the next character is not [, then it is *
1067
     * definitely not a valid identifier.         */
1068 1069
    if (!*ss)
	return 1;
1070 1071
    if (s == ss)
	return 0;
1072 1073
    if (*ss != '[')
	return 0;
Tanaka Akira's avatar
Tanaka Akira committed
1074

1075
    /* Require balanced [ ] pairs with something between */
1076
    if (!(ss = parse_subscript(++ss, 1, ']')))
1077
	return 0;
1078 1079
    untokenize(s);
    return !ss[1];
1080
}
Tanaka Akira's avatar
Tanaka Akira committed
1081

1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
/*
 * Parse a single argument to a parameter subscript.
 * The subscripts starts at *str; *str is updated (input/output)
 *
 * *inv is set to indicate if the subscript is reversed (output)
 * v is the Value for the parameter being accessed (input; note
 *  v->isarr may be modified, and if v is a hash the parameter will
 *  be updated to the element of the hash)
 * a2 is 1 if this is the second subscript of a range (input)
 * *w is only set if we need to find the end of a word (input; should
 *  be set to 0 by the caller).
 *
 * The final two arguments are to support multibyte characters.
 * If supplied they are set to the length of the character before
 * the index position and the one at the index position.  If
 * multibyte characters are not in use they are set to 1 for
1098 1099
 * consistency.  Note they aren't fully handled if a2 is non-zero,
 * since they aren't needed.
1100 1101 1102
 *
 * Returns a raw offset into the value from the start or end (i.e.
 * after the arithmetic for Meta and possible multibyte characters has
1103 1104
 * been taken into account).  This actually gives the offset *after*
 * the character in question; subtract *prevcharlen if necessary.
1105 1106
 */

Tanaka Akira's avatar
Tanaka Akira committed
1107
/**/
1108
static zlong
1109 1110
getarg(char **str, int *inv, Value v, int a2, zlong *w,
       int *prevcharlen, int *nextcharlen)
Tanaka Akira's avatar
Tanaka Akira committed
1111
{
1112
    int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
1113
    int keymatch = 0, needtok = 0, arglen, len;
1114
    char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt, c;
1115
    zlong num = 1, beg = 0, r = 0, quote_arg = 0;
1116 1117
    Patprog pprog = NULL;

1118
    /*
1119 1120
     * If in NO_EXEC mode, the parameters won't be set up properly,
     * so just pretend everything is a hash for subscript parsing
1121 1122
     */

1123 1124
    ishash = (unset(EXECOPT) ||
	      (v->pm && PM_TYPE(v->pm->node.flags) == PM_HASHED));
1125 1126 1127 1128
    if (prevcharlen)
	*prevcharlen = 1;
    if (nextcharlen)
	*nextcharlen = 1;
Tanaka Akira's avatar
Tanaka Akira committed
1129 1130

    /* first parse any subscription flags */
1131
    if (v->pm && (*s == '(' || *s == Inpar)) {
Tanaka Akira's avatar
Tanaka Akira committed
1132 1133 1134 1135 1136 1137
	int escapes = 0;
	int waste;
	for (s++; *s != ')' && *s != Outpar && s != *str; s++) {
	    switch (*s) {
	    case 'r':
		rev = 1;
1138
		keymatch = down = ind = 0;
Tanaka Akira's avatar
Tanaka Akira committed
1139 1140
		break;
	    case 'R':
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
		rev = down = 1;
		keymatch = ind = 0;
		break;
	    case 'k':
		keymatch = ishash;
		rev = 1;
		down = ind = 0;
		break;
	    case 'K':
		keymatch = ishash;
Tanaka Akira's avatar
Tanaka Akira committed
1151 1152 1153 1154 1155
		rev = down = 1;
		ind = 0;
		break;
	    case 'i':
		rev = ind = 1;
1156
		down = keymatch = 0;
Tanaka Akira's avatar
Tanaka Akira committed
1157 1158 1159
		break;
	    case 'I':
		rev = ind = down = 1;
1160
		keymatch = 0;
Tanaka Akira's avatar
Tanaka Akira committed
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
		break;
	    case 'w':
		/* If the parameter is a scalar, then make subscription *
		 * work on a per-word basis instead of characters.      */
		word = 1;
		break;
	    case 'f':
		word = 1;
		sep = "\n";
		break;
	    case 'e':
1172
		quote_arg = 1;
Tanaka Akira's avatar
Tanaka Akira committed
1173 1174
		break;
	    case 'n':
1175
		t = get_strarg(++s, &arglen);
Tanaka Akira's avatar
Tanaka Akira committed
1176 1177 1178 1179
		if (!*t)
		    goto flagerr;
		sav = *t;
		*t = '\0';
1180
		num = mathevalarg(s + arglen, &d);
Tanaka Akira's avatar
Tanaka Akira committed
1181 1182 1183
		if (!num)
		    num = 1;
		*t = sav;
1184
		s = t + arglen - 1;
Tanaka Akira's avatar
Tanaka Akira committed
1185
		break;
1186 1187
	    case 'b':
		hasbeg = 1;
1188
		t = get_strarg(++s, &arglen);
1189 1190 1191 1192
		if (!*t)
		    goto flagerr;
		sav = *t;
		*t = '\0';
1193
		if ((beg = mathevalarg(s + arglen, &d)) > 0)
1194 1195
		    beg--;
		*t = sav;
1196
		s = t + arglen - 1;
1197
		break;
Tanaka Akira's avatar
Tanaka Akira committed
1198 1199 1200 1201 1202
	    case 'p':
		escapes = 1;
		break;
	    case 's':
		/* This gives the string that separates words *
1203
		 * (for use with the `w' flag).               */
1204
		t = get_strarg(++s, &arglen);
Tanaka Akira's avatar
Tanaka Akira committed
1205 1206 1207 1208
		if (!*t)
		    goto flagerr;
		sav = *t;
		*t = '\0';
1209 1210 1211
		s += arglen;
		sep = escapes ? getkeystring(s, &waste, GETKEYS_SEP, NULL)
		    : dupstring(s);
Tanaka Akira's avatar
Tanaka Akira committed
1212
		*t = sav;
1213
		s = t + arglen - 1;
Tanaka Akira's avatar
Tanaka Akira committed
1214 1215 1216 1217
		break;
	    default:
	      flagerr:
		num = 1;
1218
		word = rev = ind = down = keymatch = 0;
Tanaka Akira's avatar
Tanaka Akira committed
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
		sep = NULL;
		s = *str - 1;
	    }
	}
	if (s != *str)
	    s++;
    }
    if (num < 0) {
	down = !down;
	num = -num;
    }
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240
    if (v->isarr & SCANPM_WANTKEYS)
	*inv = (ind || !(v->isarr & SCANPM_WANTVALS));
    else if (v->isarr & SCANPM_WANTVALS)
	*inv = 0;
    else {
	if (v->isarr) {
	    if (ind) {
		v->isarr |= SCANPM_WANTKEYS;
		v->isarr &= ~SCANPM_WANTVALS;
	    } else if (rev)
		v->isarr |= SCANPM_WANTVALS;
1241 1242 1243 1244 1245
	    /*
	     * This catches the case where we are using "k" (rather
	     * than "K") on a hash.
	     */
	    if (!down && keymatch && ishash)
1246 1247 1248 1249
		v->isarr &= ~SCANPM_MATCHMANY;
	}
	*inv = ind;
    }
Tanaka Akira's avatar
Tanaka Akira committed
1250

1251
    for (t = s, i = 0;
1252
	 (c = *t) && ((c != Outbrack &&
1253
		       (ishash || c != ',')) || i); t++) {
1254 1255
	/* Untokenize inull() except before brackets and double-quotes */
	if (inull(c)) {
1256 1257 1258 1259
	    c = t[1];
	    if (c == '[' || c == ']' ||
		c == '(' || c == ')' ||
		c == '{' || c == '}') {
1260 1261
		/* This test handles nested subscripts in hash keys */
		if (ishash && i)
1262
		    *t = ztokens[*t - Pound];
1263 1264
		needtok = 1;
		++t;
1265
	    } else if (c != '"')
1266
		*t = ztokens[*t - Pound];
1267 1268 1269
	    continue;
	}
	/* Inbrack and Outbrack are probably never found here ... */
1270
	if (c == '[' || c == Inbrack)
Tanaka Akira's avatar
Tanaka Akira committed
1271
	    i++;
1272
	else if (c == ']' || c == Outbrack)
Tanaka Akira's avatar
Tanaka Akira committed
1273
	    i--;
1274 1275 1276 1277
	if (ispecial(c))
	    needtok = 1;
    }
    if (!c)
Tanaka Akira's avatar
Tanaka Akira committed
1278 1279
	return 0;
    *str = tt = t;
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290

    /*
     * If in NO_EXEC mode, the parameters won't be set up properly,
     * so there's no additional sanity checking we can do.
     * Just return 0 now.
     */
    if (unset(EXECOPT))
	return 0;

    s = dupstrpfx(s, t - s);

1291
    /* If we're NOT reverse subscripting, strip the inull()s so brackets *
1292 1293 1294
     * are not backslashed after parsestr().  Otherwise leave them alone *
     * so that the brackets will be escaped when we patcompile() or when *
     * subscript arithmetic is performed (for nested subscripts).        */
1295
    if (ishash && (keymatch || !rev))
1296
	remnulargs(s);
1297
    if (needtok) {
1298 1299
	s = dupstring(s);
	if (parsestr(&s))
1300 1301
	    return 0;
	singsub(&s);
1302 1303
    } else if (rev)
	remnulargs(s);	/* This is probably always a no-op, but ... */
Tanaka Akira's avatar
Tanaka Akira committed
1304
    if (!rev) {
1305
	if (ishash) {
1306
	    HashTable ht = v->pm->gsu.h->getfn(v->pm);
1307
	    if (!ht) {
1308
		ht = newparamtable(17, v->pm->node.nam);
1309
		v->pm->gsu.h->setfn(v->pm, ht);
1310 1311 1312 1313 1314 1315 1316 1317 1318
	    }
	    untokenize(s);
	    if (!(v->pm = (Param) ht->getnode(ht, s))) {
		HashTable tht = paramtab;
		paramtab = ht;
		v->pm = createparam(s, PM_SCALAR|PM_UNSET);
		paramtab = tht;
	    }
	    v->isarr = (*inv ? SCANPM_WANTINDEX : 0);
1319
	    v->start = 0;
1320
	    *inv = 0;	/* We've already obtained the "index" (key) */
1321
	    *w = v->end = -1;
1322
	    r = isset(KSHARRAYS) ? 1 : 0;
1323 1324
	} else {
	    r = mathevalarg(s, &s);
1325
	    if (isset(KSHARRAYS) && r >= 0)
1326 1327
		r++;
	}
Tanaka Akira's avatar
Tanaka Akira committed
1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
	if (word && !v->isarr) {
	    s = t = getstrvalue(v);
	    i = wordcount(s, sep, 0);
	    if (r < 0)
		r += i + 1;
	    if (r < 1)
		r = 1;
	    if (r > i)
		r = i;
	    if (!s || !*s)
		return 0;
	    while ((d = findword(&s, sep)) && --r);
	    if (!d)
		return 0;

	    if (!a2 && *tt != ',')
1344
		*w = (zlong)(s - t);
Tanaka Akira's avatar
Tanaka Akira committed
1345 1346 1347

	    return (a2 ? s : d + 1) - t;
	} else if (!v->isarr && !word) {
1348
	    int lastcharlen = 1;
Tanaka Akira's avatar
Tanaka Akira committed
1349
	    s = getstrvalue(v);
1350 1351 1352 1353 1354 1355 1356
	    /*
	     * Note for the confused (= pws):  the index r we
	     * have so far is that specified by the user.  The value
	     * passed back is an offset from the start or end of
	     * the string.  Hence it needs correcting at least
	     * for Meta characters and maybe for multibyte characters.
	     */
Tanaka Akira's avatar
Tanaka Akira committed
1357
	    if (r > 0) {
1358 1359 1360 1361 1362 1363 1364
		zlong nchars = r;

		MB_METACHARINIT();
		for (t = s; nchars && *t; nchars--)
		    t += (lastcharlen = MB_METACHARLEN(t));
		/* for consistency, keep any remainder off the end */
		r = (zlong)(t - s) + nchars;
1365
		if (prevcharlen && !nchars /* ignore if off the end */)
1366 1367 1368
		    *prevcharlen = lastcharlen;
		if (nextcharlen && *t)
		    *nextcharlen = MB_METACHARLEN(t);
1369 1370 1371 1372 1373 1374 1375
	    } else if (r == 0) {
		if (prevcharlen)
		    *prevcharlen = 0;
		if (nextcharlen && *s) {
		    MB_METACHARINIT();
		    *nextcharlen = MB_METACHARLEN(s);
		}
Tanaka Akira's avatar
Tanaka Akira committed
1376
	    } else {
1377 1378 1379
		zlong nchars = (zlong)MB_METASTRLEN(s) + r;

		if (nchars < 0) {
1380 1381
		    /* make sure this isn't valid as a raw pointer */
		    r -= (zlong)strlen(s);
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
		} else {
		    MB_METACHARINIT();
		    for (t = s; nchars && *t; nchars--)
			t += (lastcharlen = MB_METACHARLEN(t));
		    r = - (zlong)strlen(t); /* keep negative */
		    if (prevcharlen)
			*prevcharlen = lastcharlen;
		    if (nextcharlen && *t)
			*nextcharlen = MB_METACHARLEN(t);
		}
Tanaka Akira's avatar
Tanaka Akira committed
1392 1393 1394 1395 1396 1397 1398
	    }
	}
    } else {
	if (!v->isarr && !word) {
	    l = strlen(s);
	    if (a2) {
		if (!l || *s != '*') {
1399
		    d = (char *) hcalloc(l + 2);
Tanaka Akira's avatar
Tanaka Akira committed
1400 1401 1402 1403 1404
		    *d = '*';
		    strcpy(d + 1, s);
		    s = d;
		}
	    } else {
1405
		if (!l || s[l - 1] != '*' || (l > 1 && s[l - 2] == '\\')) {
1406
		    d = (char *) hcalloc(l + 2);
Tanaka Akira's avatar
Tanaka Akira committed