Add the '-S' and '-P' options. The '-S' option can be used to split

apart a string, and supports some text substitutions.  This can be
used to provide all the flexibility (and more!) that was lost by recent
changes to how the kernel parses #!-lines in shell scripts.

The '-P' option provides a way to specify an alternate set of directories
to use when searching for the 'utility' program to run.  This way you can
be sure what directories are used for that search, without changing the
value of PATH that the user has set.  Note that on FreeBSD 6.0, this
option is worthless unless the '-S' option is also used.

Approved by:	re (blanket `env')
This commit is contained in:
Garance A Drosehn 2005-06-20 03:43:25 +00:00
parent 6dcd6cac03
commit 8fbe7ebf7d
5 changed files with 831 additions and 34 deletions

View File

@ -2,5 +2,7 @@
# $FreeBSD$
PROG= env
SRCS= env.c envopts.c
WARNS?= 6
.include <bsd.prog.mk>

374
usr.bin/env/env.1 vendored
View File

@ -35,7 +35,7 @@
.\" From FreeBSD: src/usr.bin/printenv/printenv.1,v 1.17 2002/11/26 17:33:35 ru Exp
.\" $FreeBSD$
.\"
.Dd May 12, 2003
.Dd June 20, 2005
.Dt ENV 1
.Os
.Sh NAME
@ -43,7 +43,9 @@
.Nd set and print environment
.Sh SYNOPSIS
.Nm
.Op Fl i
.Op Fl iv
.Op Fl P Ar altpath
.Op Fl S Ar string
.Op Ar name Ns = Ns Ar value ...
.Op Ar utility Op Ar argument ...
.Sh DESCRIPTION
@ -53,52 +55,275 @@ utility executes another
.Ar utility
after modifying the environment as
specified on the command line.
The option
Each
.Ar name Ns = Ns Ar value
specifies
an environment variable,
option specifies the setting of an environment variable,
.Ar name ,
with a value of
.Ar value .
All such environment variables are set before the
.Ar utility
is executed.
.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl i
Execute the
.Ar utility
with only those environment variables specified.
with only those environment variables specified by
.Ar name Ns = Ns Ar value
options.
The environment inherited
by
.Nm
is ignored completely.
.\" -P
.It Fl P Ar altpath
Search the set of directories as specified by
.Ar altpath
to locate the specified
.Ar utility
program, instead of using the value of the PATH environment variable.
.\" -S
.It Fl S Ar string
Split apart the given
.Ar string
into multiple strings, and process each of the resulting strings
as separate arguments to the
.Nm
utility.
The
.Fl S
option recognizes some special character escape sequences and
also supports environment-variable substitution, as described
below.
.\" -v
.It Fl v
Print verbose information for each step of processing done by the
.Nm
utility.
Additional information will be printed if
.Fl v
is specified multiple times.
.El
.Pp
The above options are only recognized when they are specified
before any
.Ar name Ns = Ns Ar value
options.
.Pp
If no
.Ar utility
is specified,
.Nm
prints out the names and values
of the variables in the environment, with one name/value pair per line.
.\"
.Ss Details of -S (split-string) processing
.Pp
The processing of the
.Fl S
option will split the given
.Ar string
into separate arguments based on any space or <tab> characters found in the
.Ar string .
Each of those new arguments will then be treated as if it had been
specified as a separate argument on the original
.Nm
command.
.Pp
Spaces and tabs may be embedded in one of those new arguments by using
single (``\ '\ '') or double (``"'') quotes, or backslashes (``\e'').
Single quotes will escape all non-single quote characters, up to
the matching single quote.
Double quotes will escape all non-double quote characters, up to
the matching double quote.
It is an error if the end of the
.Ar string
is reached before the matching quote character.
.Pp
If
.Fl S
would create a new argument that starts with the
.Ql #
character, then that argument and the remainder of the
.Ar string
will be ignored.
The
.Ql \e#
sequence can be used when you want a new argument to start
with a
.Ql #
character, without causing the remainder of the
.Ar string
to be skipped.
.Pp
While processing the
.Ar string
value,
.Fl S
processing will treat certain character combinations as escape
sequences which represent some action to take.
The character escape sequences are in backslash notation.
The characters and their meanings are as follows:
.Pp
.Bl -tag -width Ds -offset indent -compact
.It Cm \ec
Ignore the remaining characters in the
.Ar string .
This must not appear inside a double-quoted string.
.It Cm \ef
Replace with a <form-feed> character.
.It Cm \en
Replace with a <new-line> character.
.It Cm \er
Replace with a <carriage return> character.
.It Cm \et
Replace with a <tab> character.
.It Cm \ev
Replace with a <vertical tab> character.
.It Cm \e#
Replace with a
.Ql #
character.
This would be useful when you need a
.Ql #
as the first character in one of the arguments created
by splitting apart the given
.Ar string .
.It Cm \e$
Replace with a
.Ql $
character.
.It Cm \e_
If this is found inside of a double-quoted string, then replace it
with a single blank.
If this is found outside of a quoted string, then treat this as the
separator character between new arguments in the original
.Ar string .
.It Cm \e"
Replace with a <double quote> character.
.It Cm \e\'
Replace with a <single quote> character.
.It Cm \e\e
Replace with a backslash character.
.El
.Pp
The sequences for <single-quote> and backslash are the only sequences
which are recognized inside of a single-quoted string.
The other sequences have no special meaning inside a single-quoted
string.
All escape sequences are recognized inside of a double-quoted string.
It is an error if a single
.Ql \e
character is followed by a character other than the ones listed above.
.Pp
The processing of
.Fl S
also supports substitution of values from environment variables.
To do this, the name of the environment variable must be inside of
.Ql ${} ,
such as: ${SOMEVAR}.
The common shell syntax of $SOMEVAR is not supported.
All values substituted will be the values of the environment variables
as they were when the
.Nm
utility was originally invoked.
Those values will not be checked for any of the escape sequences as
described above.
And any settings of
.Ar name Ns = Ns Ar value
will not effect the values used for substitution in
.Fl S
processing.
.Pp
Also,
.Fl S
processing can not reference the value of the special parameters
which are defined by most shells.
For instance,
.Fl S
can not recognize special parameters such as:
.Ql $* ,
.Ql $@ ,
.Ql $# ,
.Ql $?
or
.Ql $$
if they appear inside the given
.Ar string .
.\"
.Ss Use in shell-scripts
The
.Nm
utility is often used as the
.Ar interpreter
on the first line of interpreted scripts, as
described in
.Xr execve 2 .
.Pp
Note that the way the kernel parses the
.Ql #!
(first line) of an interpreted script has changed as of
.Fx 6.0 .
Prior to that, the
.Fx
kernel would split that first line into separate arguments based
on any whitespace (space or <tab> characters) found in the line.
So, if a script named
.Pa /usr/local/bin/someport
had a first line of:
.Pp
.D1 Li "#!/usr/local/bin/php -n -q -dsafe_mode=0"
.Pp
then the
.Pa /usr/local/bin/php
program would have been started with the arguments of:
.Pp
.D1 Li "arg[0] = '/usr/local/bin/php'"
.D1 Li "arg[1] = '-n'"
.D1 Li "arg[2] = '-q'"
.D1 Li "arg[3] = '-dsafe_mode=0'"
.D1 Li "arg[4] = '/usr/local/bin/someport'"
.Pp
plus any arguments the user specifed when executing
.Pa someport .
However, this processing of multiple options on the
.Ql #!
line is not the way any other operating system parses the
first line of an interpreted script.
So after a change which was made for
.Fx 6.0
release, that script will result in
.Pa /usr/local/bin/php
being started with the arguments of:
.Pp
.D1 Li "arg[0] = '/usr/local/bin/php'"
.D1 Li "arg[1] = '-n -q -dsafe_mode=0'"
.D1 Li "arg[2] = '/usr/local/bin/someport'"
.Pp
plus any arguments the user specifed.
This caused a significant change in the behavior of a few scripts.
In the case of above script, to have it behave the same way under
.Fx 6.0
as it did under earlier releases, the first line should be
changed to:
.Pp
.D1 Li "#!/usr/bin/env -S /usr/local/bin/php -n -q -dsafe_mode=0"
.Pp
The
.Nm
utility is sometimes useful with the
.Ql #!
construct (see
.Xr execve 2 ) .
The only difference between
.Dq Li #!/usr/local/bin/foo
and
.Dq Li "#!/usr/bin/env /usr/local/bin/foo"
is that the latter works even if
.Pa /usr/local/bin/foo
is itself interpreted.
Using
.Nm
this way also allows one to reference
.Pa foo
without the path,
as well as set up the environment as desired.
utility will be started with the entire line as a single
argument:
.Pp
.D1 Li "arg[1] = '-S /usr/local/bin/php -n -q -dsafe_mode=0'"
.Pp
and then
.Fl S
processing will split that line into separate arguments before
executing
.Pa /usr/local/bin/php .
.\"
.Sh ENVIRONMENT
The
.Nm
@ -108,7 +333,9 @@ environment variable to locate the requested
.Ar utility
if the name contains no
.Ql /
characters.
characters, unless the
.Fl P
option has been specifed.
.Sh EXIT STATUS
.Ex -std
An exit status of 126 indicates that
@ -117,6 +344,65 @@ was found, but could not be executed.
An exit status of 127 indicates that
.Ar utility
could not be found.
.Sh EXAMPLES
Since the
.Nm
utility is often used as part of the first line of an interpreted script,
the following examples show a number of ways that the
.Nm
utility can be useful in scripts.
.Pp
The kernel processing of an interpreted script does not allow a script
to directly reference some other script as its own interpreter.
As a way around this, the main difference between
.Pp
.D1 Li #!/usr/local/bin/foo
and
.D1 Li "#!/usr/bin/env /usr/local/bin/foo"
.Pp
is that the latter works even if
.Pa /usr/local/bin/foo
is itself an interpreted script.
.Pp
Probably the most common use of
.Nm
is to find the correct interpreter for a script, when the interpreter
may be in different directories on different systems.
The following example will find the
.Ql perl
interpreter by searching through the directories specifed by PATH.
.Pp
.D1 Li "#!/usr/bin/env perl"
.Pp
One limitation of that example is that it assumes the user's value
for PATH is set to a value which will find the interpreter you want
to execute.
The
.Fl P
option can be used to make sure a specific list of directories are
used in the search for
.Ar utility .
Note that the
.Fl S
option is also required for this example to work correctly.
.Pp
.D1 Li "#!/usr/bin/env -S -P/usr/local/bin:/usr/bin perl"
.Pp
The above finds
.Ql perl
only if it is in
.Pa /usr/local/bin
or
.Pa /usr/bin .
That could be combined with the present value of PATH, to provide
more flexibility.
Note that spaces are not required between the
.Fl S
and
.Fl P
options:
.Pp
.D1 Li "#!/usr/bin/env -S-P/usr/local/bin:/usr/bin:${PATH} perl"
.Sh COMPATIBILITY
The
.Nm
@ -134,14 +420,40 @@ The
.Nm
utility conforms to
.St -p1003.1-2001 .
.\".Sh HISTORY
.\"The
.\".Nm
.\"command appeared in
.\".Bx 3.0 .
The
.Fl P , S
and
.Fl v
options are non-standard
.Fx
extensions which may not be available on other operating systems.
.Sh HISTORY
The
.Nm
command appeared in
.Bx 4.4 .
The
.Fl P , S
and
.Fl v
options were added in
.Fx 6.0 .
.Sh BUGS
The
.Nm
utility does not handle utility arguments with equal signs
utility does not handle values of
.Ar utility
which have an equals sign
.Pq Ql =
in their names, for obvious reasons.
in their name (for obvious reasons), unless the
.Ar utility
name begins with a
.Ql /
character.
.Pp
The
.Nm
utility does not take multibyte characters into account when
processing the
.Fl S
option, which may lead to incorrect results in some locales.

22
usr.bin/env/env.c vendored
View File

@ -53,6 +53,8 @@ __FBSDID("$FreeBSD$");
#include <stdlib.h>
#include <unistd.h>
#include "envopts.h"
extern char **environ;
int env_verbosity;
@ -62,17 +64,28 @@ static void usage(void);
int
main(int argc, char **argv)
{
char **ep, *p, **parg;
char *altpath, **ep, *p, **parg;
char *cleanenv[1];
int ch, want_clear;
altpath = NULL;
want_clear = 0;
while ((ch = getopt(argc, argv, "-iv")) != -1)
while ((ch = getopt(argc, argv, "-iP:S:v")) != -1)
switch(ch) {
case '-':
case 'i':
want_clear = 1;
break;
case 'P':
altpath = strdup(optarg);
break;
case 'S':
/*
* The -S option, for "split string on spaces, with
* support for some simple substitutions"...
*/
split_spaces(optarg, &optind, &argc, &argv);
break;
case 'v':
env_verbosity++;
if (env_verbosity > 1)
@ -96,6 +109,8 @@ main(int argc, char **argv)
(void)setenv(*argv, ++p, 1);
}
if (*argv) {
if (altpath)
search_paths(altpath, argv);
if (env_verbosity) {
fprintf(stderr, "#env executing:\t%s\n", *argv);
for (parg = argv, argc = 0; *parg; parg++, argc++)
@ -116,6 +131,7 @@ static void
usage(void)
{
(void)fprintf(stderr,
"usage: env [-iv] [name=value ...] [utility [argument ...]]\n");
"usage: env [-iv] [-P utilpath] [-S string] [name=value ...]"
" [utility [argument ...]]\n");
exit(1);
}

430
usr.bin/env/envopts.c vendored Normal file
View File

@ -0,0 +1,430 @@
/*-
* Copyright (c) 2005 - Garance Alistair Drosehn <gad@FreeBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the FreeBSD Project.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/stat.h>
#include <sys/param.h>
#include <err.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "envopts.h"
static void expand_vars(char **thisarg_p, char **dest_p, const char
**src_p);
static int is_there(char *candidate);
/*
* The is*() routines take a parameter of 'int', but expect values in the range
* of unsigned char. Define some wrappers which take a value of type 'char',
* whether signed or unsigned, and ensure the value ends up in the right range.
*/
#define isalnumch(Anychar) isalnum((u_char)(Anychar))
#define isalphach(Anychar) isalpha((u_char)(Anychar))
#define isspacech(Anychar) isspace((u_char)(Anychar))
/*
* Routine to determine if a given fully-qualified filename is executable.
* This is copied almost verbatim from FreeBSD's usr.bin/which/which.c.
*/
static int
is_there(char *candidate)
{
struct stat fin;
/* XXX work around access(2) false positives for superuser */
if (access(candidate, X_OK) == 0 &&
stat(candidate, &fin) == 0 &&
S_ISREG(fin.st_mode) &&
(getuid() != 0 ||
(fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) {
if (env_verbosity > 1)
fprintf(stderr, "#env matched:\t'%s'\n", candidate);
return (1);
}
return (0);
}
/**
* Routine to search through an alternate path-list, looking for a given
* filename to execute. If the file is found, replace the original
* unqualified name with a fully-qualified path. This allows `env' to
* execute programs from a specific strict list of possible paths, without
* changing the value of PATH seen by the program which will be executed.
* E.G.:
* #!/usr/bin/env -S-P/usr/local/bin:/usr/bin perl
* will execute /usr/local/bin/perl or /usr/bin/perl (whichever is found
* first), no matter what the current value of PATH is, and without
* changing the value of PATH that the script will see when it runs.
*
* This is similar to the print_matches() routine in usr.bin/which/which.c.
*/
void
search_paths(char *path, char **argv)
{
char candidate[PATH_MAX];
const char *d;
char *filename, *fqname;
/* If the file has a `/' in it, then no search is done */
filename = *argv;
if (strchr(filename, '/') != NULL)
return;
if (env_verbosity > 1) {
fprintf(stderr, "#env Searching:\t'%s'\n", path);
fprintf(stderr, "#env for file:\t'%s'\n", filename);
}
fqname = NULL;
while ((d = strsep(&path, ":")) != NULL) {
if (*d == '\0')
d = ".";
if (snprintf(candidate, sizeof(candidate), "%s/%s", d,
filename) >= (int)sizeof(candidate))
continue;
if (is_there(candidate)) {
fqname = candidate;
break;
}
}
if (fqname == NULL) {
errno = ENOENT;
err(127, "%s", filename);
}
*argv = strdup(candidate);
}
/**
* Routine to split a string into multiple parameters, while recognizing a
* few special characters. It recognizes both single and double-quoted
* strings. This processing is designed entirely for the benefit of the
* parsing of "#!"-lines (aka "shebang" lines == the first line of an
* executable script). Different operating systems parse that line in very
* different ways, and this split-on-spaces processing is meant to provide
* ways to specify arbitrary arguments on that line, no matter how the OS
* parses it.
*
* Within a single-quoted string, the two characters "\'" are treated as
* a literal "'" character to add to the string, and "\\" are treated as
* a literal "\" character to add. Other than that, all characters are
* copied until the processing gets to a terminating "'".
*
* Within a double-quoted string, many more "\"-style escape sequences
* are recognized, mostly copied from what is recognized in the `printf'
* command. Some OS's will not allow a literal blank character to be
* included in the one argument that they recognize on a shebang-line,
* so a few additional escape-sequences are defined to provide ways to
* specify blanks.
*
* Within a double-quoted string "\_" is turned into a literal blank.
* (Inside of a single-quoted string, the two characters are just copied)
* Outside of a quoted string, "\_" is treated as both a blank, and the
* end of the current argument. So with a shelbang-line of:
* #!/usr/bin/env -SA=avalue\_perl
* the -S value would be broken up into arguments "A=avalue" and "perl".
*/
void
split_spaces(const char *str, int *origind, int *origc, char ***origv)
{
const char *bq_src, *src;
char *dest, **newargv, *newstr, **nextarg, **oldarg;
int addcount, bq_destlen, copychar, found_sep, in_arg, in_dq, in_sq;
/*
* Ignore leading space on the string, and then malloc enough room
* to build a copy of it. The copy might end up shorter than the
* original, due to quoted strings and '\'-processing.
*/
while (isspacech(*str))
str++;
if (*str == '\0')
return;
newstr = malloc(strlen(str) + 1);
/*
* Allocate plenty of space for the new array of arg-pointers,
* and start that array off with the first element of the old
* array.
*/
newargv = malloc((*origc + (strlen(str) / 2) + 2) * sizeof(char *));
nextarg = newargv;
*nextarg++ = **origv;
/* Come up with the new args by splitting up the given string. */
addcount = 0;
bq_destlen = in_arg = in_dq = in_sq = 0;
bq_src = NULL;
for (src = str, dest = newstr; *src != '\0'; src++) {
copychar = found_sep = 0;
switch (*src) {
case '"':
if (in_sq)
copychar = *src;
else if (in_dq)
in_dq = 0;
else {
in_dq = 1;
bq_destlen = dest - *(nextarg - 1);
bq_src = src;
}
break;
case '$':
if (in_sq)
copychar = *src;
else {
expand_vars((nextarg - 1), &dest, &src);
}
break;
case '\'':
if (in_dq)
copychar = *src;
else if (in_sq)
in_sq = 0;
else {
in_sq = 1;
bq_destlen = dest - *(nextarg - 1);
bq_src = src;
}
break;
case '\\':
if (in_sq) {
/*
* Inside single-quoted strings, only the
* "\'" and "\\" are recognized as special
* strings.
*/
copychar = *(src + 1);
if (copychar == '\'' || copychar == '\\')
src++;
else
copychar = *src;
break;
}
src++;
switch (*src) {
case '"':
case '#':
case '$':
case '\'':
case '\\':
copychar = *src;
break;
case '_':
/*
* Alternate way to get a blank, which allows
* that blank be used to separate arguments
* when it is not inside a quoted string.
*/
if (in_dq)
copychar = ' ';
else {
found_sep = 1;
src++;
}
break;
case 'c':
/*
* Ignore remaining characters in the -S string.
* This would not make sense if found in the
* middle of a quoted string.
*/
if (in_dq)
errx(1, "Sequence '\\%c' is not allowed"
" in quoted strings", *src);
goto str_done;
case 'f':
copychar = '\f';
break;
case 'n':
copychar = '\n';
break;
case 'r':
copychar = '\r';
break;
case 't':
copychar = '\t';
break;
case 'v':
copychar = '\v';
break;
default:
if (isspacech(*src))
copychar = *src;
else
errx(1, "Invalid sequence '\\%c' in -S",
*src);
}
break;
default:
if ((in_dq || in_sq) && in_arg)
copychar = *src;
else if (in_arg && isspacech(*src))
found_sep = 1;
else {
/*
* If the first character of a new argument
* is `#', then ignore the remaining chars.
*/
if (!in_arg && *src == '#')
goto str_done;
copychar = *src;
}
}
if (copychar) {
if (!in_arg) {
/* This is the first byte of a new argument */
*nextarg++ = dest;
addcount++;
in_arg = 1;
}
*dest++ = (char)copychar;
} else if (found_sep) {
*dest++ = '\0';
while (isspacech(*src))
src++;
--src;
in_arg = 0;
}
}
str_done:
*dest = '\0';
*nextarg = NULL;
if (in_dq || in_sq) {
errx(1, "No terminating quote for string: %.*s%s",
bq_destlen, *(nextarg - 1), bq_src);
}
if (env_verbosity > 1) {
fprintf(stderr, "#env split -S:\t'%s'\n", str);
oldarg = newargv + 1;
fprintf(stderr, "#env into:\t'%s'\n", *oldarg);
for (oldarg++; *oldarg; oldarg++)
fprintf(stderr, "#env &\t'%s'\n", *oldarg);
}
/* Copy the unprocessed arg-pointers from the original array */
for (oldarg = *origv + *origind; *oldarg; oldarg++)
*nextarg++ = *oldarg;
*nextarg = NULL;
/* Update optind/argc/argv in the calling routine */
*origind = 1;
*origc += addcount;
*origv = newargv;
}
/**
* Routine to split expand any environment variables referenced in the string
* that -S is processing. For now it only supports the form ${VARNAME}. It
* explicitly does not support $VARNAME, and obviously can not handle special
* shell-variables such as $?, $*, $1, etc. It is called with *src_p pointing
* at the initial '$', and if successful it will update *src_p, *dest_p, and
* possibly *thisarg_p in the calling routine.
*/
void
expand_vars(char **thisarg_p, char **dest_p, const char **src_p)
{
const char *vbegin, *vend, *vvalue;
char *edest, *newstr, *vname;
int bad_reference;
size_t namelen, newlen;
bad_reference = 1;
vbegin = vend = (*src_p) + 1;
if (*vbegin++ == '{')
if (*vbegin == '_' || isalphach(*vbegin)) {
vend = vbegin + 1;
while (*vend == '_' || isalnumch(*vend))
vend++;
if (*vend == '}')
bad_reference = 0;
}
if (bad_reference)
errx(1, "Only ${VARNAME} expansion is supported, error at: %s",
*src_p);
/*
* We now know we have a valid environment variable name, so update
* the caller's source-pointer to the last character in that reference,
* and then pick up the matching value. If the variable is not found,
* or if it has a null value, then our work here is done.
*/
*src_p = vend;
namelen = vend - vbegin + 1;
vname = malloc(namelen);
strlcpy(vname, vbegin, namelen);
vvalue = getenv(vname);
if (vvalue == NULL || *vvalue == '\0') {
if (env_verbosity > 2)
fprintf(stderr,
"#env replacing ${%s} with null string\n",
vname);
return;
}
if (env_verbosity > 2)
fprintf(stderr, "#env expanding ${%s} into '%s'\n", vname,
vvalue);
/*
* There is some value to copy to the destination. If the value is
* shorter than the ${VARNAME} reference that it replaces, then we
* can just copy the value to the existing destination.
*/
edest = *dest_p;
if (strlen(vname) + 3 >= strlen(vvalue)) {
while (*vvalue != '\0')
*edest++ = *vvalue++;
*dest_p = edest;
return;
}
/*
* The value is longer than the string it replaces, which means the
* present destination area is too small to hold it. Create a new
* destination area, copy the present 'thisarg' value and the value
* of the referenced-variable to it, and then update the caller's
* 'thisarg' and 'dest' variables to match.
*/
*edest = '\0'; /* Provide terminator for 'thisarg' */
newlen = strlen(*thisarg_p) + strlen(vvalue) + strlen(*src_p) + 1;
newstr = malloc(newlen);
strcpy(newstr, *thisarg_p);
strcat(newstr, vvalue);
*thisarg_p = newstr;
*dest_p = strchr(newstr, '\0');
}

37
usr.bin/env/envopts.h vendored Normal file
View File

@ -0,0 +1,37 @@
/*-
* Copyright (c) 2005 - Garance Alistair Drosehn <gad@FreeBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the FreeBSD Project.
*
* $FreeBSD$
*/
void search_paths(char *path, char **argv);
void split_spaces(const char *str, int *origind, int *origc,
char ***origv);
extern int env_verbosity;