]> git.ipfire.org Git - thirdparty/bash.git/blame - assoc.c
Bash-5.2 patch 26: fix typo when specifying readline's custom color prefix
[thirdparty/bash.git] / assoc.c
CommitLineData
3185942a
JA
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
74091dd4 10/* Copyright (C) 2008,2009,2011-2021 Free Software Foundation, Inc.
3185942a
JA
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
8868edaf 47static WORD_LIST *assoc_to_word_list_internal PARAMS((HASH_TABLE *, int));
3185942a
JA
48
49/* assoc_create == hash_create */
50
51void
52assoc_dispose (hash)
53 HASH_TABLE *hash;
54{
55 if (hash)
56 {
57 hash_flush (hash, 0);
58 hash_dispose (hash);
59 }
60}
61
62void
63assoc_flush (hash)
64 HASH_TABLE *hash;
65{
66 hash_flush (hash, 0);
67}
ac50fbac 68
3185942a
JA
69int
70assoc_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;
47a76730
CR
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);
3185942a
JA
85 FREE (b->data);
86 b->data = value ? savestring (value) : (char *)0;
87 return (0);
88}
89
ac50fbac
CR
90/* Like assoc_insert, but returns b->data instead of freeing it */
91PTR_T
92assoc_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
3185942a
JA
113void
114assoc_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
129char *
130assoc_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 */
145HASH_TABLE *
146assoc_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 */
169HASH_TABLE *
170assoc_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
191HASH_TABLE *
192assoc_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
213HASH_TABLE *
214assoc_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
235HASH_TABLE *
236assoc_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 */
260char *
8868edaf
CR
261assoc_subrange (hash, start, nelem, starsub, quoted, pflags)
262 HASH_TABLE *hash;
263 arrayind_t start, nelem;
264 int starsub, quoted, pflags;
3185942a
JA
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)
a0c0a00f
CR
280 {
281 dispose_words (save);
282 return ((char *)NULL);
283 }
3185942a
JA
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
8868edaf 292 ret = string_list_pos_params (starsub ? '*' : '@', h, quoted, pflags);
3185942a
JA
293
294 if (t != l)
295 t->next = l;
296
297 dispose_words (save);
298 return (ret);
299
300}
301
302char *
303assoc_patsub (h, pat, rep, mflags)
304 HASH_TABLE *h;
305 char *pat, *rep;
306 int mflags;
307{
d233b485 308 char *t;
8868edaf 309 int pchar, qflags, pflags;
d233b485 310 WORD_LIST *wl, *save;
3185942a
JA
311
312 if (h == 0 || assoc_empty (h))
313 return ((char *)NULL);
314
d233b485
CR
315 wl = assoc_to_word_list (h);
316 if (wl == 0)
317 return (char *)NULL;
3185942a 318
d233b485 319 for (save = wl; wl; wl = wl->next)
3185942a 320 {
d233b485
CR
321 t = pat_subst (wl->word->word, pat, rep, mflags);
322 FREE (wl->word->word);
323 wl->word->word = t;
3185942a 324 }
3185942a 325
d233b485
CR
326 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
327 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
8868edaf 328 pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
d233b485 329
8868edaf 330 t = string_list_pos_params (pchar, save, qflags, pflags);
d233b485 331 dispose_words (save);
3185942a
JA
332
333 return t;
334}
335
336char *
337assoc_modcase (h, pat, modop, mflags)
338 HASH_TABLE *h;
339 char *pat;
340 int modop;
341 int mflags;
342{
d233b485 343 char *t;
8868edaf 344 int pchar, qflags, pflags;
d233b485 345 WORD_LIST *wl, *save;
3185942a
JA
346
347 if (h == 0 || assoc_empty (h))
348 return ((char *)NULL);
349
d233b485
CR
350 wl = assoc_to_word_list (h);
351 if (wl == 0)
352 return ((char *)NULL);
3185942a 353
d233b485 354 for (save = wl; wl; wl = wl->next)
3185942a 355 {
d233b485
CR
356 t = sh_modcase (wl->word->word, pat, modop);
357 FREE (wl->word->word);
358 wl->word->word = t;
3185942a 359 }
3185942a 360
d233b485
CR
361 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
362 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
8868edaf 363 pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
d233b485 364
8868edaf 365 t = string_list_pos_params (pchar, save, qflags, pflags);
d233b485 366 dispose_words (save);
3185942a
JA
367
368 return t;
369}
370
8868edaf
CR
371char *
372assoc_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
3185942a
JA
441char *
442assoc_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 {
a0c0a00f
CR
461 if (ansic_shouldquote (tlist->key))
462 istr = ansic_quote (tlist->key, 0, (int *)0);
463 else if (sh_contains_shell_metas (tlist->key))
0001803f 464 istr = sh_double_quote (tlist->key);
1cfd8d27
CR
465 else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
466 istr = sh_double_quote (tlist->key);
0001803f
CR
467 else
468 istr = tlist->key;
a0c0a00f
CR
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;
3185942a
JA
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
0001803f
CR
490 if (istr != tlist->key)
491 FREE (istr);
492
3185942a
JA
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
510static WORD_LIST *
511assoc_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
533WORD_LIST *
534assoc_to_word_list (h)
535 HASH_TABLE *h;
536{
537 return (assoc_to_word_list_internal (h, 0));
538}
539
540WORD_LIST *
541assoc_keys_to_word_list (h)
542 HASH_TABLE *h;
543{
544 return (assoc_to_word_list_internal (h, 1));
545}
546
74091dd4
CR
547WORD_LIST *
548assoc_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
3185942a
JA
571char *
572assoc_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;
ac50fbac 588 l = list = NULL;
3185942a
JA
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 ("");
ac50fbac
CR
606 dispose_words (l);
607
3185942a
JA
608 return result;
609}
610
611#endif /* ARRAY_VARS */