xref: /illumos-gate/usr/src/lib/libc/port/regex/wordexp.c (revision 07a48826732249fcd3aa8dd53c8389595e9f1fbc)
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
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:
31  *
32  *	$Id: wordexp.c 1.22 1994/11/21 18:24:50 miked
33  *
34  * Additional modifications have been made to this code to make it
35  * 64-bit clean.
36  */
37 
38 /*
39  * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines.
40  *
41  * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
42  * Modified by Roland Mainz <roland.mainz@nrubsig.org> to support ksh93.
43  */
44 
45 #pragma	weak _wordexp = wordexp
46 #pragma	weak _wordfree = wordfree
47 
48 /* Safeguard against mistakes in the Makefiles */
49 #ifndef WORDEXP_KSH93
50 #error "WORDEXP_KSH93 not set. Please check the Makefile flags."
51 #endif
52 
53 #include "lint.h"
54 #include <stdio.h>
55 #include <unistd.h>
56 #include <limits.h>
57 #include <fcntl.h>
58 #include <limits.h>
59 #include <stdlib.h>
60 #if WORDEXP_KSH93
61 #include <alloca.h>
62 #endif /* WORDEXP_KSH93 */
63 #include <string.h>
64 #include <sys/wait.h>
65 #include <pthread.h>
66 #include <unistd.h>
67 #include <wordexp.h>
68 #include <stdio.h>
69 #include <spawn.h>
70 #include <errno.h>
71 
72 #define	INITIAL	8		/* initial pathv allocation */
73 #define	BUFSZ	256		/* allocation unit of the line buffer */
74 
75 /*
76  * Needs no locking if fetched only once.
77  * See getenv()/putenv()/setenv().
78  */
79 extern	const char **_environ;
80 
81 /* Local prototypes */
82 static int append(wordexp_t *, char *);
83 
84 #if WORDEXP_KSH93
85 /*
86  * |mystpcpy| - like |strcpy()| but returns the end of the buffer
87  * We'll add this later (and a matching multibyte/widechar version)
88  * as normal libc function.
89  *
90  * Copy string s2 to s1.  s1 must be large enough.
91  * return s1-1 (position of string terminator ('\0') in destination buffer).
92  */
93 static char *
94 mystpcpy(char *s1, const char *s2)
95 {
96 	while (*s1++ = *s2++)
97 		;
98 	return (s1-1);
99 }
100 
101 /*
102  * Do word expansion.
103  * We build a mini-script in |buff| which takes care of all details,
104  * including stdin/stdout/stderr redirection, WRDE_NOCMD mode and
105  * the word expansion itself.
106  */
107 int
108 wordexp(const char *word, wordexp_t *wp, int flags)
109 {
110 	const char *path = "/usr/bin/ksh93";
111 	wordexp_t wptmp;
112 	size_t si;
113 	pid_t pid;
114 	char *line, *eob, *cp;		/* word from shell */
115 	int rv = WRDE_ERRNO;
116 	int status;
117 	int pv[2];			/* pipe from shell stdout */
118 	FILE *fp;			/* pipe read stream */
119 	int tmpalloc;
120 	char *wd = NULL;
121 	const char **env = NULL;
122 	const char **envp;
123 	const char *ev;
124 	int n;
125 	posix_spawnattr_t attr;
126 	posix_spawn_file_actions_t fact;
127 	int error;
128 	int cancel_state;
129 	size_t bufflen; /* Length of |buff| */
130 	char *buff;
131 	char *currbuffp; /* Current position of '\0' in |buff| */
132 	char *args[10];
133 	int i;
134 
135 	/*
136 	 * Do absolute minimum necessary for the REUSE flag. Eventually
137 	 * want to be able to actually avoid excessive malloc calls.
138 	 */
139 	if (flags & WRDE_REUSE)
140 		wordfree(wp);
141 
142 	/*
143 	 * Initialize wordexp_t
144 	 *
145 	 * XPG requires that the struct pointed to by wp not be modified
146 	 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
147 	 * So we work with wptmp, and only copy wptmp to wp if one of the
148 	 * previously mentioned conditions is satisfied.
149 	 */
150 	wptmp = *wp;
151 
152 	/*
153 	 * Man page says:
154 	 * 2. All of the calls must set WRDE_DOOFFS, or all must not
155 	 *    set it.
156 	 * Therefore, if it's not set, we_offs will always be reset.
157 	 */
158 	if ((flags & WRDE_DOOFFS) == 0)
159 		wptmp.we_offs = 0;
160 
161 	/*
162 	 * If we get APPEND|REUSE, how should we do?
163 	 * allocating buffer anyway to avoid segfault.
164 	 */
165 	tmpalloc = 0;
166 	if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
167 		wptmp.we_wordc = 0;
168 		wptmp.we_wordn = wptmp.we_offs + INITIAL;
169 		wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn);
170 		if (wptmp.we_wordv == NULL)
171 			return (WRDE_NOSPACE);
172 		wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
173 		for (si = 0; si < wptmp.we_offs; si++)
174 			wptmp.we_wordv[si] = NULL;
175 		tmpalloc = 1;
176 	}
177 
178 	/*
179 	 * The UNIX98 Posix conformance test suite requires
180 	 * |wordexp()| to not be a cancellation point.
181 	 */
182 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
183 
184 	/*
185 	 * Make sure PWD is in the environment.
186 	 */
187 	if ((envp = _environ) == NULL) {
188 		/* can happen when processing a SunOS 4.x AOUT file */
189 		ev = NULL;
190 		n = 0;
191 	} else {
192 		for (n = 0; (ev = envp[n]) != NULL; n++) {
193 			if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0)
194 				break;
195 		}
196 	}
197 	if (ev == NULL) {	/* PWD missing from the environment */
198 		/* allocate a new environment */
199 		if ((env = malloc((n + 2) * sizeof (char *))) == NULL ||
200 		    (wd = malloc(PATH_MAX + 4)) == NULL)
201 			goto cleanup;
202 		for (i = 0; i < n; i++)
203 			env[i] = envp[i];
204 		(void) strcpy(wd, "PWD=");
205 		if (getcwd(&wd[4], PATH_MAX) == NULL)
206 			(void) strcpy(&wd[4], "/");
207 		env[i] = wd;
208 		env[i + 1] = NULL;
209 		envp = env;
210 	}
211 
212 	/*
213 	 * Calculate size of required buffer (which is size of the
214 	 * input string (|word|) plus all string literals below;
215 	 * this value MUST be adjusted each time the literals are
216 	 * changed!!).
217 	 */
218 	bufflen = 165 + strlen(word);
219 	buff = alloca(bufflen);
220 	i = 0;
221 
222 	/* Start filling the buffer */
223 	buff[0] = '\0';
224 	currbuffp = buff;
225 
226 	if (flags & WRDE_UNDEF)
227 		currbuffp = mystpcpy(currbuffp, "set -o nounset\n");
228 	if ((flags & WRDE_SHOWERR) == 0) {
229 		/*
230 		 * The newline ('\n') is neccesary to make sure that
231 		 * the redirection to /dev/null is already active in
232 		 * the case the printf below contains a syntax
233 		 * error...
234 		 */
235 		currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n");
236 	}
237 	/* Squish stdin */
238 	currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n");
239 
240 	if (flags & WRDE_NOCMD) {
241 		/*
242 		 * Switch to restricted shell (rksh) mode here to
243 		 * put the word expansion into a "cage" which
244 		 * prevents users from executing external commands
245 		 * (outside those listed by ${PATH} (which we set
246 		 * explicitly to /usr/no/such/path/element/)).
247 		 */
248 		currbuffp = mystpcpy(currbuffp,
249 		    "export PATH=/usr/no/such/path/element/ ; "
250 		    "set -o restricted\n");
251 	}
252 
253 	(void) snprintf(currbuffp, bufflen,
254 	    "print -f '%%s\\000' -- %s", word);
255 
256 	args[i++] = strrchr(path, '/') + 1;
257 	args[i++] = "-c";
258 	args[i++] = buff;
259 	args[i++] = NULL;
260 
261 	if ((error = posix_spawnattr_init(&attr)) != 0) {
262 		errno = error;
263 		goto cleanup;
264 	}
265 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
266 		(void) posix_spawnattr_destroy(&attr);
267 		errno = error;
268 		goto cleanup;
269 	}
270 
271 	/*
272 	 * Set up pipe from shell stdout to "fp" for us
273 	 */
274 	if (pipe(pv) < 0) {
275 		error = errno;
276 		(void) posix_spawnattr_destroy(&attr);
277 		(void) posix_spawn_file_actions_destroy(&fact);
278 		errno = error;
279 		goto cleanup;
280 	}
281 
282 	/*
283 	 * Spawn shell
284 	 */
285 	error = posix_spawnattr_setflags(&attr,
286 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
287 	if (error == 0)
288 		error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1);
289 	if (error == 0 && pv[0] != 1)
290 		error = posix_spawn_file_actions_addclose(&fact, pv[0]);
291 	if (error == 0 && pv[1] != 1)
292 		error = posix_spawn_file_actions_addclose(&fact, pv[1]);
293 	if (error == 0 && !(flags & WRDE_SHOWERR))
294 		error = posix_spawn_file_actions_addopen(&fact, 2,
295 		    "/dev/null", O_WRONLY, 0);
296 
297 	if (error == 0)
298 		error = posix_spawn(&pid, path, &fact, &attr,
299 		    (char *const *)args, (char *const *)envp);
300 	(void) posix_spawnattr_destroy(&attr);
301 	(void) posix_spawn_file_actions_destroy(&fact);
302 	(void) close(pv[1]);
303 	if (error) {
304 		(void) close(pv[0]);
305 		errno = error;
306 		goto cleanup;
307 	}
308 
309 	if ((fp = fdopen(pv[0], "rF")) == NULL) {
310 		error = errno;
311 		(void) close(pv[0]);
312 		errno = error;
313 		goto wait_cleanup;
314 	}
315 
316 	/*
317 	 * Read words from shell, separated with '\0'.
318 	 * Since there is no way to disable IFS splitting,
319 	 * it would be possible to separate the output with '\n'.
320 	 */
321 	cp = line = malloc(BUFSZ);
322 	if (line == NULL) {
323 		error = errno;
324 		(void) fclose(fp);
325 		errno = error;
326 		goto wait_cleanup;
327 	}
328 	eob = line + BUFSZ;
329 
330 	rv = 0;
331 	flockfile(fp);
332 	while ((i = getc_unlocked(fp)) != EOF) {
333 		*cp++ = (char)i;
334 		if (i == '\0') {
335 			cp = line;
336 			if ((rv = append(&wptmp, cp)) != 0) {
337 				break;
338 			}
339 		}
340 		if (cp == eob) {
341 			size_t bs = (eob - line);
342 			char *nl;
343 
344 			if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
345 				rv = WRDE_NOSPACE;
346 				break;
347 			}
348 			line = nl;
349 			cp = line + bs;
350 			eob = cp + BUFSZ;
351 		}
352 	}
353 	funlockfile(fp);
354 
355 	wptmp.we_wordp[wptmp.we_wordc] = NULL;
356 
357 	free(line);
358 	(void) fclose(fp);	/* kill shell if still writing */
359 
360 wait_cleanup:
361 	while (waitpid(pid, &status, 0) == -1) {
362 		if (errno != EINTR) {
363 			if (rv == 0)
364 				rv = WRDE_ERRNO;
365 			break;
366 		}
367 	}
368 	if (rv == 0)
369 		rv = WEXITSTATUS(status); /* shell WRDE_* status */
370 
371 cleanup:
372 	if (rv == 0)
373 		*wp = wptmp;
374 	else if (tmpalloc)
375 		wordfree(&wptmp);
376 
377 	if (env)
378 		free(env);
379 	if (wd)
380 		free(wd);
381 	/*
382 	 * Map ksh93 errors to |wordexp()| errors
383 	 */
384 	switch (rv) {
385 		case 1:
386 			rv = WRDE_BADVAL;
387 			break;
388 		case 127:
389 			rv = WRDE_BADCHAR;
390 			break;
391 	}
392 
393 	(void) pthread_setcancelstate(cancel_state, NULL);
394 	return (rv);
395 }
396 
397 #else /* WORDEXP_KSH93 */
398 
399 extern	int __xpg4;	/* defined in _xpg4.c; 0 if not xpg4-compiled program */
400 
401 /*
402  * Do word expansion.
403  * We just pass our arguments to shell with -E option.  Note that the
404  * underlying shell must recognize the -E option, and do the right thing
405  * with it.
406  */
407 int
408 wordexp(const char *word, wordexp_t *wp, int flags)
409 {
410 	char options[9];
411 	char *optendp = options;
412 	char *argv[4];
413 	const char *path;
414 	wordexp_t wptmp;
415 	size_t si;
416 	int i;
417 	pid_t pid;
418 	char *line, *eob, *cp;		/* word from shell */
419 	int rv = WRDE_ERRNO;
420 	int status;
421 	int pv[2];			/* pipe from shell stdout */
422 	FILE *fp;			/* pipe read stream */
423 	int tmpalloc;
424 	char *wd = NULL;
425 	const char **env = NULL;
426 	const char **envp;
427 	const char *ev;
428 	int n;
429 	posix_spawnattr_t attr;
430 	posix_spawn_file_actions_t fact;
431 	int error;
432 	int cancel_state;
433 
434 	static const char *sun_path = "/bin/ksh";
435 	static const char *xpg4_path = "/usr/xpg4/bin/sh";
436 
437 	/*
438 	 * Do absolute minimum necessary for the REUSE flag. Eventually
439 	 * want to be able to actually avoid excessive malloc calls.
440 	 */
441 	if (flags & WRDE_REUSE)
442 		wordfree(wp);
443 
444 	/*
445 	 * Initialize wordexp_t
446 	 *
447 	 * XPG requires that the struct pointed to by wp not be modified
448 	 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
449 	 * So we work with wptmp, and only copy wptmp to wp if one of the
450 	 * previously mentioned conditions is satisfied.
451 	 */
452 	wptmp = *wp;
453 
454 	/*
455 	 * Man page says:
456 	 * 2. All of the calls must set WRDE_DOOFFS, or all must not
457 	 *    set it.
458 	 * Therefore, if it's not set, we_offs will always be reset.
459 	 */
460 	if ((flags & WRDE_DOOFFS) == 0)
461 		wptmp.we_offs = 0;
462 
463 	/*
464 	 * If we get APPEND|REUSE, how should we do?
465 	 * allocating buffer anyway to avoid segfault.
466 	 */
467 	tmpalloc = 0;
468 	if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
469 		wptmp.we_wordc = 0;
470 		wptmp.we_wordn = wptmp.we_offs + INITIAL;
471 		wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn);
472 		if (wptmp.we_wordv == NULL)
473 			return (WRDE_NOSPACE);
474 		wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
475 		for (si = 0; si < wptmp.we_offs; si++)
476 			wptmp.we_wordv[si] = NULL;
477 		tmpalloc = 1;
478 	}
479 
480 	/*
481 	 * The UNIX98 Posix conformance test suite requires
482 	 * wordexp() to not be a cancellation point.
483 	 */
484 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
485 
486 	/*
487 	 * Turn flags into shell options
488 	 */
489 	*optendp++ = '-';
490 	*optendp++ = (char)0x05;		/* ksh -^E */
491 	if (flags & WRDE_UNDEF)
492 		*optendp++ = 'u';
493 	if (flags & WRDE_NOCMD)
494 		*optendp++ = 'N';
495 	*optendp = '\0';
496 
497 	/*
498 	 * Make sure PWD is in the environment.
499 	 */
500 	if ((envp = _environ) == NULL) {
501 		/* can happen when processing a SunOS 4.x AOUT file */
502 		ev = NULL;
503 		n = 0;
504 	} else {
505 		for (n = 0; (ev = envp[n]) != NULL; n++) {
506 			if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0)
507 				break;
508 		}
509 	}
510 	if (ev == NULL) {	/* PWD missing from the environment */
511 		/* allocate a new environment */
512 		if ((env = malloc((n + 2) * sizeof (char *))) == NULL ||
513 		    (wd = malloc(PATH_MAX + 4)) == NULL)
514 			goto cleanup;
515 		for (i = 0; i < n; i++)
516 			env[i] = envp[i];
517 		(void) strcpy(wd, "PWD=");
518 		if (getcwd(&wd[4], PATH_MAX) == NULL)
519 			(void) strcpy(&wd[4], "/");
520 		env[i] = wd;
521 		env[i + 1] = NULL;
522 		envp = env;
523 	}
524 
525 	if ((error = posix_spawnattr_init(&attr)) != 0) {
526 		errno = error;
527 		goto cleanup;
528 	}
529 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
530 		(void) posix_spawnattr_destroy(&attr);
531 		errno = error;
532 		goto cleanup;
533 	}
534 
535 	/*
536 	 * Set up pipe from shell stdout to "fp" for us
537 	 */
538 	if (pipe(pv) < 0) {
539 		error = errno;
540 		(void) posix_spawnattr_destroy(&attr);
541 		(void) posix_spawn_file_actions_destroy(&fact);
542 		errno = error;
543 		goto cleanup;
544 	}
545 
546 	/*
547 	 * Spawn shell with -E word
548 	 */
549 	error = posix_spawnattr_setflags(&attr,
550 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
551 	if (error == 0)
552 		error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1);
553 	if (error == 0 && pv[0] != 1)
554 		error = posix_spawn_file_actions_addclose(&fact, pv[0]);
555 	if (error == 0 && pv[1] != 1)
556 		error = posix_spawn_file_actions_addclose(&fact, pv[1]);
557 	if (error == 0 && !(flags & WRDE_SHOWERR))
558 		error = posix_spawn_file_actions_addopen(&fact, 2,
559 		    "/dev/null", O_WRONLY, 0);
560 	path = __xpg4 ? xpg4_path : sun_path;
561 	argv[0] = strrchr(path, '/') + 1;
562 	argv[1] = options;
563 	argv[2] = (char *)word;
564 	argv[3] = NULL;
565 	if (error == 0)
566 		error = posix_spawn(&pid, path, &fact, &attr,
567 		    (char *const *)argv, (char *const *)envp);
568 	(void) posix_spawnattr_destroy(&attr);
569 	(void) posix_spawn_file_actions_destroy(&fact);
570 	(void) close(pv[1]);
571 	if (error) {
572 		(void) close(pv[0]);
573 		errno = error;
574 		goto cleanup;
575 	}
576 
577 	if ((fp = fdopen(pv[0], "rF")) == NULL) {
578 		error = errno;
579 		(void) close(pv[0]);
580 		errno = error;
581 		goto wait_cleanup;
582 	}
583 
584 	/*
585 	 * Read words from shell, separated with '\0'.
586 	 * Since there is no way to disable IFS splitting,
587 	 * it would be possible to separate the output with '\n'.
588 	 */
589 	cp = line = malloc(BUFSZ);
590 	if (line == NULL) {
591 		error = errno;
592 		(void) fclose(fp);
593 		errno = error;
594 		goto wait_cleanup;
595 	}
596 	eob = line + BUFSZ;
597 
598 	rv = 0;
599 	flockfile(fp);
600 	while ((i = getc_unlocked(fp)) != EOF) {
601 		*cp++ = (char)i;
602 		if (i == '\0') {
603 			cp = line;
604 			if ((rv = append(&wptmp, cp)) != 0) {
605 				break;
606 			}
607 		}
608 		if (cp == eob) {
609 			size_t bs = (eob - line);
610 			char *nl;
611 
612 			if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
613 				rv = WRDE_NOSPACE;
614 				break;
615 			}
616 			line = nl;
617 			cp = line + bs;
618 			eob = cp + BUFSZ;
619 		}
620 	}
621 	funlockfile(fp);
622 
623 	wptmp.we_wordp[wptmp.we_wordc] = NULL;
624 
625 	free(line);
626 	(void) fclose(fp);	/* kill shell if still writing */
627 
628 wait_cleanup:
629 	while (waitpid(pid, &status, 0) == -1) {
630 		if (errno != EINTR) {
631 			if (rv == 0)
632 				rv = WRDE_ERRNO;
633 			break;
634 		}
635 	}
636 	if (rv == 0)
637 		rv = WEXITSTATUS(status); /* shell WRDE_* status */
638 
639 cleanup:
640 	if (rv == 0)
641 		*wp = wptmp;
642 	else if (tmpalloc)
643 		wordfree(&wptmp);
644 
645 	if (env)
646 		free(env);
647 	if (wd)
648 		free(wd);
649 	/*
650 	 * Map ksh errors to wordexp() errors
651 	 */
652 	if (rv == 4)
653 		rv = WRDE_CMDSUB;
654 	else if (rv == 5)
655 		rv = WRDE_BADVAL;
656 	else if (rv == 6)
657 		rv = WRDE_SYNTAX;
658 
659 	(void) pthread_setcancelstate(cancel_state, NULL);
660 	return (rv);
661 }
662 
663 #endif /* WORDEXP_KSH93 */
664 
665 /*
666  * Append a word to the wordexp_t structure, growing it as necessary.
667  */
668 static int
669 append(wordexp_t *wp, char *str)
670 {
671 	char *cp;
672 	char **nwp;
673 
674 	/*
675 	 * We will be adding one entry and later adding
676 	 * one more NULL. So we need 2 more free slots.
677 	 */
678 	if ((wp->we_wordp + wp->we_wordc) ==
679 	    (wp->we_wordv + wp->we_wordn - 1)) {
680 		nwp = realloc(wp->we_wordv,
681 		    (wp->we_wordn + INITIAL) * sizeof (char *));
682 		if (nwp == NULL)
683 			return (WRDE_NOSPACE);
684 		wp->we_wordn += INITIAL;
685 		wp->we_wordv = nwp;
686 		wp->we_wordp = wp->we_wordv + wp->we_offs;
687 	}
688 	if ((cp = strdup(str)) == NULL)
689 		return (WRDE_NOSPACE);
690 	wp->we_wordp[wp->we_wordc++] = cp;
691 	return (0);
692 }
693 
694 /*
695  * Free all space owned by wordexp_t.
696  */
697 void
698 wordfree(wordexp_t *wp)
699 {
700 	size_t i;
701 
702 	if (wp->we_wordv == NULL)
703 		return;
704 	for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++)
705 		free(wp->we_wordv[i]);
706 	free((void *)wp->we_wordv);
707 	wp->we_wordc = 0;
708 	wp->we_wordv = NULL;
709 }
710