Line data Source code
1 : /*
2 : * assoc.c - functions to manipulate associative arrays
3 : *
4 : * Associative arrays are standard shell hash tables.
5 : *
6 : * Chet Ramey
7 : * chet@ins.cwru.edu
8 : */
9 :
10 : /* Copyright (C) 2008,2009,2011 Free Software Foundation, Inc.
11 :
12 : This file is part of GNU Bash, the Bourne Again SHell.
13 :
14 : Bash is free software: you can redistribute it and/or modify
15 : it under the terms of the GNU General Public License as published by
16 : the Free Software Foundation, either version 3 of the License, or
17 : (at your option) any later version.
18 :
19 : Bash is distributed in the hope that it will be useful,
20 : but WITHOUT ANY WARRANTY; without even the implied warranty of
21 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 : GNU General Public License for more details.
23 :
24 : You should have received a copy of the GNU General Public License
25 : along with Bash. If not, see <http://www.gnu.org/licenses/>.
26 : */
27 :
28 : #include "config.h"
29 :
30 : #if defined (ARRAY_VARS)
31 :
32 : #if defined (HAVE_UNISTD_H)
33 : # ifdef _MINIX
34 : # include <sys/types.h>
35 : # endif
36 : # include <unistd.h>
37 : #endif
38 :
39 : #include <stdio.h>
40 : #include "bashansi.h"
41 :
42 : #include "shell.h"
43 : #include "array.h"
44 : #include "assoc.h"
45 : #include "builtins/common.h"
46 :
47 : static WORD_LIST *assoc_to_word_list_internal __P((HASH_TABLE *, int));
48 :
49 : /* assoc_create == hash_create */
50 :
51 : void
52 0 : assoc_dispose (hash)
53 : HASH_TABLE *hash;
54 : {
55 0 : if (hash)
56 : {
57 0 : hash_flush (hash, 0);
58 0 : hash_dispose (hash);
59 : }
60 0 : }
61 :
62 : void
63 0 : assoc_flush (hash)
64 : HASH_TABLE *hash;
65 : {
66 0 : hash_flush (hash, 0);
67 0 : }
68 :
69 : int
70 0 : assoc_insert (hash, key, value)
71 : HASH_TABLE *hash;
72 : char *key;
73 : char *value;
74 : {
75 0 : BUCKET_CONTENTS *b;
76 :
77 0 : b = hash_search (key, hash, HASH_CREATE);
78 0 : if (b == 0)
79 : return -1;
80 : /* If we are overwriting an existing element's value, we're not going to
81 : use the key. Nothing in the array assignment code path frees the key
82 : string, so we can free it here to avoid a memory leak. */
83 0 : if (b->key != key)
84 0 : free (key);
85 0 : FREE (b->data);
86 0 : b->data = value ? savestring (value) : (char *)0;
87 0 : return (0);
88 : }
89 :
90 : /* Like assoc_insert, but returns b->data instead of freeing it */
91 : PTR_T
92 0 : assoc_replace (hash, key, value)
93 : HASH_TABLE *hash;
94 : char *key;
95 : char *value;
96 : {
97 0 : BUCKET_CONTENTS *b;
98 0 : PTR_T t;
99 :
100 0 : b = hash_search (key, hash, HASH_CREATE);
101 0 : if (b == 0)
102 : return (PTR_T)0;
103 : /* If we are overwriting an existing element's value, we're not going to
104 : use the key. Nothing in the array assignment code path frees the key
105 : string, so we can free it here to avoid a memory leak. */
106 0 : if (b->key != key)
107 0 : free (key);
108 0 : t = b->data;
109 0 : b->data = value ? savestring (value) : (char *)0;
110 0 : return t;
111 : }
112 :
113 : void
114 0 : assoc_remove (hash, string)
115 : HASH_TABLE *hash;
116 : char *string;
117 : {
118 0 : BUCKET_CONTENTS *b;
119 :
120 0 : b = hash_remove (string, hash, 0);
121 0 : if (b)
122 : {
123 0 : free ((char *)b->data);
124 0 : free (b->key);
125 0 : free (b);
126 : }
127 0 : }
128 :
129 : char *
130 0 : assoc_reference (hash, string)
131 : HASH_TABLE *hash;
132 : char *string;
133 : {
134 0 : BUCKET_CONTENTS *b;
135 :
136 0 : if (hash == 0)
137 : return (char *)0;
138 :
139 0 : b = hash_search (string, hash, 0);
140 0 : return (b ? (char *)b->data : 0);
141 : }
142 :
143 : /* Quote the data associated with each element of the hash table ASSOC,
144 : using quote_string */
145 : HASH_TABLE *
146 0 : assoc_quote (h)
147 : HASH_TABLE *h;
148 : {
149 0 : int i;
150 0 : BUCKET_CONTENTS *tlist;
151 0 : char *t;
152 :
153 0 : if (h == 0 || assoc_empty (h))
154 : return ((HASH_TABLE *)NULL);
155 :
156 0 : for (i = 0; i < h->nbuckets; i++)
157 0 : for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
158 : {
159 0 : t = quote_string ((char *)tlist->data);
160 0 : FREE (tlist->data);
161 0 : tlist->data = t;
162 : }
163 :
164 : return h;
165 : }
166 :
167 : /* Quote escape characters in the data associated with each element
168 : of the hash table ASSOC, using quote_escapes */
169 : HASH_TABLE *
170 0 : assoc_quote_escapes (h)
171 : HASH_TABLE *h;
172 : {
173 0 : int i;
174 0 : BUCKET_CONTENTS *tlist;
175 0 : char *t;
176 :
177 0 : if (h == 0 || assoc_empty (h))
178 : return ((HASH_TABLE *)NULL);
179 :
180 0 : for (i = 0; i < h->nbuckets; i++)
181 0 : for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
182 : {
183 0 : t = quote_escapes ((char *)tlist->data);
184 0 : FREE (tlist->data);
185 0 : tlist->data = t;
186 : }
187 :
188 : return h;
189 : }
190 :
191 : HASH_TABLE *
192 0 : assoc_dequote (h)
193 : HASH_TABLE *h;
194 : {
195 0 : int i;
196 0 : BUCKET_CONTENTS *tlist;
197 0 : char *t;
198 :
199 0 : if (h == 0 || assoc_empty (h))
200 : return ((HASH_TABLE *)NULL);
201 :
202 0 : for (i = 0; i < h->nbuckets; i++)
203 0 : for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
204 : {
205 0 : t = dequote_string ((char *)tlist->data);
206 0 : FREE (tlist->data);
207 0 : tlist->data = t;
208 : }
209 :
210 : return h;
211 : }
212 :
213 : HASH_TABLE *
214 0 : assoc_dequote_escapes (h)
215 : HASH_TABLE *h;
216 : {
217 0 : int i;
218 0 : BUCKET_CONTENTS *tlist;
219 0 : char *t;
220 :
221 0 : if (h == 0 || assoc_empty (h))
222 : return ((HASH_TABLE *)NULL);
223 :
224 0 : for (i = 0; i < h->nbuckets; i++)
225 0 : for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
226 : {
227 0 : t = dequote_escapes ((char *)tlist->data);
228 0 : FREE (tlist->data);
229 0 : tlist->data = t;
230 : }
231 :
232 : return h;
233 : }
234 :
235 : HASH_TABLE *
236 0 : assoc_remove_quoted_nulls (h)
237 : HASH_TABLE *h;
238 : {
239 0 : int i;
240 0 : BUCKET_CONTENTS *tlist;
241 0 : char *t;
242 :
243 0 : if (h == 0 || assoc_empty (h))
244 : return ((HASH_TABLE *)NULL);
245 :
246 0 : for (i = 0; i < h->nbuckets; i++)
247 0 : for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
248 : {
249 0 : t = remove_quoted_nulls ((char *)tlist->data);
250 0 : tlist->data = t;
251 : }
252 :
253 : return h;
254 : }
255 :
256 : /*
257 : * Return a string whose elements are the members of array H beginning at
258 : * the STARTth element and spanning NELEM members. Null elements are counted.
259 : */
260 : char *
261 0 : assoc_subrange (hash, start, nelem, starsub, quoted)
262 : HASH_TABLE *hash;
263 : arrayind_t start, nelem;
264 : int starsub, quoted;
265 : {
266 0 : WORD_LIST *l, *save, *h, *t;
267 0 : int i, j;
268 0 : char *ret;
269 :
270 0 : if (assoc_empty (hash))
271 : return ((char *)NULL);
272 :
273 0 : save = l = assoc_to_word_list (hash);
274 0 : if (save == 0)
275 : return ((char *)NULL);
276 :
277 0 : for (i = 1; l && i < start; i++)
278 0 : l = l->next;
279 0 : if (l == 0)
280 : {
281 0 : dispose_words (save);
282 0 : return ((char *)NULL);
283 : }
284 0 : for (j = 0,h = t = l; l && j < nelem; j++)
285 : {
286 0 : t = l;
287 0 : l = l->next;
288 : }
289 :
290 0 : t->next = (WORD_LIST *)NULL;
291 :
292 0 : ret = string_list_pos_params (starsub ? '*' : '@', h, quoted);
293 :
294 0 : if (t != l)
295 0 : t->next = l;
296 :
297 0 : dispose_words (save);
298 0 : return (ret);
299 :
300 : }
301 :
302 : char *
303 0 : assoc_patsub (h, pat, rep, mflags)
304 : HASH_TABLE *h;
305 : char *pat, *rep;
306 : int mflags;
307 : {
308 0 : BUCKET_CONTENTS *tlist;
309 0 : int i, slen;
310 0 : HASH_TABLE *h2;
311 0 : char *t, *sifs, *ifs;
312 :
313 0 : if (h == 0 || assoc_empty (h))
314 : return ((char *)NULL);
315 :
316 0 : h2 = assoc_copy (h);
317 0 : for (i = 0; i < h2->nbuckets; i++)
318 0 : for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
319 : {
320 0 : t = pat_subst ((char *)tlist->data, pat, rep, mflags);
321 0 : FREE (tlist->data);
322 0 : tlist->data = t;
323 : }
324 :
325 0 : if (mflags & MATCH_QUOTED)
326 0 : assoc_quote (h2);
327 : else
328 0 : assoc_quote_escapes (h2);
329 :
330 0 : if (mflags & MATCH_STARSUB)
331 : {
332 0 : assoc_remove_quoted_nulls (h2);
333 0 : sifs = ifs_firstchar ((int *)NULL);
334 0 : t = assoc_to_string (h2, sifs, 0);
335 0 : free (sifs);
336 : }
337 0 : else if (mflags & MATCH_QUOTED)
338 : {
339 : /* ${array[@]} */
340 0 : sifs = ifs_firstchar (&slen);
341 0 : ifs = getifs ();
342 0 : if (ifs == 0 || *ifs == 0)
343 : {
344 0 : if (slen < 2)
345 0 : sifs = xrealloc (sifs, 2);
346 0 : sifs[0] = ' ';
347 0 : sifs[1] = '\0';
348 : }
349 0 : t = assoc_to_string (h2, sifs, 0);
350 0 : free(sifs);
351 : }
352 : else
353 0 : t = assoc_to_string (h2, " ", 0);
354 :
355 0 : assoc_dispose (h2);
356 :
357 0 : return t;
358 : }
359 :
360 : char *
361 0 : assoc_modcase (h, pat, modop, mflags)
362 : HASH_TABLE *h;
363 : char *pat;
364 : int modop;
365 : int mflags;
366 : {
367 0 : BUCKET_CONTENTS *tlist;
368 0 : int i, slen;
369 0 : HASH_TABLE *h2;
370 0 : char *t, *sifs, *ifs;
371 :
372 0 : if (h == 0 || assoc_empty (h))
373 : return ((char *)NULL);
374 :
375 0 : h2 = assoc_copy (h);
376 0 : for (i = 0; i < h2->nbuckets; i++)
377 0 : for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
378 : {
379 0 : t = sh_modcase ((char *)tlist->data, pat, modop);
380 0 : FREE (tlist->data);
381 0 : tlist->data = t;
382 : }
383 :
384 0 : if (mflags & MATCH_QUOTED)
385 0 : assoc_quote (h2);
386 : else
387 0 : assoc_quote_escapes (h2);
388 :
389 0 : if (mflags & MATCH_STARSUB)
390 : {
391 0 : assoc_remove_quoted_nulls (h2);
392 0 : sifs = ifs_firstchar ((int *)NULL);
393 0 : t = assoc_to_string (h2, sifs, 0);
394 0 : free (sifs);
395 : }
396 0 : else if (mflags & MATCH_QUOTED)
397 : {
398 : /* ${array[@]} */
399 0 : sifs = ifs_firstchar (&slen);
400 0 : ifs = getifs ();
401 0 : if (ifs == 0 || *ifs == 0)
402 : {
403 0 : if (slen < 2)
404 0 : sifs = xrealloc (sifs, 2);
405 0 : sifs[0] = ' ';
406 0 : sifs[1] = '\0';
407 : }
408 0 : t = assoc_to_string (h2, sifs, 0);
409 0 : free(sifs);
410 : }
411 : else
412 0 : t = assoc_to_string (h2, " ", 0);
413 :
414 0 : assoc_dispose (h2);
415 :
416 0 : return t;
417 : }
418 :
419 : char *
420 358 : assoc_to_assign (hash, quoted)
421 : HASH_TABLE *hash;
422 : int quoted;
423 : {
424 358 : char *ret;
425 358 : char *istr, *vstr;
426 358 : int i, rsize, rlen, elen;
427 358 : BUCKET_CONTENTS *tlist;
428 :
429 358 : if (hash == 0 || assoc_empty (hash))
430 : return (char *)0;
431 :
432 0 : ret = xmalloc (rsize = 128);
433 0 : ret[0] = '(';
434 0 : rlen = 1;
435 :
436 0 : for (i = 0; i < hash->nbuckets; i++)
437 0 : for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
438 : {
439 0 : if (ansic_shouldquote (tlist->key))
440 0 : istr = ansic_quote (tlist->key, 0, (int *)0);
441 0 : else if (sh_contains_shell_metas (tlist->key))
442 0 : istr = sh_double_quote (tlist->key);
443 0 : else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
444 0 : istr = sh_double_quote (tlist->key);
445 : else
446 : istr = tlist->key;
447 :
448 0 : vstr = tlist->data ? (ansic_shouldquote ((char *)tlist->data) ?
449 0 : ansic_quote ((char *)tlist->data, 0, (int *)0) :
450 0 : sh_double_quote ((char *)tlist->data))
451 0 : : (char *)0;
452 :
453 0 : elen = STRLEN (istr) + 8 + STRLEN (vstr);
454 0 : RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
455 :
456 0 : ret[rlen++] = '[';
457 0 : strcpy (ret+rlen, istr);
458 0 : rlen += STRLEN (istr);
459 0 : ret[rlen++] = ']';
460 0 : ret[rlen++] = '=';
461 0 : if (vstr)
462 : {
463 0 : strcpy (ret + rlen, vstr);
464 0 : rlen += STRLEN (vstr);
465 : }
466 0 : ret[rlen++] = ' ';
467 :
468 :
469 0 : if (istr != tlist->key)
470 0 : FREE (istr);
471 :
472 0 : FREE (vstr);
473 : }
474 :
475 0 : RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
476 0 : ret[rlen++] = ')';
477 0 : ret[rlen] = '\0';
478 :
479 0 : if (quoted)
480 : {
481 0 : vstr = sh_single_quote (ret);
482 0 : free (ret);
483 0 : ret = vstr;
484 : }
485 :
486 : return ret;
487 : }
488 :
489 : static WORD_LIST *
490 0 : assoc_to_word_list_internal (h, t)
491 : HASH_TABLE *h;
492 : int t;
493 : {
494 0 : WORD_LIST *list;
495 0 : int i;
496 0 : BUCKET_CONTENTS *tlist;
497 0 : char *w;
498 :
499 0 : if (h == 0 || assoc_empty (h))
500 : return((WORD_LIST *)NULL);
501 : list = (WORD_LIST *)NULL;
502 :
503 0 : for (i = 0; i < h->nbuckets; i++)
504 0 : for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
505 : {
506 0 : w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
507 0 : list = make_word_list (make_bare_word(w), list);
508 : }
509 0 : return (REVERSE_LIST(list, WORD_LIST *));
510 : }
511 :
512 : WORD_LIST *
513 0 : assoc_to_word_list (h)
514 : HASH_TABLE *h;
515 : {
516 0 : return (assoc_to_word_list_internal (h, 0));
517 : }
518 :
519 : WORD_LIST *
520 0 : assoc_keys_to_word_list (h)
521 : HASH_TABLE *h;
522 : {
523 0 : return (assoc_to_word_list_internal (h, 1));
524 : }
525 :
526 : char *
527 0 : assoc_to_string (h, sep, quoted)
528 : HASH_TABLE *h;
529 : char *sep;
530 : int quoted;
531 : {
532 0 : BUCKET_CONTENTS *tlist;
533 0 : int i;
534 0 : char *result, *t, *w;
535 0 : WORD_LIST *list, *l;
536 :
537 0 : if (h == 0)
538 : return ((char *)NULL);
539 0 : if (assoc_empty (h))
540 0 : return (savestring (""));
541 :
542 : result = NULL;
543 : l = list = NULL;
544 : /* This might be better implemented directly, but it's simple to implement
545 : by converting to a word list first, possibly quoting the data, then
546 : using list_string */
547 0 : for (i = 0; i < h->nbuckets; i++)
548 0 : for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
549 : {
550 0 : w = (char *)tlist->data;
551 0 : if (w == 0)
552 : continue;
553 0 : t = quoted ? quote_string (w) : savestring (w);
554 0 : list = make_word_list (make_bare_word(t), list);
555 0 : FREE (t);
556 : }
557 :
558 0 : l = REVERSE_LIST(list, WORD_LIST *);
559 :
560 0 : result = l ? string_list_internal (l, sep) : savestring ("");
561 0 : dispose_words (l);
562 :
563 0 : return result;
564 : }
565 :
566 : #endif /* ARRAY_VARS */
|