]> git.ipfire.org Git - thirdparty/bash.git/blob - assoc.c
fix for SIGINT in sourced script
[thirdparty/bash.git] / assoc.c
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 assoc_dispose (hash)
53 HASH_TABLE *hash;
54 {
55 if (hash)
56 {
57 hash_flush (hash, 0);
58 hash_dispose (hash);
59 }
60 }
61
62 void
63 assoc_flush (hash)
64 HASH_TABLE *hash;
65 {
66 hash_flush (hash, 0);
67 }
68
69 int
70 assoc_insert (hash, key, value)
71 HASH_TABLE *hash;
72 char *key;
73 char *value;
74 {
75 BUCKET_CONTENTS *b;
76
77 b = hash_search (key, hash, HASH_CREATE);
78 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 if (b->key != key)
84 free (key);
85 FREE (b->data);
86 b->data = value ? savestring (value) : (char *)0;
87 return (0);
88 }
89
90 /* Like assoc_insert, but returns b->data instead of freeing it */
91 PTR_T
92 assoc_replace (hash, key, value)
93 HASH_TABLE *hash;
94 char *key;
95 char *value;
96 {
97 BUCKET_CONTENTS *b;
98 PTR_T t;
99
100 b = hash_search (key, hash, HASH_CREATE);
101 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 if (b->key != key)
107 free (key);
108 t = b->data;
109 b->data = value ? savestring (value) : (char *)0;
110 return t;
111 }
112
113 void
114 assoc_remove (hash, string)
115 HASH_TABLE *hash;
116 char *string;
117 {
118 BUCKET_CONTENTS *b;
119
120 b = hash_remove (string, hash, 0);
121 if (b)
122 {
123 free ((char *)b->data);
124 free (b->key);
125 free (b);
126 }
127 }
128
129 char *
130 assoc_reference (hash, string)
131 HASH_TABLE *hash;
132 char *string;
133 {
134 BUCKET_CONTENTS *b;
135
136 if (hash == 0)
137 return (char *)0;
138
139 b = hash_search (string, hash, 0);
140 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 assoc_quote (h)
147 HASH_TABLE *h;
148 {
149 int i;
150 BUCKET_CONTENTS *tlist;
151 char *t;
152
153 if (h == 0 || assoc_empty (h))
154 return ((HASH_TABLE *)NULL);
155
156 for (i = 0; i < h->nbuckets; i++)
157 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
158 {
159 t = quote_string ((char *)tlist->data);
160 FREE (tlist->data);
161 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 assoc_quote_escapes (h)
171 HASH_TABLE *h;
172 {
173 int i;
174 BUCKET_CONTENTS *tlist;
175 char *t;
176
177 if (h == 0 || assoc_empty (h))
178 return ((HASH_TABLE *)NULL);
179
180 for (i = 0; i < h->nbuckets; i++)
181 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
182 {
183 t = quote_escapes ((char *)tlist->data);
184 FREE (tlist->data);
185 tlist->data = t;
186 }
187
188 return h;
189 }
190
191 HASH_TABLE *
192 assoc_dequote (h)
193 HASH_TABLE *h;
194 {
195 int i;
196 BUCKET_CONTENTS *tlist;
197 char *t;
198
199 if (h == 0 || assoc_empty (h))
200 return ((HASH_TABLE *)NULL);
201
202 for (i = 0; i < h->nbuckets; i++)
203 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
204 {
205 t = dequote_string ((char *)tlist->data);
206 FREE (tlist->data);
207 tlist->data = t;
208 }
209
210 return h;
211 }
212
213 HASH_TABLE *
214 assoc_dequote_escapes (h)
215 HASH_TABLE *h;
216 {
217 int i;
218 BUCKET_CONTENTS *tlist;
219 char *t;
220
221 if (h == 0 || assoc_empty (h))
222 return ((HASH_TABLE *)NULL);
223
224 for (i = 0; i < h->nbuckets; i++)
225 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
226 {
227 t = dequote_escapes ((char *)tlist->data);
228 FREE (tlist->data);
229 tlist->data = t;
230 }
231
232 return h;
233 }
234
235 HASH_TABLE *
236 assoc_remove_quoted_nulls (h)
237 HASH_TABLE *h;
238 {
239 int i;
240 BUCKET_CONTENTS *tlist;
241 char *t;
242
243 if (h == 0 || assoc_empty (h))
244 return ((HASH_TABLE *)NULL);
245
246 for (i = 0; i < h->nbuckets; i++)
247 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
248 {
249 t = remove_quoted_nulls ((char *)tlist->data);
250 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 assoc_subrange (hash, start, nelem, starsub, quoted)
262 HASH_TABLE *hash;
263 arrayind_t start, nelem;
264 int starsub, quoted;
265 {
266 WORD_LIST *l, *save, *h, *t;
267 int i, j;
268 char *ret;
269
270 if (assoc_empty (hash))
271 return ((char *)NULL);
272
273 save = l = assoc_to_word_list (hash);
274 if (save == 0)
275 return ((char *)NULL);
276
277 for (i = 1; l && i < start; i++)
278 l = l->next;
279 if (l == 0)
280 {
281 dispose_words (save);
282 return ((char *)NULL);
283 }
284 for (j = 0,h = t = l; l && j < nelem; j++)
285 {
286 t = l;
287 l = l->next;
288 }
289
290 t->next = (WORD_LIST *)NULL;
291
292 ret = string_list_pos_params (starsub ? '*' : '@', h, quoted);
293
294 if (t != l)
295 t->next = l;
296
297 dispose_words (save);
298 return (ret);
299
300 }
301
302 char *
303 assoc_patsub (h, pat, rep, mflags)
304 HASH_TABLE *h;
305 char *pat, *rep;
306 int mflags;
307 {
308 BUCKET_CONTENTS *tlist;
309 int i, slen;
310 HASH_TABLE *h2;
311 char *t, *sifs, *ifs;
312
313 if (h == 0 || assoc_empty (h))
314 return ((char *)NULL);
315
316 h2 = assoc_copy (h);
317 for (i = 0; i < h2->nbuckets; i++)
318 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
319 {
320 t = pat_subst ((char *)tlist->data, pat, rep, mflags);
321 FREE (tlist->data);
322 tlist->data = t;
323 }
324
325 if (mflags & MATCH_QUOTED)
326 assoc_quote (h2);
327 else
328 assoc_quote_escapes (h2);
329
330 if (mflags & MATCH_STARSUB)
331 {
332 assoc_remove_quoted_nulls (h2);
333 sifs = ifs_firstchar ((int *)NULL);
334 t = assoc_to_string (h2, sifs, 0);
335 free (sifs);
336 }
337 else if (mflags & MATCH_QUOTED)
338 {
339 /* ${array[@]} */
340 sifs = ifs_firstchar (&slen);
341 ifs = getifs ();
342 if (ifs == 0 || *ifs == 0)
343 {
344 if (slen < 2)
345 sifs = xrealloc (sifs, 2);
346 sifs[0] = ' ';
347 sifs[1] = '\0';
348 }
349 t = assoc_to_string (h2, sifs, 0);
350 free(sifs);
351 }
352 else
353 t = assoc_to_string (h2, " ", 0);
354
355 assoc_dispose (h2);
356
357 return t;
358 }
359
360 char *
361 assoc_modcase (h, pat, modop, mflags)
362 HASH_TABLE *h;
363 char *pat;
364 int modop;
365 int mflags;
366 {
367 BUCKET_CONTENTS *tlist;
368 int i, slen;
369 HASH_TABLE *h2;
370 char *t, *sifs, *ifs;
371
372 if (h == 0 || assoc_empty (h))
373 return ((char *)NULL);
374
375 h2 = assoc_copy (h);
376 for (i = 0; i < h2->nbuckets; i++)
377 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
378 {
379 t = sh_modcase ((char *)tlist->data, pat, modop);
380 FREE (tlist->data);
381 tlist->data = t;
382 }
383
384 if (mflags & MATCH_QUOTED)
385 assoc_quote (h2);
386 else
387 assoc_quote_escapes (h2);
388
389 if (mflags & MATCH_STARSUB)
390 {
391 assoc_remove_quoted_nulls (h2);
392 sifs = ifs_firstchar ((int *)NULL);
393 t = assoc_to_string (h2, sifs, 0);
394 free (sifs);
395 }
396 else if (mflags & MATCH_QUOTED)
397 {
398 /* ${array[@]} */
399 sifs = ifs_firstchar (&slen);
400 ifs = getifs ();
401 if (ifs == 0 || *ifs == 0)
402 {
403 if (slen < 2)
404 sifs = xrealloc (sifs, 2);
405 sifs[0] = ' ';
406 sifs[1] = '\0';
407 }
408 t = assoc_to_string (h2, sifs, 0);
409 free(sifs);
410 }
411 else
412 t = assoc_to_string (h2, " ", 0);
413
414 assoc_dispose (h2);
415
416 return t;
417 }
418
419 char *
420 assoc_to_assign (hash, quoted)
421 HASH_TABLE *hash;
422 int quoted;
423 {
424 char *ret;
425 char *istr, *vstr;
426 int i, rsize, rlen, elen;
427 BUCKET_CONTENTS *tlist;
428
429 if (hash == 0 || assoc_empty (hash))
430 return (char *)0;
431
432 ret = xmalloc (rsize = 128);
433 ret[0] = '(';
434 rlen = 1;
435
436 for (i = 0; i < hash->nbuckets; i++)
437 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
438 {
439 if (ansic_shouldquote (tlist->key))
440 istr = ansic_quote (tlist->key, 0, (int *)0);
441 else if (sh_contains_shell_metas (tlist->key))
442 istr = sh_double_quote (tlist->key);
443 else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
444 istr = sh_double_quote (tlist->key);
445 else
446 istr = tlist->key;
447
448 vstr = tlist->data ? (ansic_shouldquote ((char *)tlist->data) ?
449 ansic_quote ((char *)tlist->data, 0, (int *)0) :
450 sh_double_quote ((char *)tlist->data))
451 : (char *)0;
452
453 elen = STRLEN (istr) + 8 + STRLEN (vstr);
454 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
455
456 ret[rlen++] = '[';
457 strcpy (ret+rlen, istr);
458 rlen += STRLEN (istr);
459 ret[rlen++] = ']';
460 ret[rlen++] = '=';
461 if (vstr)
462 {
463 strcpy (ret + rlen, vstr);
464 rlen += STRLEN (vstr);
465 }
466 ret[rlen++] = ' ';
467
468
469 if (istr != tlist->key)
470 FREE (istr);
471
472 FREE (vstr);
473 }
474
475 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
476 ret[rlen++] = ')';
477 ret[rlen] = '\0';
478
479 if (quoted)
480 {
481 vstr = sh_single_quote (ret);
482 free (ret);
483 ret = vstr;
484 }
485
486 return ret;
487 }
488
489 static WORD_LIST *
490 assoc_to_word_list_internal (h, t)
491 HASH_TABLE *h;
492 int t;
493 {
494 WORD_LIST *list;
495 int i;
496 BUCKET_CONTENTS *tlist;
497 char *w;
498
499 if (h == 0 || assoc_empty (h))
500 return((WORD_LIST *)NULL);
501 list = (WORD_LIST *)NULL;
502
503 for (i = 0; i < h->nbuckets; i++)
504 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
505 {
506 w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
507 list = make_word_list (make_bare_word(w), list);
508 }
509 return (REVERSE_LIST(list, WORD_LIST *));
510 }
511
512 WORD_LIST *
513 assoc_to_word_list (h)
514 HASH_TABLE *h;
515 {
516 return (assoc_to_word_list_internal (h, 0));
517 }
518
519 WORD_LIST *
520 assoc_keys_to_word_list (h)
521 HASH_TABLE *h;
522 {
523 return (assoc_to_word_list_internal (h, 1));
524 }
525
526 char *
527 assoc_to_string (h, sep, quoted)
528 HASH_TABLE *h;
529 char *sep;
530 int quoted;
531 {
532 BUCKET_CONTENTS *tlist;
533 int i;
534 char *result, *t, *w;
535 WORD_LIST *list, *l;
536
537 if (h == 0)
538 return ((char *)NULL);
539 if (assoc_empty (h))
540 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 for (i = 0; i < h->nbuckets; i++)
548 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
549 {
550 w = (char *)tlist->data;
551 if (w == 0)
552 continue;
553 t = quoted ? quote_string (w) : savestring (w);
554 list = make_word_list (make_bare_word(t), list);
555 FREE (t);
556 }
557
558 l = REVERSE_LIST(list, WORD_LIST *);
559
560 result = l ? string_list_internal (l, sep) : savestring ("");
561 dispose_words (l);
562
563 return result;
564 }
565
566 #endif /* ARRAY_VARS */