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> 31faea1495STim J. Robbins #include <fcntl.h> 32faea1495STim J. Robbins #include <paths.h> 33faea1495STim J. Robbins #include <stdio.h> 34faea1495STim J. Robbins #include <stdlib.h> 35faea1495STim J. Robbins #include <string.h> 36faea1495STim J. Robbins #include <unistd.h> 37faea1495STim J. Robbins #include <wordexp.h> 38faea1495STim J. Robbins #include "un-namespace.h" 39faea1495STim J. Robbins 40faea1495STim J. Robbins __FBSDID("$FreeBSD$"); 41faea1495STim J. Robbins 42faea1495STim J. Robbins static int we_askshell(const char *, wordexp_t *, int); 43faea1495STim J. Robbins static int we_check(const char *, int); 44faea1495STim J. Robbins 45faea1495STim J. Robbins /* 46faea1495STim J. Robbins * wordexp -- 47faea1495STim J. Robbins * Perform shell word expansion on `words' and place the resulting list 48faea1495STim J. Robbins * of words in `we'. See wordexp(3). 49faea1495STim J. Robbins * 50faea1495STim J. Robbins * Specified by IEEE Std. 1003.1-2001. 51faea1495STim J. Robbins */ 52faea1495STim J. Robbins int 53faea1495STim J. Robbins wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags) 54faea1495STim J. Robbins { 55faea1495STim J. Robbins int error; 56faea1495STim J. Robbins 57faea1495STim J. Robbins if (flags & WRDE_REUSE) 58faea1495STim J. Robbins wordfree(we); 59faea1495STim J. Robbins if ((flags & WRDE_APPEND) == 0) { 60faea1495STim J. Robbins we->we_wordc = 0; 61faea1495STim J. Robbins we->we_wordv = NULL; 62faea1495STim J. Robbins we->we_strings = NULL; 63faea1495STim J. Robbins we->we_nbytes = 0; 64faea1495STim J. Robbins } 65faea1495STim J. Robbins if ((error = we_check(words, flags)) != 0) { 66faea1495STim J. Robbins wordfree(we); 67faea1495STim J. Robbins return (error); 68faea1495STim J. Robbins } 69faea1495STim J. Robbins if ((error = we_askshell(words, we, flags)) != 0) { 70faea1495STim J. Robbins wordfree(we); 71faea1495STim J. Robbins return (error); 72faea1495STim J. Robbins } 73faea1495STim J. Robbins return (0); 74faea1495STim J. Robbins } 75faea1495STim J. Robbins 76faea1495STim J. Robbins /* 77faea1495STim J. Robbins * we_askshell -- 78faea1495STim J. Robbins * Use the `wordexp' /bin/sh builtin function to do most of the work 79faea1495STim J. Robbins * in expanding the word string. This function is complicated by 80faea1495STim J. Robbins * memory management. 81faea1495STim J. Robbins */ 82faea1495STim J. Robbins static int 83faea1495STim J. Robbins we_askshell(const char *words, wordexp_t *we, int flags) 84faea1495STim J. Robbins { 85faea1495STim J. Robbins int pdes[2]; /* Pipe to child */ 86faea1495STim J. Robbins char bbuf[9]; /* Buffer for byte count */ 87faea1495STim J. Robbins char wbuf[9]; /* Buffer for word count */ 88faea1495STim J. Robbins long nwords, nbytes; /* Number of words, bytes from child */ 89faea1495STim J. Robbins long i; /* Handy integer */ 90faea1495STim J. Robbins size_t sofs; /* Offset into we->we_strings */ 91faea1495STim J. Robbins size_t vofs; /* Offset into we->we_wordv */ 92faea1495STim J. Robbins pid_t pid; /* Process ID of child */ 93faea1495STim J. Robbins int status; /* Child exit status */ 94faea1495STim J. Robbins char *ifs; /* IFS env. var. */ 95faea1495STim J. Robbins char *np, *p; /* Handy pointers */ 96faea1495STim J. Robbins char *nstrings; /* Temporary for realloc() */ 97faea1495STim J. Robbins char **nwv; /* Temporary for realloc() */ 98faea1495STim J. Robbins 99faea1495STim J. Robbins if ((ifs = getenv("IFS")) == NULL) 100faea1495STim J. Robbins ifs = " \t\n"; 101faea1495STim J. Robbins 102faea1495STim J. Robbins if (pipe(pdes) < 0) 103faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 104faea1495STim J. Robbins if ((pid = fork()) < 0) { 105faea1495STim J. Robbins close(pdes[0]); 106faea1495STim J. Robbins close(pdes[1]); 107faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 108faea1495STim J. Robbins } 109faea1495STim J. Robbins else if (pid == 0) { 110faea1495STim J. Robbins /* 111faea1495STim J. Robbins * We are the child; just get /bin/sh to run the wordexp 112faea1495STim J. Robbins * builtin on `words'. 113faea1495STim J. Robbins */ 114faea1495STim J. Robbins int devnull; 115faea1495STim J. Robbins char *cmd; 116faea1495STim J. Robbins 117faea1495STim J. Robbins close(pdes[0]); 118faea1495STim J. Robbins if (dup2(pdes[1], STDOUT_FILENO) < 0) 119faea1495STim J. Robbins _exit(1); 120faea1495STim J. Robbins close(pdes[1]); 121faea1495STim J. Robbins if (asprintf(&cmd, "wordexp%c%s\n", *ifs, words) < 0) 122faea1495STim J. Robbins _exit(1); 123faea1495STim J. Robbins if ((flags & WRDE_SHOWERR) == 0) { 124faea1495STim J. Robbins if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0666)) < 0) 125faea1495STim J. Robbins _exit(1); 126faea1495STim J. Robbins if (dup2(devnull, STDERR_FILENO) < 0) 127faea1495STim J. Robbins _exit(1); 128faea1495STim J. Robbins close(devnull); 129faea1495STim J. Robbins } 130faea1495STim J. Robbins execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 131faea1495STim J. Robbins "-c", cmd, NULL); 132faea1495STim J. Robbins _exit(1); 133faea1495STim J. Robbins } 134faea1495STim J. Robbins 135faea1495STim J. Robbins /* 136faea1495STim J. Robbins * We are the parent; read the output of the shell wordexp function, 137faea1495STim J. Robbins * which is a 32-bit hexadecimal word count, a 32-bit hexadecimal 138faea1495STim J. Robbins * byte count (not including terminating null bytes), followed by 139faea1495STim J. Robbins * the expanded words separated by nulls. 140faea1495STim J. Robbins */ 141faea1495STim J. Robbins close(pdes[1]); 142faea1495STim J. Robbins if (read(pdes[0], wbuf, 8) != 8 || read(pdes[0], bbuf, 8) != 8) { 143faea1495STim J. Robbins close(pdes[0]); 144faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 145faea1495STim J. Robbins } 146faea1495STim J. Robbins wbuf[8] = bbuf[8] = '\0'; 147faea1495STim J. Robbins nwords = strtol(wbuf, NULL, 16); 148faea1495STim J. Robbins nbytes = strtol(bbuf, NULL, 16) + nwords; 149faea1495STim J. Robbins 150faea1495STim J. Robbins /* 151faea1495STim J. Robbins * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 152faea1495STim J. Robbins * and string storage buffers for the expanded words we're about to 153faea1495STim J. Robbins * read from the child. 154faea1495STim J. Robbins */ 155faea1495STim J. Robbins sofs = we->we_nbytes; 156faea1495STim J. Robbins vofs = we->we_wordc; 157faea1495STim J. Robbins we->we_wordc += nwords; 158faea1495STim J. Robbins we->we_nbytes += nbytes; 159faea1495STim J. Robbins if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + 160faea1495STim J. Robbins (flags & WRDE_DOOFS ? we->we_offs : 0)) * 161faea1495STim J. Robbins sizeof(char *))) == NULL) { 162faea1495STim J. Robbins close(pdes[0]); 163faea1495STim J. Robbins return (WRDE_NOSPACE); 164faea1495STim J. Robbins } 165faea1495STim J. Robbins we->we_wordv = nwv; 166faea1495STim J. Robbins if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 167faea1495STim J. Robbins close(pdes[0]); 168faea1495STim J. Robbins return (WRDE_NOSPACE); 169faea1495STim J. Robbins } 170faea1495STim J. Robbins for (i = 0; i < vofs; i++) 171faea1495STim J. Robbins if (we->we_wordv[i] != NULL) 172faea1495STim J. Robbins we->we_wordv[i] += nstrings - we->we_strings; 173faea1495STim J. Robbins we->we_strings = nstrings; 174faea1495STim J. Robbins 175faea1495STim J. Robbins if (read(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 176faea1495STim J. Robbins close(pdes[0]); 177faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 178faea1495STim J. Robbins } 179faea1495STim J. Robbins 180faea1495STim J. Robbins if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || 181faea1495STim J. Robbins WEXITSTATUS(status) != 0) { 182faea1495STim J. Robbins close(pdes[0]); 183faea1495STim J. Robbins return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); 184faea1495STim J. Robbins } 185faea1495STim J. Robbins close(pdes[0]); 186faea1495STim J. Robbins 187faea1495STim J. Robbins /* 188faea1495STim J. Robbins * Break the null-terminated expanded word strings out into 189faea1495STim J. Robbins * the vector. 190faea1495STim J. Robbins */ 191faea1495STim J. Robbins if (vofs == 0 && flags & WRDE_DOOFS) 192faea1495STim J. Robbins while (vofs < we->we_offs) 193faea1495STim J. Robbins we->we_wordv[vofs++] = NULL; 194faea1495STim J. Robbins p = we->we_strings + sofs; 195faea1495STim J. Robbins while (nwords-- != 0) { 196faea1495STim J. Robbins we->we_wordv[vofs++] = p; 197faea1495STim J. Robbins if ((np = memchr(p, '\0', nbytes)) == NULL) 198faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 199faea1495STim J. Robbins nbytes -= np - p + 1; 200faea1495STim J. Robbins p = np + 1; 201faea1495STim J. Robbins } 202faea1495STim J. Robbins we->we_wordv[vofs] = NULL; 203faea1495STim J. Robbins 204faea1495STim J. Robbins return (0); 205faea1495STim J. Robbins } 206faea1495STim J. Robbins 207faea1495STim J. Robbins /* 208faea1495STim J. Robbins * we_check -- 209faea1495STim J. Robbins * Check that the string contains none of the following unquoted 210faea1495STim J. Robbins * special characters: <newline> |&;<>(){} 211faea1495STim J. Robbins * or command substitutions when WRDE_NOCMD is set in flags. 212faea1495STim J. Robbins */ 213faea1495STim J. Robbins int 214faea1495STim J. Robbins we_check(const char *words, int flags) 215faea1495STim J. Robbins { 216faea1495STim J. Robbins char c; 217faea1495STim J. Robbins int dquote, level, quote, squote; 218faea1495STim J. Robbins 219faea1495STim J. Robbins quote = squote = dquote = 0; 220faea1495STim J. Robbins while ((c = *words++) != '\0') { 221faea1495STim J. Robbins switch (c) { 222faea1495STim J. Robbins case '\\': 223faea1495STim J. Robbins quote ^= 1; 224faea1495STim J. Robbins break; 225faea1495STim J. Robbins case '\'': 226faea1495STim J. Robbins if (quote + dquote == 0) 227faea1495STim J. Robbins squote ^= 1; 228faea1495STim J. Robbins break; 229faea1495STim J. Robbins case '"': 230faea1495STim J. Robbins if (quote + squote == 0) 231faea1495STim J. Robbins dquote ^= 1; 232faea1495STim J. Robbins break; 233faea1495STim J. Robbins case '`': 234faea1495STim J. Robbins if (quote + squote == 0 && flags & WRDE_NOCMD) 235faea1495STim J. Robbins return (WRDE_CMDSUB); 236faea1495STim J. Robbins while ((c = *words++) != '\0' && c != '`') 237faea1495STim J. Robbins if (c == '\\' && (c = *words++) == '\0') 238faea1495STim J. Robbins break; 239faea1495STim J. Robbins if (c == '\0') 240faea1495STim J. Robbins return (WRDE_SYNTAX); 241faea1495STim J. Robbins break; 242faea1495STim J. Robbins case '|': case '&': case ';': case '<': case '>': 243faea1495STim J. Robbins case '{': case '}': case '(': case ')': case '\n': 244faea1495STim J. Robbins if (quote + squote + dquote == 0) 245faea1495STim J. Robbins return (WRDE_BADCHAR); 246faea1495STim J. Robbins break; 247faea1495STim J. Robbins case '$': 248faea1495STim J. Robbins if ((c = *words++) == '\0') 249faea1495STim J. Robbins break; 250faea1495STim J. Robbins else if (c == '(') { 251faea1495STim J. Robbins if (flags & WRDE_NOCMD) 252faea1495STim J. Robbins return (WRDE_CMDSUB); 253faea1495STim J. Robbins level = 1; 254faea1495STim J. Robbins while ((c = *words++) != '\0') { 255faea1495STim J. Robbins if (c == '\\') { 256faea1495STim J. Robbins if ((c = *words++) == '\0') 257faea1495STim J. Robbins break; 258faea1495STim J. Robbins } else if (c == '(') 259faea1495STim J. Robbins level++; 260faea1495STim J. Robbins else if (c == ')' && --level == 0) 261faea1495STim J. Robbins break; 262faea1495STim J. Robbins } 263faea1495STim J. Robbins if (c == '\0' || level != 0) 264faea1495STim J. Robbins return (WRDE_SYNTAX); 265faea1495STim J. Robbins } else if (c == '{') { 266faea1495STim J. Robbins level = 1; 267faea1495STim J. Robbins while ((c = *words++) != '\0') { 268faea1495STim J. Robbins if (c == '\\') { 269faea1495STim J. Robbins if ((c = *words++) == '\0') 270faea1495STim J. Robbins break; 271faea1495STim J. Robbins } else if (c == '{') 272faea1495STim J. Robbins level++; 273faea1495STim J. Robbins else if (c == '}' && --level == 0) 274faea1495STim J. Robbins break; 275faea1495STim J. Robbins } 276faea1495STim J. Robbins if (c == '\0' || level != 0) 277faea1495STim J. Robbins return (WRDE_SYNTAX); 278faea1495STim J. Robbins } 279faea1495STim J. Robbins break; 280faea1495STim J. Robbins default: 281faea1495STim J. Robbins break; 282faea1495STim J. Robbins } 283faea1495STim J. Robbins } 284faea1495STim J. Robbins if (quote + squote + dquote != 0) 285faea1495STim J. Robbins return (WRDE_SYNTAX); 286faea1495STim J. Robbins 287faea1495STim J. Robbins return (0); 288faea1495STim J. Robbins } 289faea1495STim J. Robbins 290faea1495STim J. Robbins /* 291faea1495STim J. Robbins * wordfree -- 292faea1495STim J. Robbins * Free the result of wordexp(). See wordexp(3). 293faea1495STim J. Robbins * 294faea1495STim J. Robbins * Specified by IEEE Std. 1003.1-2001. 295faea1495STim J. Robbins */ 296faea1495STim J. Robbins void 297faea1495STim J. Robbins wordfree(wordexp_t *we) 298faea1495STim J. Robbins { 299faea1495STim J. Robbins 300faea1495STim J. Robbins if (we == NULL) 301faea1495STim J. Robbins return; 302faea1495STim J. Robbins free(we->we_wordv); 303faea1495STim J. Robbins free(we->we_strings); 304faea1495STim J. Robbins we->we_wordv = NULL; 305faea1495STim J. Robbins we->we_strings = NULL; 306faea1495STim J. Robbins we->we_nbytes = 0; 307faea1495STim J. Robbins we->we_wordc = 0; 308faea1495STim J. Robbins } 309