Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/var.c
Parent Directory | Revision Log
Revision 1122 -
(show annotations)
(download)
Wed Aug 18 21:11:40 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 13375 byte(s)
Wed Aug 18 21:11:40 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 13375 byte(s)
-updated to klibc-1.5.19
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 | { 0, VSTRFIXED|VTEXTFIXED, "LINENO=1", 0 }, |
94 | #ifndef SMALL |
95 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM\0", 0 }, |
96 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE\0", sethistsize }, |
97 | #endif |
98 | }; |
99 | |
100 | STATIC struct var *vartab[VTABSIZE]; |
101 | |
102 | STATIC void mklocal(char *); |
103 | STATIC struct var **hashvar(const char *); |
104 | STATIC int vpcmp(const void *, const void *); |
105 | STATIC struct var **findvar(struct var **, const char *); |
106 | |
107 | /* |
108 | * Initialize the varable symbol tables and import the environment |
109 | */ |
110 | |
111 | #ifdef mkinit |
112 | INCLUDE <unistd.h> |
113 | INCLUDE <sys/types.h> |
114 | INCLUDE <sys/stat.h> |
115 | INCLUDE "cd.h" |
116 | INCLUDE "output.h" |
117 | INCLUDE "var.h" |
118 | MKINIT char **environ; |
119 | INIT { |
120 | char **envp; |
121 | static char ppid[32] = "PPID="; |
122 | const char *p; |
123 | struct stat st1, st2; |
124 | |
125 | initvar(); |
126 | for (envp = environ ; *envp ; envp++) { |
127 | if (strchr(*envp, '=')) { |
128 | setvareq(*envp, VEXPORT|VTEXTFIXED); |
129 | } |
130 | } |
131 | |
132 | fmtstr(ppid + 5, sizeof(ppid) - 5, "%ld", (long) getppid()); |
133 | setvareq(ppid, VTEXTFIXED); |
134 | |
135 | p = lookupvar("PWD"); |
136 | if (p) |
137 | if (*p != '/' || stat(p, &st1) || stat(".", &st2) || |
138 | st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) |
139 | p = 0; |
140 | setpwd(p, 0); |
141 | } |
142 | #endif |
143 | |
144 | |
145 | /* |
146 | * This routine initializes the builtin variables. It is called when the |
147 | * shell is initialized. |
148 | */ |
149 | |
150 | void |
151 | initvar(void) |
152 | { |
153 | struct var *vp; |
154 | struct var *end; |
155 | struct var **vpp; |
156 | |
157 | vp = varinit; |
158 | end = vp + sizeof(varinit) / sizeof(varinit[0]); |
159 | do { |
160 | vpp = hashvar(vp->text); |
161 | vp->next = *vpp; |
162 | *vpp = vp; |
163 | } while (++vp < end); |
164 | /* |
165 | * PS1 depends on uid |
166 | */ |
167 | if (!geteuid()) |
168 | vps1.text = "PS1=# "; |
169 | } |
170 | |
171 | /* |
172 | * Set the value of a variable. The flags argument is ored with the |
173 | * flags of the variable. If val is NULL, the variable is unset. |
174 | */ |
175 | |
176 | void |
177 | setvar(const char *name, const char *val, int flags) |
178 | { |
179 | char *p, *q; |
180 | size_t namelen; |
181 | char *nameeq; |
182 | size_t vallen; |
183 | |
184 | q = endofname(name); |
185 | p = strchrnul(q, '='); |
186 | namelen = p - name; |
187 | if (!namelen || p != q) |
188 | sh_error("%.*s: bad variable name", namelen, name); |
189 | vallen = 0; |
190 | if (val == NULL) { |
191 | flags |= VUNSET; |
192 | } else { |
193 | vallen = strlen(val); |
194 | } |
195 | INTOFF; |
196 | p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen); |
197 | if (val) { |
198 | *p++ = '='; |
199 | p = mempcpy(p, val, vallen); |
200 | } |
201 | *p = '\0'; |
202 | setvareq(nameeq, flags | VNOSAVE); |
203 | INTON; |
204 | } |
205 | |
206 | /* |
207 | * Set the given integer as the value of a variable. The flags argument is |
208 | * ored with the flags of the variable. |
209 | */ |
210 | |
211 | intmax_t setvarint(const char *name, intmax_t val, int flags) |
212 | { |
213 | int len = max_int_length(sizeof(val)); |
214 | char buf[len]; |
215 | |
216 | fmtstr(buf, len, "%jd", val); |
217 | setvar(name, buf, flags); |
218 | return val; |
219 | } |
220 | |
221 | |
222 | |
223 | /* |
224 | * Same as setvar except that the variable and value are passed in |
225 | * the first argument as name=value. Since the first argument will |
226 | * be actually stored in the table, it should not be a string that |
227 | * will go away. |
228 | * Called with interrupts off. |
229 | */ |
230 | |
231 | void |
232 | setvareq(char *s, int flags) |
233 | { |
234 | struct var *vp, **vpp; |
235 | |
236 | vpp = hashvar(s); |
237 | flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); |
238 | vp = *findvar(vpp, s); |
239 | if (vp) { |
240 | if (vp->flags & VREADONLY) { |
241 | const char *n; |
242 | |
243 | if (flags & VNOSAVE) |
244 | free(s); |
245 | n = vp->text; |
246 | sh_error("%.*s: is read only", strchrnul(n, '=') - n, |
247 | n); |
248 | } |
249 | |
250 | if (flags & VNOSET) |
251 | return; |
252 | |
253 | if (vp->func && (flags & VNOFUNC) == 0) |
254 | (*vp->func)(strchrnul(s, '=') + 1); |
255 | |
256 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) |
257 | ckfree(vp->text); |
258 | |
259 | flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); |
260 | } else { |
261 | if (flags & VNOSET) |
262 | return; |
263 | /* not found */ |
264 | vp = ckmalloc(sizeof (*vp)); |
265 | vp->next = *vpp; |
266 | vp->func = NULL; |
267 | *vpp = vp; |
268 | } |
269 | if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) |
270 | s = savestr(s); |
271 | vp->text = s; |
272 | vp->flags = flags; |
273 | } |
274 | |
275 | |
276 | |
277 | /* |
278 | * Process a linked list of variable assignments. |
279 | */ |
280 | |
281 | void |
282 | listsetvar(struct strlist *list, int flags) |
283 | { |
284 | struct strlist *lp; |
285 | |
286 | lp = list; |
287 | if (!lp) |
288 | return; |
289 | INTOFF; |
290 | do { |
291 | setvareq(lp->text, flags); |
292 | } while ((lp = lp->next)); |
293 | INTON; |
294 | } |
295 | |
296 | |
297 | /* |
298 | * Find the value of a variable. Returns NULL if not set. |
299 | */ |
300 | |
301 | char * |
302 | lookupvar(const char *name) |
303 | { |
304 | struct var *v; |
305 | |
306 | if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) { |
307 | return strchrnul(v->text, '=') + 1; |
308 | } |
309 | return NULL; |
310 | } |
311 | |
312 | intmax_t lookupvarint(const char *name) |
313 | { |
314 | return atomax(lookupvar(name) ?: nullstr, 0); |
315 | } |
316 | |
317 | |
318 | |
319 | /* |
320 | * Search the environment of a builtin command. |
321 | */ |
322 | |
323 | char * |
324 | bltinlookup(const char *name) |
325 | { |
326 | struct strlist *sp; |
327 | |
328 | for (sp = cmdenviron ; sp ; sp = sp->next) { |
329 | if (varequal(sp->text, name)) |
330 | return strchrnul(sp->text, '=') + 1; |
331 | } |
332 | return lookupvar(name); |
333 | } |
334 | |
335 | |
336 | |
337 | /* |
338 | * Generate a list of variables satisfying the given conditions. |
339 | */ |
340 | |
341 | char ** |
342 | listvars(int on, int off, char ***end) |
343 | { |
344 | struct var **vpp; |
345 | struct var *vp; |
346 | char **ep; |
347 | int mask; |
348 | |
349 | STARTSTACKSTR(ep); |
350 | vpp = vartab; |
351 | mask = on | off; |
352 | do { |
353 | for (vp = *vpp ; vp ; vp = vp->next) |
354 | if ((vp->flags & mask) == on) { |
355 | if (ep == stackstrend()) |
356 | ep = growstackstr(); |
357 | *ep++ = (char *) vp->text; |
358 | } |
359 | } while (++vpp < vartab + VTABSIZE); |
360 | if (ep == stackstrend()) |
361 | ep = growstackstr(); |
362 | if (end) |
363 | *end = ep; |
364 | *ep++ = NULL; |
365 | return grabstackstr(ep); |
366 | } |
367 | |
368 | |
369 | |
370 | /* |
371 | * POSIX requires that 'set' (but not export or readonly) output the |
372 | * variables in lexicographic order - by the locale's collating order (sigh). |
373 | * Maybe we could keep them in an ordered balanced binary tree |
374 | * instead of hashed lists. |
375 | * For now just roll 'em through qsort for printing... |
376 | */ |
377 | |
378 | int |
379 | showvars(const char *prefix, int on, int off) |
380 | { |
381 | const char *sep; |
382 | char **ep, **epend; |
383 | |
384 | ep = listvars(on, off, &epend); |
385 | qsort(ep, epend - ep, sizeof(char *), vpcmp); |
386 | |
387 | sep = *prefix ? spcstr : prefix; |
388 | |
389 | for (; ep < epend; ep++) { |
390 | const char *p; |
391 | const char *q; |
392 | |
393 | p = strchrnul(*ep, '='); |
394 | q = nullstr; |
395 | if (*p) |
396 | q = single_quote(++p); |
397 | |
398 | out1fmt("%s%s%.*s%s\n", prefix, sep, (int)(p - *ep), *ep, q); |
399 | } |
400 | |
401 | return 0; |
402 | } |
403 | |
404 | |
405 | |
406 | /* |
407 | * The export and readonly commands. |
408 | */ |
409 | |
410 | int |
411 | exportcmd(int argc, char **argv) |
412 | { |
413 | struct var *vp; |
414 | char *name; |
415 | const char *p; |
416 | char **aptr; |
417 | int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; |
418 | int notp; |
419 | |
420 | notp = nextopt("p") - 'p'; |
421 | if (notp && ((name = *(aptr = argptr)))) { |
422 | do { |
423 | if ((p = strchr(name, '=')) != NULL) { |
424 | p++; |
425 | } else { |
426 | if ((vp = *findvar(hashvar(name), name))) { |
427 | vp->flags |= flag; |
428 | continue; |
429 | } |
430 | } |
431 | setvar(name, p, flag); |
432 | } while ((name = *++aptr) != NULL); |
433 | } else { |
434 | showvars(argv[0], flag, 0); |
435 | } |
436 | return 0; |
437 | } |
438 | |
439 | |
440 | /* |
441 | * The "local" command. |
442 | */ |
443 | |
444 | int |
445 | localcmd(int argc, char **argv) |
446 | { |
447 | char *name; |
448 | |
449 | argv = argptr; |
450 | while ((name = *argv++) != NULL) { |
451 | mklocal(name); |
452 | } |
453 | return 0; |
454 | } |
455 | |
456 | |
457 | /* |
458 | * Make a variable a local variable. When a variable is made local, it's |
459 | * value and flags are saved in a localvar structure. The saved values |
460 | * will be restored when the shell function returns. We handle the name |
461 | * "-" as a special case. |
462 | */ |
463 | |
464 | STATIC void |
465 | mklocal(char *name) |
466 | { |
467 | struct localvar *lvp; |
468 | struct var **vpp; |
469 | struct var *vp; |
470 | |
471 | INTOFF; |
472 | lvp = ckmalloc(sizeof (struct localvar)); |
473 | if (name[0] == '-' && name[1] == '\0') { |
474 | char *p; |
475 | p = ckmalloc(sizeof(optlist)); |
476 | lvp->text = memcpy(p, optlist, sizeof(optlist)); |
477 | vp = NULL; |
478 | } else { |
479 | char *eq; |
480 | |
481 | vpp = hashvar(name); |
482 | vp = *findvar(vpp, name); |
483 | eq = strchr(name, '='); |
484 | if (vp == NULL) { |
485 | if (eq) |
486 | setvareq(name, VSTRFIXED); |
487 | else |
488 | setvar(name, NULL, VSTRFIXED); |
489 | vp = *vpp; /* the new variable */ |
490 | lvp->flags = VUNSET; |
491 | } else { |
492 | lvp->text = vp->text; |
493 | lvp->flags = vp->flags; |
494 | vp->flags |= VSTRFIXED|VTEXTFIXED; |
495 | if (eq) |
496 | setvareq(name, 0); |
497 | } |
498 | } |
499 | lvp->vp = vp; |
500 | lvp->next = localvars; |
501 | localvars = lvp; |
502 | INTON; |
503 | } |
504 | |
505 | |
506 | /* |
507 | * Called after a function returns. |
508 | * Interrupts must be off. |
509 | */ |
510 | |
511 | void |
512 | poplocalvars(void) |
513 | { |
514 | struct localvar *lvp; |
515 | struct var *vp; |
516 | |
517 | while ((lvp = localvars) != NULL) { |
518 | localvars = lvp->next; |
519 | vp = lvp->vp; |
520 | TRACE(("poplocalvar %s", vp ? vp->text : "-")); |
521 | if (vp == NULL) { /* $- saved */ |
522 | memcpy(optlist, lvp->text, sizeof(optlist)); |
523 | ckfree(lvp->text); |
524 | optschanged(); |
525 | } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { |
526 | unsetvar(vp->text); |
527 | } else { |
528 | if (vp->func) |
529 | (*vp->func)(strchrnul(lvp->text, '=') + 1); |
530 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) |
531 | ckfree(vp->text); |
532 | vp->flags = lvp->flags; |
533 | vp->text = lvp->text; |
534 | } |
535 | ckfree(lvp); |
536 | } |
537 | } |
538 | |
539 | |
540 | /* |
541 | * The unset builtin command. We unset the function before we unset the |
542 | * variable to allow a function to be unset when there is a readonly variable |
543 | * with the same name. |
544 | */ |
545 | |
546 | int |
547 | unsetcmd(int argc, char **argv) |
548 | { |
549 | char **ap; |
550 | int i; |
551 | int flag = 0; |
552 | int ret = 0; |
553 | |
554 | while ((i = nextopt("vf")) != '\0') { |
555 | flag = i; |
556 | } |
557 | |
558 | for (ap = argptr; *ap ; ap++) { |
559 | if (flag != 'f') { |
560 | i = unsetvar(*ap); |
561 | ret |= i; |
562 | if (!(i & 2)) |
563 | continue; |
564 | } |
565 | if (flag != 'v') |
566 | unsetfunc(*ap); |
567 | } |
568 | return ret & 1; |
569 | } |
570 | |
571 | |
572 | /* |
573 | * Unset the specified variable. |
574 | */ |
575 | |
576 | int |
577 | unsetvar(const char *s) |
578 | { |
579 | struct var **vpp; |
580 | struct var *vp; |
581 | int retval; |
582 | |
583 | vpp = findvar(hashvar(s), s); |
584 | vp = *vpp; |
585 | retval = 2; |
586 | if (vp) { |
587 | int flags = vp->flags; |
588 | |
589 | retval = 1; |
590 | if (flags & VREADONLY) |
591 | goto out; |
592 | if (flags & VUNSET) |
593 | goto ok; |
594 | if ((flags & VSTRFIXED) == 0) { |
595 | INTOFF; |
596 | if ((flags & (VTEXTFIXED|VSTACK)) == 0) |
597 | ckfree(vp->text); |
598 | *vpp = vp->next; |
599 | ckfree(vp); |
600 | INTON; |
601 | } else { |
602 | setvar(s, 0, 0); |
603 | vp->flags &= ~VEXPORT; |
604 | } |
605 | ok: |
606 | retval = 0; |
607 | } |
608 | |
609 | out: |
610 | return retval; |
611 | } |
612 | |
613 | |
614 | |
615 | /* |
616 | * Find the appropriate entry in the hash table from the name. |
617 | */ |
618 | |
619 | STATIC struct var ** |
620 | hashvar(const char *p) |
621 | { |
622 | unsigned int hashval; |
623 | |
624 | hashval = ((unsigned char) *p) << 4; |
625 | while (*p && *p != '=') |
626 | hashval += (unsigned char) *p++; |
627 | return &vartab[hashval % VTABSIZE]; |
628 | } |
629 | |
630 | |
631 | |
632 | /* |
633 | * Compares two strings up to the first = or '\0'. The first |
634 | * string must be terminated by '='; the second may be terminated by |
635 | * either '=' or '\0'. |
636 | */ |
637 | |
638 | int |
639 | varcmp(const char *p, const char *q) |
640 | { |
641 | int c, d; |
642 | |
643 | while ((c = *p) == (d = *q)) { |
644 | if (!c || c == '=') |
645 | goto out; |
646 | p++; |
647 | q++; |
648 | } |
649 | if (c == '=') |
650 | c = 0; |
651 | if (d == '=') |
652 | d = 0; |
653 | out: |
654 | return c - d; |
655 | } |
656 | |
657 | STATIC int |
658 | vpcmp(const void *a, const void *b) |
659 | { |
660 | return varcmp(*(const char **)a, *(const char **)b); |
661 | } |
662 | |
663 | STATIC struct var ** |
664 | findvar(struct var **vpp, const char *name) |
665 | { |
666 | for (; *vpp; vpp = &(*vpp)->next) { |
667 | if (varequal((*vpp)->text, name)) { |
668 | break; |
669 | } |
670 | } |
671 | return vpp; |
672 | } |