]> git.ipfire.org Git - thirdparty/bash.git/blame - assoc.c
Bash-4.4 patch 4
[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
ac50fbac 10/* Copyright (C) 2008,2009,2011 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
47static WORD_LIST *assoc_to_word_list_internal __P((HASH_TABLE *, int));
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 *
261assoc_subrange (hash, start, nelem, starsub, quoted)
262HASH_TABLE *hash;
263arrayind_t start, nelem;
264int starsub, quoted;
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
292 ret = string_list_pos_params (starsub ? '*' : '@', h, quoted);
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{
308 BUCKET_CONTENTS *tlist;
309 int i, slen;
310 HASH_TABLE *h2;
311 char *t, *sifs, *ifs;
312
313 if (h == 0 || assoc_empty (h))
314 return ((char *)NULL);
315
316 h2 = assoc_copy (h);
317 for (i = 0; i < h2->nbuckets; i++)
318 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
319 {
320 t = pat_subst ((char *)tlist->data, pat, rep, mflags);
321 FREE (tlist->data);
322 tlist->data = t;
323 }
324
325 if (mflags & MATCH_QUOTED)
326 assoc_quote (h2);
327 else
328 assoc_quote_escapes (h2);
329
330 if (mflags & MATCH_STARSUB)
331 {
332 assoc_remove_quoted_nulls (h2);
333 sifs = ifs_firstchar ((int *)NULL);
334 t = assoc_to_string (h2, sifs, 0);
335 free (sifs);
336 }
337 else if (mflags & MATCH_QUOTED)
338 {
339 /* ${array[@]} */
340 sifs = ifs_firstchar (&slen);
341 ifs = getifs ();
342 if (ifs == 0 || *ifs == 0)
343 {
344 if (slen < 2)
345 sifs = xrealloc (sifs, 2);
346 sifs[0] = ' ';
347 sifs[1] = '\0';
348 }
349 t = assoc_to_string (h2, sifs, 0);
350 free(sifs);
351 }
352 else
353 t = assoc_to_string (h2, " ", 0);
354
355 assoc_dispose (h2);
356
357 return t;
358}
359
360char *
361assoc_modcase (h, pat, modop, mflags)
362 HASH_TABLE *h;
363 char *pat;
364 int modop;
365 int mflags;
366{
367 BUCKET_CONTENTS *tlist;
368 int i, slen;
369 HASH_TABLE *h2;
370 char *t, *sifs, *ifs;
371
372 if (h == 0 || assoc_empty (h))
373 return ((char *)NULL);
374
375 h2 = assoc_copy (h);
376 for (i = 0; i < h2->nbuckets; i++)
377 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
378 {
379 t = sh_modcase ((char *)tlist->data, pat, modop);
380 FREE (tlist->data);
381 tlist->data = t;
382 }
383
384 if (mflags & MATCH_QUOTED)
385 assoc_quote (h2);
386 else
387 assoc_quote_escapes (h2);
388
389 if (mflags & MATCH_STARSUB)
390 {
391 assoc_remove_quoted_nulls (h2);
392 sifs = ifs_firstchar ((int *)NULL);
393 t = assoc_to_string (h2, sifs, 0);
394 free (sifs);
395 }
396 else if (mflags & MATCH_QUOTED)
397 {
398 /* ${array[@]} */
399 sifs = ifs_firstchar (&slen);
400 ifs = getifs ();
401 if (ifs == 0 || *ifs == 0)
402 {
403 if (slen < 2)
404 sifs = xrealloc (sifs, 2);
405 sifs[0] = ' ';
406 sifs[1] = '\0';
407 }
408 t = assoc_to_string (h2, sifs, 0);
409 free(sifs);
410 }
411 else
412 t = assoc_to_string (h2, " ", 0);
413
414 assoc_dispose (h2);
415
416 return t;
417}
418
419char *
420assoc_to_assign (hash, quoted)
421 HASH_TABLE *hash;
422 int quoted;
423{
424 char *ret;
425 char *istr, *vstr;
426 int i, rsize, rlen, elen;
427 BUCKET_CONTENTS *tlist;
428
429 if (hash == 0 || assoc_empty (hash))
430 return (char *)0;
431
432 ret = xmalloc (rsize = 128);
433 ret[0] = '(';
434 rlen = 1;
435
436 for (i = 0; i < hash->nbuckets; i++)
437 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
438 {
a0c0a00f
CR
439 if (ansic_shouldquote (tlist->key))
440 istr = ansic_quote (tlist->key, 0, (int *)0);
441 else if (sh_contains_shell_metas (tlist->key))
0001803f 442 istr = sh_double_quote (tlist->key);
1cfd8d27
CR
443 else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
444 istr = sh_double_quote (tlist->key);
0001803f
CR
445 else
446 istr = tlist->key;
a0c0a00f
CR
447
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))
451 : (char *)0;
3185942a
JA
452
453 elen = STRLEN (istr) + 8 + STRLEN (vstr);
454 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
455
456 ret[rlen++] = '[';
457 strcpy (ret+rlen, istr);
458 rlen += STRLEN (istr);
459 ret[rlen++] = ']';
460 ret[rlen++] = '=';
461 if (vstr)
462 {
463 strcpy (ret + rlen, vstr);
464 rlen += STRLEN (vstr);
465 }
466 ret[rlen++] = ' ';
467
0001803f
CR
468
469 if (istr != tlist->key)
470 FREE (istr);
471
3185942a
JA
472 FREE (vstr);
473 }
474
475 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
476 ret[rlen++] = ')';
477 ret[rlen] = '\0';
478
479 if (quoted)
480 {
481 vstr = sh_single_quote (ret);
482 free (ret);
483 ret = vstr;
484 }
485
486 return ret;
487}
488
489static WORD_LIST *
490assoc_to_word_list_internal (h, t)
491 HASH_TABLE *h;
492 int t;
493{
494 WORD_LIST *list;
495 int i;
496 BUCKET_CONTENTS *tlist;
497 char *w;
498
499 if (h == 0 || assoc_empty (h))
500 return((WORD_LIST *)NULL);
501 list = (WORD_LIST *)NULL;
502
503 for (i = 0; i < h->nbuckets; i++)
504 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
505 {
506 w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
507 list = make_word_list (make_bare_word(w), list);
508 }
509 return (REVERSE_LIST(list, WORD_LIST *));
510}
511
512WORD_LIST *
513assoc_to_word_list (h)
514 HASH_TABLE *h;
515{
516 return (assoc_to_word_list_internal (h, 0));
517}
518
519WORD_LIST *
520assoc_keys_to_word_list (h)
521 HASH_TABLE *h;
522{
523 return (assoc_to_word_list_internal (h, 1));
524}
525
526char *
527assoc_to_string (h, sep, quoted)
528 HASH_TABLE *h;
529 char *sep;
530 int quoted;
531{
532 BUCKET_CONTENTS *tlist;
533 int i;
534 char *result, *t, *w;
535 WORD_LIST *list, *l;
536
537 if (h == 0)
538 return ((char *)NULL);
539 if (assoc_empty (h))
540 return (savestring (""));
541
542 result = NULL;
ac50fbac 543 l = list = NULL;
3185942a
JA
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
546 using list_string */
547 for (i = 0; i < h->nbuckets; i++)
548 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
549 {
550 w = (char *)tlist->data;
551 if (w == 0)
552 continue;
553 t = quoted ? quote_string (w) : savestring (w);
554 list = make_word_list (make_bare_word(t), list);
555 FREE (t);
556 }
557
558 l = REVERSE_LIST(list, WORD_LIST *);
559
560 result = l ? string_list_internal (l, sep) : savestring ("");
ac50fbac
CR
561 dispose_words (l);
562
3185942a
JA
563 return result;
564}
565
566#endif /* ARRAY_VARS */