1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2012 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * David Korn <dgk@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * AT&T Labs
23 *
24 */
25
26 #include "defs.h"
27 #include "variables.h"
28 #include "builtins.h"
29 #include "path.h"
30
31 static void assign(Namval_t*,const char*,int,Namfun_t*);
32
nv_compare(Dt_t * dict,Void_t * sp,Void_t * dp,Dtdisc_t * disc)33 int nv_compare(Dt_t* dict, Void_t *sp, Void_t *dp, Dtdisc_t *disc)
34 {
35 if(sp==dp)
36 return(0);
37 return(strcmp((char*)sp,(char*)dp));
38 }
39
40 /*
41 * call the next getval function in the chain
42 */
nv_getv(Namval_t * np,register Namfun_t * nfp)43 char *nv_getv(Namval_t *np, register Namfun_t *nfp)
44 {
45 register Namfun_t *fp;
46 register char *cp;
47 if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
48 fp = nfp = nfp->next;
49 nv_local=0;
50 for(; fp; fp=fp->next)
51 {
52 if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
53 continue;
54 if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
55 break;
56 }
57 if(fp && fp->disc->getval)
58 cp = (*fp->disc->getval)(np,fp);
59 else if(fp && fp->disc->getnum)
60 {
61 sfprintf(sh.strbuf,"%.*Lg",12,(*fp->disc->getnum)(np,fp));
62 cp = sfstruse(sh.strbuf);
63 }
64 else
65 {
66 nv_local=1;
67 cp = nv_getval(np);
68 }
69 return(cp);
70 }
71
72 /*
73 * call the next getnum function in the chain
74 */
nv_getn(Namval_t * np,register Namfun_t * nfp)75 Sfdouble_t nv_getn(Namval_t *np, register Namfun_t *nfp)
76 {
77 register Namfun_t *fp;
78 register Sfdouble_t d=0;
79 Shell_t *shp = sh_getinterp();
80 char *str;
81 if((fp = nfp) != NIL(Namfun_t*) && !nv_local)
82 fp = nfp = nfp->next;
83 nv_local=0;
84 for(; fp; fp=fp->next)
85 {
86 if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
87 continue;
88 if(!fp->disc->getnum && nv_isattr(np,NV_INTEGER))
89 continue;
90 if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
91 break;
92 }
93 if(fp && fp->disc && fp->disc->getnum)
94 d = (*fp->disc->getnum)(np,fp);
95 else if(nv_isattr(np,NV_INTEGER))
96 {
97 nv_local = 1;
98 d = nv_getnum(np);
99 }
100 else
101 {
102 if(fp && fp->disc && fp->disc->getval)
103 str = (*fp->disc->getval)(np,fp);
104 else
105 str = nv_getv(np,fp?fp:nfp);
106 if(str && *str)
107 {
108 if(nv_isattr(np,NV_LJUST|NV_RJUST) || (*str=='0' && !(str[1]=='x'||str[1]=='X')))
109 {
110 while(*str=='0')
111 str++;
112 }
113 d = sh_arith(shp,str);
114 }
115 }
116 return(d);
117 }
118
119 /*
120 * call the next assign function in the chain
121 */
nv_putv(Namval_t * np,const char * value,int flags,register Namfun_t * nfp)122 void nv_putv(Namval_t *np, const char *value, int flags, register Namfun_t *nfp)
123 {
124 register Namfun_t *fp, *fpnext;
125 Namarr_t *ap;
126 if((fp=nfp) != NIL(Namfun_t*) && !nv_local)
127 fp = nfp = nfp->next;
128 nv_local=0;
129 if(flags&NV_NODISC)
130 fp = 0;
131 for(; fp; fp=fpnext)
132 {
133 fpnext = fp->next;
134 if(!fp->disc || !fp->disc->putval)
135 {
136 if(!value && (!(ap=nv_arrayptr(np)) || ap->nelem==0))
137 {
138 if(fp->disc || !(fp->nofree&1))
139 nv_disc(np,fp,NV_POP);
140 if(!(fp->nofree&1))
141 free((void*)fp);
142 }
143 continue;
144 }
145 if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np))
146 break;
147 }
148 if(!value && (flags&NV_TYPE) && fp && fp->disc->putval==assign)
149 fp = 0;
150 if(fp && fp->disc->putval)
151 (*fp->disc->putval)(np,value, flags, fp);
152 else
153 {
154 nv_local=1;
155 if(value)
156 nv_putval(np, value, flags);
157 else
158 _nv_unset(np, flags&(NV_RDONLY|NV_EXPORT));
159 }
160 }
161
162 #define LOOKUPS 0
163 #define ASSIGN 1
164 #define APPEND 2
165 #define UNASSIGN 3
166 #define LOOKUPN 4
167 #define BLOCKED ((Namval_t*)&nv_local)
168
169 struct vardisc
170 {
171 Namfun_t fun;
172 Namval_t *disc[5];
173 };
174
175 struct blocked
176 {
177 struct blocked *next;
178 Namval_t *np;
179 int flags;
180 void *sub;
181 int isub;
182 };
183
184 static struct blocked *blist;
185
186 #define isblocked(bp,type) ((bp)->flags & (1<<(type)))
187 #define block(bp,type) ((bp)->flags |= (1<<(type)))
188 #define unblock(bp,type) ((bp)->flags &= ~(1<<(type)))
189
190 /*
191 * returns pointer to blocking structure
192 */
block_info(Namval_t * np,struct blocked * pp)193 static struct blocked *block_info(Namval_t *np, struct blocked *pp)
194 {
195 register struct blocked *bp;
196 void *sub=0;
197 int isub=0;
198 if(nv_isarray(np) && (isub=nv_aindex(np)) < 0)
199 sub = nv_associative(np,(const char*)0,NV_ACURRENT);
200 for(bp=blist ; bp; bp=bp->next)
201 {
202 if(bp->np==np && bp->sub==sub && bp->isub==isub)
203 return(bp);
204 }
205 if(pp)
206 {
207 pp->np = np;
208 pp->flags = 0;
209 pp->isub = isub;
210 pp->sub = sub;
211 pp->next = blist;
212 blist = pp;
213 }
214 return(pp);
215 }
216
block_done(struct blocked * bp)217 static void block_done(struct blocked *bp)
218 {
219 blist = bp = bp->next;
220 if(bp && (bp->isub>=0 || bp->sub))
221 nv_putsub(bp->np, bp->sub,(bp->isub<0?0:bp->isub)|ARRAY_SETSUB);
222 }
223
224 /*
225 * free discipline if no more discipline functions
226 */
chktfree(register Namval_t * np,register struct vardisc * vp)227 static void chktfree(register Namval_t *np, register struct vardisc *vp)
228 {
229 register int n;
230 for(n=0; n< sizeof(vp->disc)/sizeof(*vp->disc); n++)
231 {
232 if(vp->disc[n])
233 break;
234 }
235 if(n>=sizeof(vp->disc)/sizeof(*vp->disc))
236 {
237 /* no disc left so pop */
238 Namfun_t *fp;
239 if((fp=nv_stack(np, NIL(Namfun_t*))) && !(fp->nofree&1))
240 free((void*)fp);
241 }
242 }
243
244 /*
245 * This function performs an assignment disc on the given node <np>
246 */
assign(Namval_t * np,const char * val,int flags,Namfun_t * handle)247 static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle)
248 {
249 int type = (flags&NV_APPEND)?APPEND:ASSIGN;
250 register struct vardisc *vp = (struct vardisc*)handle;
251 register Namval_t *nq = vp->disc[type];
252 struct blocked block, *bp = block_info(np, &block);
253 Namval_t node;
254 union Value *up = np->nvalue.up;
255 #if SHOPT_TYPEDEF
256 Namval_t *tp, *nr;
257 if(val && (tp=nv_type(np)) && (nr=nv_open(val,sh.var_tree,NV_VARNAME|NV_ARRAY|NV_NOADD|NV_NOFAIL)) && tp==nv_type(nr))
258 {
259 char *sub = nv_getsub(np);
260 _nv_unset(np,0);
261 if(sub)
262 {
263 nv_putsub(np, sub, ARRAY_ADD);
264 nv_putval(np,nv_getval(nr), 0);
265 }
266 else
267 nv_clone(nr,np,0);
268 goto done;
269 }
270 #endif /* SHOPT_TYPEDEF */
271 if(val || isblocked(bp,type))
272 {
273 if(!nq || isblocked(bp,type))
274 {
275 nv_putv(np,val,flags,handle);
276 goto done;
277 }
278 node = *SH_VALNOD;
279 if(!nv_isnull(SH_VALNOD))
280 {
281 nv_onattr(SH_VALNOD,NV_NOFREE);
282 _nv_unset(SH_VALNOD,0);
283 }
284 if(flags&NV_INTEGER)
285 nv_onattr(SH_VALNOD,(flags&(NV_LONG|NV_DOUBLE|NV_EXPNOTE|NV_HEXFLOAT|NV_SHORT)));
286 nv_putval(SH_VALNOD, val, (flags&NV_INTEGER)?flags:NV_NOFREE);
287 }
288 else
289 nq = vp->disc[type=UNASSIGN];
290 if(nq && !isblocked(bp,type))
291 {
292 int bflag=0;
293 block(bp,type);
294 if (type==APPEND && (bflag= !isblocked(bp,LOOKUPS)))
295 block(bp,LOOKUPS);
296 sh_fun(nq,np,(char**)0);
297 unblock(bp,type);
298 if(bflag)
299 unblock(bp,LOOKUPS);
300 if(!vp->disc[type])
301 chktfree(np,vp);
302 }
303 if(nv_isarray(np))
304 np->nvalue.up = up;
305 if(val)
306 {
307 register char *cp;
308 Sfdouble_t d;
309 if(nv_isnull(SH_VALNOD))
310 cp=0;
311 else if(flags&NV_INTEGER)
312 {
313 d = nv_getnum(SH_VALNOD);
314 cp = (char*)(&d);
315 flags |= (NV_LONG|NV_DOUBLE);
316 flags &= ~NV_SHORT;
317 }
318 else
319 cp = nv_getval(SH_VALNOD);
320 if(cp)
321 nv_putv(np,cp,flags|NV_RDONLY,handle);
322 _nv_unset(SH_VALNOD,0);
323 /* restore everything but the nvlink field */
324 memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink));
325 }
326 else if(sh_isstate(SH_INIT) || np==SH_FUNNAMENOD)
327 {
328 /* don't free functions during reinitialization */
329 nv_putv(np,val,flags,handle);
330 }
331 else if(!nq || !isblocked(bp,type))
332 {
333 Dt_t *root = sh_subfuntree(1);
334 int n;
335 Namarr_t *ap;
336 block(bp,type);
337 nv_disc(np,handle,NV_POP);
338 nv_putv(np, val, flags, handle);
339 if(sh.subshell)
340 goto done;
341 if(nv_isarray(np) && (ap=nv_arrayptr(np)) && ap->nelem>0)
342 goto done;
343 for(n=0; n < sizeof(vp->disc)/sizeof(*vp->disc); n++)
344 {
345 if((nq=vp->disc[n]) && !nv_isattr(nq,NV_NOFREE))
346 {
347 _nv_unset(nq,0);
348 dtdelete(root,nq);
349 }
350 }
351 unblock(bp,type);
352 if(!(handle->nofree&1))
353 free(handle);
354 }
355 done:
356 if(bp== &block)
357 block_done(bp);
358 if(nq && nq->nvalue.rp->running==1)
359 {
360 nq->nvalue.rp->running=0;
361 _nv_unset(nq,0);
362 }
363 }
364
365 /*
366 * This function executes a lookup disc and then performs
367 * the lookup on the given node <np>
368 */
lookup(Namval_t * np,int type,Sfdouble_t * dp,Namfun_t * handle)369 static char* lookup(Namval_t *np, int type, Sfdouble_t *dp,Namfun_t *handle)
370 {
371 register struct vardisc *vp = (struct vardisc*)handle;
372 struct blocked block, *bp = block_info(np, &block);
373 register Namval_t *nq = vp->disc[type];
374 register char *cp=0;
375 Namval_t node;
376 union Value *up = np->nvalue.up;
377 if(nq && !isblocked(bp,type))
378 {
379 node = *SH_VALNOD;
380 if(!nv_isnull(SH_VALNOD))
381 {
382 nv_onattr(SH_VALNOD,NV_NOFREE);
383 _nv_unset(SH_VALNOD,0);
384 }
385 if(type==LOOKUPN)
386 {
387 nv_onattr(SH_VALNOD,NV_DOUBLE|NV_INTEGER);
388 nv_setsize(SH_VALNOD,10);
389 }
390 block(bp,type);
391 sh_fun(nq,np,(char**)0);
392 unblock(bp,type);
393 if(!vp->disc[type])
394 chktfree(np,vp);
395 if(type==LOOKUPN)
396 {
397 cp = (char*)(SH_VALNOD->nvalue.cp);
398 *dp = nv_getnum(SH_VALNOD);
399 }
400 else if(cp = nv_getval(SH_VALNOD))
401 cp = stkcopy(stkstd,cp);
402 _nv_unset(SH_VALNOD,NV_RDONLY);
403 if(!nv_isnull(&node))
404 {
405 /* restore everything but the nvlink field */
406 memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink));
407 }
408 }
409 if(nv_isarray(np))
410 np->nvalue.up = up;
411 if(!cp)
412 {
413 if(type==LOOKUPS)
414 cp = nv_getv(np,handle);
415 else
416 *dp = nv_getn(np,handle);
417 }
418 if(bp== &block)
419 block_done(bp);
420 if(nq && nq->nvalue.rp->running==1)
421 {
422 nq->nvalue.rp->running=0;
423 _nv_unset(nq,0);
424 }
425 return(cp);
426 }
427
lookups(Namval_t * np,Namfun_t * handle)428 static char* lookups(Namval_t *np, Namfun_t *handle)
429 {
430 return(lookup(np,LOOKUPS,(Sfdouble_t*)0,handle));
431 }
432
lookupn(Namval_t * np,Namfun_t * handle)433 static Sfdouble_t lookupn(Namval_t *np, Namfun_t *handle)
434 {
435 Sfdouble_t d;
436 lookup(np,LOOKUPN, &d ,handle);
437 return(d);
438 }
439
440
441 /*
442 * Set disc on given <event> to <action>
443 * If action==np, the current disc is returned
444 * A null return value indicates that no <event> is known for <np>
445 * If <event> is NULL, then return the event name after <action>
446 * If <event> is NULL, and <action> is NULL, return the first event
447 */
nv_setdisc(register Namval_t * np,register const char * event,Namval_t * action,register Namfun_t * fp)448 char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
449 {
450 register struct vardisc *vp = (struct vardisc*)np->nvfun;
451 register int type;
452 char *empty = "";
453 while(vp)
454 {
455 if(vp->fun.disc && (vp->fun.disc->setdisc || vp->fun.disc->putval == assign))
456 break;
457 vp = (struct vardisc*)vp->fun.next;
458 }
459 if(vp && !vp->fun.disc)
460 vp = 0;
461 if(np == (Namval_t*)fp)
462 {
463 register const char *name;
464 register int getname=0;
465 /* top level call, check for get/set */
466 if(!event)
467 {
468 if(!action)
469 return((char*)nv_discnames[0]);
470 getname=1;
471 event = (char*)action;
472 }
473 for(type=0; name=nv_discnames[type]; type++)
474 {
475 if(strcmp(event,name)==0)
476 break;
477 }
478 if(getname)
479 {
480 event = 0;
481 if(name && !(name = nv_discnames[++type]))
482 action = 0;
483 }
484 if(!name)
485 {
486 for(fp=(Namfun_t*)vp; fp; fp=fp->next)
487 {
488 if(fp->disc && fp->disc->setdisc)
489 return((*fp->disc->setdisc)(np,event,action,fp));
490 }
491 }
492 else if(getname)
493 return((char*)name);
494 }
495 if(!fp)
496 return(NIL(char*));
497 if(np != (Namval_t*)fp)
498 {
499 /* not the top level */
500 while(fp = fp->next)
501 {
502 if(fp->disc && fp->disc->setdisc)
503 return((*fp->disc->setdisc)(np,event,action,fp));
504 }
505 return(NIL(char*));
506 }
507 /* Handle GET/SET/APPEND/UNSET disc */
508 if(vp && vp->fun.disc->putval!=assign)
509 vp = 0;
510 if(!vp)
511 {
512 Namdisc_t *dp;
513 if(action==np)
514 return((char*)action);
515 if(!(vp = newof(NIL(struct vardisc*),struct vardisc,1,sizeof(Namdisc_t))))
516 return(0);
517 dp = (Namdisc_t*)(vp+1);
518 vp->fun.disc = dp;
519 memset(dp,0,sizeof(*dp));
520 dp->dsize = sizeof(struct vardisc);
521 dp->putval = assign;
522 if(nv_isarray(np) && !nv_arrayptr(np))
523 nv_putsub(np,(char*)0, 1);
524 nv_stack(np, (Namfun_t*)vp);
525 }
526 if(action==np)
527 {
528 action = vp->disc[type];
529 empty = 0;
530 }
531 else if(action)
532 {
533 Namdisc_t *dp = (Namdisc_t*)vp->fun.disc;
534 if(type==LOOKUPS)
535 dp->getval = lookups;
536 else if(type==LOOKUPN)
537 dp->getnum = lookupn;
538 vp->disc[type] = action;
539 }
540 else
541 {
542 struct blocked *bp;
543 action = vp->disc[type];
544 vp->disc[type] = 0;
545 if(!(bp=block_info(np,(struct blocked*)0)) || !isblocked(bp,UNASSIGN))
546 chktfree(np,vp);
547 }
548 return(action?(char*)action:empty);
549 }
550
551 /*
552 * Set disc on given <event> to <action>
553 * If action==np, the current disc is returned
554 * A null return value indicates that no <event> is known for <np>
555 * If <event> is NULL, then return the event name after <action>
556 * If <event> is NULL, and <action> is NULL, return the first event
557 */
setdisc(register Namval_t * np,register const char * event,Namval_t * action,register Namfun_t * fp)558 static char *setdisc(register Namval_t* np,register const char *event,Namval_t *action,register Namfun_t *fp)
559 {
560 register Nambfun_t *vp = (Nambfun_t*)fp;
561 register int type,getname=0;
562 register const char *name;
563 const char **discnames = vp->bnames;
564 /* top level call, check for discipline match */
565 if(!event)
566 {
567 if(!action)
568 return((char*)discnames[0]);
569 getname=1;
570 event = (char*)action;
571 }
572 for(type=0; name=discnames[type]; type++)
573 {
574 if(strcmp(event,name)==0)
575 break;
576 }
577 if(getname)
578 {
579 event = 0;
580 if(name && !(name = discnames[++type]))
581 action = 0;
582 }
583 if(!name)
584 return(nv_setdisc(np,event,action,fp));
585 else if(getname)
586 return((char*)name);
587 /* Handle the disciplines */
588 if(action==np)
589 action = vp->bltins[type];
590 else if(action)
591 {
592 Namval_t *tp = nv_type(np);
593 if(tp && (np = (Namval_t*)vp->bltins[type]) && nv_isattr(np,NV_STATICF))
594 errormsg(SH_DICT,ERROR_exit(1),e_staticfun,name,tp->nvname);
595 vp->bltins[type] = action;
596 }
597 else
598 {
599 action = vp->bltins[type];
600 vp->bltins[type] = 0;
601 }
602 return((char*)action);
603 }
604
putdisc(Namval_t * np,const char * val,int flag,Namfun_t * fp)605 static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp)
606 {
607 nv_putv(np,val,flag,fp);
608 if(!val && !(flag&NV_NOFREE))
609 {
610 register Nambfun_t *vp = (Nambfun_t*)fp;
611 register int i;
612 for(i=0; vp->bnames[i]; i++)
613 {
614 register Namval_t *mp;
615 if((mp=vp->bltins[i]) && !nv_isattr(mp,NV_NOFREE))
616 {
617 if(is_abuiltin(mp))
618 {
619 if(mp->nvfun && !nv_isattr(mp,NV_NOFREE))
620 free((void*)mp->nvfun);
621 dtdelete(sh.bltin_tree,mp);
622 free((void*)mp);
623 }
624 }
625 }
626 nv_disc(np,fp,NV_POP);
627 if(!(fp->nofree&1))
628 free((void*)fp);
629
630 }
631 }
632
633 static const Namdisc_t Nv_bdisc = { 0, putdisc, 0, 0, setdisc };
634
nv_clone_disc(register Namfun_t * fp,int flags)635 Namfun_t *nv_clone_disc(register Namfun_t *fp, int flags)
636 {
637 register Namfun_t *nfp;
638 register int size;
639 if(!fp->disc && !fp->next && (fp->nofree&1))
640 return(fp);
641 if(!(size=fp->dsize) && (!fp->disc || !(size=fp->disc->dsize)))
642 size = sizeof(Namfun_t);
643 if(!(nfp=newof(NIL(Namfun_t*),Namfun_t,1,size-sizeof(Namfun_t))))
644 return(0);
645 memcpy(nfp,fp,size);
646 nfp->nofree &= ~1;
647 nfp->nofree |= (flags&NV_RDONLY)?1:0;
648 return(nfp);
649 }
650
nv_adddisc(Namval_t * np,const char ** names,Namval_t ** funs)651 int nv_adddisc(Namval_t *np, const char **names, Namval_t **funs)
652 {
653 register Nambfun_t *vp;
654 register int n=0;
655 register const char **av=names;
656 if(av)
657 {
658 while(*av++)
659 n++;
660 }
661 if(!(vp = newof(NIL(Nambfun_t*),Nambfun_t,1,n*sizeof(Namval_t*))))
662 return(0);
663 vp->fun.dsize = sizeof(Nambfun_t)+n*sizeof(Namval_t*);
664 vp->fun.nofree |= 2;
665 vp->num = n;
666 if(funs)
667 memcpy((void*)vp->bltins, (void*)funs,n*sizeof(Namval_t*));
668 else while(n>=0)
669 vp->bltins[n--] = 0;
670 vp->fun.disc = &Nv_bdisc;
671 vp->bnames = names;
672 nv_stack(np,&vp->fun);
673 return(1);
674 }
675
676 /*
677 * push, pop, clne, or reorder disciplines onto node <np>
678 * mode can be one of
679 * NV_FIRST: Move or push <fp> to top of the stack or delete top
680 * NV_LAST: Move or push <fp> to bottom of stack or delete last
681 * NV_POP: Delete <fp> from top of the stack
682 * NV_CLONE: Replace fp with a copy created my malloc() and return it
683 */
nv_disc(register Namval_t * np,register Namfun_t * fp,int mode)684 Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode)
685 {
686 Namfun_t *lp, **lpp;
687 if(nv_isref(np))
688 return(0);
689 if(mode==NV_CLONE && !fp)
690 return(0);
691 if(fp)
692 {
693 fp->subshell = sh.subshell;
694 if((lp=np->nvfun)==fp)
695 {
696 if(mode==NV_CLONE)
697 {
698 lp = nv_clone_disc(fp,0);
699 return(np->nvfun=lp);
700 }
701 if(mode==NV_FIRST || mode==0)
702 return(fp);
703 np->nvfun = lp->next;
704 if(mode==NV_POP)
705 return(fp);
706 if(mode==NV_LAST && (lp->next==0 || lp->next->disc==0))
707 return(fp);
708 }
709 /* see if <fp> is on the list already */
710 lpp = &np->nvfun;
711 if(lp)
712 {
713 while(lp->next && lp->next->disc)
714 {
715 if(lp->next==fp)
716 {
717 if(mode==NV_LAST && fp->next==0)
718 return(fp);
719 if(mode==NV_CLONE)
720 {
721 fp = nv_clone_disc(fp,0);
722 lp->next = fp;
723 return(fp);
724 }
725 lp->next = fp->next;
726 if(mode==NV_POP)
727 return(fp);
728 if(mode!=NV_LAST)
729 break;
730 }
731 lp = lp->next;
732 }
733 if(mode==NV_LAST && lp->disc)
734 lpp = &lp->next;
735 }
736 if(mode==NV_POP)
737 return(0);
738 /* push */
739 nv_offattr(np,NV_NODISC);
740 if(mode==NV_LAST)
741 {
742 if(lp && !lp->disc)
743 fp->next = lp;
744 else
745 fp->next = *lpp;
746 }
747 else
748 {
749 if((fp->nofree&1) && *lpp)
750 fp = nv_clone_disc(fp,0);
751 fp->next = *lpp;
752 }
753 *lpp = fp;
754 }
755 else
756 {
757 if(mode==NV_FIRST)
758 return(np->nvfun);
759 else if(mode==NV_LAST)
760 for(lp=np->nvfun; lp; fp=lp,lp=lp->next);
761 else if(fp = np->nvfun)
762 np->nvfun = fp->next;
763 }
764 return(fp);
765 }
766
767 /*
768 * returns discipline pointer if discipline with specified functions
769 * is on the discipline stack
770 */
nv_hasdisc(Namval_t * np,const Namdisc_t * dp)771 Namfun_t *nv_hasdisc(Namval_t *np, const Namdisc_t *dp)
772 {
773 register Namfun_t *fp;
774 for(fp=np->nvfun; fp; fp = fp->next)
775 {
776 if(fp->disc== dp)
777 return(fp);
778 }
779 return(0);
780 }
781
782 struct notify
783 {
784 Namfun_t hdr;
785 char **ptr;
786 };
787
put_notify(Namval_t * np,const char * val,int flags,Namfun_t * fp)788 static void put_notify(Namval_t* np,const char *val,int flags,Namfun_t *fp)
789 {
790 struct notify *pp = (struct notify*)fp;
791 nv_putv(np,val,flags,fp);
792 nv_stack(np,fp);
793 nv_stack(np,(Namfun_t*)0);
794 *pp->ptr = 0;
795 if(!(fp->nofree&1))
796 free((void*)fp);
797 }
798
799 static const Namdisc_t notify_disc = { 0, put_notify };
800
nv_unsetnotify(Namval_t * np,char ** addr)801 int nv_unsetnotify(Namval_t *np, char **addr)
802 {
803 register Namfun_t *fp;
804 for(fp=np->nvfun;fp;fp=fp->next)
805 {
806 if(fp->disc->putval==put_notify && ((struct notify*)fp)->ptr==addr)
807 {
808 nv_stack(np,fp);
809 nv_stack(np,(Namfun_t*)0);
810 if(!(fp->nofree&1))
811 free((void*)fp);
812 return(1);
813 }
814 }
815 return(0);
816 }
817
nv_setnotify(Namval_t * np,char ** addr)818 int nv_setnotify(Namval_t *np, char **addr)
819 {
820 struct notify *pp = newof(0,struct notify, 1,0);
821 if(!pp)
822 return(0);
823 pp->ptr = addr;
824 pp->hdr.disc = ¬ify_disc;
825 nv_stack(np,&pp->hdr);
826 return(1);
827 }
828
newnode(const char * name)829 static void *newnode(const char *name)
830 {
831 register int s;
832 register Namval_t *np = newof(0,Namval_t,1,s=strlen(name)+1);
833 if(np)
834 {
835 np->nvname = (char*)np+sizeof(Namval_t);
836 memcpy(np->nvname,name,s);
837 }
838 return((void*)np);
839 }
840
841 /*
842 * clone a numeric value
843 */
num_clone(register Namval_t * np,void * val)844 static void *num_clone(register Namval_t *np, void *val)
845 {
846 register int size;
847 void *nval;
848 if(!val)
849 return(0);
850 if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE)
851 {
852 if(nv_isattr(np,NV_LONG))
853 size = sizeof(Sfdouble_t);
854 else if(nv_isattr(np,NV_SHORT))
855 size = sizeof(float);
856 else
857 size = sizeof(double);
858 }
859 else
860 {
861 if(nv_isattr(np,NV_LONG))
862 size = sizeof(Sflong_t);
863 else if(nv_isattr(np,NV_SHORT))
864 {
865 if(nv_isattr(np,NV_INT16P)==NV_INT16P)
866 size = sizeof(short);
867 else
868 return((void*)np->nvalue.ip);
869 }
870 else
871 size = sizeof(int32_t);
872 }
873 if(!(nval = malloc(size)))
874 return(0);
875 memcpy(nval,val,size);
876 return(nval);
877 }
878
clone_all_disc(Namval_t * np,Namval_t * mp,int flags)879 void clone_all_disc( Namval_t *np, Namval_t *mp, int flags)
880 {
881 register Namfun_t *fp, **mfp = &mp->nvfun, *nfp, *fpnext;
882 for(fp=np->nvfun; fp;fp=fpnext)
883 {
884 fpnext = fp->next;
885 if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
886 return;
887 if((fp->nofree&2) && (flags&NV_NODISC))
888 nfp = 0;
889 if(fp->disc && fp->disc->clonef)
890 nfp = (*fp->disc->clonef)(np,mp,flags,fp);
891 else if(flags&NV_MOVE)
892 nfp = fp;
893 else
894 nfp = nv_clone_disc(fp,flags);
895 if(!nfp)
896 continue;
897 nfp->next = 0;
898 *mfp = nfp;
899 mfp = &nfp->next;
900 }
901 }
902
903 /*
904 * clone <mp> from <np> flags can be one of the following
905 * NV_APPEND - append <np> onto <mp>
906 * NV_MOVE - move <np> to <mp>
907 * NV_NOFREE - mark the new node as nofree
908 * NV_NODISC - discplines with funs non-zero will not be copied
909 * NV_COMVAR - cloning a compound variable
910 */
nv_clone(Namval_t * np,Namval_t * mp,int flags)911 int nv_clone(Namval_t *np, Namval_t *mp, int flags)
912 {
913 Namfun_t *fp, *fpnext;
914 const char *val = mp->nvalue.cp;
915 unsigned short flag = mp->nvflag;
916 unsigned short size = mp->nvsize;
917 for(fp=mp->nvfun; fp; fp=fpnext)
918 {
919 fpnext = fp->next;
920 if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef)
921 break;
922 if(!(fp->nofree&1))
923 free((void*)fp);
924 }
925 mp->nvfun = fp;
926 if(fp=np->nvfun)
927 {
928 if(nv_isattr(mp,NV_EXPORT|NV_MINIMAL) == (NV_EXPORT|NV_MINIMAL))
929 {
930 mp->nvenv = 0;
931 nv_offattr(mp,NV_MINIMAL);
932 }
933 if(!(flags&NV_COMVAR) && !nv_isattr(np,NV_MINIMAL) && np->nvenv && !(nv_isattr(mp,NV_MINIMAL)))
934 mp->nvenv = np->nvenv;
935 mp->nvflag &= NV_MINIMAL;
936 mp->nvflag |= np->nvflag&~(NV_ARRAY|NV_MINIMAL|NV_NOFREE);
937 flag = mp->nvflag;
938 clone_all_disc(np, mp, flags);
939 }
940 if(flags&NV_APPEND)
941 return(1);
942 if(mp->nvsize == size)
943 nv_setsize(mp,nv_size(np));
944 if(mp->nvflag == flag)
945 mp->nvflag = (np->nvflag&~(NV_MINIMAL))|(mp->nvflag&NV_MINIMAL);
946 if(nv_isattr(np,NV_EXPORT))
947 mp->nvflag |= (np->nvflag&NV_MINIMAL);
948 if(mp->nvalue.cp==val && !nv_isattr(np,NV_INTEGER))
949 {
950 if(np->nvalue.cp && np->nvalue.cp!=Empty && (flags&NV_COMVAR) && !(flags&NV_MOVE))
951 {
952 if(size)
953 mp->nvalue.cp = (char*)memdup(np->nvalue.cp,size);
954 else
955 mp->nvalue.cp = strdup(np->nvalue.cp);
956 nv_offattr(mp,NV_NOFREE);
957 }
958 else if((np->nvfun || !nv_isattr(np,NV_ARRAY)) && !(mp->nvalue.cp = np->nvalue.cp))
959 nv_offattr(mp,NV_NOFREE);
960 }
961 if(flags&NV_MOVE)
962 {
963 if(nv_isattr(np,NV_INTEGER))
964 mp->nvalue.ip = np->nvalue.ip;
965 np->nvfun = 0;
966 np->nvalue.cp = 0;
967 if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT))
968 {
969 mp->nvenv = np->nvenv;
970 if(nv_isattr(np,NV_MINIMAL))
971 {
972 np->nvenv = 0;
973 np->nvflag = NV_EXPORT;
974 }
975 else
976 np->nvflag = 0;
977 }
978 else
979 np->nvflag &= NV_MINIMAL;
980 nv_setsize(np,0);
981 return(1);
982 }
983 else if((flags&NV_ARRAY) && !nv_isattr(np,NV_MINIMAL))
984 mp->nvenv = np->nvenv;
985 if(nv_isattr(np,NV_INTEGER) && mp->nvalue.ip!=np->nvalue.ip && np->nvalue.cp!=Empty)
986 {
987 mp->nvalue.ip = (int*)num_clone(np,(void*)np->nvalue.ip);
988 nv_offattr(mp,NV_NOFREE);
989 }
990 else if((flags&NV_NOFREE) && !nv_arrayptr(np))
991 nv_onattr(np,NV_NOFREE);
992 return(1);
993 }
994
995 /*
996 * The following discipline is for copy-on-write semantics
997 */
clone_getv(Namval_t * np,Namfun_t * handle)998 static char* clone_getv(Namval_t *np, Namfun_t *handle)
999 {
1000 return(np->nvalue.np?nv_getval(np->nvalue.np):0);
1001 }
1002
clone_getn(Namval_t * np,Namfun_t * handle)1003 static Sfdouble_t clone_getn(Namval_t *np, Namfun_t *handle)
1004 {
1005 return(np->nvalue.np?nv_getnum(np->nvalue.np):0);
1006 }
1007
clone_putv(Namval_t * np,const char * val,int flags,Namfun_t * handle)1008 static void clone_putv(Namval_t *np,const char* val,int flags,Namfun_t *handle)
1009 {
1010 Namfun_t *dp = nv_stack(np,(Namfun_t*)0);
1011 Namval_t *mp = np->nvalue.np;
1012 if(!sh.subshell)
1013 free((void*)dp);
1014 if(val)
1015 nv_clone(mp,np,NV_NOFREE);
1016 np->nvalue.cp = 0;
1017 nv_putval(np,val,flags);
1018 }
1019
1020 static const Namdisc_t clone_disc =
1021 {
1022 0,
1023 clone_putv,
1024 clone_getv,
1025 clone_getn
1026 };
1027
nv_mkclone(Namval_t * mp)1028 Namval_t *nv_mkclone(Namval_t *mp)
1029 {
1030 Namval_t *np;
1031 Namfun_t *dp;
1032 np = newof(0,Namval_t,1,0);
1033 np->nvflag = mp->nvflag;
1034 np->nvsize = mp->nvsize;
1035 np->nvname = mp->nvname;
1036 np->nvalue.np = mp;
1037 np->nvflag = mp->nvflag;
1038 dp = newof(0,Namfun_t,1,0);
1039 dp->disc = &clone_disc;
1040 nv_stack(np,dp);
1041 dtinsert(nv_dict(sh.namespace),np);
1042 return(np);
1043 }
1044
nv_search(const char * name,Dt_t * root,int mode)1045 Namval_t *nv_search(const char *name, Dt_t *root, int mode)
1046 {
1047 register Namval_t *np;
1048 register Dt_t *dp = 0;
1049 if(mode&HASH_NOSCOPE)
1050 dp = dtview(root,0);
1051 if(mode&HASH_BUCKET)
1052 {
1053 Namval_t *mp = (void*)name;
1054 if(!(np = dtsearch(root,mp)) && (mode&NV_ADD))
1055 name = nv_name(mp);
1056 }
1057 else
1058 {
1059 if(*name=='.' && root==sh.var_tree && !dp)
1060 root = sh.var_base;
1061 np = dtmatch(root,(void*)name);
1062 }
1063 #if SHOPT_COSHELL
1064 if(sh.inpool)
1065 mode |= HASH_NOSCOPE;
1066 #endif /* SHOPT_COSHELL */
1067 if(!np && (mode&NV_ADD))
1068 {
1069 if(sh.namespace && !(mode&HASH_NOSCOPE) && root==sh.var_tree)
1070 root = nv_dict(sh.namespace);
1071 else if(!dp && !(mode&HASH_NOSCOPE))
1072 {
1073 register Dt_t *next;
1074 while(next=dtvnext(root))
1075 root = next;
1076 }
1077 np = (Namval_t*)dtinsert(root,newnode(name));
1078 }
1079 if(dp)
1080 dtview(root,dp);
1081 return(np);
1082 }
1083
1084 /*
1085 * finds function or builtin for given name and the discipline variable
1086 * if var!=0 the variable pointer is returned and the built-in name
1087 * is put onto the stack at the current offset.
1088 * otherwise, a pointer to the builtin (variable or type) is returned
1089 * and var contains the poiner to the variable
1090 * if last==0 and first component of name is a reference, nv_bfsearch()
1091 will return 0.
1092 */
nv_bfsearch(const char * name,Dt_t * root,Namval_t ** var,char ** last)1093 Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last)
1094 {
1095 Shell_t *shp = sh_getinterp();
1096 int c,offset = staktell();
1097 register char *sp, *cp=0;
1098 Namval_t *np, *nq;
1099 char *dname=0;
1100 if(var)
1101 *var = 0;
1102 /* check for . in the name before = */
1103 for(sp=(char*)name+1; *sp; sp++)
1104 {
1105 if(*sp=='=')
1106 return(0);
1107 if(*sp=='[')
1108 {
1109 while(*sp=='[')
1110 {
1111 sp = nv_endsubscript((Namval_t*)0,(char*)sp,0);
1112 if(sp[-1]!=']')
1113 return(0);
1114 }
1115 if(*sp==0)
1116 break;
1117 if(*sp!='.')
1118 return(0);
1119 cp = sp;
1120 }
1121 else if(*sp=='.')
1122 cp = sp;
1123 }
1124 if(!cp)
1125 return(var?nv_search(name,root,0):0);
1126 stakputs(name);
1127 stakputc(0);
1128 dname = cp+1;
1129 cp = stakptr(offset) + (cp-name);
1130 if(last)
1131 *last = cp;
1132 c = *cp;
1133 *cp = 0;
1134 nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_NOASSIGN|NV_NOADD|NV_NOFAIL);
1135 *cp = c;
1136 if(!nq)
1137 {
1138 np = 0;
1139 goto done;
1140 }
1141 if(!var)
1142 {
1143 np = nq;
1144 goto done;
1145 }
1146 *var = nq;
1147 if(c=='[')
1148 nv_endsubscript(nq, cp,NV_NOADD);
1149 stakseek(offset);
1150 #if SHOPT_NAMESPACE
1151 if(nv_istable(nq))
1152 {
1153 Namval_t *nsp = shp->namespace;
1154 if(last==0)
1155 return(nv_search(name,root,0));
1156 shp->namespace = 0;
1157 stakputs(nv_name(nq));
1158 shp->namespace = nsp;
1159 stakputs(dname-1);
1160 stakputc(0);
1161 np = nv_search(stakptr(offset),root,0);
1162 stakseek(offset);
1163 return(np);
1164 }
1165 #endif /* SHOPT_NAMESPACE */
1166 while(nv_isarray(nq) && !nv_isattr(nq,NV_MINIMAL|NV_EXPORT) && nq->nvenv && nv_isarray((Namval_t*)nq->nvenv))
1167 nq = (Namval_t*)nq->nvenv;
1168 return((Namval_t*)nv_setdisc(nq,dname,nq,(Namfun_t*)nq));
1169 done:
1170 stakseek(offset);
1171 return(np);
1172 }
1173
1174 /*
1175 * add or replace built-in version of command corresponding to <path>
1176 * The <bltin> argument is a pointer to the built-in
1177 * if <extra>==1, the built-in will be deleted
1178 * Special builtins cannot be added or deleted return failure
1179 * The return value for adding builtins is a pointer to the node or NULL on
1180 * failure. For delete NULL means success and the node that cannot be
1181 * deleted is returned on failure.
1182 */
sh_addbuiltin(const char * path,Shbltin_f bltin,void * extra)1183 Namval_t *sh_addbuiltin(const char *path, Shbltin_f bltin, void *extra)
1184 {
1185 register const char *name;
1186 char *cp;
1187 register Namval_t *np, *nq=0;
1188 int offset=staktell();
1189 if(extra==(void*)1)
1190 name = path;
1191 else if((name = path_basename(path))==path && bltin!=(Shbltin_f)SYSTYPESET->nvalue.bfp && (nq=nv_bfsearch(name,sh.bltin_tree,(Namval_t**)0,&cp)))
1192 path = name = stakptr(offset);
1193 else if(sh.bltin_dir && extra!=(void*)1)
1194 {
1195 stakputs(sh.bltin_dir);
1196 stakputc('/');
1197 stakputs(name);
1198 path = stakptr(offset);
1199 }
1200 if(np = nv_search(name,sh.bltin_tree,0))
1201 {
1202 /* exists without a path */
1203 stakseek(offset);
1204 if(extra == (void*)1)
1205 {
1206 if(np->nvfun && !nv_isattr(np,NV_NOFREE))
1207 free((void*)np->nvfun);
1208 dtdelete(sh.bltin_tree,np);
1209 return(0);
1210 }
1211 if(!bltin)
1212 return(np);
1213 }
1214 else for(np=(Namval_t*)dtfirst(sh.bltin_tree);np;np=(Namval_t*)dtnext(sh.bltin_tree,np))
1215 {
1216 if(strcmp(name,path_basename(nv_name(np))))
1217 continue;
1218 /* exists probably with different path so delete it */
1219 if(strcmp(path,nv_name(np)))
1220 {
1221 if(nv_isattr(np,BLT_SPC))
1222 return(np);
1223 if(!bltin)
1224 bltin = (Shbltin_f)np->nvalue.bfp;
1225 if(np->nvenv)
1226 dtdelete(sh.bltin_tree,np);
1227 if(extra == (void*)1)
1228 return(0);
1229 np = 0;
1230 }
1231 break;
1232 }
1233 if(!np && !(np = nv_search(path,sh.bltin_tree,bltin?NV_ADD:0)))
1234 return(0);
1235 stakseek(offset);
1236 if(nv_isattr(np,BLT_SPC))
1237 {
1238 if(extra)
1239 np->nvfun = (Namfun_t*)extra;
1240 return(np);
1241 }
1242 np->nvenv = 0;
1243 np->nvfun = 0;
1244 if(bltin)
1245 {
1246 np->nvalue.bfp = (Nambfp_f)bltin;
1247 nv_onattr(np,NV_BLTIN|NV_NOFREE);
1248 np->nvfun = (Namfun_t*)extra;
1249 }
1250 if(nq)
1251 {
1252 cp=nv_setdisc(nq,cp+1,np,(Namfun_t*)nq);
1253 nv_close(nq);
1254 if(!cp)
1255 errormsg(SH_DICT,ERROR_exit(1),e_baddisc,name);
1256 }
1257 if(extra == (void*)1)
1258 return(0);
1259 return(np);
1260 }
1261
1262 #undef nv_stack
nv_stack(register Namval_t * np,register Namfun_t * fp)1263 extern Namfun_t *nv_stack(register Namval_t *np, register Namfun_t* fp)
1264 {
1265 return(nv_disc(np,fp,0));
1266 }
1267
1268 struct table
1269 {
1270 Namfun_t fun;
1271 Namval_t *parent;
1272 Shell_t *shp;
1273 Dt_t *dict;
1274 };
1275
next_table(register Namval_t * np,Dt_t * root,Namfun_t * fp)1276 static Namval_t *next_table(register Namval_t* np, Dt_t *root,Namfun_t *fp)
1277 {
1278 struct table *tp = (struct table *)fp;
1279 if(root)
1280 return((Namval_t*)dtnext(root,np));
1281 else
1282 return((Namval_t*)dtfirst(tp->dict));
1283 }
1284
create_table(Namval_t * np,const char * name,int flags,Namfun_t * fp)1285 static Namval_t *create_table(Namval_t *np,const char *name,int flags,Namfun_t *fp)
1286 {
1287 struct table *tp = (struct table *)fp;
1288 tp->shp->last_table = np;
1289 return(nv_create(name, tp->dict, flags, fp));
1290 }
1291
clone_table(Namval_t * np,Namval_t * mp,int flags,Namfun_t * fp)1292 static Namfun_t *clone_table(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp)
1293 {
1294 struct table *tp = (struct table*)fp;
1295 struct table *ntp = (struct table*)nv_clone_disc(fp,0);
1296 Dt_t *oroot=tp->dict,*nroot=dtopen(&_Nvdisc,Dtoset);
1297 if(!nroot)
1298 return(0);
1299 memcpy((void*)ntp,(void*)fp,sizeof(struct table));
1300 ntp->dict = nroot;
1301 ntp->parent = nv_lastdict();
1302 for(np=(Namval_t*)dtfirst(oroot);np;np=(Namval_t*)dtnext(oroot,np))
1303 {
1304 mp = (Namval_t*)dtinsert(nroot,newnode(np->nvname));
1305 nv_clone(np,mp,flags);
1306 }
1307 return(&ntp->fun);
1308 }
1309
1310 struct adata
1311 {
1312 Shell_t *sh;
1313 Namval_t *tp;
1314 char *mapname;
1315 char **argnam;
1316 int attsize;
1317 char *attval;
1318 };
1319
delete_fun(Namval_t * np,void * data)1320 static void delete_fun(Namval_t *np, void *data)
1321 {
1322 Shell_t *shp = ((struct adata*)data)->sh;
1323 nv_delete(np,shp->fun_tree,NV_NOFREE);
1324 }
1325
put_table(register Namval_t * np,const char * val,int flags,Namfun_t * fp)1326 static void put_table(register Namval_t* np, const char* val, int flags, Namfun_t* fp)
1327 {
1328 register Dt_t *root = ((struct table*)fp)->dict;
1329 register Namval_t *nq, *mp;
1330 Namarr_t *ap;
1331 struct adata data;
1332 if(val)
1333 {
1334 nv_putv(np,val,flags,fp);
1335 return;
1336 }
1337 if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap))
1338 return;
1339 memset(&data,0,sizeof(data));
1340 data.mapname = nv_name(np);
1341 data.sh = ((struct table*)fp)->shp;
1342 nv_scan(data.sh->fun_tree,delete_fun,(void*)&data,NV_FUNCTION,NV_FUNCTION|NV_NOSCOPE);
1343 dtview(root,0);
1344 for(mp=(Namval_t*)dtfirst(root);mp;mp=nq)
1345 {
1346 _nv_unset(mp,flags);
1347 nq = (Namval_t*)dtnext(root,mp);
1348 dtdelete(root,mp);
1349 free((void*)mp);
1350 }
1351 dtclose(root);
1352 if(!(fp->nofree&1))
1353 free((void*)fp);
1354 np->nvfun = 0;
1355 }
1356
1357 /*
1358 * return space separated list of names of variables in given tree
1359 */
get_table(Namval_t * np,Namfun_t * fp)1360 static char *get_table(Namval_t *np, Namfun_t *fp)
1361 {
1362 register Dt_t *root = ((struct table*)fp)->dict;
1363 static Sfio_t *out;
1364 register int first=1;
1365 register Dt_t *base = dtview(root,0);
1366 if(out)
1367 sfseek(out,(Sfoff_t)0,SEEK_SET);
1368 else
1369 out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
1370 for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np))
1371 {
1372 if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))
1373 {
1374 if(!first)
1375 sfputc(out,' ');
1376 else
1377 first = 0;
1378 sfputr(out,np->nvname,-1);
1379 }
1380 }
1381 sfputc(out,0);
1382 if(base)
1383 dtview(root,base);
1384 return((char*)out->_data);
1385 }
1386
1387 static const Namdisc_t table_disc =
1388 {
1389 sizeof(struct table),
1390 put_table,
1391 get_table,
1392 0,
1393 0,
1394 create_table,
1395 clone_table,
1396 0,
1397 next_table,
1398 };
1399
nv_parent(Namval_t * np)1400 Namval_t *nv_parent(Namval_t *np)
1401 {
1402 struct table *tp = (struct table *)nv_hasdisc(np,&table_disc);
1403 if(tp)
1404 return(tp->parent);
1405 return(0);
1406 }
1407
nv_dict(Namval_t * np)1408 Dt_t *nv_dict(Namval_t* np)
1409 {
1410 Shell_t *shp=sh_getinterp();
1411 struct table *tp = (struct table*)nv_hasdisc(np,&table_disc);
1412 if(tp)
1413 return(tp->dict);
1414 np = shp->last_table;
1415 while(np)
1416 {
1417 if(tp = (struct table*)nv_hasdisc(np,&table_disc))
1418 return(tp->dict);
1419 #if 0
1420 np = nv_create(np,(const char*)0, NV_FIRST, (Namfun_t*)0);
1421 #else
1422 break;
1423 #endif
1424 }
1425 return(shp->var_tree);
1426 }
1427
nv_istable(Namval_t * np)1428 int nv_istable(Namval_t *np)
1429 {
1430 return(nv_hasdisc(np,&table_disc)!=0);
1431 }
1432
1433 /*
1434 * create a mountable name-value pair tree
1435 */
nv_mount(Namval_t * np,const char * name,Dt_t * dict)1436 Namval_t *nv_mount(Namval_t *np, const char *name, Dt_t *dict)
1437 {
1438 Namval_t *mp, *pp;
1439 struct table *tp;
1440 if(nv_hasdisc(np,&table_disc))
1441 pp = np;
1442 else
1443 pp = nv_lastdict();
1444 if(!(tp = newof((struct table*)0, struct table,1,0)))
1445 return(0);
1446 if(name)
1447 {
1448 Namfun_t *fp = pp->nvfun;
1449 mp = (*fp->disc->createf)(pp,name,0,fp);
1450 }
1451 else
1452 mp = np;
1453 nv_offattr(mp,NV_TABLE);
1454 if(!nv_isnull(mp))
1455 _nv_unset(mp,NV_RDONLY);
1456 tp->shp = sh_getinterp();
1457 tp->dict = dict;
1458 tp->parent = pp;
1459 tp->fun.disc = &table_disc;
1460 nv_disc(mp, &tp->fun, NV_FIRST);
1461 return(mp);
1462 }
1463
nv_discfun(int which)1464 const Namdisc_t *nv_discfun(int which)
1465 {
1466 switch(which)
1467 {
1468 case NV_DCADD:
1469 return(&Nv_bdisc);
1470 case NV_DCRESTRICT:
1471 return(&RESTRICTED_disc);
1472 }
1473 return(0);
1474 }
1475
nv_hasget(Namval_t * np)1476 int nv_hasget(Namval_t *np)
1477 {
1478 register Namfun_t *fp;
1479 for(fp=np->nvfun; fp; fp=fp->next)
1480 {
1481 if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval))
1482 continue;
1483 return(1);
1484 }
1485 return(0);
1486 }
1487
1488 #if SHOPT_NAMESPACE
sh_fsearch(Shell_t * shp,const char * fname,int add)1489 Namval_t *sh_fsearch(Shell_t *shp, const char *fname, int add)
1490 {
1491 Stk_t *stkp = shp->stk;
1492 int offset = stktell(stkp);
1493 sfputr(stkp,nv_name(shp->namespace),'.');
1494 sfputr(stkp,fname,0);
1495 fname = stkptr(stkp,offset);
1496 return(nv_search(fname,sh_subfuntree(add&NV_ADD),add));
1497 }
1498 #endif /* SHOPT_NAMESPACE */
1499