Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/var.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 13344 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 13344 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /*- |
2 | * Copyright (c) 1991, 1993 |
3 | * The Regents of the University of California. All rights reserved. |
4 | * Copyright (c) 1997-2005 |
5 | * Herbert Xu <herbert@gondor.apana.org.au>. 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. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. |
33 | */ |
34 | |
35 | #include <unistd.h> |
36 | #include <stdlib.h> |
37 | #include <paths.h> |
38 | |
39 | /* |
40 | * Shell variables. |
41 | */ |
42 | |
43 | #include "shell.h" |
44 | #include "output.h" |
45 | #include "expand.h" |
46 | #include "nodes.h" /* for other headers */ |
47 | #include "eval.h" /* defines cmdenviron */ |
48 | #include "exec.h" |
49 | #include "syntax.h" |
50 | #include "options.h" |
51 | #include "mail.h" |
52 | #include "var.h" |
53 | #include "memalloc.h" |
54 | #include "error.h" |
55 | #include "mystring.h" |
56 | #include "parser.h" |
57 | #include "show.h" |
58 | #ifndef SMALL |
59 | #include "myhistedit.h" |
60 | #endif |
61 | #include "system.h" |
62 | |
63 | |
64 | #define VTABSIZE 39 |
65 | |
66 | |
67 | struct localvar *localvars; |
68 | |
69 | const char defpathvar[] = |
70 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; |
71 | #ifdef IFS_BROKEN |
72 | const char defifsvar[] = "IFS= \t\n"; |
73 | #else |
74 | const char defifs[] = " \t\n"; |
75 | #endif |
76 | |
77 | struct var varinit[] = { |
78 | #if ATTY |
79 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY\0", 0 }, |
80 | #endif |
81 | #ifdef IFS_BROKEN |
82 | { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 }, |
83 | #else |
84 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 }, |
85 | #endif |
86 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail }, |
87 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, |
88 | { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath }, |
89 | { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 }, |
90 | { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, |
91 | { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, |
92 | { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, |
93 | #ifndef SMALL |
94 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM\0", 0 }, |
95 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE\0", sethistsize }, |
96 | #endif |
97 | }; |
98 | |
99 | STATIC struct var *vartab[VTABSIZE]; |
100 | |
101 | STATIC void mklocal(char *); |
102 | STATIC struct var **hashvar(const char *); |
103 | STATIC int vpcmp(const void *, const void *); |
104 | STATIC struct var **findvar(struct var **, const char *); |
105 | |
106 | /* |
107 | * Initialize the varable symbol tables and import the environment |
108 | */ |
109 | |
110 | #ifdef mkinit |
111 | INCLUDE <unistd.h> |
112 | INCLUDE <sys/types.h> |
113 | INCLUDE <sys/stat.h> |
114 | INCLUDE "cd.h" |
115 | INCLUDE "output.h" |
116 | INCLUDE "var.h" |
117 | MKINIT char **environ; |
118 | INIT { |
119 | char **envp; |
120 | static char ppid[32] = "PPID="; |
121 | const char *p; |
122 | struct stat st1, st2; |
123 | |
124 | initvar(); |
125 | for (envp = environ ; *envp ; envp++) { |
126 | if (strchr(*envp, '=')) { |
127 | setvareq(*envp, VEXPORT|VTEXTFIXED); |
128 | } |
129 | } |
130 | |
131 | fmtstr(ppid + 5, sizeof(ppid) - 5, "%ld", (long) getppid()); |
132 | setvareq(ppid, VTEXTFIXED); |
133 | |
134 | p = lookupvar("PWD"); |
135 | if (p) |
136 | if (*p != '/' || stat(p, &st1) || stat(".", &st2) || |
137 | st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) |
138 | p = 0; |
139 | setpwd(p, 0); |
140 | } |
141 | #endif |
142 | |
143 | |
144 | /* |
145 | * This routine initializes the builtin variables. It is called when the |
146 | * shell is initialized. |
147 | */ |
148 | |
149 | void |
150 | initvar(void) |
151 | { |
152 | struct var *vp; |
153 | struct var *end; |
154 | struct var **vpp; |
155 | |
156 | vp = varinit; |
157 | end = vp + sizeof(varinit) / sizeof(varinit[0]); |
158 | do { |
159 | vpp = hashvar(vp->text); |
160 | vp->next = *vpp; |
161 | *vpp = vp; |
162 | } while (++vp < end); |
163 | /* |
164 | * PS1 depends on uid |
165 | */ |
166 | if (!geteuid()) |
167 | vps1.text = "PS1=# "; |
168 | } |
169 | |
170 | /* |
171 | * Safe version of setvar, returns 1 on success 0 on failure. |
172 | */ |
173 | |
174 | int |
175 | setvarsafe(const char *name, const char *val, int flags) |
176 | { |
177 | int err; |
178 | volatile int saveint; |
179 | struct jmploc *volatile savehandler = handler; |
180 | struct jmploc jmploc; |
181 | |
182 | SAVEINT(saveint); |
183 | if (setjmp(jmploc.loc)) |
184 | err = 1; |
185 | else { |
186 | handler = &jmploc; |
187 | setvar(name, val, flags); |
188 | err = 0; |
189 | } |
190 | handler = savehandler; |
191 | RESTOREINT(saveint); |
192 | return err; |
193 | } |
194 | |
195 | /* |
196 | * Set the value of a variable. The flags argument is ored with the |
197 | * flags of the variable. If val is NULL, the variable is unset. |
198 | */ |
199 | |
200 | void |
201 | setvar(const char *name, const char *val, int flags) |
202 | { |
203 | char *p, *q; |
204 | size_t namelen; |
205 | char *nameeq; |
206 | size_t vallen; |
207 | |
208 | q = endofname(name); |
209 | p = strchrnul(q, '='); |
210 | namelen = p - name; |
211 | if (!namelen || p != q) |
212 | sh_error("%.*s: bad variable name", namelen, name); |
213 | vallen = 0; |
214 | if (val == NULL) { |
215 | flags |= VUNSET; |
216 | } else { |
217 | vallen = strlen(val); |
218 | } |
219 | INTOFF; |
220 | p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen); |
221 | if (val) { |
222 | *p++ = '='; |
223 | p = mempcpy(p, val, vallen); |
224 | } |
225 | *p = '\0'; |
226 | setvareq(nameeq, flags | VNOSAVE); |
227 | INTON; |
228 | } |
229 | |
230 | |
231 | |
232 | /* |
233 | * Same as setvar except that the variable and value are passed in |
234 | * the first argument as name=value. Since the first argument will |
235 | * be actually stored in the table, it should not be a string that |
236 | * will go away. |
237 | * Called with interrupts off. |
238 | */ |
239 | |
240 | void |
241 | setvareq(char *s, int flags) |
242 | { |
243 | struct var *vp, **vpp; |
244 | |
245 | vpp = hashvar(s); |
246 | flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); |
247 | vp = *findvar(vpp, s); |
248 | if (vp) { |
249 | if (vp->flags & VREADONLY) { |
250 | const char *n; |
251 | |
252 | if (flags & VNOSAVE) |
253 | free(s); |
254 | n = vp->text; |
255 | sh_error("%.*s: is read only", strchrnul(n, '=') - n, |
256 | n); |
257 | } |
258 | |
259 | if (flags & VNOSET) |
260 | return; |
261 | |
262 | if (vp->func && (flags & VNOFUNC) == 0) |
263 | (*vp->func)(strchrnul(s, '=') + 1); |
264 | |
265 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) |
266 | ckfree(vp->text); |
267 | |
268 | flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); |
269 | } else { |
270 | if (flags & VNOSET) |
271 | return; |
272 | /* not found */ |
273 | vp = ckmalloc(sizeof (*vp)); |
274 | vp->next = *vpp; |
275 | vp->func = NULL; |
276 | *vpp = vp; |
277 | } |
278 | if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) |
279 | s = savestr(s); |
280 | vp->text = s; |
281 | vp->flags = flags; |
282 | } |
283 | |
284 | |
285 | |
286 | /* |
287 | * Process a linked list of variable assignments. |
288 | */ |
289 | |
290 | void |
291 | listsetvar(struct strlist *list, int flags) |
292 | { |
293 | struct strlist *lp; |
294 | |
295 | lp = list; |
296 | if (!lp) |
297 | return; |
298 | INTOFF; |
299 | do { |
300 | setvareq(lp->text, flags); |
301 | } while ((lp = lp->next)); |
302 | INTON; |
303 | } |
304 | |
305 | |
306 | /* |
307 | * Find the value of a variable. Returns NULL if not set. |
308 | */ |
309 | |
310 | char * |
311 | lookupvar(const char *name) |
312 | { |
313 | struct var *v; |
314 | |
315 | if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) { |
316 | return strchrnul(v->text, '=') + 1; |
317 | } |
318 | return NULL; |
319 | } |
320 | |
321 | |
322 | |
323 | /* |
324 | * Search the environment of a builtin command. |
325 | */ |
326 | |
327 | char * |
328 | bltinlookup(const char *name) |
329 | { |
330 | struct strlist *sp; |
331 | |
332 | for (sp = cmdenviron ; sp ; sp = sp->next) { |
333 | if (varequal(sp->text, name)) |
334 | return strchrnul(sp->text, '=') + 1; |
335 | } |
336 | return lookupvar(name); |
337 | } |
338 | |
339 | |
340 | |
341 | /* |
342 | * Generate a list of variables satisfying the given conditions. |
343 | */ |
344 | |
345 | char ** |
346 | listvars(int on, int off, char ***end) |
347 | { |
348 | struct var **vpp; |
349 | struct var *vp; |
350 | char **ep; |
351 | int mask; |
352 | |
353 | STARTSTACKSTR(ep); |
354 | vpp = vartab; |
355 | mask = on | off; |
356 | do { |
357 | for (vp = *vpp ; vp ; vp = vp->next) |
358 | if ((vp->flags & mask) == on) { |
359 | if (ep == stackstrend()) |
360 | ep = growstackstr(); |
361 | *ep++ = (char *) vp->text; |
362 | } |
363 | } while (++vpp < vartab + VTABSIZE); |
364 | if (ep == stackstrend()) |
365 | ep = growstackstr(); |
366 | if (end) |
367 | *end = ep; |
368 | *ep++ = NULL; |
369 | return grabstackstr(ep); |
370 | } |
371 | |
372 | |
373 | |
374 | /* |
375 | * POSIX requires that 'set' (but not export or readonly) output the |
376 | * variables in lexicographic order - by the locale's collating order (sigh). |
377 | * Maybe we could keep them in an ordered balanced binary tree |
378 | * instead of hashed lists. |
379 | * For now just roll 'em through qsort for printing... |
380 | */ |
381 | |
382 | int |
383 | showvars(const char *prefix, int on, int off) |
384 | { |
385 | const char *sep; |
386 | char **ep, **epend; |
387 | |
388 | ep = listvars(on, off, &epend); |
389 | qsort(ep, epend - ep, sizeof(char *), vpcmp); |
390 | |
391 | sep = *prefix ? spcstr : prefix; |
392 | |
393 | for (; ep < epend; ep++) { |
394 | const char *p; |
395 | const char *q; |
396 | |
397 | p = strchrnul(*ep, '='); |
398 | q = nullstr; |
399 | if (*p) |
400 | q = single_quote(++p); |
401 | |
402 | out1fmt("%s%s%.*s%s\n", prefix, sep, (int)(p - *ep), *ep, q); |
403 | } |
404 | |
405 | return 0; |
406 | } |
407 | |
408 | |
409 | |
410 | /* |
411 | * The export and readonly commands. |
412 | */ |
413 | |
414 | int |
415 | exportcmd(int argc, char **argv) |
416 | { |
417 | struct var *vp; |
418 | char *name; |
419 | const char *p; |
420 | char **aptr; |
421 | int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; |
422 | int notp; |
423 | |
424 | notp = nextopt("p") - 'p'; |
425 | if (notp && ((name = *(aptr = argptr)))) { |
426 | do { |
427 | if ((p = strchr(name, '=')) != NULL) { |
428 | p++; |
429 | } else { |
430 | if ((vp = *findvar(hashvar(name), name))) { |
431 | vp->flags |= flag; |
432 | continue; |
433 | } |
434 | } |
435 | setvar(name, p, flag); |
436 | } while ((name = *++aptr) != NULL); |
437 | } else { |
438 | showvars(argv[0], flag, 0); |
439 | } |
440 | return 0; |
441 | } |
442 | |
443 | |
444 | /* |
445 | * The "local" command. |
446 | */ |
447 | |
448 | int |
449 | localcmd(int argc, char **argv) |
450 | { |
451 | char *name; |
452 | |
453 | argv = argptr; |
454 | while ((name = *argv++) != NULL) { |
455 | mklocal(name); |
456 | } |
457 | return 0; |
458 | } |
459 | |
460 | |
461 | /* |
462 | * Make a variable a local variable. When a variable is made local, it's |
463 | * value and flags are saved in a localvar structure. The saved values |
464 | * will be restored when the shell function returns. We handle the name |
465 | * "-" as a special case. |
466 | */ |
467 | |
468 | STATIC void |
469 | mklocal(char *name) |
470 | { |
471 | struct localvar *lvp; |
472 | struct var **vpp; |
473 | struct var *vp; |
474 | |
475 | INTOFF; |
476 | lvp = ckmalloc(sizeof (struct localvar)); |
477 | if (name[0] == '-' && name[1] == '\0') { |
478 | char *p; |
479 | p = ckmalloc(sizeof(optlist)); |
480 | lvp->text = memcpy(p, optlist, sizeof(optlist)); |
481 | vp = NULL; |
482 | } else { |
483 | char *eq; |
484 | |
485 | vpp = hashvar(name); |
486 | vp = *findvar(vpp, name); |
487 | eq = strchr(name, '='); |
488 | if (vp == NULL) { |
489 | if (eq) |
490 | setvareq(name, VSTRFIXED); |
491 | else |
492 | setvar(name, NULL, VSTRFIXED); |
493 | vp = *vpp; /* the new variable */ |
494 | lvp->flags = VUNSET; |
495 | } else { |
496 | lvp->text = vp->text; |
497 | lvp->flags = vp->flags; |
498 | vp->flags |= VSTRFIXED|VTEXTFIXED; |
499 | if (eq) |
500 | setvareq(name, 0); |
501 | } |
502 | } |
503 | lvp->vp = vp; |
504 | lvp->next = localvars; |
505 | localvars = lvp; |
506 | INTON; |
507 | } |
508 | |
509 | |
510 | /* |
511 | * Called after a function returns. |
512 | * Interrupts must be off. |
513 | */ |
514 | |
515 | void |
516 | poplocalvars(void) |
517 | { |
518 | struct localvar *lvp; |
519 | struct var *vp; |
520 | |
521 | while ((lvp = localvars) != NULL) { |
522 | localvars = lvp->next; |
523 | vp = lvp->vp; |
524 | TRACE(("poplocalvar %s", vp ? vp->text : "-")); |
525 | if (vp == NULL) { /* $- saved */ |
526 | memcpy(optlist, lvp->text, sizeof(optlist)); |
527 | ckfree(lvp->text); |
528 | optschanged(); |
529 | } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { |
530 | unsetvar(vp->text); |
531 | } else { |
532 | if (vp->func) |
533 | (*vp->func)(strchrnul(lvp->text, '=') + 1); |
534 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) |
535 | ckfree(vp->text); |
536 | vp->flags = lvp->flags; |
537 | vp->text = lvp->text; |
538 | } |
539 | ckfree(lvp); |
540 | } |
541 | } |
542 | |
543 | |
544 | /* |
545 | * The unset builtin command. We unset the function before we unset the |
546 | * variable to allow a function to be unset when there is a readonly variable |
547 | * with the same name. |
548 | */ |
549 | |
550 | int |
551 | unsetcmd(int argc, char **argv) |
552 | { |
553 | char **ap; |
554 | int i; |
555 | int flag = 0; |
556 | int ret = 0; |
557 | |
558 | while ((i = nextopt("vf")) != '\0') { |
559 | flag = i; |
560 | } |
561 | |
562 | for (ap = argptr; *ap ; ap++) { |
563 | if (flag != 'f') { |
564 | i = unsetvar(*ap); |
565 | ret |= i; |
566 | if (!(i & 2)) |
567 | continue; |
568 | } |
569 | if (flag != 'v') |
570 | unsetfunc(*ap); |
571 | } |
572 | return ret & 1; |
573 | } |
574 | |
575 | |
576 | /* |
577 | * Unset the specified variable. |
578 | */ |
579 | |
580 | int |
581 | unsetvar(const char *s) |
582 | { |
583 | struct var **vpp; |
584 | struct var *vp; |
585 | int retval; |
586 | |
587 | vpp = findvar(hashvar(s), s); |
588 | vp = *vpp; |
589 | retval = 2; |
590 | if (vp) { |
591 | int flags = vp->flags; |
592 | |
593 | retval = 1; |
594 | if (flags & VREADONLY) |
595 | goto out; |
596 | if (flags & VUNSET) |
597 | goto ok; |
598 | if ((flags & VSTRFIXED) == 0) { |
599 | INTOFF; |
600 | if ((flags & (VTEXTFIXED|VSTACK)) == 0) |
601 | ckfree(vp->text); |
602 | *vpp = vp->next; |
603 | ckfree(vp); |
604 | INTON; |
605 | } else { |
606 | setvar(s, 0, 0); |
607 | vp->flags &= ~VEXPORT; |
608 | } |
609 | ok: |
610 | retval = 0; |
611 | } |
612 | |
613 | out: |
614 | return retval; |
615 | } |
616 | |
617 | |
618 | |
619 | /* |
620 | * Find the appropriate entry in the hash table from the name. |
621 | */ |
622 | |
623 | STATIC struct var ** |
624 | hashvar(const char *p) |
625 | { |
626 | unsigned int hashval; |
627 | |
628 | hashval = ((unsigned char) *p) << 4; |
629 | while (*p && *p != '=') |
630 | hashval += (unsigned char) *p++; |
631 | return &vartab[hashval % VTABSIZE]; |
632 | } |
633 | |
634 | |
635 | |
636 | /* |
637 | * Compares two strings up to the first = or '\0'. The first |
638 | * string must be terminated by '='; the second may be terminated by |
639 | * either '=' or '\0'. |
640 | */ |
641 | |
642 | int |
643 | varcmp(const char *p, const char *q) |
644 | { |
645 | int c, d; |
646 | |
647 | while ((c = *p) == (d = *q)) { |
648 | if (!c || c == '=') |
649 | goto out; |
650 | p++; |
651 | q++; |
652 | } |
653 | if (c == '=') |
654 | c = 0; |
655 | if (d == '=') |
656 | d = 0; |
657 | out: |
658 | return c - d; |
659 | } |
660 | |
661 | STATIC int |
662 | vpcmp(const void *a, const void *b) |
663 | { |
664 | return varcmp(*(const char **)a, *(const char **)b); |
665 | } |
666 | |
667 | STATIC struct var ** |
668 | findvar(struct var **vpp, const char *name) |
669 | { |
670 | for (; *vpp; vpp = &(*vpp)->next) { |
671 | if (varequal((*vpp)->text, name)) { |
672 | break; |
673 | } |
674 | } |
675 | return vpp; |
676 | } |