4561de4247530f9f6d08d3ec7c6406a3b24b66a0
[platform/upstream/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 */