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