Rev 2714 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2714 | cejka | 1 | /* $NetBSD: var.c,v 1.26 2000/12/20 00:15:10 cgd Exp $ */ |
2 | |||
3 | /*- |
||
4 | * Copyright (c) 1991, 1993 |
||
5 | * The Regents of the University of California. All rights reserved. |
||
6 | * |
||
7 | * This code is derived from software contributed to Berkeley by |
||
8 | * Kenneth Almquist. |
||
9 | * |
||
10 | * Redistribution and use in source and binary forms, with or without |
||
11 | * modification, are permitted provided that the following conditions |
||
12 | * are met: |
||
13 | * 1. Redistributions of source code must retain the above copyright |
||
14 | * notice, this list of conditions and the following disclaimer. |
||
15 | * 2. Redistributions in binary form must reproduce the above copyright |
||
16 | * notice, this list of conditions and the following disclaimer in the |
||
17 | * documentation and/or other materials provided with the distribution. |
||
18 | * 3. All advertising materials mentioning features or use of this software |
||
19 | * must display the following acknowledgement: |
||
20 | * This product includes software developed by the University of |
||
21 | * California, Berkeley and its contributors. |
||
22 | * 4. Neither the name of the University nor the names of its contributors |
||
23 | * may be used to endorse or promote products derived from this software |
||
24 | * without specific prior written permission. |
||
25 | * |
||
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
36 | * SUCH DAMAGE. |
||
37 | */ |
||
38 | |||
39 | #include <sys/cdefs.h> |
||
40 | #ifndef lint |
||
41 | #if 0 |
||
42 | static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; |
||
43 | #else |
||
44 | __RCSID("$NetBSD: var.c,v 1.26 2000/12/20 00:15:10 cgd Exp $"); |
||
45 | #endif |
||
46 | #endif /* not lint */ |
||
47 | |||
48 | #include <unistd.h> |
||
49 | #include <stdlib.h> |
||
50 | #include <paths.h> |
||
51 | |||
52 | /* |
||
53 | * Shell variables. |
||
54 | */ |
||
55 | |||
56 | #include "shell.h" |
||
57 | #include "output.h" |
||
58 | #include "expand.h" |
||
59 | #include "nodes.h" /* for other headers */ |
||
60 | #include "eval.h" /* defines cmdenviron */ |
||
61 | #include "exec.h" |
||
62 | #include "syntax.h" |
||
63 | #include "options.h" |
||
64 | #include "mail.h" |
||
65 | #include "var.h" |
||
66 | #include "memalloc.h" |
||
67 | #include "error.h" |
||
68 | #include "mystring.h" |
||
69 | #include "parser.h" |
||
70 | #ifndef SMALL |
||
71 | #include "myhistedit.h" |
||
72 | #endif |
||
73 | |||
74 | |||
75 | #define VTABSIZE 39 |
||
76 | |||
77 | |||
78 | struct varinit { |
||
79 | struct var *var; |
||
80 | int flags; |
||
81 | const char *text; |
||
82 | void (*func) (const char *); |
||
83 | }; |
||
84 | |||
85 | |||
86 | #if ATTY |
||
87 | struct var vatty; |
||
88 | #endif |
||
89 | #ifndef SMALL |
||
90 | struct var vhistsize; |
||
91 | struct var vterm; |
||
92 | #endif |
||
93 | struct var vifs; |
||
94 | struct var vmail; |
||
95 | struct var vmpath; |
||
96 | struct var vpath; |
||
97 | struct var vps1; |
||
98 | struct var vps2; |
||
99 | struct var vvers; |
||
100 | struct var voptind; |
||
101 | |||
102 | const struct varinit varinit[] = { |
||
103 | #if ATTY |
||
104 | { &vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=", |
||
105 | NULL }, |
||
106 | #endif |
||
107 | #ifndef SMALL |
||
108 | { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", |
||
109 | sethistsize }, |
||
110 | #endif |
||
111 | { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n", |
||
112 | NULL }, |
||
113 | { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", |
||
114 | NULL }, |
||
115 | { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", |
||
116 | NULL }, |
||
117 | { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", |
||
118 | changepath }, |
||
119 | /* |
||
120 | * vps1 depends on uid |
||
121 | */ |
||
122 | { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", |
||
123 | NULL }, |
||
124 | #ifndef SMALL |
||
125 | { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=", |
||
126 | setterm }, |
||
127 | #endif |
||
128 | { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", |
||
129 | getoptsreset }, |
||
130 | { NULL, 0, NULL, |
||
131 | NULL } |
||
132 | }; |
||
133 | |||
134 | struct var *vartab[VTABSIZE]; |
||
135 | |||
136 | STATIC struct var **hashvar (const char *); |
||
137 | STATIC int varequal (const char *, const char *); |
||
138 | |||
139 | /* |
||
140 | * Initialize the varable symbol tables and import the environment |
||
141 | * Setting PWD added by herbert |
||
142 | */ |
||
143 | |||
144 | #ifdef mkinit |
||
145 | INCLUDE "cd.h" |
||
146 | INCLUDE "var.h" |
||
147 | INIT { |
||
148 | char **envp; |
||
149 | extern char **environ; |
||
150 | extern char *curdir; |
||
151 | |||
152 | initvar(); |
||
153 | for (envp = environ ; *envp ; envp++) { |
||
154 | if (strchr(*envp, '=')) { |
||
155 | setvareq(*envp, VEXPORT|VTEXTFIXED); |
||
156 | } |
||
157 | } |
||
158 | |||
159 | getpwd(); |
||
160 | setvar("PWD", curdir, VEXPORT|VTEXTFIXED); |
||
161 | } |
||
162 | #endif |
||
163 | |||
164 | |||
165 | /* |
||
166 | * This routine initializes the builtin variables. It is called when the |
||
167 | * shell is initialized and again when a shell procedure is spawned. |
||
168 | */ |
||
169 | |||
170 | void |
||
171 | initvar() { |
||
172 | const struct varinit *ip; |
||
173 | struct var *vp; |
||
174 | struct var **vpp; |
||
175 | char ppid[30]; |
||
176 | |||
177 | for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { |
||
178 | if ((vp->flags & VEXPORT) == 0) { |
||
179 | vpp = hashvar(ip->text); |
||
180 | vp->next = *vpp; |
||
181 | *vpp = vp; |
||
182 | vp->text = strdup(ip->text); |
||
183 | vp->flags = ip->flags; |
||
184 | vp->func = ip->func; |
||
185 | } |
||
186 | } |
||
187 | /* |
||
188 | * PS1 depends on uid |
||
189 | */ |
||
190 | if ((vps1.flags & VEXPORT) == 0) { |
||
191 | vpp = hashvar("PS1="); |
||
192 | vps1.next = *vpp; |
||
193 | *vpp = &vps1; |
||
194 | vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# "); |
||
195 | vps1.flags = VSTRFIXED|VTEXTFIXED; |
||
196 | } |
||
197 | |||
198 | snprintf(ppid, 29, "%ld", (long)getppid()); |
||
199 | setvar("PPID", ppid, VREADONLY|VNOFUNC); |
||
200 | } |
||
201 | |||
202 | /* |
||
203 | * Safe version of setvar, returns 1 on success 0 on failure. |
||
204 | */ |
||
205 | |||
206 | int |
||
207 | setvarsafe(name, val, flags) |
||
208 | const char *name, *val; |
||
209 | int flags; |
||
210 | { |
||
211 | struct jmploc jmploc; |
||
212 | struct jmploc *volatile savehandler = handler; |
||
213 | int err = 0; |
||
214 | #ifdef __GNUC__ |
||
215 | (void) &err; |
||
216 | #endif |
||
217 | |||
218 | if (setjmp(jmploc.loc)) |
||
219 | err = 1; |
||
220 | else { |
||
221 | handler = &jmploc; |
||
222 | setvar(name, val, flags); |
||
223 | } |
||
224 | handler = savehandler; |
||
225 | return err; |
||
226 | } |
||
227 | |||
228 | /* |
||
229 | * Set the value of a variable. The flags argument is ored with the |
||
230 | * flags of the variable. If val is NULL, the variable is unset. |
||
231 | */ |
||
232 | |||
233 | void |
||
234 | setvar(name, val, flags) |
||
235 | const char *name, *val; |
||
236 | int flags; |
||
237 | { |
||
238 | const char *p; |
||
239 | const char *q; |
||
240 | char *d; |
||
241 | int len; |
||
242 | int namelen; |
||
243 | char *nameeq; |
||
244 | int isbad; |
||
245 | |||
246 | isbad = 0; |
||
247 | p = name; |
||
248 | if (! is_name(*p)) |
||
249 | isbad = 1; |
||
250 | p++; |
||
251 | for (;;) { |
||
252 | if (! is_in_name(*p)) { |
||
253 | if (*p == '\0' || *p == '=') |
||
254 | break; |
||
255 | isbad = 1; |
||
256 | } |
||
257 | p++; |
||
258 | } |
||
259 | namelen = p - name; |
||
260 | if (isbad) |
||
261 | error("%.*s: bad variable name", namelen, name); |
||
262 | len = namelen + 2; /* 2 is space for '=' and '\0' */ |
||
263 | if (val == NULL) { |
||
264 | flags |= VUNSET; |
||
265 | } else { |
||
266 | len += strlen(val); |
||
267 | } |
||
268 | d = nameeq = ckmalloc(len); |
||
269 | q = name; |
||
270 | while (--namelen >= 0) |
||
271 | *d++ = *q++; |
||
272 | *d++ = '='; |
||
273 | *d = '\0'; |
||
274 | if (val) |
||
275 | scopy(val, d); |
||
276 | setvareq(nameeq, flags); |
||
277 | } |
||
278 | |||
279 | |||
280 | |||
281 | /* |
||
282 | * Same as setvar except that the variable and value are passed in |
||
283 | * the first argument as name=value. Since the first argument will |
||
284 | * be actually stored in the table, it should not be a string that |
||
285 | * will go away. |
||
286 | */ |
||
287 | |||
288 | void |
||
289 | setvareq(s, flags) |
||
290 | char *s; |
||
291 | int flags; |
||
292 | { |
||
293 | struct var *vp, **vpp; |
||
294 | |||
295 | vpp = hashvar(s); |
||
296 | flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); |
||
297 | for (vp = *vpp ; vp ; vp = vp->next) { |
||
298 | if (varequal(s, vp->text)) { |
||
299 | if (vp->flags & VREADONLY) { |
||
300 | size_t len = strchr(s, '=') - s; |
||
301 | error("%.*s: is read only", len, s); |
||
302 | } |
||
303 | INTOFF; |
||
304 | |||
305 | if (vp->func && (flags & VNOFUNC) == 0) |
||
306 | (*vp->func)(strchr(s, '=') + 1); |
||
307 | |||
308 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) |
||
309 | ckfree(vp->text); |
||
310 | |||
311 | vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); |
||
312 | vp->flags |= flags; |
||
313 | vp->text = s; |
||
314 | |||
315 | /* |
||
316 | * We could roll this to a function, to handle it as |
||
317 | * a regular variable function callback, but why bother? |
||
318 | */ |
||
319 | if (iflag && |
||
320 | (vp == &vmpath || (vp == &vmail && ! mpathset()))) |
||
321 | chkmail(1); |
||
322 | INTON; |
||
323 | return; |
||
324 | } |
||
325 | } |
||
326 | /* not found */ |
||
327 | vp = ckmalloc(sizeof (*vp)); |
||
328 | vp->flags = flags; |
||
329 | vp->text = s; |
||
330 | vp->next = *vpp; |
||
331 | vp->func = NULL; |
||
332 | *vpp = vp; |
||
333 | } |
||
334 | |||
335 | |||
336 | |||
337 | /* |
||
338 | * Process a linked list of variable assignments. |
||
339 | */ |
||
340 | |||
341 | void |
||
342 | listsetvar(list) |
||
343 | struct strlist *list; |
||
344 | { |
||
345 | struct strlist *lp; |
||
346 | |||
347 | INTOFF; |
||
348 | for (lp = list ; lp ; lp = lp->next) { |
||
349 | setvareq(savestr(lp->text), 0); |
||
350 | } |
||
351 | INTON; |
||
352 | } |
||
353 | |||
354 | |||
355 | |||
356 | /* |
||
357 | * Find the value of a variable. Returns NULL if not set. |
||
358 | */ |
||
359 | |||
360 | char * |
||
361 | lookupvar(name) |
||
362 | const char *name; |
||
363 | { |
||
364 | struct var *v; |
||
365 | |||
366 | for (v = *hashvar(name) ; v ; v = v->next) { |
||
367 | if (varequal(v->text, name)) { |
||
368 | if (v->flags & VUNSET) |
||
369 | return NULL; |
||
370 | return strchr(v->text, '=') + 1; |
||
371 | } |
||
372 | } |
||
373 | return NULL; |
||
374 | } |
||
375 | |||
376 | |||
377 | |||
378 | /* |
||
379 | * Search the environment of a builtin command. If the second argument |
||
380 | * is nonzero, return the value of a variable even if it hasn't been |
||
381 | * exported. |
||
382 | */ |
||
383 | |||
384 | char * |
||
385 | bltinlookup(name, doall) |
||
386 | const char *name; |
||
387 | int doall; |
||
388 | { |
||
389 | struct strlist *sp; |
||
390 | struct var *v; |
||
391 | |||
392 | for (sp = cmdenviron ; sp ; sp = sp->next) { |
||
393 | if (varequal(sp->text, name)) |
||
394 | return strchr(sp->text, '=') + 1; |
||
395 | } |
||
396 | for (v = *hashvar(name) ; v ; v = v->next) { |
||
397 | if (varequal(v->text, name)) { |
||
398 | if ((v->flags & VUNSET) |
||
399 | || (!doall && (v->flags & VEXPORT) == 0)) |
||
400 | return NULL; |
||
401 | return strchr(v->text, '=') + 1; |
||
402 | } |
||
403 | } |
||
404 | return NULL; |
||
405 | } |
||
406 | |||
407 | |||
408 | |||
409 | /* |
||
410 | * Generate a list of exported variables. This routine is used to construct |
||
411 | * the third argument to execve when executing a program. |
||
412 | */ |
||
413 | |||
414 | char ** |
||
415 | environment() { |
||
416 | int nenv; |
||
417 | struct var **vpp; |
||
418 | struct var *vp; |
||
419 | char **env; |
||
420 | char **ep; |
||
421 | |||
422 | nenv = 0; |
||
423 | for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { |
||
424 | for (vp = *vpp ; vp ; vp = vp->next) |
||
425 | if (vp->flags & VEXPORT) |
||
426 | nenv++; |
||
427 | } |
||
428 | ep = env = stalloc((nenv + 1) * sizeof *env); |
||
429 | for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { |
||
430 | for (vp = *vpp ; vp ; vp = vp->next) |
||
431 | if (vp->flags & VEXPORT) |
||
432 | *ep++ = vp->text; |
||
433 | } |
||
434 | *ep = NULL; |
||
435 | return env; |
||
436 | } |
||
437 | |||
438 | |||
439 | /* |
||
440 | * Called when a shell procedure is invoked to clear out nonexported |
||
441 | * variables. It is also necessary to reallocate variables of with |
||
442 | * VSTACK set since these are currently allocated on the stack. |
||
443 | */ |
||
444 | |||
445 | #ifdef mkinit |
||
446 | MKINIT void shprocvar (void); |
||
447 | |||
448 | SHELLPROC { |
||
449 | shprocvar(); |
||
450 | } |
||
451 | #endif |
||
452 | |||
453 | void |
||
454 | shprocvar() { |
||
455 | struct var **vpp; |
||
456 | struct var *vp, **prev; |
||
457 | |||
458 | for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { |
||
459 | for (prev = vpp ; (vp = *prev) != NULL ; ) { |
||
460 | if ((vp->flags & VEXPORT) == 0) { |
||
461 | *prev = vp->next; |
||
462 | if ((vp->flags & VTEXTFIXED) == 0) |
||
463 | ckfree(vp->text); |
||
464 | if ((vp->flags & VSTRFIXED) == 0) |
||
465 | ckfree(vp); |
||
466 | } else { |
||
467 | if (vp->flags & VSTACK) { |
||
468 | vp->text = savestr(vp->text); |
||
469 | vp->flags &=~ VSTACK; |
||
470 | } |
||
471 | prev = &vp->next; |
||
472 | } |
||
473 | } |
||
474 | } |
||
475 | initvar(); |
||
476 | } |
||
477 | |||
478 | |||
479 | |||
480 | /* |
||
481 | * Command to list all variables which are set. Currently this command |
||
482 | * is invoked from the set command when the set command is called without |
||
483 | * any variables. |
||
484 | */ |
||
485 | |||
486 | int |
||
487 | showvarscmd(argc, argv) |
||
488 | int argc; |
||
489 | char **argv; |
||
490 | { |
||
491 | struct var **vpp; |
||
492 | struct var *vp; |
||
493 | |||
494 | for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { |
||
495 | for (vp = *vpp ; vp ; vp = vp->next) { |
||
496 | if ((vp->flags & VUNSET) == 0) |
||
497 | out1fmt("%s\n", vp->text); |
||
498 | } |
||
499 | } |
||
500 | return 0; |
||
501 | } |
||
502 | |||
503 | |||
504 | |||
505 | /* |
||
506 | * The export and readonly commands. |
||
507 | */ |
||
508 | |||
509 | int |
||
510 | exportcmd(argc, argv) |
||
511 | int argc; |
||
512 | char **argv; |
||
513 | { |
||
514 | struct var **vpp; |
||
515 | struct var *vp; |
||
516 | char *name; |
||
517 | const char *p; |
||
518 | int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; |
||
519 | int pflag; |
||
520 | |||
521 | listsetvar(cmdenviron); |
||
522 | pflag = (nextopt("p") == 'p'); |
||
523 | if (argc > 1 && !pflag) { |
||
524 | while ((name = *argptr++) != NULL) { |
||
525 | if ((p = strchr(name, '=')) != NULL) { |
||
526 | p++; |
||
527 | } else { |
||
528 | vpp = hashvar(name); |
||
529 | for (vp = *vpp ; vp ; vp = vp->next) { |
||
530 | if (varequal(vp->text, name)) { |
||
531 | vp->flags |= flag; |
||
532 | goto found; |
||
533 | } |
||
534 | } |
||
535 | } |
||
536 | setvar(name, p, flag); |
||
537 | found:; |
||
538 | } |
||
539 | } else { |
||
540 | for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { |
||
541 | for (vp = *vpp ; vp ; vp = vp->next) { |
||
542 | if ((vp->flags & flag) == 0) |
||
543 | continue; |
||
544 | if (pflag) { |
||
545 | out1fmt("%s %s\n", argv[0], vp->text); |
||
546 | } else { |
||
547 | for (p = vp->text ; *p != '=' ; p++) |
||
548 | out1c(*p); |
||
549 | out1c('\n'); |
||
550 | } |
||
551 | } |
||
552 | } |
||
553 | } |
||
554 | return 0; |
||
555 | } |
||
556 | |||
557 | |||
558 | /* |
||
559 | * The "local" command. |
||
560 | */ |
||
561 | |||
562 | int |
||
563 | localcmd(argc, argv) |
||
564 | int argc; |
||
565 | char **argv; |
||
566 | { |
||
567 | char *name; |
||
568 | |||
569 | if (! in_function()) |
||
570 | error("Not in a function"); |
||
571 | while ((name = *argptr++) != NULL) { |
||
572 | mklocal(name); |
||
573 | } |
||
574 | return 0; |
||
575 | } |
||
576 | |||
577 | |||
578 | /* |
||
579 | * Make a variable a local variable. When a variable is made local, it's |
||
580 | * value and flags are saved in a localvar structure. The saved values |
||
581 | * will be restored when the shell function returns. We handle the name |
||
582 | * "-" as a special case. |
||
583 | */ |
||
584 | |||
585 | void |
||
586 | mklocal(name) |
||
587 | char *name; |
||
588 | { |
||
589 | struct localvar *lvp; |
||
590 | struct var **vpp; |
||
591 | struct var *vp; |
||
592 | |||
593 | INTOFF; |
||
594 | lvp = ckmalloc(sizeof (struct localvar)); |
||
595 | if (name[0] == '-' && name[1] == '\0') { |
||
596 | char *p; |
||
597 | p = ckmalloc(sizeof optlist); |
||
598 | lvp->text = memcpy(p, optlist, sizeof optlist); |
||
599 | vp = NULL; |
||
600 | } else { |
||
601 | vpp = hashvar(name); |
||
602 | for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next); |
||
603 | if (vp == NULL) { |
||
604 | if (strchr(name, '=')) |
||
605 | setvareq(savestr(name), VSTRFIXED); |
||
606 | else |
||
607 | setvar(name, NULL, VSTRFIXED); |
||
608 | vp = *vpp; /* the new variable */ |
||
609 | lvp->text = NULL; |
||
610 | lvp->flags = VUNSET; |
||
611 | } else { |
||
612 | lvp->text = vp->text; |
||
613 | lvp->flags = vp->flags; |
||
614 | vp->flags |= VSTRFIXED|VTEXTFIXED; |
||
615 | if (strchr(name, '=')) |
||
616 | setvareq(savestr(name), 0); |
||
617 | } |
||
618 | } |
||
619 | lvp->vp = vp; |
||
620 | lvp->next = localvars; |
||
621 | localvars = lvp; |
||
622 | INTON; |
||
623 | } |
||
624 | |||
625 | |||
626 | /* |
||
627 | * Called after a function returns. |
||
628 | */ |
||
629 | |||
630 | void |
||
631 | poplocalvars() { |
||
632 | struct localvar *lvp; |
||
633 | struct var *vp; |
||
634 | |||
635 | while ((lvp = localvars) != NULL) { |
||
636 | localvars = lvp->next; |
||
637 | vp = lvp->vp; |
||
638 | if (vp == NULL) { /* $- saved */ |
||
639 | memcpy(optlist, lvp->text, sizeof optlist); |
||
640 | ckfree(lvp->text); |
||
641 | } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { |
||
642 | (void)unsetvar(vp->text); |
||
643 | } else { |
||
644 | if ((vp->flags & VTEXTFIXED) == 0) |
||
645 | ckfree(vp->text); |
||
646 | vp->flags = lvp->flags; |
||
647 | vp->text = lvp->text; |
||
648 | } |
||
649 | ckfree(lvp); |
||
650 | } |
||
651 | } |
||
652 | |||
653 | |||
654 | int |
||
655 | setvarcmd(argc, argv) |
||
656 | int argc; |
||
657 | char **argv; |
||
658 | { |
||
659 | if (argc <= 2) |
||
660 | return unsetcmd(argc, argv); |
||
661 | else if (argc == 3) |
||
662 | setvar(argv[1], argv[2], 0); |
||
663 | else |
||
664 | error("List assignment not implemented"); |
||
665 | return 0; |
||
666 | } |
||
667 | |||
668 | |||
669 | /* |
||
670 | * The unset builtin command. We unset the function before we unset the |
||
671 | * variable to allow a function to be unset when there is a readonly variable |
||
672 | * with the same name. |
||
673 | */ |
||
674 | |||
675 | int |
||
676 | unsetcmd(argc, argv) |
||
677 | int argc; |
||
678 | char **argv; |
||
679 | { |
||
680 | char **ap; |
||
681 | int i; |
||
682 | int flg_func = 0; |
||
683 | int flg_var = 0; |
||
684 | int ret = 0; |
||
685 | |||
686 | while ((i = nextopt("vf")) != '\0') { |
||
687 | if (i == 'f') |
||
688 | flg_func = 1; |
||
689 | else |
||
690 | flg_var = 1; |
||
691 | } |
||
692 | if (flg_func == 0 && flg_var == 0) |
||
693 | flg_var = 1; |
||
694 | |||
695 | for (ap = argptr; *ap ; ap++) { |
||
696 | if (flg_func) |
||
697 | ret |= unsetfunc(*ap); |
||
698 | if (flg_var) |
||
699 | ret |= unsetvar(*ap); |
||
700 | } |
||
701 | return ret; |
||
702 | } |
||
703 | |||
704 | |||
705 | /* |
||
706 | * Unset the specified variable. |
||
707 | */ |
||
708 | |||
709 | int |
||
710 | unsetvar(s) |
||
711 | const char *s; |
||
712 | { |
||
713 | struct var **vpp; |
||
714 | struct var *vp; |
||
715 | |||
716 | vpp = hashvar(s); |
||
717 | for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { |
||
718 | if (varequal(vp->text, s)) { |
||
719 | if (vp->flags & VREADONLY) |
||
720 | return (1); |
||
721 | INTOFF; |
||
722 | if (*(strchr(vp->text, '=') + 1) != '\0') |
||
723 | setvar(s, nullstr, 0); |
||
724 | vp->flags &= ~VEXPORT; |
||
725 | vp->flags |= VUNSET; |
||
726 | if ((vp->flags & VSTRFIXED) == 0) { |
||
727 | if ((vp->flags & VTEXTFIXED) == 0) |
||
728 | ckfree(vp->text); |
||
729 | *vpp = vp->next; |
||
730 | ckfree(vp); |
||
731 | } |
||
732 | INTON; |
||
733 | return (0); |
||
734 | } |
||
735 | } |
||
736 | |||
737 | return (1); |
||
738 | } |
||
739 | |||
740 | |||
741 | |||
742 | /* |
||
743 | * Find the appropriate entry in the hash table from the name. |
||
744 | */ |
||
745 | |||
746 | STATIC struct var ** |
||
747 | hashvar(p) |
||
748 | const char *p; |
||
749 | { |
||
750 | unsigned int hashval; |
||
751 | |||
752 | hashval = ((unsigned char) *p) << 4; |
||
753 | while (*p && *p != '=') |
||
754 | hashval += (unsigned char) *p++; |
||
755 | return &vartab[hashval % VTABSIZE]; |
||
756 | } |
||
757 | |||
758 | |||
759 | |||
760 | /* |
||
761 | * Returns true if the two strings specify the same varable. The first |
||
762 | * variable name is terminated by '='; the second may be terminated by |
||
763 | * either '=' or '\0'. |
||
764 | */ |
||
765 | |||
766 | STATIC int |
||
767 | varequal(p, q) |
||
768 | const char *p, *q; |
||
769 | { |
||
770 | while (*p == *q++) { |
||
771 | if (*p++ == '=') |
||
772 | return 1; |
||
773 | } |
||
774 | if (*p == '=' && *(q - 1) == '\0') |
||
775 | return 1; |
||
776 | return 0; |
||
777 | } |