Home | History | Annotate | Line # | Download | only in gen
      1  1.4  christos /*	$NetBSD: wordexp.c,v 1.4 2024/01/20 14:52:47 christos Exp $	*/
      2  1.1       seb 
      3  1.1       seb /*-
      4  1.1       seb  * Copyright (c) 2002 Tim J. Robbins.
      5  1.1       seb  * All rights reserved.
      6  1.1       seb  *
      7  1.1       seb  * Redistribution and use in source and binary forms, with or without
      8  1.1       seb  * modification, are permitted provided that the following conditions
      9  1.1       seb  * are met:
     10  1.1       seb  * 1. Redistributions of source code must retain the above copyright
     11  1.1       seb  *    notice, this list of conditions and the following disclaimer.
     12  1.1       seb  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1       seb  *    notice, this list of conditions and the following disclaimer in the
     14  1.1       seb  *    documentation and/or other materials provided with the distribution.
     15  1.1       seb  *
     16  1.1       seb  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  1.1       seb  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  1.1       seb  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  1.1       seb  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  1.1       seb  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  1.1       seb  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  1.1       seb  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  1.1       seb  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  1.1       seb  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  1.1       seb  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  1.1       seb  * SUCH DAMAGE.
     27  1.1       seb  */
     28  1.1       seb 
     29  1.1       seb #include "namespace.h"
     30  1.1       seb #include <sys/cdefs.h>
     31  1.1       seb #include <sys/types.h>
     32  1.1       seb #include <assert.h>
     33  1.1       seb #include <sys/wait.h>
     34  1.1       seb #include <fcntl.h>
     35  1.1       seb #include <paths.h>
     36  1.1       seb #include <stdio.h>
     37  1.1       seb #include <stdlib.h>
     38  1.1       seb #include <string.h>
     39  1.1       seb #include <unistd.h>
     40  1.1       seb #include <wordexp.h>
     41  1.4  christos #include "extern.h"
     42  1.1       seb 
     43  1.1       seb #if defined(LIBC_SCCS) && !defined(lint)
     44  1.1       seb #if 0
     45  1.1       seb __FBSDID("$FreeBSD: /repoman/r/ncvs/src/lib/libc/gen/wordexp.c,v 1.5 2004/04/09 11:32:32 tjr Exp $");
     46  1.1       seb #else
     47  1.4  christos __RCSID("$NetBSD: wordexp.c,v 1.4 2024/01/20 14:52:47 christos Exp $");
     48  1.1       seb #endif
     49  1.1       seb #endif /* LIBC_SCCS and not lint */
     50  1.1       seb 
     51  1.1       seb static int	we_askshell(const char *, wordexp_t *, int);
     52  1.1       seb static int	we_check(const char *, int);
     53  1.1       seb 
     54  1.1       seb /*
     55  1.1       seb  * wordexp --
     56  1.1       seb  *	Perform shell word expansion on `words' and place the resulting list
     57  1.1       seb  *	of words in `we'. See wordexp(3).
     58  1.1       seb  *
     59  1.1       seb  */
     60  1.1       seb int
     61  1.1       seb wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags)
     62  1.1       seb {
     63  1.1       seb 	int error;
     64  1.1       seb 
     65  1.1       seb 	_DIAGASSERT(we != NULL);
     66  1.1       seb 	_DIAGASSERT(words != NULL);
     67  1.1       seb 	if (flags & WRDE_REUSE)
     68  1.1       seb 		wordfree(we);
     69  1.1       seb 	if ((flags & WRDE_APPEND) == 0) {
     70  1.1       seb 		we->we_wordc = 0;
     71  1.1       seb 		we->we_wordv = NULL;
     72  1.1       seb 		we->we_strings = NULL;
     73  1.1       seb 		we->we_nbytes = 0;
     74  1.1       seb 	}
     75  1.1       seb 	if ((error = we_check(words, flags)) != 0) {
     76  1.1       seb 		wordfree(we);
     77  1.1       seb 		return (error);
     78  1.1       seb 	}
     79  1.1       seb 	if ((error = we_askshell(words, we, flags)) != 0) {
     80  1.1       seb 		wordfree(we);
     81  1.1       seb 		return (error);
     82  1.1       seb 	}
     83  1.1       seb 	return (0);
     84  1.1       seb }
     85  1.1       seb 
     86  1.1       seb /*
     87  1.1       seb  * we_askshell --
     88  1.1       seb  *	Use the `wordexp' /bin/sh builtin function to do most of the work
     89  1.1       seb  *	in expanding the word string. This function is complicated by
     90  1.1       seb  *	memory management.
     91  1.1       seb  */
     92  1.1       seb static int
     93  1.1       seb we_askshell(const char *words, wordexp_t *we, int flags)
     94  1.1       seb {
     95  1.1       seb 	int pdes[2];			/* Pipe to child */
     96  1.1       seb 	size_t nwords, nbytes;		/* Number of words, bytes from child */
     97  1.1       seb 	int i;				/* Handy integer */
     98  1.3     lukem 	unsigned int ui;		/* For array iteration */
     99  1.1       seb 	size_t sofs;			/* Offset into we->we_strings */
    100  1.1       seb 	size_t vofs;			/* Offset into we->we_wordv */
    101  1.1       seb 	pid_t pid;			/* Process ID of child */
    102  1.1       seb 	int status;			/* Child exit status */
    103  1.2  christos 	const char *ifs;		/* IFS env. var. */
    104  1.1       seb 	char *np, *p;			/* Handy pointers */
    105  1.1       seb 	char *nstrings;			/* Temporary for realloc() */
    106  1.1       seb 	char **nwv;			/* Temporary for realloc() */
    107  1.1       seb 	FILE *fp;			/* Stream to read pipe */
    108  1.1       seb 	char *cmd;
    109  1.1       seb 
    110  1.1       seb 	if ((ifs = getenv("IFS")) == NULL)
    111  1.1       seb 		ifs = " \t\n";
    112  1.1       seb 	if (asprintf(&cmd, "wordexp%c%s\n", *ifs, words) < 0)
    113  1.1       seb 		return (WRDE_NOSPACE);
    114  1.1       seb 	if (pipe(pdes) < 0) {
    115  1.1       seb 		free(cmd);
    116  1.1       seb 		return (WRDE_ERRNO);
    117  1.1       seb 	}
    118  1.1       seb 	if ((fp = fdopen(pdes[0], "r")) == NULL) {
    119  1.1       seb 		free(cmd);
    120  1.1       seb 		return (WRDE_ERRNO);
    121  1.1       seb 	}
    122  1.1       seb 	if ((pid = fork()) < 0) {
    123  1.1       seb 		free(cmd);
    124  1.1       seb 		fclose(fp);
    125  1.1       seb 		close(pdes[1]);
    126  1.1       seb 		return (WRDE_ERRNO);
    127  1.1       seb 	}
    128  1.1       seb 	else if (pid == 0) {
    129  1.1       seb 		/*
    130  1.1       seb 		 * We are the child; just get /bin/sh to run the wordexp
    131  1.1       seb 		 * builtin on `words'.
    132  1.1       seb 		 */
    133  1.1       seb 		int devnull;
    134  1.1       seb 
    135  1.1       seb 		close(pdes[0]);
    136  1.1       seb 		if (pdes[1] != STDOUT_FILENO) {
    137  1.1       seb 			if (dup2(pdes[1], STDOUT_FILENO) < 0)
    138  1.1       seb 				_exit(1);
    139  1.1       seb 			close(pdes[1]);
    140  1.1       seb 		}
    141  1.1       seb 		if ((flags & WRDE_SHOWERR) == 0) {
    142  1.1       seb 			if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0666)) < 0)
    143  1.1       seb 				_exit(1);
    144  1.1       seb 			if (dup2(devnull, STDERR_FILENO) < 0)
    145  1.1       seb 				_exit(1);
    146  1.1       seb 			close(devnull);
    147  1.1       seb 		}
    148  1.1       seb 		execle(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u",
    149  1.1       seb 		    "-c", cmd, (char *)NULL, environ);
    150  1.1       seb 		_exit(1);
    151  1.1       seb 	}
    152  1.1       seb 
    153  1.1       seb 	/*
    154  1.1       seb 	 * We are the parent; read the output of the shell wordexp function,
    155  1.1       seb 	 * which is a decimal word count, an null, a decimal byte count,
    156  1.1       seb 	 * (not including terminating null bytes), a null and then followed
    157  1.1       seb 	 * by the expanded words separated by nulls.
    158  1.1       seb 	 */
    159  1.1       seb 	free(cmd);
    160  1.1       seb 	close(pdes[1]);
    161  1.1       seb 	/* read the word count */
    162  1.1       seb 	nwords = 0;
    163  1.1       seb 	while ((i = getc(fp)) != EOF) {
    164  1.1       seb 		if (i == '\0')
    165  1.1       seb 			break;
    166  1.1       seb 		nwords *= 10;
    167  1.1       seb 		nwords += (i - '0');
    168  1.1       seb 	}
    169  1.1       seb 	/* read the byte count */
    170  1.1       seb 	nbytes = 0;
    171  1.1       seb 	while ((i = getc(fp)) != EOF) {
    172  1.1       seb 		if (i == '\0')
    173  1.1       seb 			break;
    174  1.1       seb 		nbytes *= 10;
    175  1.1       seb 		nbytes += (i - '0');
    176  1.1       seb 	}
    177  1.1       seb 	if (i == EOF) {
    178  1.1       seb 		fclose(fp);
    179  1.1       seb 		waitpid(pid, &status, 0);
    180  1.1       seb 		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
    181  1.1       seb 	}
    182  1.1       seb 	nbytes += nwords;
    183  1.1       seb 
    184  1.1       seb 	/*
    185  1.1       seb 	 * Allocate or reallocate (when flags & WRDE_APPEND) the word vector
    186  1.1       seb 	 * and string storage buffers for the expanded words we're about to
    187  1.1       seb 	 * read from the child.
    188  1.1       seb 	 */
    189  1.1       seb 	sofs = we->we_nbytes;
    190  1.1       seb 	vofs = we->we_wordc;
    191  1.1       seb 	if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND))
    192  1.1       seb 		vofs += we->we_offs;
    193  1.1       seb 	we->we_wordc += nwords;
    194  1.1       seb 	we->we_nbytes += nbytes;
    195  1.1       seb 	if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
    196  1.1       seb 	    (flags & WRDE_DOOFFS ?  we->we_offs : 0)) *
    197  1.1       seb 	    sizeof(char *))) == NULL) {
    198  1.1       seb 		fclose(fp);
    199  1.1       seb 		waitpid(pid, &status, 0);
    200  1.1       seb 		return (WRDE_NOSPACE);
    201  1.1       seb 	}
    202  1.1       seb 	we->we_wordv = nwv;
    203  1.1       seb 	if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
    204  1.1       seb 		fclose(fp);
    205  1.1       seb 		waitpid(pid, &status, 0);
    206  1.1       seb 		return (WRDE_NOSPACE);
    207  1.1       seb 	}
    208  1.3     lukem 	for (ui = 0; ui < vofs; ui++)
    209  1.3     lukem 		if (we->we_wordv[ui] != NULL)
    210  1.3     lukem 			we->we_wordv[ui] += nstrings - we->we_strings;
    211  1.1       seb 	we->we_strings = nstrings;
    212  1.1       seb 
    213  1.1       seb 	if (fread(we->we_strings + sofs, sizeof(char), nbytes, fp) != nbytes) {
    214  1.1       seb 		fclose(fp);
    215  1.1       seb 		waitpid(pid, &status, 0);
    216  1.1       seb 		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
    217  1.1       seb 	}
    218  1.1       seb 
    219  1.1       seb 	if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) ||
    220  1.1       seb 	    WEXITSTATUS(status) != 0) {
    221  1.1       seb 		fclose(fp);
    222  1.1       seb 		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
    223  1.1       seb 	}
    224  1.1       seb 	fclose(fp);
    225  1.1       seb 
    226  1.1       seb 	/*
    227  1.1       seb 	 * Break the null-terminated expanded word strings out into
    228  1.1       seb 	 * the vector.
    229  1.1       seb 	 */
    230  1.1       seb 	if (vofs == 0 && flags & WRDE_DOOFFS)
    231  1.1       seb 		while (vofs < we->we_offs)
    232  1.1       seb 			we->we_wordv[vofs++] = NULL;
    233  1.1       seb 	p = we->we_strings + sofs;
    234  1.1       seb 	while (nwords-- != 0) {
    235  1.1       seb 		we->we_wordv[vofs++] = p;
    236  1.1       seb 		if ((np = memchr(p, '\0', nbytes)) == NULL)
    237  1.1       seb 			return (WRDE_NOSPACE);	/* XXX */
    238  1.1       seb 		nbytes -= np - p + 1;
    239  1.1       seb 		p = np + 1;
    240  1.1       seb 	}
    241  1.1       seb 	we->we_wordv[vofs] = NULL;
    242  1.1       seb 
    243  1.1       seb 	return (0);
    244  1.1       seb }
    245  1.1       seb 
    246  1.1       seb /*
    247  1.1       seb  * we_check --
    248  1.1       seb  *	Check that the string contains none of the following unquoted
    249  1.1       seb  *	special characters: <newline> |&;<>(){}
    250  1.1       seb  *	or command substitutions when WRDE_NOCMD is set in flags.
    251  1.1       seb  */
    252  1.1       seb static int
    253  1.1       seb we_check(const char *words, int flags)
    254  1.1       seb {
    255  1.1       seb 	char c;
    256  1.1       seb 	int dquote, level, quote, squote;
    257  1.1       seb 
    258  1.1       seb 	quote = squote = dquote = 0;
    259  1.1       seb 	while ((c = *words++) != '\0') {
    260  1.1       seb 		switch (c) {
    261  1.1       seb 		case '\\':
    262  1.1       seb 			quote ^= 1;
    263  1.1       seb 			continue;
    264  1.1       seb 		case '\'':
    265  1.1       seb 			if (quote + dquote == 0)
    266  1.1       seb 				squote ^= 1;
    267  1.1       seb 			break;
    268  1.1       seb 		case '"':
    269  1.1       seb 			if (quote + squote == 0)
    270  1.1       seb 				dquote ^= 1;
    271  1.1       seb 			break;
    272  1.1       seb 		case '`':
    273  1.1       seb 			if (quote + squote == 0 && flags & WRDE_NOCMD)
    274  1.1       seb 				return (WRDE_CMDSUB);
    275  1.1       seb 			while ((c = *words++) != '\0' && c != '`')
    276  1.1       seb 				if (c == '\\' && (c = *words++) == '\0')
    277  1.1       seb 					break;
    278  1.1       seb 			if (c == '\0')
    279  1.1       seb 				return (WRDE_SYNTAX);
    280  1.1       seb 			break;
    281  1.1       seb 		case '|': case '&': case ';': case '<': case '>':
    282  1.1       seb 		case '{': case '}': case '(': case ')': case '\n':
    283  1.1       seb 			if (quote + squote + dquote == 0)
    284  1.1       seb 				return (WRDE_BADCHAR);
    285  1.1       seb 			break;
    286  1.1       seb 		case '$':
    287  1.1       seb 			if ((c = *words++) == '\0')
    288  1.1       seb 				break;
    289  1.1       seb 			else if (quote + squote == 0 && c == '(') {
    290  1.1       seb 				if (flags & WRDE_NOCMD && *words != '(')
    291  1.1       seb 					return (WRDE_CMDSUB);
    292  1.1       seb 				level = 1;
    293  1.1       seb 				while ((c = *words++) != '\0') {
    294  1.1       seb 					if (c == '\\') {
    295  1.1       seb 						if ((c = *words++) == '\0')
    296  1.1       seb 							break;
    297  1.1       seb 					} else if (c == '(')
    298  1.1       seb 						level++;
    299  1.1       seb 					else if (c == ')' && --level == 0)
    300  1.1       seb 						break;
    301  1.1       seb 				}
    302  1.1       seb 				if (c == '\0' || level != 0)
    303  1.1       seb 					return (WRDE_SYNTAX);
    304  1.1       seb 			} else if (quote + squote == 0 && c == '{') {
    305  1.1       seb 				level = 1;
    306  1.1       seb 				while ((c = *words++) != '\0') {
    307  1.1       seb 					if (c == '\\') {
    308  1.1       seb 						if ((c = *words++) == '\0')
    309  1.1       seb 							break;
    310  1.1       seb 					} else if (c == '{')
    311  1.1       seb 						level++;
    312  1.1       seb 					else if (c == '}' && --level == 0)
    313  1.1       seb 						break;
    314  1.1       seb 				}
    315  1.1       seb 				if (c == '\0' || level != 0)
    316  1.1       seb 					return (WRDE_SYNTAX);
    317  1.1       seb 			} else
    318  1.1       seb 				c = *--words;
    319  1.1       seb 			break;
    320  1.1       seb 		default:
    321  1.1       seb 			break;
    322  1.1       seb 		}
    323  1.1       seb 		quote = 0;
    324  1.1       seb 	}
    325  1.1       seb 	if (quote + squote + dquote != 0)
    326  1.1       seb 		return (WRDE_SYNTAX);
    327  1.1       seb 
    328  1.1       seb 	return (0);
    329  1.1       seb }
    330  1.1       seb 
    331  1.1       seb /*
    332  1.1       seb  * wordfree --
    333  1.1       seb  *	Free the result of wordexp(). See wordexp(3).
    334  1.1       seb  *
    335  1.1       seb  */
    336  1.1       seb void
    337  1.1       seb wordfree(wordexp_t *we)
    338  1.1       seb {
    339  1.1       seb 	_DIAGASSERT(we != NULL);
    340  1.1       seb 	free(we->we_wordv);
    341  1.1       seb 	free(we->we_strings);
    342  1.1       seb 	we->we_wordv = NULL;
    343  1.1       seb 	we->we_strings = NULL;
    344  1.1       seb 	we->we_nbytes = 0;
    345  1.1       seb 	we->we_wordc = 0;
    346  1.1       seb }
    347