wordexp.c raw

   1  #include <wordexp.h>
   2  #include <unistd.h>
   3  #include <stdio.h>
   4  #include <string.h>
   5  #include <limits.h>
   6  #include <stdint.h>
   7  #include <stdlib.h>
   8  #include <sys/wait.h>
   9  #include <signal.h>
  10  #include <errno.h>
  11  #include <fcntl.h>
  12  #include "pthread_impl.h"
  13  
  14  static void reap(pid_t pid)
  15  {
  16  	int status;
  17  	while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
  18  }
  19  
  20  static char *getword(FILE *f)
  21  {
  22  	char *s = 0;
  23  	return getdelim(&s, (size_t [1]){0}, 0, f) < 0 ? 0 : s;
  24  }
  25  
  26  static int do_wordexp(const char *s, wordexp_t *we, int flags)
  27  {
  28  	size_t i, l;
  29  	int sq=0, dq=0;
  30  	size_t np=0;
  31  	char *w, **tmp;
  32  	char *redir = (flags & WRDE_SHOWERR) ? "" : "2>/dev/null";
  33  	int err = 0;
  34  	FILE *f;
  35  	size_t wc = 0;
  36  	char **wv = 0;
  37  	int p[2];
  38  	pid_t pid;
  39  	sigset_t set;
  40  
  41  	if (flags & WRDE_REUSE) wordfree(we);
  42  
  43  	if (flags & WRDE_NOCMD) for (i=0; s[i]; i++) switch (s[i]) {
  44  	case '\\':
  45  		if (!sq && !s[++i]) return WRDE_SYNTAX;
  46  		break;
  47  	case '\'':
  48  		if (!dq) sq^=1;
  49  		break;
  50  	case '"':
  51  		if (!sq) dq^=1;
  52  		break;
  53  	case '(':
  54  		if (np) {
  55  			np++;
  56  			break;
  57  		}
  58  	case ')':
  59  		if (np) {
  60  			np--;
  61  			break;
  62  		}
  63  	case '\n':
  64  	case '|':
  65  	case '&':
  66  	case ';':
  67  	case '<':
  68  	case '>':
  69  	case '{':
  70  	case '}':
  71  		if (!(sq|dq|np)) return WRDE_BADCHAR;
  72  		break;
  73  	case '$':
  74  		if (sq) break;
  75  		if (s[i+1]=='(' && s[i+2]=='(') {
  76  			i += 2;
  77  			np += 2;
  78  			break;
  79  		} else if (s[i+1] != '(') break;
  80  	case '`':
  81  		if (sq) break;
  82  		return WRDE_CMDSUB;
  83  	}
  84  
  85  	if (flags & WRDE_APPEND) {
  86  		wc = we->we_wordc;
  87  		wv = we->we_wordv;
  88  	}
  89  
  90  	i = wc;
  91  	if (flags & WRDE_DOOFFS) {
  92  		if (we->we_offs > SIZE_MAX/sizeof(void *)/4)
  93  			goto nospace;
  94  		i += we->we_offs;
  95  	} else {
  96  		we->we_offs = 0;
  97  	}
  98  
  99  	if (pipe2(p, O_CLOEXEC) < 0) goto nospace;
 100  	__block_all_sigs(&set);
 101  	pid = fork();
 102  	__restore_sigs(&set);
 103  	if (pid < 0) {
 104  		close(p[0]);
 105  		close(p[1]);
 106  		goto nospace;
 107  	}
 108  	if (!pid) {
 109  		if (p[1] == 1) fcntl(1, F_SETFD, 0);
 110  		else dup2(p[1], 1);
 111  		execl("/bin/sh", "sh", "-c",
 112  			"eval \"printf %s\\\\\\\\0 x $1 $2\"",
 113  			"sh", s, redir, (char *)0);
 114  		_exit(1);
 115  	}
 116  	close(p[1]);
 117  	
 118  	f = fdopen(p[0], "r");
 119  	if (!f) {
 120  		close(p[0]);
 121  		kill(pid, SIGKILL);
 122  		reap(pid);
 123  		goto nospace;
 124  	}
 125  
 126  	l = wv ? i+1 : 0;
 127  
 128  	free(getword(f));
 129  	if (feof(f)) {
 130  		fclose(f);
 131  		reap(pid);
 132  		return WRDE_SYNTAX;
 133  	}
 134  
 135  	while ((w = getword(f))) {
 136  		if (i+1 >= l) {
 137  			l += l/2+10;
 138  			tmp = realloc(wv, l*sizeof(char *));
 139  			if (!tmp) break;
 140  			wv = tmp;
 141  		}
 142  		wv[i++] = w;
 143  		wv[i] = 0;
 144  	}
 145  	if (!feof(f)) err = WRDE_NOSPACE;
 146  
 147  	fclose(f);
 148  	reap(pid);
 149  
 150  	if (!wv) wv = calloc(i+1, sizeof *wv);
 151  
 152  	we->we_wordv = wv;
 153  	we->we_wordc = i;
 154  
 155  	if (flags & WRDE_DOOFFS) {
 156  		if (wv) for (i=we->we_offs; i; i--)
 157  			we->we_wordv[i-1] = 0;
 158  		we->we_wordc -= we->we_offs;
 159  	}
 160  	return err;
 161  
 162  nospace:
 163  	if (!(flags & WRDE_APPEND)) {
 164  		we->we_wordc = 0;
 165  		we->we_wordv = 0;
 166  	}
 167  	return WRDE_NOSPACE;
 168  }
 169  
 170  int wordexp(const char *restrict s, wordexp_t *restrict we, int flags)
 171  {
 172  	int r, cs;
 173  	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
 174  	r = do_wordexp(s, we, flags);
 175  	pthread_setcancelstate(cs, 0);
 176  	return r;
 177  }
 178  
 179  void wordfree(wordexp_t *we)
 180  {
 181  	size_t i;
 182  	if (!we->we_wordv) return;
 183  	for (i=0; i<we->we_wordc; i++) free(we->we_wordv[we->we_offs+i]);
 184  	free(we->we_wordv);
 185  	we->we_wordv = 0;
 186  	we->we_wordc = 0;
 187  }
 188