Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtksh / ksh93 / src / cmd / ksh93 / sh / name.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: name.c /main/4 1996/11/07 16:19:40 rswiston $ */
24 /***************************************************************
25 *                                                              *
26 *                      AT&T - PROPRIETARY                      *
27 *                                                              *
28 *        THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF        *
29 *                    AT&T BELL LABORATORIES                    *
30 *         AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN         *
31 *            ACCORDANCE WITH APPLICABLE AGREEMENTS             *
32 *                                                              *
33 *                Copyright (c) 1995 AT&T Corp.                 *
34 *              Unpublished & Not for Publication               *
35 *                     All Rights Reserved                      *
36 *                                                              *
37 *       The copyright notice above does not evidence any       *
38 *      actual or intended publication of such source code      *
39 *                                                              *
40 *               This software was created by the               *
41 *           Advanced Software Technology Department            *
42 *                    AT&T Bell Laboratories                    *
43 *                                                              *
44 *               For further information contact                *
45 *                    {research,attmail}!dgk                    *
46 *                                                              *
47 ***************************************************************/
48
49 /* : : generated by proto : : */
50
51 #if !defined(__PROTO__)
52 #if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
53 #if defined(__cplusplus)
54 #define __MANGLE__      "C"
55 #else
56 #define __MANGLE__
57 #endif
58 #define __STDARG__
59 #define __PROTO__(x)    x
60 #define __OTORP__(x)
61 #define __PARAM__(n,o)  n
62 #if !defined(__STDC__) && !defined(__cplusplus)
63 #if !defined(c_plusplus)
64 #define const
65 #endif
66 #define signed
67 #define void            int
68 #define volatile
69 #define __V_            char
70 #else
71 #define __V_            void
72 #endif
73 #else
74 #define __PROTO__(x)    ()
75 #define __OTORP__(x)    x
76 #define __PARAM__(n,o)  o
77 #define __MANGLE__
78 #define __V_            char
79 #define const
80 #define signed
81 #define void            int
82 #define volatile
83 #endif
84 #if defined(__cplusplus) || defined(c_plusplus)
85 #define __VARARG__      ...
86 #else
87 #define __VARARG__
88 #endif
89 #if defined(__STDARG__)
90 #define __VA_START__(p,a)       va_start(p,a)
91 #else
92 #define __VA_START__(p,a)       va_start(p)
93 #endif
94 #endif
95 #include        "defs.h"
96 #include        <ctype.h>
97 #include        "variables.h"
98 #include        "path.h"
99 #include        "lexstates.h"
100 #include        "timeout.h"
101 #include        "FEATURE/locale"
102 #include        "national.h"
103
104 #ifdef apollo
105     extern __MANGLE__ void      ev_$delete_var(__VARARG__);
106     extern __MANGLE__ void      ev_$set_var(__VARARG__);
107 #endif  /* apollo */
108
109 static void     attstore __PROTO__((Namval_t*));
110 static void     pushnam __PROTO__((Namval_t*));
111 static char     *staknam __PROTO__((Namval_t*, char*));
112 static void     utol __PROTO__((const char*,char*));
113 static void     ltou __PROTO__((const char*,char*));
114 static void     rightjust __PROTO__((char*, int, int));
115
116 static char     **argnam;
117 static int      attsize;
118 static char     *attval;
119 static char     forced;
120 static char     local;
121 static void(*nullscan) __PROTO__((Namval_t*));
122
123 /* ========     name value pair routines        ======== */
124
125 #include        "shnodes.h"
126 #include        "builtins.h"
127
128 /*
129  * necessary to force linking to system putenv.  Otherwise putenv is defined
130  * to be "setenv" which is resolved in libast.a
131  */
132 #undef putenv
133
134 extern char *getenv();
135
136 /*
137  * Remove an element from the environ array.  Code mostly taken from
138  * DtEnvRemove, but this code does pointer comparisons rather than
139  * strcmp calls, and does not free anything.
140  */
141 extern char     **environ;
142 int remove_env_entry __PARAM__((char *pEntry), (pEntry)) __OTORP__(char *pEntry;)
143 {
144         char **pEnviron, **pEnviron2 = (char **)environ;
145         char *p;
146         int temp;
147
148         int count  = 0;  /* count is the number of items in the */
149                          /* environ                             */
150         int index;   /* index will range from 0 to count - 1 */
151         int len;
152
153         if (!(len = strlen(pEntry)))
154                 return(1);
155
156         pEnviron = pEnviron2;
157
158         p = *pEnviron;
159
160         while (p)
161         {
162                 pEnviron++;
163                 count++;
164                 p = *pEnviron;
165         }
166
167         pEnviron = pEnviron2;
168         p = *pEnviron;
169
170         for  (index = 0; index < count; index++)
171         {
172                 if (p == pEntry)
173                 {
174                     for (temp = index; temp != count - 1; temp++)
175                     {
176                             pEnviron2[temp] = pEnviron2[temp + 1];
177                     }
178                     pEnviron2[count - 1] = NULL;
179                     return(0);
180                 }
181                 pEnviron++;
182                 p = *pEnviron;
183         }
184         return(1);
185 }
186
187 /*
188  * Utility to mirror changes in our environment hash table into the
189  * environment seen by library routines in internally-linked libc
190  * libraries.
191  */
192 void mirror_env __PARAM__((Namval_t *np), (np)) __OTORP__(Namval_t *np;)
193 {
194         char *buf, *val = nv_getval(np), *name = nv_name(np);
195         int freeIt = 0;
196
197         /*
198          * CDExc22516: Under some circumstances, dtksh's environ
199          * is NULL on Fujitsu.  This in itself may be a problem
200          * that should be researched, but for now, we simply
201          * protect against using putenv/getenv if environ is
202          * NULL.  rswiston@x.org 11/07/96
203          */
204         if((name != (char *)NULL) && (environ != (char **)NULL))
205         {
206                 /*
207                 * This is funky, but I'm not confident that I fully understand the
208                 * use/reuse patterns of the NamVal_t structs.  This code ensures
209                 * that we only free up environment variables that we are replacing.
210                 */
211                 if((np->nvdtenv != (char *)NULL) &&
212                 (getenv(name) == (np->nvdtenv + strlen(name) + 1)))
213                                 freeIt = 1;
214
215                 if(val != (char *)NULL)
216                         buf = (char *)malloc(strlen(name) + strlen(val) + 2);
217                 else
218                         buf = (char *)malloc(strlen(name) + 2);
219                 strcpy(buf, name);
220                 strcat(buf, "=");
221                 if(val != (char *)NULL)
222                         strcat(buf, val);
223
224                 putenv(buf);
225                 if(freeIt == 1)
226                         free((__V_*)np->nvdtenv);
227                 np->nvdtenv = buf;
228         }
229 }
230
231
232 /*
233  * Perform parameter assignment for a linked list of parameters
234  * <flags> contains attributes for the parameters
235  */
236 void nv_setlist __PARAM__((register struct argnod *arg,register int flags), (arg, flags)) __OTORP__(register struct argnod *arg;register int flags;){
237         register char *cp;
238         register Namval_t *np;
239         int traceon = (sh_isoption(SH_XTRACE)!=0);
240         if(sh_isoption(SH_ALLEXPORT))
241                 flags |= NV_EXPORT;
242         if(sh.prefix)
243         {
244                 flags &= ~(NV_IDENT|NV_EXPORT);
245                 flags |= NV_VARNAME;
246         }
247         for(;arg; arg=arg->argnxt.ap)
248         {
249                 sh.used_pos = 0;
250                 if(arg->argflag&ARG_MAC)
251                         cp = sh_mactrim(arg->argval,-1);
252                 else
253                 {
254                         stakseek(0);
255                         if(*arg->argval==0)
256                         {
257                                 int flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN);
258                                 struct fornod *fp=(struct fornod*)arg->argchn.ap;
259                                 register union anynode *tp=fp->fortre;
260                                 char *prefix = sh.prefix;
261                                 cp = fp->fornam;
262                                 error_info.line = fp->fortyp-sh.st.firstline;
263                                 if(sh.fn_depth && (Namval_t*)tp->com.comnamp==SYSTYPESET)
264                                         flag |= NV_NOSCOPE;
265                                 np = nv_open(cp,sh.var_tree,flag);
266                                 nv_unset(np);
267                                 /* check for array assignment */
268                                 if(tp->tre.tretyp!=TLST && !tp->com.comset)
269                                 {
270                                         int argc;
271                                         char **argv = sh_argbuild(&argc,&tp->com);
272                                         nv_setvec(np,argc,argv);
273                                         if(traceon)
274                                         {
275                                                 sh_trace(NIL(char**),0);
276                                                 sfputr(sfstderr,nv_name(np),'=');
277                                                 sfwrite(sfstderr,"( ",2);
278                                                 while(cp= *argv++)
279                                                         sfputr(sfstderr,sh_fmtq(cp),' ');
280                                                 sfwrite(sfstderr,")\n",2);
281                                         }
282                                         continue;
283                                 }
284                                 if(tp->tre.tretyp==TLST || tp->com.comset->argval[0]!='[')
285                                         nv_setvtree(np);
286                                 else
287                                         nv_setarray(np,nv_associative);
288                                 if(prefix)
289                                 {
290                                         stakputs(prefix);
291                                         stakputc('.');
292                                         stakputs(cp);
293                                         cp = stakfreeze(1);
294                                 }
295                                 sh.prefix = cp;
296                                 sh_exec(tp,sh_isstate(SH_ERREXIT));
297                                 sh.prefix = prefix;
298                                 continue;
299                         }
300                         cp = arg->argval;
301                 }
302                 np = nv_open(cp,sh.var_tree,flags);
303                 if(sh.used_pos)
304                         np->nvsize |= NV_PARAM;
305                 else
306                         np->nvsize &= ~NV_PARAM;
307                 if(traceon)
308                 {
309                         register char *sp=cp;
310                         sh_trace(NIL(char**),0);
311                         sfputr(sfstderr,nv_name(np),-1);
312                         if(nv_isattr(np,NV_ARRAY) && (cp=strchr(sp,'[')))
313                         {
314                                 /* no quoting needed up to the = */
315                                 sp = nv_endsubscript(np,cp,0);
316                                 sfwrite(sfstderr,cp,sp -cp);
317                                 cp = sp;
318                         }
319                         else
320                                 cp = strchr(sp,'=');
321                         if(cp)
322                                 sfprintf(sfstderr,"=%s\n",sh_fmtq(cp+1));
323                 }
324         }
325 }
326
327 /*
328  * construct a new name from a prefix and base name on the stack
329  */
330 static char *newname __PARAM__((register const char *prefix, register const char *name), (prefix, name)) __OTORP__(register const char *prefix; register const char *name;){
331         register int offset = staktell();
332         stakputs(prefix);
333         if(*name!='[')
334                 stakputc('.');
335         stakputs(name);
336         stakputc(0);
337         stakseek(offset);
338         return(stakptr(offset));
339 }
340
341 /*
342  * Put <arg> into associative memory.
343  * If <flags> & NV_ARRAY then subscript is not allowed
344  * If <flags> & NV_NOSCOPE then use the current scope only
345  * If <flags> & NV_ASSIGN then assignment is allowed
346  * If <flags> & NV_IDENT then name must be an identifier
347  * If <flags> & NV_VARNAME then name must be a valid variable name
348  * If <flags> & NV_NOADD then node will not be added if not found
349  * SH_INIT is only set while initializing the environment
350  */
351 Namval_t        *nv_open __PARAM__((const char *name,Hashtab_t *root,int flags), (name, root, flags)) __OTORP__(const char *name;Hashtab_t *root;int flags;){
352         register char *cp = (char*)name;
353         register Namval_t       *np=0;
354         register int sep = *cp;
355         register char *lastdot = 0;
356         register long mode = ((flags&NV_NOADD)?0:NV_ADD);
357         if(root==sh.alias_tree)
358         {
359                 while((sep= *(unsigned char*)cp) && (sep!='=') && (sep!='/') && 
360                         (!(sep=sh_lexstates[ST_NORM][sep]) || sep==S_EPAT))
361                         cp++;
362         }
363         else
364         {
365                 if(!root)
366                         root = sh.var_tree;
367                 if(!(flags&(NV_IDENT|NV_VARNAME|NV_ASSIGN)))
368                 {
369                         if(flags&NV_NOSCOPE)
370                                 mode |= HASH_SCOPE|HASH_NOSCOPE;
371                         np = nv_search(name,root,mode);
372                         if(np && !(flags&NV_REF))
373                         {
374                                 while(nv_isattr(np,NV_REF))
375                                         np=np->nvalue.np;
376                         }
377                         return(np);
378                 }
379                 if(sh.prefix && (flags&NV_ASSIGN))
380                         name = cp = newname(sh.prefix,name);
381                 /* first skip over alpha-numeric */
382                 while(1)
383                 {
384                         if(sep=='.')
385                         {
386                                 if(flags&NV_IDENT)
387                                         goto failed;
388                                 if(root==sh.var_tree)
389                                         flags &= ~(NV_NOSCOPE|NV_EXPORT);
390                                 if(!lastdot && cp!=name && (flags&NV_VARNAME))
391                                 {
392                                         /* see whether first component is ref */
393                                         *cp = 0;
394                                         np = nv_search(name,sh.var_tree,0);
395                                         *cp = '.';
396                                         if(np && nv_isattr(np,NV_REF))
397                                         {
398                                                 /* substitute ref name */
399                                                 while(nv_isattr(np,NV_REF))
400                                                         np=np->nvalue.np;
401                                                 name = nv_name(np);
402                                                 sep = strlen(name);
403                                                 name=lastdot=newname(name,cp+1);
404                                                 cp = lastdot + sep +1;
405                                         }
406                                         else
407                                                 lastdot = cp++;
408                                 }
409                                 else
410                                         lastdot = cp++;
411                         }
412                         if(sep= *(unsigned char*)cp, !isaletter(sep))
413                                 break;
414                         while(sep= *(unsigned char*)(++cp),isaname(sep));
415                 }
416                 /* if name doesn't have to be an varname or ident skip to '=' */
417 #ifdef SHOPT_APPEND
418                 if(sep && sep!='=' && sep!='[' && sep!='+')
419 #else
420                 if(sep && sep!='=' && sep!='[')
421 #endif /* SHOPT_APPEND */
422                 {
423                         if(flags&NV_IDENT)
424                                 goto failed;
425                         else if(flags&NV_VARNAME)
426                                 error(ERROR_exit(1),(root==sh.var_tree?e_varname:e_funname),name);
427                         while((sep= *++cp) && sep!='=');
428                 }
429                 if(lastdot)
430                         np = nv_search(name,root,0);
431                 while(!np && lastdot && lastdot>name)
432                 {
433                         *lastdot=0;
434                         np = nv_search(name,sh.var_tree,0);
435                         *lastdot='.';
436                         if(np)
437                         {
438                                 char *sp;
439                                 Namval_t *nq;
440                                 if(np->nvfun && (np->nvfun)->disc->create)
441                                 {
442                                         *cp = 0;
443                                         np = nv_create(nq=np,lastdot+1,(Namfun_t*)np);
444                                         *cp = sep;
445                                         if(np==nq)
446                                                 error(ERROR_exit(1),e_varname,name);
447                                 }
448                                 else if((sp=strchr(lastdot+1,'.')) && sp<cp)
449                                         error(ERROR_exit(1),e_noparent,name);
450                                 else
451                                         np = 0;
452                                 break;
453                         }
454                         while(--lastdot>name && *lastdot!='.');
455                         if(lastdot==name || (!np && root!=sh.var_tree))
456                                 error(ERROR_exit(1),e_noparent,name);
457                 }
458         }
459         if(cp!=name)
460         {
461                 if(sep)
462                         *cp = 0;
463                 if((flags&NV_NOSCOPE) && hashscope(root) && root==sh.var_tree 
464                         && (np=nv_search(name,sh.var_base,0)))
465                 {
466                         Namfun_t *disc = nv_cover(np);
467                         if(np=nv_search((char*)np,root,mode|HASH_NOSCOPE|HASH_SCOPE|HASH_BUCKET))
468                                 np->nvfun = disc;
469                 }
470                 else if(!np)
471                 {
472                         if(flags&NV_NOSCOPE)
473                                 mode |= HASH_SCOPE|HASH_NOSCOPE;
474                         np = nv_search(name,root,mode);
475                 }
476                 if(sep)
477                         *cp = sep;
478                 if((flags&NV_REF) || (!np && (flags&NV_NOADD)))
479                         return(np);
480                 while(nv_isattr(np,NV_REF))
481                         np=np->nvalue.np;
482                 /* check for subscript*/
483                 if(sep=='[' && !(flags&NV_ARRAY))
484                 {
485                         sep = (flags&NV_ASSIGN?NV_ADD:0);
486                         cp = nv_endsubscript(np,cp,NV_ADD);
487                         sep = *cp;
488                 }
489                 else if(nv_isattr(np,NV_ARRAY))
490                         nv_putsub(np,NIL(char*),ARRAY_UNDEF);
491 #ifdef SHOPT_APPEND
492                 if(sep=='+')
493                         sep = *++cp;
494 #endif /* SHOPT_APPEND */
495                 if(sep && ((sep!='=')||!(flags&NV_ASSIGN)))
496                 {
497                         if(sh_isstate(SH_INIT))
498                                 return(0);
499                         goto failed;
500                 }
501                 if(root==sh.alias_tree)
502                 {
503                         if(nv_isattr(np,NV_TAGGED|NV_NOALIAS))
504                                 nv_offattr(np,(NV_NOALIAS|NV_TAGGED|NV_EXPORT));
505                 }
506                 if(sep == '=')
507                 {
508                         cp++;
509                         if(sh_isstate(SH_INIT))
510                         {
511                                 nv_putval(np, cp, NV_RDONLY);
512                                 if(np==PWDNOD)
513                                         nv_onattr(np,NV_TAGGED);
514                         }
515                         else
516                         {
517                                 nv_putval(np, cp, 0);
518 #ifdef SHOPT_BSH
519                                 if(flags&NV_EXPORT)
520                                         nv_offattr(np,NV_IMPORT);
521 #endif /* SHOPT_BSH */
522                         }
523                         nv_onattr(np, flags&NV_ATTRIBUTES);
524 #ifdef apollo
525                         /*
526                          * Set environment variable defined in the underlying
527                          * DOMAIN_OS cache. This is done because dsee will only
528                          * process the path if it has changed since the last
529                          * time it looked.
530                          */
531                         if(nv_isattr(np,NV_EXPORT) && !nv_isattr(np,NV_IMPORT) 
532                                 && (flags&NV_ASSIGN) && !(flags&(NV_NOSCOPE|NV_ARRAY)))
533                         {
534                                 short namlen,vallen;
535                                 namlen =strlen(nv_name(np));
536                                 vallen = strlen(cp);
537                                 ev_$set_var(nv_name(np),&namlen,cp,&vallen);
538                         }
539 #endif /* apollo */
540                         /*
541                          * Set environment variable defined in the underlying
542                          * libc environ. This is done because routines within
543                          * a shared libc will only look in the libc environ,
544                          * not in our hash trees to find environment variables.
545                          */
546                         if(nv_isattr(np,NV_EXPORT))
547                                 mirror_env(np);
548                 }
549                 return(np);
550         }
551 failed:
552         if(!sh_isstate(SH_INIT))
553                 error(ERROR_exit(1),(root==sh.alias_tree?e_aliname:e_ident),name);
554         return(0);
555 }
556
557 #ifdef SHOPT_MULTIBYTE
558     static char *savep;
559     static char savechars[ESS_MAXCHAR+1];
560     static int ja_size __PROTO__((char*, int, int));
561     static void ja_restore __PROTO__((void));
562 #endif /* SHOPT_MULTIBYTE */
563
564 /*
565  * put value <string> into name-value node <np>.
566  * If <np> is an array, then the element given by the
567  *   current index is assigned to.
568  * If <flags> contains NV_RDONLY, readonly attribute is ignored
569  * If <flags> contains NV_INTEGER, string is a pointer to a number
570  * If <flags> contains NV_NOFREE, previous value is freed, and <string>
571  * becomes value of node and <flags> becomes attributes
572  */
573 void nv_putval __PARAM__((register Namval_t *np, const char *string, int flags), (np, string, flags)) __OTORP__(register Namval_t *np; const char *string; int flags;){
574         register const char *sp=string;
575         register union Value *up;
576         register char *cp;
577         register int size = 0;
578         register int dot;
579         if(!(flags&NV_RDONLY) && nv_isattr (np, NV_RDONLY))
580                 error(ERROR_exit(1),e_readonly, nv_name(np));
581         /* The following could cause the shell to fork if assignment
582          * would cause a side effect
583          */
584         if(sh.subshell)
585                 np = sh_assignok(np,1);
586         if(nv_isattr(np, NV_ARRAY))
587                 array_check(np,ARRAY_ASSIGN);
588         if(np->nvfun)
589         {
590                 /* This function contains disc */
591                 if(!local)
592                 {
593                         local=1;
594                         nv_putv(np,sp,flags,np->nvfun);
595                         return;
596                 }
597                 /* called from disc, assign the actual value */
598                 local=0;
599         }
600         if(flags&(NV_REF|NV_NOFREE))
601         {
602                 nv_unset(np);
603                 np->nvalue.cp = (char*)sp;
604                 nv_setattr(np,flags|NV_NOFREE);
605                 return;
606         }
607         if(nv_isattr(np, NV_ARRAY))
608                 up = array_find(np,ARRAY_ASSIGN);
609         else
610                 up= &np->nvalue;
611 #ifndef SHOPT_BSH
612         nv_offattr(np,NV_IMPORT);
613         np->nvenv = 0;
614 #endif /* SHOPT_BSH */
615         if(nv_isattr (np, NV_INTEGER))
616         {
617                 if(nv_isattr (np, NV_CPOINTER))
618                         up->cp = (char*)sp;
619                 else if(nv_isattr(np, NV_DOUBLE))
620                 {
621                         double d;
622                         if(flags&NV_INTEGER)
623                                 d = *(double*)sp;
624                         else
625                                 d = sh_arith(sp);
626                         if(!up->dp)
627                                 up->dp = new_of(double,0);
628                         *(up->dp) = d;
629                 }
630                 else
631                 {
632                         long l = 0;
633                         if(flags&NV_INTEGER)
634                                 l = *(double*)sp;
635                         else if(sp)
636                                 l = (long)sh_arith(sp);
637                         else
638                                 sh.lastbase = 10;
639                         if(nv_size(np) <= 1)
640                                 nv_setsize(np,sh.lastbase);
641                         if(nv_isattr (np, NV_SHORT))
642                                 up->s = (short)l;
643                         else
644                         {
645                                 if(!up->lp)
646                                         up->lp = new_of(long,0);
647                                 *(up->lp) = l;
648                                 if(l && *sp++ == '0')
649                                         nv_onattr(np,NV_UNSIGN);
650                         }
651                 }
652         }
653         else
654         {
655                 const char *tofree=0;
656                 if(flags&NV_INTEGER)
657                         sp = sh_etos(*((double*)sp),12);
658 #ifdef apollo
659                 if(nv_isattr(np, NV_HOST)==NV_HOST && sp)
660                 {
661                         /*
662                          * return the host file name given the UNIX name
663                          * non-unix hosts that use file name mapping
664                          * should change this
665                          */
666                         char pathname[1024];
667                         short pathlen;
668                         unix_fio_$get_name(sp,pathname,&pathlen);
669                         pathname[pathlen] = 0;
670                         sp = pathname;
671                 }
672 #endif  /* apollo */
673                 if((nv_isattr(np, NV_RJUST|NV_ZFILL|NV_LJUST)) && sp)
674                 {
675                         for(;*sp == ' '|| *sp=='\t';sp++);
676                         if((nv_isattr(np,NV_ZFILL)) && (nv_isattr(np,NV_LJUST)))
677                                 for(;*sp=='0';sp++);
678                         size = nv_size(np);
679 #ifdef SHOPT_MULTIBYTE
680                         if(size)
681                                 size = ja_size((char*)sp,size,nv_isattr(np,NV_RJUST|NV_ZFILL));
682 #endif /* SHOPT_MULTIBYTE */
683                 }
684                 if(!nv_isattr(np, NV_NOFREE|NV_NOALLOC))
685                 {
686                         /* delay free in case <sp> points into free region */
687                         tofree = up->cp;
688                 }
689                 if(nv_isattr(np, NV_NOALLOC))
690                         cp = (char*)up->cp;
691                 else
692                 {
693                         nv_offattr(np,NV_NOFREE);
694                         if (sp)
695                         {
696                                 dot = strlen(sp);
697                                 if(size==0 && nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL))
698                                         nv_setsize(np,size=dot);
699                                 else if(size > dot)
700                                         dot = size;
701                                 cp = (char*)malloc(((unsigned)dot+1));
702                         }
703                         else
704                                 cp = 0;
705                         up->cp = cp;
706                 }
707                 if(sp)
708                 {
709                         if(nv_isattr(np, NV_LTOU))
710                                 ltou(sp,cp);
711                         else if(nv_isattr (np, NV_UTOL))
712                                 utol(sp,cp);
713                         else
714                                 strcpy(cp, sp);
715                         if(nv_isattr(np, NV_RJUST) && nv_isattr(np, NV_ZFILL))
716                                 rightjust(cp,size,'0');
717                         else if(nv_isattr(np, NV_RJUST))
718                                 rightjust(cp,size,' ');
719                         else if(nv_isattr(np, NV_LJUST))
720                         {
721                                 register char *dp;
722                                 dp = strlen (cp) + cp;
723                                 *(cp = (cp + size)) = 0;
724                                 for (; dp < cp; *dp++ = ' ');
725                          }
726 #ifdef SHOPT_MULTIBYTE
727                         /* restore original string */
728                         if(savep)
729                                 ja_restore();
730 #endif /* SHOPT_MULTIBYTE */
731                 }
732                 if(tofree)
733                         free((__V_*)tofree);
734         }
735 #ifdef apollo
736         if((flags&NV_RDONLY) && nv_isattr(np,NV_EXPORT))
737         {
738                 short namlen, vallen;
739                 char *vp = nv_getval(np);
740                 namlen =strlen(nv_name(np));
741                 vallen = strlen(vp);
742                 ev_$set_var(nv_name(np),&namlen,vp,&vallen);
743         }
744 #endif /* apollo */
745         /*
746          * Set environment variable defined in the underlying
747          * libc environ. This is done because routines within
748          * a shared libc will only look in the libc environ,
749          * not in our hash trees to find environment variables.
750          */
751         if((flags&NV_RDONLY) && nv_isattr(np,NV_EXPORT))
752                 mirror_env(np);
753         return;
754 }
755
756 /*
757  *
758  *   Right-justify <str> so that it contains no more than
759  *   <size> characters.  If <str> contains fewer than <size>
760  *   characters, left-pad with <fill>.  Trailing blanks
761  *   in <str> will be ignored.
762  *
763  *   If the leftmost digit in <str> is not a digit, <fill>
764  *   will default to a blank.
765  */
766 static void rightjust __PARAM__((char *str, int size, int fill), (str, size, fill)) __OTORP__(char *str; int size; int fill;){
767         register int n;
768         register char *cp,*sp;
769         n = strlen(str);
770
771         /* ignore trailing blanks */
772         for(cp=str+n;n && *--cp == ' ';n--);
773         if (n == size)
774                 return;
775         if(n > size)
776         {
777                 *(str+n) = 0;
778                 for (sp = str, cp = str+n-size; sp <= str+size; *sp++ = *cp++);
779                 return;
780         }
781         else *(sp = str+size) = 0;
782         if (n == 0)  
783         {
784                 while (sp > str)
785                         *--sp = ' ';
786                 return;
787         }
788         while(n--)
789         {
790                 sp--;
791                 *sp = *cp--;
792         }
793         if(!isdigit(*str))
794                 fill = ' ';
795         while(sp>str)
796                 *--sp = fill;
797         return;
798 }
799
800 #ifdef SHOPT_MULTIBYTE
801     /*
802      * handle left and right justified fields for multi-byte chars
803      * given physical size, return a logical size which reflects the
804      * screen width of multi-byte characters
805      * Multi-width characters replaced by spaces if they cross the boundary
806      * <type> is non-zero for right justified  fields
807      */
808
809     static int ja_size __PARAM__((char *str,int size,int type), (str, size, type)) __OTORP__(char *str;int size;int type;){
810         register char *cp = str;
811         register int c, n=size;
812         int oldn;
813         wchar_t w;
814         while(*cp)
815         {
816                 oldn = n;
817                 if((c=mbtowc(&w,cp,MB_CUR_MAX))>0)
818                 {
819                         register int outsize =  wcwidth(w);
820                         /* allow room for excess input bytes */
821                         n += (c-outsize);
822                         size -= outsize;
823                 }
824                 else
825                 {
826                         c = 1;
827                         size -=1;
828                 }
829                 if(size<=0 && type==0)
830                         break;
831                 cp += c;
832         }
833         /* check for right justified fields that need truncating */
834         if(size <0)
835         {
836                 if(type==0)
837                 {
838                         /* left justified and character crosses field boundary */
839                         n = oldn;
840                         /* save boundary char and replace with spaces */
841                         size = c;
842                         savechars[size] = 0;
843                         while(size--)
844                         {
845                                 savechars[size] = cp[size];
846                                 cp[size] = ' ';
847                         }
848                         savep = cp;
849                 }
850                 size = -size;
851                 if(type)
852                         n -= (ja_size(str,size,0)-size);
853         }
854         return(n);
855     }
856
857     static void ja_restore __PARAM__((void), ()){
858         register char *cp = savechars;
859         while(*cp)
860                 *savep++ = *cp++;
861         savep = 0;
862     }
863 #endif /* SHOPT_MULTIBYTE */
864
865 static char *staknam __PARAM__((register Namval_t *np, char *value), (np, value)) __OTORP__(register Namval_t *np; char *value;){
866         register char *p,*q;
867         q = stakalloc(strlen(nv_name(np))+(value?strlen(value):0)+2);
868         p=strcopy(q,nv_name(np));
869         if(value)
870         {
871                 *p++ = '=';
872                 strcpy(p,value);
873         }
874         return(q);
875 }
876
877 /*
878  * put the name and attribute into value of attributes variable
879  */
880 static void attstore __PARAM__((register Namval_t *np), (np)) __OTORP__(register Namval_t *np;){
881         register int flag = np->nvflag;
882         if(!(flag&NV_EXPORT) || (flag&NV_FUNCT))
883                 return;
884         flag &= (NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER);
885         *attval++ = '=';
886         *attval++ = ' '+flag;
887         if(flag&NV_INTEGER)
888                 *attval = ' ' + nv_size(np);
889         else
890                 *attval = ' ';
891         attval = strcopy(++attval,nv_name(np));
892 }
893
894 static void pushnam __PARAM__((Namval_t *np), (np)) __OTORP__(Namval_t *np;){
895         register char *value;
896         if(nv_isattr(np,NV_IMPORT))
897         {
898                 if(np->nvenv)
899                         *argnam++ = np->nvenv;
900         }
901         else if(value=nv_getval(np))
902                 *argnam++ = staknam(np,value);
903         if(nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER))
904                 attsize += (strlen(nv_name(np))+4);
905 }
906
907 /*
908  * Generate the environment list for the child.
909  */
910
911 char **sh_envgen __PARAM__((void), ()){
912         register char **er;
913         register int namec;
914         register char *cp;
915         /* L_ARGNOD gets generated automatically as full path name of command */
916         nv_offattr(L_ARGNOD,NV_EXPORT);
917         attsize = 6;
918         namec = nv_scan(sh.var_tree,nullscan,NV_EXPORT,NV_EXPORT);
919         er = (char**)stakalloc((namec+3)*sizeof(char*));
920         argnam = ++er;
921         nv_scan(sh.var_tree, pushnam, NV_EXPORT, NV_EXPORT);
922         *argnam = (char*)stakalloc(attsize);
923         cp = attval = strcopy(*argnam,e_envmarker);
924         nv_scan(sh.var_tree, attstore,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER));
925         *attval = 0;
926         if(cp!=attval)
927                 argnam++;
928         *argnam = 0;
929         return(er);
930 }
931
932 static void (*scanfn) __PROTO__((Namval_t*));
933 static int scanmask;
934 static int scanflags;
935 static int scancount = 0;
936
937 static int scanfilter __PARAM__((const char *name, char *arg, __V_ *notused), (name, arg, notused)) __OTORP__(const char *name; char *arg; __V_ *notused;){
938         register Namval_t *np = (Namval_t*)arg;
939         register int k=np->nvflag;
940         NOT_USED(name);
941         NOT_USED(notused);
942         if(scanmask?(k&scanmask)==scanflags:(!scanflags || (k&scanflags)))
943         {
944                 if(!np->nvalue.cp && !nv_isattr(np,~NV_DEFAULT))
945                         return(0);
946                 if(scanfn)
947                 {
948                         if(nv_isattr(np,NV_ARRAY))
949                                 nv_putsub(np,NIL(char*),0L);
950                         (*scanfn)(np);
951                 }
952                 scancount++;
953         }
954         return(0);
955 }
956
957 /*
958  * Walk through the name-value pairs
959  * if <mask> is non-zero, then only nodes with (nvflags&mask)==flags
960  *      are visited
961  * If <mask> is zero, and <flags> non-zero, then nodes with one or
962  *      more of <flags> is visited
963  * If <mask> and <flags> are zero, then all nodes are visted
964  */
965 int nv_scan __PARAM__((Hashtab_t *root, void (*fn)(Namval_t*), int mask, int flags), (root, fn, mask, flags)) __OTORP__(Hashtab_t *root; void (*fn)(); int mask; int flags;){
966         int (*hashfn) __PROTO__((const char*, char*, __V_*));
967         scanmask = mask;
968         scanflags = flags;
969         scanfn = fn;
970         hashfn = scanfilter;
971         scancount = 0;
972         hashwalk(root, 0, hashfn,NIL(char*));
973         return(scancount);
974 }
975
976 /*
977  * create a new environment scope
978  */
979 void nv_scope __PARAM__((struct argnod *envlist), (envlist)) __OTORP__(struct argnod *envlist;){
980         register Hashtab_t *newscope;
981         newscope = hashalloc(sh.var_tree,HASH_set,HASH_SCOPE|HASH_ALLOCATE,0);
982         sh.var_tree = newscope;
983         nv_setlist(envlist,NV_EXPORT|NV_NOSCOPE|NV_IDENT|NV_ASSIGN);
984 }
985
986 /* 
987  * Remove freeable local space associated with the nvalue field
988  * of nnod. This includes any strings representing the value(s) of the
989  * node, as well as its dope vector, if it is an array.
990  */
991
992 void    sh_envnolocal  __PARAM__((register Namval_t *np), (np)) __OTORP__(register Namval_t *np;){
993         if(nv_isattr(np,NV_EXPORT|NV_NOFREE))
994         {
995                 if(nv_isattr(np,NV_REF))
996                 {
997                         nv_offattr(np,NV_NOFREE|NV_REF);
998                         np->nvalue.cp = 0;
999                 }
1000                 return;
1001         }
1002         if(nv_isattr(np, NV_ARRAY))
1003                 nv_putsub(np,NIL(char*),ARRAY_SCAN);
1004         forced = 1;
1005         nv_unset(np);
1006         forced = 0;
1007         nv_setattr(np,0);
1008 }
1009
1010 /*
1011  * Currently this is a dummy, but someday will be needed
1012  * for reference counting
1013  */
1014 void    nv_close __PARAM__((Namval_t *np), (np)) __OTORP__(Namval_t *np;){
1015         NOT_USED(np);
1016 }
1017
1018 /*
1019  *
1020  *   Set the value of <np> to 0, and nullify any attributes
1021  *   that <np> may have had.  Free any freeable space occupied
1022  *   by the value of <np>.  If <np> denotes an array member, it
1023  *   will retain its attributes.
1024  */
1025 void    nv_unset __PARAM__((register Namval_t *np), (np)) __OTORP__(register Namval_t *np;){
1026         register union Value *up = &np->nvalue;
1027         register Namarr_t *ap;
1028         if(!forced && nv_isattr (np,NV_RDONLY))
1029                 error(ERROR_exit(1),e_readonly, nv_name(np));
1030         if(sh.subshell && !nv_isnull(np))
1031                 np = sh_assignok(np,0);
1032         if(ap = nv_arrayptr(np))
1033                 array_check(np,ARRAY_DELETE);
1034         if(np->nvfun)
1035         {
1036                 /* This function contains disc */
1037                 if(!local)
1038                 {
1039                         local=1;
1040                         nv_putv(np,NIL(char*),forced?NV_RDONLY:0,np->nvfun);
1041                         return;
1042                 }
1043                 /* called from disc, assign the actual value */
1044                 local=0;
1045         }
1046         forced = 0;
1047         do
1048         {
1049                 if(ap)
1050                 {
1051                         if(!(up=array_find(np,ARRAY_DELETE)))
1052                                 return;
1053                 }
1054                 if((!nv_isattr (np, NV_NOFREE)) && up->cp)
1055                         free((__V_*)up->cp);
1056                 up->cp = 0;
1057         }
1058         while(ap?nv_nextsub(np):0);
1059         if(!nv_isattr(np,NV_ARRAY))
1060         {
1061                 nv_setattr(np,0);
1062                 nv_setsize(np,0);
1063                 np->nvenv = 0;
1064         }
1065         /* dtksh-specific code to sync hash table & environ */
1066         if(np->nvdtenv != (char *)NULL)
1067         {
1068                 remove_env_entry(np->nvdtenv);
1069                 free((__V_*)np->nvdtenv);
1070                 np->nvdtenv = (char *)NULL;
1071         }
1072 }
1073
1074 /*
1075  * return the node pointer in the highest level scope
1076  */
1077 Namval_t *nv_scoped __PARAM__((register Namval_t *np), (np)) __OTORP__(register Namval_t *np;){
1078         if(hashscope(sh.var_tree))
1079                 return(nv_search((char*)np,sh.var_tree,HASH_BUCKET));
1080         else
1081                 return(np);
1082 }
1083
1084 /*
1085  *   Return a pointer to a character string that denotes the value
1086  *   of <np>.  If <np> refers to an array,  return a pointer to
1087  *   the value associated with the current index.
1088  *
1089  *   If the value of <np> is an integer, the string returned will
1090  *   be overwritten by the next call to nv_getval.
1091  *
1092  *   If <np> has no value, 0 is returned.
1093  */
1094
1095 char *nv_getval __PARAM__((register Namval_t *np), (np)) __OTORP__(register Namval_t *np;){
1096         register union Value *up= &np->nvalue;
1097         register int numeric;
1098         register Namarr_t *ap;
1099         if(!np->nvfun && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF))
1100                 goto done;
1101         if(ap = nv_arrayptr(np))
1102                 array_check(np,ARRAY_LOOKUP);
1103         if(np->nvfun)
1104         {
1105                 if(!local)
1106                 {
1107                         local=1;
1108                         return(nv_getv(np, np->nvfun));
1109                 }
1110                 local=0;
1111         }
1112         numeric = ((nv_isattr (np, NV_INTEGER)) != 0);
1113         if(ap)
1114         {
1115                 if(!(up = array_find(np,ARRAY_LOOKUP)))
1116                         return (NIL(char*));
1117         }
1118         if(numeric)
1119         {
1120                 register long l;
1121                 if(!up->cp || nv_isattr (np,NV_CPOINTER))
1122                         return((char*)up->cp);
1123                 else if(nv_isattr (np,NV_DOUBLE))
1124                 {
1125                         double d = *up->dp;
1126                         if(nv_isattr (np,NV_EXPNOTE))
1127                                 return(sh_etos(d,nv_size(np)));
1128                         else
1129                                 return(sh_ftos(d,nv_size(np)));
1130                 }
1131                 else if(nv_isattr (np,NV_SHORT))
1132                         l = up->s;
1133                 else
1134                         l = *(up->lp);
1135                 if((numeric=nv_size(np))==10 && !nv_isattr(np,NV_UNSIGN))
1136                         numeric = 0;
1137                 return(fmtbase(l,numeric, numeric&&numeric!=10));
1138         }
1139         if(nv_isattr(np,NV_REF))
1140                 return(nv_name(up->np));
1141 done:
1142 #ifdef SHOPT_OO
1143         /* This is not nearly complete */
1144         if(!up->cp && (np=nv_class(np)))
1145                 return(nv_getval(np));
1146 #endif /* SHOPT_OO */
1147         return ((char*)up->cp);
1148 }
1149
1150 double nv_getnum __PARAM__((register Namval_t *np), (np)) __OTORP__(register Namval_t *np;){
1151         register union Value *up;
1152         register double r=0;
1153         register char *str;
1154         if(np->nvfun)
1155         {
1156                 if(!local)
1157                 {
1158                         local=1;
1159                         return(nv_getn(np, np->nvfun));
1160                 }
1161                 local=0;
1162         }
1163         if(nv_isattr (np, NV_INTEGER))
1164         {
1165                 if(nv_isattr(np, NV_ARRAY))
1166                         up = array_find(np,ARRAY_ASSIGN);
1167                 else
1168                         up= &np->nvalue;
1169                 if(!up->lp)
1170                         r = 0;
1171                 else if(nv_isattr(np, NV_DOUBLE))
1172                         r = *up->dp;
1173                 else
1174                         r = *up->lp;
1175         }
1176         else if((str=nv_getval(np)) && *str!=0)
1177                 r = sh_arith(str);
1178         return(r);
1179 }
1180 /*
1181  *   Give <np> the attributes <newatts,> and change its current
1182  *   value to conform to <newatts>.  The <size> of left and right
1183  *   justified fields may be given.
1184  */
1185 void nv_newattr  __PARAM__((register Namval_t *np, unsigned newatts, int size), (np, newatts, size)) __OTORP__(register Namval_t *np; unsigned newatts; int size;){
1186         register char *sp;
1187         register char *cp = 0;
1188         register unsigned int n;
1189         Namarr_t *ap = 0;
1190         int oldsize,oldatts;
1191
1192         /* check for restrictions */
1193         if(sh_isoption(SH_RESTRICTED) && ((sp=nv_name(np))==nv_name(PATHNOD) || sp==nv_name(SHELLNOD) || sp==nv_name(ENVNOD) ))
1194                 error(ERROR_exit(1),e_restricted,nv_name(np));
1195         /* handle attributes that do not change data separately */
1196         n = np->nvflag;
1197 #ifdef SHOPT_BSH
1198         if(newatts&NV_EXPORT)
1199                 nv_offattr(np,NV_IMPORT);
1200 #endif /* SHOPT_BSH */
1201 #ifdef apollo
1202         if(((n^newatts)&NV_EXPORT))
1203         /* record changes to the environment */
1204         {
1205                 short namlen = strlen(nv_name(np));
1206                 if(n&NV_EXPORT)
1207                         ev_$delete_var(nv_name(np),&namlen);
1208                 else
1209                 {
1210                         char *vp = nv_getval(np);
1211                         short vallen = strlen(vp);
1212                         ev_$set_var(nv_name(np),&namlen,vp,&vallen);
1213                 }
1214         }
1215 #endif /* apollo */
1216         /*
1217          * Set environment variable defined in the underlying
1218          * libc environ. This is done because routines within
1219          * a shared libc will only look in the libc environ,
1220          * not in our hash trees to find environment variables.
1221          */
1222         if(newatts&NV_EXPORT)
1223                 mirror_env(np);
1224
1225         if((size==0||(n&NV_INTEGER)) && ((n^newatts)&~NV_NOCHANGE)==0)
1226         {
1227                 if(size)
1228                         nv_setsize(np,size);
1229                 nv_offattr(np, ~NV_NOFREE);
1230                 nv_onattr(np, newatts);
1231                 return;
1232         }
1233         /* for an array, change all the elements */
1234         if((ap=nv_arrayptr(np)) && ap->nelem>0)
1235                 nv_putsub(np,NIL(char*),ARRAY_SCAN);
1236         oldsize = nv_size(np);
1237         oldatts = np->nvflag;
1238         do
1239         {
1240                 nv_setsize(np,oldsize);
1241                 np->nvflag = oldatts;
1242                 if (sp = nv_getval(np))
1243                 {
1244                         cp = (char*)malloc((n=strlen (sp)) + 1);
1245                         strcpy(cp, sp);
1246                         if(ap)  /* add element to prevent array deletion */
1247                         {
1248                                 ap->nelem &= ~ARRAY_SCAN;
1249                                 ap->nelem++;
1250                         }
1251                         nv_unset(np);
1252                         if(ap)
1253                         {
1254                                 ap->nelem--;
1255                                 ap->nelem |= ARRAY_SCAN;
1256                         }
1257                         if(size==0 && (newatts&(NV_LJUST|NV_RJUST|NV_ZFILL)))
1258                                 size = n;
1259                 }
1260                 else
1261                         nv_unset(np);
1262                 nv_setsize(np,size);
1263                 np->nvflag &= NV_ARRAY;
1264                 np->nvflag |= newatts;
1265                 if (cp)
1266                 {
1267                         nv_putval (np, cp, NV_RDONLY);
1268                         free(cp);
1269                 }
1270         }
1271         while(ap && nv_nextsub(np));
1272         return;
1273 }
1274
1275 #ifndef _NEXT_SOURCE
1276 static char *oldgetenv __PARAM__((const char *string), (string)) __OTORP__(const
1277  char *string;){
1278         register char c0,c1;
1279         register const char *cp, *sp;
1280         register char **av = environ;
1281         if(!string || (c0= *string)==0)
1282                 return(0);
1283         if((c1= *++string)==0)
1284                 c1= '=';
1285         while(cp = *av++)
1286         {
1287                 if(cp[0]!=c0 || cp[1]!=c1)
1288                         continue;
1289                 sp = string;
1290                 while(*sp && *sp++ == *++cp);
1291                 if(*sp==0 && *++cp=='=')
1292                         return((char*)(cp+1));
1293         }
1294         return(0);
1295 }
1296
1297 /*
1298  * This version of getenv the hash storage to access environment values
1299  *
1300  * This had to be renamed to ksh_getenv to allow the dtksh code access to
1301  * the libc getenv. While some platforms support calling _getenv to work
1302  * around the problems, others don't.
1303  */
1304 char *ksh_getenv __PARAM__((const char *name), (name)) __OTORP__(const char *name;){
1305         register Namval_t *np;
1306         if(!sh.var_tree)
1307                 return(oldgetenv(name));
1308         if((np = nv_search(name,sh.var_tree,0)) && nv_isattr(np,NV_EXPORT))
1309                 return(nv_getval(np));
1310         return(0);
1311 }
1312 #endif /* _NEXT_SOURCE */
1313
1314 #undef putenv
1315 /*
1316  * This version of putenv uses the hash storage to assign environment values
1317  *
1318  * The original ksh93 code had this routine called "putenv".  This hid
1319  * the libc version of putenv, which caused problems for dtksh on systems
1320  * with shared libraries, as it caused the existance of two separate and
1321  * non-overlapping environments.  To the best of my knowledge there are
1322  * no calls to this routine. - harry phinney  8/15/1994.
1323  */
1324 int ksh_putenv __PARAM__((const char *name), (name)) __OTORP__(const char *name;){
1325         register Namval_t *np;
1326         if(name)
1327         {
1328                 np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_ARRAY|NV_ASSIGN);
1329                 if(!strchr(name,'='))
1330                         nv_unset(np);
1331         }
1332         return(0);
1333 }
1334
1335
1336 /*
1337  * Override libast setenv()
1338  */
1339 char* setenviron __PARAM__((const char *name), (name)) __OTORP__(const char *name;){
1340         register Namval_t *np;
1341         if(name)
1342         {
1343                 np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_ARRAY|NV_ASSIGN);
1344                 if(strchr(name,'='))
1345                         return(nv_getval(np));
1346                 nv_unset(np);
1347         }
1348         return("");
1349 }
1350
1351 /*
1352  * copy <str1> to <str2> changing lower case to upper case
1353  * <str2> must be big enough to hold <str1>
1354  * <str1> and <str2> may point to the same place.
1355  */
1356
1357 static void ltou __PARAM__((register char const *str1,register char *str2), (str1, str2)) __OTORP__(register char const *str1;register char *str2;){
1358         register int c;
1359         for(; c= *((unsigned char*)str1); str1++,str2++)
1360         {
1361                 if(islower(c))
1362                         *str2 = toupper(c);
1363                 else
1364                         *str2 = c;
1365         }
1366         *str2 = 0;
1367 }
1368
1369 /*
1370  * copy <str1> to <str2> changing upper case to lower case
1371  * <str2> must be big enough to hold <str1>
1372  * <str1> and <str2> may point to the same place.
1373  */
1374
1375 static void utol __PARAM__((register char const *str1,register char *str2), (str1, str2)) __OTORP__(register char const *str1;register char *str2;){
1376         register int c;
1377         for(; c= *((unsigned char*)str1); str1++,str2++)
1378         {
1379                 if(isupper(c))
1380                         *str2 = tolower(c);
1381                 else
1382                         *str2 = c;
1383         }
1384         *str2 = 0;
1385 }
1386
1387 /*
1388  * Push or pop getval and putval functions to node <np>
1389  * <fp> is null to pop, otherwise, <fp> is pushed onto stack
1390  * The top of the stack is returned
1391  */
1392 Namfun_t *nv_stack __PARAM__((register Namval_t *np, register Namfun_t* fp), (np, fp)) __OTORP__(register Namval_t *np; register Namfun_t* fp;){
1393         register Namfun_t *lp;
1394         if(fp)
1395         {
1396                 if((lp=np->nvfun)==fp)
1397                         return(fp);
1398                 /* see if <fp> is on the list already and move to top */
1399                 if(lp) while(lp->next)
1400                 {
1401                         if(lp->next==fp)
1402                         {
1403                                 lp->next = fp->next;
1404                                 break;
1405                         }
1406                         lp = lp->next;
1407                 }
1408                 /* push */
1409                 fp->next = np->nvfun;
1410                 np->nvfun = fp;
1411         }
1412         else if(fp = np->nvfun)
1413                 np->nvfun = fp->next;
1414         return(fp);
1415 }
1416
1417 /*
1418  * add or replace built-in version of command commresponding to <path>
1419  * The <bltin> argument is a pointer to the built-in
1420  * Special builtins cannot be replaced and return -1
1421  */
1422 int     sh_addbuiltin __PARAM__((const char *path, int (*bltin)(int, char*[],__V_*),__V_ *extra), (path, bltin, extra)) __OTORP__(const char *path; int (*bltin)();__V_ *extra;){
1423         register const char *cp, *name = path_basename(path);
1424         register Namval_t *np, *nq=0;
1425         if(name==path && (cp=strchr(name,'.')) && cp!=name)
1426         {
1427                 int offset = staktell();
1428                 stakwrite(name,cp-name);
1429                 stakputc(0);
1430                 nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_NOARRAY);
1431                 offset = staktell();
1432                 stakputs(nv_name(nq));
1433                 stakputs(cp);
1434                 stakputc(0);
1435                 path = name = stakptr(offset);
1436         }
1437         np = nv_search(name,sh.bltin_tree,NV_ADD);
1438         if(!np || nv_isattr(np,BLT_SPC))
1439                 return(-1);
1440         if(np->nvenv && !nv_isattr(np,NV_NOFREE))
1441                 free((__V_*)np->nvenv);
1442         np->nvenv = 0;
1443         np->nvfun = 0;
1444         nv_setattr(np,0);
1445         if(bltin)
1446         {
1447                 np->nvalue.bfp = bltin;
1448                 nv_onattr(np,NV_BLTIN);
1449                 if(path!=name)
1450                         np->nvenv = strdup(path);
1451                 np->nvfun = (Namfun_t*)extra;
1452         }
1453         if(nq)
1454         {
1455                 cp=nv_setdisc(nq,cp+1,np,(Namfun_t*)nq);
1456                 nv_close(nq);
1457                 if(!cp)
1458                         error(ERROR_exit(1),e_baddisc,name);
1459         }
1460         return(0);
1461 }
1462
1463 static  int maxbufsize;
1464 static  char *curbuf;
1465
1466 /*
1467  *   Return a pointer to a string denoting the value of <val>
1468  *   with <places> places after the decimal point.  The string
1469  *   will be stored within static variable <numbuf>.
1470  */
1471 char *sh_ftos __PARAM__((double val,register int places), (val, places)) __OTORP__(double val;register int places;){
1472         register char *cp, *sp;
1473         int decpt,sign;
1474
1475         cp = sffcvt(val,places,&decpt,&sign);
1476         if(decpt>0)
1477                 places += decpt;
1478         places +=3;     /* room for sign, decimal point and null byte */
1479         if(!curbuf)
1480                 curbuf = (char*)malloc(maxbufsize=places);
1481         else if(places > maxbufsize)
1482                 curbuf = (char*)realloc(curbuf,maxbufsize=places);
1483         sp = curbuf;
1484         if(sign)
1485                 *sp++ = '-';
1486         while(decpt-- > 0)
1487                 *sp++ = *cp++;
1488         *sp++ = GETDECIMAL(0);
1489         while(++decpt < 0)
1490                 *sp++  = '0';
1491         while(*sp++ = *cp++);
1492         return(curbuf);
1493 }
1494
1495 /*
1496  * convert <val> to a string with <places> significant figures
1497  * The result is placed in a local buffer and a pointer returned
1498  */
1499 char *sh_etos __PARAM__((double val,register int places), (val, places)) __OTORP__(double val;register int places;){
1500         register int bufsize = places+8;
1501         if(!curbuf)
1502                 curbuf = (char*)malloc(maxbufsize=bufsize);
1503         else if(bufsize > maxbufsize)
1504                 curbuf = (char*)realloc(curbuf,maxbufsize=bufsize);
1505         sfsprintf(curbuf,bufsize,"%.*g",places,val);
1506         return(curbuf);
1507 }
1508
1509 /*
1510  * call the next getval function in the chain
1511  */
1512 char *nv_getv __PARAM__((Namval_t *np, register Namfun_t *nfp), (np, nfp)) __OTORP__(Namval_t *np; register Namfun_t *nfp;){
1513         register Namfun_t       *fp;
1514         register char *cp;
1515         if((fp = nfp) != NIL(Namfun_t*) && !local)
1516                 fp = nfp = nfp->next;
1517         local=0;
1518         while(fp && !fp->disc->getnum && !fp->disc->getval)
1519                 fp = fp->next;
1520         if(fp)
1521                 nfp = fp;
1522         if(nfp && nfp->disc->getval)
1523                 cp = (*nfp->disc->getval)(np,nfp);
1524         else if(nfp && nfp->disc->getnum)
1525                 cp = sh_etos((*nfp->disc->getnum)(np,nfp),12);
1526         else
1527         {
1528                 local=1;
1529                 cp = nv_getval(np);
1530         }
1531         return(cp);
1532 }
1533
1534 /*
1535  * call the next getnum function in the chain
1536  */
1537 double nv_getn __PARAM__((Namval_t *np, register Namfun_t *nfp), (np, nfp)) __OTORP__(Namval_t *np; register Namfun_t *nfp;){
1538         register Namfun_t       *fp;
1539         register double d=0;
1540         if((fp = nfp) != NIL(Namfun_t*) && !local)
1541                 fp = nfp = nfp->next;
1542         local=0;
1543         while(fp && !fp->disc->getnum && !fp->disc->getval)
1544                 fp = fp->next;
1545         if(fp)
1546                 nfp = fp;
1547         if(nfp && nfp->disc->getnum)
1548                 d = (*nfp->disc->getnum)(np,nfp);
1549         else if(nfp && nfp->disc->getval)
1550         {
1551                 char *str = (*nfp->disc->getval)(np,nfp);
1552                 if(str && *str)
1553                         d = sh_arith(str);
1554         }
1555         else
1556         {
1557                 local=1;
1558                 d = nv_getnum(np);
1559         }
1560         return(d);
1561 }
1562
1563 /*
1564  * call the next assign function in the chain
1565  */
1566 void nv_putv __PARAM__((Namval_t *np, const char *value, int flags, register Namfun_t *nfp), (np, value, flags, nfp)) __OTORP__(Namval_t *np; const char *value; int flags; register Namfun_t *nfp;){
1567         register Namfun_t       *fp;
1568         if((fp=nfp) != NIL(Namfun_t*) && !local)
1569                 fp = nfp = nfp->next;
1570         local=0;
1571         while(fp && !fp->disc->putval)
1572                 fp = fp->next;
1573         if(fp)
1574                 nfp = fp;
1575         if(nfp && nfp->disc->putval)
1576                 (*nfp->disc->putval)(np,value, flags, nfp);
1577         else
1578         {
1579                 local=1;
1580                 if(value)
1581                         nv_putval(np, value, flags);
1582                 else
1583                         nv_unset(np);
1584         }
1585 }
1586
1587 #define LOOKUP          0
1588 #define ASSIGN          1
1589 #define UNASSIGN        2
1590 #define BLOCKED         ((Namval_t*)&local)
1591
1592 struct  vardisc
1593 {
1594         Namfun_t        fun;
1595         Namval_t        *disc[3];
1596 };
1597
1598 /*
1599  * free discipline if no more discipline functions
1600  */
1601 static void chktfree __PARAM__((register Namval_t *np, register struct vardisc *vp), (np, vp)) __OTORP__(register Namval_t *np; register struct vardisc *vp;){
1602         register int n;
1603         for(n=0; n< sizeof(vp->disc)/sizeof(*vp->disc); n++)
1604         {
1605                 if(vp->disc[n])
1606                         break;
1607         }
1608         if(n>=sizeof(vp->disc)/sizeof(*vp->disc))
1609         {
1610                 /* no disc left so pop */
1611                 Namfun_t *fp;
1612                 if(fp=nv_stack(np, NIL(Namfun_t*)))
1613                         free((__V_*)fp);
1614         }
1615 }
1616
1617 /*
1618  * This function performs an assignment disc on the given node <np>
1619  */
1620 static void     assign __PARAM__((Namval_t *np,const char* val,int flags,Namfun_t *handle), (np, val, flags, handle)) __OTORP__(Namval_t *np;const char* val;int flags;Namfun_t *handle;){
1621         register struct vardisc *vp = (struct vardisc*)handle;
1622         register Namval_t *nq, **disc;
1623         if(val)
1624         {
1625                 if(!(nq=vp->disc[ASSIGN]))
1626                 {
1627                         nv_putv(np,val,flags,handle);
1628                         return;
1629                 }
1630                 nv_putval(SH_VALNOD, val, (flags&NV_INTEGER)?NV_NOFREE|NV_INTEGER|NV_DOUBLE|NV_EXPNOTE:NV_NOFREE);
1631         }
1632         disc = (val? &(vp->disc[ASSIGN]):&(vp->disc[UNASSIGN]));
1633         if((nq= *disc) && nq!=BLOCKED)
1634         {
1635                 *disc=BLOCKED;
1636                 sh_fun(nq,np);
1637                 if(*disc==BLOCKED)
1638                         *disc=nq;
1639                 else if(!*disc)
1640                         chktfree(np,vp);
1641         }
1642         if(val)
1643         {
1644                 register char *cp;
1645                 double d;
1646                 if(nv_isnull(SH_VALNOD))
1647                         cp=0;
1648                 else if(flags&NV_INTEGER)
1649                 {
1650                         d = nv_getnum(SH_VALNOD);
1651                         cp = (char*)(&d);
1652                 }
1653                 else
1654                         cp = nv_getval(SH_VALNOD);
1655                 if(cp)
1656                         nv_putv(np,cp,flags|NV_RDONLY,handle);
1657                 nv_unset(SH_VALNOD);
1658         }
1659         else if(!nq || nq==BLOCKED)
1660         {
1661                 if(vp->disc[ASSIGN])
1662                         nv_unset(vp->disc[ASSIGN]);
1663                 if(vp->disc[LOOKUP])
1664                         nv_unset(vp->disc[LOOKUP]);
1665                 if(vp->disc[UNASSIGN])
1666                         nv_unset(vp->disc[UNASSIGN]);
1667                 if(handle=nv_stack(np, NIL(Namfun_t*)))
1668                         free((__V_*)handle);
1669                 nv_unset(np);
1670         }
1671 }
1672
1673 /*
1674  * This function executes a lookup disc and then performs
1675  * the lookup on the given node <np>
1676  */
1677 static char*    lookup __PARAM__((Namval_t *np, Namfun_t *handle), (np, handle)) __OTORP__(Namval_t *np; Namfun_t *handle;){
1678         register struct vardisc *vp = (struct vardisc*)handle;
1679         register Namval_t *nq;
1680         register char *cp=0;
1681         if((nq=vp->disc[LOOKUP]) &&  nq!=BLOCKED)
1682         {
1683                 nv_unset(SH_VALNOD);
1684                 vp->disc[LOOKUP]=BLOCKED;
1685                 sh_fun(nq,np);
1686                 if(vp->disc[LOOKUP]==BLOCKED)
1687                         vp->disc[LOOKUP]=nq;
1688                 else if(!vp->disc[LOOKUP])
1689                         chktfree(np,vp);
1690                 cp = nv_getval(SH_VALNOD);
1691         }
1692         if(!cp)
1693                 cp = nv_getv(np,handle);
1694         return(cp);
1695 }
1696
1697 /*
1698  * node creation discipline
1699  */
1700 Namval_t *nv_create __PARAM__((register Namval_t* np,const char *name,register Namfun_t *fp), (np, name, fp)) __OTORP__(register Namval_t* np;const char *name;register Namfun_t *fp;){
1701         if(np == (Namval_t*)fp)
1702                 fp = np->nvfun;
1703         else if(fp)
1704                 fp = fp->next;
1705         while(fp && !fp->disc->create)
1706                 fp = fp->next;
1707         if(fp && fp->disc->create)
1708                 return((*fp->disc->create)(np,name,fp));
1709         return(NIL(Namval_t*));
1710 }
1711
1712 static const Namdisc_t shdisc =
1713 {
1714         sizeof(struct vardisc),
1715         assign,
1716         lookup
1717 };
1718
1719
1720 /*
1721  * Set disc on given <event> to <action>
1722  * If action==np, the current disc is returned
1723  * A null return value indicates that no <event> is known for <np>
1724  * If <event> is NULL, then return the event name after <action>
1725  * If <event> is NULL, and <action> is NULL, return the first event
1726  */
1727 char *nv_setdisc __PARAM__((register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp), (np, event, action, fp)) __OTORP__(register Namval_t* np;register const char *event;Namval_t *action;register Namfun_t *fp;){
1728         register struct vardisc *vp = (struct vardisc*)np->nvfun;
1729         register int type;
1730         if(np == (Namval_t*)fp)
1731         {
1732                 static const char *discnames[] = { "get", "set", "unset", 0 };
1733                 register const char *name;
1734                 register int getname=0;
1735                 /* top level call, check for get/set */
1736                 if(!event)
1737                 {
1738                         if(!action)
1739                                 return((char*)discnames[0]);
1740                         getname=1;
1741                         event = (char*)action;
1742                 }
1743                 for(type=0; name=discnames[type]; type++)
1744                 {
1745                         if(strcmp(event,name)==0)
1746                                 break;
1747                 }
1748                 if(getname)
1749                 {
1750                         event = 0;
1751                         if(name && !(name = discnames[++type]))
1752                                 action = 0;
1753                 }
1754                 if(!name)
1755                 {
1756                         if((fp=(Namfun_t*)vp) && fp->disc->setdisc)
1757                                 return((*fp->disc->setdisc)(np,event,action,fp));
1758                 }
1759                 else if(getname)
1760                         return((char*)name);
1761         }
1762         if(!fp)
1763                 return(NIL(char*));
1764         if(np != (Namval_t*)fp)
1765         {
1766                 /* not the top level */
1767                 while(fp = fp->next)
1768                 {
1769                         if(fp->disc->setdisc)
1770                                 return((*fp->disc->setdisc)(np,event,action,fp));
1771                 }
1772                 return(NIL(char*));
1773         }
1774         /* Handle GET/SET/UNSET disc */
1775         if(vp && vp->fun.disc->putval!=assign)
1776                 vp = 0;
1777         if(!vp)
1778         {
1779                 if(action==np)
1780                         return((char*)action);
1781                 if(!(vp = newof(NIL(struct vardisc*),struct vardisc,1,0)))
1782                         return(0);
1783                 vp->fun.disc = &shdisc;
1784                 nv_stack(np, (Namfun_t*)vp);
1785         }
1786         if(action==np)
1787                 action = vp->disc[type];
1788         else if(action)
1789                 vp->disc[type] = action;
1790         else
1791         {
1792                 action = vp->disc[type];
1793                 vp->disc[type] = 0;
1794                 if(action!=BLOCKED)
1795                         chktfree(np,vp);
1796         }
1797         return(action?(char*)action:"");
1798 }
1799
1800 /*
1801  * Create a reference node from <np>
1802  */
1803 void nv_setref __PARAM__((register Namval_t *np), (np)) __OTORP__(register Namval_t *np;){
1804         register Namval_t *nq, *nr;
1805         register char *cp;
1806         register int flags = NV_ARRAY|NV_VARNAME|NV_REF;
1807         Hashtab_t *hp=sh.var_tree;
1808         if(nv_isattr(np,NV_REF))
1809                 return;
1810         if(nv_isattr(np,NV_ARRAY))
1811                 error(ERROR_exit(1),e_badref,nv_name(np));
1812         if(!(cp=nv_getval(np)))
1813                 error(ERROR_exit(1),e_noref,nv_name(np));
1814         if(np->nvsize&NV_PARAM)
1815         {
1816                 if(!(hp=(Hashtab_t*)sh.st.par_tree))
1817                 {
1818                         if(!(hp=hashscope(sh.var_tree)))
1819                                 hp = sh.var_tree;
1820                 }
1821         }
1822         nr= nq = nv_open(cp, hp, NV_ARRAY|NV_VARNAME|NV_REF);
1823         while(nv_isattr(nr,NV_REF))
1824                 nr = nr->nvalue.np;
1825         if(nr==np) 
1826         {
1827                 if(!(hp=hashscope(hp)))
1828                         error(ERROR_exit(1),e_selfref,nv_name(np));
1829                 /* bind to earlier scope, or add to global scope */
1830                 nq=nv_search((char*)np,hp,NV_ADD|HASH_BUCKET);
1831         }
1832         nv_unset(np);
1833         np->nvalue.np = nq;
1834         nv_onattr(np,NV_REF|NV_NOFREE);
1835 }
1836
1837 /*
1838 * return the scope corresponding to <index>
1839 * if <index>==0, global scope returned
1840 * otherwise <index>th previous scope on function call stack returned
1841 * If <index> greater than or equal to number of scopes, NULL returned
1842 */
1843 Hashtab_t *nv_getscope(int index)
1844 {
1845         Hashtab_t *hp;
1846         struct sh_scoped *sp;
1847         if(index==0)    /* return global scope */
1848                 return((hp=hashscope(sh.var_tree))?hp:sh.var_tree);
1849         sp = &sh.st;
1850         while(sp && index-->0)
1851         {
1852                 if(--index==0)
1853                         return((Hashtab_t*)sp->par_tree);
1854                 sp = sp->prevst;
1855         }
1856         return(NIL(Hashtab_t*));
1857
1858 }