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