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 #include "synonyms.h" 47 #include <stdio.h> 48 #include <unistd.h> 49 #include <limits.h> 50 #include <fcntl.h> 51 #include <limits.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <sys/wait.h> 55 #include <unistd.h> 56 #include <wordexp.h> 57 #include <stdio.h> 58 #include <errno.h> 59 60 #define INITIAL 8 /* initial pathv allocation */ 61 #define BUFSZ 256 /* allocation unit of the line buffer */ 62 63 static int append(wordexp_t *, char *); 64 65 extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ 66 67 /* 68 * Do word expansion. 69 * We just pass our arguments to shell with -E option. Note that the 70 * underlying shell must recognize the -E option, and do the right thing 71 * with it. 72 */ 73 int 74 wordexp(const char *word, wordexp_t *wp, int flags) 75 { 76 static char options[9] = "-"; 77 static char *args[4]; 78 const char *path; 79 wordexp_t wptmp; 80 size_t si; 81 int i; 82 pid_t pid; 83 char *line, *eob, *cp; /* word from shell */ 84 int rv = WRDE_ERRNO; 85 int status; 86 int pv[2]; /* pipe from shell stdout */ 87 FILE *fp; /* pipe read stream */ 88 char *optendp = options+1; 89 int serrno, tmpalloc; 90 char *wd = NULL; 91 92 static const char *sun_path = "/bin/ksh"; 93 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 94 95 /* 96 * Do absolute minimum neccessary for the REUSE flag. Eventually 97 * want to be able to actually avoid excessive malloc calls. 98 */ 99 if (flags & WRDE_REUSE) 100 wordfree(wp); 101 102 /* 103 * Initialize wordexp_t 104 * 105 * XPG requires that the struct pointed to by wp not be modified 106 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE. 107 * So we work with wptmp, and only copy wptmp to wp if one of the 108 * previously mentioned conditions is satisfied. 109 */ 110 wptmp = *wp; 111 112 /* 113 * Man page says: 114 * 2. All of the calls must set WRDE_DOOFFS, or all must not 115 * set it. 116 * Therefore, if it's not set, we_offs will always be reset. 117 */ 118 if ((flags & WRDE_DOOFFS) == 0) 119 wptmp.we_offs = 0; 120 121 /* 122 * If we get APPEND|REUSE, how should we do? 123 * allocating buffer anyway to avoid segfault. 124 */ 125 tmpalloc = 0; 126 if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) { 127 wptmp.we_wordc = 0; 128 wptmp.we_wordn = wptmp.we_offs + INITIAL; 129 wptmp.we_wordv = (char **)malloc( 130 sizeof (char *) * wptmp.we_wordn); 131 if (wptmp.we_wordv == NULL) 132 return (WRDE_NOSPACE); 133 wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs; 134 for (si = 0; si < wptmp.we_offs; si++) 135 wptmp.we_wordv[si] = NULL; 136 tmpalloc = 1; 137 } 138 139 /* 140 * Turn flags into shell options 141 */ 142 *optendp++ = (char)0x05; /* ksh -^E */ 143 if (flags & WRDE_UNDEF) 144 *optendp++ = 'u'; 145 if (flags & WRDE_NOCMD) 146 *optendp++ = 'N'; 147 *optendp = '\0'; 148 149 if (getenv("PWD") == NULL) { 150 if ((wd = malloc(PATH_MAX + 4)) == NULL) 151 goto cleanup; 152 (void) strcpy(wd, "PWD="); 153 if (getcwd(&wd[4], PATH_MAX) == NULL) 154 (void) strcpy(&wd[4], "/"); 155 } 156 157 /* 158 * Set up pipe from shell stdout to "fp" for us 159 */ 160 if (pipe(pv) < 0) 161 goto cleanup; 162 163 /* 164 * Fork/exec shell with -E word 165 */ 166 167 if ((pid = fork1()) == -1) { 168 serrno = errno; 169 (void) close(pv[0]); 170 (void) close(pv[1]); 171 errno = serrno; 172 goto cleanup; 173 } 174 175 if (pid == 0) { /* child */ 176 if (wd != NULL) { 177 /* 178 * fork1 handler takes care of __environ_lock. 179 * Thus we can safely call putenv(). 180 */ 181 (void) putenv(wd); 182 } 183 184 (void) dup2(pv[1], 1); 185 (void) close(pv[0]); 186 (void) close(pv[1]); 187 188 if ((flags & WRDE_SHOWERR) == 0) { 189 int devnull; 190 devnull = open("/dev/null", O_WRONLY); 191 (void) dup2(devnull, 2); 192 if (devnull != 2) 193 (void) close(devnull); 194 } 195 196 path = __xpg4 ? xpg4_path : sun_path; 197 args[0] = strrchr(path, '/') + 1; 198 args[1] = options; 199 args[2] = (char *)word; 200 args[3] = NULL; 201 202 (void) execv(path, args); 203 _exit(127); 204 } 205 206 (void) close(pv[1]); 207 208 if ((fp = fdopen(pv[0], "rF")) == NULL) { 209 serrno = errno; 210 (void) close(pv[0]); 211 errno = serrno; 212 goto wait_cleanup; 213 } 214 215 /* 216 * Read words from shell, separated with '\0'. 217 * Since there is no way to disable IFS splitting, 218 * it would be possible to separate the output with '\n'. 219 */ 220 cp = line = malloc(BUFSZ); 221 if (line == NULL) { 222 (void) fclose(fp); 223 rv = WRDE_NOSPACE; 224 goto wait_cleanup; 225 } 226 eob = line + BUFSZ; 227 228 rv = 0; 229 while ((i = getc(fp)) != EOF) { 230 *cp++ = (char)i; 231 if (i == '\0') { 232 cp = line; 233 if ((rv = append(&wptmp, cp)) != 0) { 234 break; 235 } 236 } 237 if (cp == eob) { 238 size_t bs = (eob - line); 239 char *nl; 240 241 if ((nl = realloc(line, bs + BUFSZ)) == NULL) { 242 rv = WRDE_NOSPACE; 243 break; 244 } 245 line = nl; 246 cp = line + bs; 247 eob = cp + BUFSZ; 248 } 249 } 250 251 wptmp.we_wordp[wptmp.we_wordc] = NULL; 252 253 free(line); 254 (void) fclose(fp); /* kill shell if still writing */ 255 256 wait_cleanup: 257 if (waitpid(pid, &status, 0) == -1) 258 rv = WRDE_ERRNO; 259 else if (rv == 0) 260 rv = WEXITSTATUS(status); /* shell WRDE_* status */ 261 262 cleanup: 263 if (rv == 0) 264 *wp = wptmp; 265 else { 266 if (tmpalloc) 267 wordfree(&wptmp); 268 } 269 270 if (wd) 271 free(wd); 272 /* 273 * Map ksh errors to wordexp() errors 274 */ 275 if (rv == 4) 276 rv = WRDE_CMDSUB; 277 else if (rv == 5) 278 rv = WRDE_BADVAL; 279 else if (rv == 6) 280 rv = WRDE_SYNTAX; 281 return (rv); 282 } 283 284 /* 285 * Append a word to the wordexp_t structure, growing it as neccessary. 286 */ 287 static int 288 append(wordexp_t *wp, char *str) 289 { 290 char *cp; 291 char **nwp; 292 293 /* 294 * We will be adding one entry and later adding 295 * one more NULL. So we need 2 more free slots. 296 */ 297 if ((wp->we_wordp + wp->we_wordc) == 298 (wp->we_wordv + wp->we_wordn - 1)) { 299 nwp = realloc(wp->we_wordv, 300 (wp->we_wordn + INITIAL) * sizeof (char *)); 301 if (nwp == NULL) 302 return (WRDE_NOSPACE); 303 wp->we_wordn += INITIAL; 304 wp->we_wordv = nwp; 305 wp->we_wordp = wp->we_wordv + wp->we_offs; 306 } 307 if ((cp = strdup(str)) == NULL) 308 return (WRDE_NOSPACE); 309 wp->we_wordp[wp->we_wordc++] = cp; 310 return (0); 311 } 312 313 /* 314 * Free all space owned by wordexp_t. 315 */ 316 void 317 wordfree(wordexp_t *wp) 318 { 319 size_t i; 320 321 if (wp->we_wordv == NULL) 322 return; 323 for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++) 324 free(wp->we_wordv[i]); 325 free((void *)wp->we_wordv); 326 wp->we_wordc = 0; 327 wp->we_wordv = NULL; 328 } 329