]>
git.ipfire.org Git - thirdparty/bash.git/blob - assoc.c
2 * assoc.c - functions to manipulate associative arrays
4 * Associative arrays are standard shell hash tables.
10 /* Copyright (C) 2008,2009,2011-2023 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 (HASH_TABLE
*, int);
49 /* assoc_create == hash_create */
52 assoc_dispose (HASH_TABLE
*hash
)
62 assoc_flush (HASH_TABLE
*hash
)
68 assoc_insert (HASH_TABLE
*hash
, char *key
, char *value
)
72 b
= hash_search (key
, hash
, HASH_CREATE
);
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. */
81 b
->data
= value
? savestring (value
) : (char *)0;
85 /* Like assoc_insert, but returns b->data instead of freeing it */
87 assoc_replace (HASH_TABLE
*hash
, char *key
, char *value
)
92 b
= hash_search (key
, hash
, HASH_CREATE
);
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. */
101 b
->data
= value
? savestring (value
) : (char *)0;
106 assoc_remove (HASH_TABLE
*hash
, const char *string
)
110 b
= hash_remove (string
, hash
, 0);
113 free ((char *)b
->data
);
120 assoc_reference (HASH_TABLE
*hash
, const char *string
)
127 b
= hash_search (string
, hash
, 0);
128 return (b
? (char *)b
->data
: 0);
131 /* Quote the data associated with each element of the hash table ASSOC,
132 using quote_string */
134 assoc_quote (HASH_TABLE
*h
)
137 BUCKET_CONTENTS
*tlist
;
140 if (h
== 0 || assoc_empty (h
))
141 return ((HASH_TABLE
*)NULL
);
143 for (i
= 0; i
< h
->nbuckets
; i
++)
144 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
146 t
= quote_string ((char *)tlist
->data
);
154 /* Quote escape characters in the data associated with each element
155 of the hash table ASSOC, using quote_escapes */
157 assoc_quote_escapes (HASH_TABLE
*h
)
160 BUCKET_CONTENTS
*tlist
;
163 if (h
== 0 || assoc_empty (h
))
164 return ((HASH_TABLE
*)NULL
);
166 for (i
= 0; i
< h
->nbuckets
; i
++)
167 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
169 t
= quote_escapes ((char *)tlist
->data
);
178 assoc_dequote (HASH_TABLE
*h
)
181 BUCKET_CONTENTS
*tlist
;
184 if (h
== 0 || assoc_empty (h
))
185 return ((HASH_TABLE
*)NULL
);
187 for (i
= 0; i
< h
->nbuckets
; i
++)
188 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
190 t
= dequote_string ((char *)tlist
->data
);
199 assoc_dequote_escapes (HASH_TABLE
*h
)
202 BUCKET_CONTENTS
*tlist
;
205 if (h
== 0 || assoc_empty (h
))
206 return ((HASH_TABLE
*)NULL
);
208 for (i
= 0; i
< h
->nbuckets
; i
++)
209 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
211 t
= dequote_escapes ((char *)tlist
->data
);
220 assoc_remove_quoted_nulls (HASH_TABLE
*h
)
223 BUCKET_CONTENTS
*tlist
;
226 if (h
== 0 || assoc_empty (h
))
227 return ((HASH_TABLE
*)NULL
);
229 for (i
= 0; i
< h
->nbuckets
; i
++)
230 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
232 t
= remove_quoted_nulls ((char *)tlist
->data
);
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.
244 assoc_subrange (HASH_TABLE
*hash
, arrayind_t start
, arrayind_t nelem
, int starsub
, int quoted
, int pflags
)
246 WORD_LIST
*l
, *save
, *h
, *t
;
250 if (assoc_empty (hash
))
251 return ((char *)NULL
);
253 save
= l
= assoc_to_word_list (hash
);
255 return ((char *)NULL
);
257 for (i
= 1; l
&& i
< start
; i
++)
261 dispose_words (save
);
262 return ((char *)NULL
);
264 for (j
= 0,h
= t
= l
; l
&& j
< nelem
; j
++)
270 t
->next
= (WORD_LIST
*)NULL
;
272 ret
= string_list_pos_params (starsub
? '*' : '@', h
, quoted
, pflags
);
277 dispose_words (save
);
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. */
284 assoc_patsub (HASH_TABLE
*h
, char *pat
, char *rep
, int mflags
)
287 int pchar
, qflags
, pflags
;
288 WORD_LIST
*wl
, *save
;
290 if (h
== 0 || assoc_empty (h
))
291 return ((char *)NULL
);
293 wl
= assoc_to_word_list (h
);
297 for (save
= wl
; wl
; wl
= wl
->next
)
299 t
= pat_subst (wl
->word
->word
, pat
, rep
, mflags
);
300 FREE (wl
->word
->word
);
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;
308 t
= string_list_pos_params (pchar
, save
, qflags
, pflags
);
309 dispose_words (save
);
315 assoc_modcase (HASH_TABLE
*h
, char *pat
, int modop
, int mflags
)
318 int pchar
, qflags
, pflags
;
319 WORD_LIST
*wl
, *save
;
321 if (h
== 0 || assoc_empty (h
))
322 return ((char *)NULL
);
324 wl
= assoc_to_word_list (h
);
326 return ((char *)NULL
);
328 for (save
= wl
; wl
; wl
= wl
->next
)
330 t
= sh_modcase (wl
->word
->word
, pat
, modop
);
331 FREE (wl
->word
->word
);
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;
339 t
= string_list_pos_params (pchar
, save
, qflags
, pflags
);
340 dispose_words (save
);
346 assoc_to_kvpair (HASH_TABLE
*hash
, int quoted
)
350 int i
, rsize
, rlen
, elen
;
351 BUCKET_CONTENTS
*tlist
;
353 if (hash
== 0 || assoc_empty (hash
))
356 ret
= xmalloc (rsize
= 128);
357 ret
[rlen
= 0] = '\0';
359 for (i
= 0; i
< hash
->nbuckets
; i
++)
360 for (tlist
= hash_items (i
, hash
); tlist
; tlist
= tlist
->next
)
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
);
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
))
376 elen
= STRLEN (istr
) + 4 + STRLEN (vstr
);
377 RESIZE_MALLOCED_BUFFER (ret
, rlen
, (elen
+1), rsize
, rsize
);
379 strcpy (ret
+rlen
, istr
);
380 rlen
+= STRLEN (istr
);
384 strcpy (ret
+ rlen
, vstr
);
385 rlen
+= STRLEN (vstr
);
389 strcpy (ret
+ rlen
, "\"\"");
394 if (istr
!= tlist
->key
)
400 RESIZE_MALLOCED_BUFFER (ret
, rlen
, 1, rsize
, 8);
405 vstr
= sh_single_quote (ret
);
414 assoc_to_assign (HASH_TABLE
*hash
, int quoted
)
418 int i
, rsize
, rlen
, elen
;
419 BUCKET_CONTENTS
*tlist
;
421 if (hash
== 0 || assoc_empty (hash
))
424 ret
= xmalloc (rsize
= 128);
428 for (i
= 0; i
< hash
->nbuckets
; i
++)
429 for (tlist
= hash_items (i
, hash
); tlist
; tlist
= tlist
->next
)
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
);
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
))
445 elen
= STRLEN (istr
) + 8 + STRLEN (vstr
);
446 RESIZE_MALLOCED_BUFFER (ret
, rlen
, (elen
+1), rsize
, rsize
);
449 strcpy (ret
+rlen
, istr
);
450 rlen
+= STRLEN (istr
);
455 strcpy (ret
+ rlen
, vstr
);
456 rlen
+= STRLEN (vstr
);
460 if (istr
!= tlist
->key
)
466 RESIZE_MALLOCED_BUFFER (ret
, rlen
, 1, rsize
, 8);
472 vstr
= sh_single_quote (ret
);
481 assoc_to_word_list_internal (HASH_TABLE
*h
, int t
)
485 BUCKET_CONTENTS
*tlist
;
488 if (h
== 0 || assoc_empty (h
))
489 return((WORD_LIST
*)NULL
);
490 list
= (WORD_LIST
*)NULL
;
492 for (i
= 0; i
< h
->nbuckets
; i
++)
493 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
495 w
= (t
== 0) ? (char *)tlist
->data
: (char *)tlist
->key
;
496 list
= make_word_list (make_bare_word(w
), list
);
498 return (REVERSE_LIST(list
, WORD_LIST
*));
502 assoc_to_word_list (HASH_TABLE
*h
)
504 return (assoc_to_word_list_internal (h
, 0));
508 assoc_keys_to_word_list (HASH_TABLE
*h
)
510 return (assoc_to_word_list_internal (h
, 1));
514 assoc_to_kvpair_list (HASH_TABLE
*h
)
518 BUCKET_CONTENTS
*tlist
;
521 if (h
== 0 || assoc_empty (h
))
522 return((WORD_LIST
*)NULL
);
523 list
= (WORD_LIST
*)NULL
;
525 for (i
= 0; i
< h
->nbuckets
; i
++)
526 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
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
);
533 return (REVERSE_LIST(list
, WORD_LIST
*));
537 assoc_to_string (HASH_TABLE
*h
, char *sep
, int quoted
)
539 BUCKET_CONTENTS
*tlist
;
541 char *result
, *t
, *w
;
545 return ((char *)NULL
);
547 return (savestring (""));
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
554 for (i
= 0; i
< h
->nbuckets
; i
++)
555 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
557 w
= (char *)tlist
->data
;
560 t
= quoted
? quote_string (w
) : savestring (w
);
561 list
= make_word_list (make_bare_word(t
), list
);
565 l
= REVERSE_LIST(list
, WORD_LIST
*);
567 result
= l
? string_list_internal (l
, sep
) : savestring ("");
573 #endif /* ARRAY_VARS */