home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
High Voltage Shareware
/
high1.zip
/
high1
/
DIR24
/
BASH_112.ZIP
/
BASH-112.TAR
/
bash-1.12
/
subst.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-20
|
90KB
|
3,494 lines
/* substitutions.c -- The part of the shell that does parameter,
command, and globbing substitutions. */
/* 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 <sys/types.h>
#include <pwd.h>
#include <signal.h>
#include <string.h>
#include "shell.h"
#include "flags.h"
#include "alias.h"
#include "jobs.h"
#include "filecntl.h"
#include <readline/history.h>
#include <glob/fnmatch.h>
#include <errno.h> /* 3/93 ROB */
/* The size that strings change by. */
#define DEFAULT_ARRAY_SIZE 512
/* How to quote and dequote the character C. */
#define QUOTE_CHAR(c) ((unsigned char)(c) | 0x80)
#define DEQUOTE_CHAR(c) ((unsigned char)(c) & 0x7f)
#define QUOTED_CHAR(c) ((unsigned char)(c) > 0x7f)
/* Process ID of the last command executed within command substitution. */
pid_t last_command_subst_pid = NO_PID;
/* Some forward declarations. */
extern WORD_LIST *expand_string (), *expand_word (), *list_string ();
extern char *string_list ();
extern WORD_DESC *make_word ();
extern WORD_DESC *copy_word ();
extern WORD_LIST *copy_word_list();
#if defined(__EMX__)
#define INCL_DOSPROCESS
#include <os2.h>
extern HFILE pipeReadHandle,
pipeWriteHandle;
extern VOID APIENTRY childExitListProc(void);
#endif
#if defined(__DEBUG__)
extern FILE *debugFile;
#define DEBUG_OUT writeDebugInfo
#endif
static WORD_LIST *expand_string_internal (), *expand_words_internal ();
static char *quote_string (), *dequote_string ();
static int unquoted_substring ();
static void quote_list ();
/* **************************************************************** */
/* */
/* Utility Functions */
/* */
/* **************************************************************** */
/* Cons a new string from STRING starting at START and ending at END,
not including END. */
char *
substring (string, start, end)
char *string;
int start, end;
{
register int len = end - start;
register char *result = (char *)xmalloc (len + 1);
strncpy (result, string + start, len);
result[len] = '\0';
return (result);
}
/* Just like string_extract, but doesn't hack backslashes or any of
that other stuff. */
char *
string_extract_verbatim (string, sindex, charlist)
char *string, *charlist;
int *sindex;
{
register int i = *sindex;
int c;
char *temp;
while ((c = string[i]) && (!member (c, charlist))) i++;
temp = (char *)xmalloc (1 + (i - *sindex));
strncpy (temp, string + (*sindex), i - (*sindex));
temp[i - (*sindex)] = '\0';
*sindex = i;
return (temp);
}
/* Extract a substring from STRING, starting at SINDEX and ending with
one of the characters in CHARLIST. Don't make the ending character
part of the string. Leave SINDEX pointing at the ending character.
Understand about backslashes in the string. */
char *
string_extract (string, sindex, charlist)
char *string, *charlist;
int *sindex;
{
register int c, i = *sindex;
char *temp;
while (c = string[i]) {
if (c == '\\')
if (string[i + 1])
i++;
else
break;
else
if (member (c, charlist))
break;
i++;
}
temp = (char *)xmalloc (1 + (i - *sindex));
strncpy (temp, string + (*sindex), i - (*sindex));
temp[i - (*sindex)] = '\0';
*sindex = i;
return (temp);
}
/* Remove backslashes which are quoting backquotes from STRING. Modifies
STRING, and returns a pointer to it. */
char *
de_backslash (string)
char *string;
{
register int i, l = strlen (string);
for (i = 0; i < l; i++)
if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' ||
string[i + 1] == '$'))
strcpy (&string[i], &string[i + 1]);
return (string);
}
/* Replace instances of \! in a string with !. */
void
unquote_bang (string)
char *string;
{
register int i, j;
register char *temp;
temp = (char *)alloca (1 + strlen (string));
for (i = 0, j = 0; (temp[j] = string[i]); i++, j++)
{
if (string[i] == '\\' && string[i + 1] == '!')
{
temp[j] = '!';
i++;
}
}
strcpy (string, temp);
}
/* Extract the $( construct in STRING, and return a new string.
Start extracting at (SINDEX) as if we had just seen "$(".
Make (SINDEX) get the position just after the matching ")". */
char *
extract_command_subst (string, sindex)
char *string;
int *sindex;
{
char *extract_delimited_string ();
return (extract_delimited_string (string, sindex, "$(", "(", ")"));
}
/* Extract the $[ construct in STRING, and return a new string.
Start extracting at (SINDEX) as if we had just seen "$[".
Make (SINDEX) get the position just after the matching "]".
Strictly speaking, according to the letter of POSIX.2, arithmetic
substitutions cannot be nested. This code allows nesting, however,
and it is fully implemented. */
char *
extract_arithmetic_subst (string, sindex)
char *string;
int *sindex;
{
char *extract_delimited_string ();
return (extract_delimited_string (string, sindex, "$[", "[", "]"));
}
/* Extract and create a new string from the contents of STRING, a
character string delimited with OPENER and CLOSER. SINDEX is
the address of an int describing the current offset in STRING;
it should point to just after the first OPENER found. On exit,
SINDEX gets the position just after the matching CLOSER. If
OPENER is more than a single character, ALT_OPENER, if non-null,
contains a character string that can also match CLOSER and thus
needs to be skipped. */
char *
extract_delimited_string (string, sindex, opener, alt_opener, closer)
char *string;
int *sindex;
char *opener, *alt_opener, *closer;
{
register int i, c, l;
int pass_character, nesting_level;
int delimiter, delimited_nesting_level;
int len_closer, len_opener, len_alt_opener;
char *result;
len_opener = strlen (opener);
len_alt_opener = alt_opener ? strlen (alt_opener) : 0;
len_closer = strlen (closer);
pass_character = delimiter = delimited_nesting_level = 0;
nesting_level = 1;
for (i = *sindex; c = string[i]; i++)
{
if (pass_character)
{
pass_character = 0;
continue;
}
if (c == '\\')
{
if ((delimiter == '"') &&
(member (string[i + 1], slashify_in_quotes)))
{
pass_character++;
continue;
}
}
if (!delimiter || delimiter == '"')
{
if (strncmp (string + i, opener, len_opener) == 0)
{
if (!delimiter)
nesting_level++;
else
delimited_nesting_level++;
i += len_opener - 1;
continue;
}
if (len_alt_opener &&
strncmp (string + i, alt_opener, len_alt_opener) == 0)
{
if (!delimiter)
nesting_level++;
else
delimited_nesting_level++;
i += len_alt_opener - 1;
continue;
}
if (strncmp (string + i, closer, len_closer) == 0)
{
i += len_closer - 1;
if (delimiter && delimited_nesting_level)
delimited_nesting_level--;
if (!delimiter)
{
nesting_level--;
if (nesting_level == 0)
break;
}
}
}
if (delimiter)
{
if (c == delimiter || delimiter == '\\')
delimiter = 0;
continue;
}
else
{
if (c == '"' || c == '\'' || c == '\\')
delimiter = c;
}
}
l = i - *sindex;
result = (char *)xmalloc (1 + l);
strncpy (result, &string[*sindex], l);
result[l] = '\0';
*sindex = i;
if (!c && (delimiter || nesting_level))
{
report_error ("bad substitution: %s%s", opener, result);
free (result);
longjmp (top_level, DISCARD);
}
return (result);
}
/* An artifact for extracting the contents of a quoted string. Since the
string is about to be evaluated, we pass everything through, and only
strip backslash before backslash or quote. */
/* This is a mini state machine. */
char *
string_extract_double_quoted (string, sindex)
char *string;
int *sindex;
{
register int c, j, i;
char *temp; /* The new string we return. */
int pass_next, backquote; /* State variables for the machine. */
pass_next = backquote = 0;
temp = (char *)xmalloc (1 + (strlen (string) - *sindex));
for (j = 0, i = *sindex; c = string[i]; i++)
{
/* Process a character that was quoted by a backslash. */
if (pass_next)
{
/* Posix.2 sez:
``The backslash shall retain its special meaning as an escape
character only when followed by one of the characters:
$ ` " \ <newline>''.
We handle the double quotes here. expand_word_internal handles
the rest. */
if (c != '"')
temp[j++] = '\\';
temp[j++] = c;
pass_next = 0;
continue;
}
/* A backslash protects the next character. The code just above
handles preserving the backslash in front of any character but
a double quote. */
if (c == '\\')
{
pass_next++;
continue;
}
/* Inside backquotes, ``the portion of the quoted string from the
initial backquote and the characters up to the next backquote
that is not preceded by a backslash, having escape characters
removed, defines that command''. */
if (backquote)
{
if (c == '`')
backquote = 0;
temp[j++] = c;
continue;
}
if (c == '`')
{
temp[j++] = c;
backquote++;
continue;
}
/* Pass everything between `$(' and the matching `)' through verbatim. */
if (c == '$' && string[i + 1] == '(')
{
register int t;
int si;
char *ret;
si = i + 2;
ret = extract_delimited_string (string, &si, "$(", "(", ")");
temp[j++] = '$';
temp[j++] = '(';
for (t = 0; ret[t]; t++)
temp[j++] = ret[t];
i = si;
temp[j++] = string[i];
free (ret);
continue;
}
/* An unescaped double quote serves to terminate the string. */
if (c == '"')
break;
/* Add the character to the quoted string we're accumulating. */
temp[j++] = c;
}
temp[j] = '\0';
*sindex = i;
return (temp);
}
/* Extract the name of the variable to bind to from the assignment string. */
char *
assignment_name (string)
char *string;
{
int offset = assignment (string);
char *temp;
if (!offset) return (char *)NULL;
temp = (char *)xmalloc (offset + 1);
strncpy (temp, string, offset);
temp[offset] = '\0';
return (temp);
}
/* Return a single string of all the words in LIST. SEP is the separator
to put between individual elements of LIST in the output string. */
static char *
string_list_internal (list, sep)
WORD_LIST *list;
char *sep;
{
char *result = (char *)NULL;
int sep_len;
sep_len = strlen (sep);
while (list)
{
/* Can't simply let xrealloc malloc the bytes for us the first time
because of the strcat (result, ...) -- we need to make sure result
is initialized to null after being allocated initially. */
if (!result)
result = savestring ("");
result = (char *)xrealloc
(result, 2 + sep_len + strlen (result) + strlen (list->word->word));
strcat (result, list->word->word);
if (list->next)
strcat (result, sep);
list = list->next;
}
return (result);
}
/* Return a single string of all the words present in LIST, separating
each word with a space. */
char *
string_list (list)
WORD_LIST *list;
{
return (string_list_internal (list, " "));
}
/* Return a single string of all the words present in LIST, obeying the
quoting rules for "$*", to wit: (P1003.2, draft 11, 3.5.2, "If the
expansion [of $*] appears within a double quoted string, it expands
to a single field with the value of each parameter separated by the
first character of the IFS variable, or by a <space> if IFS is unset
[or null]." */
char *
string_list_dollar_star (list)
WORD_LIST *list;
{
char *ifs = get_string_value ("IFS");
char sep[2];
if (!ifs)
sep[0] = ' ';
else if (!*ifs)
sep[0] = '\0';
else
sep[0] = *ifs;
sep[1] = '\0';
return (string_list_internal (list, sep));
}
/* Return the list of words present in STRING. Separate the string into
words at any of the characters found in SEPARATORS. If QUOTED is
non-zero then word in the list will have its quoted flag set, otherwise
the quoted flag is left as make_word () deemed fit.
This obeys the P1003.2 draft 11 word splitting semantics. If `separators'
is exactly <space><tab><newline>, then the splitting algorithm is that of
the Bourne shell, which treats any sequence of characters from `separators'
as a delimiter. If IFS is unset, which results in `separators' being set
to "", no splitting occurs. If separators has some other value, the
following rules are applied (`IFS white space' means zero or more
occurrences of <space>, <tab>, or <newline>, as long as those characters
are in `separators'):
1) IFS white space is ignored at the start and the end of the
string.
2) Each occurrence of a character in `separators' that is not
IFS white space, along with any adjacent occurrences of
IFS white space delimits a field.
3) Any nonzero-length sequence of IFS white space delimits a field.
*/
/* BEWARE! list_string strips null arguments. Don't call it twice and
expect to have "" preserved! */
/* Is C a quoted NULL character? */
#define QUOTED_NULL(c) ((unsigned char)(c) == (unsigned char)0x80)
/* Perform quoted null character removal on STRING. */
void
remove_quoted_nulls (string)
char *string;
{
register char *s;
for (s = string; s && *s; s++)
{
if (QUOTED_NULL (*s))
{
strcpy (s, s + 1);
s--;
}
}
}
/* Perform quoted null character removal on each element of LIST.
This modifies LIST. */
word_list_remove_quoted_nulls (list)
WORD_LIST *list;
{
register WORD_LIST *t;
t = list;
while (t)
{
remove_quoted_nulls (t->word->word);
t = t->next;
}
}
/* This performs word splitting and quoted null character removal on
STRING. */
#define issep(c) (member ((c), separators))
#define spctabnl(c) ((c) == ' '|| (c) == '\t' || (c) == '\n')
WORD_LIST *
list_string (string, separators, quoted)
register char *string, *separators;
int quoted;
{
WORD_LIST *result = (WORD_LIST *)NULL;
char *current_word = (char *)NULL, *s;
int sindex = 0;
int sh_style_split;
if (!string || !*string)
return ((WORD_LIST *)NULL);
sh_style_split = separators && *separators && (!strcmp (separators, " \t\n"));
/* Remove sequences of whitespace at the beginning and end of STRING, as
long as those characters appear in IFS. */
for (s = string; *s && spctabnl (*s) && issep (*s); s++);
if (!*s)
return ((WORD_LIST *)NULL);
string = s;
s += strlen (s) - 1;
for ( ; s > string && *s && spctabnl (*s) & issep (*s); s--);
if (!*s)
return ((WORD_LIST *)NULL);
*++s = '\0';
/* OK, now STRING points to a word that does not begin with white space.
The splitting algorithm is:
extract a word, stopping at a separator
skip sequences of spc, tab, or nl as long as they are separators
This obeys the field splitting rules in Posix.2 draft 11.x. */
while (string[sindex])
{
current_word = string_extract_verbatim (string, &sindex, separators);
if (!current_word)
break;
/* If we have a quoted empty string, add a quoted null argument. We
want to preserve the quoted null character iff this is a quoted
empty string; otherwise the quoted null characters are removed
below. */
if (QUOTED_NULL (current_word[0]) && current_word[1] == '\0')
{
WORD_DESC *t = make_word (" ");
t->quoted++;
t->word[0] = (unsigned char)QUOTE_CHAR ('\0');
result = make_word_list (t, result);
}
/* If we have something, then add it regardless. */
else if (strlen (current_word))
{
register char *temp_string;
/* Perform quoted null character removal on the current word. */
for (temp_string = current_word; *temp_string; temp_string++)
if (QUOTED_NULL (*temp_string))
{
strcpy (temp_string, temp_string + 1);
temp_string--;
}
result = make_word_list (make_word (current_word), result);
if (quoted)
result->word->quoted++;
}
/* If we're not doing sequences of separators in the traditional
Bourne shell style, then add a quoted null argument. */
else if (!sh_style_split && !spctabnl (string[sindex]))
{
result = make_word_list (make_word (""), result);
result->word->quoted++;
}
free (current_word);
/* Move past the current separator character. */
if (string[sindex])
sindex++;
/* Now skip sequences of space, tab, or newline characters if they are
in the list of separators. */
while (string[sindex] &&
spctabnl (string[sindex]) &&
issep (string[sindex]))
sindex++;
}
return (WORD_LIST *)reverse_list (result);
}
/* Given STRING, an assignment string, get the value of the right side
of the `=', and bind it to the left side. If EXPAND is true, then
perform parameter expansion, command substitution, and arithmetic
expansion on the right-hand side. Perform tilde expansion in any
case. Do not perform word splitting on the result of expansion. */
do_assignment_internal (string, expand)
char *string;
int expand;
{
int offset = assignment (string);
char *name = savestring (string);
char *value = (char *)NULL;
SHELL_VAR *entry = (SHELL_VAR *)NULL;
if (name[offset] == '=')
{
char *tilde_expand (), *string_list ();
WORD_LIST *list, *expand_string_unsplit ();
char *temp;
name[offset] = 0;
temp = name + offset + 1;
if (expand)
{
if (index (temp, '~'))
temp = tilde_expand (temp);
else
temp = savestring (temp);
list = expand_string_unsplit (temp, 0);
if (list)
{
value = string_list (list);
dispose_words (list);
}
free (temp);
}
else
value = savestring (temp);
}
if (!value)
value = savestring ("");
entry = bind_variable (name, value);
if (echo_command_at_execute)
{
extern char *indirection_level_string ();
fprintf (stderr, "%s%s=%s\n", indirection_level_string (), name, value);
}
/* Yes, here is where the special shell variables get tested for.
Don't ask me, I just work here. This is really stupid. I would
swear, but I've decided that that is an impolite thing to do in
source that is to be distributed around the net, even if this code
is totally brain-damaged. */
/* if (strcmp (name, "PATH") == 0) Yeeecchhh!!!*/
stupidly_hack_special_variables (name);
if (entry)
entry->attributes &= ~att_invisible;
if (value)
free (value);
free (name);
}
/* Perform the assignment statement in STRING, and expand the
right side by doing command and parameter expansion. */
do_assignment (string)
char *string;
{
do_assignment_internal (string, 1);
}
/* Given STRING, an assignment string, get the value of the right side
of the `=', and bind it to the left side. Do not do command and
parameter substitution on the right hand side. */
do_assignment_no_expand (string)
char *string;
{
do_assignment_internal (string, 0);
}
/* Most of the substitutions must be done in parallel. In order
to avoid using tons of unclear goto's, I have some functions
for manipulating malloc'ed strings. They all take INDEX, a
pointer to an integer which is the offset into the string
where manipulation is taking place. They also take SIZE, a
pointer to an integer which is the current length of the
character array for this string. */
/* Append SOURCE to TARGET at INDEX. SIZE is the current amount
of space allocated to TARGET. SOURCE can be NULL, in which
case nothing happens. Gets rid of SOURCE by free ()ing it.
Returns TARGET in case the location has changed. */
char *
sub_append_string (source, target, index, size)
char *source, *target;
int *index, *size;
{
if (source)
{
while ((int)strlen (source) >= (int)(*size - *index))
target = (char *)xrealloc (target, *size += DEFAULT_ARRAY_SIZE);
strcat (target, source);
*index += strlen (source);
free (source);
}
return (target);
}
/* Append the textual representation of NUMBER to TARGET.
INDEX and SIZE are as in SUB_APPEND_STRING. */
char *
sub_append_number (number, target, index, size)
int number, *index, *size;
char *target;
{
char *temp = (char *)xmalloc (10);
sprintf (temp, "%d", number);
return (sub_append_string (temp, target, index, size));
}
/* Return the word list that corresponds to `$*'. */
WORD_LIST *
list_rest_of_args ()
{
register WORD_LIST *list = (WORD_LIST *)NULL;
register WORD_LIST *args = rest_of_args;
int i;
for (i = 1; i < 10; i++)
if (dollar_vars[i])
list = make_word_list (make_word (dollar_vars[i]), list);
while (args)
{
list = make_word_list (make_word (args->word->word), list);
args = args->next;
}
return ((WORD_LIST *)reverse_list (list));
}
/* Make a single large string out of the dollar digit variables,
and the rest_of_args. If DOLLAR_STAR is 1, then obey the special
case of "$*" with respect to IFS. */
char *
string_rest_of_args (dollar_star)
int dollar_star;
{
register WORD_LIST *list = list_rest_of_args ();
char *string;
if (!dollar_star)
string = string_list (list);
else
string = string_list_dollar_star (list);
dispose_words (list);
return (string);
}
/***************************************************
* *
* Functions to Expand a String *
* *
***************************************************/
/* Perform parameter expansion, command substitution, and arithmetic
expansion on STRING, as if it were a word. Leave the result quoted. */
static WORD_LIST *
expand_string_internal (string, quoted)
char *string;
int quoted;
{
WORD_DESC *make_word (), *temp = make_word (string);
WORD_LIST *tresult, *expand_word_internal ();
tresult = expand_word_internal (temp, quoted, (int *)NULL, (int *)NULL);
dispose_word (temp);
return (tresult);
}
/* Expand STRING by performing parameter expansion, command substitution,
and arithmetic expansion. Dequote the resulting WORD_LIST before
returning it, but do not perform word splitting. The call to
remove_quoted_nulls () is in here because word splitting normally
takes care of quote removal. */
WORD_LIST *
expand_string_unsplit (string, quoted)
char *string;
int quoted;
{
WORD_LIST *value = expand_string_internal (string, quoted);
if (value && value->word)
remove_quoted_nulls (value->word->word);
if (value)
dequote_list (value);
return (value);
}
/* Expand STRING just as if you were expanding a word. This also returns
a list of words. Note that filename globbing is *NOT* done for word
or string expansion, just when the shell is expanding a command. This
does parameter expansion, command substitution, arithmetic expansion,
and word splitting. Dequote the resultant WORD_LIST before returning. */
WORD_LIST *
expand_string (string, quoted)
char *string;
int quoted;
{
WORD_LIST *value = expand_string_internal (string, quoted);
WORD_LIST *result, *word_list_split ();
result = word_list_split (value);
dispose_words (value);
if (result)
dequote_list (result);
return (result);
}
/* Expand STRING just as if you were expanding a word, but do not dequote
the resultant WORD_LIST. This is called only from within this file,
and is used to correctly preserve quoted characters when expanding
things like ${1+"$@"}. This does parameter expansion, command
subsitution, arithmetic expansion, and word splitting. */
static WORD_LIST *
expand_string_leave_quoted (string, quoted)
char *string;
int quoted;
{
WORD_LIST *tlist = expand_string_internal (string, quoted);
WORD_LIST *tresult, *word_list_split ();
tresult = word_list_split (tlist);
dispose_words (tlist);
return (tresult);
}
/***************************************************
* *
* Functions to handle quoting chars *
* *
***************************************************/
/* I'm going to have to rewrite expansion because filename globbing is
beginning to make the entire arrangement ugly. I'll do this soon. */
dequote_list (list)
register WORD_LIST *list;
{
register char *s;
while (list)
{
s = dequote_string (list->word->word);
free (list->word->word);
list->word->word = s;
list = list->next;
}
}
/* Quote the string S. Return a new string. */
static char *
quote_string (s)
char *s;
{
unsigned char *result;
/* If S is an empty string then simply create a string consisting of a
quoted null. */
if (s[0] == '\0')
{
result = (unsigned char *)xmalloc (2);
result[0] = (unsigned char)QUOTE_CHAR ('\0');
result[1] = '\0';
}
else
{
register unsigned char *t;
result = (unsigned char *)savestring (s);
for (t = result; t && *t ; t++)
*t |= 0x80;
}
return ((char *)result);
}
/* De-quoted quoted characters in string s. */
static char *
dequote_string (s)
char *s;
{
register unsigned char *t;
unsigned char *result;
result = (unsigned char *)savestring (s);
for (t = result; t && *t ; t++)
*t = DEQUOTE_CHAR (*t);
return ((char *)result);
}
/* Quote the entire WORD_LIST list. */
static void
quote_list (list)
WORD_LIST *list;
{
register WORD_LIST *w;
for (w = list; w; w = w->next)
{
char *t = w->word->word;
w->word->word = quote_string (t);
free (t);
w->word->quoted = 1;
}
}
/* **************************************************************** */
/* */
/* Functions for Removing Patterns */
/* */
/* **************************************************************** */
/* Remove the portion of PARAM matched by PATTERN according to OP, where OP
can have one of 4 values:
RP_LONG_LEFT remove longest matching portion at start of PARAM
RP_SHORT_LEFT remove shortest matching portion at start of PARAM
RP_LONG_RIGHT remove longest matching portion at end of PARAM
RP_SHORT_RIGHT remove shortest matching portion at end of PARAM
*/
#define RP_LONG_LEFT 1
#define RP_SHORT_LEFT 2
#define RP_LONG_RIGHT 3
#define RP_SHORT_RIGHT 4
static char *
remove_pattern (param, pattern, op)
char *param, *pattern;
int op;
{
register int len = param ? strlen (param) : 0;
register char *end = param + len;
register char *p, *ret, c;
if (pattern == NULL || *pattern == '\0') /* minor optimization */
return (savestring (param));
if (param == NULL || *param == '\0')
return (param);
switch (op)
{
case RP_LONG_LEFT: /* remove longest match at start */
for (p = end; p >= param; p--)
{
c = *p; *p = '\0';
if (fnmatch (pattern, param, 0) != FNM_NOMATCH)
{
*p = c;
return (savestring (p));
}
*p = c;
}
break;
case RP_SHORT_LEFT: /* remove shortest match at start */
for (p = param; p <= end; p++)
{
c = *p; *p = '\0';
if (fnmatch (pattern, param, 0) != FNM_NOMATCH)
{
*p = c;
return (savestring (p));
}
*p = c;
}
break;
case RP_LONG_RIGHT: /* remove longest match at end */
for (p = param; p <= end; p++)
{
if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
{
c = *p;
*p = '\0';
ret = savestring (param);
*p = c;
return (ret);
}
}
break;
case RP_SHORT_RIGHT: /* remove shortest match at end */
for (p = end; p >= param; p--)
{
if (fnmatch (pattern, p, 0) != FNM_NOMATCH)
{
c = *p;
*p = '\0';
ret = savestring (param);
*p = c;
return (ret);
}
}
break;
}
return (savestring (param)); /* no match, return original string */
}
/*******************************************
* *
* Functions to expand WORD_DESCs *
* *
*******************************************/
/* Expand WORD, performing word splitting on the result. This does
parameter expansion, command substitution, arithmetic expansion,
word splitting, and quote removal. */
WORD_LIST *
expand_word (word, quoted)
WORD_DESC *word;
int quoted;
{
WORD_LIST *word_list_split (), *expand_word_internal ();
WORD_LIST *result, *tresult;
tresult = expand_word_internal (word, quoted, (int *)NULL, (int *)NULL);
result = word_list_split (tresult);
dispose_words (tresult);
if (result)
dequote_list (result);
return (result);
}
/* Expand WORD, but do not perform word splitting on the result. This
does parameter expansion, command substitution, arithmetic expansion,
and quote removal. */
WORD_LIST *
expand_word_no_split (word, quoted)
WORD_DESC *word;
int quoted;
{
WORD_LIST *expand_word_internal ();
WORD_LIST *result;
result = expand_word_internal (word, quoted, (int *)NULL, (int *)NULL);
if (result)
dequote_list (result);
return (result);
}
WORD_LIST *
expand_word_leave_quoted (word, quoted)
WORD_DESC *word;
int quoted;
{
WORD_LIST *expand_word_internal (), *result;
result = expand_word_internal (word, quoted, (int *)NULL, (int *)NULL);
return (result);
}
/* Return the value of a positional parameter. This handles values > 10. */
char *
get_dollar_var_value (ind)
int ind;
{
char *temp;
if (ind < 10)
{
if (dollar_vars[ind])
temp = savestring (dollar_vars[ind]);
else
temp = (char *)NULL;
}
else /* We want something like ${11} */
{
WORD_LIST *p = rest_of_args;
ind -= 10;
while (p && ind--)
p = p->next;
if (p)
temp = savestring (p->word->word);
else
temp = (char *)NULL;
}
return (temp);
}
/* Perform command substitution on STRING. This returns a string,
possibly quoted. */
static char *
command_substitute (string, quoted)
char *string;
int quoted;
{
pid_t pid, old_pid;
int fildes[2];
char *istring = (char *)NULL;
int istring_index, istring_size, c = 1;
extern int interactive, last_command_exit_value;
#if defined(__OS2__)
HFILE localPipeIn,
localPipeOut;
ULONG bytesWritten,
rc;
#endif
istring_index = istring_size = 0;
/* Don't fork () if there is no need to. In the case of no command to
run, just return NULL. */
if (!string || !*string)
return ((char *)NULL);
/* Pipe the output of executing STRING into the current shell. */
if (pipe (fildes) < 0)
{
report_error ("Can't make pipes for command substitution!");
goto error_exit;
}
old_pid = last_made_pid;
#if defined (JOB_CONTROL)
{
pid_t old_pipeline_pgrp = pipeline_pgrp;
pipeline_pgrp = shell_pgrp;
pid = make_child (savestring ("command substitution"), 0);
stop_making_children ();
pipeline_pgrp = old_pipeline_pgrp;
}
#else /* JOB_CONTROL */
#if defined(__EMX__)
localPipeIn = pipeReadHandle;
localPipeOut = pipeWriteHandle;
rc = DosCreatePipe(&pipeReadHandle, &pipeWriteHandle, 20);
#if defined(__DEBUG__)
DEBUG_OUT("command_substitute: pid=%d,localPipeIn=%d, localPipeOut=%d, \
pipeReadHandle=%d, pipeWriteHandle=%d\n", getpid(), localPipeIn, localPipeOut,
pipeReadHandle, pipeWriteHandle);
DEBUG_OUT("command_substitute: forking at line 1209 string=%s, myPid=%d\n",
string, getpid());
DosSetPriority(PRTYS_PROCESS, PRTYC_NOCHANGE, 5, 0);
#endif
#endif
pid = make_child (savestring ("command substitution"), 0);
#endif /* JOB_CONTROL */
if (pid < 0)
{
#if defined(__EMX__)
DosClose(pipeWriteHandle);
DosClose(pipeReadHandle);
pipeReadHandle = localPipeIn;
pipeWriteHandle = localPipeOut;
DosSetPriority(PRTYS_PROCESS, PRTYC_NOCHANGE, -5, 0);
#endif
report_error ("Can't make a child for command substitution!");
error_exit:
if (istring)
free (istring);
return ((char *)NULL);
}
if (pid == 0)
{int childLastPipeOut;
int result_code;
#if defined(__EMX__)
DosSetPriority(PRTYS_PROCESS, PRTYC_NOCHANGE, -5, 0);
closeForkPipes(localPipeIn, localPipeOut);
DosClose(pipeReadHandle);
DosExitList(0x2000+EXLST_ADD, (PFNEXITLIST) childExitListProc);
pipeReadHandle = 0;
#endif
#if defined (JOB_CONTROL)
set_job_control (0);
#endif
if (dup2 (fildes[1], 1) < 0)
{
extern int errno;
report_error ("command_substitute: cannot duplicate pipe as fd 1: %s\n",
strerror (errno));
#if defined(__EMX__)
result_code = EXECUTION_FAILURE;
DosSetPriority(PRTYS_PROCESS, PRTYC_NOCHANGE, +5, 0);
rc = DosWrite(pipeWriteHandle, &result_code, 4, &bytesWritten);
#if defined(__DEBUG__)
DEBUG_OUT("command_substitute: (writting) pid=%d, pipeWriteHandle=%d, result_code=%d,
rc=%d\n", getpid(), pipeWriteHandle, result_code, rc);
#endif
DosClose(pipeWriteHandle);
pipeWriteHandle = 0;
#endif
exit (EXECUTION_FAILURE);
}
close (fildes[1]);
/* If standard output is closed in the parent shell
(such as after `exec >&-'), file descriptor 1 will be
the lowest available file descriptor, and end up in
fildes[0]. This can happen for stdin and stderr as well,
but stdout is more important -- it will cause no output
to be generated from this command. */
if (fildes[0] > 2)
close (fildes[0]);
interactive = 0;
childLastPipeOut = pipeWriteHandle;
result_code = parse_and_execute (string, "command substitution");
pipeWriteHandle = childLastPipeOut;
#if defined(__EMX__)
DosSetPriority(PRTYS_PROCESS, PRTYC_NOCHANGE, +5, 0);
rc = DosWrite(pipeWriteHandle, &result_code, 4, &bytesWritten);
#if defined(__DEBUG__)
DEBUG_OUT("command_substitute (writting) pid=%d, pipeWriteHandle=%d, result_code=%d,\
rc=%d\n", getpid(), pipeWriteHandle, result_code, rc);
#endif
DosClose(pipeWriteHandle);
pipeWriteHandle = 0;
#endif
exit(result_code);
} /* child */
else
{
FILE *istream;
istream = fdopen (fildes[0], "r");
#if defined (JOB_CONTROL) && defined (PGRP_PIPE)
close_pgrp_pipe ();
#endif /* JOB_CONTROL && PGRP_PIPE */
close (fildes[1]);
if (!istream)
{
DosClose(pipeReadHandle);
DosClose(pipeWriteHandle);
pipeReadHandle = localPipeIn;
pipeWriteHandle = localPipeOut;
report_error ("Can't reopen pipe to command substitution");
DosSetPriority(PRTYS_PROCESS, PRTYC_NOCHANGE, -5, 0);
goto error_exit;
}
/* Read the output of the command through the pipe. */
while (1)
{
#if defined (USG) || (defined (_POSIX_VERSION) && defined (Ultrix))
c = sysv_getc (istream);
#else
c = getc (istream);
#endif
if (c == EOF)
break;
/* Add the character to ISTRING. */
while (istring_index + 1 >= istring_size)
istring = (char *) xrealloc
(istring, istring_size += DEFAULT_ARRAY_SIZE);
if (quoted)
istring[istring_index++] = QUOTE_CHAR (c);
else
istring[istring_index++] = c;
istring[istring_index] = '\0';
}
fclose (istream);
close (fildes[0]);
DosClose(pipeWriteHandle);
pipeWriteHandle = 0;
last_command_exit_value = wait_for (pid);
DosSetPriority(PRTYS_PROCESS, PRTYC_NOCHANGE, -5, 0);
pipeReadHandle = localPipeIn;
pipeWriteHandle = localPipeOut;
last_command_subst_pid = pid;
last_made_pid = old_pid;
#if defined (JOB_CONTROL)
/* If last_command_exit_value > 128, then the substituted command
was terminated by a signal. If that signal was SIGINT, then send
SIGINT to ourselves. This will break out of loops, for instance. */
if (last_command_exit_value == (128 + SIGINT))
kill (getpid (), SIGINT);
/* wait_for gives the terminal back to shell_pgrp. If some other
process group should have it, give it away to that group here. */
if (pipeline_pgrp != (pid_t)0)
give_terminal_to (pipeline_pgrp);
#endif /* JOB_CONTROL */
/* If we read no output, just return now and save ourselves some
trouble. */
if (istring_index == 0)
goto error_exit;
/* Strip trailing newlines from the output of the command. */
if (quoted)
{
while (istring_index > 0 &&
DEQUOTE_CHAR (istring[istring_index - 1]) == '\n')
--istring_index;
istring[istring_index] = '\0';
}
else
{
strip_trailing (istring, 1);
istring_index = strlen (istring);
}
return (istring);
}
}
/********************************************************
* *
* Utility functions for parameter expansion *
* *
********************************************************/
/* Handle removing a pattern from a string as a result of ${name%[%]value}
or ${name#[#]value}. */
static char *
parameter_brace_remove_pattern (value, temp, c)
char *value, *temp;
int c;
{
int pattern_specifier;
WORD_LIST *l;
char *pattern, *t;
if (c == '#')
{
if (*value == '#')
{
value++;
pattern_specifier = RP_LONG_LEFT;
}
else
pattern_specifier = RP_SHORT_LEFT;
}
else /* c == '%' */
{
if (*value == '%')
{
value++;
pattern_specifier = RP_LONG_RIGHT;
}
else
pattern_specifier = RP_SHORT_RIGHT;
}
l = expand_string (value, 0);
pattern = (char *)string_list (l);
dispose_words (l);
t = remove_pattern (temp, pattern, pattern_specifier);
free (pattern);
return (t);
}
/* Parameter expand NAME, and return a new string which is the expansion,
or NULL if there was no expansion.
VAR_IS_SPECIAL is non-zero if NAME is one of the special variables in
the shell, e.g., "@", "$", "*", etc. QUOTED, if non-zero, means that
NAME was found inside of a double-quoted expression. */
static char *
parameter_brace_expand_word (name, var_is_special, quoted)
char *name;
int var_is_special, quoted;
{
char *temp = (char *)NULL;
/* Handle multiple digit arguments, as in ${11}. */
if (digit (*name))
{
int arg_index = atoi (name);
temp = get_dollar_var_value (arg_index);
}
else if (var_is_special) /* ${@} */
{
char *tt;
WORD_LIST *l;
tt = (char *)alloca (2 + strlen (name));
tt[0] = '$'; tt[1] = '\0';
strcat (tt, name);
l = expand_string_leave_quoted (tt, quoted);
temp = string_list (l);
dispose_words (l);
}
else
{
SHELL_VAR *var = find_variable (name);
if (var && !invisible_p (var) && (temp = value_cell (var)))
temp = savestring (temp);
}
return (temp);
}
/* Expand the right side of a parameter expansion of the form ${NAMEcVALUE},
depending on the value of C, the separating character. C can be one of
"-", "+", or "=". */
static char *
parameter_brace_expand_rhs (name, value, c, quoted)
char *name, *value;
int c;
{
extern char *tilde_expand ();
WORD_LIST *l;
char *t, *t1, *temp;
if (value[0] == '~' ||
(index (value, '~') && unquoted_substring ("=~", value)))
temp = tilde_expand (value);
else
temp = savestring (value);
l = expand_string_leave_quoted (temp, quoted);
free (temp);
temp = (char *)string_list (l);
dispose_words (l);
if (c == '-' || c == '+')
return (temp);
/* c == '=' */
if (temp)
t = savestring (temp);
else
t = savestring ("");
t1 = dequote_string (t);
free (t);
t = t1;
bind_variable (name, t);
free (t);
return (temp);
}
/* Deal with the right hand side of a ${name:?value} expansion in the case
that NAME is null or not set. If VALUE is non-null it is expanded and
used as the error message to print, otherwise a standard message is
printed. */
static void
parameter_brace_expand_error (name, value)
char *name, *value;
{
extern int interactive;
if (value && *value)
{
WORD_LIST *l = expand_string (value, 0);
char *temp1 = string_list (l);
fprintf (stderr, "%s: %s\n", name, temp1 ? temp1 : value);
if (temp1)
free (temp1);
dispose_words (l);
}
else
report_error ("%s: parameter null or not set", name);
/* Free the data we have allocated during this expansion, since we
are about to longjmp out. */
free (name);
if (value)
free (value);
if (!interactive)
longjmp (top_level, FORCE_EOF);
else
longjmp (top_level, DISCARD);
}
/* Handle the parameter brace expansion that requires us to return the
length of a parameter. */
static int
parameter_brace_expand_length (name)
char *name;
{
char *t;
int number = 0;
if (name[1] == '\0') /* ${#} */
{
WORD_LIST *l = list_rest_of_args ();
number = list_length (l);
dispose_words (l);
}
else if (name[1] != '*' && name[1] != '@')
{
number = 0;
if (digit (name[1])) /* ${#1} */
{
if (t = get_dollar_var_value (atoi (&name[1])))
{
number = strlen (t);
free (t);
}
}
else /* ${#PS1} */
{
WORD_LIST *list;
char *newname;
newname = savestring (name);
newname[0] = '$';
list = expand_string (newname, 0);
t = string_list (list);
free (newname);
dispose_words (list);
if (t)
number = strlen (t);
}
}
else /* ${#@} and ${#*} */
{
if (t = string_rest_of_args (1))
{
number = strlen (t);
free (t);
}
}
return (number);
}
/* Make a word list which is the parameter and variable expansion,
command substitution, arithmetic substitution, and quote removed
expansion of WORD. Return a pointer to a WORD_LIST which is the
result of the expansion. If WORD contains a null word, the word
list returned is also null.
QUOTED, when non-zero specifies that the text of WORD is treated
as if it were surrounded by double quotes.
CONTAINS_DOLLAR_AT and EXPANDED_SOMETHING are return values; when non-null
they point to an integer value which receives information about expansion.
CONTAINS_DOLLAR_AT gets non-zero if WORD contained "$@", else zero.
EXPANDED_SOMETHING get non-zero if WORD contained any parameter expansions,
else zero.
This only does word splitting in the case of $@ expansion. In that
case, we split on ' '. */
WORD_LIST *
expand_word_internal (word, quoted, contains_dollar_at, expanded_something)
WORD_DESC *word;
int quoted;
int *contains_dollar_at;
int *expanded_something;
{
extern char *itos ();
extern int last_command_exit_value;
/* The thing that we finally output. */
WORD_LIST *result = (WORD_LIST *)NULL;
/* The intermediate string that we build while expanding. */
char *istring = (char *)xmalloc (DEFAULT_ARRAY_SIZE);
/* The current size of the above object. */
int istring_size = DEFAULT_ARRAY_SIZE;
/* Index into ISTRING. */
int istring_index = 0;
/* Temporary string storage. */
char *temp = (char *)NULL;
/* The text of WORD. */
register char *string = word->word;
/* The index into STRING. */
register int sindex = 0;
/* This gets 1 if we see a $@ while quoted. */
int quoted_dollar_at = 0;
/* This gets 1 if we are to treat backslashes as if we are within double
quotes, but not otherwise behave as if the word is quoted. This is
used for things like expansion of patterns in case statement pattern
lists. This is a private variable, but the incoming value of
Q_KEEP_BACKSLASH is passed to recursive invocations of this function. */
int preserve_backslashes = 0;
register int c; /* Current character. */
int number; /* Temporary number value. */
int t_index; /* For calls to string_extract_xxx. */
extern int interactive;
char *command_subst_result; /* For calls to command_substitute (). */
istring[0] = '\0';
if (!string) goto final_exit;
if (quoted & Q_KEEP_BACKSLASH)
{
preserve_backslashes = 1;
quoted &= ~Q_KEEP_BACKSLASH;
}
if (contains_dollar_at)
*contains_dollar_at = 0;
/* Begin the expansion. */
for (;;) {
c = string[sindex];
switch (c) { /* Case on toplevel character. */
case '\0':
goto finished_with_string;
case '$':
if (expanded_something)
*expanded_something = 1;
c = string[++sindex];
/* Do simple cases first. Switch on what follows '$'. */
switch (c)
{
/* $0 .. $9? */
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (dollar_vars[digit_value (c)])
temp = savestring (dollar_vars[digit_value (c)]);
else
temp = (char *)NULL;
goto dollar_add_string;
case '$': /* $$ -- pid of the invoking shell. */
{
extern int dollar_dollar_pid;
number = dollar_dollar_pid;
}
add_number:
temp = itos (number);
dollar_add_string:
if (string[sindex]) sindex++;
/* Add TEMP to ISTRING. */
add_string:
istring =
sub_append_string (temp, istring, &istring_index, &istring_size);
break;
/* $# -- number of positional parameters. */
case '#':
{
WORD_LIST *list = list_rest_of_args ();
number = list_length (list);
dispose_words (list);
goto add_number;
}
/* $? -- return value of the last synchronous command. */
case '?':
number = last_command_exit_value;
goto add_number;
/* $- -- flags supplied to the shell on invocation or by `set'. */
case '-':
temp = (char *)which_set_flags ();
goto dollar_add_string;
/* $! -- Pid of the last asynchronous command. */
case '!':
{
number = (int)last_asynchronous_pid;
/* If no asynchronous pids have been created, echo nothing. */
if (number == (int)NO_PID)
{
if (string[sindex])
sindex++;
if (expanded_something)
*expanded_something = 0;
break;
}
goto add_number;
}
/* The only difference between this and $@ is when the
arg is quoted. */
case '*': /* `$*' */
temp = string_rest_of_args (quoted);
/* In the case of a quoted string, quote the entire arg-list.
"$1 $2 $3". */
if (quoted && temp)
{
char *james_brown = temp;
temp = quote_string (temp);
free (james_brown);
}
goto dollar_add_string;
/* When we have "$@" what we want is "$1" "$2" "$3" ... This
means that we have to turn quoting off after we split into
the individually quoted arguments so that the final split
on the first character of $IFS is still done. */
case '@': /* `$@' */
{
WORD_LIST *tlist = list_rest_of_args ();
if (quoted && tlist)
quote_list (tlist);
/* We want to flag the fact that we saw this. We can't turn off
quoting entirely, because other characters in the string might
need it (consider "\"$@\""), but we need some way to signal
that the final split on the first character of $IFS should be
done, even though QUOTED is 1. */
if (quoted)
quoted_dollar_at = 1;
if (contains_dollar_at)
*contains_dollar_at = 1;
temp = string_list (tlist);
goto dollar_add_string;
}
/* ${[#]name[[:]#[#]%[%]-=?+[word]]} */
case '{':
{
int check_nullness = 0;
int var_is_set = 0;
int var_is_null = 0;
int var_is_special = 0;
char *name, *value;
sindex++;
t_index = sindex;
name = string_extract (string, &t_index, "#%:-=?+}");
/* If the name really consists of a special variable, then
make sure that we have the entire name. */
if (sindex == t_index &&
(string[sindex] == '-' ||
string[sindex] == '?' ||
string[sindex] == '#'))
{
char *tt;
t_index++;
free (name);
tt = (string_extract (string, &t_index, "#%:-=?+}"));
name = (char *)xmalloc (2 + (strlen (tt)));
*name = string[sindex];
strcpy (name + 1, tt);
free (tt);
}
sindex = t_index;
/* Find out what character ended the variable name. Then
do the appropriate thing. */
if (c = string[sindex])
sindex++;
if (c == ':')
{
check_nullness++;
if (c = string[sindex])
sindex++;
}
/* Determine the value of this variable. */
if (digit (*name) ||
(strlen (name) == 1 && member (*name, "#-?$!@*")))
var_is_special++;
/* Check for special expansion things. */
if (*name == '#')
{
/* Handle ${#-} and ${#?}. They return the lengths of
$- and $?, respectively. */
if (string[sindex] == '}' &&
!name[1] &&
!check_nullness &&
(c == '-' || c == '?'))
{
char *s;
free (name);
if (c == '-')
s = (char *)which_set_flags ();
else
s = itos (last_command_exit_value);
number = s ? strlen (s) : 0;
if (s)
free (s);
goto add_number;
}
/* Don't allow things like ${#:-foo} to go by; they are
errors. If we are not pointing at the character just
after the closing brace, then we haven't gotten all of
the name. Since it begins with a special character,
this is a bad substitution. Explicitly check for ${#:},
which the rules do not catch. */
if (string[sindex - 1] != '}' || member (c, "?-=+") ||
(string[sindex - 1] == '}' && !name[1] && c == '}' &&
check_nullness))
{
free (name);
name = string;
goto bad_substitution;
}
number = parameter_brace_expand_length (name);
/* We are pointing one character after the brace which
closes this expression. Since the code at add_number
increments SINDEX, we back up a single character here. */
sindex--;
goto add_number;
}
/* ${@} is identical to $@. */
if (name[0] == '@' && name[1] == '\0')
{
if (quoted)
quoted_dollar_at = 1;
if (contains_dollar_at)
*contains_dollar_at = 1;
}
temp = parameter_brace_expand_word (name, var_is_special, quoted);
if (temp)
var_is_set++;
if (!var_is_set || !temp || !*temp)
var_is_null++;
if (!check_nullness)
var_is_null = 0;
/* Get the rest of the stuff inside the braces. */
if (c && c != '}')
{
/* Scan forward searching for last `{'. This is a hack,
it will always be a hack, and it always has been a hack. */
t_index = sindex;
value = extract_delimited_string (string, &t_index,
"{", (char *)NULL, "}");
sindex = t_index;
if (string[sindex] == '}')
sindex++;
else
{
if (value)
free (value);
free (name);
name = string;
goto bad_substitution;
}
}
else
value = (char *)NULL;
/* Do the right thing based on which character ended the variable
name. */
switch (c)
{
case '\0':
bad_substitution:
report_error ("%s: bad substitution", name ? name : "??");
free (name);
longjmp (top_level, DISCARD);
case '}':
break;
case '#': /* ${param#[#]pattern} */
case '%': /* ${param%[%]pattern} */
{
char *t;
if (!value || !*value || !temp || !*temp)
break;
t = parameter_brace_remove_pattern (value, temp, c);
free (temp);
free (value);
temp = t;
}
break;
case '-':
case '=':
case '?':
case '+':
if (var_is_set && !var_is_null)
{
/* We don't want the value of the named variable for
anything, just the value of the right hand side. */
if (c == '+')
{
if (temp)
free (temp);
if (value)
temp = parameter_brace_expand_rhs (name, value, c, quoted);
else
temp = (char *)NULL;
}
/* Otherwise do nothing. Just use the value in temp. */
}
else /* var not set or var is null */
{
if (temp)
free (temp);
temp = (char *)NULL;
if (c == '=' && var_is_special)
{
report_error ("$%s: cannot assign in this way", name);
free (name);
free (value);
longjmp (top_level, DISCARD);
}
else if (c == '?')
parameter_brace_expand_error (name, value);
else if (c != '+')
temp =
parameter_brace_expand_rhs (name, value, c, quoted);
free (value);
}
break;
} /* end case on closing character. */
free (name);
goto add_string;
} /* end case '{' */
/* break; */
case '(': /* Do command or arithmetic substitution. */
/* We have to extract the contents of this paren substitution. */
{
char *extract_command_subst ();
int old_index = ++sindex;
temp = extract_command_subst (string, &old_index);
sindex = old_index;
/* For the Posix.2-style $(( )) form of arithmetic substitution,
extract the expression and pass it to the evaluator. */
if (temp && *temp == '(')
{
char *t = temp + 1;
int last = strlen (t) - 1;
extern long evalexp ();
if (t[last] != ')')
{
report_error ("%s: bad arithmetic substitution", temp);
free (temp);
/* XXX - these are mem leaks */
longjmp (top_level, DISCARD);
}
/* Cut off ending `)' */
t[last] = '\0';
number = (int)evalexp (t);
free (temp);
goto add_number;
}
goto handle_command_substitution;
}
/* Do straight arithmetic substitution. */
case '[':
/* We have to extract the contents of this
arithmetic substitution. */
{
char *extract_arithmetic_subst (), *t;
int old_index = ++sindex;
WORD_LIST *l;
extern long evalexp ();
extern char *this_command_name;
temp = extract_arithmetic_subst (string, &old_index);
sindex = old_index;
/* Do initial variable expansion. */
l = expand_string (temp, 1);
t = string_list (l);
dispose_words (l);
/* No error messages. */
this_command_name = (char *)NULL;
number = (int)evalexp (t);
free (t);
goto add_number;
}
default:
{
/* Find the variable in VARIABLE_LIST. */
int old_index = sindex;
char *name;
SHELL_VAR *var;
temp = (char *)NULL;
for (;
(c = string[sindex]) &&
(isletter (c) || digit (c) || c == '_');
sindex++);
name = (char *)substring (string, old_index, sindex);
/* If this isn't a variable name, then just output the `$'. */
if (!name || !*name)
{
free (name);
temp = savestring ("$");
if (expanded_something)
*expanded_something = 0;
goto add_string;
}
/* If the variable exists, return its value cell. */
var = find_variable (name);
if (var && !invisible_p (var) && value_cell (var))
{
temp = savestring (value_cell (var));
free (name);
goto add_string;
}
else
temp = (char *)NULL;
if (unbound_vars_is_error)
report_error ("%s: unbound variable", name);
else
goto add_string;
free (name);
longjmp (top_level, DISCARD);
}
}
break; /* End case '$': */
case '`': /* Backquoted command substitution. */
{
sindex++;
if (expanded_something)
*expanded_something = 1;
t_index = sindex;
temp = string_extract (string, &t_index, "`");
sindex = t_index;
de_backslash (temp);
handle_command_substitution:
command_subst_result = command_substitute (temp, quoted);
if (temp)
free (temp);
temp = command_subst_result;
if (string[sindex])
sindex++;
goto add_string;
}
case '\\':
if (string[sindex + 1] == '\n')
{
sindex += 2;
continue;
}
else
{
char *slashify_chars = "";
c = string[++sindex];
if (quoted == Q_HERE_DOCUMENT)
slashify_chars = slashify_in_here_document;
else if (quoted == Q_DOUBLE_QUOTES)
slashify_chars = slashify_in_quotes;
if (preserve_backslashes || (quoted && !member (c, slashify_chars)))
{
temp = (char *)xmalloc (3);
temp[0] = '\\'; temp[1] = c; temp[2] = '\0';
if (c)
sindex++;
goto add_string;
}
else
{
/* This character is quoted, so add it in quoted mode. */
c = QUOTE_CHAR (c);
goto add_character;
}
}
case '"':
if (quoted)
goto add_character;
sindex++;
{
WORD_LIST *tresult = (WORD_LIST *)NULL;
t_index = sindex;
temp = string_extract_double_quoted (string, &t_index);
sindex = t_index;
if (string[sindex])
sindex++;
if (temp && *temp)
{
int dollar_at_flag;
int quoting_flags = Q_DOUBLE_QUOTES;
WORD_DESC *temp_word = make_word (temp);
free (temp);
if (preserve_backslashes)
quoting_flags |= Q_KEEP_BACKSLASH;
tresult = expand_word_internal (temp_word, quoting_flags,
&dollar_at_flag, (int *)NULL);
dispose_word (temp_word);
if (!tresult && dollar_at_flag)
break;
/* If we get "$@", we know we have expanded something, so we
need to remember it for the final split on $IFS. This is
a special case; it's the only case where a quoted string
can expand into more than one word. It's going to come back
from the above call to expand_word_internal as a list with
a single word, in which all characters are quoted and
separated by blanks. What we want to do is to turn it back
into a list for the next piece of code. */
dequote_list (tresult);
if (dollar_at_flag)
quoted_dollar_at++;
if (expanded_something)
*expanded_something = 1;
}
else
{
/* What we have is "". This is a minor optimization. */
free (temp);
tresult = (WORD_LIST *)NULL;
}
/* The code above *might* return a list (consider the case of "$@",
where it returns "$1", "$2", etc.). We can't throw away the rest
of the list, and we have to make sure each word gets added as
quoted. We test on tresult->next: if it is non-NULL, we quote
the whole list, save it to a string with string_list, and add that
string. We don't need to quote the results of this (and it would be
wrong, since that would quote the separators as well), so we go
directly to add_string. */
if (tresult)
{
if (tresult->next)
{
quote_list (tresult);
temp = string_list (tresult);
dispose_words (tresult);
goto add_string;
}
else
{
temp = savestring (tresult->word->word);
dispose_words (tresult);
}
}
else
temp = (char *)NULL;
add_quoted_string:
if (temp)
{
char *t = temp;
temp = quote_string (temp);
free (t);
}
else
{
/* Add NULL arg. */
temp = savestring (" ");
temp[0] = (unsigned char)QUOTE_CHAR ('\0');
}
goto add_string;
}
/* break; */
case '\'':
{
if (!quoted)
{
sindex++;
t_index = sindex;
temp = string_extract_verbatim (string, &t_index, "'");
sindex = t_index;
if (string[sindex])
sindex++;
if (!*temp)
{
free (temp);
temp = (char *)NULL;
}
goto add_quoted_string;
}
else
goto add_character;
break;
}
default:
/* This is the fix for " $@ " */
if (quoted)
c = QUOTE_CHAR (c);
add_character:
while (istring_index + 1 >= istring_size)
istring = (char *)
xrealloc (istring, istring_size += DEFAULT_ARRAY_SIZE);
istring[istring_index++] = c;
istring[istring_index] = '\0';
/* Next character. */
sindex++;
}
}
finished_with_string:
final_exit:
/* OK, we're ready to return. If we have a quoted string, and
quoted_dollar_at is not set, we do no splitting at all; otherwise
we split on ' '. The routines that call this will handle what to
do if nothing has been expanded. */
if (istring)
{
WORD_LIST *temp_list;
if (quoted_dollar_at)
temp_list = list_string (istring, " ", quoted);
else if (*istring)
{
temp_list = make_word_list (make_word (istring), (WORD_LIST *)NULL);
temp_list->word->quoted = quoted;
}
else
temp_list = (WORD_LIST *)NULL;
free (istring);
result = (WORD_LIST *)list_append (reverse_list (result), temp_list);
}
else
result = (WORD_LIST *)NULL;
return (result);
}
/* **************************************************************** */
/* */
/* Functions for Quote Removal */
/* */
/* **************************************************************** */
/* Perform quote removal on STRING. If QUOTED > 0, assume we are obeying the
backslash quoting rules for within double quotes. */
char *
string_quote_removal (string, quoted)
char *string;
int quoted;
{
char *r, *result_string, *temp, *temp1;
int sindex, tindex, c;
/* The result can be no longer than the original string. */
r = result_string = xmalloc (strlen (string) + 1);
sindex = 0;
for (;;)
{
c = string[sindex];
if (c == '\0')
break;
switch (c)
{
case '\\':
c = string[++sindex];
if (quoted && !member (c, slashify_in_quotes))
{
*r++ = '\\';
*r++ = c;
}
else
*r++ = c;
sindex++;
break;
case '"':
tindex = ++sindex;
temp = string_extract_double_quoted (string, &tindex);
sindex = tindex;
if (string[sindex])
sindex++;
temp1 = string_quote_removal (temp, 1); /* XXX is this needed? */
if (temp)
free (temp);
if (temp1)
{
strcpy (r, temp1);
r += strlen (r);
free (temp1);
}
break;
case '\'':
if (quoted)
{
*r++ = c;
sindex++;
}
else
{
tindex = ++sindex;
temp = string_extract_verbatim (string, &tindex, "'");
sindex = tindex;
if (string[sindex])
sindex++;
if (temp)
{
strcpy (r, temp);
r += strlen (r);
free (temp);
}
}
break;
default:
*r++ = c;
sindex++;
break;
}
}
*r = '\0';
return (result_string);
}
/* Perform quote removal on word WORD. This allocates and returns a new
WORD_DESC *. */
WORD_DESC *
word_quote_removal (word, quoted)
WORD_DESC *word;
int quoted;
{
WORD_DESC *w;
char *t;
t = string_quote_removal (word->word, quoted);
w = make_word (t);
return (w);
}
/* Perform quote removal on all words in LIST. If QUOTED is non-zero,
the members of the list are treated as if they are surrounded by
double quotes. Return a new list, or NULL if LIST is NULL. */
WORD_LIST *
word_list_quote_removal (list, quoted)
WORD_LIST *list;
int quoted;
{
WORD_LIST *result = (WORD_LIST *)NULL, *t, *tresult;
t = list;
while (t)
{
tresult = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
tresult->word = word_quote_removal (t->word, quoted);
tresult->next = (WORD_LIST *)NULL;
result = (WORD_LIST *) list_append (result, tresult);
t = t->next;
}
return (result);
}
#if defined (NOTDEF)
/* Currently unused. */
/* Return 1 if CHARACTER appears in an unquoted portion of
STRING. Return 0 otherwise. */
static int
unquoted_member (character, string)
int character;
char *string;
{
int sindex, tindex, c;
char *temp;
sindex = 0;
while (c = string[sindex])
{
if (c == character)
return (1);
switch (c)
{
case '\\':
sindex++;
if (string[sindex])
sindex++;
break;
case '"':
case '\'':
tindex = ++sindex;
if (c == '"')
temp = string_extract_double_quoted (string, &tindex);
else
temp = string_extract_verbatim (string, &tindex, "'");
sindex = tindex;
if (string[sindex])
sindex++;
if (temp)
free (temp);
break;
default:
sindex++;
break;
}
}
return (0);
}
#endif /* NOTDEF */
/* Return 1 if SUBSTR appears in an unquoted portion of STRING. */
static int
unquoted_substring (substr, string)
char *substr, *string;
{
int sindex, tindex, c, sublen;
char *temp;
if (!substr || !*substr)
return (0);
sublen = strlen (substr);
sindex = 0;
while (c = string[sindex])
{
if (c == *substr &&
strncmp (string + sindex, substr, sublen) == 0)
return (1);
switch (c)
{
case '\\':
sindex++;
if (string[sindex])
sindex++;
break;
case '"':
case '\'':
tindex = ++sindex;
if (c == '"')
temp = string_extract_double_quoted (string, &tindex);
else
temp = string_extract_verbatim (string, &tindex, "'");
sindex = tindex;
if (string[sindex])
sindex++;
if (temp)
free (temp);
break;
default:
sindex++;
break;
}
}
return (0);
}
/*******************************************
* *
* Functions to perform word splitting *
* *
*******************************************/
/* This splits a single word into a WORD LIST on $IFS, but only if the word
is not quoted. list_string () performs quote removal for us, even if we
don't do any splitting. */
WORD_LIST *
word_split (w)
WORD_DESC *w;
{
WORD_LIST *result;
if (w)
{
SHELL_VAR *ifs = find_variable ("IFS");
char *ifs_chars;
/* If IFS is unset, it defaults to " \t\n". */
if (ifs)
ifs_chars = value_cell (ifs);
else
ifs_chars = " \t\n";
if (w->quoted || !ifs_chars)
ifs_chars = "";
result = list_string (w->word, ifs_chars, w->quoted);
}
else
result = (WORD_LIST *)NULL;
return (result);
}
/* Perform word splitting on LIST and return the RESULT. It is possible
to return (WORD_LIST *)NULL. */
WORD_LIST *
word_list_split (list)
WORD_LIST *list;
{
WORD_LIST *result = (WORD_LIST *)NULL, *t, *tresult;
t = list;
while (t)
{
tresult = word_split (t->word);
result = (WORD_LIST *) list_append (result, tresult);
t = t->next;
}
return (result);
}
/**************************************************
* *
* Functions to expand an entire WORD_LIST *
* *
**************************************************/
/* Do all of the assignments in LIST up to a word which isn't an
assignment. */
WORD_LIST *
get_rid_of_variable_assignments (list)
WORD_LIST *list;
{
WORD_LIST *orig = list;
while (list)
if (!list->word->assignment)
{
WORD_LIST *new_list = copy_word_list (list);
dispose_words (orig);
return (new_list);
}
else
{
do_assignment (list->word->word);
list = list->next;
}
dispose_words (orig);
return ((WORD_LIST *)NULL);
}
/* Check and handle the case where there are some variable assignments
in LIST which go into the environment for this command. */
WORD_LIST *
get_rid_of_environment_assignments (list)
WORD_LIST *list;
{
register WORD_LIST *tlist = list;
register WORD_LIST *new_list;
while (tlist)
{
if (!tlist->word->assignment) goto make_assignments;
tlist = tlist->next;
}
/* Since all of the assignments are variable assignments. */
return (list);
make_assignments:
tlist = list;
while (tlist)
{
if (tlist->word->assignment)
assign_in_env (tlist->word->word);
else
{
if (!place_keywords_in_env)
{
new_list = copy_word_list (tlist);
dispose_words (list);
return (new_list);
}
}
tlist = tlist->next;
}
/* We got all of the keywords assigned. Now return the remainder
of the words. */
{
register WORD_LIST *new_list = (WORD_LIST *)NULL;
tlist = list;
/* Skip the ones at the start. */
while (tlist && tlist->word->assignment)
tlist = tlist->next;
/* If we placed all the keywords in the list into the environment,
then remove them from the output list. */
if (place_keywords_in_env)
{
while (tlist)
{
if (!tlist->word->assignment)
new_list = make_word_list (copy_word (tlist->word), new_list);
tlist = tlist->next;
}
new_list = (WORD_LIST *)reverse_list (new_list);
}
else
{
/* Just copy the list. */
new_list = copy_word_list (tlist);
}
dispose_words (list);
return (new_list);
}
}
/* Take the list of words in LIST and do the various substitutions. Return
a new list of words which is the expanded list, and without things like
variable assignments. */
static WORD_LIST *expand_words_internal ();
WORD_LIST *
expand_words (list)
WORD_LIST *list;
{
return (expand_words_internal (list, 1));
}
/* Same as expand_words (), but doesn't hack variable or environment
variables. */
WORD_LIST *
expand_words_no_vars (list)
WORD_LIST *list;
{
return (expand_words_internal (list, 0));
}
/* Non-zero means to allow unmatched globbed filenames to expand to
a null file. */
static int allow_null_glob_expansion = 0;
/* The workhorse for expand_words () and expand_words_no_var ().
First arg is LIST, a WORD_LIST of words.
Second arg DO_VARS is non-zero if you want to do environment and
variable assignments, else zero.
This does all of the subsitutions: brace expansion, tilde expansion,
parameter expansion, command substitution, arithmetic expansion,
word splitting, and pathname expansion. */
static WORD_LIST *
expand_words_internal (list, do_vars)
WORD_LIST *list;
int do_vars;
{
register WORD_LIST *tlist, *new_list = (WORD_LIST *)NULL;
WORD_LIST *orig_list;
extern int no_brace_expansion;
tlist = (WORD_LIST *)copy_word_list (list);
if (do_vars)
{
/* Handle the case where the arguments are assignments for
the environment of this command. */
tlist = get_rid_of_environment_assignments (tlist);
/* Handle the case where the arguments are all variable assignments. */
tlist = get_rid_of_variable_assignments (tlist);
}
/* Begin expanding the words that remain. The expansions take place on
things that aren't really variable assignments. */
if (!tlist)
return ((WORD_LIST *)NULL);
/* Do brace expansion on this word if there are any brace characters
in the string. */
if (!no_brace_expansion)
{
extern char **brace_expand ();
register char **expansions;
WORD_LIST *braces = (WORD_LIST *)NULL;
int eindex;
orig_list = tlist;
while (tlist)
{
/* Only do brace expansion if the word has a brace character. If
not, just copy the word list element, add it to braces, and
continue. In the common case, at least when running shell
scripts, this will degenerate to a bunch of calls to `index',
and then what is basically the body of copy_word_list. */
if (index (tlist->word->word, '{') != NULL)
{
expansions = brace_expand (tlist->word->word);
for (eindex = 0; expansions[eindex]; eindex++)
{
braces = make_word_list (make_word (expansions[eindex]),
braces);
free (expansions[eindex]);
}
free (expansions);
}
else
{
WORD_LIST *new = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
new->word = copy_word (tlist->word);
new->next = braces;
braces = new;
}
tlist = tlist->next;
}
dispose_words (orig_list);
tlist = (WORD_LIST *)reverse_list (braces);
}
orig_list = tlist;
/* We do tilde expansion all the time. This is what 1003.2 says. */
while (tlist)
{
register char *current_word;
WORD_LIST *expanded, *t;
int expanded_something = 0;
current_word = tlist->word->word;
if (current_word[0] == '~' ||
(index (current_word, '~') &&
unquoted_substring ("=~", current_word)))
{
char *tilde_expand (), *tt;
tt = tlist->word->word;
tlist->word->word = tilde_expand (tt);
free (tt);
}
expanded = expand_word_internal
(tlist->word, 0, (int *)NULL, &expanded_something);
if (expanded_something)
t = word_list_split (expanded);
else
{
/* If no parameter expansion, command substitution, or arithmetic
substitution took place, then do not do word splitting. We
still have to remove quoted null characters from the result. */
word_list_remove_quoted_nulls (expanded);
t = copy_word_list (expanded);
}
new_list =
(WORD_LIST *)list_append (reverse_list (t), new_list);
dispose_words (expanded);
tlist = tlist->next;
}
new_list = (WORD_LIST *)reverse_list (new_list);
dispose_words (orig_list);
/* Okay, we're almost done. Now let's just do some filename
globbing. */
{
char **shell_glob_filename (), **temp_list = (char **)NULL;
register int list_index;
WORD_LIST *glob_list;
orig_list = (WORD_LIST *)NULL;
tlist = new_list;
if (!disallow_filename_globbing)
{
while (tlist)
{
/* If the word isn't quoted, then glob it. */
if (!tlist->word->quoted && glob_pattern_p (tlist->word->word, 0))
{
temp_list = shell_glob_filename (tlist->word->word);
/* Fix the hi-bits. (This is how we quoted
special characters.) */
{
register char *t = dequote_string (tlist->word->word);
free (tlist->word->word);
tlist->word->word = t;
}
/* Handle error cases.
I don't think we should report errors like "No such file
or directory". However, I would like to report errors
like "Read failed". */
#if defined (USE_GLOB_LIBRARY)
if (!temp_list)
#else
if (temp_list == (char **)-1)
#endif /* !USE_GLOB_LIBRARY */
{
/* file_error (tlist->word->word); */
/* A small memory leak, I think */
temp_list = (char **) xmalloc (sizeof (char *));
temp_list[0] = '\0';
}
#if !defined (USE_GLOB_LIBRARY)
if (!temp_list)
abort ();
#endif /* !USE_GLOB_LIBRARY */
/* Make the array into a word list. */
glob_list = (WORD_LIST *)NULL;
for (list_index = 0; temp_list[list_index]; list_index++)
glob_list =
make_word_list (make_word (temp_list[list_index]), glob_list);
if (glob_list)
orig_list = (WORD_LIST *)list_append (glob_list, orig_list);
else
if (!allow_null_glob_expansion)
orig_list =
make_word_list (copy_word (tlist->word), orig_list);
}
else
{
/* Fix the hi-bits. (This is how we quoted special
characters.) */
register char *t = dequote_string (tlist->word->word);
free (tlist->word->word);
tlist->word->word = t;
orig_list = make_word_list (copy_word (tlist->word), orig_list);
}
free_array (temp_list);
temp_list = (char **)NULL;
tlist = tlist->next;
}
dispose_words (new_list);
new_list = orig_list;
}
else
{
/* Fix the hi-bits. (This is how we quoted special characters.) */
register WORD_LIST *wl = new_list;
register char *wp;
while (wl)
{
wp = dequote_string (wl->word->word);
free (wl->word->word);
wl->word->word = wp;
wl = wl->next;
}
return (new_list);
}
}
return (WORD_LIST *)(reverse_list (new_list));
}
/* Call the glob library to do globbing on PATHNAME.
PATHNAME can contain characters with the hi bit set; this indicates
that the character is to be quoted. We quote it here. */
char **
shell_glob_filename (pathname)
char *pathname;
#if defined (USE_GLOB_LIBRARY)
{
extern int glob_dot_filenames;
register int i, j;
char *temp, **return_value;
glob_t filenames;
int glob_flags;
temp = (char *)alloca (1 + (2 * strlen (pathname)));
for (i = j = 0; pathname[i]; i++, j++)
{
if (QUOTED_CHAR (pathname[i]))
temp[j++] = '\\';
temp[j] = DEQUOTE_CHAR (pathname[i]);
}
temp[j] = '\0';
filenames.gl_offs = 0;
glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
glob_flags |= (GLOB_ERR | GLOB_DOOFFS);
i = glob (temp, glob_flags, (Function *)NULL, &filenames);
if (i == GLOB_NOSPACE || i == GLOB_ABEND)
return ((char **)NULL);
if (i == GLOB_NOMATCH)
filenames.gl_pathv[0] = (char *)NULL;
return (filenames.gl_pathv);
}
#else /* !USE_GLOB_LIBRARY */
{
extern char **glob_filename ();
extern int glob_dot_filenames, noglob_dot_filenames;
register int i, j;
char *temp, **results;
temp = (char *)alloca (1 + (2 * strlen (pathname)));
noglob_dot_filenames = !glob_dot_filenames;
for (i = j = 0; pathname[i]; i++, j++)
{
if (QUOTED_CHAR (pathname[i]))
{
temp[j++] = '\\';
temp[j] = DEQUOTE_CHAR (pathname[i]);
}
else
temp[j] = pathname[i];
}
temp[j] = '\0';
results = glob_filename (temp);
if (results && results != (char **)-1)
sort_char_array (results);
return (results);
}
#endif /* !USE_GLOB_LIBRARY */
/*************************************************
* *
* Functions to manage special variables *
* *
*************************************************/
/* An alist of name.function for each special variable. Most of the
functions don't do much, and in fact, this would be faster with a
switch statement, but by the end of this file, I am sick of switch
statements. */
/* The functions that get called. */
int
sv_path (), sv_mail (), sv_terminal (), sv_histsize (), sv_histfilesize (),
sv_uids (), sv_ignoreeof (), sv_glob_dot_filenames (), sv_histchars (),
sv_nolinks (), sv_hostname_completion_file (), sv_history_control (),
sv_noclobber (), sv_allow_null_glob_expansion (),
sv_command_oriented_history ();
#if defined (GETOPTS_BUILTIN)
int sv_optind (), sv_opterr ();
#endif /* GETOPTS_BUILTIN */
#if defined (JOB_CONTROL)
extern int sv_notify ();
#endif
struct name_and_function {
char *name;
Function *function;
} special_vars[] = {
{ "PATH", sv_path },
{ "MAIL", sv_mail },
{ "MAILPATH", sv_mail },
{ "MAILCHECK", sv_mail },
{ "TERMCAP", sv_terminal },
{ "TERM", sv_terminal },
{ "HISTSIZE", sv_histsize },
{ "HISTFILESIZE", sv_histfilesize },
{ "EUID", sv_uids},
{ "UID", sv_uids},
{ "IGNOREEOF", sv_ignoreeof },
{ "ignoreeof", sv_ignoreeof },
#if defined (GETOPTS_BUILTIN)
{ "OPTIND", sv_optind },
{ "OPTERR", sv_opterr },
#endif /* GETOPTS_BUILTIN */
#if defined (JOB_CONTROL)
{ "notify", sv_notify },
#endif /* JOB_CONTROL */
{ "glob_dot_filenames", sv_glob_dot_filenames },
{ "allow_null_glob_expansion", sv_allow_null_glob_expansion },
{ "command_oriented_history", sv_command_oriented_history },
{ "histchars", sv_histchars },
{ "hostname_completion_file", sv_hostname_completion_file },
{ "history_control", sv_history_control },
{ "noclobber", sv_noclobber },
{ "nolinks", sv_nolinks },
{ (char *)0x00, (Function *)0x00 }
};
/* The variable in NAME has just had its state changed. Check to see if it
is one of the special ones where something special happens. */
stupidly_hack_special_variables (name)
char *name;
{
int i = 0;
while (special_vars[i].name)
{
if (STREQ (special_vars[i].name, name))
{
(*(special_vars[i].function)) (name);
return;
}
i++;
}
}
/* Set/unset noclobber. */
sv_noclobber (name)
char *name;
{
extern int noclobber;
if (find_variable (name))
noclobber = 1;
else
noclobber = 0;
}
/* What to do just after the PATH variable has changed. */
sv_path (name)
char *name;
{
/* hash -r */
WORD_LIST *args;
args = make_word_list (make_word ("-r"), NULL);
hash_builtin (args);
dispose_words (args);
}
/* What to do just after one of the MAILxxxx variables has changed. NAME
is the name of the variable. */
sv_mail (name)
char *name;
{
/* If the time interval for checking the files has changed, then
reset the mail timer. Otherwise, one of the pathname vars
to the users mailbox has changed, so rebuild the array of
filenames. */
if (strcmp (name, "MAILCHECK") == 0)
reset_mail_timer ();
else
{
if ((strcmp (name, "MAIL") == 0) || (strcmp (name, "MAILPATH") == 0))
{
free_mail_files ();
remember_mail_dates ();
}
}
}
/* What to do just after one of the TERMxxx variables has changed.
If we are an interactive shell, then try to reset the terminal
information in readline. */
sv_terminal (name)
char *name;
{
extern int interactive;
if (interactive)
rl_reset_terminal (get_string_value ("TERM"));
}
/* What to do after the HISTSIZE variable changes.
If there is a value for this variable (and it is numeric), then stifle
the history. Otherwise, if there is NO value for this variable,
unstifle the history. */
sv_histsize (name)
char *name;
{
char *temp = get_string_value (name);
if (temp)
{
int num;
if (sscanf (temp, "%d", &num) == 1)
{
extern int history_lines_this_session;
stifle_history (num);
if (history_lines_this_session > where_history ())
history_lines_this_session = where_history ();
}
}
else
unstifle_history ();
}
/* What to do if the HISTFILESIZE variable changes. */
sv_histfilesize (name)
char *name;
{
char *temp = get_string_value (name);
if (temp)
{
extern int history_lines_in_file;
int num;
if (sscanf (temp, "%d", &num) == 1)
{
history_truncate_file (get_string_value ("HISTFILE"), num);
if (num <= history_lines_in_file)
history_lines_in_file = num;
}
}
}
/* A nit for picking at history saving.
Value of 0 means save all lines parsed by the shell on the history.
Value of 1 means save all lines that do not start with a space.
Value of 2 means save all lines that do not match the last line saved. */
int history_control = 0;
/* What to do after the HISTORY_CONTROL variable changes. */
sv_history_control (name)
char *name;
{
char *temp = get_string_value (name);
history_control = 0;
if (temp && *temp)
{
if (strcmp (temp, "ignorespace") == 0)
history_control = 1;
else if (strcmp (temp, "ignoredups") == 0)
history_control = 2;
}
}
/* By default, every line is saved in the history individually. I.e.,
if the user enters:
bash$ for i in a b c
> do
> echo $i
> done
Each line will be individually saved in the history.
bash$ history
10 for i in a b c
11 do
12 echo $i
13 done
14 history
If the variable command_oriented_history is set, multiple lines
which form one command will be saved as one history entry.
bash$ for i in a b c
> do
> echo $i
> done
bash$ history
10 for i in a b c
do
echo $i
done
11 history
The user can then recall the whole command all at once instead
of just being able to recall one line at a time.
*/
int command_oriented_history = 0;
/* What to do after the COMMAND_ORIENTED_HISTORY variable changes. */
sv_command_oriented_history (name)
char *name;
{
if (find_variable (name) != (SHELL_VAR *)NULL)
command_oriented_history = 1;
else
command_oriented_history = 0;
}
/* If the variable exists, then the value of it can be the number
of times we actually ignore the EOF. The default is small,
(smaller than csh, anyway). */
sv_ignoreeof (name)
char *name;
{
extern int eof_encountered, eof_encountered_limit;
char *temp = get_string_value (name);
int new_limit;
eof_encountered = 0;
if (temp && (sscanf (temp, "%d", &new_limit) == 1))
eof_encountered_limit = new_limit;
else
eof_encountered_limit = 10; /* csh uses 26. */
}
/* Control whether * matches .files in globbing. Yechh. */
int glob_dot_filenames = 0;
sv_glob_dot_filenames (name)
char *name;
{
if (find_variable (name) != (SHELL_VAR *)NULL)
glob_dot_filenames = 1;
else
glob_dot_filenames = 0;
}
/* Setting/unsetting of the history expansion character. */
char old_history_expansion_char = '!';
char old_history_comment_char = '#';
char old_history_subst_char = '^';
sv_histchars (name)
char *name;
{
extern char history_expansion_char;
extern char history_comment_char;
extern char history_subst_char;
char *temp = get_string_value (name);
if (temp)
{
old_history_expansion_char = history_expansion_char;
history_expansion_char = *temp;
if (temp[1])
{
old_history_subst_char = history_subst_char;
history_subst_char = temp[1];
if (temp[2])
{
old_history_comment_char = history_comment_char;
history_comment_char = temp[2];
}
}
}
else
{
history_expansion_char = '!';
history_subst_char = '^';
history_comment_char = '#';
}
}
#if defined (JOB_CONTROL)
/* Job notification feature desired? */
sv_notify (name)
char *name;
{
extern int asynchronous_notification;
if (get_string_value (name))
asynchronous_notification = 1;
else
asynchronous_notification = 0;
}
#endif /* JOB_CONTROL */
/* If the variable `nolinks' exists, it specifies that symbolic links are
not to be followed in `cd' commands. */
sv_nolinks (name)
char *name;
{
extern int follow_symbolic_links;
follow_symbolic_links = !find_variable (name);
}
/* Don't let users hack the user id variables. */
sv_uids (name)
char *name;
{
int uid = getuid ();
int euid = geteuid ();
char buff[10];
register SHELL_VAR *v;
sprintf (buff, "%d", uid);
v = find_variable ("UID");
if (v)
v->attributes &= ~att_readonly;
v = bind_variable ("UID", buff);
v->attributes |= (att_readonly | att_integer);
sprintf (buff, "%d", euid);
v = find_variable ("EUID");
if (v)
v->attributes &= ~att_readonly;
v = bind_variable ("EUID", buff);
v->attributes |= (att_readonly | att_integer);
}
sv_hostname_completion_file (name)
char *name;
{
extern int hostname_list_initialized;
hostname_list_initialized = 0;
}
sv_allow_null_glob_expansion (name)
char *name;
{
allow_null_glob_expansion = (int)find_variable (name);
}
#if defined (GETOPTS_BUILTIN)
sv_optind (name)
char *name;
{
char *tt = get_string_value ("OPTIND");
int s = 0;
if (tt && *tt)
{
s = atoi (tt);
/* According to POSIX, setting OPTIND=1 resets the internal state
of getopt (). */
if (s < 0 || s == 1)
s = 0;
}
getopts_reset (s);
}
int
sv_opterr (name)
char *name;
{
char *tt = get_string_value ("OPTERR");
int s = 1;
extern int opterr;
if (tt)
s = atoi (tt);
opterr = s;
return (0);
}
#endif /* GETOPTS_BUILTIN */