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) { 1052005f192STim J. Robbins _close(pdes[0]); 1062005f192STim 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 1172005f192STim J. Robbins _close(pdes[0]); 1182005f192STim J. Robbins if (_dup2(pdes[1], STDOUT_FILENO) < 0) 119faea1495STim J. Robbins _exit(1); 1202005f192STim 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) { 1242005f192STim J. Robbins if ((devnull = _open(_PATH_DEVNULL, O_RDWR, 0666)) < 0) 125faea1495STim J. Robbins _exit(1); 1262005f192STim J. Robbins if (_dup2(devnull, STDERR_FILENO) < 0) 127faea1495STim J. Robbins _exit(1); 1282005f192STim J. Robbins _close(devnull); 129faea1495STim J. Robbins } 130faea1495STim J. Robbins execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u", 1317937c23dSTim J. Robbins "-c", cmd, (char *)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 */ 1412005f192STim J. Robbins _close(pdes[1]); 1422005f192STim J. Robbins if (_read(pdes[0], wbuf, 8) != 8 || _read(pdes[0], bbuf, 8) != 8) { 1432005f192STim J. Robbins _close(pdes[0]); 1442005f192STim J. Robbins _waitpid(pid, &status, 0); 145fe634ca7STim J. Robbins return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); 146faea1495STim J. Robbins } 147faea1495STim J. Robbins wbuf[8] = bbuf[8] = '\0'; 148faea1495STim J. Robbins nwords = strtol(wbuf, NULL, 16); 149faea1495STim J. Robbins nbytes = strtol(bbuf, NULL, 16) + nwords; 150faea1495STim J. Robbins 151faea1495STim J. Robbins /* 152faea1495STim J. Robbins * Allocate or reallocate (when flags & WRDE_APPEND) the word vector 153faea1495STim J. Robbins * and string storage buffers for the expanded words we're about to 154faea1495STim J. Robbins * read from the child. 155faea1495STim J. Robbins */ 156faea1495STim J. Robbins sofs = we->we_nbytes; 157faea1495STim J. Robbins vofs = we->we_wordc; 158b7114d4aSTim J. Robbins if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND)) 159fe634ca7STim J. Robbins vofs += we->we_offs; 160faea1495STim J. Robbins we->we_wordc += nwords; 161faea1495STim J. Robbins we->we_nbytes += nbytes; 162faea1495STim J. Robbins if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 + 163b7114d4aSTim J. Robbins (flags & WRDE_DOOFFS ? we->we_offs : 0)) * 164faea1495STim J. Robbins sizeof(char *))) == NULL) { 1652005f192STim J. Robbins _close(pdes[0]); 1662005f192STim J. Robbins _waitpid(pid, &status, 0); 167faea1495STim J. Robbins return (WRDE_NOSPACE); 168faea1495STim J. Robbins } 169faea1495STim J. Robbins we->we_wordv = nwv; 170faea1495STim J. Robbins if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) { 1712005f192STim J. Robbins _close(pdes[0]); 1722005f192STim J. Robbins _waitpid(pid, &status, 0); 173faea1495STim J. Robbins return (WRDE_NOSPACE); 174faea1495STim J. Robbins } 175faea1495STim J. Robbins for (i = 0; i < vofs; i++) 176faea1495STim J. Robbins if (we->we_wordv[i] != NULL) 177faea1495STim J. Robbins we->we_wordv[i] += nstrings - we->we_strings; 178faea1495STim J. Robbins we->we_strings = nstrings; 179faea1495STim J. Robbins 1802005f192STim J. Robbins if (_read(pdes[0], we->we_strings + sofs, nbytes) != nbytes) { 1812005f192STim J. Robbins _close(pdes[0]); 1822005f192STim J. Robbins _waitpid(pid, &status, 0); 183fe634ca7STim J. Robbins return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); 184faea1495STim J. Robbins } 185faea1495STim J. Robbins 1862005f192STim J. Robbins if (_waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) || 187faea1495STim J. Robbins WEXITSTATUS(status) != 0) { 1882005f192STim J. Robbins _close(pdes[0]); 189faea1495STim J. Robbins return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX); 190faea1495STim J. Robbins } 1912005f192STim J. Robbins _close(pdes[0]); 192faea1495STim J. Robbins 193faea1495STim J. Robbins /* 194faea1495STim J. Robbins * Break the null-terminated expanded word strings out into 195faea1495STim J. Robbins * the vector. 196faea1495STim J. Robbins */ 197b7114d4aSTim J. Robbins if (vofs == 0 && flags & WRDE_DOOFFS) 198faea1495STim J. Robbins while (vofs < we->we_offs) 199faea1495STim J. Robbins we->we_wordv[vofs++] = NULL; 200faea1495STim J. Robbins p = we->we_strings + sofs; 201faea1495STim J. Robbins while (nwords-- != 0) { 202faea1495STim J. Robbins we->we_wordv[vofs++] = p; 203faea1495STim J. Robbins if ((np = memchr(p, '\0', nbytes)) == NULL) 204faea1495STim J. Robbins return (WRDE_NOSPACE); /* XXX */ 205faea1495STim J. Robbins nbytes -= np - p + 1; 206faea1495STim J. Robbins p = np + 1; 207faea1495STim J. Robbins } 208faea1495STim J. Robbins we->we_wordv[vofs] = NULL; 209faea1495STim J. Robbins 210faea1495STim J. Robbins return (0); 211faea1495STim J. Robbins } 212faea1495STim J. Robbins 213faea1495STim J. Robbins /* 214faea1495STim J. Robbins * we_check -- 215faea1495STim J. Robbins * Check that the string contains none of the following unquoted 216faea1495STim J. Robbins * special characters: <newline> |&;<>(){} 217faea1495STim J. Robbins * or command substitutions when WRDE_NOCMD is set in flags. 218faea1495STim J. Robbins */ 21997c1c8f8STim J. Robbins static int 220faea1495STim J. Robbins we_check(const char *words, int flags) 221faea1495STim J. Robbins { 222faea1495STim J. Robbins char c; 223faea1495STim J. Robbins int dquote, level, quote, squote; 224faea1495STim J. Robbins 225faea1495STim J. Robbins quote = squote = dquote = 0; 226faea1495STim J. Robbins while ((c = *words++) != '\0') { 227faea1495STim J. Robbins switch (c) { 228faea1495STim J. Robbins case '\\': 229faea1495STim J. Robbins quote ^= 1; 230fe634ca7STim J. Robbins continue; 231faea1495STim J. Robbins case '\'': 232faea1495STim J. Robbins if (quote + dquote == 0) 233faea1495STim J. Robbins squote ^= 1; 234faea1495STim J. Robbins break; 235faea1495STim J. Robbins case '"': 236faea1495STim J. Robbins if (quote + squote == 0) 237faea1495STim J. Robbins dquote ^= 1; 238faea1495STim J. Robbins break; 239faea1495STim J. Robbins case '`': 240faea1495STim J. Robbins if (quote + squote == 0 && flags & WRDE_NOCMD) 241faea1495STim J. Robbins return (WRDE_CMDSUB); 242faea1495STim J. Robbins while ((c = *words++) != '\0' && c != '`') 243faea1495STim J. Robbins if (c == '\\' && (c = *words++) == '\0') 244faea1495STim J. Robbins break; 245faea1495STim J. Robbins if (c == '\0') 246faea1495STim J. Robbins return (WRDE_SYNTAX); 247faea1495STim J. Robbins break; 248faea1495STim J. Robbins case '|': case '&': case ';': case '<': case '>': 249faea1495STim J. Robbins case '{': case '}': case '(': case ')': case '\n': 250faea1495STim J. Robbins if (quote + squote + dquote == 0) 251faea1495STim J. Robbins return (WRDE_BADCHAR); 252faea1495STim J. Robbins break; 253faea1495STim J. Robbins case '$': 254faea1495STim J. Robbins if ((c = *words++) == '\0') 255faea1495STim J. Robbins break; 256fe634ca7STim J. Robbins else if (quote + squote == 0 && c == '(') { 257fe634ca7STim J. Robbins if (flags & WRDE_NOCMD && *words != '(') 258faea1495STim J. Robbins return (WRDE_CMDSUB); 259faea1495STim J. Robbins level = 1; 260faea1495STim J. Robbins while ((c = *words++) != '\0') { 261faea1495STim J. Robbins if (c == '\\') { 262faea1495STim J. Robbins if ((c = *words++) == '\0') 263faea1495STim J. Robbins break; 264faea1495STim J. Robbins } else if (c == '(') 265faea1495STim J. Robbins level++; 266faea1495STim J. Robbins else if (c == ')' && --level == 0) 267faea1495STim J. Robbins break; 268faea1495STim J. Robbins } 269faea1495STim J. Robbins if (c == '\0' || level != 0) 270faea1495STim J. Robbins return (WRDE_SYNTAX); 271fe634ca7STim J. Robbins } else if (quote + squote == 0 && c == '{') { 272faea1495STim J. Robbins level = 1; 273faea1495STim J. Robbins while ((c = *words++) != '\0') { 274faea1495STim J. Robbins if (c == '\\') { 275faea1495STim J. Robbins if ((c = *words++) == '\0') 276faea1495STim J. Robbins break; 277faea1495STim J. Robbins } else if (c == '{') 278faea1495STim J. Robbins level++; 279faea1495STim J. Robbins else if (c == '}' && --level == 0) 280faea1495STim J. Robbins break; 281faea1495STim J. Robbins } 282faea1495STim J. Robbins if (c == '\0' || level != 0) 283faea1495STim J. Robbins return (WRDE_SYNTAX); 284fe634ca7STim J. Robbins } else 285fe634ca7STim J. Robbins c = *--words; 286faea1495STim J. Robbins break; 287faea1495STim J. Robbins default: 288faea1495STim J. Robbins break; 289faea1495STim J. Robbins } 290fe634ca7STim J. Robbins quote = 0; 291faea1495STim J. Robbins } 292faea1495STim J. Robbins if (quote + squote + dquote != 0) 293faea1495STim J. Robbins return (WRDE_SYNTAX); 294faea1495STim J. Robbins 295faea1495STim J. Robbins return (0); 296faea1495STim J. Robbins } 297faea1495STim J. Robbins 298faea1495STim J. Robbins /* 299faea1495STim J. Robbins * wordfree -- 300faea1495STim J. Robbins * Free the result of wordexp(). See wordexp(3). 301faea1495STim J. Robbins * 302faea1495STim J. Robbins * Specified by IEEE Std. 1003.1-2001. 303faea1495STim J. Robbins */ 304faea1495STim J. Robbins void 305faea1495STim J. Robbins wordfree(wordexp_t *we) 306faea1495STim J. Robbins { 307faea1495STim J. Robbins 308faea1495STim J. Robbins if (we == NULL) 309faea1495STim J. Robbins return; 310faea1495STim J. Robbins free(we->we_wordv); 311faea1495STim J. Robbins free(we->we_strings); 312faea1495STim J. Robbins we->we_wordv = NULL; 313faea1495STim J. Robbins we->we_strings = NULL; 314faea1495STim J. Robbins we->we_nbytes = 0; 315faea1495STim J. Robbins we->we_wordc = 0; 316faea1495STim J. Robbins } 317