summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortjr <tjr@FreeBSD.org>2002-12-26 14:34:18 +0000
committertjr <tjr@FreeBSD.org>2002-12-26 14:34:18 +0000
commiteeed1ed55b1ce8ed4e5a28c181e75d715ae1d901 (patch)
treefeb95759207fb411ee91f10d2be4ecadf6e1e695
parentd6329a70b8547e10585b9261f66d77e646eb767d (diff)
downloadFreeBSD-src-eeed1ed55b1ce8ed4e5a28c181e75d715ae1d901.zip
FreeBSD-src-eeed1ed55b1ce8ed4e5a28c181e75d715ae1d901.tar.gz
Add an implementation of the POSIX wordexp() and wordfree() functions,
which perform shell-style word expansion on strings. This is still a little rough around the edges. PR: 13420
-rw-r--r--lib/libc/gen/Makefile.inc5
-rw-r--r--lib/libc/gen/wordexp.3207
-rw-r--r--lib/libc/gen/wordexp.c308
3 files changed, 518 insertions, 2 deletions
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
index ba35e04..39ae3a2 100644
--- a/lib/libc/gen/Makefile.inc
+++ b/lib/libc/gen/Makefile.inc
@@ -29,7 +29,7 @@ SRCS+= __xuname.c _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \
sysconf.c sysctl.c sysctlbyname.c sysctlnametomib.c \
syslog.c telldir.c termios.c time.c times.c timezone.c ttyname.c \
ttyslot.c ualarm.c ulimit.c uname.c unvis.c usleep.c utime.c \
- valloc.c vis.c wait.c wait3.c waitpid.c
+ valloc.c vis.c wait.c wait3.c waitpid.c wordexp.c
# machine-dependent gen sources
.if exists(${.CURDIR}/${MACHINE_ARCH}/gen/Makefile.inc)
@@ -56,7 +56,7 @@ MAN+= alarm.3 arc4random.3 \
strtofflags.3 sysconf.3 sysctl.3 syslog.3 tcgetpgrp.3 \
tcsendbreak.3 tcsetattr.3 tcsetpgrp.3 time.3 times.3 timezone.3 \
ttyname.3 tzset.3 ualarm.3 ucontext.3 ulimit.3 uname.3 \
- unvis.3 usleep.3 utime.3 valloc.3 vis.3
+ unvis.3 usleep.3 utime.3 valloc.3 vis.3 wordexp.3
MLINKS+=arc4random.3 arc4random_addrandom.3 arc4random.3 arc4random_stir.3
MLINKS+=ctermid.3 ctermid_r.3
@@ -131,3 +131,4 @@ MLINKS+=ttyname.3 isatty.3 ttyname.3 ttyslot.3
MLINKS+=tzset.3 tzsetwall.3
MLINKS+=unvis.3 strunvis.3 unvis.3 strunvisx.3
MLINKS+=vis.3 strvis.3 vis.3 strvisx.3
+MLINKS+=wordexp.3 wordfree.3
diff --git a/lib/libc/gen/wordexp.3 b/lib/libc/gen/wordexp.3
new file mode 100644
index 0000000..895e90e
--- /dev/null
+++ b/lib/libc/gen/wordexp.3
@@ -0,0 +1,207 @@
+.\"
+.\" Copyright (c) 2002 Tim J. Robbins
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 27, 2002
+.Dt WORDEXP 3
+.Os
+.Sh NAME
+.Nm wordexp
+.Nd "perform shell-style word expansions"
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In sys/types.h
+.In wordexp.h
+.Ft int
+.Fo wordexp
+.Fa "const char * restrict words"
+.Fa "wordexp_t * restrict we"
+.Fa "int flags"
+.Fc
+.Fo wordfree
+.Fa "wordexp_t *we"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn wordexp
+function performs shell-style word expansion on
+.Fa words
+and places the list of words into the
+.Va we_wordv
+member of
+.Va we ,
+and the number of words into
+.Va we_wordc .
+.Pp
+The
+.Va flags
+argument is the bitwise inclusive OR of any of the following constants:
+.Bl -tag -width ".Dv WRDE_SHOWERR"
+.It Dv WRDE_APPEND
+Append the words to those generate by a previous call to
+.Fn wordexp .
+.It Dv WRDE_DOOFS
+As many
+.Dv NULL
+pointers as are specified by the
+.Va we_offs
+member of
+.Va we
+are added to the front of
+.Va we_wordv .
+.It Dv WRDE_NOCMD
+Disallow command substitution in
+.Fa words .
+See the note in
+.Sx BUGS
+before using this.
+.It Dv WRDE_REUSE
+The
+.Va we
+argument was passed to a previous successful call to
+.Fn wordexp
+but has not been passed to
+.Fn wordfree .
+The implementation may reuse the space allocated to it.
+.It Dv WRDE_SHOWERR
+Do not redirect shell error messages to
+.Pa /dev/null .
+.It Dv WRDE_UNDEF
+Report error on an attempt to expand an undefined shell variable.
+.El
+.Pp
+The
+.Vt wordexp_t
+structure is defined in
+.Pa wordexp.h
+as:
+.Bd -literal -offset indent
+typedef struct {
+ size_t we_wordc; /* count of words matched */
+ char **we_wordv; /* pointer to list of words */
+ size_t we_offs; /* slots to reserve in we_wordv */
+} wordexp_t;
+.Ed
+.Pp
+The
+.Fn wordfree
+function frees the memory allocated by
+.Fn wordexp .
+.Sh IMPLEMENTATION NOTES
+The
+.Fn wordexp
+function is implemented as a wrapper around the undocumented
+.Ic wordexp
+shell built-in command.
+.Sh RETURN VALUES
+The
+.Fn wordexp
+function returns zero if successful, otherwise it returns one of the following
+error codes:
+.Bl -tag -width ".Dv WRDE_NOSPACE"
+.It Dv WRDE_BADCHAR
+The
+.Fa words
+argument contains one of the following unquoted characters:
+<newline>,
+.Ql | ,
+.Ql & ,
+.Ql \&; ,
+.Ql < ,
+.Ql > ,
+.Ql \&( ,
+.Ql \&) ,
+.Ql { ,
+.Ql } .
+.It Dv WRDE_BADVAL
+An attempt was made to expand an undefined shell variable and
+.Dv WRDE_UNDEF
+is set in
+.Fa flags .
+.It Dv WRDE_CMDSUB
+An attempt was made to use command substitution and
+.Dv WRDE_NOCMD
+is set in
+.Fa flags .
+.It Dv WRDE_NOSPACE
+Not enough memory to store the result.
+.It Dv WRDE_SYNTAX
+Shell syntax error in
+.Fa words .
+.El
+.Pp
+The
+.Fn wordfree
+function returns no value.
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev IFS"
+.It Ev IFS
+Field separator.
+.El
+.Sh EXAMPLES
+Invoke the editor on all
+.Dq Li \&.c
+files in the current directory,
+and
+.Pa /etc/motd
+(error checking omitted):
+.Bd -literal -offset indent
+wordexp_t we;
+
+wordexp("${EDITOR:-vi} *.c /etc/motd", &we, 0);
+execvp(we->we_wordv[0], we->we_wordv);
+.Ed
+.Sh DIAGNOSTICS
+Diagnostic messages from the shell are written to the standard error output
+if
+.Dv WRDE_SHOWERR
+is set in
+.Fa flags .
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr fnmatch 3 ,
+.Xr glob 3 ,
+.Xr popen 3 ,
+.Xr system 3
+.Sh STANDARDS
+The
+.Fn wordexp
+and
+.Fn wordfree
+functions conforms to
+.St -p1003.1-2001 .
+.Sh BUGS
+Do not pass untrusted user data to
+.Fn wordexp ,
+regardless of whether the
+.Dv WRDE_NOCMD
+flag is set.
+The
+.Fn wordexp
+function attempts to detect input that would cause commands to be
+executed before passing it to the shell
+but it does not use the same parser so it may be fooled.
diff --git a/lib/libc/gen/wordexp.c b/lib/libc/gen/wordexp.c
new file mode 100644
index 0000000..dd16fcf
--- /dev/null
+++ b/lib/libc/gen/wordexp.c
@@ -0,0 +1,308 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins.
+ * 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.
+ */
+
+#include "namespace.h"
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wordexp.h>
+#include "un-namespace.h"
+
+__FBSDID("$FreeBSD$");
+
+static int we_askshell(const char *, wordexp_t *, int);
+static int we_check(const char *, int);
+
+/*
+ * wordexp --
+ * Perform shell word expansion on `words' and place the resulting list
+ * of words in `we'. See wordexp(3).
+ *
+ * Specified by IEEE Std. 1003.1-2001.
+ */
+int
+wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags)
+{
+ int error;
+
+ if (flags & WRDE_REUSE)
+ wordfree(we);
+ if ((flags & WRDE_APPEND) == 0) {
+ we->we_wordc = 0;
+ we->we_wordv = NULL;
+ we->we_strings = NULL;
+ we->we_nbytes = 0;
+ }
+ if ((error = we_check(words, flags)) != 0) {
+ wordfree(we);
+ return (error);
+ }
+ if ((error = we_askshell(words, we, flags)) != 0) {
+ wordfree(we);
+ return (error);
+ }
+ return (0);
+}
+
+/*
+ * we_askshell --
+ * Use the `wordexp' /bin/sh builtin function to do most of the work
+ * in expanding the word string. This function is complicated by
+ * memory management.
+ */
+static int
+we_askshell(const char *words, wordexp_t *we, int flags)
+{
+ int pdes[2]; /* Pipe to child */
+ char bbuf[9]; /* Buffer for byte count */
+ char wbuf[9]; /* Buffer for word count */
+ long nwords, nbytes; /* Number of words, bytes from child */
+ long i; /* Handy integer */
+ size_t sofs; /* Offset into we->we_strings */
+ size_t vofs; /* Offset into we->we_wordv */
+ pid_t pid; /* Process ID of child */
+ int status; /* Child exit status */
+ char *ifs; /* IFS env. var. */
+ char *np, *p; /* Handy pointers */
+ char *nstrings; /* Temporary for realloc() */
+ char **nwv; /* Temporary for realloc() */
+
+ if ((ifs = getenv("IFS")) == NULL)
+ ifs = " \t\n";
+
+ if (pipe(pdes) < 0)
+ return (WRDE_NOSPACE); /* XXX */
+ if ((pid = fork()) < 0) {
+ close(pdes[0]);
+ close(pdes[1]);
+ return (WRDE_NOSPACE); /* XXX */
+ }
+ else if (pid == 0) {
+ /*
+ * We are the child; just get /bin/sh to run the wordexp
+ * builtin on `words'.
+ */
+ int devnull;
+ char *cmd;
+
+ close(pdes[0]);
+ if (dup2(pdes[1], STDOUT_FILENO) < 0)
+ _exit(1);
+ close(pdes[1]);
+ if (asprintf(&cmd, "wordexp%c%s\n", *ifs, words) < 0)
+ _exit(1);
+ if ((flags & WRDE_SHOWERR) == 0) {
+ if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0666)) < 0)
+ _exit(1);
+ if (dup2(devnull, STDERR_FILENO) < 0)
+ _exit(1);
+ close(devnull);
+ }
+ execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u",
+ "-c", cmd, NULL);
+ _exit(1);
+ }
+
+ /*
+ * We are the parent; read the output of the shell wordexp function,
+ * which is a 32-bit hexadecimal word count, a 32-bit hexadecimal
+ * byte count (not including terminating null bytes), followed by
+ * the expanded words separated by nulls.
+ */
+ close(pdes[1]);
+ if (read(pdes[0], wbuf, 8) != 8 || read(pdes[0], bbuf, 8) != 8) {
+ close(pdes[0]);
+ return (WRDE_NOSPACE); /* XXX */
+ }
+ wbuf[8] = bbuf[8] = '\0';
+ nwords = strtol(wbuf, NULL, 16);
+ nbytes = strtol(bbuf, NULL, 16) + nwords;
+
+ /*
+ * Allocate or reallocate (when flags & WRDE_APPEND) the word vector
+ * and string storage buffers for the expanded words we're about to
+ * read from the child.
+ */
+ sofs = we->we_nbytes;
+ vofs = we->we_wordc;
+ we->we_wordc += nwords;
+ we->we_nbytes += nbytes;
+ if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
+ (flags & WRDE_DOOFS ? we->we_offs : 0)) *
+ sizeof(char *))) == NULL) {
+ close(pdes[0]);
+ return (WRDE_NOSPACE);
+ }
+ we->we_wordv = nwv;
+ if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
+ close(pdes[0]);
+ return (WRDE_NOSPACE);
+ }
+ for (i = 0; i < vofs; i++)
+ if (we->we_wordv[i] != NULL)
+ we->we_wordv[i] += nstrings - we->we_strings;
+ we->we_strings = nstrings;
+
+ if (read(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
+ close(pdes[0]);
+ return (WRDE_NOSPACE); /* XXX */
+ }
+
+ if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) ||
+ WEXITSTATUS(status) != 0) {
+ close(pdes[0]);
+ return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
+ }
+ close(pdes[0]);
+
+ /*
+ * Break the null-terminated expanded word strings out into
+ * the vector.
+ */
+ if (vofs == 0 && flags & WRDE_DOOFS)
+ while (vofs < we->we_offs)
+ we->we_wordv[vofs++] = NULL;
+ p = we->we_strings + sofs;
+ while (nwords-- != 0) {
+ we->we_wordv[vofs++] = p;
+ if ((np = memchr(p, '\0', nbytes)) == NULL)
+ return (WRDE_NOSPACE); /* XXX */
+ nbytes -= np - p + 1;
+ p = np + 1;
+ }
+ we->we_wordv[vofs] = NULL;
+
+ return (0);
+}
+
+/*
+ * we_check --
+ * Check that the string contains none of the following unquoted
+ * special characters: <newline> |&;<>(){}
+ * or command substitutions when WRDE_NOCMD is set in flags.
+ */
+int
+we_check(const char *words, int flags)
+{
+ char c;
+ int dquote, level, quote, squote;
+
+ quote = squote = dquote = 0;
+ while ((c = *words++) != '\0') {
+ switch (c) {
+ case '\\':
+ quote ^= 1;
+ break;
+ case '\'':
+ if (quote + dquote == 0)
+ squote ^= 1;
+ break;
+ case '"':
+ if (quote + squote == 0)
+ dquote ^= 1;
+ break;
+ case '`':
+ if (quote + squote == 0 && flags & WRDE_NOCMD)
+ return (WRDE_CMDSUB);
+ while ((c = *words++) != '\0' && c != '`')
+ if (c == '\\' && (c = *words++) == '\0')
+ break;
+ if (c == '\0')
+ return (WRDE_SYNTAX);
+ break;
+ case '|': case '&': case ';': case '<': case '>':
+ case '{': case '}': case '(': case ')': case '\n':
+ if (quote + squote + dquote == 0)
+ return (WRDE_BADCHAR);
+ break;
+ case '$':
+ if ((c = *words++) == '\0')
+ break;
+ else if (c == '(') {
+ if (flags & WRDE_NOCMD)
+ return (WRDE_CMDSUB);
+ level = 1;
+ while ((c = *words++) != '\0') {
+ if (c == '\\') {
+ if ((c = *words++) == '\0')
+ break;
+ } else if (c == '(')
+ level++;
+ else if (c == ')' && --level == 0)
+ break;
+ }
+ if (c == '\0' || level != 0)
+ return (WRDE_SYNTAX);
+ } else if (c == '{') {
+ level = 1;
+ while ((c = *words++) != '\0') {
+ if (c == '\\') {
+ if ((c = *words++) == '\0')
+ break;
+ } else if (c == '{')
+ level++;
+ else if (c == '}' && --level == 0)
+ break;
+ }
+ if (c == '\0' || level != 0)
+ return (WRDE_SYNTAX);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (quote + squote + dquote != 0)
+ return (WRDE_SYNTAX);
+
+ return (0);
+}
+
+/*
+ * wordfree --
+ * Free the result of wordexp(). See wordexp(3).
+ *
+ * Specified by IEEE Std. 1003.1-2001.
+ */
+void
+wordfree(wordexp_t *we)
+{
+
+ if (we == NULL)
+ return;
+ free(we->we_wordv);
+ free(we->we_strings);
+ we->we_wordv = NULL;
+ we->we_strings = NULL;
+ we->we_nbytes = 0;
+ we->we_wordc = 0;
+}
OpenPOWER on IntegriCloud