2 * assoc.c - functions to manipulate associative arrays
4 * Associative arrays are standard shell hash tables.
10 /* Copyright (C) 2008,2009,2011-2021 Free Software Foundation, Inc.
12 This file is part of GNU Bash, the Bourne Again SHell.
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.
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.
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/>.
30 #if defined (ARRAY_VARS)
32 #if defined (HAVE_UNISTD_H)
34 # include <sys/types.h>
45 #include "builtins/common.h"
47 static WORD_LIST
*assoc_to_word_list_internal
PARAMS((HASH_TABLE
*, int));
49 /* assoc_create == hash_create */
70 assoc_insert (hash
, key
, value
)
77 b
= hash_search (key
, hash
, HASH_CREATE
);
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. */
86 b
->data
= value
? savestring (value
) : (char *)0;
90 /* Like assoc_insert, but returns b->data instead of freeing it */
92 assoc_replace (hash
, key
, value
)
100 b
= hash_search (key
, hash
, HASH_CREATE
);
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. */
109 b
->data
= value
? savestring (value
) : (char *)0;
114 assoc_remove (hash
, string
)
120 b
= hash_remove (string
, hash
, 0);
123 free ((char *)b
->data
);
130 assoc_reference (hash
, string
)
139 b
= hash_search (string
, hash
, 0);
140 return (b
? (char *)b
->data
: 0);
143 /* Quote the data associated with each element of the hash table ASSOC,
144 using quote_string */
150 BUCKET_CONTENTS
*tlist
;
153 if (h
== 0 || assoc_empty (h
))
154 return ((HASH_TABLE
*)NULL
);
156 for (i
= 0; i
< h
->nbuckets
; i
++)
157 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
159 t
= quote_string ((char *)tlist
->data
);
167 /* Quote escape characters in the data associated with each element
168 of the hash table ASSOC, using quote_escapes */
170 assoc_quote_escapes (h
)
174 BUCKET_CONTENTS
*tlist
;
177 if (h
== 0 || assoc_empty (h
))
178 return ((HASH_TABLE
*)NULL
);
180 for (i
= 0; i
< h
->nbuckets
; i
++)
181 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
183 t
= quote_escapes ((char *)tlist
->data
);
196 BUCKET_CONTENTS
*tlist
;
199 if (h
== 0 || assoc_empty (h
))
200 return ((HASH_TABLE
*)NULL
);
202 for (i
= 0; i
< h
->nbuckets
; i
++)
203 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
205 t
= dequote_string ((char *)tlist
->data
);
214 assoc_dequote_escapes (h
)
218 BUCKET_CONTENTS
*tlist
;
221 if (h
== 0 || assoc_empty (h
))
222 return ((HASH_TABLE
*)NULL
);
224 for (i
= 0; i
< h
->nbuckets
; i
++)
225 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
227 t
= dequote_escapes ((char *)tlist
->data
);
236 assoc_remove_quoted_nulls (h
)
240 BUCKET_CONTENTS
*tlist
;
243 if (h
== 0 || assoc_empty (h
))
244 return ((HASH_TABLE
*)NULL
);
246 for (i
= 0; i
< h
->nbuckets
; i
++)
247 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
249 t
= remove_quoted_nulls ((char *)tlist
->data
);
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.
261 assoc_subrange (hash
, start
, nelem
, starsub
, quoted
, pflags
)
263 arrayind_t start
, nelem
;
264 int starsub
, quoted
, pflags
;
266 WORD_LIST
*l
, *save
, *h
, *t
;
270 if (assoc_empty (hash
))
271 return ((char *)NULL
);
273 save
= l
= assoc_to_word_list (hash
);
275 return ((char *)NULL
);
277 for (i
= 1; l
&& i
< start
; i
++)
281 dispose_words (save
);
282 return ((char *)NULL
);
284 for (j
= 0,h
= t
= l
; l
&& j
< nelem
; j
++)
290 t
->next
= (WORD_LIST
*)NULL
;
292 ret
= string_list_pos_params (starsub
? '*' : '@', h
, quoted
, pflags
);
297 dispose_words (save
);
303 assoc_patsub (h
, pat
, rep
, mflags
)
309 int pchar
, qflags
, pflags
;
310 WORD_LIST
*wl
, *save
;
312 if (h
== 0 || assoc_empty (h
))
313 return ((char *)NULL
);
315 wl
= assoc_to_word_list (h
);
319 for (save
= wl
; wl
; wl
= wl
->next
)
321 t
= pat_subst (wl
->word
->word
, pat
, rep
, mflags
);
322 FREE (wl
->word
->word
);
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;
330 t
= string_list_pos_params (pchar
, save
, qflags
, pflags
);
331 dispose_words (save
);
337 assoc_modcase (h
, pat
, modop
, mflags
)
344 int pchar
, qflags
, pflags
;
345 WORD_LIST
*wl
, *save
;
347 if (h
== 0 || assoc_empty (h
))
348 return ((char *)NULL
);
350 wl
= assoc_to_word_list (h
);
352 return ((char *)NULL
);
354 for (save
= wl
; wl
; wl
= wl
->next
)
356 t
= sh_modcase (wl
->word
->word
, pat
, modop
);
357 FREE (wl
->word
->word
);
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;
365 t
= string_list_pos_params (pchar
, save
, qflags
, pflags
);
366 dispose_words (save
);
372 assoc_to_kvpair (hash
, quoted
)
378 int i
, rsize
, rlen
, elen
;
379 BUCKET_CONTENTS
*tlist
;
381 if (hash
== 0 || assoc_empty (hash
))
384 ret
= xmalloc (rsize
= 128);
385 ret
[rlen
= 0] = '\0';
387 for (i
= 0; i
< hash
->nbuckets
; i
++)
388 for (tlist
= hash_items (i
, hash
); tlist
; tlist
= tlist
->next
)
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
);
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
))
404 elen
= STRLEN (istr
) + 4 + STRLEN (vstr
);
405 RESIZE_MALLOCED_BUFFER (ret
, rlen
, (elen
+1), rsize
, rsize
);
407 strcpy (ret
+rlen
, istr
);
408 rlen
+= STRLEN (istr
);
412 strcpy (ret
+ rlen
, vstr
);
413 rlen
+= STRLEN (vstr
);
417 strcpy (ret
+ rlen
, "\"\"");
422 if (istr
!= tlist
->key
)
428 RESIZE_MALLOCED_BUFFER (ret
, rlen
, 1, rsize
, 8);
433 vstr
= sh_single_quote (ret
);
442 assoc_to_assign (hash
, quoted
)
448 int i
, rsize
, rlen
, elen
;
449 BUCKET_CONTENTS
*tlist
;
451 if (hash
== 0 || assoc_empty (hash
))
454 ret
= xmalloc (rsize
= 128);
458 for (i
= 0; i
< hash
->nbuckets
; i
++)
459 for (tlist
= hash_items (i
, hash
); tlist
; tlist
= tlist
->next
)
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
);
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
))
475 elen
= STRLEN (istr
) + 8 + STRLEN (vstr
);
476 RESIZE_MALLOCED_BUFFER (ret
, rlen
, (elen
+1), rsize
, rsize
);
479 strcpy (ret
+rlen
, istr
);
480 rlen
+= STRLEN (istr
);
485 strcpy (ret
+ rlen
, vstr
);
486 rlen
+= STRLEN (vstr
);
490 if (istr
!= tlist
->key
)
496 RESIZE_MALLOCED_BUFFER (ret
, rlen
, 1, rsize
, 8);
502 vstr
= sh_single_quote (ret
);
511 assoc_to_word_list_internal (h
, t
)
517 BUCKET_CONTENTS
*tlist
;
520 if (h
== 0 || assoc_empty (h
))
521 return((WORD_LIST
*)NULL
);
522 list
= (WORD_LIST
*)NULL
;
524 for (i
= 0; i
< h
->nbuckets
; i
++)
525 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
527 w
= (t
== 0) ? (char *)tlist
->data
: (char *)tlist
->key
;
528 list
= make_word_list (make_bare_word(w
), list
);
530 return (REVERSE_LIST(list
, WORD_LIST
*));
534 assoc_to_word_list (h
)
537 return (assoc_to_word_list_internal (h
, 0));
541 assoc_keys_to_word_list (h
)
544 return (assoc_to_word_list_internal (h
, 1));
548 assoc_to_kvpair_list (h
)
553 BUCKET_CONTENTS
*tlist
;
556 if (h
== 0 || assoc_empty (h
))
557 return((WORD_LIST
*)NULL
);
558 list
= (WORD_LIST
*)NULL
;
560 for (i
= 0; i
< h
->nbuckets
; i
++)
561 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
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
);
568 return (REVERSE_LIST(list
, WORD_LIST
*));
572 assoc_to_string (h
, sep
, quoted
)
577 BUCKET_CONTENTS
*tlist
;
579 char *result
, *t
, *w
;
583 return ((char *)NULL
);
585 return (savestring (""));
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
592 for (i
= 0; i
< h
->nbuckets
; i
++)
593 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
595 w
= (char *)tlist
->data
;
598 t
= quoted
? quote_string (w
) : savestring (w
);
599 list
= make_word_list (make_bare_word(t
), list
);
603 l
= REVERSE_LIST(list
, WORD_LIST
*);
605 result
= l
? string_list_internal (l
, sep
) : savestring ("");
611 #endif /* ARRAY_VARS */