import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / regex / wordexp.c
blobea9acb0e40b5ca354ca864be7a7533dc3a198026
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * This code is MKS code ported to Solaris originally with minimum
29 * modifications so that upgrades from MKS would readily integrate.
30 * The MKS basis for this modification was:
32 * $Id: wordexp.c 1.22 1994/11/21 18:24:50 miked
34 * Additional modifications have been made to this code to make it
35 * 64-bit clean.
39 * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines.
41 * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved.
42 * Modified by Roland Mainz <roland.mainz@nrubsig.org> to support ksh93.
45 #pragma weak _wordexp = wordexp
46 #pragma weak _wordfree = wordfree
48 #include "lint.h"
49 #include <stdio.h>
50 #include <unistd.h>
51 #include <limits.h>
52 #include <fcntl.h>
53 #include <limits.h>
54 #include <stdlib.h>
55 #include <alloca.h>
56 #include <string.h>
57 #include <sys/wait.h>
58 #include <pthread.h>
59 #include <unistd.h>
60 #include <wordexp.h>
61 #include <stdio.h>
62 #include <spawn.h>
63 #include <errno.h>
65 #define INITIAL 8 /* initial pathv allocation */
66 #define BUFSZ 256 /* allocation unit of the line buffer */
69 * Needs no locking if fetched only once.
70 * See getenv()/putenv()/setenv().
72 extern const char **_environ;
74 /* Local prototypes */
75 static int append(wordexp_t *, char *);
78 * |mystpcpy| - like |strcpy()| but returns the end of the buffer
79 * We'll add this later (and a matching multibyte/widechar version)
80 * as normal libc function.
82 * Copy string s2 to s1. s1 must be large enough.
83 * return s1-1 (position of string terminator ('\0') in destination buffer).
85 static char *
86 mystpcpy(char *s1, const char *s2)
88 while (*s1++ = *s2++)
90 return (s1-1);
94 * Do word expansion.
95 * We build a mini-script in |buff| which takes care of all details,
96 * including stdin/stdout/stderr redirection, WRDE_NOCMD mode and
97 * the word expansion itself.
99 int
100 wordexp(const char *word, wordexp_t *wp, int flags)
102 const char *path = "/usr/bin/ksh93";
103 wordexp_t wptmp;
104 size_t si;
105 pid_t pid;
106 char *line, *eob, *cp; /* word from shell */
107 int rv = WRDE_ERRNO;
108 int status;
109 int pv[2]; /* pipe from shell stdout */
110 FILE *fp; /* pipe read stream */
111 int tmpalloc;
112 char *wd = NULL;
113 const char **env = NULL;
114 const char **envp;
115 const char *ev;
116 int n;
117 posix_spawnattr_t attr;
118 posix_spawn_file_actions_t fact;
119 int error;
120 int cancel_state;
121 size_t bufflen; /* Length of |buff| */
122 char *buff;
123 char *currbuffp; /* Current position of '\0' in |buff| */
124 char *args[10];
125 int i;
128 * Do absolute minimum necessary for the REUSE flag. Eventually
129 * want to be able to actually avoid excessive malloc calls.
131 if (flags & WRDE_REUSE)
132 wordfree(wp);
135 * Initialize wordexp_t
137 * XPG requires that the struct pointed to by wp not be modified
138 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
139 * So we work with wptmp, and only copy wptmp to wp if one of the
140 * previously mentioned conditions is satisfied.
142 wptmp = *wp;
145 * Man page says:
146 * 2. All of the calls must set WRDE_DOOFFS, or all must not
147 * set it.
148 * Therefore, if it's not set, we_offs will always be reset.
150 if ((flags & WRDE_DOOFFS) == 0)
151 wptmp.we_offs = 0;
154 * If we get APPEND|REUSE, how should we do?
155 * allocating buffer anyway to avoid segfault.
157 tmpalloc = 0;
158 if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
159 wptmp.we_wordc = 0;
160 wptmp.we_wordn = wptmp.we_offs + INITIAL;
161 wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn);
162 if (wptmp.we_wordv == NULL)
163 return (WRDE_NOSPACE);
164 wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
165 for (si = 0; si < wptmp.we_offs; si++)
166 wptmp.we_wordv[si] = NULL;
167 tmpalloc = 1;
171 * The UNIX98 Posix conformance test suite requires
172 * |wordexp()| to not be a cancellation point.
174 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
177 * Make sure PWD is in the environment.
179 if ((envp = _environ) == NULL) {
180 /* can happen when processing a SunOS 4.x AOUT file */
181 ev = NULL;
182 n = 0;
183 } else {
184 for (n = 0; (ev = envp[n]) != NULL; n++) {
185 if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0)
186 break;
189 if (ev == NULL) { /* PWD missing from the environment */
190 /* allocate a new environment */
191 if ((env = malloc((n + 2) * sizeof (char *))) == NULL ||
192 (wd = malloc(PATH_MAX + 4)) == NULL)
193 goto cleanup;
194 for (i = 0; i < n; i++)
195 env[i] = envp[i];
196 (void) strcpy(wd, "PWD=");
197 if (getcwd(&wd[4], PATH_MAX) == NULL)
198 (void) strcpy(&wd[4], "/");
199 env[i] = wd;
200 env[i + 1] = NULL;
201 envp = env;
205 * Calculate size of required buffer (which is size of the
206 * input string (|word|) plus all string literals below;
207 * this value MUST be adjusted each time the literals are
208 * changed!!).
210 bufflen = 165 + strlen(word);
211 buff = alloca(bufflen);
212 i = 0;
214 /* Start filling the buffer */
215 buff[0] = '\0';
216 currbuffp = buff;
218 if (flags & WRDE_UNDEF)
219 currbuffp = mystpcpy(currbuffp, "set -o nounset\n");
220 if ((flags & WRDE_SHOWERR) == 0) {
222 * The newline ('\n') is neccesary to make sure that
223 * the redirection to /dev/null is already active in
224 * the case the printf below contains a syntax
225 * error...
227 currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n");
229 /* Squish stdin */
230 currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n");
232 if (flags & WRDE_NOCMD) {
234 * Switch to restricted shell (rksh) mode here to
235 * put the word expansion into a "cage" which
236 * prevents users from executing external commands
237 * (outside those listed by ${PATH} (which we set
238 * explicitly to /usr/no/such/path/element/)).
240 currbuffp = mystpcpy(currbuffp,
241 "export PATH=/usr/no/such/path/element/ ; "
242 "set -o restricted\n");
245 (void) snprintf(currbuffp, bufflen,
246 "print -f '%%s\\000' -- %s", word);
248 args[i++] = strrchr(path, '/') + 1;
249 args[i++] = "-c";
250 args[i++] = buff;
251 args[i++] = NULL;
253 if ((error = posix_spawnattr_init(&attr)) != 0) {
254 errno = error;
255 goto cleanup;
257 if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
258 (void) posix_spawnattr_destroy(&attr);
259 errno = error;
260 goto cleanup;
264 * Set up pipe from shell stdout to "fp" for us
266 if (pipe(pv) < 0) {
267 error = errno;
268 (void) posix_spawnattr_destroy(&attr);
269 (void) posix_spawn_file_actions_destroy(&fact);
270 errno = error;
271 goto cleanup;
275 * Spawn shell
277 error = posix_spawnattr_setflags(&attr,
278 POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
279 if (error == 0)
280 error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1);
281 if (error == 0 && pv[0] != 1)
282 error = posix_spawn_file_actions_addclose(&fact, pv[0]);
283 if (error == 0 && pv[1] != 1)
284 error = posix_spawn_file_actions_addclose(&fact, pv[1]);
285 if (error == 0 && !(flags & WRDE_SHOWERR))
286 error = posix_spawn_file_actions_addopen(&fact, 2,
287 "/dev/null", O_WRONLY, 0);
289 if (error == 0)
290 error = posix_spawn(&pid, path, &fact, &attr,
291 (char *const *)args, (char *const *)envp);
292 (void) posix_spawnattr_destroy(&attr);
293 (void) posix_spawn_file_actions_destroy(&fact);
294 (void) close(pv[1]);
295 if (error) {
296 (void) close(pv[0]);
297 errno = error;
298 goto cleanup;
301 if ((fp = fdopen(pv[0], "rF")) == NULL) {
302 error = errno;
303 (void) close(pv[0]);
304 errno = error;
305 goto wait_cleanup;
309 * Read words from shell, separated with '\0'.
310 * Since there is no way to disable IFS splitting,
311 * it would be possible to separate the output with '\n'.
313 cp = line = malloc(BUFSZ);
314 if (line == NULL) {
315 error = errno;
316 (void) fclose(fp);
317 errno = error;
318 goto wait_cleanup;
320 eob = line + BUFSZ;
322 rv = 0;
323 flockfile(fp);
324 while ((i = getc_unlocked(fp)) != EOF) {
325 *cp++ = (char)i;
326 if (i == '\0') {
327 cp = line;
328 if ((rv = append(&wptmp, cp)) != 0) {
329 break;
332 if (cp == eob) {
333 size_t bs = (eob - line);
334 char *nl;
336 if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
337 rv = WRDE_NOSPACE;
338 break;
340 line = nl;
341 cp = line + bs;
342 eob = cp + BUFSZ;
345 funlockfile(fp);
347 wptmp.we_wordp[wptmp.we_wordc] = NULL;
349 free(line);
350 (void) fclose(fp); /* kill shell if still writing */
352 wait_cleanup:
353 while (waitpid(pid, &status, 0) == -1) {
354 if (errno != EINTR) {
355 if (rv == 0)
356 rv = WRDE_ERRNO;
357 break;
360 if (rv == 0)
361 rv = WEXITSTATUS(status); /* shell WRDE_* status */
363 cleanup:
364 if (rv == 0)
365 *wp = wptmp;
366 else if (tmpalloc)
367 wordfree(&wptmp);
369 free(env);
370 free(wd);
372 * Map ksh93 errors to |wordexp()| errors
374 switch (rv) {
375 case 1:
376 rv = WRDE_BADVAL;
377 break;
378 case 127:
379 rv = WRDE_BADCHAR;
380 break;
383 (void) pthread_setcancelstate(cancel_state, NULL);
384 return (rv);
388 * Append a word to the wordexp_t structure, growing it as necessary.
390 static int
391 append(wordexp_t *wp, char *str)
393 char *cp;
394 char **nwp;
397 * We will be adding one entry and later adding
398 * one more NULL. So we need 2 more free slots.
400 if ((wp->we_wordp + wp->we_wordc) ==
401 (wp->we_wordv + wp->we_wordn - 1)) {
402 nwp = reallocarray(wp->we_wordv, wp->we_wordn + INITIAL,
403 sizeof (char *));
404 if (nwp == NULL)
405 return (WRDE_NOSPACE);
406 wp->we_wordn += INITIAL;
407 wp->we_wordv = nwp;
408 wp->we_wordp = wp->we_wordv + wp->we_offs;
410 if ((cp = strdup(str)) == NULL)
411 return (WRDE_NOSPACE);
412 wp->we_wordp[wp->we_wordc++] = cp;
413 return (0);
417 * Free all space owned by wordexp_t.
419 void
420 wordfree(wordexp_t *wp)
422 size_t i;
424 if (wp->we_wordv == NULL)
425 return;
426 for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++)
427 free(wp->we_wordv[i]);
428 free((void *)wp->we_wordv);
429 wp->we_wordc = 0;
430 wp->we_wordv = NULL;