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; +}