1039 lines
24 KiB
C
1039 lines
24 KiB
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[];
|
|
{
|
|
extern char *primary_prompt, *secondary_prompt;
|
|
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");
|
|
|
|
/* Magic machine types. Pretty convenient. */
|
|
bind_variable ("HOSTTYPE", HOSTTYPE);
|
|
|
|
/* 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 char *dist_version;
|
|
extern int build_version;
|
|
|
|
sprintf (tt, "%s.%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");
|
|
}
|
|
|
|
/* Gee, might as well get parent pid. */
|
|
{
|
|
char aval[10];
|
|
|
|
sprintf (aval, "%d", getppid ());
|
|
bind_variable ("PPID", aval);
|
|
}
|
|
|
|
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);
|
|
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;
|
|
{
|
|
if (var->value)
|
|
{
|
|
printf ("%s=", var->name);
|
|
print_var_value (var);
|
|
printf ("\n");
|
|
}
|
|
|
|
if (var->function)
|
|
{
|
|
printf ("%s=", var->name);
|
|
print_var_function (var);
|
|
printf ("\n");
|
|
}
|
|
}
|
|
|
|
/* Print the value cell of VAR, a shell variable. Do not print
|
|
the name, nor leading/trailing newline. */
|
|
print_var_value (var)
|
|
SHELL_VAR *var;
|
|
{
|
|
if (var->value)
|
|
printf ("%s", var->value);
|
|
}
|
|
|
|
/* Print the function cell of VAR, a shell variable. Do not
|
|
print the name, nor leading/trailing newline. */
|
|
print_var_function (var)
|
|
SHELL_VAR *var;
|
|
{
|
|
char *named_function_string ();
|
|
|
|
if (var->function)
|
|
printf ("%s", named_function_string ((char *)NULL, var->function, 1));
|
|
}
|
|
|
|
/* 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 only 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)
|
|
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->function = (char *)copy_command (old_var->function);
|
|
|
|
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 (!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);
|
|
|
|
if (value)
|
|
entry->value = savestring (value);
|
|
else
|
|
entry->value = (char *)NULL;
|
|
|
|
entry->function = (char *)NULL;
|
|
|
|
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 (entry->value)
|
|
free (entry->value);
|
|
|
|
if (value)
|
|
entry->value = savestring (value);
|
|
else
|
|
entry->value = (char *)NULL;
|
|
}
|
|
|
|
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 (var->function)
|
|
dispose_command (var->function);
|
|
|
|
if (var->value)
|
|
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);
|
|
}
|
|
|
|
/* Unbind the function cell of a variable. If the variable has
|
|
no function cell, then discard it. Return -1 if the variable
|
|
couldn't be found. */
|
|
unbind_function (name)
|
|
char *name;
|
|
{
|
|
SHELL_VAR *var = find_variable (name);
|
|
|
|
if (!var)
|
|
return (-1);
|
|
|
|
if (var->function)
|
|
{
|
|
dispose_command (var->function);
|
|
var->function = (char *)NULL;
|
|
}
|
|
|
|
if (!var->value)
|
|
makunbound (name);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Unbind the value cell of a variable. If the variable has
|
|
no value cell, then discard it. Return -1 if the variable
|
|
couldn't be found. */
|
|
unbind_variable (name)
|
|
char *name;
|
|
{
|
|
SHELL_VAR *var = find_variable (name);
|
|
|
|
if (!var)
|
|
return (-1);
|
|
|
|
if (var->value)
|
|
{
|
|
free (var->value);
|
|
var->value = (char *)NULL;
|
|
}
|
|
|
|
if (!var->function)
|
|
makunbound (name);
|
|
|
|
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 = find_variable (name);
|
|
|
|
if (!entry)
|
|
{
|
|
entry = bind_variable (name, "");
|
|
free (entry->value);
|
|
entry->value = (char *)NULL;
|
|
}
|
|
|
|
if (entry->function)
|
|
dispose_command (entry->function);
|
|
|
|
entry->function = (char *)copy_command (value);
|
|
|
|
entry->attributes |= att_function;
|
|
|
|
if (mark_modified_vars)
|
|
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 (var->function)
|
|
copy->function = (char *)copy_command (var->function);
|
|
|
|
if (var->value)
|
|
copy->value = savestring (var->value);
|
|
else
|
|
copy->value = (char *)NULL;
|
|
|
|
copy->context = var->context;
|
|
|
|
/* Don't bother copying previous contexts along with this variable. */
|
|
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;
|
|
int hack_it_twice = 0;
|
|
|
|
while (list)
|
|
{
|
|
if (exported_p (list) && !invisible_p (list))
|
|
{
|
|
if (list->function)
|
|
count++;
|
|
|
|
if (list->value)
|
|
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 (list->function && !hack_it_twice)
|
|
{
|
|
value =
|
|
named_function_string ((char *)NULL,
|
|
(COMMAND *)list->function, 0);
|
|
hack_it_twice++;
|
|
}
|
|
else
|
|
{
|
|
value = list->value;
|
|
hack_it_twice = 0;
|
|
}
|
|
|
|
if (value)
|
|
{
|
|
array[count] =
|
|
(char *)xmalloc (2 + strlen (list->name) + strlen (value));
|
|
sprintf (array[count], "%s=%s", list->name, value);
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (!hack_it_twice)
|
|
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;
|
|
}
|
|
|
|
/* Find a variable in the temporary environment that is named NAME.
|
|
Return a consed variable, or NULL if not found. */
|
|
SHELL_VAR *
|
|
find_tempenv_variable (name)
|
|
char *name;
|
|
{
|
|
register int i, l = strlen (name);
|
|
|
|
if (!temporary_env)
|
|
return ((SHELL_VAR *)NULL);
|
|
|
|
for (i = 0; temporary_env[i]; i++)
|
|
{
|
|
if (strncmp (temporary_env[i], name, l) == 0 &&
|
|
temporary_env[i][l] == '=')
|
|
{
|
|
SHELL_VAR *temp = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
|
|
|
|
temp->next = (SHELL_VAR *)NULL;
|
|
temp->name = savestring (name);
|
|
if (temporary_env[i][l + 1])
|
|
temp->value = savestring (&temporary_env[i][l + 1]);
|
|
else
|
|
temp->value = savestring ("");
|
|
temp->function = (char *)NULL;
|
|
temp->attributes = att_exported;
|
|
temp->context = 0;
|
|
temp->prev_context = (SHELL_VAR *)NULL;
|
|
|
|
return (temp);
|
|
}
|
|
}
|
|
return ((SHELL_VAR *)NULL);
|
|
}
|
|
|
|
/* 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 *last_tempenv_value = (char *)NULL;
|
|
|
|
char *
|
|
getenv (name)
|
|
char *name;
|
|
{
|
|
SHELL_VAR *var = find_tempenv_variable (name);
|
|
|
|
if (var)
|
|
{
|
|
if (last_tempenv_value)
|
|
free (last_tempenv_value);
|
|
|
|
last_tempenv_value = savestring (value_cell (var));
|
|
dispose_variable (var);
|
|
return (last_tempenv_value);
|
|
}
|
|
else
|
|
{
|
|
var = find_variable (name);
|
|
if (var && exported_p (var))
|
|
return (value_cell (var));
|
|
}
|
|
return ((char *)NULL);
|
|
}
|