summaryrefslogtreecommitdiffstats
path: root/assoc.c
diff options
context:
space:
mode:
Diffstat (limited to 'assoc.c')
-rw-r--r--assoc.c587
1 files changed, 587 insertions, 0 deletions
diff --git a/assoc.c b/assoc.c
new file mode 100644
index 0000000..5782e85
--- /dev/null
+++ b/assoc.c
@@ -0,0 +1,587 @@
+/*
+ * assoc.c - functions to manipulate associative arrays
+ *
+ * Associative arrays are standard shell hash tables.
+ *
+ * Chet Ramey
+ * chet@ins.cwru.edu
+ */
+
+/* Copyright (C) 2008,2009,2011-2020 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#if defined (ARRAY_VARS)
+
+#if defined (HAVE_UNISTD_H)
+# ifdef _MINIX
+# include <sys/types.h>
+# endif
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include "bashansi.h"
+
+#include "shell.h"
+#include "array.h"
+#include "assoc.h"
+#include "builtins/common.h"
+
+static WORD_LIST *assoc_to_word_list_internal PARAMS((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;
+ /* If we are overwriting an existing element's value, we're not going to
+ use the key. Nothing in the array assignment code path frees the key
+ string, so we can free it here to avoid a memory leak. */
+ if (b->key != key)
+ free (key);
+ FREE (b->data);
+ b->data = value ? savestring (value) : (char *)0;
+ return (0);
+}
+
+/* Like assoc_insert, but returns b->data instead of freeing it */
+PTR_T
+assoc_replace (hash, key, value)
+ HASH_TABLE *hash;
+ char *key;
+ char *value;
+{
+ BUCKET_CONTENTS *b;
+ PTR_T t;
+
+ b = hash_search (key, hash, HASH_CREATE);
+ if (b == 0)
+ return (PTR_T)0;
+ /* If we are overwriting an existing element's value, we're not going to
+ use the key. Nothing in the array assignment code path frees the key
+ string, so we can free it here to avoid a memory leak. */
+ if (b->key != key)
+ free (key);
+ t = b->data;
+ b->data = value ? savestring (value) : (char *)0;
+ return t;
+}
+
+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, pflags)
+ HASH_TABLE *hash;
+ arrayind_t start, nelem;
+ int starsub, quoted, pflags;
+{
+ 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)
+ {
+ dispose_words (save);
+ 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, pflags);
+
+ 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;
+{
+ char *t;
+ int pchar, qflags, pflags;
+ WORD_LIST *wl, *save;
+
+ if (h == 0 || assoc_empty (h))
+ return ((char *)NULL);
+
+ wl = assoc_to_word_list (h);
+ if (wl == 0)
+ return (char *)NULL;
+
+ for (save = wl; wl; wl = wl->next)
+ {
+ t = pat_subst (wl->word->word, pat, rep, mflags);
+ FREE (wl->word->word);
+ wl->word->word = t;
+ }
+
+ pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
+ qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
+ pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
+
+ t = string_list_pos_params (pchar, save, qflags, pflags);
+ dispose_words (save);
+
+ return t;
+}
+
+char *
+assoc_modcase (h, pat, modop, mflags)
+ HASH_TABLE *h;
+ char *pat;
+ int modop;
+ int mflags;
+{
+ char *t;
+ int pchar, qflags, pflags;
+ WORD_LIST *wl, *save;
+
+ if (h == 0 || assoc_empty (h))
+ return ((char *)NULL);
+
+ wl = assoc_to_word_list (h);
+ if (wl == 0)
+ return ((char *)NULL);
+
+ for (save = wl; wl; wl = wl->next)
+ {
+ t = sh_modcase (wl->word->word, pat, modop);
+ FREE (wl->word->word);
+ wl->word->word = t;
+ }
+
+ pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
+ qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
+ pflags = (mflags & MATCH_ASSIGNRHS) == MATCH_ASSIGNRHS ? PF_ASSIGNRHS : 0;
+
+ t = string_list_pos_params (pchar, save, qflags, pflags);
+ dispose_words (save);
+
+ return t;
+}
+
+char *
+assoc_to_kvpair (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[rlen = 0] = '\0';
+
+ for (i = 0; i < hash->nbuckets; i++)
+ for (tlist = hash_items (i, hash); tlist; tlist = tlist->next)
+ {
+ if (ansic_shouldquote (tlist->key))
+ istr = ansic_quote (tlist->key, 0, (int *)0);
+ else if (sh_contains_shell_metas (tlist->key))
+ istr = sh_double_quote (tlist->key);
+ else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
+ istr = sh_double_quote (tlist->key);
+ else
+ istr = tlist->key;
+
+ vstr = tlist->data ? (ansic_shouldquote ((char *)tlist->data) ?
+ ansic_quote ((char *)tlist->data, 0, (int *)0) :
+ sh_double_quote ((char *)tlist->data))
+ : (char *)0;
+
+ elen = STRLEN (istr) + 4 + STRLEN (vstr);
+ RESIZE_MALLOCED_BUFFER (ret, rlen, (elen+1), rsize, rsize);
+
+ strcpy (ret+rlen, istr);
+ rlen += STRLEN (istr);
+ ret[rlen++] = ' ';
+ if (vstr)
+ {
+ strcpy (ret + rlen, vstr);
+ rlen += STRLEN (vstr);
+ }
+ else
+ {
+ strcpy (ret + rlen, "\"\"");
+ rlen += 2;
+ }
+ ret[rlen++] = ' ';
+
+ if (istr != tlist->key)
+ FREE (istr);
+
+ FREE (vstr);
+ }
+
+ RESIZE_MALLOCED_BUFFER (ret, rlen, 1, rsize, 8);
+ ret[rlen] = '\0';
+
+ if (quoted)
+ {
+ vstr = sh_single_quote (ret);
+ free (ret);
+ ret = vstr;
+ }
+
+ return ret;
+}
+
+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 (ansic_shouldquote (tlist->key))
+ istr = ansic_quote (tlist->key, 0, (int *)0);
+ else if (sh_contains_shell_metas (tlist->key))
+ istr = sh_double_quote (tlist->key);
+ else if (ALL_ELEMENT_SUB (tlist->key[0]) && tlist->key[1] == '\0')
+ istr = sh_double_quote (tlist->key);
+ else
+ istr = tlist->key;
+
+ vstr = tlist->data ? (ansic_shouldquote ((char *)tlist->data) ?
+ ansic_quote ((char *)tlist->data, 0, (int *)0) :
+ 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;
+ l = 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 ("");
+ dispose_words (l);
+
+ return result;
+}
+
+#endif /* ARRAY_VARS */