]> git.ipfire.org Git - thirdparty/bash.git/blob - assoc.c
Bash-4.2 patch 16
[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 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 void
91 assoc_remove (hash, string)
92 HASH_TABLE *hash;
93 char *string;
94 {
95 BUCKET_CONTENTS *b;
96
97 b = hash_remove (string, hash, 0);
98 if (b)
99 {
100 free ((char *)b->data);
101 free (b->key);
102 free (b);
103 }
104 }
105
106 char *
107 assoc_reference (hash, string)
108 HASH_TABLE *hash;
109 char *string;
110 {
111 BUCKET_CONTENTS *b;
112
113 if (hash == 0)
114 return (char *)0;
115
116 b = hash_search (string, hash, 0);
117 return (b ? (char *)b->data : 0);
118 }
119
120 /* Quote the data associated with each element of the hash table ASSOC,
121 using quote_string */
122 HASH_TABLE *
123 assoc_quote (h)
124 HASH_TABLE *h;
125 {
126 int i;
127 BUCKET_CONTENTS *tlist;
128 char *t;
129
130 if (h == 0 || assoc_empty (h))
131 return ((HASH_TABLE *)NULL);
132
133 for (i = 0; i < h->nbuckets; i++)
134 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
135 {
136 t = quote_string ((char *)tlist->data);
137 FREE (tlist->data);
138 tlist->data = t;
139 }
140
141 return h;
142 }
143
144 /* Quote escape characters in the data associated with each element
145 of the hash table ASSOC, using quote_escapes */
146 HASH_TABLE *
147 assoc_quote_escapes (h)
148 HASH_TABLE *h;
149 {
150 int i;
151 BUCKET_CONTENTS *tlist;
152 char *t;
153
154 if (h == 0 || assoc_empty (h))
155 return ((HASH_TABLE *)NULL);
156
157 for (i = 0; i < h->nbuckets; i++)
158 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
159 {
160 t = quote_escapes ((char *)tlist->data);
161 FREE (tlist->data);
162 tlist->data = t;
163 }
164
165 return h;
166 }
167
168 HASH_TABLE *
169 assoc_dequote (h)
170 HASH_TABLE *h;
171 {
172 int i;
173 BUCKET_CONTENTS *tlist;
174 char *t;
175
176 if (h == 0 || assoc_empty (h))
177 return ((HASH_TABLE *)NULL);
178
179 for (i = 0; i < h->nbuckets; i++)
180 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
181 {
182 t = dequote_string ((char *)tlist->data);
183 FREE (tlist->data);
184 tlist->data = t;
185 }
186
187 return h;
188 }
189
190 HASH_TABLE *
191 assoc_dequote_escapes (h)
192 HASH_TABLE *h;
193 {
194 int i;
195 BUCKET_CONTENTS *tlist;
196 char *t;
197
198 if (h == 0 || assoc_empty (h))
199 return ((HASH_TABLE *)NULL);
200
201 for (i = 0; i < h->nbuckets; i++)
202 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
203 {
204 t = dequote_escapes ((char *)tlist->data);
205 FREE (tlist->data);
206 tlist->data = t;
207 }
208
209 return h;
210 }
211
212 HASH_TABLE *
213 assoc_remove_quoted_nulls (h)
214 HASH_TABLE *h;
215 {
216 int i;
217 BUCKET_CONTENTS *tlist;
218 char *t;
219
220 if (h == 0 || assoc_empty (h))
221 return ((HASH_TABLE *)NULL);
222
223 for (i = 0; i < h->nbuckets; i++)
224 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
225 {
226 t = remove_quoted_nulls ((char *)tlist->data);
227 tlist->data = t;
228 }
229
230 return h;
231 }
232
233 /*
234 * Return a string whose elements are the members of array H beginning at
235 * the STARTth element and spanning NELEM members. Null elements are counted.
236 */
237 char *
238 assoc_subrange (hash, start, nelem, starsub, quoted)
239 HASH_TABLE *hash;
240 arrayind_t start, nelem;
241 int starsub, quoted;
242 {
243 WORD_LIST *l, *save, *h, *t;
244 int i, j;
245 char *ret;
246
247 if (assoc_empty (hash))
248 return ((char *)NULL);
249
250 save = l = assoc_to_word_list (hash);
251 if (save == 0)
252 return ((char *)NULL);
253
254 for (i = 1; l && i < start; i++)
255 l = l->next;
256 if (l == 0)
257 return ((char *)NULL);
258 for (j = 0,h = t = l; l && j < nelem; j++)
259 {
260 t = l;
261 l = l->next;
262 }
263
264 t->next = (WORD_LIST *)NULL;
265
266 ret = string_list_pos_params (starsub ? '*' : '@', h, quoted);
267
268 if (t != l)
269 t->next = l;
270
271 dispose_words (save);
272 return (ret);
273
274 }
275
276 char *
277 assoc_patsub (h, pat, rep, mflags)
278 HASH_TABLE *h;
279 char *pat, *rep;
280 int mflags;
281 {
282 BUCKET_CONTENTS *tlist;
283 int i, slen;
284 HASH_TABLE *h2;
285 char *t, *sifs, *ifs;
286
287 if (h == 0 || assoc_empty (h))
288 return ((char *)NULL);
289
290 h2 = assoc_copy (h);
291 for (i = 0; i < h2->nbuckets; i++)
292 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
293 {
294 t = pat_subst ((char *)tlist->data, pat, rep, mflags);
295 FREE (tlist->data);
296 tlist->data = t;
297 }
298
299 if (mflags & MATCH_QUOTED)
300 assoc_quote (h2);
301 else
302 assoc_quote_escapes (h2);
303
304 if (mflags & MATCH_STARSUB)
305 {
306 assoc_remove_quoted_nulls (h2);
307 sifs = ifs_firstchar ((int *)NULL);
308 t = assoc_to_string (h2, sifs, 0);
309 free (sifs);
310 }
311 else if (mflags & MATCH_QUOTED)
312 {
313 /* ${array[@]} */
314 sifs = ifs_firstchar (&slen);
315 ifs = getifs ();
316 if (ifs == 0 || *ifs == 0)
317 {
318 if (slen < 2)
319 sifs = xrealloc (sifs, 2);
320 sifs[0] = ' ';
321 sifs[1] = '\0';
322 }
323 t = assoc_to_string (h2, sifs, 0);
324 free(sifs);
325 }
326 else
327 t = assoc_to_string (h2, " ", 0);
328
329 assoc_dispose (h2);
330
331 return t;
332 }
333
334 char *
335 assoc_modcase (h, pat, modop, mflags)
336 HASH_TABLE *h;
337 char *pat;
338 int modop;
339 int mflags;
340 {
341 BUCKET_CONTENTS *tlist;
342 int i, slen;
343 HASH_TABLE *h2;
344 char *t, *sifs, *ifs;
345
346 if (h == 0 || assoc_empty (h))
347 return ((char *)NULL);
348
349 h2 = assoc_copy (h);
350 for (i = 0; i < h2->nbuckets; i++)
351 for (tlist = hash_items (i, h2); tlist; tlist = tlist->next)
352 {
353 t = sh_modcase ((char *)tlist->data, pat, modop);
354 FREE (tlist->data);
355 tlist->data = t;
356 }
357
358 if (mflags & MATCH_QUOTED)
359 assoc_quote (h2);
360 else
361 assoc_quote_escapes (h2);
362
363 if (mflags & MATCH_STARSUB)
364 {
365 assoc_remove_quoted_nulls (h2);
366 sifs = ifs_firstchar ((int *)NULL);
367 t = assoc_to_string (h2, sifs, 0);
368 free (sifs);
369 }
370 else if (mflags & MATCH_QUOTED)
371 {
372 /* ${array[@]} */
373 sifs = ifs_firstchar (&slen);
374 ifs = getifs ();
375 if (ifs == 0 || *ifs == 0)
376 {
377 if (slen < 2)
378 sifs = xrealloc (sifs, 2);
379 sifs[0] = ' ';
380 sifs[1] = '\0';
381 }
382 t = assoc_to_string (h2, sifs, 0);
383 free(sifs);
384 }
385 else
386 t = assoc_to_string (h2, " ", 0);
387
388 assoc_dispose (h2);
389
390 return t;
391 }
392
393 char *
394 assoc_to_assign (hash, quoted)
395 HASH_TABLE *hash;
396 int quoted;
397 {
398 char *ret;
399 char *istr, *vstr;
400 int i, rsize, rlen, elen;
401 BUCKET_CONTENTS *tlist;
402
403 if (hash == 0 || assoc_empty (hash))
404 return (char *)0;
405
406 ret = xmalloc (rsize = 128);
407 ret[0] = '(';
408 rlen = 1;
409
410 for (i = 0; i < hash->nbuckets; i++)
411 for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
412 {
413 #if 1
414 if (sh_contains_shell_metas (tlist->key))
415 istr = sh_double_quote (tlist->key);
416 else
417 istr = tlist->key;
418 #else
419 istr = tlist->key;
420 #endif
421 vstr = tlist->data ? sh_double_quote ((char *)tlist->data) : (char *)0;
422
423 elen = STRLEN (istr) + 8 + STRLEN (vstr);
424 RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
425
426 ret[rlen++] = '[';
427 strcpy (ret+rlen, istr);
428 rlen += STRLEN (istr);
429 ret[rlen++] = ']';
430 ret[rlen++] = '=';
431 if (vstr)
432 {
433 strcpy (ret + rlen, vstr);
434 rlen += STRLEN (vstr);
435 }
436 ret[rlen++] = ' ';
437
438
439 if (istr != tlist->key)
440 FREE (istr);
441
442 FREE (vstr);
443 }
444
445 RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
446 ret[rlen++] = ')';
447 ret[rlen] = '\0';
448
449 if (quoted)
450 {
451 vstr = sh_single_quote (ret);
452 free (ret);
453 ret = vstr;
454 }
455
456 return ret;
457 }
458
459 static WORD_LIST *
460 assoc_to_word_list_internal (h, t)
461 HASH_TABLE *h;
462 int t;
463 {
464 WORD_LIST *list;
465 int i;
466 BUCKET_CONTENTS *tlist;
467 char *w;
468
469 if (h == 0 || assoc_empty (h))
470 return((WORD_LIST *)NULL);
471 list = (WORD_LIST *)NULL;
472
473 for (i = 0; i < h->nbuckets; i++)
474 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
475 {
476 w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
477 list = make_word_list (make_bare_word(w), list);
478 }
479 return (REVERSE_LIST(list, WORD_LIST *));
480 }
481
482 WORD_LIST *
483 assoc_to_word_list (h)
484 HASH_TABLE *h;
485 {
486 return (assoc_to_word_list_internal (h, 0));
487 }
488
489 WORD_LIST *
490 assoc_keys_to_word_list (h)
491 HASH_TABLE *h;
492 {
493 return (assoc_to_word_list_internal (h, 1));
494 }
495
496 char *
497 assoc_to_string (h, sep, quoted)
498 HASH_TABLE *h;
499 char *sep;
500 int quoted;
501 {
502 BUCKET_CONTENTS *tlist;
503 int i;
504 char *result, *t, *w;
505 WORD_LIST *list, *l;
506
507 if (h == 0)
508 return ((char *)NULL);
509 if (assoc_empty (h))
510 return (savestring (""));
511
512 result = NULL;
513 list = NULL;
514 /* This might be better implemented directly, but it's simple to implement
515 by converting to a word list first, possibly quoting the data, then
516 using list_string */
517 for (i = 0; i < h->nbuckets; i++)
518 for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
519 {
520 w = (char *)tlist->data;
521 if (w == 0)
522 continue;
523 t = quoted ? quote_string (w) : savestring (w);
524 list = make_word_list (make_bare_word(t), list);
525 FREE (t);
526 }
527
528 l = REVERSE_LIST(list, WORD_LIST *);
529
530 result = l ? string_list_internal (l, sep) : savestring ("");
531 return result;
532 }
533
534 #endif /* ARRAY_VARS */