1faea1495STim J. Robbins /*- 2faea1495STim J. Robbins * Copyright (c) 2002 Tim J. Robbins. 3faea1495STim J. Robbins * All rights reserved. 4faea1495STim J. Robbins * 5faea1495STim J. Robbins * Redistribution and use in source and binary forms, with or without 6faea1495STim J. Robbins * modification, are permitted provided that the following conditions 7faea1495STim J. Robbins * are met: 8faea1495STim J. Robbins * 1. Redistributions of source code must retain the above copyright 9faea1495STim J. Robbins * notice, this list of conditions and the following disclaimer. 10faea1495STim J. Robbins * 2. Redistributions in binary form must reproduce the above copyright 11faea1495STim J. Robbins * notice, this list of conditions and the following disclaimer in the 12faea1495STim J. Robbins * documentation and/or other materials provided with the distribution. 13faea1495STim J. Robbins * 14faea1495STim J. Robbins * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15faea1495STim J. Robbins * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16faea1495STim J. Robbins * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17faea1495STim J. Robbins * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18faea1495STim J. Robbins * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19faea1495STim J. Robbins * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20faea1495STim J. Robbins * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21faea1495STim J. Robbins * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22faea1495STim J. Robbins * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23faea1495STim J. Robbins * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24faea1495STim J. Robbins * SUCH DAMAGE. 25faea1495STim J. Robbins */ 26faea1495STim J. Robbins 27faea1495STim J. Robbins #include "namespace.h" 28faea1495STim J. Robbins #include <sys/cdefs.h> 29faea1495STim J. Robbins #include <sys/types.h> 30faea1495STim J. Robbins #include <sys/wait.h> 31364e9ccbSJilles Tjoelker #include <errno.h> 32faea1495STim J. Robbins #include <fcntl.h> 33faea1495STim J. Robbins #include <paths.h> 34364e9ccbSJilles Tjoelker #include <signal.h> 35d358fa78SJilles Tjoelker #include <stdbool.h> 36faea1495STim J. Robbins #include <stdio.h> 37faea1495STim J. Robbins #include <stdlib.h> 38faea1495STim J. Robbins #include <string.h> 39faea1495STim J. Robbins #include <unistd.h> 40faea1495STim J. Robbins #include <wordexp.h> 41faea1495STim J. Robbins #include "un-namespace.h" 42bd6060a1SKonstantin Belousov #include "libc_private.h" 43faea1495STim J. Robbins 44faea1495STim J. Robbins __FBSDID("$FreeBSD$"); 45faea1495STim J. Robbins 46faea1495STim J. Robbins static int we_askshell(const char *, wordexp_t *, int); 47d358fa78SJilles Tjoelker static int we_check(const char *); 48faea1495STim J. Robbins 49faea1495STim J. Robbins /* 50faea1495STim J. Robbins * wordexp -- 51faea1495STim J. Robbins * Perform shell word expansion on `words' and place the resulting list 52faea1495STim J. Robbins * of words in `we'. See wordexp(3). 53faea1495STim J. Robbins * 54faea1495STim J. Robbins * Specified by IEEE Std. 1003.1-2001. 55faea1495STim J. Robbins */ 56faea1495STim J. Robbins int 57faea1495STim J. Robbins wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) 58faea1495STim J. Robbins { 59faea1495STim J. Robbins int error; 60faea1495STim J. Robbins 61faea1495STim J. Robbins if (flags & WRDE_REUSE) 62faea1495STim J. Robbins wordfree(we); 63faea1495STim J. Robbins if ((flags & WRDE_APPEND) == 0) { 64faea1495STim J. Robbins we->we_wordc = 0; 65faea1495STim J. Robbins we->we_wordv = NULL; 66faea1495STim J. Robbins we->we_strings = NULL; 67faea1495STim J. Robbins we->we_nbytes = 0; 68faea1495STim J. Robbins } 69d358fa78SJilles Tjoelker if ((error = we_check(words)) != 0) { 70faea1495STim J. Robbins wordfree(we); 71faea1495STim J. Robbins return (error); 72faea1495STim J. Robbins } 73faea1495STim J. Robbins if ((error = we_askshell(words, we, flags)) != 0) { 74faea1495STim J. Robbins wordfree(we); 75faea1495STim J. Robbins return (error); 76faea1495STim J. Robbins } 77faea1495STim J. Robbins return (0); 78faea1495STim J. Robbins } 79faea1495STim J. Robbins 80364e9ccbSJilles Tjoelker static size_t 81364e9ccbSJilles Tjoelker we_read_fully(int fd, char *buffer, size_t len) 82364e9ccbSJilles Tjoelker { 83364e9ccbSJilles Tjoelker size_t done; 84364e9ccbSJilles Tjoelker ssize_t nread; 85364e9ccbSJilles Tjoelker 86364e9ccbSJilles Tjoelker done = 0; 87364e9ccbSJilles Tjoelker do { 88364e9ccbSJilles Tjoelker nread = _read(fd, buffer + done, len - done); 89364e9ccbSJilles Tjoelker if (nread == -1 && errno == EINTR) 90364e9ccbSJilles Tjoelker continue; 91364e9ccbSJilles Tjoelker if (nread <= 0) 92364e9ccbSJilles Tjoelker break; 93364e9ccbSJilles Tjoelker done += nread; 94364e9ccbSJilles Tjoelker } while (done != len); 95364e9ccbSJilles Tjoelker return done; 96364e9ccbSJilles Tjoelker } 97364e9ccbSJilles Tjoelker 98d358fa78SJilles Tjoelker static bool 99d358fa78SJilles Tjoelker we_write_fully(int fd, const char *buffer, size_t len) 100d358fa78SJilles Tjoelker { 101d358fa78SJilles Tjoelker size_t done; 102d358fa78SJilles Tjoelker ssize_t nwritten; 103d358fa78SJilles Tjoelker 104d358fa78SJilles Tjoelker done = 0; 105d358fa78SJilles Tjoelker do { 106d358fa78SJilles Tjoelker nwritten = _write(fd, buffer + done, len - done); 107d358fa78SJilles Tjoelker if (nwritten == -1 && errno == EINTR) 108d358fa78SJilles Tjoelker continue; 109d358fa78SJilles Tjoelker if (nwritten <= 0) 110d358fa78SJilles Tjoelker return (false); 111d358fa78SJilles Tjoelker done += nwritten; 112d358fa78SJilles Tjoelker } while (done != len); 113d358fa78SJilles Tjoelker return (true); 114d358fa78SJilles Tjoelker } 115d358fa78SJilles Tjoelker 116faea1495STim J. Robbins /* 117faea1495STim J. Robbins * we_askshell -- 118d358fa78SJilles Tjoelker * Use the `freebsd_wordexp' /bin/sh builtin function to do most of the 119d358fa78SJilles Tjoelker * work in expanding the word string. This function is complicated by 120faea1495STim J. Robbins * memory management. 121faea1495STim J. Robbins */ 122faea1495STim J. Robbins static int 123faea1495STim J. Robbins we_askshell(const char *words, wordexp_t *we, int flags) 124faea1495STim J. Robbins { 125d358fa78SJilles Tjoelker int pdesw[2]; /* Pipe for writing words */ 126d358fa78SJilles Tjoelker int pdes[2]; /* Pipe for reading output */ 127d358fa78SJilles Tjoelker char wfdstr[sizeof(int) * 3 + 1]; 128d358fa78SJilles Tjoelker char buf[35]; /* Buffer for byte and word count */ 129faea1495STim J. Robbins long nwords, nbytes; /* Number of words, bytes from child */ 130faea1495STim J. Robbins long i; /* Handy integer */ 131faea1495STim J. Robbins size_t sofs; /* Offset into we->we_strings */ 132faea1495STim J. Robbins size_t vofs; /* Offset into we->we_wordv */ 133faea1495STim J. Robbins pid_t pid; /* Process ID of child */ 134364e9ccbSJilles Tjoelker pid_t wpid; /* waitpid return value */ 135faea1495STim J. Robbins int status; /* Child exit status */ 136364e9ccbSJilles Tjoelker int error; /* Our return value */ 137364e9ccbSJilles Tjoelker int serrno; /* errno to return */ 138faea1495STim J. Robbins char *np, *p; /* Handy pointers */ 139faea1495STim J. Robbins char *nstrings; /* Temporary for realloc() */ 140faea1495STim J. Robbins char **nwv; /* Temporary for realloc() */ 141364e9ccbSJilles Tjoelker sigset_t newsigblock, oldsigblock; 1422f61288cSJilles Tjoelker const char *ifs; 143faea1495STim J. Robbins 144364e9ccbSJilles Tjoelker serrno = errno; 1452f61288cSJilles Tjoelker ifs = getenv("IFS"); 146faea1495STim J. Robbins 147d358fa78SJilles Tjoelker if (pipe2(pdesw, O_CLOEXEC) < 0) 148faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 149d358fa78SJilles Tjoelker snprintf(wfdstr, sizeof(wfdstr), "%d", pdesw[0]); 150d358fa78SJilles Tjoelker if (pipe2(pdes, O_CLOEXEC) < 0) { 151d358fa78SJilles Tjoelker _close(pdesw[0]); 152d358fa78SJilles Tjoelker _close(pdesw[1]); 153d358fa78SJilles Tjoelker return (WRDE_NOSPACE); /* XXX */ 154d358fa78SJilles Tjoelker } 155364e9ccbSJilles Tjoelker (void)sigemptyset(&newsigblock); 156364e9ccbSJilles Tjoelker (void)sigaddset(&newsigblock, SIGCHLD); 157bd6060a1SKonstantin Belousov (void)__libc_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 158faea1495STim J. Robbins if ((pid = fork()) < 0) { 159364e9ccbSJilles Tjoelker serrno = errno; 160d358fa78SJilles Tjoelker _close(pdesw[0]); 161d358fa78SJilles Tjoelker _close(pdesw[1]); 1622005f192STim J. Robbins _close(pdes[0]); 1632005f192STim J. Robbins _close(pdes[1]); 164bd6060a1SKonstantin Belousov (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 165364e9ccbSJilles Tjoelker errno = serrno; 166faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 167faea1495STim J. Robbins } 168faea1495STim J. Robbins else if (pid == 0) { 169faea1495STim J. Robbins /* 170842ad8acSJilles Tjoelker * We are the child; make /bin/sh expand `words'. 171faea1495STim J. Robbins */ 172bd6060a1SKonstantin Belousov (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 173f6d7148dSJilles Tjoelker if ((pdes[1] != STDOUT_FILENO ? 174f6d7148dSJilles Tjoelker _dup2(pdes[1], STDOUT_FILENO) : 175f6d7148dSJilles Tjoelker _fcntl(pdes[1], F_SETFD, 0)) < 0) 176faea1495STim J. Robbins _exit(1); 177d358fa78SJilles Tjoelker if (_fcntl(pdesw[0], F_SETFD, 0) < 0) 178d358fa78SJilles Tjoelker _exit(1); 179faea1495STim J. Robbins execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 180d358fa78SJilles Tjoelker "-c", "IFS=$1;eval \"$2\";" 181d358fa78SJilles Tjoelker "freebsd_wordexp -f \"$3\" ${4:+\"$4\"}", 182842ad8acSJilles Tjoelker "", 1832f61288cSJilles Tjoelker ifs != NULL ? ifs : " \t\n", 184d358fa78SJilles Tjoelker flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", 185d358fa78SJilles Tjoelker wfdstr, 186d358fa78SJilles Tjoelker flags & WRDE_NOCMD ? "-p" : "", 187ae4c676cSJilles Tjoelker (char *)NULL); 188faea1495STim J. Robbins _exit(1); 189faea1495STim J. Robbins } 190faea1495STim J. Robbins 191faea1495STim J. Robbins /* 192d358fa78SJilles Tjoelker * We are the parent; write the words. 193faea1495STim J. Robbins */ 1942005f192STim J. Robbins _close(pdes[1]); 195d358fa78SJilles Tjoelker _close(pdesw[0]); 196d358fa78SJilles Tjoelker if (!we_write_fully(pdesw[1], words, strlen(words))) { 197d358fa78SJilles Tjoelker _close(pdesw[1]); 198d358fa78SJilles Tjoelker error = WRDE_SYNTAX; 199d358fa78SJilles Tjoelker goto cleanup; 200d358fa78SJilles Tjoelker } 201d358fa78SJilles Tjoelker _close(pdesw[1]); 202d358fa78SJilles Tjoelker /* 203d358fa78SJilles Tjoelker * Read the output of the shell wordexp function, 204d358fa78SJilles Tjoelker * which is a byte indicating that the words were parsed successfully, 205d358fa78SJilles Tjoelker * a 64-bit hexadecimal word count, a dummy byte, a 64-bit hexadecimal 206d358fa78SJilles Tjoelker * byte count (not including terminating null bytes), followed by the 207d358fa78SJilles Tjoelker * expanded words separated by nulls. 208d358fa78SJilles Tjoelker */ 209d358fa78SJilles Tjoelker switch (we_read_fully(pdes[0], buf, 34)) { 21089cead33SJilles Tjoelker case 1: 211d358fa78SJilles Tjoelker error = buf[0] == 'C' ? WRDE_CMDSUB : WRDE_BADVAL; 21289cead33SJilles Tjoelker serrno = errno; 21389cead33SJilles Tjoelker goto cleanup; 214d358fa78SJilles Tjoelker case 34: 21589cead33SJilles Tjoelker break; 21689cead33SJilles Tjoelker default: 21789cead33SJilles Tjoelker error = WRDE_SYNTAX; 218364e9ccbSJilles Tjoelker serrno = errno; 219364e9ccbSJilles Tjoelker goto cleanup; 220faea1495STim J. Robbins } 22189cead33SJilles Tjoelker buf[17] = '\0'; 222d358fa78SJilles Tjoelker nwords = strtol(buf + 1, NULL, 16); 223d358fa78SJilles Tjoelker buf[34] = '\0'; 224d358fa78SJilles Tjoelker nbytes = strtol(buf + 18, NULL, 16) + nwords; 225faea1495STim J. Robbins 226faea1495STim J. Robbins /* 227faea1495STim J. Robbins * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 228faea1495STim J. Robbins * and string storage buffers for the expanded words we're about to 229faea1495STim J. Robbins * read from the child. 230faea1495STim J. Robbins */ 231faea1495STim J. Robbins sofs = we->we_nbytes; 232faea1495STim J. Robbins vofs = we->we_wordc; 233b7114d4aSTim J. Robbins if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 234fe634ca7STim J. Robbins vofs += we->we_offs; 235faea1495STim J. Robbins we->we_wordc += nwords; 236faea1495STim J. Robbins we->we_nbytes += nbytes; 237*9f36610fSPedro F. Giffuni if ((nwv = reallocarray(we->we_wordv, (we->we_wordc + 1 + 238*9f36610fSPedro F. Giffuni (flags & WRDE_DOOFFS ? we->we_offs : 0)), 239faea1495STim J. Robbins sizeof(char *))) == NULL) { 240364e9ccbSJilles Tjoelker error = WRDE_NOSPACE; 241364e9ccbSJilles Tjoelker goto cleanup; 242faea1495STim J. Robbins } 243faea1495STim J. Robbins we->we_wordv = nwv; 244faea1495STim J. Robbins if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 245364e9ccbSJilles Tjoelker error = WRDE_NOSPACE; 246364e9ccbSJilles Tjoelker goto cleanup; 247faea1495STim J. Robbins } 248faea1495STim J. Robbins for (i = 0; i < vofs; i++) 249faea1495STim J. Robbins if (we->we_wordv[i] != NULL) 250faea1495STim J. Robbins we->we_wordv[i] += nstrings - we->we_strings; 251faea1495STim J. Robbins we->we_strings = nstrings; 252faea1495STim J. Robbins 253364e9ccbSJilles Tjoelker if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 25489cead33SJilles Tjoelker error = WRDE_NOSPACE; /* abort for unknown reason */ 255364e9ccbSJilles Tjoelker serrno = errno; 256364e9ccbSJilles Tjoelker goto cleanup; 257faea1495STim J. Robbins } 258faea1495STim J. Robbins 259364e9ccbSJilles Tjoelker error = 0; 260364e9ccbSJilles Tjoelker cleanup: 2612005f192STim J. Robbins _close(pdes[0]); 262364e9ccbSJilles Tjoelker do 263364e9ccbSJilles Tjoelker wpid = _waitpid(pid, &status, 0); 264364e9ccbSJilles Tjoelker while (wpid < 0 && errno == EINTR); 265bd6060a1SKonstantin Belousov (void)__libc_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 266364e9ccbSJilles Tjoelker if (error != 0) { 267364e9ccbSJilles Tjoelker errno = serrno; 268364e9ccbSJilles Tjoelker return (error); 269faea1495STim J. Robbins } 270364e9ccbSJilles Tjoelker if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) 27189cead33SJilles Tjoelker return (WRDE_NOSPACE); /* abort for unknown reason */ 272faea1495STim J. Robbins 273faea1495STim J. Robbins /* 274faea1495STim J. Robbins * Break the null-terminated expanded word strings out into 275faea1495STim J. Robbins * the vector. 276faea1495STim J. Robbins */ 277b7114d4aSTim J. Robbins if (vofs == 0 && flags & WRDE_DOOFFS) 278faea1495STim J. Robbins while (vofs < we->we_offs) 279faea1495STim J. Robbins we->we_wordv[vofs++] = NULL; 280faea1495STim J. Robbins p = we->we_strings + sofs; 281faea1495STim J. Robbins while (nwords-- != 0) { 282faea1495STim J. Robbins we->we_wordv[vofs++] = p; 283faea1495STim J. Robbins if ((np = memchr(p, '\0', nbytes)) == NULL) 284faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 285faea1495STim J. Robbins nbytes -= np - p + 1; 286faea1495STim J. Robbins p = np + 1; 287faea1495STim J. Robbins } 288faea1495STim J. Robbins we->we_wordv[vofs] = NULL; 289faea1495STim J. Robbins 290faea1495STim J. Robbins return (0); 291faea1495STim J. Robbins } 292faea1495STim J. Robbins 293faea1495STim J. Robbins /* 294faea1495STim J. Robbins * we_check -- 295faea1495STim J. Robbins * Check that the string contains none of the following unquoted 296faea1495STim J. Robbins * special characters: <newline> |&;<>(){} 297d358fa78SJilles Tjoelker * This mainly serves for {} which are normally legal in sh. 298d358fa78SJilles Tjoelker * It deliberately does not attempt to model full sh syntax. 299faea1495STim J. Robbins */ 30097c1c8f8STim J. Robbins static int 301d358fa78SJilles Tjoelker we_check(const char *words) 302faea1495STim J. Robbins { 303faea1495STim J. Robbins char c; 304d358fa78SJilles Tjoelker /* Saw \ or $, possibly not special: */ 305d358fa78SJilles Tjoelker bool quote = false, dollar = false; 306d358fa78SJilles Tjoelker /* Saw ', ", ${, ` or $(, possibly not special: */ 307d358fa78SJilles Tjoelker bool have_sq = false, have_dq = false, have_par_begin = false; 308d358fa78SJilles Tjoelker bool have_cmd = false; 309d358fa78SJilles Tjoelker /* Definitely saw a ', ", ${, ` or $(, need a closing character: */ 310d358fa78SJilles Tjoelker bool need_sq = false, need_dq = false, need_par_end = false; 311d358fa78SJilles Tjoelker bool need_cmd_old = false, need_cmd_new = false; 312faea1495STim J. Robbins 313faea1495STim J. Robbins while ((c = *words++) != '\0') { 314faea1495STim J. Robbins switch (c) { 315faea1495STim J. Robbins case '\\': 316d358fa78SJilles Tjoelker quote = !quote; 317d358fa78SJilles Tjoelker continue; 318d358fa78SJilles Tjoelker case '$': 319d358fa78SJilles Tjoelker if (quote) 320d358fa78SJilles Tjoelker quote = false; 321d358fa78SJilles Tjoelker else 322d358fa78SJilles Tjoelker dollar = !dollar; 323fe634ca7STim J. Robbins continue; 324faea1495STim J. Robbins case '\'': 325d358fa78SJilles Tjoelker if (!quote && !have_sq && !have_dq) 326d358fa78SJilles Tjoelker need_sq = true; 327d358fa78SJilles Tjoelker else 328d358fa78SJilles Tjoelker need_sq = false; 329d358fa78SJilles Tjoelker have_sq = true; 330faea1495STim J. Robbins break; 331faea1495STim J. Robbins case '"': 332d358fa78SJilles Tjoelker if (!quote && !have_sq && !have_dq) 333d358fa78SJilles Tjoelker need_dq = true; 334d358fa78SJilles Tjoelker else 335d358fa78SJilles Tjoelker need_dq = false; 336d358fa78SJilles Tjoelker have_dq = true; 337faea1495STim J. Robbins break; 338faea1495STim J. Robbins case '`': 339d358fa78SJilles Tjoelker if (!quote && !have_sq && !have_cmd) 340d358fa78SJilles Tjoelker need_cmd_old = true; 341d358fa78SJilles Tjoelker else 342d358fa78SJilles Tjoelker need_cmd_old = false; 343d358fa78SJilles Tjoelker have_cmd = true; 344faea1495STim J. Robbins break; 345d358fa78SJilles Tjoelker case '{': 346d358fa78SJilles Tjoelker if (!quote && !dollar && !have_sq && !have_dq && 347d358fa78SJilles Tjoelker !have_cmd) 348faea1495STim J. Robbins return (WRDE_BADCHAR); 349d358fa78SJilles Tjoelker if (dollar) { 350d358fa78SJilles Tjoelker if (!quote && !have_sq) 351d358fa78SJilles Tjoelker need_par_end = true; 352d358fa78SJilles Tjoelker have_par_begin = true; 353faea1495STim J. Robbins } 354faea1495STim J. Robbins break; 355d358fa78SJilles Tjoelker case '}': 356d358fa78SJilles Tjoelker if (!quote && !have_sq && !have_dq && !have_par_begin && 357d358fa78SJilles Tjoelker !have_cmd) 358d358fa78SJilles Tjoelker return (WRDE_BADCHAR); 359d358fa78SJilles Tjoelker need_par_end = false; 360faea1495STim J. Robbins break; 361d358fa78SJilles Tjoelker case '(': 362d358fa78SJilles Tjoelker if (!quote && !dollar && !have_sq && !have_dq && 363d358fa78SJilles Tjoelker !have_cmd) 364d358fa78SJilles Tjoelker return (WRDE_BADCHAR); 365d358fa78SJilles Tjoelker if (dollar) { 366d358fa78SJilles Tjoelker if (!quote && !have_sq) 367d358fa78SJilles Tjoelker need_cmd_new = true; 368d358fa78SJilles Tjoelker have_cmd = true; 369faea1495STim J. Robbins } 370d358fa78SJilles Tjoelker break; 371d358fa78SJilles Tjoelker case ')': 372d358fa78SJilles Tjoelker if (!quote && !have_sq && !have_dq && !have_cmd) 373d358fa78SJilles Tjoelker return (WRDE_BADCHAR); 374d358fa78SJilles Tjoelker need_cmd_new = false; 375d358fa78SJilles Tjoelker break; 376d358fa78SJilles Tjoelker case '|': case '&': case ';': case '<': case '>': case '\n': 377d358fa78SJilles Tjoelker if (!quote && !have_sq && !have_dq && !have_cmd) 378d358fa78SJilles Tjoelker return (WRDE_BADCHAR); 379faea1495STim J. Robbins break; 380faea1495STim J. Robbins default: 381faea1495STim J. Robbins break; 382faea1495STim J. Robbins } 383d358fa78SJilles Tjoelker quote = dollar = false; 384faea1495STim J. Robbins } 385d358fa78SJilles Tjoelker if (quote || dollar || need_sq || need_dq || need_par_end || 386d358fa78SJilles Tjoelker need_cmd_old || need_cmd_new) 387faea1495STim J. Robbins return (WRDE_SYNTAX); 388faea1495STim J. Robbins 389faea1495STim J. Robbins return (0); 390faea1495STim J. Robbins } 391faea1495STim J. Robbins 392faea1495STim J. Robbins /* 393faea1495STim J. Robbins * wordfree -- 394faea1495STim J. Robbins * Free the result of wordexp(). See wordexp(3). 395faea1495STim J. Robbins * 396faea1495STim J. Robbins * Specified by IEEE Std. 1003.1-2001. 397faea1495STim J. Robbins */ 398faea1495STim J. Robbins void 399faea1495STim J. Robbins wordfree(wordexp_t *we) 400faea1495STim J. Robbins { 401faea1495STim J. Robbins 402faea1495STim J. Robbins if (we == NULL) 403faea1495STim J. Robbins return; 404faea1495STim J. Robbins free(we->we_wordv); 405faea1495STim J. Robbins free(we->we_strings); 406faea1495STim J. Robbins we->we_wordv = NULL; 407faea1495STim J. Robbins we->we_strings = NULL; 408faea1495STim J. Robbins we->we_nbytes = 0; 409faea1495STim J. Robbins we->we_wordc = 0; 410faea1495STim J. Robbins } 411