|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T v
Length: 20783 (0x512f)
Types: TextFile
Names: »variables.c«
└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89
└─⟦ca1f037a2⟧ »./bash-1.04.tar.Z«
└─⟦46465a4db⟧
└─⟦this⟧ »bash-1.04/variables.c«
/* variables.c -- Functions for hacking shell variables. */
/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 1, or (at your option) any later
version.
Bash is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with Bash; see the file COPYING. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <stdio.h>
#include <ctype.h>
#include <pwd.h>
#include "shell.h"
#include "flags.h"
#include "version.h"
#ifdef SYSV
struct passwd *getpwuid (), *getpwent ();
#endif
/* The list of shell variables that the user has created, or that came from
the environment. */
SHELL_VAR *variable_list;
/* The current variable context. This is really a count of how deep into
executing functions we are. */
int variable_context = 0;
/* The array of shell assignments which are made only in the environment
for a single command. */
char **temporary_env = (char **)NULL;
/* Some funky variables which are known about specially. Here is where
"$*", "$1", and all the cruft is kept. */
char *dollar_vars[10];
WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
/* The value of $$. */
int dollar_dollar_pid;
/* An array which is passed to commands as their environment. It is
manufactured from the overlap of the initial environment and the
shell variables that are marked for export. */
char **export_env = (char **)NULL;
/* Non-zero means that we have to remake EXPORT_ENV. */
int array_needs_making = 1;
/* The list of variables that may not be unset in this shell. */
char **non_unsettable_vars = (char **)NULL;
#ifdef SYSV
#define DEFAULT_MAIL_PATH "/usr/mail/"
#else
#define DEFAULT_MAIL_PATH "/usr/spool/mail/"
#endif
/* Initialize the shell variables from the current environment. */
initialize_shell_variables (env)
char *env[];
{
char *name, *string;
int c, char_index;
int string_index = 0;
SHELL_VAR *temp_var;
while (string = env[string_index++])
{
char_index = 0;
name = (char *)alloca (1 + strlen (string));
while ((c = *string++) && c != '=')
name[char_index++] = c;
name[char_index] = '\0';
/* If exported function, define it now. Sigh. */
if (strncmp ("() {", string, 4) == 0)
{
char *eval_string =
(char *)xmalloc (3 + strlen (string) + strlen (name));
sprintf (eval_string, "%s %s", name, string);
parse_and_execute (eval_string, name);
}
else
bind_variable (name, string);
set_var_auto_export (name);
}
/* Remember this pid. */
dollar_dollar_pid = getpid ();
/* Now make our own defaults in case the vars that we think are
important are missing. */
set_if_not ("PATH", DEFAULT_PATH_VALUE);
set_var_auto_export ("PATH");
set_if_not ("TERM", "dumb");
set_var_auto_export ("TERM");
set_if_not ("PS1", primary_prompt);
set_if_not ("PS2", secondary_prompt);
set_if_not ("IFS", " \t\n");
/* Default MAILPATH, and MAILCHECK. */
set_if_not ("MAILCHECK", "60");
if ((get_string_value ("MAIL") == (char *)NULL) &&
(get_string_value ("MAILPATH") == (char *)NULL))
{
extern char *current_user_name;
char *tem;
tem = (char *)xmalloc (1 + sizeof (DEFAULT_MAIL_PATH)
+ strlen (current_user_name));
strcpy (tem, DEFAULT_MAIL_PATH);
strcat (tem, current_user_name);
bind_variable ("MAILPATH", tem);
free (tem);
}
/* Set up $PWD. */
{
char *get_working_directory (), *cd;
cd = get_working_directory ("shell-init");
if (cd)
{
bind_variable ("PWD", cd);
free (cd);
}
}
/* Do some things with shell level. */
{
extern int shell_level;
char new_level[10];
int old_level;
set_if_not ("SHLVL", "0");
set_var_auto_export ("SHLVL");
sscanf (get_string_value ("SHLVL"), "%d", &old_level);
shell_level = old_level + 1;
sprintf (new_level, "%d", shell_level);
bind_variable ("SHLVL", new_level);
}
/* Get the full pathname to THIS shell, and set the BASH variable
to it. */
{
extern char *shell_name, *find_user_command (), *full_pathname ();
extern int login_shell;
char *tname = find_user_command (shell_name);
if ((login_shell == 1) && (*shell_name != '/'))
{
struct passwd *entry = getpwuid (getuid ());
if (entry)
{
/* If HOME doesn't exist, set it. */
temp_var = (SHELL_VAR *)find_variable ("HOME");
if (!temp_var)
{
temp_var = bind_variable ("HOME", entry->pw_dir);
temp_var->attributes |= att_exported;
}
name = savestring (entry->pw_shell);
}
else
name = savestring ("a.out");
}
else
{
if (!tname)
{
char *make_absolute ();
name = make_absolute (shell_name, get_string_value ("PWD"));
}
else
{
name = full_pathname (tname);
free (tname);
}
}
/* Make the exported environment variable SHELL be whatever the name of
this shell is. Note that the `tset' command looks at this variable
to determine what style of commands to output; if it ends in "csh",
then C-shell commands are output, else Bourne shell commands. */
set_if_not ("SHELL", name);
set_var_auto_export ("SHELL");
/* Make a variable called BASH, which is the name of THIS shell. */
temp_var = bind_variable ("BASH", name);
temp_var->attributes |= att_exported;
free (name);
}
/* Make a variable called BASH_VERSION which contains the version info. */
{
char tt[12];
extern float dist_version;
extern int build_version;
sprintf (tt, "%.2f.%d", dist_version, build_version);
bind_variable ("BASH_VERSION", tt);
}
/* Set history variables to defaults, and then do whatever we would
do if the variable had just been set. */
{
char *tilde_expand ();
char *tem = tilde_expand ("~/.bash_history");
set_if_not ("HISTFILE", tem);
free (tem);
set_if_not ("HISTSIZE", "500");
sv_histsize ("HISTSIZE");
}
non_unsettable ("PATH");
non_unsettable ("PS1");
non_unsettable ("PS2");
non_unsettable ("IFS");
/* Get the users real user id, and save that in an readonly variable.
To make the variable *really* readonly, we have added it to a special
list of vars. */
sv_uids ();
set_var_read_only ("UID");
set_var_read_only ("EUID");
non_unsettable ("EUID");
non_unsettable ("UID");
}
/* Add NAME to the list of variables that cannot be unset
if it isn't already there. */
non_unsettable (name)
char *name;
{
register int i;
if (!non_unsettable_vars)
{
non_unsettable_vars = (char **)xmalloc (1 * sizeof (char *));
non_unsettable_vars[0] = (char *)NULL;
}
for (i = 0; non_unsettable_vars[i]; i++)
if (strcmp (non_unsettable_vars[i], name) == 0)
return;
non_unsettable_vars =
(char **)xrealloc (non_unsettable_vars, (2 + i) * sizeof (char *));
non_unsettable_vars[i] = savestring (name);
non_unsettable_vars[i + 1] = (char *)NULL;
}
/* Set NAME to VALUE if NAME has no value. */
set_if_not (name, value)
char *name, *value;
{
char *temp = get_string_value (name);
if (!temp)
bind_variable (name, value);
}
/* Print LIST (a linked list of shell variables) to stdout
in such a way that they can be read back in. */
print_var_list (list)
register SHELL_VAR *list;
{
while (list)
{
if (!invisible_p (list))
{
print_assignment (list);
printf ("\n");
}
list = list->next;
}
}
/* Print the value of a single SHELL_VAR. No newline is
output, but the variable is printed in such a way that
it can be read back in. */
print_assignment (var)
SHELL_VAR *var;
{
printf ("%s=", var->name);
print_var_value (var);
}
/* Print the value of VAR, a shell variable. Do not print the
name, nor leading/trailing newline. */
print_var_value (var)
SHELL_VAR *var;
{
char *named_function_string ();
if (function_p (var))
printf ("%s", named_function_string ((char *)NULL, var->value, 1));
else
printf ("%s", var->value);
}
/* Look up the variable entry whose name matches STRING.
Returns the entry or NULL. */
SHELL_VAR *
find_variable (string)
char *string;
{
register SHELL_VAR *list = variable_list;
while (list)
{
if (strcmp (string, list->name) == 0)
return (list);
list = list->next;
}
return ((SHELL_VAR *)NULL);
}
/* Return the string value of a variable. Return NULL if the variable
doesn't exist, or has a function as a value. Don't cons a new string. */
char *
get_string_value (var_name)
char *var_name;
{
SHELL_VAR *var = find_variable (var_name);
if (!var || function_p (var))
return (char *)NULL;
else
return (var->value);
}
/* Create a local variable referenced by NAME. */
SHELL_VAR *
make_local_variable (name)
char *name;
{
SHELL_VAR *new_var, *old_var, *bind_variable ();
int var_has_name ();
/* local foo; local foo; is a no-op. */
{
old_var = find_variable (name);
if (old_var && old_var->context == variable_context)
return (old_var);
}
old_var = (SHELL_VAR *)delete_element (&variable_list, var_has_name, name);
/* If a variable does not already exist with this name, then
just make a new one. */
if ((int)old_var == -1)
{
new_var = bind_variable (name, "");
}
else
{
new_var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
new_var->name = savestring (name);
new_var->value = savestring ("");
new_var->attributes = 0;
if (exported_p (old_var))
new_var->attributes |= att_exported;
new_var->prev_context = old_var;
new_var->next = variable_list;
variable_list = new_var;
}
new_var->context = variable_context;
return (new_var);
}
/* Bind a variable name to some string. This conses up the name
and value strings. */
SHELL_VAR *
bind_variable (name, value)
char *name, *value;
{
SHELL_VAR *entry = find_variable (name);
if (!value)
value = "";
if (!entry)
{
/* Make a new entry for this variable. Then do the binding. */
entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
entry->attributes = 0;
entry->name = savestring (name);
entry->value = savestring (value);
entry->next = variable_list;
/* Always assume variables are to be made at toplevel!
make_local_variable has the responsibilty of changing the
variable context. */
entry->context = 0;
entry->prev_context = (SHELL_VAR *)NULL;
variable_list = entry;
}
else
{
if (readonly_p (entry))
{
report_error ("%s: read-only variable", name);
return (entry);
}
if (function_p (entry))
dispose_command (entry->value);
else
free (entry->value);
entry->value = savestring (value);
}
entry->attributes &= ~att_function;
if (mark_modified_vars)
entry->attributes |= att_exported;
if (exported_p (entry))
array_needs_making = 1;
return (entry);
}
/* Dispose of the information attached to a variable. */
dispose_variable (var)
SHELL_VAR *var;
{
if (!var)
return;
if (function_p (var))
dispose_command (var->value);
else
free (var->value);
free (var->name);
if (exported_p (var))
array_needs_making = 1;
free (var);
}
/* Return 1 if VAR (a variable) is named NAME (a string). */
var_has_name (var, name)
SHELL_VAR *var;
char *name;
{
if (var)
return ((strcmp (var->name, name) == 0));
else
return (0);
}
/* Make the variable associated with NAME go away.
Return non-zero if the variable couldn't be found. */
makunbound (name)
char *name;
{
SHELL_VAR *elt =
(SHELL_VAR *)delete_element (&variable_list, var_has_name, name);
if ((int)elt != -1)
{
if (elt->prev_context)
{
SHELL_VAR *new = elt->prev_context;
new->next = variable_list;
variable_list = new;
}
if (exported_p (elt))
set_var_auto_export (elt->name);
dispose_variable (elt);
return (0);
}
return (-1);
}
/* Remove the variable with NAME if it is a local variable in the
current context. */
kill_local_variable (name)
char *name;
{
SHELL_VAR *temp = find_variable (name);
if (temp && (temp->context == variable_context))
{
makunbound (name);
return (0);
}
return (-1);
}
/* Get rid of all of the variables in the current context. */
kill_all_local_variables ()
{
register SHELL_VAR *list = variable_list;
register SHELL_VAR *prev = (SHELL_VAR *)NULL;
while (list)
{
if (list->context && list->context == variable_context)
{
if (list->prev_context)
list->prev_context->next = list->next;
else
list->prev_context = list->next;
if (prev)
{
prev->next = list->prev_context;
dispose_variable (list);
list = prev;
}
else
{
variable_list = list->prev_context;
dispose_variable (list);
list = variable_list;
}
}
prev = list;
list = list->next;
}
}
/* Do a function binding to a variable. You pass the name and
the command to bind to. This conses the name and command. */
SHELL_VAR *
bind_function (name, value)
char *name;
COMMAND *value;
{
SHELL_VAR *entry = bind_variable (name, "");
free (entry->value);
entry->value = (char *)copy_command (value);
entry->attributes |= att_function;
entry->attributes &= ~att_exported;
array_needs_making = 1;
return (entry);
}
/* Copy VAR to a new data structure and return that structure. */
SHELL_VAR *
copy_variable (var)
SHELL_VAR *var;
{
SHELL_VAR *copy = (SHELL_VAR *)NULL;
if (var) {
copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
copy->attributes = var->attributes;
copy->name = savestring (var->name);
if (function_p (copy))
copy->value = (char *)copy_command (var->value);
else
copy->value = savestring (var->value);
copy->context = var->context;
/* Don't bother copying previous contexts along with this variable.
Right? ??? */
copy->prev_context = (SHELL_VAR *)NULL;
}
return (copy);
}
/* Make the variable associated with NAME be read-only.
If NAME does not exist yet, create it. */
set_var_read_only (name)
char *name;
{
SHELL_VAR *entry = find_variable (name);
if (!entry)
{
entry = bind_variable (name, "");
if (!no_invisible_vars)
entry->attributes |= att_invisible;
}
entry->attributes |= att_readonly;
}
/* Make the variable associated with NAME be auto-exported.
If NAME does not exist yet, create it. */
set_var_auto_export (name)
char *name;
{
SHELL_VAR *entry = find_variable (name);
if (!entry)
{
entry = bind_variable (name, "");
if (!no_invisible_vars)
entry->attributes |= att_invisible;
}
entry->attributes |= att_exported;
array_needs_making = 1;
}
/* Returns non-zero if STRING is an assignment statement. The returned value
is the index of the `=' sign. */
assignment (string)
char *string;
{
register int c, index = 0;
c = string[index];
if (!isletter (c) && c != '_')
return (0);
while (c = string[index])
{
/* The following is safe. Note that '=' at the start of a word
is not an assignment statement. */
if (c == '=')
return (index);
if (!isletter (c) && !digit (c) && c != '_')
return (0);
index++;
}
return (0);
}
/* Make an array out of LIST, a list of SHELL_VAR. Only visible
variables which are marked for export are eligible. */
char **
make_var_array (list)
SHELL_VAR *list;
{
register int count = 0;
register char **array;
register SHELL_VAR *head = list;
while (list)
{
if (exported_p (list) && !invisible_p (list))
count ++;
list = list->next;
}
array = (char **)xmalloc ((1 + count) * sizeof (char *));
list = head;
count = 0;
while (list)
{
if (exported_p (list) && !invisible_p (list))
{
char *value, *named_function_string ();
if (function_p (list))
value =
named_function_string ((char *)NULL, (COMMAND *)list->value, 0);
else
value = list->value;
array[count] =
(char *)xmalloc (2 + strlen (list->name) + strlen (value));
sprintf (array[count], "%s=%s", list->name, value);
count++;
}
list = list->next;
}
array[count] = (char *)NULL;
return (array);
}
/* Add STRING to the array of foo=bar strings that we already
have to add to the environment. */
assign_in_env (string)
char *string;
{
int size;
int offset = assignment (string);
char *name = savestring (string);
char *temp, *value = (char *)NULL;
if (name[offset] == '=')
{
char *tilde_expand (), *string_list ();
WORD_LIST *list, *expand_string ();
extern int disallow_filename_globbing;
name[offset] = 0;
temp = name + offset + 1;
if (!disallow_filename_globbing)
temp = tilde_expand (temp);
else
temp = savestring (temp);
list = expand_string (temp, 0);
value = string_list (list);
if (list)
free (list);
free (temp);
}
if (!value) value = savestring ("");
temp = (char *)xmalloc (2 + strlen (name) + strlen (value));
sprintf (temp, "%s=%s", name, value);
free (name);
if (!temporary_env)
{
temporary_env = (char **)xmalloc (sizeof (char *));
temporary_env [0] = (char *)NULL;
}
size = array_len (temporary_env);
temporary_env =
(char **)xrealloc (temporary_env, (size + 2) * (sizeof (char *)));
temporary_env[size] = (temp);
temporary_env[size + 1] = (char *)NULL;
array_needs_making = 1;
}
/* Free the storage used in the variable array for temporary
environment variables. */
dispose_used_env_vars ()
{
if (!temporary_env)
return;
free_array (temporary_env);
temporary_env = (char **)NULL;
array_needs_making = 1;
}
/* Stupid comparison routine for qsort () ing strings. */
qsort_string_compare (s1, s2)
register char **s1, **s2;
{
return (strcmp (*s1, *s2));
}
/* Add ASSIGN to ARRAY, or supercede a previous assignment in the
array with the same left-hand side. Return the new array. */
char **
add_or_supercede (assign, array)
char *assign;
register char **array;
{
register int i;
int equal_offset = assignment (assign);
if (!equal_offset)
return (array);
for (i = 0; array[i]; i++)
{
if (strncmp (assign, array[i], equal_offset + 1) == 0)
{
free (array[i]);
array[i] = savestring (assign);
return (array);
}
}
array = (char **)xrealloc (array, (2 + array_len (array)) * sizeof (char *));
array[i++] = savestring (assign);
array[i] = (char *)NULL;
return (array);
}
/* Make the environment array for the command about to be executed. If the
array needs making. Otherwise, do nothing. If a shell action could
change the array that commands receive for their environment, then the
code should `array_needs_making++'. */
maybe_make_export_env ()
{
register int i;
register char **temp_array;
if (array_needs_making)
{
if (export_env)
free_array (export_env);
#ifdef SHADOWED_ENV
export_env =
(char **)xmalloc ((1 + array_len (shell_environment)) * sizeof (char *));
for (i = 0; shell_environment[i]; i++)
export_env[i] = savestring (shell_environment[i]);
export_env[i] = (char *)NULL;
#else /* !SHADOWED_ENV */
export_env = (char **)xmalloc (sizeof (char *));
export_env[0] = (char *)NULL;
#endif /* SHADOWED_ENV */
temp_array = make_var_array (variable_list);
for (i = 0; temp_array[i]; i++)
export_env = add_or_supercede (temp_array[i], export_env);
free_array (temp_array);
if (temporary_env) {
for (i = 0; temporary_env[i]; i++)
export_env = add_or_supercede (temporary_env[i], export_env);
/* Sort the array alphabetically. */
qsort (export_env, array_len (export_env),
sizeof (char *), qsort_string_compare);
}
array_needs_making = 0;
}
}
/* We supply our own version of getenv () because we want library routines
to get the changed values of exported variables. */
char *
getenv (name)
char *name;
{
SHELL_VAR *var = find_variable (name);
if (!var || function_p (var) || !exported_p (var))
return ((char *)NULL);
return (var->value);
}