476facb340dcd3f429f3669220dea10d6036076a
[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   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         istr = tlist->key;
409         vstr = tlist->data ? sh_double_quote ((char *)tlist->data) : (char *)0;
410
411         elen = STRLEN (istr) + 8 + STRLEN (vstr);
412         RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
413
414         ret[rlen++] = '[';
415         strcpy (ret+rlen, istr);
416         rlen += STRLEN (istr);
417         ret[rlen++] = ']';
418         ret[rlen++] = '=';
419         if (vstr)
420           {
421             strcpy (ret + rlen, vstr);
422             rlen += STRLEN (vstr);
423           }
424         ret[rlen++] = ' ';
425
426         FREE (vstr);
427     }
428
429   RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
430   ret[rlen++] = ')';
431   ret[rlen] = '\0';
432
433   if (quoted)
434     {
435       vstr = sh_single_quote (ret);
436       free (ret);
437       ret = vstr;
438     }
439
440   return ret;
441 }
442
443 static WORD_LIST *
444 assoc_to_word_list_internal (h, t)
445      HASH_TABLE *h;
446      int t;
447 {
448   WORD_LIST *list;
449   int i;
450   BUCKET_CONTENTS *tlist;
451   char *w;
452
453   if (h == 0 || assoc_empty (h))
454     return((WORD_LIST *)NULL);
455   list = (WORD_LIST *)NULL;
456   
457   for (i = 0; i < h->nbuckets; i++)
458     for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
459       {
460         w = (t == 0) ? (char *)tlist->data : (char *)tlist->key;
461         list = make_word_list (make_bare_word(w), list);
462       }
463   return (REVERSE_LIST(list, WORD_LIST *));
464 }
465
466 WORD_LIST *
467 assoc_to_word_list (h)
468      HASH_TABLE *h;
469 {
470   return (assoc_to_word_list_internal (h, 0));
471 }
472
473 WORD_LIST *
474 assoc_keys_to_word_list (h)
475      HASH_TABLE *h;
476 {
477   return (assoc_to_word_list_internal (h, 1));
478 }
479
480 char *
481 assoc_to_string (h, sep, quoted)
482      HASH_TABLE *h;
483      char *sep;
484      int quoted;
485 {
486   BUCKET_CONTENTS *tlist;
487   int i;
488   char *result, *t, *w;
489   WORD_LIST *list, *l;
490
491   if (h == 0)
492     return ((char *)NULL);
493   if (assoc_empty (h))
494     return (savestring (""));
495
496   result = NULL;
497   list = NULL;
498   /* This might be better implemented directly, but it's simple to implement
499      by converting to a word list first, possibly quoting the data, then
500      using list_string */
501   for (i = 0; i < h->nbuckets; i++)
502     for (tlist = hash_items (i, h); tlist; tlist = tlist->next)
503       {
504         w = (char *)tlist->data;
505         if (w == 0)
506           continue;
507         t = quoted ? quote_string (w) : savestring (w);
508         list = make_word_list (make_bare_word(t), list);
509         FREE (t);
510       }
511
512   l = REVERSE_LIST(list, WORD_LIST *);
513
514   result = l ? string_list_internal (l, sep) : savestring ("");
515   return result;
516 }
517
518 #endif /* ARRAY_VARS */