1/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
2
3/* Copyright (C) 2001-2006 Free Software Foundation, Inc.
4
5   This file is part of GNU Bash, the Bourne Again SHell.
6
7   Bash is free software; you can redistribute it and/or modify it under
8   the terms of the GNU General Public License as published by the Free
9   Software Foundation; either version 2, or (at your option) any later
10   version.
11
12   Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or
14   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15   for more details.
16
17   You should have received a copy of the GNU General Public License along
18   with Bash; see the file COPYING.  If not, write to the Free Software
19   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20
21#include "config.h"
22
23#if defined (ARRAY_VARS)
24
25#if defined (HAVE_UNISTD_H)
26#  include <unistd.h>
27#endif
28#include <stdio.h>
29
30#include "bashintl.h"
31
32#include "shell.h"
33
34#include "shmbutil.h"
35
36#include "builtins/common.h"
37
38extern char *this_command_name;
39extern int last_command_exit_value;
40extern int array_needs_making;
41
42static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, int));
43
44static void quote_array_assignment_chars __P((WORD_LIST *));
45static char *array_value_internal __P((char *, int, int, int *));
46
47/* Standard error message to use when encountering an invalid array subscript */
48char *bash_badsub_errmsg = N_("bad array subscript");
49
50/* **************************************************************** */
51/*								    */
52/*  Functions to manipulate array variables and perform assignments */
53/*								    */
54/* **************************************************************** */
55
56/* Convert a shell variable to an array variable.  The original value is
57   saved as array[0]. */
58SHELL_VAR *
59convert_var_to_array (var)
60     SHELL_VAR *var;
61{
62  char *oldval;
63  ARRAY *array;
64
65  oldval = value_cell (var);
66  array = array_create ();
67  if (oldval)
68    array_insert (array, 0, oldval);
69
70  FREE (value_cell (var));
71  var_setarray (var, array);
72
73  /* these aren't valid anymore */
74  var->dynamic_value = (sh_var_value_func_t *)NULL;
75  var->assign_func = (sh_var_assign_func_t *)NULL;
76
77  INVALIDATE_EXPORTSTR (var);
78  if (exported_p (var))
79    array_needs_making++;
80
81  VSETATTR (var, att_array);
82  VUNSETATTR (var, att_invisible);
83
84  return var;
85}
86
87static SHELL_VAR *
88bind_array_var_internal (entry, ind, value, flags)
89     SHELL_VAR *entry;
90     arrayind_t ind;
91     char *value;
92     int flags;
93{
94  SHELL_VAR *dentry;
95  char *newval;
96
97  /* If we're appending, we need the old value of the array reference, so
98     fake out make_variable_value with a dummy SHELL_VAR */
99  if (flags & ASS_APPEND)
100    {
101      dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
102      dentry->name = savestring (entry->name);
103      newval = array_reference (array_cell (entry), ind);
104      if (newval)
105	dentry->value = savestring (newval);
106      else
107	{
108	  dentry->value = (char *)xmalloc (1);
109	  dentry->value[0] = '\0';
110	}
111      dentry->exportstr = 0;
112      dentry->attributes = entry->attributes & ~(att_array|att_exported);
113      /* Leave the rest of the members uninitialized; the code doesn't look
114	 at them. */
115      newval = make_variable_value (dentry, value, flags);
116      dispose_variable (dentry);
117    }
118  else
119    newval = make_variable_value (entry, value, flags);
120
121  if (entry->assign_func)
122    (*entry->assign_func) (entry, newval, ind);
123  else
124    array_insert (array_cell (entry), ind, newval);
125  FREE (newval);
126
127  return (entry);
128}
129
130/* Perform an array assignment name[ind]=value.  If NAME already exists and
131   is not an array, and IND is 0, perform name=value instead.  If NAME exists
132   and is not an array, and IND is not 0, convert it into an array with the
133   existing value as name[0].
134
135   If NAME does not exist, just create an array variable, no matter what
136   IND's value may be. */
137SHELL_VAR *
138bind_array_variable (name, ind, value, flags)
139     char *name;
140     arrayind_t ind;
141     char *value;
142     int flags;
143{
144  SHELL_VAR *entry;
145
146  entry = var_lookup (name, shell_variables);
147
148  if (entry == (SHELL_VAR *) 0)
149    entry = make_new_array_variable (name);
150  else if (readonly_p (entry) || noassign_p (entry))
151    {
152      if (readonly_p (entry))
153	err_readonly (name);
154      return (entry);
155    }
156  else if (array_p (entry) == 0)
157    entry = convert_var_to_array (entry);
158
159  /* ENTRY is an array variable, and ARRAY points to the value. */
160  return (bind_array_var_internal (entry, ind, value, flags));
161}
162
163/* Parse NAME, a lhs of an assignment statement of the form v[s], and
164   assign VALUE to that array element by calling bind_array_variable(). */
165SHELL_VAR *
166assign_array_element (name, value, flags)
167     char *name, *value;
168     int flags;
169{
170  char *sub, *vname;
171  arrayind_t ind;
172  int sublen;
173  SHELL_VAR *entry;
174
175  vname = array_variable_name (name, &sub, &sublen);
176
177  if (vname == 0)
178    return ((SHELL_VAR *)NULL);
179
180  if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
181    {
182      free (vname);
183      err_badarraysub (name);
184      return ((SHELL_VAR *)NULL);
185    }
186
187  ind = array_expand_index (sub, sublen);
188  if (ind < 0)
189    {
190      free (vname);
191      err_badarraysub (name);
192      return ((SHELL_VAR *)NULL);
193    }
194
195  entry = bind_array_variable (vname, ind, value, flags);
196
197  free (vname);
198  return (entry);
199}
200
201/* Find the array variable corresponding to NAME.  If there is no variable,
202   create a new array variable.  If the variable exists but is not an array,
203   convert it to an indexed array.  If CHECK_FLAGS is non-zero, an existing
204   variable is checked for the readonly or noassign attribute in preparation
205   for assignment (e.g., by the `read' builtin). */
206SHELL_VAR *
207find_or_make_array_variable (name, check_flags)
208     char *name;
209     int check_flags;
210{
211  SHELL_VAR *var;
212
213  var = find_variable (name);
214
215  if (var == 0)
216    var = make_new_array_variable (name);
217  else if (check_flags && (readonly_p (var) || noassign_p (var)))
218    {
219      if (readonly_p (var))
220	err_readonly (name);
221      return ((SHELL_VAR *)NULL);
222    }
223  else if (array_p (var) == 0)
224    var = convert_var_to_array (var);
225
226  return (var);
227}
228
229/* Perform a compound assignment statement for array NAME, where VALUE is
230   the text between the parens:  NAME=( VALUE ) */
231SHELL_VAR *
232assign_array_from_string (name, value, flags)
233     char *name, *value;
234     int flags;
235{
236  SHELL_VAR *var;
237
238  var = find_or_make_array_variable (name, 1);
239  if (var == 0)
240    return ((SHELL_VAR *)NULL);
241
242  return (assign_array_var_from_string (var, value, flags));
243}
244
245/* Sequentially assign the indices of indexed array variable VAR from the
246   words in LIST. */
247SHELL_VAR *
248assign_array_var_from_word_list (var, list, flags)
249     SHELL_VAR *var;
250     WORD_LIST *list;
251     int flags;
252{
253  register arrayind_t i;
254  register WORD_LIST *l;
255  ARRAY *a;
256
257  a = array_cell (var);
258  i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
259
260  for (l = list; l; l = l->next, i++)
261    if (var->assign_func)
262      (*var->assign_func) (var, l->word->word, i);
263    else
264      array_insert (a, i, l->word->word);
265  return var;
266}
267
268WORD_LIST *
269expand_compound_array_assignment (value, flags)
270     char *value;
271     int flags;
272{
273  WORD_LIST *list, *nlist;
274  char *val;
275  int ni;
276
277  /* I don't believe this condition is ever true any more. */
278  if (*value == '(')	/*)*/
279    {
280      ni = 1;
281      val = extract_array_assignment_list (value, &ni);
282      if (val == 0)
283	return (WORD_LIST *)NULL;
284    }
285  else
286    val = value;
287
288  /* Expand the value string into a list of words, performing all the
289     shell expansions including pathname generation and word splitting. */
290  /* First we split the string on whitespace, using the shell parser
291     (ksh93 seems to do this). */
292  list = parse_string_to_word_list (val, 1, "array assign");
293
294  /* If we're using [subscript]=value, we need to quote each [ and ] to
295     prevent unwanted filename expansion. */
296  if (list)
297    quote_array_assignment_chars (list);
298
299  /* Now that we've split it, perform the shell expansions on each
300     word in the list. */
301  nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
302
303  dispose_words (list);
304
305  if (val != value)
306    free (val);
307
308  return nlist;
309}
310
311void
312assign_compound_array_list (var, nlist, flags)
313     SHELL_VAR *var;
314     WORD_LIST *nlist;
315     int flags;
316{
317  ARRAY *a;
318  WORD_LIST *list;
319  char *w, *val, *nval;
320  int len, iflags;
321  arrayind_t ind, last_ind;
322
323  a = array_cell (var);
324
325  /* Now that we are ready to assign values to the array, kill the existing
326     value. */
327  if (a && (flags & ASS_APPEND) == 0)
328    array_flush (a);
329  last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
330
331  for (list = nlist; list; list = list->next)
332    {
333      iflags = flags;
334      w = list->word->word;
335
336      /* We have a word of the form [ind]=value */
337      if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
338	{
339	  len = skipsubscript (w, 0);
340
341	  /* XXX - changes for `+=' */
342 	  if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
343	    {
344	      nval = make_variable_value (var, w, flags);
345	      if (var->assign_func)
346		(*var->assign_func) (var, nval, last_ind);
347	      else
348		array_insert (a, last_ind, nval);
349	      FREE (nval);
350	      last_ind++;
351	      continue;
352	    }
353
354	  if (len == 1)
355	    {
356	      err_badarraysub (w);
357	      continue;
358	    }
359
360	  if (ALL_ELEMENT_SUB (w[1]) && len == 2)
361	    {
362	      report_error (_("%s: cannot assign to non-numeric index"), w);
363	      continue;
364	    }
365
366	  ind = array_expand_index (w + 1, len);
367	  if (ind < 0)
368	    {
369	      err_badarraysub (w);
370	      continue;
371	    }
372	  last_ind = ind;
373	  /* XXX - changes for `+=' -- just accept the syntax.  ksh93 doesn't do this */
374	  if (w[len + 1] == '+' && w[len + 2] == '=')
375	    {
376	      iflags |= ASS_APPEND;
377	      val = w + len + 3;
378	    }
379	  else
380	    val = w + len + 2;
381	}
382      else		/* No [ind]=value, just a stray `=' */
383	{
384	  ind = last_ind;
385	  val = w;
386	}
387
388      if (integer_p (var))
389	this_command_name = (char *)NULL;	/* no command name for errors */
390      bind_array_var_internal (var, ind, val, iflags);
391      last_ind++;
392    }
393}
394
395/* Perform a compound array assignment:  VAR->name=( VALUE ).  The
396   VALUE has already had the parentheses stripped. */
397SHELL_VAR *
398assign_array_var_from_string (var, value, flags)
399     SHELL_VAR *var;
400     char *value;
401     int flags;
402{
403  WORD_LIST *nlist;
404
405  if (value == 0)
406    return var;
407
408  nlist = expand_compound_array_assignment (value, flags);
409  assign_compound_array_list (var, nlist, flags);
410
411  if (nlist)
412    dispose_words (nlist);
413  return (var);
414}
415
416/* For each word in a compound array assignment, if the word looks like
417   [ind]=value, quote the `[' and `]' before the `=' to protect them from
418   unwanted filename expansion. */
419static void
420quote_array_assignment_chars (list)
421     WORD_LIST *list;
422{
423  char *s, *t, *nword;
424  int saw_eq;
425  WORD_LIST *l;
426
427  for (l = list; l; l = l->next)
428    {
429      if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
430	continue;	/* should not happen, but just in case... */
431      /* Don't bother if it doesn't look like [ind]=value */
432      if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
433	continue;
434      s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1);
435      saw_eq = 0;
436      for (t = l->word->word; *t; )
437	{
438	  if (*t == '=')
439	    saw_eq = 1;
440	  if (saw_eq == 0 && (*t == '[' || *t == ']'))
441	    *s++ = '\\';
442	  *s++ = *t++;
443	}
444      *s = '\0';
445      free (l->word->word);
446      l->word->word = nword;
447    }
448}
449
450/* This function assumes s[i] == '['; returns with s[ret] == ']' if
451   an array subscript is correctly parsed. */
452int
453skipsubscript (s, i)
454     const char *s;
455     int i;
456{
457  int count, c;
458#if defined (HANDLE_MULTIBYTE)
459  mbstate_t state, state_bak;
460  size_t slength, mblength;
461#endif
462
463#if defined (HANDLE_MULTIBYTE)
464  memset (&state, '\0', sizeof (mbstate_t));
465  slength = strlen (s + i);
466#endif
467
468  count = 1;
469  while (count)
470    {
471      /* Advance one (possibly multibyte) character in S starting at I. */
472#if defined (HANDLE_MULTIBYTE)
473      if (MB_CUR_MAX > 1)
474	{
475	  state_bak = state;
476	  mblength = mbrlen (s + i, slength, &state);
477
478	  if (MB_INVALIDCH (mblength))
479	    {
480	      state = state_bak;
481	      i++;
482	      slength--;
483	    }
484	  else if (MB_NULLWCH (mblength))
485	    return i;
486	  else
487	    {
488	      i += mblength;
489	      slength -= mblength;
490	    }
491	}
492      else
493#endif
494      ++i;
495
496      c = s[i];
497
498      if (c == 0)
499        break;
500      else if (c == '[')
501	count++;
502      else if (c == ']')
503	count--;
504    }
505
506  return i;
507}
508
509/* This function is called with SUB pointing to just after the beginning
510   `[' of an array subscript and removes the array element to which SUB
511   expands from array VAR.  A subscript of `*' or `@' unsets the array. */
512int
513unbind_array_element (var, sub)
514     SHELL_VAR *var;
515     char *sub;
516{
517  int len;
518  arrayind_t ind;
519  ARRAY_ELEMENT *ae;
520
521  len = skipsubscript (sub, 0);
522  if (sub[len] != ']' || len == 0)
523    {
524      builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
525      return -1;
526    }
527  sub[len] = '\0';
528
529  if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
530    {
531      unbind_variable (var->name);
532      return (0);
533    }
534  ind = array_expand_index (sub, len+1);
535  if (ind < 0)
536    {
537      builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
538      return -1;
539    }
540  ae = array_remove (array_cell (var), ind);
541  if (ae)
542    array_dispose_element (ae);
543  return 0;
544}
545
546/* Format and output an array assignment in compound form VAR=(VALUES),
547   suitable for re-use as input. */
548void
549print_array_assignment (var, quoted)
550     SHELL_VAR *var;
551     int quoted;
552{
553  char *vstr;
554
555  vstr = array_to_assign (array_cell (var), quoted);
556
557  if (vstr == 0)
558    printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
559  else
560    {
561      printf ("%s=%s\n", var->name, vstr);
562      free (vstr);
563    }
564}
565
566/***********************************************************************/
567/*								       */
568/* Utility functions to manage arrays and their contents for expansion */
569/*								       */
570/***********************************************************************/
571
572/* Return 1 if NAME is a properly-formed array reference v[sub]. */
573int
574valid_array_reference (name)
575     char *name;
576{
577  char *t;
578  int r, len;
579
580  t = xstrchr (name, '[');	/* ] */
581  if (t)
582    {
583      *t = '\0';
584      r = legal_identifier (name);
585      *t = '[';
586      if (r == 0)
587	return 0;
588      /* Check for a properly-terminated non-blank subscript. */
589      len = skipsubscript (t, 0);
590      if (t[len] != ']' || len == 1)
591	return 0;
592      for (r = 1; r < len; r++)
593	if (whitespace (t[r]) == 0)
594	  return 1;
595      return 0;
596    }
597  return 0;
598}
599
600/* Expand the array index beginning at S and extending LEN characters. */
601arrayind_t
602array_expand_index (s, len)
603     char *s;
604     int len;
605{
606  char *exp, *t;
607  int expok;
608  arrayind_t val;
609
610  exp = (char *)xmalloc (len);
611  strncpy (exp, s, len - 1);
612  exp[len - 1] = '\0';
613  t = expand_arith_string (exp, 0);
614  this_command_name = (char *)NULL;
615  val = evalexp (t, &expok);
616  free (t);
617  free (exp);
618  if (expok == 0)
619    {
620      last_command_exit_value = EXECUTION_FAILURE;
621
622      top_level_cleanup ();
623      jump_to_top_level (DISCARD);
624    }
625  return val;
626}
627
628/* Return the name of the variable specified by S without any subscript.
629   If SUBP is non-null, return a pointer to the start of the subscript
630   in *SUBP. If LENP is non-null, the length of the subscript is returned
631   in *LENP.  This returns newly-allocated memory. */
632char *
633array_variable_name (s, subp, lenp)
634     char *s, **subp;
635     int *lenp;
636{
637  char *t, *ret;
638  int ind, ni;
639
640  t = xstrchr (s, '[');
641  if (t == 0)
642    {
643      if (subp)
644      	*subp = t;
645      if (lenp)
646	*lenp = 0;
647      return ((char *)NULL);
648    }
649  ind = t - s;
650  ni = skipsubscript (s, ind);
651  if (ni <= ind + 1 || s[ni] != ']')
652    {
653      err_badarraysub (s);
654      if (subp)
655      	*subp = t;
656      if (lenp)
657	*lenp = 0;
658      return ((char *)NULL);
659    }
660
661  *t = '\0';
662  ret = savestring (s);
663  *t++ = '[';		/* ] */
664
665  if (subp)
666    *subp = t;
667  if (lenp)
668    *lenp = ni - ind;
669
670  return ret;
671}
672
673/* Return the variable specified by S without any subscript.  If SUBP is
674   non-null, return a pointer to the start of the subscript in *SUBP.
675   If LENP is non-null, the length of the subscript is returned in *LENP. */
676SHELL_VAR *
677array_variable_part (s, subp, lenp)
678     char *s, **subp;
679     int *lenp;
680{
681  char *t;
682  SHELL_VAR *var;
683
684  t = array_variable_name (s, subp, lenp);
685  if (t == 0)
686    return ((SHELL_VAR *)NULL);
687  var = find_variable (t);
688
689  free (t);
690  return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
691}
692
693/* Return a string containing the elements in the array and subscript
694   described by S.  If the subscript is * or @, obeys quoting rules akin
695   to the expansion of $* and $@ including double quoting.  If RTYPE
696   is non-null it gets 1 if the array reference is name[@] or name[*]
697   and 0 otherwise. */
698static char *
699array_value_internal (s, quoted, allow_all, rtype)
700     char *s;
701     int quoted, allow_all, *rtype;
702{
703  int len;
704  arrayind_t ind;
705  char *retval, *t, *temp;
706  WORD_LIST *l;
707  SHELL_VAR *var;
708
709  var = array_variable_part (s, &t, &len);
710
711  /* Expand the index, even if the variable doesn't exist, in case side
712     effects are needed, like ${w[i++]} where w is unset. */
713#if 0
714  if (var == 0)
715    return (char *)NULL;
716#endif
717
718  if (len == 0)
719    return ((char *)NULL);	/* error message already printed */
720
721  /* [ */
722  if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
723    {
724      if (rtype)
725	*rtype = (t[0] == '*') ? 1 : 2;
726      if (allow_all == 0)
727	{
728	  err_badarraysub (s);
729	  return ((char *)NULL);
730	}
731      else if (var == 0 || value_cell (var) == 0)
732	return ((char *)NULL);
733      else if (array_p (var) == 0)
734	l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
735      else
736	{
737	  l = array_to_word_list (array_cell (var));
738	  if (l == (WORD_LIST *)NULL)
739	    return ((char *) NULL);
740	}
741
742      if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
743	{
744	  temp = string_list_dollar_star (l);
745	  retval = quote_string (temp);
746	  free (temp);
747	}
748      else	/* ${name[@]} or unquoted ${name[*]} */
749	retval = string_list_dollar_at (l, quoted);
750
751      dispose_words (l);
752    }
753  else
754    {
755      if (rtype)
756	*rtype = 0;
757      ind = array_expand_index (t, len);
758      if (ind < 0)
759	{
760	  if (var)
761	    err_badarraysub (var->name);
762	  else
763	    {
764	      t[-1] = '\0';
765	      err_badarraysub (s);
766	      t[-1] = '[';	/* ] */
767	    }
768	  return ((char *)NULL);
769	}
770      if (var == 0)
771	return ((char *)NULL);
772      if (array_p (var) == 0)
773	return (ind == 0 ? value_cell (var) : (char *)NULL);
774      retval = array_reference (array_cell (var), ind);
775    }
776
777  return retval;
778}
779
780/* Return a string containing the elements described by the array and
781   subscript contained in S, obeying quoting for subscripts * and @. */
782char *
783array_value (s, quoted, rtype)
784     char *s;
785     int quoted, *rtype;
786{
787  return (array_value_internal (s, quoted, 1, rtype));
788}
789
790/* Return the value of the array indexing expression S as a single string.
791   If ALLOW_ALL is 0, do not allow `@' and `*' subscripts.  This is used
792   by other parts of the shell such as the arithmetic expression evaluator
793   in expr.c. */
794char *
795get_array_value (s, allow_all, rtype)
796     char *s;
797     int allow_all, *rtype;
798{
799  return (array_value_internal (s, 0, allow_all, rtype));
800}
801
802char *
803array_keys (s, quoted)
804     char *s;
805     int quoted;
806{
807  int len;
808  char *retval, *t, *temp;
809  WORD_LIST *l;
810  SHELL_VAR *var;
811
812  var = array_variable_part (s, &t, &len);
813
814  /* [ */
815  if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
816    return (char *)NULL;
817
818  if (array_p (var) == 0)
819    l = add_string_to_list ("0", (WORD_LIST *)NULL);
820  else
821    {
822      l = array_keys_to_word_list (array_cell (var));
823      if (l == (WORD_LIST *)NULL)
824        return ((char *) NULL);
825    }
826
827  if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
828    {
829      temp = string_list_dollar_star (l);
830      retval = quote_string (temp);
831      free (temp);
832    }
833  else	/* ${!name[@]} or unquoted ${!name[*]} */
834    retval = string_list_dollar_at (l, quoted);
835
836  dispose_words (l);
837  return retval;
838}
839#endif /* ARRAY_VARS */
840