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