| 1 | /* arrayfunc.c -- High-level array functions used by other parts of the shell. */ | 
|---|
| 2 |  | 
|---|
| 3 | /* Copyright (C) 2001-2005 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 |  | 
|---|
| 38 | extern char *this_command_name; | 
|---|
| 39 | extern int last_command_exit_value; | 
|---|
| 40 | extern int array_needs_making; | 
|---|
| 41 |  | 
|---|
| 42 | static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, int)); | 
|---|
| 43 |  | 
|---|
| 44 | static void quote_array_assignment_chars __P((WORD_LIST *)); | 
|---|
| 45 | static char *array_value_internal __P((char *, int, int, int *)); | 
|---|
| 46 |  | 
|---|
| 47 | /* Standard error message to use when encountering an invalid array subscript */ | 
|---|
| 48 | char *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]. */ | 
|---|
| 58 | SHELL_VAR * | 
|---|
| 59 | convert_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 |  | 
|---|
| 87 | static SHELL_VAR * | 
|---|
| 88 | bind_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. */ | 
|---|
| 137 | SHELL_VAR * | 
|---|
| 138 | bind_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(). */ | 
|---|
| 165 | SHELL_VAR * | 
|---|
| 166 | assign_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). */ | 
|---|
| 206 | SHELL_VAR * | 
|---|
| 207 | find_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 ) */ | 
|---|
| 231 | SHELL_VAR * | 
|---|
| 232 | assign_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. */ | 
|---|
| 247 | SHELL_VAR * | 
|---|
| 248 | assign_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 |  | 
|---|
| 268 | /* Perform a compound array assignment:  VAR->name=( VALUE ).  The | 
|---|
| 269 | VALUE has already had the parentheses stripped. */ | 
|---|
| 270 | SHELL_VAR * | 
|---|
| 271 | assign_array_var_from_string (var, value, flags) | 
|---|
| 272 | SHELL_VAR *var; | 
|---|
| 273 | char *value; | 
|---|
| 274 | int flags; | 
|---|
| 275 | { | 
|---|
| 276 | ARRAY *a; | 
|---|
| 277 | WORD_LIST *list, *nlist; | 
|---|
| 278 | char *w, *val, *nval; | 
|---|
| 279 | int ni, len; | 
|---|
| 280 | arrayind_t ind, last_ind; | 
|---|
| 281 |  | 
|---|
| 282 | if (value == 0) | 
|---|
| 283 | return var; | 
|---|
| 284 |  | 
|---|
| 285 | /* If this is called from declare_builtin, value[0] == '(' and | 
|---|
| 286 | xstrchr(value, ')') != 0.  In this case, we need to extract | 
|---|
| 287 | the value from between the parens before going on. */ | 
|---|
| 288 | if (*value == '(')    /*)*/ | 
|---|
| 289 | { | 
|---|
| 290 | ni = 1; | 
|---|
| 291 | val = extract_array_assignment_list (value, &ni); | 
|---|
| 292 | if (val == 0) | 
|---|
| 293 | return var; | 
|---|
| 294 | } | 
|---|
| 295 | else | 
|---|
| 296 | val = value; | 
|---|
| 297 |  | 
|---|
| 298 | /* Expand the value string into a list of words, performing all the | 
|---|
| 299 | shell expansions including pathname generation and word splitting. */ | 
|---|
| 300 | /* First we split the string on whitespace, using the shell parser | 
|---|
| 301 | (ksh93 seems to do this). */ | 
|---|
| 302 | list = parse_string_to_word_list (val, 1, "array assign"); | 
|---|
| 303 |  | 
|---|
| 304 | /* If we're using [subscript]=value, we need to quote each [ and ] to | 
|---|
| 305 | prevent unwanted filename expansion. */ | 
|---|
| 306 | if (list) | 
|---|
| 307 | quote_array_assignment_chars (list); | 
|---|
| 308 |  | 
|---|
| 309 | /* Now that we've split it, perform the shell expansions on each | 
|---|
| 310 | word in the list. */ | 
|---|
| 311 | nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL; | 
|---|
| 312 |  | 
|---|
| 313 | dispose_words (list); | 
|---|
| 314 |  | 
|---|
| 315 | if (val != value) | 
|---|
| 316 | free (val); | 
|---|
| 317 |  | 
|---|
| 318 | a = array_cell (var); | 
|---|
| 319 |  | 
|---|
| 320 | /* Now that we are ready to assign values to the array, kill the existing | 
|---|
| 321 | value. */ | 
|---|
| 322 | if (a && (flags & ASS_APPEND) == 0) | 
|---|
| 323 | array_flush (a); | 
|---|
| 324 | last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0; | 
|---|
| 325 |  | 
|---|
| 326 | for (list = nlist; list; list = list->next) | 
|---|
| 327 | { | 
|---|
| 328 | w = list->word->word; | 
|---|
| 329 |  | 
|---|
| 330 | /* We have a word of the form [ind]=value */ | 
|---|
| 331 | if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[') | 
|---|
| 332 | { | 
|---|
| 333 | len = skipsubscript (w, 0); | 
|---|
| 334 |  | 
|---|
| 335 | #if 1 | 
|---|
| 336 | /* XXX - changes for `+=' */ | 
|---|
| 337 | if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '='))) | 
|---|
| 338 | #else | 
|---|
| 339 | if (w[len] != ']' || w[len+1] != '=') | 
|---|
| 340 | #endif | 
|---|
| 341 | { | 
|---|
| 342 | nval = make_variable_value (var, w, flags); | 
|---|
| 343 | if (var->assign_func) | 
|---|
| 344 | (*var->assign_func) (var, nval, last_ind); | 
|---|
| 345 | else | 
|---|
| 346 | array_insert (a, last_ind, nval); | 
|---|
| 347 | FREE (nval); | 
|---|
| 348 | last_ind++; | 
|---|
| 349 | continue; | 
|---|
| 350 | } | 
|---|
| 351 |  | 
|---|
| 352 | if (len == 1) | 
|---|
| 353 | { | 
|---|
| 354 | err_badarraysub (w); | 
|---|
| 355 | continue; | 
|---|
| 356 | } | 
|---|
| 357 |  | 
|---|
| 358 | if (ALL_ELEMENT_SUB (w[1]) && len == 2) | 
|---|
| 359 | { | 
|---|
| 360 | report_error (_("%s: cannot assign to non-numeric index"), w); | 
|---|
| 361 | continue; | 
|---|
| 362 | } | 
|---|
| 363 |  | 
|---|
| 364 | ind = array_expand_index (w + 1, len); | 
|---|
| 365 | if (ind < 0) | 
|---|
| 366 | { | 
|---|
| 367 | err_badarraysub (w); | 
|---|
| 368 | continue; | 
|---|
| 369 | } | 
|---|
| 370 | last_ind = ind; | 
|---|
| 371 | /* XXX - changes for `+=' */ | 
|---|
| 372 | if (w[len + 1] == '+' && w[len + 2] == '=') | 
|---|
| 373 | { | 
|---|
| 374 | flags |= ASS_APPEND; | 
|---|
| 375 | val = w + len + 3; | 
|---|
| 376 | } | 
|---|
| 377 | else | 
|---|
| 378 | val = w + len + 2; | 
|---|
| 379 | } | 
|---|
| 380 | else              /* No [ind]=value, just a stray `=' */ | 
|---|
| 381 | { | 
|---|
| 382 | ind = last_ind; | 
|---|
| 383 | val = w; | 
|---|
| 384 | } | 
|---|
| 385 |  | 
|---|
| 386 | if (integer_p (var)) | 
|---|
| 387 | this_command_name = (char *)NULL;       /* no command name for errors */ | 
|---|
| 388 | bind_array_var_internal (var, ind, val, flags); | 
|---|
| 389 | last_ind++; | 
|---|
| 390 | } | 
|---|
| 391 |  | 
|---|
| 392 | dispose_words (nlist); | 
|---|
| 393 | return (var); | 
|---|
| 394 | } | 
|---|
| 395 |  | 
|---|
| 396 | /* For each word in a compound array assignment, if the word looks like | 
|---|
| 397 | [ind]=value, quote the `[' and `]' before the `=' to protect them from | 
|---|
| 398 | unwanted filename expansion. */ | 
|---|
| 399 | static void | 
|---|
| 400 | quote_array_assignment_chars (list) | 
|---|
| 401 | WORD_LIST *list; | 
|---|
| 402 | { | 
|---|
| 403 | char *s, *t, *nword; | 
|---|
| 404 | int saw_eq; | 
|---|
| 405 | WORD_LIST *l; | 
|---|
| 406 |  | 
|---|
| 407 | for (l = list; l; l = l->next) | 
|---|
| 408 | { | 
|---|
| 409 | if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0') | 
|---|
| 410 | continue;       /* should not happen, but just in case... */ | 
|---|
| 411 | /* Don't bother if it doesn't look like [ind]=value */ | 
|---|
| 412 | if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */ | 
|---|
| 413 | continue; | 
|---|
| 414 | s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1); | 
|---|
| 415 | saw_eq = 0; | 
|---|
| 416 | for (t = l->word->word; *t; ) | 
|---|
| 417 | { | 
|---|
| 418 | if (*t == '=') | 
|---|
| 419 | saw_eq = 1; | 
|---|
| 420 | if (saw_eq == 0 && (*t == '[' || *t == ']')) | 
|---|
| 421 | *s++ = '\\'; | 
|---|
| 422 | *s++ = *t++; | 
|---|
| 423 | } | 
|---|
| 424 | *s = '\0'; | 
|---|
| 425 | free (l->word->word); | 
|---|
| 426 | l->word->word = nword; | 
|---|
| 427 | } | 
|---|
| 428 | } | 
|---|
| 429 |  | 
|---|
| 430 | /* This function assumes s[i] == '['; returns with s[ret] == ']' if | 
|---|
| 431 | an array subscript is correctly parsed. */ | 
|---|
| 432 | int | 
|---|
| 433 | skipsubscript (s, i) | 
|---|
| 434 | const char *s; | 
|---|
| 435 | int i; | 
|---|
| 436 | { | 
|---|
| 437 | int count, c; | 
|---|
| 438 | #if defined (HANDLE_MULTIBYTE) | 
|---|
| 439 | mbstate_t state, state_bak; | 
|---|
| 440 | size_t slength, mblength; | 
|---|
| 441 | size_t mb_cur_max; | 
|---|
| 442 | #endif | 
|---|
| 443 |  | 
|---|
| 444 | #if defined (HANDLE_MULTIBYTE) | 
|---|
| 445 | memset (&state, '\0', sizeof (mbstate_t)); | 
|---|
| 446 | slength = strlen (s + i); | 
|---|
| 447 | mb_cur_max = MB_CUR_MAX; | 
|---|
| 448 | #endif | 
|---|
| 449 |  | 
|---|
| 450 | count = 1; | 
|---|
| 451 | while (count) | 
|---|
| 452 | { | 
|---|
| 453 | /* Advance one (possibly multibyte) character in S starting at I. */ | 
|---|
| 454 | #if defined (HANDLE_MULTIBYTE) | 
|---|
| 455 | if (mb_cur_max > 1) | 
|---|
| 456 | { | 
|---|
| 457 | state_bak = state; | 
|---|
| 458 | mblength = mbrlen (s + i, slength, &state); | 
|---|
| 459 |  | 
|---|
| 460 | if (MB_INVALIDCH (mblength)) | 
|---|
| 461 | { | 
|---|
| 462 | state = state_bak; | 
|---|
| 463 | i++; | 
|---|
| 464 | slength--; | 
|---|
| 465 | } | 
|---|
| 466 | else if (MB_NULLWCH (mblength)) | 
|---|
| 467 | return i; | 
|---|
| 468 | else | 
|---|
| 469 | { | 
|---|
| 470 | i += mblength; | 
|---|
| 471 | slength -= mblength; | 
|---|
| 472 | } | 
|---|
| 473 | } | 
|---|
| 474 | else | 
|---|
| 475 | #endif | 
|---|
| 476 | ++i; | 
|---|
| 477 |  | 
|---|
| 478 | c = s[i]; | 
|---|
| 479 |  | 
|---|
| 480 | if (c == 0) | 
|---|
| 481 | break; | 
|---|
| 482 | else if (c == '[') | 
|---|
| 483 | count++; | 
|---|
| 484 | else if (c == ']') | 
|---|
| 485 | count--; | 
|---|
| 486 | } | 
|---|
| 487 |  | 
|---|
| 488 | return i; | 
|---|
| 489 | } | 
|---|
| 490 |  | 
|---|
| 491 | /* This function is called with SUB pointing to just after the beginning | 
|---|
| 492 | `[' of an array subscript and removes the array element to which SUB | 
|---|
| 493 | expands from array VAR.  A subscript of `*' or `@' unsets the array. */ | 
|---|
| 494 | int | 
|---|
| 495 | unbind_array_element (var, sub) | 
|---|
| 496 | SHELL_VAR *var; | 
|---|
| 497 | char *sub; | 
|---|
| 498 | { | 
|---|
| 499 | int len; | 
|---|
| 500 | arrayind_t ind; | 
|---|
| 501 | ARRAY_ELEMENT *ae; | 
|---|
| 502 |  | 
|---|
| 503 | len = skipsubscript (sub, 0); | 
|---|
| 504 | if (sub[len] != ']' || len == 0) | 
|---|
| 505 | { | 
|---|
| 506 | builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg)); | 
|---|
| 507 | return -1; | 
|---|
| 508 | } | 
|---|
| 509 | sub[len] = '\0'; | 
|---|
| 510 |  | 
|---|
| 511 | if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0) | 
|---|
| 512 | { | 
|---|
| 513 | unbind_variable (var->name); | 
|---|
| 514 | return (0); | 
|---|
| 515 | } | 
|---|
| 516 | ind = array_expand_index (sub, len+1); | 
|---|
| 517 | if (ind < 0) | 
|---|
| 518 | { | 
|---|
| 519 | builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg)); | 
|---|
| 520 | return -1; | 
|---|
| 521 | } | 
|---|
| 522 | ae = array_remove (array_cell (var), ind); | 
|---|
| 523 | if (ae) | 
|---|
| 524 | array_dispose_element (ae); | 
|---|
| 525 | return 0; | 
|---|
| 526 | } | 
|---|
| 527 |  | 
|---|
| 528 | /* Format and output an array assignment in compound form VAR=(VALUES), | 
|---|
| 529 | suitable for re-use as input. */ | 
|---|
| 530 | void | 
|---|
| 531 | print_array_assignment (var, quoted) | 
|---|
| 532 | SHELL_VAR *var; | 
|---|
| 533 | int quoted; | 
|---|
| 534 | { | 
|---|
| 535 | char *vstr; | 
|---|
| 536 |  | 
|---|
| 537 | vstr = array_to_assign (array_cell (var), quoted); | 
|---|
| 538 |  | 
|---|
| 539 | if (vstr == 0) | 
|---|
| 540 | printf ("%s=%s\n", var->name, quoted ? "'()'" : "()"); | 
|---|
| 541 | else | 
|---|
| 542 | { | 
|---|
| 543 | printf ("%s=%s\n", var->name, vstr); | 
|---|
| 544 | free (vstr); | 
|---|
| 545 | } | 
|---|
| 546 | } | 
|---|
| 547 |  | 
|---|
| 548 | /***********************************************************************/ | 
|---|
| 549 | /*                                                                     */ | 
|---|
| 550 | /* Utility functions to manage arrays and their contents for expansion */ | 
|---|
| 551 | /*                                                                     */ | 
|---|
| 552 | /***********************************************************************/ | 
|---|
| 553 |  | 
|---|
| 554 | /* Return 1 if NAME is a properly-formed array reference v[sub]. */ | 
|---|
| 555 | int | 
|---|
| 556 | valid_array_reference (name) | 
|---|
| 557 | char *name; | 
|---|
| 558 | { | 
|---|
| 559 | char *t; | 
|---|
| 560 | int r, len; | 
|---|
| 561 |  | 
|---|
| 562 | t = xstrchr (name, '[');      /* ] */ | 
|---|
| 563 | if (t) | 
|---|
| 564 | { | 
|---|
| 565 | *t = '\0'; | 
|---|
| 566 | r = legal_identifier (name); | 
|---|
| 567 | *t = '['; | 
|---|
| 568 | if (r == 0) | 
|---|
| 569 | return 0; | 
|---|
| 570 | /* Check for a properly-terminated non-blank subscript. */ | 
|---|
| 571 | len = skipsubscript (t, 0); | 
|---|
| 572 | if (t[len] != ']' || len == 1) | 
|---|
| 573 | return 0; | 
|---|
| 574 | for (r = 1; r < len; r++) | 
|---|
| 575 | if (whitespace (t[r]) == 0) | 
|---|
| 576 | return 1; | 
|---|
| 577 | return 0; | 
|---|
| 578 | } | 
|---|
| 579 | return 0; | 
|---|
| 580 | } | 
|---|
| 581 |  | 
|---|
| 582 | /* Expand the array index beginning at S and extending LEN characters. */ | 
|---|
| 583 | arrayind_t | 
|---|
| 584 | array_expand_index (s, len) | 
|---|
| 585 | char *s; | 
|---|
| 586 | int len; | 
|---|
| 587 | { | 
|---|
| 588 | char *exp, *t; | 
|---|
| 589 | int expok; | 
|---|
| 590 | arrayind_t val; | 
|---|
| 591 |  | 
|---|
| 592 | exp = (char *)xmalloc (len); | 
|---|
| 593 | strncpy (exp, s, len - 1); | 
|---|
| 594 | exp[len - 1] = '\0'; | 
|---|
| 595 | t = expand_arith_string (exp, 0); | 
|---|
| 596 | this_command_name = (char *)NULL; | 
|---|
| 597 | val = evalexp (t, &expok); | 
|---|
| 598 | free (t); | 
|---|
| 599 | free (exp); | 
|---|
| 600 | if (expok == 0) | 
|---|
| 601 | { | 
|---|
| 602 | last_command_exit_value = EXECUTION_FAILURE; | 
|---|
| 603 | jump_to_top_level (DISCARD); | 
|---|
| 604 | } | 
|---|
| 605 | return val; | 
|---|
| 606 | } | 
|---|
| 607 |  | 
|---|
| 608 | /* Return the name of the variable specified by S without any subscript. | 
|---|
| 609 | If SUBP is non-null, return a pointer to the start of the subscript | 
|---|
| 610 | in *SUBP. If LENP is non-null, the length of the subscript is returned | 
|---|
| 611 | in *LENP.  This returns newly-allocated memory. */ | 
|---|
| 612 | char * | 
|---|
| 613 | array_variable_name (s, subp, lenp) | 
|---|
| 614 | char *s, **subp; | 
|---|
| 615 | int *lenp; | 
|---|
| 616 | { | 
|---|
| 617 | char *t, *ret; | 
|---|
| 618 | int ind, ni; | 
|---|
| 619 |  | 
|---|
| 620 | t = xstrchr (s, '['); | 
|---|
| 621 | if (t == 0) | 
|---|
| 622 | { | 
|---|
| 623 | if (subp) | 
|---|
| 624 | *subp = t; | 
|---|
| 625 | if (lenp) | 
|---|
| 626 | *lenp = 0; | 
|---|
| 627 | return ((char *)NULL); | 
|---|
| 628 | } | 
|---|
| 629 | ind = t - s; | 
|---|
| 630 | ni = skipsubscript (s, ind); | 
|---|
| 631 | if (ni <= ind + 1 || s[ni] != ']') | 
|---|
| 632 | { | 
|---|
| 633 | err_badarraysub (s); | 
|---|
| 634 | if (subp) | 
|---|
| 635 | *subp = t; | 
|---|
| 636 | if (lenp) | 
|---|
| 637 | *lenp = 0; | 
|---|
| 638 | return ((char *)NULL); | 
|---|
| 639 | } | 
|---|
| 640 |  | 
|---|
| 641 | *t = '\0'; | 
|---|
| 642 | ret = savestring (s); | 
|---|
| 643 | *t++ = '[';           /* ] */ | 
|---|
| 644 |  | 
|---|
| 645 | if (subp) | 
|---|
| 646 | *subp = t; | 
|---|
| 647 | if (lenp) | 
|---|
| 648 | *lenp = ni - ind; | 
|---|
| 649 |  | 
|---|
| 650 | return ret; | 
|---|
| 651 | } | 
|---|
| 652 |  | 
|---|
| 653 | /* Return the variable specified by S without any subscript.  If SUBP is | 
|---|
| 654 | non-null, return a pointer to the start of the subscript in *SUBP. | 
|---|
| 655 | If LENP is non-null, the length of the subscript is returned in *LENP. */ | 
|---|
| 656 | SHELL_VAR * | 
|---|
| 657 | array_variable_part (s, subp, lenp) | 
|---|
| 658 | char *s, **subp; | 
|---|
| 659 | int *lenp; | 
|---|
| 660 | { | 
|---|
| 661 | char *t; | 
|---|
| 662 | SHELL_VAR *var; | 
|---|
| 663 |  | 
|---|
| 664 | t = array_variable_name (s, subp, lenp); | 
|---|
| 665 | if (t == 0) | 
|---|
| 666 | return ((SHELL_VAR *)NULL); | 
|---|
| 667 | var = find_variable (t); | 
|---|
| 668 |  | 
|---|
| 669 | free (t); | 
|---|
| 670 | return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var; | 
|---|
| 671 | } | 
|---|
| 672 |  | 
|---|
| 673 | /* Return a string containing the elements in the array and subscript | 
|---|
| 674 | described by S.  If the subscript is * or @, obeys quoting rules akin | 
|---|
| 675 | to the expansion of $* and $@ including double quoting.  If RTYPE | 
|---|
| 676 | is non-null it gets 1 if the array reference is name[@] or name[*] | 
|---|
| 677 | and 0 otherwise. */ | 
|---|
| 678 | static char * | 
|---|
| 679 | array_value_internal (s, quoted, allow_all, rtype) | 
|---|
| 680 | char *s; | 
|---|
| 681 | int quoted, allow_all, *rtype; | 
|---|
| 682 | { | 
|---|
| 683 | int len; | 
|---|
| 684 | arrayind_t ind; | 
|---|
| 685 | char *retval, *t, *temp; | 
|---|
| 686 | WORD_LIST *l; | 
|---|
| 687 | SHELL_VAR *var; | 
|---|
| 688 |  | 
|---|
| 689 | var = array_variable_part (s, &t, &len); | 
|---|
| 690 |  | 
|---|
| 691 | /* Expand the index, even if the variable doesn't exist, in case side | 
|---|
| 692 | effects are needed, like ${w[i++]} where w is unset. */ | 
|---|
| 693 | #if 0 | 
|---|
| 694 | if (var == 0) | 
|---|
| 695 | return (char *)NULL; | 
|---|
| 696 | #endif | 
|---|
| 697 |  | 
|---|
| 698 | if (len == 0) | 
|---|
| 699 | return ((char *)NULL);      /* error message already printed */ | 
|---|
| 700 |  | 
|---|
| 701 | /* [ */ | 
|---|
| 702 | if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']') | 
|---|
| 703 | { | 
|---|
| 704 | if (rtype) | 
|---|
| 705 | *rtype = 1; | 
|---|
| 706 | if (allow_all == 0) | 
|---|
| 707 | { | 
|---|
| 708 | err_badarraysub (s); | 
|---|
| 709 | return ((char *)NULL); | 
|---|
| 710 | } | 
|---|
| 711 | else if (var == 0 || value_cell (var) == 0) | 
|---|
| 712 | return ((char *)NULL); | 
|---|
| 713 | else if (array_p (var) == 0) | 
|---|
| 714 | l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL); | 
|---|
| 715 | else | 
|---|
| 716 | { | 
|---|
| 717 | l = array_to_word_list (array_cell (var)); | 
|---|
| 718 | if (l == (WORD_LIST *)NULL) | 
|---|
| 719 | return ((char *) NULL); | 
|---|
| 720 | } | 
|---|
| 721 |  | 
|---|
| 722 | if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) | 
|---|
| 723 | { | 
|---|
| 724 | temp = string_list_dollar_star (l); | 
|---|
| 725 | retval = quote_string (temp); | 
|---|
| 726 | free (temp); | 
|---|
| 727 | } | 
|---|
| 728 | else      /* ${name[@]} or unquoted ${name[*]} */ | 
|---|
| 729 | retval = string_list_dollar_at (l, quoted); | 
|---|
| 730 |  | 
|---|
| 731 | dispose_words (l); | 
|---|
| 732 | } | 
|---|
| 733 | else | 
|---|
| 734 | { | 
|---|
| 735 | if (rtype) | 
|---|
| 736 | *rtype = 0; | 
|---|
| 737 | ind = array_expand_index (t, len); | 
|---|
| 738 | if (ind < 0) | 
|---|
| 739 | { | 
|---|
| 740 | if (var) | 
|---|
| 741 | err_badarraysub (var->name); | 
|---|
| 742 | else | 
|---|
| 743 | { | 
|---|
| 744 | t[-1] = '\0'; | 
|---|
| 745 | err_badarraysub (s); | 
|---|
| 746 | t[-1] = '[';      /* ] */ | 
|---|
| 747 | } | 
|---|
| 748 | return ((char *)NULL); | 
|---|
| 749 | } | 
|---|
| 750 | if (var == 0) | 
|---|
| 751 | return ((char *)NULL); | 
|---|
| 752 | if (array_p (var) == 0) | 
|---|
| 753 | return (ind == 0 ? value_cell (var) : (char *)NULL); | 
|---|
| 754 | retval = array_reference (array_cell (var), ind); | 
|---|
| 755 | } | 
|---|
| 756 |  | 
|---|
| 757 | return retval; | 
|---|
| 758 | } | 
|---|
| 759 |  | 
|---|
| 760 | /* Return a string containing the elements described by the array and | 
|---|
| 761 | subscript contained in S, obeying quoting for subscripts * and @. */ | 
|---|
| 762 | char * | 
|---|
| 763 | array_value (s, quoted, rtype) | 
|---|
| 764 | char *s; | 
|---|
| 765 | int quoted, *rtype; | 
|---|
| 766 | { | 
|---|
| 767 | return (array_value_internal (s, quoted, 1, rtype)); | 
|---|
| 768 | } | 
|---|
| 769 |  | 
|---|
| 770 | /* Return the value of the array indexing expression S as a single string. | 
|---|
| 771 | If ALLOW_ALL is 0, do not allow `@' and `*' subscripts.  This is used | 
|---|
| 772 | by other parts of the shell such as the arithmetic expression evaluator | 
|---|
| 773 | in expr.c. */ | 
|---|
| 774 | char * | 
|---|
| 775 | get_array_value (s, allow_all, rtype) | 
|---|
| 776 | char *s; | 
|---|
| 777 | int allow_all, *rtype; | 
|---|
| 778 | { | 
|---|
| 779 | return (array_value_internal (s, 0, allow_all, rtype)); | 
|---|
| 780 | } | 
|---|
| 781 |  | 
|---|
| 782 | char * | 
|---|
| 783 | array_keys (s, quoted) | 
|---|
| 784 | char *s; | 
|---|
| 785 | int quoted; | 
|---|
| 786 | { | 
|---|
| 787 | int len; | 
|---|
| 788 | char *retval, *t, *temp; | 
|---|
| 789 | WORD_LIST *l; | 
|---|
| 790 | SHELL_VAR *var; | 
|---|
| 791 |  | 
|---|
| 792 | var = array_variable_part (s, &t, &len); | 
|---|
| 793 |  | 
|---|
| 794 | /* [ */ | 
|---|
| 795 | if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']') | 
|---|
| 796 | return (char *)NULL; | 
|---|
| 797 |  | 
|---|
| 798 | if (array_p (var) == 0) | 
|---|
| 799 | l = add_string_to_list ("0", (WORD_LIST *)NULL); | 
|---|
| 800 | else | 
|---|
| 801 | { | 
|---|
| 802 | l = array_keys_to_word_list (array_cell (var)); | 
|---|
| 803 | if (l == (WORD_LIST *)NULL) | 
|---|
| 804 | return ((char *) NULL); | 
|---|
| 805 | } | 
|---|
| 806 |  | 
|---|
| 807 | if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) | 
|---|
| 808 | { | 
|---|
| 809 | temp = string_list_dollar_star (l); | 
|---|
| 810 | retval = quote_string (temp); | 
|---|
| 811 | free (temp); | 
|---|
| 812 | } | 
|---|
| 813 | else  /* ${!name[@]} or unquoted ${!name[*]} */ | 
|---|
| 814 | retval = string_list_dollar_at (l, quoted); | 
|---|
| 815 |  | 
|---|
| 816 | dispose_words (l); | 
|---|
| 817 | return retval; | 
|---|
| 818 | } | 
|---|
| 819 | #endif /* ARRAY_VARS */ | 
|---|