Patch-ID: bash41-010
[bash.git] / assoc.c
blobbbc7b177720245f0a9255bfa3007e46875064c59
1 /*
2 * assoc.c - functions to manipulate associative arrays
4 * Associative arrays are standard shell hash tables.
6 * Chet Ramey
7 * chet@ins.cwru.edu
8 */
10 /* Copyright (C) 2008,2009 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/>.
28 #include "config.h"
30 #if defined (ARRAY_VARS)
32 #if defined (HAVE_UNISTD_H)
33 # ifdef _MINIX
34 # include <sys/types.h>
35 # endif
36 # include <unistd.h>
37 #endif
39 #include <stdio.h>
40 #include "bashansi.h"
42 #include "shell.h"
43 #include "array.h"
44 #include "assoc.h"
45 #include "builtins/common.h"
47 static WORD_LIST *assoc_to_word_list_internal __P((HASH_TABLE *, int));
49 /* assoc_create == hash_create */
51 void
52 assoc_dispose (hash)
53 HASH_TABLE *hash;
55 if (hash)
57 hash_flush (hash, 0);
58 hash_dispose (hash);
62 void
63 assoc_flush (hash)
64 HASH_TABLE *hash;
66 hash_flush (hash, 0);
69 int
70 assoc_insert (hash, key, value)
71 HASH_TABLE *hash;
72 char *key;
73 char *value;
75 BUCKET_CONTENTS *b;
77 b = hash_search (key, hash, HASH_CREATE);
78 if (b == 0)
79 return -1;
80 FREE (b->data);
81 b->data = value ? savestring (value) : (char *)0;
82 return (0);
85 void
86 assoc_remove (hash, string)
87 HASH_TABLE *hash;
88 char *string;
90 BUCKET_CONTENTS *b;
92 b = hash_remove (string, hash, 0);
93 if (b)
95 free ((char *)b->data);
96 free (b->key);
97 free (b);
101 char *
102 assoc_reference (hash, string)
103 HASH_TABLE *hash;
104 char *string;
106 BUCKET_CONTENTS *b;
108 if (hash == 0)
109 return (char *)0;
111 b = hash_search (string, hash, 0);
112 return (b ? (char *)b->data : 0);
115 /* Quote the data associated with each element of the hash table ASSOC,
116 using quote_string */
117 HASH_TABLE *
118 assoc_quote (h)
119 HASH_TABLE *h;
121 int i;
122 BUCKET_CONTENTS *tlist;
123 char *t;
125 if (h == 0 || assoc_empty (h))
126 return ((HASH_TABLE *)NULL);
128 for (i = 0; i < h->nbuckets; i++)
129 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
131 t = quote_string ((char *)tlist->data);
132 FREE (tlist->data);
133 tlist->data = t;
136 return h;
139 /* Quote escape characters in the data associated with each element
140 of the hash table ASSOC, using quote_escapes */
141 HASH_TABLE *
142 assoc_quote_escapes (h)
143 HASH_TABLE *h;
145 int i;
146 BUCKET_CONTENTS *tlist;
147 char *t;
149 if (h == 0 || assoc_empty (h))
150 return ((HASH_TABLE *)NULL);
152 for (i = 0; i < h->nbuckets; i++)
153 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
155 t = quote_escapes ((char *)tlist->data);
156 FREE (tlist->data);
157 tlist->data = t;
160 return h;
163 HASH_TABLE *
164 assoc_dequote (h)
165 HASH_TABLE *h;
167 int i;
168 BUCKET_CONTENTS *tlist;
169 char *t;
171 if (h == 0 || assoc_empty (h))
172 return ((HASH_TABLE *)NULL);
174 for (i = 0; i < h->nbuckets; i++)
175 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
177 t = dequote_string ((char *)tlist->data);
178 FREE (tlist->data);
179 tlist->data = t;
182 return h;
185 HASH_TABLE *
186 assoc_dequote_escapes (h)
187 HASH_TABLE *h;
189 int i;
190 BUCKET_CONTENTS *tlist;
191 char *t;
193 if (h == 0 || assoc_empty (h))
194 return ((HASH_TABLE *)NULL);
196 for (i = 0; i < h->nbuckets; i++)
197 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
199 t = dequote_escapes ((char *)tlist->data);
200 FREE (tlist->data);
201 tlist->data = t;
204 return h;
207 HASH_TABLE *
208 assoc_remove_quoted_nulls (h)
209 HASH_TABLE *h;
211 int i;
212 BUCKET_CONTENTS *tlist;
213 char *t;
215 if (h == 0 || assoc_empty (h))
216 return ((HASH_TABLE *)NULL);
218 for (i = 0; i < h->nbuckets; i++)
219 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
221 t = remove_quoted_nulls ((char *)tlist->data);
222 tlist->data = t;
225 return h;
229 * Return a string whose elements are the members of array H beginning at
230 * the STARTth element and spanning NELEM members. Null elements are counted.
232 char *
233 assoc_subrange (hash, start, nelem, starsub, quoted)
234 HASH_TABLE *hash;
235 arrayind_t start, nelem;
236 int starsub, quoted;
238 WORD_LIST *l, *save, *h, *t;
239 int i, j;
240 char *ret;
242 if (assoc_empty (hash))
243 return ((char *)NULL);
245 save = l = assoc_to_word_list (hash);
246 if (save == 0)
247 return ((char *)NULL);
249 for (i = 1; l && i < start; i++)
250 l = l->next;
251 if (l == 0)
252 return ((char *)NULL);
253 for (j = 0,h = t = l; l && j < nelem; j++)
255 t = l;
256 l = l->next;
259 t->next = (WORD_LIST *)NULL;
261 ret = string_list_pos_params (starsub ? '*' : '@', h, quoted);
263 if (t != l)
264 t->next = l;
266 dispose_words (save);
267 return (ret);
271 char *
272 assoc_patsub (h, pat, rep, mflags)
273 HASH_TABLE *h;
274 char *pat, *rep;
275 int mflags;
277 BUCKET_CONTENTS *tlist;
278 int i, slen;
279 HASH_TABLE *h2;
280 char *t, *sifs, *ifs;
282 if (h == 0 || assoc_empty (h))
283 return ((char *)NULL);
285 h2 = assoc_copy (h);
286 for (i = 0; i < h2->nbuckets; i++)
287 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
289 t = pat_subst ((char *)tlist->data, pat, rep, mflags);
290 FREE (tlist->data);
291 tlist->data = t;
294 if (mflags & MATCH_QUOTED)
295 assoc_quote (h2);
296 else
297 assoc_quote_escapes (h2);
299 if (mflags & MATCH_STARSUB)
301 assoc_remove_quoted_nulls (h2);
302 sifs = ifs_firstchar ((int *)NULL);
303 t = assoc_to_string (h2, sifs, 0);
304 free (sifs);
306 else if (mflags & MATCH_QUOTED)
308 /* ${array[@]} */
309 sifs = ifs_firstchar (&slen);
310 ifs = getifs ();
311 if (ifs == 0 || *ifs == 0)
313 if (slen < 2)
314 sifs = xrealloc (sifs, 2);
315 sifs[0] = ' ';
316 sifs[1] = '\0';
318 t = assoc_to_string (h2, sifs, 0);
319 free(sifs);
321 else
322 t = assoc_to_string (h2, " ", 0);
324 assoc_dispose (h2);
326 return t;
329 char *
330 assoc_modcase (h, pat, modop, mflags)
331 HASH_TABLE *h;
332 char *pat;
333 int modop;
334 int mflags;
336 BUCKET_CONTENTS *tlist;
337 int i, slen;
338 HASH_TABLE *h2;
339 char *t, *sifs, *ifs;
341 if (h == 0 || assoc_empty (h))
342 return ((char *)NULL);
344 h2 = assoc_copy (h);
345 for (i = 0; i < h2->nbuckets; i++)
346 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
348 t = sh_modcase ((char *)tlist->data, pat, modop);
349 FREE (tlist->data);
350 tlist->data = t;
353 if (mflags & MATCH_QUOTED)
354 assoc_quote (h2);
355 else
356 assoc_quote_escapes (h2);
358 if (mflags & MATCH_STARSUB)
360 assoc_remove_quoted_nulls (h2);
361 sifs = ifs_firstchar ((int *)NULL);
362 t = assoc_to_string (h2, sifs, 0);
363 free (sifs);
365 else if (mflags & MATCH_QUOTED)
367 /* ${array[@]} */
368 sifs = ifs_firstchar (&slen);
369 ifs = getifs ();
370 if (ifs == 0 || *ifs == 0)
372 if (slen < 2)
373 sifs = xrealloc (sifs, 2);
374 sifs[0] = ' ';
375 sifs[1] = '\0';
377 t = assoc_to_string (h2, sifs, 0);
378 free(sifs);
380 else
381 t = assoc_to_string (h2, " ", 0);
383 assoc_dispose (h2);
385 return t;
388 char *
389 assoc_to_assign (hash, quoted)
390 HASH_TABLE *hash;
391 int quoted;
393 char *ret;
394 char *istr, *vstr;
395 int i, rsize, rlen, elen;
396 BUCKET_CONTENTS *tlist;
398 if (hash == 0 || assoc_empty (hash))
399 return (char *)0;
401 ret = xmalloc (rsize = 128);
402 ret[0] = '(';
403 rlen = 1;
405 for (i = 0; i < hash->nbuckets; i++)
406 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
408 #if 1
409 if (sh_contains_shell_metas (tlist->key))
410 istr = sh_double_quote (tlist->key);
411 else
412 istr = tlist->key;
413 #else
414 istr = tlist->key;
415 #endif
416 vstr = tlist->data ? sh_double_quote ((char *)tlist->data) : (char *)0;
418 elen = STRLEN (istr) + 8 + STRLEN (vstr);
419 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
421 ret[rlen++] = '[';
422 strcpy (ret+rlen, istr);
423 rlen += STRLEN (istr);
424 ret[rlen++] = ']';
425 ret[rlen++] = '=';
426 if (vstr)
428 strcpy (ret + rlen, vstr);
429 rlen += STRLEN (vstr);
431 ret[rlen++] = ' ';
434 if (istr != tlist->key)
435 FREE (istr);
437 FREE (vstr);
440 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
441 ret[rlen++] = ')';
442 ret[rlen] = '\0';
444 if (quoted)
446 vstr = sh_single_quote (ret);
447 free (ret);
448 ret = vstr;
451 return ret;
454 static WORD_LIST *
455 assoc_to_word_list_internal (h, t)
456 HASH_TABLE *h;
457 int t;
459 WORD_LIST *list;
460 int i;
461 BUCKET_CONTENTS *tlist;
462 char *w;
464 if (h == 0 || assoc_empty (h))
465 return((WORD_LIST *)NULL);
466 list = (WORD_LIST *)NULL;
468 for (i = 0; i < h->nbuckets; i++)
469 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
471 w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
472 list = make_word_list (make_bare_word(w), list);
474 return (REVERSE_LIST(list, WORD_LIST *));
477 WORD_LIST *
478 assoc_to_word_list (h)
479 HASH_TABLE *h;
481 return (assoc_to_word_list_internal (h, 0));
484 WORD_LIST *
485 assoc_keys_to_word_list (h)
486 HASH_TABLE *h;
488 return (assoc_to_word_list_internal (h, 1));
491 char *
492 assoc_to_string (h, sep, quoted)
493 HASH_TABLE *h;
494 char *sep;
495 int quoted;
497 BUCKET_CONTENTS *tlist;
498 int i;
499 char *result, *t, *w;
500 WORD_LIST *list, *l;
502 if (h == 0)
503 return ((char *)NULL);
504 if (assoc_empty (h))
505 return (savestring (""));
507 result = NULL;
508 list = NULL;
509 /* This might be better implemented directly, but it's simple to implement
510 by converting to a word list first, possibly quoting the data, then
511 using list_string */
512 for (i = 0; i < h->nbuckets; i++)
513 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
515 w = (char *)tlist->data;
516 if (w == 0)
517 continue;
518 t = quoted ? quote_string (w) : savestring (w);
519 list = make_word_list (make_bare_word(t), list);
520 FREE (t);
523 l = REVERSE_LIST(list, WORD_LIST *);
525 result = l ? string_list_internal (l, sep) : savestring ("");
526 return result;
529 #endif /* ARRAY_VARS */