2 * assoc.c - functions to manipulate associative arrays
4 * Associative arrays are standard shell hash tables.
10 /* Copyright (C) 2008,2009,2011 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
__P((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
)
263 arrayind_t start
, nelem
;
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
);
297 dispose_words (save
);
303 assoc_patsub (h
, pat
, rep
, mflags
)
308 BUCKET_CONTENTS
*tlist
;
311 char *t
, *sifs
, *ifs
;
313 if (h
== 0 || assoc_empty (h
))
314 return ((char *)NULL
);
317 for (i
= 0; i
< h2
->nbuckets
; i
++)
318 for (tlist
= hash_items (i
, h2
); tlist
; tlist
= tlist
->next
)
320 t
= pat_subst ((char *)tlist
->data
, pat
, rep
, mflags
);
325 if (mflags
& MATCH_QUOTED
)
328 assoc_quote_escapes (h2
);
330 if (mflags
& MATCH_STARSUB
)
332 assoc_remove_quoted_nulls (h2
);
333 sifs
= ifs_firstchar ((int *)NULL
);
334 t
= assoc_to_string (h2
, sifs
, 0);
337 else if (mflags
& MATCH_QUOTED
)
340 sifs
= ifs_firstchar (&slen
);
342 if (ifs
== 0 || *ifs
== 0)
345 sifs
= xrealloc (sifs
, 2);
349 t
= assoc_to_string (h2
, sifs
, 0);
353 t
= assoc_to_string (h2
, " ", 0);
361 assoc_modcase (h
, pat
, modop
, mflags
)
367 BUCKET_CONTENTS
*tlist
;
370 char *t
, *sifs
, *ifs
;
372 if (h
== 0 || assoc_empty (h
))
373 return ((char *)NULL
);
376 for (i
= 0; i
< h2
->nbuckets
; i
++)
377 for (tlist
= hash_items (i
, h2
); tlist
; tlist
= tlist
->next
)
379 t
= sh_modcase ((char *)tlist
->data
, pat
, modop
);
384 if (mflags
& MATCH_QUOTED
)
387 assoc_quote_escapes (h2
);
389 if (mflags
& MATCH_STARSUB
)
391 assoc_remove_quoted_nulls (h2
);
392 sifs
= ifs_firstchar ((int *)NULL
);
393 t
= assoc_to_string (h2
, sifs
, 0);
396 else if (mflags
& MATCH_QUOTED
)
399 sifs
= ifs_firstchar (&slen
);
401 if (ifs
== 0 || *ifs
== 0)
404 sifs
= xrealloc (sifs
, 2);
408 t
= assoc_to_string (h2
, sifs
, 0);
412 t
= assoc_to_string (h2
, " ", 0);
420 assoc_to_assign (hash
, quoted
)
426 int i
, rsize
, rlen
, elen
;
427 BUCKET_CONTENTS
*tlist
;
429 if (hash
== 0 || assoc_empty (hash
))
432 ret
= xmalloc (rsize
= 128);
436 for (i
= 0; i
< hash
->nbuckets
; i
++)
437 for (tlist
= hash_items (i
, hash
); tlist
; tlist
= tlist
->next
)
439 if (ansic_shouldquote (tlist
->key
))
440 istr
= ansic_quote (tlist
->key
, 0, (int *)0);
441 else if (sh_contains_shell_metas (tlist
->key
))
442 istr
= sh_double_quote (tlist
->key
);
443 else if (ALL_ELEMENT_SUB (tlist
->key
[0]) && tlist
->key
[1] == '\0')
444 istr
= sh_double_quote (tlist
->key
);
448 vstr
= tlist
->data
? (ansic_shouldquote ((char *)tlist
->data
) ?
449 ansic_quote ((char *)tlist
->data
, 0, (int *)0) :
450 sh_double_quote ((char *)tlist
->data
))
453 elen
= STRLEN (istr
) + 8 + STRLEN (vstr
);
454 RESIZE_MALLOCED_BUFFER (ret
, rlen
, (elen
+1), rsize
, rsize
);
457 strcpy (ret
+rlen
, istr
);
458 rlen
+= STRLEN (istr
);
463 strcpy (ret
+ rlen
, vstr
);
464 rlen
+= STRLEN (vstr
);
469 if (istr
!= tlist
->key
)
475 RESIZE_MALLOCED_BUFFER (ret
, rlen
, 1, rsize
, 8);
481 vstr
= sh_single_quote (ret
);
490 assoc_to_word_list_internal (h
, t
)
496 BUCKET_CONTENTS
*tlist
;
499 if (h
== 0 || assoc_empty (h
))
500 return((WORD_LIST
*)NULL
);
501 list
= (WORD_LIST
*)NULL
;
503 for (i
= 0; i
< h
->nbuckets
; i
++)
504 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
506 w
= (t
== 0) ? (char *)tlist
->data
: (char *)tlist
->key
;
507 list
= make_word_list (make_bare_word(w
), list
);
509 return (REVERSE_LIST(list
, WORD_LIST
*));
513 assoc_to_word_list (h
)
516 return (assoc_to_word_list_internal (h
, 0));
520 assoc_keys_to_word_list (h
)
523 return (assoc_to_word_list_internal (h
, 1));
527 assoc_to_string (h
, sep
, quoted
)
532 BUCKET_CONTENTS
*tlist
;
534 char *result
, *t
, *w
;
538 return ((char *)NULL
);
540 return (savestring (""));
544 /* This might be better implemented directly, but it's simple to implement
545 by converting to a word list first, possibly quoting the data, then
547 for (i
= 0; i
< h
->nbuckets
; i
++)
548 for (tlist
= hash_items (i
, h
); tlist
; tlist
= tlist
->next
)
550 w
= (char *)tlist
->data
;
553 t
= quoted
? quote_string (w
) : savestring (w
);
554 list
= make_word_list (make_bare_word(t
), list
);
558 l
= REVERSE_LIST(list
, WORD_LIST
*);
560 result
= l
? string_list_internal (l
, sep
) : savestring ("");
566 #endif /* ARRAY_VARS */