/* * assoc.c - functions to manipulate associative arrays * * Associative arrays are standard shell hash tables. * * Chet Ramey * chet@ins.cwru.edu */ /* Copyright (C) 2008,2009 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash. If not, see . */ #include "config.h" #if defined (ARRAY_VARS) #if defined (HAVE_UNISTD_H) # ifdef _MINIX # include # endif # include #endif #include #include "bashansi.h" #include "shell.h" #include "array.h" #include "assoc.h" #include "builtins/common.h" static WORD_LIST *assoc_to_word_list_internal __P((HASH_TABLE *, int)); /* assoc_create == hash_create */ void assoc_dispose (hash) HASH_TABLE *hash; { if (hash) { hash_flush (hash, 0); hash_dispose (hash); } } void assoc_flush (hash) HASH_TABLE *hash; { hash_flush (hash, 0); } int assoc_insert (hash, key, value) HASH_TABLE *hash; char *key; char *value; { BUCKET_CONTENTS *b; b = hash_search (key, hash, HASH_CREATE); if (b == 0) return -1; FREE (b->data); b->data = value ? savestring (value) : (char *)0; return (0); } void assoc_remove (hash, string) HASH_TABLE *hash; char *string; { BUCKET_CONTENTS *b; b = hash_remove (string, hash, 0); if (b) { free ((char *)b->data); free (b->key); free (b); } } char * assoc_reference (hash, string) HASH_TABLE *hash; char *string; { BUCKET_CONTENTS *b; if (hash == 0) return (char *)0; b = hash_search (string, hash, 0); return (b ? (char *)b->data : 0); } /* Quote the data associated with each element of the hash table ASSOC, using quote_string */ HASH_TABLE * assoc_quote (h) HASH_TABLE *h; { int i; BUCKET_CONTENTS *tlist; char *t; if (h == 0 || assoc_empty (h)) return ((HASH_TABLE *)NULL); for (i = 0; i < h->nbuckets; i++) for (tlist = hash_items (i, h); tlist; tlist = tlist->next) { t = quote_string ((char *)tlist->data); FREE (tlist->data); tlist->data = t; } return h; } /* Quote escape characters in the data associated with each element of the hash table ASSOC, using quote_escapes */ HASH_TABLE * assoc_quote_escapes (h) HASH_TABLE *h; { int i; BUCKET_CONTENTS *tlist; char *t; if (h == 0 || assoc_empty (h)) return ((HASH_TABLE *)NULL); for (i = 0; i < h->nbuckets; i++) for (tlist = hash_items (i, h); tlist; tlist = tlist->next) { t = quote_escapes ((char *)tlist->data); FREE (tlist->data); tlist->data = t; } return h; } HASH_TABLE * assoc_dequote (h) HASH_TABLE *h; { int i; BUCKET_CONTENTS *tlist; char *t; if (h == 0 || assoc_empty (h)) return ((HASH_TABLE *)NULL); for (i = 0; i < h->nbuckets; i++) for (tlist = hash_items (i, h); tlist; tlist = tlist->next) { t = dequote_string ((char *)tlist->data); FREE (tlist->data); tlist->data = t; } return h; } HASH_TABLE * assoc_dequote_escapes (h) HASH_TABLE *h; { int i; BUCKET_CONTENTS *tlist; char *t; if (h == 0 || assoc_empty (h)) return ((HASH_TABLE *)NULL); for (i = 0; i < h->nbuckets; i++) for (tlist = hash_items (i, h); tlist; tlist = tlist->next) { t = dequote_escapes ((char *)tlist->data); FREE (tlist->data); tlist->data = t; } return h; } HASH_TABLE * assoc_remove_quoted_nulls (h) HASH_TABLE *h; { int i; BUCKET_CONTENTS *tlist; char *t; if (h == 0 || assoc_empty (h)) return ((HASH_TABLE *)NULL); for (i = 0; i < h->nbuckets; i++) for (tlist = hash_items (i, h); tlist; tlist = tlist->next) { t = remove_quoted_nulls ((char *)tlist->data); tlist->data = t; } return h; } /* * Return a string whose elements are the members of array H beginning at * the STARTth element and spanning NELEM members. Null elements are counted. */ char * assoc_subrange (hash, start, nelem, starsub, quoted) HASH_TABLE *hash; arrayind_t start, nelem; int starsub, quoted; { WORD_LIST *l, *save, *h, *t; int i, j; char *ret; if (assoc_empty (hash)) return ((char *)NULL); save = l = assoc_to_word_list (hash); if (save == 0) return ((char *)NULL); for (i = 1; l && i < start; i++) l = l->next; if (l == 0) return ((char *)NULL); for (j = 0,h = t = l; l && j < nelem; j++) { t = l; l = l->next; } t->next = (WORD_LIST *)NULL; ret = string_list_pos_params (starsub ? '*' : '@', h, quoted); if (t != l) t->next = l; dispose_words (save); return (ret); } char * assoc_patsub (h, pat, rep, mflags) HASH_TABLE *h; char *pat, *rep; int mflags; { BUCKET_CONTENTS *tlist; int i, slen; HASH_TABLE *h2; char *t, *sifs, *ifs; if (h == 0 || assoc_empty (h)) return ((char *)NULL); h2 = assoc_copy (h); for (i = 0; i < h2->nbuckets; i++) for (tlist = hash_items (i, h2); tlist; tlist = tlist->next) { t = pat_subst ((char *)tlist->data, pat, rep, mflags); FREE (tlist->data); tlist->data = t; } if (mflags & MATCH_QUOTED) assoc_quote (h2); else assoc_quote_escapes (h2); if (mflags & MATCH_STARSUB) { assoc_remove_quoted_nulls (h2); sifs = ifs_firstchar ((int *)NULL); t = assoc_to_string (h2, sifs, 0); free (sifs); } else if (mflags & MATCH_QUOTED) { /* ${array[@]} */ sifs = ifs_firstchar (&slen); ifs = getifs (); if (ifs == 0 || *ifs == 0) { if (slen < 2) sifs = xrealloc (sifs, 2); sifs[0] = ' '; sifs[1] = '\0'; } t = assoc_to_string (h2, sifs, 0); free(sifs); } else t = assoc_to_string (h2, " ", 0); assoc_dispose (h2); return t; } char * assoc_modcase (h, pat, modop, mflags) HASH_TABLE *h; char *pat; int modop; int mflags; { BUCKET_CONTENTS *tlist; int i, slen; HASH_TABLE *h2; char *t, *sifs, *ifs; if (h == 0 || assoc_empty (h)) return ((char *)NULL); h2 = assoc_copy (h); for (i = 0; i < h2->nbuckets; i++) for (tlist = hash_items (i, h2); tlist; tlist = tlist->next) { t = sh_modcase ((char *)tlist->data, pat, modop); FREE (tlist->data); tlist->data = t; } if (mflags & MATCH_QUOTED) assoc_quote (h2); else assoc_quote_escapes (h2); if (mflags & MATCH_STARSUB) { assoc_remove_quoted_nulls (h2); sifs = ifs_firstchar ((int *)NULL); t = assoc_to_string (h2, sifs, 0); free (sifs); } else if (mflags & MATCH_QUOTED) { /* ${array[@]} */ sifs = ifs_firstchar (&slen); ifs = getifs (); if (ifs == 0 || *ifs == 0) { if (slen < 2) sifs = xrealloc (sifs, 2); sifs[0] = ' '; sifs[1] = '\0'; } t = assoc_to_string (h2, sifs, 0); free(sifs); } else t = assoc_to_string (h2, " ", 0); assoc_dispose (h2); return t; } char * assoc_to_assign (hash, quoted) HASH_TABLE *hash; int quoted; { char *ret; char *istr, *vstr; int i, rsize, rlen, elen; BUCKET_CONTENTS *tlist; if (hash == 0 || assoc_empty (hash)) return (char *)0; ret = xmalloc (rsize = 128); ret[0] = '('; rlen = 1; for (i = 0; i < hash->nbuckets; i++) for (tlist = hash_items (i, hash); tlist; tlist = tlist->next) { #if 1 if (sh_contains_shell_metas (tlist->key)) istr = sh_double_quote (tlist->key); else istr = tlist->key; #else istr = tlist->key; #endif vstr = tlist->data ? sh_double_quote ((char *)tlist->data) : (char *)0; elen = STRLEN (istr) + 8 + STRLEN (vstr); RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize); ret[rlen++] = '['; strcpy (ret+rlen, istr); rlen += STRLEN (istr); ret[rlen++] = ']'; ret[rlen++] = '='; if (vstr) { strcpy (ret + rlen, vstr); rlen += STRLEN (vstr); } ret[rlen++] = ' '; if (istr != tlist->key) FREE (istr); FREE (vstr); } RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8); ret[rlen++] = ')'; ret[rlen] = '\0'; if (quoted) { vstr = sh_single_quote (ret); free (ret); ret = vstr; } return ret; } static WORD_LIST * assoc_to_word_list_internal (h, t) HASH_TABLE *h; int t; { WORD_LIST *list; int i; BUCKET_CONTENTS *tlist; char *w; if (h == 0 || assoc_empty (h)) return((WORD_LIST *)NULL); list = (WORD_LIST *)NULL; for (i = 0; i < h->nbuckets; i++) for (tlist = hash_items (i, h); tlist; tlist = tlist->next) { w = (t == 0) ? (char *)tlist->data : (char *)tlist->key; list = make_word_list (make_bare_word(w), list); } return (REVERSE_LIST(list, WORD_LIST *)); } WORD_LIST * assoc_to_word_list (h) HASH_TABLE *h; { return (assoc_to_word_list_internal (h, 0)); } WORD_LIST * assoc_keys_to_word_list (h) HASH_TABLE *h; { return (assoc_to_word_list_internal (h, 1)); } char * assoc_to_string (h, sep, quoted) HASH_TABLE *h; char *sep; int quoted; { BUCKET_CONTENTS *tlist; int i; char *result, *t, *w; WORD_LIST *list, *l; if (h == 0) return ((char *)NULL); if (assoc_empty (h)) return (savestring ("")); result = NULL; list = NULL; /* This might be better implemented directly, but it's simple to implement by converting to a word list first, possibly quoting the data, then using list_string */ for (i = 0; i < h->nbuckets; i++) for (tlist = hash_items (i, h); tlist; tlist = tlist->next) { w = (char *)tlist->data; if (w == 0) continue; t = quoted ? quote_string (w) : savestring (w); list = make_word_list (make_bare_word(t), list); FREE (t); } l = REVERSE_LIST(list, WORD_LIST *); result = l ? string_list_internal (l, sep) : savestring (""); return result; } #endif /* ARRAY_VARS */