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