1This file is setattr.def, from which is created setattr.c. 2It implements the builtins "export" and "readonly", in Bash. 3 4Copyright (C) 1987-2020 Free Software Foundation, Inc. 5 6This file is part of GNU Bash, the Bourne Again SHell. 7 8Bash is free software: you can redistribute it and/or modify 9it under the terms of the GNU General Public License as published by 10the Free Software Foundation, either version 3 of the License, or 11(at your option) any later version. 12 13Bash is distributed in the hope that it will be useful, 14but WITHOUT ANY WARRANTY; without even the implied warranty of 15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16GNU General Public License for more details. 17 18You should have received a copy of the GNU General Public License 19along with Bash. If not, see <http://www.gnu.org/licenses/>. 20 21$PRODUCES setattr.c 22 23#include <config.h> 24 25#if defined (HAVE_UNISTD_H) 26# ifdef _MINIX 27# include <sys/types.h> 28# endif 29# include <unistd.h> 30#endif 31 32#include <stdio.h> 33#include "../bashansi.h" 34#include "../bashintl.h" 35 36#include "../shell.h" 37#include "../execute_cmd.h" 38#include "../flags.h" 39#include "common.h" 40#include "bashgetopt.h" 41 42extern sh_builtin_func_t *this_shell_builtin; 43 44#ifdef ARRAY_VARS 45extern int declare_builtin PARAMS((WORD_LIST *)); 46#endif 47 48#define READONLY_OR_EXPORT \ 49 (this_shell_builtin == readonly_builtin || this_shell_builtin == export_builtin) 50 51$BUILTIN export 52$FUNCTION export_builtin 53$SHORT_DOC export [-fn] [name[=value] ...] or export -p 54Set export attribute for shell variables. 55 56Marks each NAME for automatic export to the environment of subsequently 57executed commands. If VALUE is supplied, assign VALUE before exporting. 58 59Options: 60 -f refer to shell functions 61 -n remove the export property from each NAME 62 -p display a list of all exported variables and functions 63 64An argument of `--' disables further option processing. 65 66Exit Status: 67Returns success unless an invalid option is given or NAME is invalid. 68$END 69 70/* For each variable name in LIST, make that variable appear in the 71 environment passed to simple commands. If there is no LIST, then 72 print all such variables. An argument of `-n' says to remove the 73 exported attribute from variables named in LIST. An argument of 74 -f indicates that the names present in LIST refer to functions. */ 75int 76export_builtin (list) 77 register WORD_LIST *list; 78{ 79 return (set_or_show_attributes (list, att_exported, 0)); 80} 81 82$BUILTIN readonly 83$FUNCTION readonly_builtin 84$SHORT_DOC readonly [-aAf] [name[=value] ...] or readonly -p 85Mark shell variables as unchangeable. 86 87Mark each NAME as read-only; the values of these NAMEs may not be 88changed by subsequent assignment. If VALUE is supplied, assign VALUE 89before marking as read-only. 90 91Options: 92 -a refer to indexed array variables 93 -A refer to associative array variables 94 -f refer to shell functions 95 -p display a list of all readonly variables or functions, 96 depending on whether or not the -f option is given 97 98An argument of `--' disables further option processing. 99 100Exit Status: 101Returns success unless an invalid option is given or NAME is invalid. 102$END 103 104/* For each variable name in LIST, make that variable readonly. Given an 105 empty LIST, print out all existing readonly variables. */ 106int 107readonly_builtin (list) 108 register WORD_LIST *list; 109{ 110 return (set_or_show_attributes (list, att_readonly, 0)); 111} 112 113#if defined (ARRAY_VARS) 114# define ATTROPTS "aAfnp" 115#else 116# define ATTROPTS "fnp" 117#endif 118 119/* For each variable name in LIST, make that variable have the specified 120 ATTRIBUTE. An arg of `-n' says to remove the attribute from the the 121 remaining names in LIST (doesn't work for readonly). */ 122int 123set_or_show_attributes (list, attribute, nodefs) 124 register WORD_LIST *list; 125 int attribute, nodefs; 126{ 127 register SHELL_VAR *var; 128 int assign, undo, any_failed, assign_error, opt; 129 int functions_only, arrays_only, assoc_only; 130 int aflags; 131 char *name; 132#if defined (ARRAY_VARS) 133 WORD_LIST *nlist, *tlist; 134 WORD_DESC *w; 135 char optw[8]; 136 int opti; 137#endif 138 139 functions_only = arrays_only = assoc_only = 0; 140 undo = any_failed = assign_error = 0; 141 /* Read arguments from the front of the list. */ 142 reset_internal_getopt (); 143 while ((opt = internal_getopt (list, ATTROPTS)) != -1) 144 { 145 switch (opt) 146 { 147 case 'n': 148 undo = 1; 149 break; 150 case 'f': 151 functions_only = 1; 152 break; 153#if defined (ARRAY_VARS) 154 case 'a': 155 arrays_only = 1; 156 break; 157 case 'A': 158 assoc_only = 1; 159 break; 160#endif 161 case 'p': 162 break; 163 CASE_HELPOPT; 164 default: 165 builtin_usage (); 166 return (EX_USAGE); 167 } 168 } 169 list = loptend; 170 171 if (list) 172 { 173 if (attribute & att_exported) 174 array_needs_making = 1; 175 176 /* Cannot undo readonly status, silently disallowed. */ 177 if (undo && (attribute & att_readonly)) 178 attribute &= ~att_readonly; 179 180 while (list) 181 { 182 name = list->word->word; 183 184 if (functions_only) /* xxx -f name */ 185 { 186 var = find_function (name); 187 if (var == 0) 188 { 189 builtin_error (_("%s: not a function"), name); 190 any_failed++; 191 } 192 else if ((attribute & att_exported) && undo == 0 && exportable_function_name (name) == 0) 193 { 194 builtin_error (_("%s: cannot export"), name); 195 any_failed++; 196 } 197 else 198 SETVARATTR (var, attribute, undo); 199 200 list = list->next; 201 continue; 202 } 203 204 /* xxx [-np] name[=value] */ 205 assign = assignment (name, 0); 206 207 aflags = 0; 208 if (assign) 209 { 210 name[assign] = '\0'; 211 if (name[assign - 1] == '+') 212 { 213 aflags |= ASS_APPEND; 214 name[assign - 1] = '\0'; 215 } 216 } 217 218 if (legal_identifier (name) == 0) 219 { 220 sh_invalidid (name); 221 if (assign) 222 assign_error++; 223 else 224 any_failed++; 225 list = list->next; 226 continue; 227 } 228 229 if (assign) /* xxx [-np] name=value */ 230 { 231 name[assign] = '='; 232 if (aflags & ASS_APPEND) 233 name[assign - 1] = '+'; 234#if defined (ARRAY_VARS) 235 /* Let's try something here. Turn readonly -a xxx=yyy into 236 declare -ra xxx=yyy and see what that gets us. */ 237 if (arrays_only || assoc_only) 238 { 239 tlist = list->next; 240 list->next = (WORD_LIST *)NULL; 241 /* Add -g to avoid readonly/export creating local variables: 242 only local/declare/typeset create local variables */ 243 opti = 0; 244 optw[opti++] = '-'; 245 optw[opti++] = 'g'; 246 if (attribute & att_readonly) 247 optw[opti++] = 'r'; 248 if (attribute & att_exported) 249 optw[opti++] = 'x'; 250 if (arrays_only) 251 optw[opti++] = 'a'; 252 else 253 optw[opti++] = 'A'; 254 optw[opti] = '\0'; 255 256 w = make_word (optw); 257 nlist = make_word_list (w, list); 258 259 opt = declare_builtin (nlist); 260 if (opt != EXECUTION_SUCCESS) 261 assign_error++; 262 list->next = tlist; 263 dispose_word (w); 264 free (nlist); 265 } 266 else 267#endif 268 /* This word has already been expanded once with command 269 and parameter expansion. Call do_assignment_no_expand (), 270 which does not do command or parameter substitution. If 271 the assignment is not performed correctly, flag an error. */ 272 if (do_assignment_no_expand (name) == 0) 273 assign_error++; 274 name[assign] = '\0'; 275 if (aflags & ASS_APPEND) 276 name[assign - 1] = '\0'; 277 } 278 279 set_var_attribute (name, attribute, undo); 280 if (assign) /* restore word */ 281 { 282 name[assign] = '='; 283 if (aflags & ASS_APPEND) 284 name[assign-1] = '+'; 285 } 286 list = list->next; 287 } 288 } 289 else 290 { 291 SHELL_VAR **variable_list; 292 register int i; 293 294 if ((attribute & att_function) || functions_only) 295 { 296 variable_list = all_shell_functions (); 297 if (attribute != att_function) 298 attribute &= ~att_function; /* so declare -xf works, for example */ 299 } 300 else 301 variable_list = all_shell_variables (); 302 303#if defined (ARRAY_VARS) 304 if (attribute & att_array) 305 { 306 arrays_only++; 307 if (attribute != att_array) 308 attribute &= ~att_array; 309 } 310 else if (attribute & att_assoc) 311 { 312 assoc_only++; 313 if (attribute != att_assoc) 314 attribute &= ~att_assoc; 315 } 316#endif 317 318 if (variable_list) 319 { 320 for (i = 0; var = variable_list[i]; i++) 321 { 322#if defined (ARRAY_VARS) 323 if (arrays_only && array_p (var) == 0) 324 continue; 325 else if (assoc_only && assoc_p (var) == 0) 326 continue; 327#endif 328 329 /* If we imported a variable that's not a valid identifier, don't 330 show it in any lists. */ 331 if ((var->attributes & (att_invisible|att_imported)) == (att_invisible|att_imported)) 332 continue; 333 334 if ((var->attributes & attribute)) 335 { 336 show_var_attributes (var, READONLY_OR_EXPORT, nodefs); 337 if (any_failed = sh_chkwrite (any_failed)) 338 break; 339 } 340 } 341 free (variable_list); 342 } 343 } 344 345 return (assign_error ? EX_BADASSIGN 346 : ((any_failed == 0) ? EXECUTION_SUCCESS 347 : EXECUTION_FAILURE)); 348} 349 350/* Show all variable variables (v == 1) or functions (v == 0) with 351 attributes. */ 352int 353show_all_var_attributes (v, nodefs) 354 int v, nodefs; 355{ 356 SHELL_VAR **variable_list, *var; 357 int any_failed; 358 register int i; 359 360 variable_list = v ? all_shell_variables () : all_shell_functions (); 361 if (variable_list == 0) 362 return (EXECUTION_SUCCESS); 363 364 for (i = any_failed = 0; var = variable_list[i]; i++) 365 { 366 show_var_attributes (var, READONLY_OR_EXPORT, nodefs); 367 if (any_failed = sh_chkwrite (any_failed)) 368 break; 369 } 370 free (variable_list); 371 return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE); 372} 373 374/* Show all local variable variables with their attributes. This shows unset 375 local variables (all_local_variables called with 0 argment). */ 376int 377show_local_var_attributes (v, nodefs) 378 int v, nodefs; 379{ 380 SHELL_VAR **variable_list, *var; 381 int any_failed; 382 register int i; 383 384 variable_list = all_local_variables (0); 385 if (variable_list == 0) 386 return (EXECUTION_SUCCESS); 387 388 for (i = any_failed = 0; var = variable_list[i]; i++) 389 { 390 show_var_attributes (var, READONLY_OR_EXPORT, nodefs); 391 if (any_failed = sh_chkwrite (any_failed)) 392 break; 393 } 394 free (variable_list); 395 return (any_failed == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE); 396} 397 398int 399var_attribute_string (var, pattr, flags) 400 SHELL_VAR *var; 401 int pattr; 402 char *flags; /* filled in with attributes */ 403{ 404 int i; 405 406 i = 0; 407 408 /* pattr == 0 means we are called from `declare'. */ 409 if (pattr == 0 || posixly_correct == 0) 410 { 411#if defined (ARRAY_VARS) 412 if (array_p (var)) 413 flags[i++] = 'a'; 414 415 if (assoc_p (var)) 416 flags[i++] = 'A'; 417#endif 418 419 if (function_p (var)) 420 flags[i++] = 'f'; 421 422 if (integer_p (var)) 423 flags[i++] = 'i'; 424 425 if (nameref_p (var)) 426 flags[i++] = 'n'; 427 428 if (readonly_p (var)) 429 flags[i++] = 'r'; 430 431 if (trace_p (var)) 432 flags[i++] = 't'; 433 434 if (exported_p (var)) 435 flags[i++] = 'x'; 436 437 if (capcase_p (var)) 438 flags[i++] = 'c'; 439 440 if (lowercase_p (var)) 441 flags[i++] = 'l'; 442 443 if (uppercase_p (var)) 444 flags[i++] = 'u'; 445 } 446 else 447 { 448#if defined (ARRAY_VARS) 449 if (array_p (var)) 450 flags[i++] = 'a'; 451 452 if (assoc_p (var)) 453 flags[i++] = 'A'; 454#endif 455 456 if (function_p (var)) 457 flags[i++] = 'f'; 458 } 459 460 flags[i] = '\0'; 461 return i; 462} 463 464/* Show the attributes for shell variable VAR. If NODEFS is non-zero, 465 don't show function definitions along with the name. If PATTR is 466 non-zero, it indicates we're being called from `export' or `readonly'. 467 In POSIX mode, this prints the name of the calling builtin (`export' 468 or `readonly') instead of `declare', and doesn't print function defs 469 when called by `export' or `readonly'. */ 470int 471show_var_attributes (var, pattr, nodefs) 472 SHELL_VAR *var; 473 int pattr, nodefs; 474{ 475 char flags[MAX_ATTRIBUTES], *x; 476 int i; 477 478 i = var_attribute_string (var, pattr, flags); 479 480 /* If we're printing functions with definitions, print the function def 481 first, then the attributes, instead of printing output that can't be 482 reused as input to recreate the current state. */ 483 if (function_p (var) && nodefs == 0 && (pattr == 0 || posixly_correct == 0)) 484 { 485 printf ("%s\n", named_function_string (var->name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL)); 486 nodefs++; 487 if (pattr == 0 && i == 1 && flags[0] == 'f') 488 return 0; /* don't print `declare -f name' */ 489 } 490 491 if (pattr == 0 || posixly_correct == 0) 492 printf ("declare -%s ", i ? flags : "-"); 493 else if (i) 494 printf ("%s -%s ", this_command_name, flags); 495 else 496 printf ("%s ", this_command_name); 497 498#if defined (ARRAY_VARS) 499 if (invisible_p (var) && (array_p (var) || assoc_p (var))) 500 printf ("%s\n", var->name); 501 else if (array_p (var)) 502 print_array_assignment (var, 0); 503 else if (assoc_p (var)) 504 print_assoc_assignment (var, 0); 505 else 506#endif 507 /* force `readonly' and `export' to not print out function definitions 508 when in POSIX mode. */ 509 if (nodefs || (function_p (var) && pattr != 0 && posixly_correct)) 510 printf ("%s\n", var->name); 511 else if (function_p (var)) 512 printf ("%s\n", named_function_string (var->name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL)); 513 else if (invisible_p (var) || var_isset (var) == 0) 514 printf ("%s\n", var->name); 515 else 516 { 517 x = sh_double_quote (value_cell (var)); 518 printf ("%s=%s\n", var->name, x); 519 free (x); 520 } 521 return (0); 522} 523 524int 525show_name_attributes (name, nodefs) 526 char *name; 527 int nodefs; 528{ 529 SHELL_VAR *var; 530 531 var = find_variable_noref (name); 532 533 if (var) /* show every variable with attributes, even unset ones */ 534 { 535 show_var_attributes (var, READONLY_OR_EXPORT, nodefs); 536 return (0); 537 } 538 else 539 return (1); 540} 541 542int 543show_localname_attributes (name, nodefs) 544 char *name; 545 int nodefs; 546{ 547 SHELL_VAR *var; 548 549 var = find_variable_noref (name); 550 551 if (var && local_p (var) && var->context == variable_context) /* show every variable with attributes, even unset ones */ 552 { 553 show_var_attributes (var, READONLY_OR_EXPORT, nodefs); 554 return (0); 555 } 556 else 557 return (1); 558} 559 560int 561show_func_attributes (name, nodefs) 562 char *name; 563 int nodefs; 564{ 565 SHELL_VAR *var; 566 567 var = find_function (name); 568 569 if (var) 570 { 571 show_var_attributes (var, READONLY_OR_EXPORT, nodefs); 572 return (0); 573 } 574 else 575 return (1); 576} 577 578void 579set_var_attribute (name, attribute, undo) 580 char *name; 581 int attribute, undo; 582{ 583 SHELL_VAR *var, *tv, *v, *refvar; 584 char *tvalue; 585 586 if (undo) 587 var = find_variable (name); 588 else 589 { 590 tv = find_tempenv_variable (name); 591 /* XXX -- need to handle case where tv is a temp variable in a 592 function-scope context, since function_env has been merged into 593 the local variables table. */ 594 if (tv && tempvar_p (tv)) 595 { 596 tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring (""); 597 598 var = bind_variable (tv->name, tvalue, 0); 599 if (var == 0) 600 { 601 free (tvalue); 602 return; /* XXX - no error message here */ 603 } 604 var->attributes |= tv->attributes & ~att_tempvar; 605 /* This avoids an error message when propagating a read-only var 606 later on. */ 607 if (posixly_correct || shell_compatibility_level <= 44) 608 { 609 if (var->context == 0 && (attribute & att_readonly)) 610 { 611 /* Don't bother to set the `propagate to the global variables 612 table' flag if we've just bound the variable in that 613 table */ 614 v = find_global_variable (tv->name); 615 if (v != var) 616 VSETATTR (tv, att_propagate); 617 } 618 else 619 VSETATTR (tv, att_propagate); 620 if (var->context != 0) 621 VSETATTR (var, att_propagate); 622 } 623 624 SETVARATTR (tv, attribute, undo); /* XXX */ 625 626 stupidly_hack_special_variables (tv->name); 627 628 free (tvalue); 629 } 630 else 631 { 632 var = find_variable_notempenv (name); 633 if (var == 0) 634 { 635 /* We might have a nameref pointing to something that we can't 636 resolve to a shell variable. If we do, skip it. We do a little 637 checking just so we can print an error message. */ 638 refvar = find_variable_nameref_for_create (name, 0); 639 if (refvar == INVALID_NAMEREF_VALUE) 640 return; 641 /* Otherwise we probably have a nameref pointing to a variable 642 that hasn't been created yet. bind_variable will take care 643 of that. */ 644 } 645 if (var == 0) 646 { 647 var = bind_variable (name, (char *)NULL, 0); 648 if (var) 649 VSETATTR (var, att_invisible); 650 } 651 else if (var->context != 0) 652 VSETATTR (var, att_propagate); 653 } 654 } 655 656 if (var) 657 SETVARATTR (var, attribute, undo); 658 659 if (var && (exported_p (var) || (attribute & att_exported))) 660 array_needs_making++; /* XXX */ 661} 662