Commit | Line | Data |
---|---|---|
506fbc8d C |
1 | /* var.c: provide "public" functions for adding and removing variables from the symbol table */ |
2 | ||
3 | #include "rc.h" | |
4 | ||
5 | static void colonassign(char *, List *, bool); | |
6 | static void listassign(char *, List *, bool); | |
7 | static int hasalias(char *); | |
8 | ||
9 | static char *const aliases[] = { | |
10 | "home", "HOME", "path", "PATH", "cdpath", "CDPATH" | |
11 | }; | |
12 | ||
13 | /* assign a variable in List form to a name, stacking if appropriate */ | |
14 | ||
15 | extern void varassign(char *name, List *def, bool stack) { | |
16 | Variable *new; | |
17 | List *newdef = listcpy(def, ealloc); /* important to do the listcpy first; get_var_place() frees old values */ | |
18 | new = get_var_place(name, stack); | |
19 | new->def = newdef; | |
20 | new->extdef = NULL; | |
21 | } | |
22 | ||
23 | /* assign a variable in string form. Check to see if it is aliased (e.g., PATH and path) */ | |
24 | ||
25 | extern bool varassign_string(char *extdef) { | |
26 | static bool aliasset[arraysize(aliases)] = { | |
27 | FALSE, FALSE, FALSE, FALSE, FALSE, FALSE | |
28 | }; | |
29 | char *name = get_name(extdef); | |
30 | Variable *new; | |
31 | int i; | |
32 | if (name == NULL) | |
33 | return FALSE; /* add it to bozo env */ | |
34 | if ((i = hasalias(name)) != -1) { | |
35 | aliasset[i] = TRUE; | |
36 | i ^= 1; /* set i to the "opposite" case subscript and */ | |
37 | if (i&1 && aliasset[i]) /* don't alias variables that are already set in upper case */ | |
38 | return TRUE; | |
39 | } | |
40 | new = get_var_place(name, FALSE); | |
41 | new->def = NULL; | |
42 | new->extdef = ealloc(strlen(extdef) + 1); | |
43 | strcpy(new->extdef, extdef); | |
44 | if (i != -1) | |
45 | alias(name, varlookup(name), FALSE); | |
46 | return TRUE; | |
47 | } | |
48 | ||
49 | /* | |
50 | Return a List based on a name lookup. If the list is in external (string) form, | |
51 | convert it to internal (List) form. Treat $n (n is an integer) specially as $*(n). | |
52 | Also check to see if $status is being dereferenced. (we lazily evaluate the List | |
53 | associated with $status) | |
54 | */ | |
55 | ||
56 | extern List *varlookup(char *name) { | |
57 | Variable *look; | |
58 | List *ret, *l; | |
59 | int sub; | |
60 | if (streq(name, "status")) | |
61 | return sgetstatus(); | |
62 | if (streq(name, "apids")) | |
63 | return sgetapids(); | |
64 | if (*name != '\0' && (sub = a2u(name)) != -1) { /* handle $1, $2, etc. */ | |
65 | for (l = varlookup("*"); l != NULL && sub != 0; --sub) | |
66 | l = l->n; | |
67 | if (l == NULL) | |
68 | return NULL; | |
69 | ret = nnew(List); | |
70 | ret->w = l->w; | |
71 | ret->m = NULL; | |
72 | ret->n = NULL; | |
73 | return ret; | |
74 | } | |
75 | look = lookup_var(name); | |
76 | if (look == NULL) | |
77 | return NULL; /* not found */ | |
78 | if (look->def != NULL) | |
79 | return look->def; | |
80 | if (look->extdef == NULL) | |
81 | return NULL; /* variable was set to null, e.g., a=() echo foo */ | |
82 | ret = parse_var(name, look->extdef); | |
83 | if (ret == NULL) { | |
84 | look->extdef = NULL; | |
85 | return NULL; | |
86 | } | |
87 | return look->def = ret; | |
88 | } | |
89 | ||
90 | /* lookup a variable in external (string) form, converting if necessary. Used by makeenv() */ | |
91 | ||
92 | extern char *varlookup_string(char *name) { | |
93 | Variable *look; | |
94 | look = lookup_var(name); | |
95 | if (look == NULL) | |
96 | return NULL; | |
97 | if (look->extdef != NULL) | |
98 | return look->extdef; | |
99 | if (look->def == NULL) | |
100 | return NULL; | |
101 | return look->extdef = list2str(name, look->def); | |
102 | } | |
103 | ||
104 | /* remove a variable from the symtab. "stack" determines whether a level of scoping is popped or not */ | |
105 | ||
106 | extern void varrm(char *name, bool stack) { | |
107 | int i = hasalias(name); | |
108 | if (streq(name, "*") && !stack) { /* when assigning () to $*, we want to preserve $0 */ | |
109 | varassign("*", varlookup("0"), FALSE); | |
110 | return; | |
111 | } | |
112 | delete_var(name, stack); | |
113 | if (i != -1) | |
114 | delete_var(aliases[i^1], stack); | |
115 | } | |
116 | ||
117 | /* assign a value (List) to a variable, using array "a" as input. Used to assign $* */ | |
118 | ||
119 | extern void starassign(char *dollarzero, char **a, bool stack) { | |
120 | List *s, *var; | |
121 | var = nnew(List); | |
122 | var->w = dollarzero; | |
123 | if (*a == NULL) { | |
124 | var->n = NULL; | |
125 | varassign("*", var, stack); | |
126 | return; | |
127 | } | |
128 | var->n = s = nnew(List); | |
129 | while (1) { | |
130 | s->w = *a++; | |
131 | if (*a == NULL) { | |
132 | s->n = NULL; | |
133 | break; | |
134 | } else | |
135 | s = s->n = nnew(List); | |
136 | } | |
137 | varassign("*", var, stack); | |
138 | } | |
139 | ||
140 | /* (ugly name, huh?) assign a colon-separated value to a variable (e.g., PATH) from a List (e.g., path) */ | |
141 | ||
142 | static void colonassign(char *name, List *def, bool stack) { | |
143 | List dud; | |
144 | if (def == NULL) { | |
145 | varassign(name, NULL, stack); | |
146 | return; | |
147 | } | |
148 | dud.w = nprint("%-L", def, ":"); | |
149 | dud.n = NULL; | |
150 | varassign(name, &dud, stack); | |
151 | } | |
152 | ||
153 | /* assign a List variable (e.g., path) from a colon-separated string (e.g., PATH) */ | |
154 | ||
155 | static void listassign(char *name, List *def, bool stack) { | |
156 | List *val, *r; | |
157 | char *v, *w; | |
158 | if (def == NULL) { | |
159 | varassign(name, NULL, stack); | |
160 | return; | |
161 | } | |
162 | v = def->w; | |
163 | r = val = enew(List); | |
164 | while ((w = strchr(v, ':')) != NULL) { | |
165 | *w = '\0'; | |
166 | r->w = ecpy(v); | |
167 | *w = ':'; | |
168 | v = w + 1; | |
169 | r->n = enew(List); | |
170 | r = r->n; | |
171 | } | |
172 | r->w = ecpy(v); | |
173 | r->n = NULL; | |
174 | varassign(name, val, stack); | |
175 | } | |
176 | ||
177 | /* check to see if a particular variable is aliased; return -1 on failure, or the index */ | |
178 | ||
179 | static int hasalias(char *name) { | |
180 | int i; | |
181 | for (i = 0; i < arraysize(aliases); i++) | |
182 | if (streq(name, aliases[i])) | |
183 | return i; | |
184 | return -1; | |
185 | } | |
186 | ||
187 | /* alias a variable to its lowercase equivalent. function pointers are used to specify the conversion function */ | |
188 | ||
189 | extern void alias(char *name, List *s, bool stack) { | |
190 | static void (*vectors[])(char *, List *, bool) = { | |
191 | varassign, varassign, colonassign, listassign, colonassign, listassign | |
192 | }; | |
193 | int i = hasalias(name); | |
194 | if (i != -1) | |
195 | (*vectors[i])(aliases[i^1], s, stack); /* xor hack to reverse case of alias entry */ | |
196 | } | |
197 | ||
198 | extern void prettyprint_var(int fd, char *name, List *s) { | |
199 | int i; | |
200 | static const char * const keywords[] = { | |
201 | "if", "in", "fn", "for", "else", "switch", "while", "case" | |
202 | }; | |
203 | if (s == NULL) { | |
204 | fprint(fd, "%S=()\n", name); | |
205 | return; | |
206 | } | |
207 | if (streq(name, "*")) { | |
208 | s = s->n; | |
209 | if (s == NULL) | |
210 | return; /* Don't print $0, and if $* is not set, skip it */ | |
211 | } | |
212 | for (i = 0; i < arraysize(keywords); i++) | |
213 | if (streq(keywords[i], name)) { | |
214 | fprint(fd, "%#S=", name); | |
215 | goto value; | |
216 | } | |
217 | fprint(fd, "%S=", name); | |
218 | value: | |
219 | fprint(fd, s->n == NULL ? "%L\n" : "(%L)\n", s, " "); | |
220 | } |