tizen 2.3.1 release
[external/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 */