]> git.ipfire.org Git - thirdparty/bash.git/blob - assoc.c
Bash-4.3 patch 43
[thirdparty/bash.git] / assoc.c
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
10 /* Copyright (C) 2008,2009,2011 Free Software Foundation, Inc.
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
47 static WORD_LIST *assoc_to_word_list_internal __P((HASH_TABLE *, int));
48
49 /* assoc_create == hash_create */
50
51 void
52 assoc_dispose (hash)
53 HASH_TABLE *hash;
54 {
55 if (hash)
56 {
57 hash_flush (hash, 0);
58 hash_dispose (hash);
59 }
60 }
61
62 void
63 assoc_flush (hash)
64 HASH_TABLE *hash;
65 {
66 hash_flush (hash, 0);
67 }
68
69 int
70 assoc_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;
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);
85 FREE (b->data);
86 b->data = value ? savestring (value) : (char *)0;
87 return (0);
88 }
89
90 /* Like assoc_insert, but returns b->data instead of freeing it */
91 PTR_T
92 assoc_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
113 void
114 assoc_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
129 char *
130 assoc_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 */
145 HASH_TABLE *
146 assoc_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 */
169 HASH_TABLE *
170 assoc_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
191 HASH_TABLE *
192 assoc_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
213 HASH_TABLE *
214 assoc_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
235 HASH_TABLE *
236 assoc_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 */
260 char *
261 assoc_subrange (hash, start, nelem, starsub, quoted)
262 HASH_TABLE *hash;
263 arrayind_t start, nelem;
264 int 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)
280 return ((char *)NULL);
281 for (j = 0,h = t = l; l && j < nelem; j++)
282 {
283 t = l;
284 l = l->next;
285 }
286
287 t->next = (WORD_LIST *)NULL;
288
289 ret = string_list_pos_params (starsub ? '*' : '@', h, quoted);
290
291 if (t != l)
292 t->next = l;
293
294 dispose_words (save);
295 return (ret);
296
297 }
298
299 char *
300 assoc_patsub (h, pat, rep, mflags)
301 HASH_TABLE *h;
302 char *pat, *rep;
303 int mflags;
304 {
305 BUCKET_CONTENTS *tlist;
306 int i, slen;
307 HASH_TABLE *h2;
308 char *t, *sifs, *ifs;
309
310 if (h == 0 || assoc_empty (h))
311 return ((char *)NULL);
312
313 h2 = assoc_copy (h);
314 for (i = 0; i < h2->nbuckets; i++)
315 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
316 {
317 t = pat_subst ((char *)tlist->data, pat, rep, mflags);
318 FREE (tlist->data);
319 tlist->data = t;
320 }
321
322 if (mflags & MATCH_QUOTED)
323 assoc_quote (h2);
324 else
325 assoc_quote_escapes (h2);
326
327 if (mflags & MATCH_STARSUB)
328 {
329 assoc_remove_quoted_nulls (h2);
330 sifs = ifs_firstchar ((int *)NULL);
331 t = assoc_to_string (h2, sifs, 0);
332 free (sifs);
333 }
334 else if (mflags & MATCH_QUOTED)
335 {
336 /* ${array[@]} */
337 sifs = ifs_firstchar (&slen);
338 ifs = getifs ();
339 if (ifs == 0 || *ifs == 0)
340 {
341 if (slen < 2)
342 sifs = xrealloc (sifs, 2);
343 sifs[0] = ' ';
344 sifs[1] = '\0';
345 }
346 t = assoc_to_string (h2, sifs, 0);
347 free(sifs);
348 }
349 else
350 t = assoc_to_string (h2, " ", 0);
351
352 assoc_dispose (h2);
353
354 return t;
355 }
356
357 char *
358 assoc_modcase (h, pat, modop, mflags)
359 HASH_TABLE *h;
360 char *pat;
361 int modop;
362 int mflags;
363 {
364 BUCKET_CONTENTS *tlist;
365 int i, slen;
366 HASH_TABLE *h2;
367 char *t, *sifs, *ifs;
368
369 if (h == 0 || assoc_empty (h))
370 return ((char *)NULL);
371
372 h2 = assoc_copy (h);
373 for (i = 0; i < h2->nbuckets; i++)
374 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
375 {
376 t = sh_modcase ((char *)tlist->data, pat, modop);
377 FREE (tlist->data);
378 tlist->data = t;
379 }
380
381 if (mflags & MATCH_QUOTED)
382 assoc_quote (h2);
383 else
384 assoc_quote_escapes (h2);
385
386 if (mflags & MATCH_STARSUB)
387 {
388 assoc_remove_quoted_nulls (h2);
389 sifs = ifs_firstchar ((int *)NULL);
390 t = assoc_to_string (h2, sifs, 0);
391 free (sifs);
392 }
393 else if (mflags & MATCH_QUOTED)
394 {
395 /* ${array[@]} */
396 sifs = ifs_firstchar (&slen);
397 ifs = getifs ();
398 if (ifs == 0 || *ifs == 0)
399 {
400 if (slen < 2)
401 sifs = xrealloc (sifs, 2);
402 sifs[0] = ' ';
403 sifs[1] = '\0';
404 }
405 t = assoc_to_string (h2, sifs, 0);
406 free(sifs);
407 }
408 else
409 t = assoc_to_string (h2, " ", 0);
410
411 assoc_dispose (h2);
412
413 return t;
414 }
415
416 char *
417 assoc_to_assign (hash, quoted)
418 HASH_TABLE *hash;
419 int quoted;
420 {
421 char *ret;
422 char *istr, *vstr;
423 int i, rsize, rlen, elen;
424 BUCKET_CONTENTS *tlist;
425
426 if (hash == 0 || assoc_empty (hash))
427 return (char *)0;
428
429 ret = xmalloc (rsize = 128);
430 ret[0] = '(';
431 rlen = 1;
432
433 for (i = 0; i < hash->nbuckets; i++)
434 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
435 {
436 #if 1
437 if (sh_contains_shell_metas (tlist->key))
438 istr = sh_double_quote (tlist->key);
439 else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
440 istr = sh_double_quote (tlist->key);
441 else
442 istr = tlist->key;
443 #else
444 istr = tlist->key;
445 #endif
446 vstr = tlist->data ? sh_double_quote ((char *)tlist->data) : (char *)0;
447
448 elen = STRLEN (istr) + 8 + STRLEN (vstr);
449 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
450
451 ret[rlen++] = '[';
452 strcpy (ret+rlen, istr);
453 rlen += STRLEN (istr);
454 ret[rlen++] = ']';
455 ret[rlen++] = '=';
456 if (vstr)
457 {
458 strcpy (ret + rlen, vstr);
459 rlen += STRLEN (vstr);
460 }
461 ret[rlen++] = ' ';
462
463
464 if (istr != tlist->key)
465 FREE (istr);
466
467 FREE (vstr);
468 }
469
470 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
471 ret[rlen++] = ')';
472 ret[rlen] = '\0';
473
474 if (quoted)
475 {
476 vstr = sh_single_quote (ret);
477 free (ret);
478 ret = vstr;
479 }
480
481 return ret;
482 }
483
484 static WORD_LIST *
485 assoc_to_word_list_internal (h, t)
486 HASH_TABLE *h;
487 int t;
488 {
489 WORD_LIST *list;
490 int i;
491 BUCKET_CONTENTS *tlist;
492 char *w;
493
494 if (h == 0 || assoc_empty (h))
495 return((WORD_LIST *)NULL);
496 list = (WORD_LIST *)NULL;
497
498 for (i = 0; i < h->nbuckets; i++)
499 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
500 {
501 w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
502 list = make_word_list (make_bare_word(w), list);
503 }
504 return (REVERSE_LIST(list, WORD_LIST *));
505 }
506
507 WORD_LIST *
508 assoc_to_word_list (h)
509 HASH_TABLE *h;
510 {
511 return (assoc_to_word_list_internal (h, 0));
512 }
513
514 WORD_LIST *
515 assoc_keys_to_word_list (h)
516 HASH_TABLE *h;
517 {
518 return (assoc_to_word_list_internal (h, 1));
519 }
520
521 char *
522 assoc_to_string (h, sep, quoted)
523 HASH_TABLE *h;
524 char *sep;
525 int quoted;
526 {
527 BUCKET_CONTENTS *tlist;
528 int i;
529 char *result, *t, *w;
530 WORD_LIST *list, *l;
531
532 if (h == 0)
533 return ((char *)NULL);
534 if (assoc_empty (h))
535 return (savestring (""));
536
537 result = NULL;
538 l = list = NULL;
539 /* This might be better implemented directly, but it's simple to implement
540 by converting to a word list first, possibly quoting the data, then
541 using list_string */
542 for (i = 0; i < h->nbuckets; i++)
543 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
544 {
545 w = (char *)tlist->data;
546 if (w == 0)
547 continue;
548 t = quoted ? quote_string (w) : savestring (w);
549 list = make_word_list (make_bare_word(t), list);
550 FREE (t);
551 }
552
553 l = REVERSE_LIST(list, WORD_LIST *);
554
555 result = l ? string_list_internal (l, sep) : savestring ("");
556 dispose_words (l);
557
558 return result;
559 }
560
561 #endif /* ARRAY_VARS */