]> git.ipfire.org Git - thirdparty/bash.git/blame_incremental - assoc.c
Bash-4.3 patch 4
[thirdparty/bash.git] / assoc.c
... / ...
CommitLineData
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
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}
68
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;
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 */
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
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)
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
299char *
300assoc_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
357char *
358assoc_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
416char *
417assoc_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
440 istr = tlist->key;
441#else
442 istr = tlist->key;
443#endif
444 vstr = tlist->data ? sh_double_quote ((char *)tlist->data) : (char *)0;
445
446 elen = STRLEN (istr) + 8 + STRLEN (vstr);
447 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
448
449 ret[rlen++] = '[';
450 strcpy (ret+rlen, istr);
451 rlen += STRLEN (istr);
452 ret[rlen++] = ']';
453 ret[rlen++] = '=';
454 if (vstr)
455 {
456 strcpy (ret + rlen, vstr);
457 rlen += STRLEN (vstr);
458 }
459 ret[rlen++] = ' ';
460
461
462 if (istr != tlist->key)
463 FREE (istr);
464
465 FREE (vstr);
466 }
467
468 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
469 ret[rlen++] = ')';
470 ret[rlen] = '\0';
471
472 if (quoted)
473 {
474 vstr = sh_single_quote (ret);
475 free (ret);
476 ret = vstr;
477 }
478
479 return ret;
480}
481
482static WORD_LIST *
483assoc_to_word_list_internal (h, t)
484 HASH_TABLE *h;
485 int t;
486{
487 WORD_LIST *list;
488 int i;
489 BUCKET_CONTENTS *tlist;
490 char *w;
491
492 if (h == 0 || assoc_empty (h))
493 return((WORD_LIST *)NULL);
494 list = (WORD_LIST *)NULL;
495
496 for (i = 0; i < h->nbuckets; i++)
497 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
498 {
499 w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
500 list = make_word_list (make_bare_word(w), list);
501 }
502 return (REVERSE_LIST(list, WORD_LIST *));
503}
504
505WORD_LIST *
506assoc_to_word_list (h)
507 HASH_TABLE *h;
508{
509 return (assoc_to_word_list_internal (h, 0));
510}
511
512WORD_LIST *
513assoc_keys_to_word_list (h)
514 HASH_TABLE *h;
515{
516 return (assoc_to_word_list_internal (h, 1));
517}
518
519char *
520assoc_to_string (h, sep, quoted)
521 HASH_TABLE *h;
522 char *sep;
523 int quoted;
524{
525 BUCKET_CONTENTS *tlist;
526 int i;
527 char *result, *t, *w;
528 WORD_LIST *list, *l;
529
530 if (h == 0)
531 return ((char *)NULL);
532 if (assoc_empty (h))
533 return (savestring (""));
534
535 result = NULL;
536 l = list = NULL;
537 /* This might be better implemented directly, but it's simple to implement
538 by converting to a word list first, possibly quoting the data, then
539 using list_string */
540 for (i = 0; i < h->nbuckets; i++)
541 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
542 {
543 w = (char *)tlist->data;
544 if (w == 0)
545 continue;
546 t = quoted ? quote_string (w) : savestring (w);
547 list = make_word_list (make_bare_word(t), list);
548 FREE (t);
549 }
550
551 l = REVERSE_LIST(list, WORD_LIST *);
552
553 result = l ? string_list_internal (l, sep) : savestring ("");
554 dispose_words (l);
555
556 return result;
557}
558
559#endif /* ARRAY_VARS */