]> git.ipfire.org Git - thirdparty/bash.git/blob - assoc.c
Bash-5.2 patch 17: fix for optimizing forks when using the . builtin in a subshell
[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-2021 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 PARAMS((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, pflags)
262 HASH_TABLE *hash;
263 arrayind_t start, nelem;
264 int starsub, quoted, pflags;
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, pflags);
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 char *t;
309 int pchar, qflags, pflags;
310 WORD_LIST *wl, *save;
311
312 if (h == 0 || assoc_empty (h))
313 return ((char *)NULL);
314
315 wl = assoc_to_word_list (h);
316 if (wl == 0)
317 return (char *)NULL;
318
319 for (save = wl; wl; wl = wl->next)
320 {
321 t = pat_subst (wl->word->word, pat, rep, mflags);
322 FREE (wl->word->word);
323 wl->word->word = t;
324 }
325
326 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
327 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
328 pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
329
330 t = string_list_pos_params (pchar, save, qflags, pflags);
331 dispose_words (save);
332
333 return t;
334 }
335
336 char *
337 assoc_modcase (h, pat, modop, mflags)
338 HASH_TABLE *h;
339 char *pat;
340 int modop;
341 int mflags;
342 {
343 char *t;
344 int pchar, qflags, pflags;
345 WORD_LIST *wl, *save;
346
347 if (h == 0 || assoc_empty (h))
348 return ((char *)NULL);
349
350 wl = assoc_to_word_list (h);
351 if (wl == 0)
352 return ((char *)NULL);
353
354 for (save = wl; wl; wl = wl->next)
355 {
356 t = sh_modcase (wl->word->word, pat, modop);
357 FREE (wl->word->word);
358 wl->word->word = t;
359 }
360
361 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
362 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
363 pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
364
365 t = string_list_pos_params (pchar, save, qflags, pflags);
366 dispose_words (save);
367
368 return t;
369 }
370
371 char *
372 assoc_to_kvpair (hash, quoted)
373 HASH_TABLE *hash;
374 int quoted;
375 {
376 char *ret;
377 char *istr, *vstr;
378 int i, rsize, rlen, elen;
379 BUCKET_CONTENTS *tlist;
380
381 if (hash == 0 || assoc_empty (hash))
382 return (char *)0;
383
384 ret = xmalloc (rsize = 128);
385 ret[rlen = 0] = '\0';
386
387 for (i = 0; i < hash->nbuckets; i++)
388 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
389 {
390 if (ansic_shouldquote (tlist->key))
391 istr = ansic_quote (tlist->key, 0, (int *)0);
392 else if (sh_contains_shell_metas (tlist->key))
393 istr = sh_double_quote (tlist->key);
394 else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
395 istr = sh_double_quote (tlist->key);
396 else
397 istr = tlist->key;
398
399 vstr = tlist->data ? (ansic_shouldquote ((char *)tlist->data) ?
400 ansic_quote ((char *)tlist->data, 0, (int *)0) :
401 sh_double_quote ((char *)tlist->data))
402 : (char *)0;
403
404 elen = STRLEN (istr) + 4 + STRLEN (vstr);
405 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
406
407 strcpy (ret+rlen, istr);
408 rlen += STRLEN (istr);
409 ret[rlen++] = ' ';
410 if (vstr)
411 {
412 strcpy (ret + rlen, vstr);
413 rlen += STRLEN (vstr);
414 }
415 else
416 {
417 strcpy (ret + rlen, "\"\"");
418 rlen += 2;
419 }
420 ret[rlen++] = ' ';
421
422 if (istr != tlist->key)
423 FREE (istr);
424
425 FREE (vstr);
426 }
427
428 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
429 ret[rlen] = '\0';
430
431 if (quoted)
432 {
433 vstr = sh_single_quote (ret);
434 free (ret);
435 ret = vstr;
436 }
437
438 return ret;
439 }
440
441 char *
442 assoc_to_assign (hash, quoted)
443 HASH_TABLE *hash;
444 int quoted;
445 {
446 char *ret;
447 char *istr, *vstr;
448 int i, rsize, rlen, elen;
449 BUCKET_CONTENTS *tlist;
450
451 if (hash == 0 || assoc_empty (hash))
452 return (char *)0;
453
454 ret = xmalloc (rsize = 128);
455 ret[0] = '(';
456 rlen = 1;
457
458 for (i = 0; i < hash->nbuckets; i++)
459 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
460 {
461 if (ansic_shouldquote (tlist->key))
462 istr = ansic_quote (tlist->key, 0, (int *)0);
463 else if (sh_contains_shell_metas (tlist->key))
464 istr = sh_double_quote (tlist->key);
465 else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
466 istr = sh_double_quote (tlist->key);
467 else
468 istr = tlist->key;
469
470 vstr = tlist->data ? (ansic_shouldquote ((char *)tlist->data) ?
471 ansic_quote ((char *)tlist->data, 0, (int *)0) :
472 sh_double_quote ((char *)tlist->data))
473 : (char *)0;
474
475 elen = STRLEN (istr) + 8 + STRLEN (vstr);
476 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
477
478 ret[rlen++] = '[';
479 strcpy (ret+rlen, istr);
480 rlen += STRLEN (istr);
481 ret[rlen++] = ']';
482 ret[rlen++] = '=';
483 if (vstr)
484 {
485 strcpy (ret + rlen, vstr);
486 rlen += STRLEN (vstr);
487 }
488 ret[rlen++] = ' ';
489
490 if (istr != tlist->key)
491 FREE (istr);
492
493 FREE (vstr);
494 }
495
496 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
497 ret[rlen++] = ')';
498 ret[rlen] = '\0';
499
500 if (quoted)
501 {
502 vstr = sh_single_quote (ret);
503 free (ret);
504 ret = vstr;
505 }
506
507 return ret;
508 }
509
510 static WORD_LIST *
511 assoc_to_word_list_internal (h, t)
512 HASH_TABLE *h;
513 int t;
514 {
515 WORD_LIST *list;
516 int i;
517 BUCKET_CONTENTS *tlist;
518 char *w;
519
520 if (h == 0 || assoc_empty (h))
521 return((WORD_LIST *)NULL);
522 list = (WORD_LIST *)NULL;
523
524 for (i = 0; i < h->nbuckets; i++)
525 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
526 {
527 w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
528 list = make_word_list (make_bare_word(w), list);
529 }
530 return (REVERSE_LIST(list, WORD_LIST *));
531 }
532
533 WORD_LIST *
534 assoc_to_word_list (h)
535 HASH_TABLE *h;
536 {
537 return (assoc_to_word_list_internal (h, 0));
538 }
539
540 WORD_LIST *
541 assoc_keys_to_word_list (h)
542 HASH_TABLE *h;
543 {
544 return (assoc_to_word_list_internal (h, 1));
545 }
546
547 WORD_LIST *
548 assoc_to_kvpair_list (h)
549 HASH_TABLE *h;
550 {
551 WORD_LIST *list;
552 int i;
553 BUCKET_CONTENTS *tlist;
554 char *k, *v;
555
556 if (h == 0 || assoc_empty (h))
557 return((WORD_LIST *)NULL);
558 list = (WORD_LIST *)NULL;
559
560 for (i = 0; i < h->nbuckets; i++)
561 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
562 {
563 k = (char *)tlist->key;
564 v = (char *)tlist->data;
565 list = make_word_list (make_bare_word (k), list);
566 list = make_word_list (make_bare_word (v), list);
567 }
568 return (REVERSE_LIST(list, WORD_LIST *));
569 }
570
571 char *
572 assoc_to_string (h, sep, quoted)
573 HASH_TABLE *h;
574 char *sep;
575 int quoted;
576 {
577 BUCKET_CONTENTS *tlist;
578 int i;
579 char *result, *t, *w;
580 WORD_LIST *list, *l;
581
582 if (h == 0)
583 return ((char *)NULL);
584 if (assoc_empty (h))
585 return (savestring (""));
586
587 result = NULL;
588 l = list = NULL;
589 /* This might be better implemented directly, but it's simple to implement
590 by converting to a word list first, possibly quoting the data, then
591 using list_string */
592 for (i = 0; i < h->nbuckets; i++)
593 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
594 {
595 w = (char *)tlist->data;
596 if (w == 0)
597 continue;
598 t = quoted ? quote_string (w) : savestring (w);
599 list = make_word_list (make_bare_word(t), list);
600 FREE (t);
601 }
602
603 l = REVERSE_LIST(list, WORD_LIST *);
604
605 result = l ? string_list_internal (l, sep) : savestring ("");
606 dispose_words (l);
607
608 return result;
609 }
610
611 #endif /* ARRAY_VARS */