1#include <wordexp.h> 2#include <unistd.h> 3#include <stdio.h> 4#include <string.h> 5#include <limits.h> 6#include <stdint.h> 7#include <stdlib.h> 8#include <sys/wait.h> 9#include <signal.h> 10#include <errno.h> 11#include <fcntl.h> 12#include "pthread_impl.h" 13 14static void reap(pid_t pid) 15{ 16 int status; 17 for (;;) { 18 if (waitpid(pid, &status, 0) < 0) { 19 if (errno != EINTR) return; 20 } else { 21 if (WIFEXITED(status)) return; 22 } 23 } 24} 25 26static char *getword(FILE *f) 27{ 28 char *s = 0; 29 return getdelim(&s, (size_t [1]){0}, 0, f) < 0 ? 0 : s; 30} 31 32static int do_wordexp(const char *s, wordexp_t *we, int flags) 33{ 34 size_t i, l; 35 int sq=0, dq=0; 36 size_t np=0; 37 char *w, **tmp; 38 char *redir = (flags & WRDE_SHOWERR) ? "" : "2>/dev/null"; 39 int err = 0; 40 FILE *f; 41 size_t wc = 0; 42 char **wv = 0; 43 int p[2]; 44 pid_t pid; 45 sigset_t set; 46 47 if (flags & WRDE_REUSE) wordfree(we); 48 49 if (flags & WRDE_NOCMD) for (i=0; s[i]; i++) switch (s[i]) { 50 case '\\': 51 if (!sq) i++; 52 break; 53 case '\'': 54 if (!dq) sq^=1; 55 break; 56 case '"': 57 if (!sq) dq^=1; 58 break; 59 case '(': 60 if (np) { 61 np++; 62 break; 63 } 64 case ')': 65 if (np) { 66 np--; 67 break; 68 } 69 case '\n': 70 case '|': 71 case '&': 72 case ';': 73 case '<': 74 case '>': 75 case '{': 76 case '}': 77 if (!(sq|dq|np)) return WRDE_BADCHAR; 78 break; 79 case '$': 80 if (sq) break; 81 if (s[i+1]=='(' && s[i+2]=='(') { 82 i += 2; 83 np += 2; 84 break; 85 } else if (s[i+1] != '(') break; 86 case '`': 87 if (sq) break; 88 return WRDE_CMDSUB; 89 } 90 91 if (flags & WRDE_APPEND) { 92 wc = we->we_wordc; 93 wv = we->we_wordv; 94 } 95 96 i = wc; 97 if (flags & WRDE_DOOFFS) { 98 if (we->we_offs > SIZE_MAX/sizeof(void *)/4) 99 goto nospace; 100 i += we->we_offs; 101 } else { 102 we->we_offs = 0; 103 } 104 105 if (pipe2(p, O_CLOEXEC) < 0) goto nospace; 106 __block_all_sigs(&set); 107 pid = fork(); 108 __restore_sigs(&set); 109 if (pid < 0) { 110 close(p[0]); 111 close(p[1]); 112 goto nospace; 113 } 114 if (!pid) { 115 if (p[1] == 1) fcntl(1, F_SETFD, 0); 116 else dup2(p[1], 1); 117 execl("/bin/sh", "sh", "-c", 118 "eval \"printf %s\\\\\\\\0 x $1 $2\"", 119 "sh", s, redir, (char *)0); 120 _exit(1); 121 } 122 close(p[1]); 123 124 f = fdopen(p[0], "r"); 125 if (!f) { 126 close(p[0]); 127 kill(pid, SIGKILL); 128 reap(pid); 129 goto nospace; 130 } 131 132 l = wv ? i+1 : 0; 133 134 free(getword(f)); 135 if (feof(f)) { 136 fclose(f); 137 reap(pid); 138 return WRDE_SYNTAX; 139 } 140 141 while ((w = getword(f))) { 142 if (i+1 >= l) { 143 l += l/2+10; 144 tmp = realloc(wv, l*sizeof(char *)); 145 if (!tmp) break; 146 wv = tmp; 147 } 148 wv[i++] = w; 149 wv[i] = 0; 150 } 151 if (!feof(f)) err = WRDE_NOSPACE; 152 153 fclose(f); 154 reap(pid); 155 156 if (!wv) wv = calloc(i+1, sizeof *wv); 157 158 we->we_wordv = wv; 159 we->we_wordc = i; 160 161 if (flags & WRDE_DOOFFS) { 162 if (wv) for (i=we->we_offs; i; i--) 163 we->we_wordv[i-1] = 0; 164 we->we_wordc -= we->we_offs; 165 } 166 return err; 167 168nospace: 169 if (!(flags & WRDE_APPEND)) { 170 we->we_wordc = 0; 171 we->we_wordv = 0; 172 } 173 return WRDE_NOSPACE; 174} 175 176int wordexp(const char *restrict s, wordexp_t *restrict we, int flags) 177{ 178 int r, cs; 179 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); 180 r = do_wordexp(s, we, flags); 181 pthread_setcancelstate(cs, 0); 182 return r; 183} 184 185void wordfree(wordexp_t *we) 186{ 187 size_t i; 188 if (!we->we_wordv) return; 189 for (i=0; i<we->we_wordc; i++) free(we->we_wordv[we->we_offs+i]); 190 free(we->we_wordv); 191 we->we_wordv = 0; 192 we->we_wordc = 0; 193} 194