]> git.ipfire.org Git - thirdparty/bash.git/blame - assoc.c
Bash-5.0 patch 4: the wait builtin without arguments only waits for known children...
[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{
d233b485
CR
308 char *t;
309 int pchar, qflags;
310 WORD_LIST *wl, *save;
3185942a
JA
311
312 if (h == 0 || assoc_empty (h))
313 return ((char *)NULL);
314
d233b485
CR
315 wl = assoc_to_word_list (h);
316 if (wl == 0)
317 return (char *)NULL;
3185942a 318
d233b485 319 for (save = wl; wl; wl = wl->next)
3185942a 320 {
d233b485
CR
321 t = pat_subst (wl->word->word, pat, rep, mflags);
322 FREE (wl->word->word);
323 wl->word->word = t;
3185942a 324 }
3185942a 325
d233b485
CR
326 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
327 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
328
329 t = string_list_pos_params (pchar, save, qflags);
330 dispose_words (save);
3185942a
JA
331
332 return t;
333}
334
335char *
336assoc_modcase (h, pat, modop, mflags)
337 HASH_TABLE *h;
338 char *pat;
339 int modop;
340 int mflags;
341{
d233b485
CR
342 char *t;
343 int pchar, qflags;
344 WORD_LIST *wl, *save;
3185942a
JA
345
346 if (h == 0 || assoc_empty (h))
347 return ((char *)NULL);
348
d233b485
CR
349 wl = assoc_to_word_list (h);
350 if (wl == 0)
351 return ((char *)NULL);
3185942a 352
d233b485 353 for (save = wl; wl; wl = wl->next)
3185942a 354 {
d233b485
CR
355 t = sh_modcase (wl->word->word, pat, modop);
356 FREE (wl->word->word);
357 wl->word->word = t;
3185942a 358 }
3185942a 359
d233b485
CR
360 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
361 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
362
363 t = string_list_pos_params (pchar, save, qflags);
364 dispose_words (save);
3185942a
JA
365
366 return t;
367}
368
369char *
370assoc_to_assign (hash, quoted)
371 HASH_TABLE *hash;
372 int quoted;
373{
374 char *ret;
375 char *istr, *vstr;
376 int i, rsize, rlen, elen;
377 BUCKET_CONTENTS *tlist;
378
379 if (hash == 0 || assoc_empty (hash))
380 return (char *)0;
381
382 ret = xmalloc (rsize = 128);
383 ret[0] = '(';
384 rlen = 1;
385
386 for (i = 0; i < hash->nbuckets; i++)
387 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
388 {
a0c0a00f
CR
389 if (ansic_shouldquote (tlist->key))
390 istr = ansic_quote (tlist->key, 0, (int *)0);
391 else if (sh_contains_shell_metas (tlist->key))
0001803f 392 istr = sh_double_quote (tlist->key);
1cfd8d27
CR
393 else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
394 istr = sh_double_quote (tlist->key);
0001803f
CR
395 else
396 istr = tlist->key;
a0c0a00f
CR
397
398 vstr = tlist->data ? (ansic_shouldquote ((char *)tlist->data) ?
399 ansic_quote ((char *)tlist->data, 0, (int *)0) :
400 sh_double_quote ((char *)tlist->data))
401 : (char *)0;
3185942a
JA
402
403 elen = STRLEN (istr) + 8 + STRLEN (vstr);
404 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
405
406 ret[rlen++] = '[';
407 strcpy (ret+rlen, istr);
408 rlen += STRLEN (istr);
409 ret[rlen++] = ']';
410 ret[rlen++] = '=';
411 if (vstr)
412 {
413 strcpy (ret + rlen, vstr);
414 rlen += STRLEN (vstr);
415 }
416 ret[rlen++] = ' ';
417
0001803f
CR
418
419 if (istr != tlist->key)
420 FREE (istr);
421
3185942a
JA
422 FREE (vstr);
423 }
424
425 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
426 ret[rlen++] = ')';
427 ret[rlen] = '\0';
428
429 if (quoted)
430 {
431 vstr = sh_single_quote (ret);
432 free (ret);
433 ret = vstr;
434 }
435
436 return ret;
437}
438
439static WORD_LIST *
440assoc_to_word_list_internal (h, t)
441 HASH_TABLE *h;
442 int t;
443{
444 WORD_LIST *list;
445 int i;
446 BUCKET_CONTENTS *tlist;
447 char *w;
448
449 if (h == 0 || assoc_empty (h))
450 return((WORD_LIST *)NULL);
451 list = (WORD_LIST *)NULL;
452
453 for (i = 0; i < h->nbuckets; i++)
454 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
455 {
456 w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
457 list = make_word_list (make_bare_word(w), list);
458 }
459 return (REVERSE_LIST(list, WORD_LIST *));
460}
461
462WORD_LIST *
463assoc_to_word_list (h)
464 HASH_TABLE *h;
465{
466 return (assoc_to_word_list_internal (h, 0));
467}
468
469WORD_LIST *
470assoc_keys_to_word_list (h)
471 HASH_TABLE *h;
472{
473 return (assoc_to_word_list_internal (h, 1));
474}
475
476char *
477assoc_to_string (h, sep, quoted)
478 HASH_TABLE *h;
479 char *sep;
480 int quoted;
481{
482 BUCKET_CONTENTS *tlist;
483 int i;
484 char *result, *t, *w;
485 WORD_LIST *list, *l;
486
487 if (h == 0)
488 return ((char *)NULL);
489 if (assoc_empty (h))
490 return (savestring (""));
491
492 result = NULL;
ac50fbac 493 l = list = NULL;
3185942a
JA
494 /* This might be better implemented directly, but it's simple to implement
495 by converting to a word list first, possibly quoting the data, then
496 using list_string */
497 for (i = 0; i < h->nbuckets; i++)
498 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
499 {
500 w = (char *)tlist->data;
501 if (w == 0)
502 continue;
503 t = quoted ? quote_string (w) : savestring (w);
504 list = make_word_list (make_bare_word(t), list);
505 FREE (t);
506 }
507
508 l = REVERSE_LIST(list, WORD_LIST *);
509
510 result = l ? string_list_internal (l, sep) : savestring ("");
ac50fbac
CR
511 dispose_words (l);
512
3185942a
JA
513 return result;
514}
515
516#endif /* ARRAY_VARS */