1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * This code is MKS code ported to Solaris originally with minimum 30 * modifications so that upgrades from MKS would readily integrate. 31 * The MKS basis for this modification was: 32 * 33 * $Id: wordexp.c 1.22 1994/11/21 18:24:50 miked 34 * 35 * Additional modifications have been made to this code to make it 36 * 64-bit clean. 37 */ 38 39 /* 40 * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines. 41 * 42 * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved. 43 * 44 */ 45 46 #pragma weak wordexp = _wordexp 47 #pragma weak wordfree = _wordfree 48 49 #include "synonyms.h" 50 #include <stdio.h> 51 #include <unistd.h> 52 #include <limits.h> 53 #include <fcntl.h> 54 #include <limits.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <sys/wait.h> 58 #include <unistd.h> 59 #include <wordexp.h> 60 #include <stdio.h> 61 #include <errno.h> 62 63 #define INITIAL 8 /* initial pathv allocation */ 64 #define BUFSZ 256 /* allocation unit of the line buffer */ 65 66 static int append(wordexp_t *, char *); 67 68 extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ 69 70 /* 71 * Do word expansion. 72 * We just pass our arguments to shell with -E option. Note that the 73 * underlying shell must recognize the -E option, and do the right thing 74 * with it. 75 */ 76 int 77 wordexp(const char *word, wordexp_t *wp, int flags) 78 { 79 static char options[9] = "-"; 80 static char *args[4]; 81 const char *path; 82 wordexp_t wptmp; 83 size_t si; 84 int i; 85 pid_t pid; 86 char *line, *eob, *cp; /* word from shell */ 87 int rv = WRDE_ERRNO; 88 int status; 89 int pv[2]; /* pipe from shell stdout */ 90 FILE *fp; /* pipe read stream */ 91 char *optendp = options+1; 92 int serrno, tmpalloc; 93 char *wd = NULL; 94 95 static const char *sun_path = "/bin/ksh"; 96 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 97 98 /* 99 * Do absolute minimum neccessary for the REUSE flag. Eventually 100 * want to be able to actually avoid excessive malloc calls. 101 */ 102 if (flags & WRDE_REUSE) 103 wordfree(wp); 104 105 /* 106 * Initialize wordexp_t 107 * 108 * XPG requires that the struct pointed to by wp not be modified 109 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE. 110 * So we work with wptmp, and only copy wptmp to wp if one of the 111 * previously mentioned conditions is satisfied. 112 */ 113 wptmp = *wp; 114 115 /* 116 * Man page says: 117 * 2. All of the calls must set WRDE_DOOFFS, or all must not 118 * set it. 119 * Therefore, if it's not set, we_offs will always be reset. 120 */ 121 if ((flags & WRDE_DOOFFS) == 0) 122 wptmp.we_offs = 0; 123 124 /* 125 * If we get APPEND|REUSE, how should we do? 126 * allocating buffer anyway to avoid segfault. 127 */ 128 tmpalloc = 0; 129 if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) { 130 wptmp.we_wordc = 0; 131 wptmp.we_wordn = wptmp.we_offs + INITIAL; 132 wptmp.we_wordv = (char **)malloc( 133 sizeof (char *) * wptmp.we_wordn); 134 if (wptmp.we_wordv == NULL) 135 return (WRDE_NOSPACE); 136 wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs; 137 for (si = 0; si < wptmp.we_offs; si++) 138 wptmp.we_wordv[si] = NULL; 139 tmpalloc = 1; 140 } 141 142 /* 143 * Turn flags into shell options 144 */ 145 *optendp++ = (char)0x05; /* ksh -^E */ 146 if (flags & WRDE_UNDEF) 147 *optendp++ = 'u'; 148 if (flags & WRDE_NOCMD) 149 *optendp++ = 'N'; 150 *optendp = '\0'; 151 152 if (getenv("PWD") == NULL) { 153 if ((wd = malloc(PATH_MAX + 4)) == NULL) 154 goto cleanup; 155 (void) strcpy(wd, "PWD="); 156 if (getcwd(&wd[4], PATH_MAX) == NULL) 157 (void) strcpy(&wd[4], "/"); 158 } 159 160 /* 161 * Set up pipe from shell stdout to "fp" for us 162 */ 163 if (pipe(pv) < 0) 164 goto cleanup; 165 166 /* 167 * Fork/exec shell with -E word 168 */ 169 170 if ((pid = fork1()) == -1) { 171 serrno = errno; 172 (void) close(pv[0]); 173 (void) close(pv[1]); 174 errno = serrno; 175 goto cleanup; 176 } 177 178 if (pid == 0) { /* child */ 179 if (wd != NULL) { 180 /* 181 * fork1 handler takes care of __environ_lock. 182 * Thus we can safely call putenv(). 183 */ 184 (void) putenv(wd); 185 } 186 187 (void) dup2(pv[1], 1); 188 (void) close(pv[0]); 189 (void) close(pv[1]); 190 191 if ((flags & WRDE_SHOWERR) == 0) { 192 int devnull; 193 devnull = open("/dev/null", O_WRONLY); 194 (void) dup2(devnull, 2); 195 if (devnull != 2) 196 (void) close(devnull); 197 } 198 199 path = __xpg4 ? xpg4_path : sun_path; 200 args[0] = strrchr(path, '/') + 1; 201 args[1] = options; 202 args[2] = (char *)word; 203 args[3] = NULL; 204 205 (void) execv(path, args); 206 _exit(127); 207 } 208 209 (void) close(pv[1]); 210 211 if ((fp = fdopen(pv[0], "rF")) == NULL) { 212 serrno = errno; 213 (void) close(pv[0]); 214 errno = serrno; 215 goto wait_cleanup; 216 } 217 218 /* 219 * Read words from shell, separated with '\0'. 220 * Since there is no way to disable IFS splitting, 221 * it would be possible to separate the output with '\n'. 222 */ 223 cp = line = malloc(BUFSZ); 224 if (line == NULL) { 225 (void) fclose(fp); 226 rv = WRDE_NOSPACE; 227 goto wait_cleanup; 228 } 229 eob = line + BUFSZ; 230 231 rv = 0; 232 while ((i = getc(fp)) != EOF) { 233 *cp++ = (char)i; 234 if (i == '\0') { 235 cp = line; 236 if ((rv = append(&wptmp, cp)) != 0) { 237 break; 238 } 239 } 240 if (cp == eob) { 241 size_t bs = (eob - line); 242 char *nl; 243 244 if ((nl = realloc(line, bs + BUFSZ)) == NULL) { 245 rv = WRDE_NOSPACE; 246 break; 247 } 248 line = nl; 249 cp = line + bs; 250 eob = cp + BUFSZ; 251 } 252 } 253 254 wptmp.we_wordp[wptmp.we_wordc] = NULL; 255 256 free(line); 257 (void) fclose(fp); /* kill shell if still writing */ 258 259 wait_cleanup: 260 if (waitpid(pid, &status, 0) == -1) 261 rv = WRDE_ERRNO; 262 else if (rv == 0) 263 rv = WEXITSTATUS(status); /* shell WRDE_* status */ 264 265 cleanup: 266 if (rv == 0) 267 *wp = wptmp; 268 else { 269 if (tmpalloc) 270 wordfree(&wptmp); 271 } 272 273 if (wd) 274 free(wd); 275 /* 276 * Map ksh errors to wordexp() errors 277 */ 278 if (rv == 4) 279 rv = WRDE_CMDSUB; 280 else if (rv == 5) 281 rv = WRDE_BADVAL; 282 else if (rv == 6) 283 rv = WRDE_SYNTAX; 284 return (rv); 285 } 286 287 /* 288 * Append a word to the wordexp_t structure, growing it as neccessary. 289 */ 290 static int 291 append(wordexp_t *wp, char *str) 292 { 293 char *cp; 294 char **nwp; 295 296 /* 297 * We will be adding one entry and later adding 298 * one more NULL. So we need 2 more free slots. 299 */ 300 if ((wp->we_wordp + wp->we_wordc) == 301 (wp->we_wordv + wp->we_wordn - 1)) { 302 nwp = realloc(wp->we_wordv, 303 (wp->we_wordn + INITIAL) * sizeof (char *)); 304 if (nwp == NULL) 305 return (WRDE_NOSPACE); 306 wp->we_wordn += INITIAL; 307 wp->we_wordv = nwp; 308 wp->we_wordp = wp->we_wordv + wp->we_offs; 309 } 310 if ((cp = strdup(str)) == NULL) 311 return (WRDE_NOSPACE); 312 wp->we_wordp[wp->we_wordc++] = cp; 313 return (0); 314 } 315 316 /* 317 * Free all space owned by wordexp_t. 318 */ 319 void 320 wordfree(wordexp_t *wp) 321 { 322 size_t i; 323 324 if (wp->we_wordv == NULL) 325 return; 326 for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++) 327 free(wp->we_wordv[i]); 328 free((void *)wp->we_wordv); 329 wp->we_wordc = 0; 330 wp->we_wordv = NULL; 331 } 332