Hello,

This patch adds support for wordexp(3) and wordfree(3) to libc. These
functions conform to IEEE Std 1003.1-2001 (POSIX). The implementation
comes from NetBSD and uses a shell builtin, "wordexp", to perform the
expansion in line with the methods suggested in the specification[1].

Matthew

[1] http://www.opengroup.org/onlinepubs/9699919799/functions/wordexp.html

Index: bin/ksh/c_ksh.c
===================================================================
RCS file: /cvs/src/bin/ksh/c_ksh.c,v
retrieving revision 1.33
diff -N -u -p bin/ksh/c_ksh.c
--- bin/ksh/c_ksh.c     7 Feb 2009 14:03:24 -0000       1.33
+++ bin/ksh/c_ksh.c     6 Apr 2010 04:36:45 -0000
@@ -520,6 +520,32 @@ c_whence(char **wp)
        return ret;
 }
 
+/*
+ * Do most of the work for wordexp(3). The output is a NULL delimited string
+ * of the format: "<nwords>\0<nchars>\0<word1>\0<word2>\0...<wordn>\0".
+ */
+int
+c_wordexp(char **wp)
+{
+       unsigned int i, len;
+
+       len = 0;
+
+       if (wp[0] == NULL)
+               return (1);
+
+       for (i = 1; wp[i] != NULL; i++)
+               len += strlen(wp[i]);
+
+       shprintf("%u%c", i - 1, '\0');
+       shprintf("%u%c", len, '\0');
+
+       for (i = 1; wp[i] != NULL; i++)
+               shprintf("%s%c", wp[i], '\0');
+
+       return (0);
+}
+
 /* Deal with command -vV - command -p dealt with in comexec() */
 int
 c_command(char **wp)
@@ -1400,6 +1426,7 @@ const struct builtin kshbuiltins [] = {
        {"=typeset", c_typeset},
        {"+unalias", c_unalias},
        {"whence", c_whence},
+       {"wordexp", c_wordexp},
 #ifdef JOBS
        {"+bg", c_fgbg},
        {"+fg", c_fgbg},
Index: bin/ksh/proto.h
===================================================================
RCS file: /cvs/src/bin/ksh/proto.h,v
retrieving revision 1.32
diff -N -u -p bin/ksh/proto.h
--- bin/ksh/proto.h     29 Jan 2009 23:27:26 -0000      1.32
+++ bin/ksh/proto.h     6 Apr 2010 04:36:45 -0000
@@ -29,6 +29,7 @@ int   c_kill(char **);
 void   getopts_reset(int);
 int    c_getopts(char **);
 int    c_bind(char **);
+int    c_wordexp(char **);
 /* c_sh.c */
 int    c_label(char **);
 int    c_shift(char **);
Index: include/Makefile
===================================================================
RCS file: /cvs/src/include/Makefile,v
retrieving revision 1.153
diff -N -u -p include/Makefile
--- include/Makefile    3 Feb 2010 20:49:58 -0000       1.153
+++ include/Makefile    6 Apr 2010 04:36:45 -0000
@@ -23,7 +23,7 @@ FILES=        a.out.h ar.h assert.h bitstring.h blf.h bm.h bs
        stdbool.h stddef.h stdio.h stdlib.h \
        string.h strings.h struct.h sysexits.h tar.h \
        time.h ttyent.h tzfile.h unistd.h utime.h utmp.h vis.h \
-       wchar.h wctype.h
+       wchar.h wctype.h wordexp.h
 
 FILES+=        link.h link_aout.h link_elf.h
 
Index: include/wordexp.h
===================================================================
RCS file: include/wordexp.h
diff -N -u -p include/wordexp.h
--- /dev/null   5 Apr 2010 22:36:46 -0000
+++ include/wordexp.h   6 Apr 2010 04:36:45 -0000
@@ -0,0 +1,74 @@
+/*     $NetBSD: wordexp.h,v 1.2 2008/04/01 19:23:28 drochner Exp $     */
+
+/*-
+ * 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: /repoman/r/ncvs/src/include/wordexp.h,v 1.4 2003/01/03 12:03:38 
tjr Exp $
+ */
+
+#ifndef _WORDEXP_H_
+#define _WORDEXP_H_
+
+#include <sys/cdefs.h>
+
+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 */
+
+       /* internal */
+       char    *we_strings;            /* storage for wordv strings */
+       size_t  we_nbytes;              /* size of we_strings */
+} wordexp_t;
+
+/*
+ * Flags for wordexp().
+ */
+#define        WRDE_APPEND     0x1             /* append to previously 
generated */
+#define        WRDE_DOOFFS     0x2             /* we_offs member is valid */
+#define        WRDE_NOCMD      0x4             /* disallow command 
substitution */
+#define        WRDE_REUSE      0x8             /* reuse wordexp_t */
+#define        WRDE_SHOWERR    0x10            /* don't redirect stderr to 
/dev/null */
+#define        WRDE_UNDEF      0x20            /* disallow undefined shell 
vars */
+
+/*
+ * Return values from wordexp().
+ */
+#define        WRDE_BADCHAR    1               /* unquoted special character */
+#define        WRDE_BADVAL     2               /* undefined variable */
+#define        WRDE_CMDSUB     3               /* command substitution not 
allowed */
+#define        WRDE_NOSPACE    4               /* no memory for result */
+#if defined(_XOPEN_SOURCE)
+#define        WRDE_NOSYS      5               /* obsolete, reserved */
+#endif
+#define        WRDE_SYNTAX     6               /* shell syntax error */
+#define WRDE_ERRNO     7               /* other errors see errno */
+
+__BEGIN_DECLS
+int    wordexp(const char * __restrict, wordexp_t * __restrict, int);
+void   wordfree(wordexp_t *);
+__END_DECLS
+
+#endif /* _WORDEXP_H_ */
Index: lib/libc/gen/Makefile.inc
===================================================================
RCS file: /cvs/src/lib/libc/gen/Makefile.inc,v
retrieving revision 1.50
diff -N -u -p lib/libc/gen/Makefile.inc
--- lib/libc/gen/Makefile.inc   3 Feb 2010 20:49:00 -0000       1.50
+++ lib/libc/gen/Makefile.inc   6 Apr 2010 04:36:46 -0000
@@ -23,7 +23,7 @@ SRCS+=  alarm.c assert.c auth_subr.c authenticate.c \
         tolower_.c ttyname.c \
         ttyslot.c toupper_.c ualarm.c uname.c unvis.c usleep.c \
         utime.c valloc.c vis.c wait.c wait3.c waitpid.c warn.c \
-        warnx.c vwarn.c vwarnx.c verr.c verrx.c
+        warnx.c vwarn.c vwarnx.c verr.c verrx.c wordexp.c
 
 # indirect reference stubs, to be removed soon.
 SRCS+= _sys_errlist.c _sys_nerr.c _sys_siglist.c
@@ -62,7 +62,8 @@ MAN+= alarm.3 auth_subr.3 authenticate.3 basename.3 cl
        scandir.3 setjmp.3 setmode.3 setproctitle.3 siginterrupt.3 signal.3 \
        sigsetops.3 sleep.3 statvfs.3 sysconf.3 sysctl.3 strtofflags.3 \
        syslog.3 time.3 times.3 timezone.3 toascii.3 tolower.3 toupper.3 \
-       ttyname.3 ualarm.3 uname.3 unvis.3 usleep.3 utime.3 valloc.3 vis.3
+       ttyname.3 ualarm.3 uname.3 unvis.3 usleep.3 utime.3 valloc.3 vis.3 \
+       wordexp.3
 
 MLINKS+=auth_subr.3 auth_open.3 auth_subr.3 auth_call.3 auth_subr.3 \
        auth_challenge.3 auth_subr.3 auth_check_change.3 auth_subr.3 \
@@ -145,3 +146,4 @@ MLINKS+=toupper.3 _toupper.3
 MLINKS+=ttyname.3 ttyname_r.3 ttyname.3 isatty.3 ttyname.3 ttyslot.3
 MLINKS+=unvis.3 strunvis.3 unvis.3 strnunvis.3
 MLINKS+=vis.3 strvis.3 vis.3 strnvis.3 vis.3 strvisx.3
+MLINKS+=wordexp.3 wordfree.3
Index: lib/libc/gen/wordexp.3
===================================================================
RCS file: lib/libc/gen/wordexp.3
diff -N -u -p lib/libc/gen/wordexp.3
--- /dev/null   5 Apr 2010 22:36:46 -0000
+++ lib/libc/gen/wordexp.3      6 Apr 2010 04:36:46 -0000
@@ -0,0 +1,205 @@
+.\"
+.\" 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: src/lib/libc/gen/wordexp.3,v 1.9.12.1 2010/02/10 00:26:20 
kensmith Exp $
+.\"
+.Dd $Mdocdate: July 29 2004 $
+.Dt WORDEXP 3
+.Os
+.Sh NAME
+.Nm wordexp ,
+.Nm wordfree
+.Nd perform word expansions
+.Sh SYNOPSIS
+.Fd #include <wordexp.h>
+.Ft int
+.Fn wordexp "const char * restrict words" "wordexp_t * restrict pwordexp" "int 
flags"
+.Ft void
+.Fn wordfree "wordexp_t *pwordexp"
+.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
+.Fa pwordexp ,
+and the number of words into
+.Va we_wordc .
+.Pp
+The
+.Fa 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 generated by a previous call to
+.Fn wordexp .
+.It Dv WRDE_DOOFFS
+As many
+.Dv NULL
+pointers as are specified by the
+.Va we_offs
+member of
+.Fa pwordexp
+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
+.Fa pwordexp
+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
+.Aq 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:
+.Aq 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
+.Pa *.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 conform 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.
+.Pp
+The current
+.Fn wordexp
+implementation does not recognize multibyte characters, since the
+shell (which it invokes to perform expansions) does not.
Index: lib/libc/gen/wordexp.c
===================================================================
RCS file: lib/libc/gen/wordexp.c
diff -N -u -p lib/libc/gen/wordexp.c
--- /dev/null   5 Apr 2010 22:36:46 -0000
+++ lib/libc/gen/wordexp.c      6 Apr 2010 04:36:46 -0000
@@ -0,0 +1,340 @@
+/*     $NetBSD: wordexp.c,v 1.2.30.1 2009/05/13 19:18:23 jym Exp $     */
+
+/*-
+ * 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 <assert.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"
+
+#if 0
+__FBSDID("$FreeBSD: /repoman/r/ncvs/src/lib/libc/gen/wordexp.c,v 1.5 
2004/04/09 11:32:32 tjr Exp $");
+__RCSID("$NetBSD: wordexp.c,v 1.2.30.1 2009/05/13 19:18:23 jym Exp $");
+#endif
+
+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).
+ *
+ */
+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 */
+       size_t nwords, nbytes;          /* Number of words, bytes from child */
+       int i;                          /* Handy integer */
+       unsigned int ui;                /* For array iteration */
+       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 */
+       const char *ifs;                /* IFS env. var. */
+       char *np, *p;                   /* Handy pointers */
+       char *nstrings;                 /* Temporary for realloc() */
+       char **nwv;                     /* Temporary for realloc() */
+       FILE *fp;                       /* Stream to read pipe */
+       extern char **environ;
+       char *cmd;
+
+       if ((ifs = getenv("IFS")) == NULL)
+               ifs = " \t\n";
+       if (asprintf(&cmd, "wordexp%c%s\n", *ifs, words) < 0)
+               return (WRDE_NOSPACE);
+       if (pipe(pdes) < 0) {
+               free(cmd);
+               return (WRDE_ERRNO);
+       }
+       if ((fp = fdopen(pdes[0], "r")) == NULL) {
+               free(cmd);
+               return (WRDE_ERRNO);
+       }
+       if ((pid = fork()) < 0) {
+               free(cmd);
+               fclose(fp);
+               close(pdes[1]);
+               return (WRDE_ERRNO);
+       }
+       else if (pid == 0) {
+               /*
+                * We are the child; just get /bin/sh to run the wordexp
+                * builtin on `words'.
+                */
+               int devnull;
+
+               close(pdes[0]);
+               if (pdes[1] != STDOUT_FILENO) {
+                       if (dup2(pdes[1], STDOUT_FILENO) < 0)
+                               _exit(1);
+                       close(pdes[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);
+               }
+               execle(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u",
+                   "-c", cmd, (char *)NULL, environ);
+               _exit(1);
+       }
+
+       /*
+        * We are the parent; read the output of the shell wordexp function,
+        * which is a decimal word count, an null, a decimal byte count,
+        * (not including terminating null bytes), a null and then followed
+        * by the expanded words separated by nulls.
+        */
+       free(cmd);
+       close(pdes[1]);
+       /* read the word count */       
+       nwords = 0;
+       while ((i = getc(fp)) != EOF) {
+               if (i == '\0')
+                       break;
+               nwords *= 10;
+               nwords += (i - '0');
+       }
+       /* read the byte count */
+       nbytes = 0;
+       while ((i = getc(fp)) != EOF) {
+               if (i == '\0')
+                       break;
+               nbytes *= 10;
+               nbytes += (i - '0');
+       }
+       if (i == EOF) {
+               fclose(fp);
+               waitpid(pid, &status, 0);
+               return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
+       }
+       nbytes += 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;
+       if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND))
+               vofs += we->we_offs;
+       we->we_wordc += nwords;
+       we->we_nbytes += nbytes;
+       if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
+           (flags & WRDE_DOOFFS ?  we->we_offs : 0)) *
+           sizeof(char *))) == NULL) {
+               fclose(fp);
+               waitpid(pid, &status, 0);
+               return (WRDE_NOSPACE);
+       }
+       we->we_wordv = nwv;
+       if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
+               fclose(fp);
+               waitpid(pid, &status, 0);
+               return (WRDE_NOSPACE);
+       }
+       for (ui = 0; ui < vofs; ui++)
+               if (we->we_wordv[ui] != NULL)
+                       we->we_wordv[ui] += nstrings - we->we_strings;
+       we->we_strings = nstrings;
+
+       if (fread(we->we_strings + sofs, sizeof(char), nbytes, fp) != nbytes) {
+               fclose(fp);
+               waitpid(pid, &status, 0);
+               return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
+       }
+
+       if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) ||
+           WEXITSTATUS(status) != 0) {
+               fclose(fp);
+               return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
+       }
+       fclose(fp);
+
+       /*
+        * Break the null-terminated expanded word strings out into
+        * the vector.
+        */
+       if (vofs == 0 && flags & WRDE_DOOFFS)
+               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.
+ */
+static 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;
+                       continue;
+               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 (quote + squote == 0 && c == '(') {
+                               if (flags & WRDE_NOCMD && *words != '(')
+                                       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 (quote + squote == 0 && 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);
+                       } else
+                               c = *--words;
+                       break;
+               default:
+                       break;
+               }
+               quote = 0;
+       }
+       if (quote + squote + dquote != 0)
+               return (WRDE_SYNTAX);
+
+       return (0);
+}
+
+/*
+ * wordfree --
+ *     Free the result of wordexp(). See wordexp(3).
+ *
+ */
+void
+wordfree(wordexp_t *we)
+{
+       free(we->we_wordv);
+       free(we->we_strings);
+       we->we_wordv = NULL;
+       we->we_strings = NULL;
+       we->we_nbytes = 0;
+       we->we_wordc = 0;
+}

Reply via email to