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