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]
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
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
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).
86 mystpcpy(char *s1
, const char *s2
)
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.
100 wordexp(const char *word
, wordexp_t
*wp
, int flags
)
102 const char *path
= "/usr/bin/ksh93";
106 char *line
, *eob
, *cp
; /* word from shell */
109 int pv
[2]; /* pipe from shell stdout */
110 FILE *fp
; /* pipe read stream */
113 const char **env
= NULL
;
117 posix_spawnattr_t attr
;
118 posix_spawn_file_actions_t fact
;
121 size_t bufflen
; /* Length of |buff| */
123 char *currbuffp
; /* Current position of '\0' in |buff| */
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
)
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.
146 * 2. All of the calls must set WRDE_DOOFFS, or all must not
148 * Therefore, if it's not set, we_offs will always be reset.
150 if ((flags
& WRDE_DOOFFS
) == 0)
154 * If we get APPEND|REUSE, how should we do?
155 * allocating buffer anyway to avoid segfault.
158 if ((flags
& WRDE_APPEND
) == 0 || (flags
& WRDE_REUSE
)) {
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
;
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 */
184 for (n
= 0; (ev
= envp
[n
]) != NULL
; n
++) {
185 if (*ev
== 'P' && strncmp(ev
, "PWD=", 4) == 0)
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
)
194 for (i
= 0; i
< n
; i
++)
196 (void) strcpy(wd
, "PWD=");
197 if (getcwd(&wd
[4], PATH_MAX
) == NULL
)
198 (void) strcpy(&wd
[4], "/");
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
210 bufflen
= 165 + strlen(word
);
211 buff
= alloca(bufflen
);
214 /* Start filling the buffer */
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
227 currbuffp
= mystpcpy(currbuffp
, "exec 2>/dev/null\n");
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;
253 if ((error
= posix_spawnattr_init(&attr
)) != 0) {
257 if ((error
= posix_spawn_file_actions_init(&fact
)) != 0) {
258 (void) posix_spawnattr_destroy(&attr
);
264 * Set up pipe from shell stdout to "fp" for us
268 (void) posix_spawnattr_destroy(&attr
);
269 (void) posix_spawn_file_actions_destroy(&fact
);
277 error
= posix_spawnattr_setflags(&attr
,
278 POSIX_SPAWN_NOSIGCHLD_NP
| POSIX_SPAWN_WAITPID_NP
);
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);
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
);
301 if ((fp
= fdopen(pv
[0], "rF")) == NULL
) {
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
);
324 while ((i
= getc_unlocked(fp
)) != EOF
) {
328 if ((rv
= append(&wptmp
, cp
)) != 0) {
333 size_t bs
= (eob
- line
);
336 if ((nl
= realloc(line
, bs
+ BUFSZ
)) == NULL
) {
347 wptmp
.we_wordp
[wptmp
.we_wordc
] = NULL
;
350 (void) fclose(fp
); /* kill shell if still writing */
353 while (waitpid(pid
, &status
, 0) == -1) {
354 if (errno
!= EINTR
) {
361 rv
= WEXITSTATUS(status
); /* shell WRDE_* status */
372 * Map ksh93 errors to |wordexp()| errors
383 (void) pthread_setcancelstate(cancel_state
, NULL
);
388 * Append a word to the wordexp_t structure, growing it as necessary.
391 append(wordexp_t
*wp
, char *str
)
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
,
405 return (WRDE_NOSPACE
);
406 wp
->we_wordn
+= INITIAL
;
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
;
417 * Free all space owned by wordexp_t.
420 wordfree(wordexp_t
*wp
)
424 if (wp
->we_wordv
== NULL
)
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
);