]> git.ipfire.org Git - thirdparty/bash.git/blob - assoc.c
fix for internal redirection flags colliding with open/fcntl flags; call memfd_create...
[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-2023 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 (HASH_TABLE *, int);
48
49 /* assoc_create == hash_create */
50
51 void
52 assoc_dispose (HASH_TABLE *hash)
53 {
54 if (hash)
55 {
56 hash_flush (hash, 0);
57 hash_dispose (hash);
58 }
59 }
60
61 void
62 assoc_flush (HASH_TABLE *hash)
63 {
64 hash_flush (hash, 0);
65 }
66
67 int
68 assoc_insert (HASH_TABLE *hash, char *key, char *value)
69 {
70 BUCKET_CONTENTS *b;
71
72 b = hash_search (key, hash, HASH_CREATE);
73 if (b == 0)
74 return -1;
75 /* If we are overwriting an existing element's value, we're not going to
76 use the key. Nothing in the array assignment code path frees the key
77 string, so we can free it here to avoid a memory leak. */
78 if (b->key != key)
79 free (key);
80 FREE (b->data);
81 b->data = value ? savestring (value) : (char *)0;
82 return (0);
83 }
84
85 /* Like assoc_insert, but returns b->data instead of freeing it */
86 PTR_T
87 assoc_replace (HASH_TABLE *hash, char *key, char *value)
88 {
89 BUCKET_CONTENTS *b;
90 PTR_T t;
91
92 b = hash_search (key, hash, HASH_CREATE);
93 if (b == 0)
94 return (PTR_T)0;
95 /* If we are overwriting an existing element's value, we're not going to
96 use the key. Nothing in the array assignment code path frees the key
97 string, so we can free it here to avoid a memory leak. */
98 if (b->key != key)
99 free (key);
100 t = b->data;
101 b->data = value ? savestring (value) : (char *)0;
102 return t;
103 }
104
105 void
106 assoc_remove (HASH_TABLE *hash, const char *string)
107 {
108 BUCKET_CONTENTS *b;
109
110 b = hash_remove (string, hash, 0);
111 if (b)
112 {
113 free ((char *)b->data);
114 free (b->key);
115 free (b);
116 }
117 }
118
119 char *
120 assoc_reference (HASH_TABLE *hash, const char *string)
121 {
122 BUCKET_CONTENTS *b;
123
124 if (hash == 0)
125 return (char *)0;
126
127 b = hash_search (string, hash, 0);
128 return (b ? (char *)b->data : 0);
129 }
130
131 /* Quote the data associated with each element of the hash table ASSOC,
132 using quote_string */
133 HASH_TABLE *
134 assoc_quote (HASH_TABLE *h)
135 {
136 int i;
137 BUCKET_CONTENTS *tlist;
138 char *t;
139
140 if (h == 0 || assoc_empty (h))
141 return ((HASH_TABLE *)NULL);
142
143 for (i = 0; i < h->nbuckets; i++)
144 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
145 {
146 t = quote_string ((char *)tlist->data);
147 FREE (tlist->data);
148 tlist->data = t;
149 }
150
151 return h;
152 }
153
154 /* Quote escape characters in the data associated with each element
155 of the hash table ASSOC, using quote_escapes */
156 HASH_TABLE *
157 assoc_quote_escapes (HASH_TABLE *h)
158 {
159 int i;
160 BUCKET_CONTENTS *tlist;
161 char *t;
162
163 if (h == 0 || assoc_empty (h))
164 return ((HASH_TABLE *)NULL);
165
166 for (i = 0; i < h->nbuckets; i++)
167 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
168 {
169 t = quote_escapes ((char *)tlist->data);
170 FREE (tlist->data);
171 tlist->data = t;
172 }
173
174 return h;
175 }
176
177 HASH_TABLE *
178 assoc_dequote (HASH_TABLE *h)
179 {
180 int i;
181 BUCKET_CONTENTS *tlist;
182 char *t;
183
184 if (h == 0 || assoc_empty (h))
185 return ((HASH_TABLE *)NULL);
186
187 for (i = 0; i < h->nbuckets; i++)
188 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
189 {
190 t = dequote_string ((char *)tlist->data);
191 FREE (tlist->data);
192 tlist->data = t;
193 }
194
195 return h;
196 }
197
198 HASH_TABLE *
199 assoc_dequote_escapes (HASH_TABLE *h)
200 {
201 int i;
202 BUCKET_CONTENTS *tlist;
203 char *t;
204
205 if (h == 0 || assoc_empty (h))
206 return ((HASH_TABLE *)NULL);
207
208 for (i = 0; i < h->nbuckets; i++)
209 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
210 {
211 t = dequote_escapes ((char *)tlist->data);
212 FREE (tlist->data);
213 tlist->data = t;
214 }
215
216 return h;
217 }
218
219 HASH_TABLE *
220 assoc_remove_quoted_nulls (HASH_TABLE *h)
221 {
222 int i;
223 BUCKET_CONTENTS *tlist;
224 char *t;
225
226 if (h == 0 || assoc_empty (h))
227 return ((HASH_TABLE *)NULL);
228
229 for (i = 0; i < h->nbuckets; i++)
230 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
231 {
232 t = remove_quoted_nulls ((char *)tlist->data);
233 tlist->data = t;
234 }
235
236 return h;
237 }
238
239 /*
240 * Return a string whose elements are the members of array H beginning at
241 * the STARTth element and spanning NELEM members. Null elements are counted.
242 */
243 char *
244 assoc_subrange (HASH_TABLE *hash, arrayind_t start, arrayind_t nelem, int starsub, int quoted, int pflags)
245 {
246 WORD_LIST *l, *save, *h, *t;
247 int i, j;
248 char *ret;
249
250 if (assoc_empty (hash))
251 return ((char *)NULL);
252
253 save = l = assoc_to_word_list (hash);
254 if (save == 0)
255 return ((char *)NULL);
256
257 for (i = 1; l && i < start; i++)
258 l = l->next;
259 if (l == 0)
260 {
261 dispose_words (save);
262 return ((char *)NULL);
263 }
264 for (j = 0,h = t = l; l && j < nelem; j++)
265 {
266 t = l;
267 l = l->next;
268 }
269
270 t->next = (WORD_LIST *)NULL;
271
272 ret = string_list_pos_params (starsub ? '*' : '@', h, quoted, pflags);
273
274 if (t != l)
275 t->next = l;
276
277 dispose_words (save);
278 return (ret);
279 }
280
281 /* Substitute REP for each match of PAT in each element of hash table H,
282 qualified by FLAGS to say what kind of quoting to do. */
283 char *
284 assoc_patsub (HASH_TABLE *h, char *pat, char *rep, int mflags)
285 {
286 char *t;
287 int pchar, qflags, pflags;
288 WORD_LIST *wl, *save;
289
290 if (h == 0 || assoc_empty (h))
291 return ((char *)NULL);
292
293 wl = assoc_to_word_list (h);
294 if (wl == 0)
295 return (char *)NULL;
296
297 for (save = wl; wl; wl = wl->next)
298 {
299 t = pat_subst (wl->word->word, pat, rep, mflags);
300 FREE (wl->word->word);
301 wl->word->word = t;
302 }
303
304 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
305 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
306 pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
307
308 t = string_list_pos_params (pchar, save, qflags, pflags);
309 dispose_words (save);
310
311 return t;
312 }
313
314 char *
315 assoc_modcase (HASH_TABLE *h, char *pat, int modop, int mflags)
316 {
317 char *t;
318 int pchar, qflags, pflags;
319 WORD_LIST *wl, *save;
320
321 if (h == 0 || assoc_empty (h))
322 return ((char *)NULL);
323
324 wl = assoc_to_word_list (h);
325 if (wl == 0)
326 return ((char *)NULL);
327
328 for (save = wl; wl; wl = wl->next)
329 {
330 t = sh_modcase (wl->word->word, pat, modop);
331 FREE (wl->word->word);
332 wl->word->word = t;
333 }
334
335 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
336 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
337 pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
338
339 t = string_list_pos_params (pchar, save, qflags, pflags);
340 dispose_words (save);
341
342 return t;
343 }
344
345 char *
346 assoc_to_kvpair (HASH_TABLE *hash, int quoted)
347 {
348 char *ret;
349 char *istr, *vstr;
350 int i, rsize, rlen, elen;
351 BUCKET_CONTENTS *tlist;
352
353 if (hash == 0 || assoc_empty (hash))
354 return (char *)0;
355
356 ret = xmalloc (rsize = 128);
357 ret[rlen = 0] = '\0';
358
359 for (i = 0; i < hash->nbuckets; i++)
360 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
361 {
362 if (ansic_shouldquote (tlist->key))
363 istr = ansic_quote (tlist->key, 0, (int *)0);
364 else if (sh_contains_shell_metas (tlist->key))
365 istr = sh_double_quote (tlist->key);
366 else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
367 istr = sh_double_quote (tlist->key);
368 else
369 istr = tlist->key;
370
371 vstr = tlist->data ? (ansic_shouldquote ((char *)tlist->data) ?
372 ansic_quote ((char *)tlist->data, 0, (int *)0) :
373 sh_double_quote ((char *)tlist->data))
374 : (char *)0;
375
376 elen = STRLEN (istr) + 4 + STRLEN (vstr);
377 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
378
379 strcpy (ret+rlen, istr);
380 rlen += STRLEN (istr);
381 ret[rlen++] = ' ';
382 if (vstr)
383 {
384 strcpy (ret + rlen, vstr);
385 rlen += STRLEN (vstr);
386 }
387 else
388 {
389 strcpy (ret + rlen, "\"\"");
390 rlen += 2;
391 }
392 ret[rlen++] = ' ';
393
394 if (istr != tlist->key)
395 FREE (istr);
396
397 FREE (vstr);
398 }
399
400 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
401 ret[rlen] = '\0';
402
403 if (quoted)
404 {
405 vstr = sh_single_quote (ret);
406 free (ret);
407 ret = vstr;
408 }
409
410 return ret;
411 }
412
413 char *
414 assoc_to_assign (HASH_TABLE *hash, int quoted)
415 {
416 char *ret;
417 char *istr, *vstr;
418 int i, rsize, rlen, elen;
419 BUCKET_CONTENTS *tlist;
420
421 if (hash == 0 || assoc_empty (hash))
422 return (char *)0;
423
424 ret = xmalloc (rsize = 128);
425 ret[0] = '(';
426 rlen = 1;
427
428 for (i = 0; i < hash->nbuckets; i++)
429 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
430 {
431 if (ansic_shouldquote (tlist->key))
432 istr = ansic_quote (tlist->key, 0, (int *)0);
433 else if (sh_contains_shell_metas (tlist->key))
434 istr = sh_double_quote (tlist->key);
435 else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
436 istr = sh_double_quote (tlist->key);
437 else
438 istr = tlist->key;
439
440 vstr = tlist->data ? (ansic_shouldquote ((char *)tlist->data) ?
441 ansic_quote ((char *)tlist->data, 0, (int *)0) :
442 sh_double_quote ((char *)tlist->data))
443 : (char *)0;
444
445 elen = STRLEN (istr) + 8 + STRLEN (vstr);
446 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
447
448 ret[rlen++] = '[';
449 strcpy (ret+rlen, istr);
450 rlen += STRLEN (istr);
451 ret[rlen++] = ']';
452 ret[rlen++] = '=';
453 if (vstr)
454 {
455 strcpy (ret + rlen, vstr);
456 rlen += STRLEN (vstr);
457 }
458 ret[rlen++] = ' ';
459
460 if (istr != tlist->key)
461 FREE (istr);
462
463 FREE (vstr);
464 }
465
466 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
467 ret[rlen++] = ')';
468 ret[rlen] = '\0';
469
470 if (quoted)
471 {
472 vstr = sh_single_quote (ret);
473 free (ret);
474 ret = vstr;
475 }
476
477 return ret;
478 }
479
480 static WORD_LIST *
481 assoc_to_word_list_internal (HASH_TABLE *h, int t)
482 {
483 WORD_LIST *list;
484 int i;
485 BUCKET_CONTENTS *tlist;
486 char *w;
487
488 if (h == 0 || assoc_empty (h))
489 return((WORD_LIST *)NULL);
490 list = (WORD_LIST *)NULL;
491
492 for (i = 0; i < h->nbuckets; i++)
493 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
494 {
495 w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
496 list = make_word_list (make_bare_word(w), list);
497 }
498 return (REVERSE_LIST(list, WORD_LIST *));
499 }
500
501 WORD_LIST *
502 assoc_to_word_list (HASH_TABLE *h)
503 {
504 return (assoc_to_word_list_internal (h, 0));
505 }
506
507 WORD_LIST *
508 assoc_keys_to_word_list (HASH_TABLE *h)
509 {
510 return (assoc_to_word_list_internal (h, 1));
511 }
512
513 WORD_LIST *
514 assoc_to_kvpair_list (HASH_TABLE *h)
515 {
516 WORD_LIST *list;
517 int i;
518 BUCKET_CONTENTS *tlist;
519 char *k, *v;
520
521 if (h == 0 || assoc_empty (h))
522 return((WORD_LIST *)NULL);
523 list = (WORD_LIST *)NULL;
524
525 for (i = 0; i < h->nbuckets; i++)
526 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
527 {
528 k = (char *)tlist->key;
529 v = (char *)tlist->data;
530 list = make_word_list (make_bare_word (k), list);
531 list = make_word_list (make_bare_word (v), list);
532 }
533 return (REVERSE_LIST(list, WORD_LIST *));
534 }
535
536 char *
537 assoc_to_string (HASH_TABLE *h, char *sep, int quoted)
538 {
539 BUCKET_CONTENTS *tlist;
540 int i;
541 char *result, *t, *w;
542 WORD_LIST *list, *l;
543
544 if (h == 0)
545 return ((char *)NULL);
546 if (assoc_empty (h))
547 return (savestring (""));
548
549 result = NULL;
550 l = list = NULL;
551 /* This might be better implemented directly, but it's simple to implement
552 by converting to a word list first, possibly quoting the data, then
553 using list_string */
554 for (i = 0; i < h->nbuckets; i++)
555 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
556 {
557 w = (char *)tlist->data;
558 if (w == 0)
559 continue;
560 t = quoted ? quote_string (w) : savestring (w);
561 list = make_word_list (make_bare_word(t), list);
562 FREE (t);
563 }
564
565 l = REVERSE_LIST(list, WORD_LIST *);
566
567 result = l ? string_list_internal (l, sep) : savestring ("");
568 dispose_words (l);
569
570 return result;
571 }
572
573 #endif /* ARRAY_VARS */