Skip to content
Snippets Groups Projects
cli_hush.c 93.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    static int free_pipe_list(struct pipe *head, int indent)
    {
    	int rcode=0;   /* if list has no members */
    	struct pipe *pi, *next;
    	char *ind = indenter(indent);
    	for (pi=head; pi; pi=next) {
    		final_printf("%s pipe reserved mode %d\n", ind, pi->r_mode);
    		rcode = free_pipe(pi, indent);
    		final_printf("%s pipe followup code %d\n", ind, pi->followup);
    		next=pi->next;
    		pi->next=NULL;
    		free(pi);
    	}
    	return rcode;
    }
    
    /* Select which version we will use */
    static int run_list(struct pipe *pi)
    {
    	int rcode=0;
    #ifndef __U_BOOT__
    	if (fake_mode==0) {
    #endif
    		rcode = run_list_real(pi);
    #ifndef __U_BOOT__
    	}
    #endif
    	/* free_pipe_list has the side effect of clearing memory
    	 * In the long run that function can be merged with run_list_real,
    	 * but doing that now would hobble the debugging effort. */
    	free_pipe_list(pi,0);
    	return rcode;
    }
    
    /* The API for glob is arguably broken.  This routine pushes a non-matching
     * string into the output structure, removing non-backslashed backslashes.
     * If someone can prove me wrong, by performing this function within the
     * original glob(3) api, feel free to rewrite this routine into oblivion.
     * Return code (0 vs. GLOB_NOSPACE) matches glob(3).
     * XXX broken if the last character is '\\', check that before calling.
     */
    #ifndef __U_BOOT__
    static int globhack(const char *src, int flags, glob_t *pglob)
    {
    	int cnt=0, pathc;
    	const char *s;
    	char *dest;
    	for (cnt=1, s=src; s && *s; s++) {
    		if (*s == '\\') s++;
    		cnt++;
    	}
    	dest = malloc(cnt);
    	if (!dest) return GLOB_NOSPACE;
    	if (!(flags & GLOB_APPEND)) {
    		pglob->gl_pathv=NULL;
    		pglob->gl_pathc=0;
    		pglob->gl_offs=0;
    		pglob->gl_offs=0;
    	}
    	pathc = ++pglob->gl_pathc;
    	pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv));
    	if (pglob->gl_pathv == NULL) return GLOB_NOSPACE;
    	pglob->gl_pathv[pathc-1]=dest;
    	pglob->gl_pathv[pathc]=NULL;
    	for (s=src; s && *s; s++, dest++) {
    		if (*s == '\\') s++;
    		*dest = *s;
    	}
    	*dest='\0';
    	return 0;
    }
    
    /* XXX broken if the last character is '\\', check that before calling */
    static int glob_needed(const char *s)
    {
    	for (; *s; s++) {
    		if (*s == '\\') s++;
    		if (strchr("*[?",*s)) return 1;
    	}
    	return 0;
    }
    
    #if 0
    static void globprint(glob_t *pglob)
    {
    	int i;
    	debug_printf("glob_t at %p:\n", pglob);
    	debug_printf("  gl_pathc=%d  gl_pathv=%p  gl_offs=%d  gl_flags=%d\n",
    		pglob->gl_pathc, pglob->gl_pathv, pglob->gl_offs, pglob->gl_flags);
    	for (i=0; i<pglob->gl_pathc; i++)
    		debug_printf("pglob->gl_pathv[%d] = %p = %s\n", i,
    			pglob->gl_pathv[i], pglob->gl_pathv[i]);
    }
    #endif
    
    static int xglob(o_string *dest, int flags, glob_t *pglob)
    {
    	int gr;
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* short-circuit for null word */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	/* we can code this better when the debug_printf's are gone */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	if (dest->length == 0) {
    		if (dest->nonnull) {
    			/* bash man page calls this an "explicit" null */
    			gr = globhack(dest->data, flags, pglob);
    			debug_printf("globhack returned %d\n",gr);
    		} else {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			return 0;
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	} else if (glob_needed(dest->data)) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		gr = glob(dest->data, flags, NULL, pglob);
    		debug_printf("glob returned %d\n",gr);
    		if (gr == GLOB_NOMATCH) {
    			/* quote removal, or more accurately, backslash removal */
    			gr = globhack(dest->data, flags, pglob);
    			debug_printf("globhack returned %d\n",gr);
    		}
    	} else {
    		gr = globhack(dest->data, flags, pglob);
    		debug_printf("globhack returned %d\n",gr);
    	}
    	if (gr == GLOB_NOSPACE)
    		error_msg_and_die("out of memory during glob");
    	if (gr != 0) { /* GLOB_ABORTED ? */
    		error_msg("glob(3) error %d",gr);
    	}
    	/* globprint(glob_target); */
    	return gr;
    }
    #endif
    
    
    #ifdef __U_BOOT__
    static char *get_dollar_var(char ch);
    #endif
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /* This is used to get/check local shell variables */
    
    char *get_local_var(const char *s)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	struct variables *cur;
    
    	if (!s)
    		return NULL;
    
    
    #ifdef __U_BOOT__
    	if (*s == '$')
    		return get_dollar_var(s[1]);
    #endif
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	for (cur = top_vars; cur; cur=cur->next)
    		if(strcmp(cur->name, s)==0)
    			return cur->value;
    	return NULL;
    }
    
    /* This is used to set local shell variables
       flg_export==0 if only local (not exporting) variable
       flg_export==1 if "new" exporting environ
       flg_export>1  if current startup environ (not call putenv()) */
    
    int set_local_var(const char *s, int flg_export)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	char *name, *value;
    	int result=0;
    	struct variables *cur;
    
    
    #ifdef __U_BOOT__
    	/* might be possible! */
    	if (!isalpha(*s))
    		return -1;
    #endif
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	name=strdup(s);
    
    #ifdef __U_BOOT__
    	if (getenv(name) != NULL) {
    		printf ("ERROR: "
    
    				"There is a global environment variable with the same name.\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		return -1;
    	}
    #endif
    	/* Assume when we enter this function that we are already in
    	 * NAME=VALUE format.  So the first order of business is to
    	 * split 's' on the '=' into 'name' and 'value' */
    	value = strchr(name, '=');
    
    	if (value == NULL || *(value + 1) == 0) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		free(name);
    		return -1;
    	}
    	*value++ = 0;
    
    	for(cur = top_vars; cur; cur = cur->next) {
    		if(strcmp(cur->name, name)==0)
    			break;
    	}
    
    	if(cur) {
    		if(strcmp(cur->value, value)==0) {
    			if(flg_export>0 && cur->flg_export==0)
    				cur->flg_export=flg_export;
    			else
    				result++;
    		} else {
    			if(cur->flg_read_only) {
    				error_msg("%s: readonly variable", name);
    				result = -1;
    			} else {
    				if(flg_export>0 || cur->flg_export>1)
    					cur->flg_export=1;
    				free(cur->value);
    
    				cur->value = strdup(value);
    			}
    		}
    	} else {
    		cur = malloc(sizeof(struct variables));
    		if(!cur) {
    			result = -1;
    		} else {
    			cur->name = strdup(name);
    
    Kim Phillips's avatar
    Kim Phillips committed
    			if (cur->name == NULL) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				free(cur);
    				result = -1;
    			} else {
    				struct variables *bottom = top_vars;
    				cur->value = strdup(value);
    
    Kim Phillips's avatar
    Kim Phillips committed
    				cur->next = NULL;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				cur->flg_export = flg_export;
    				cur->flg_read_only = 0;
    				while(bottom->next) bottom=bottom->next;
    				bottom->next = cur;
    			}
    		}
    	}
    
    #ifndef __U_BOOT__
    	if(result==0 && cur->flg_export==1) {
    		*(value-1) = '=';
    		result = putenv(name);
    	} else {
    #endif
    		free(name);
    #ifndef __U_BOOT__
    		if(result>0)            /* equivalent to previous set */
    			result = 0;
    	}
    #endif
    	return result;
    }
    
    
    void unset_local_var(const char *name)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	struct variables *cur;
    
    	if (name) {
    		for (cur = top_vars; cur; cur=cur->next) {
    			if(strcmp(cur->name, name)==0)
    				break;
    		}
    
    Kim Phillips's avatar
    Kim Phillips committed
    		if (cur != NULL) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			struct variables *next = top_vars;
    			if(cur->flg_read_only) {
    				error_msg("%s: readonly variable", name);
    				return;
    			} else {
    
    #ifndef __U_BOOT__
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				if(cur->flg_export)
    					unsetenv(cur->name);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				free(cur->name);
    				free(cur->value);
    				while (next->next != cur)
    					next = next->next;
    				next->next = cur->next;
    			}
    			free(cur);
    		}
    	}
    }
    
    static int is_assignment(const char *s)
    {
    
    	if (s == NULL)
    		return 0;
    
    	if (!isalpha(*s)) return 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	++s;
    	while(isalnum(*s) || *s=='_') ++s;
    	return *s=='=';
    }
    
    #ifndef __U_BOOT__
    /* the src parameter allows us to peek forward to a possible &n syntax
     * for file descriptor duplication, e.g., "2>&1".
     * Return code is 0 normally, 1 if a syntax error is detected in src.
     * Resource errors (in xmalloc) cause the process to exit */
    static int setup_redirect(struct p_context *ctx, int fd, redir_type style,
    	struct in_str *input)
    {
    	struct child_prog *child=ctx->child;
    	struct redir_struct *redir = child->redirects;
    	struct redir_struct *last_redir=NULL;
    
    	/* Create a new redir_struct and drop it onto the end of the linked list */
    	while(redir) {
    		last_redir=redir;
    		redir=redir->next;
    	}
    	redir = xmalloc(sizeof(struct redir_struct));
    	redir->next=NULL;
    	redir->word.gl_pathv=NULL;
    	if (last_redir) {
    		last_redir->next=redir;
    	} else {
    		child->redirects=redir;
    	}
    
    	redir->type=style;
    	redir->fd= (fd==-1) ? redir_table[style].default_fd : fd ;
    
    	debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip);
    
    	/* Check for a '2>&1' type redirect */
    	redir->dup = redirect_dup_num(input);
    	if (redir->dup == -2) return 1;  /* syntax error */
    	if (redir->dup != -1) {
    		/* Erik had a check here that the file descriptor in question
    		 * is legit; I postpone that to "run time"
    		 * A "-" representation of "close me" shows up as a -3 here */
    		debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup);
    	} else {
    		/* We do _not_ try to open the file that src points to,
    		 * since we need to return and let src be expanded first.
    		 * Set ctx->pending_redirect, so we know what to do at the
    		 * end of the next parsed word.
    		 */
    		ctx->pending_redirect = redir;
    	}
    	return 0;
    }
    #endif
    
    
    Kim Phillips's avatar
    Kim Phillips committed
    static struct pipe *new_pipe(void)
    {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	struct pipe *pi;
    	pi = xmalloc(sizeof(struct pipe));
    	pi->num_progs = 0;
    	pi->progs = NULL;
    	pi->next = NULL;
    	pi->followup = 0;  /* invalid */
    
    	pi->r_mode = RES_NONE;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return pi;
    }
    
    static void initialize_context(struct p_context *ctx)
    {
    	ctx->pipe=NULL;
    #ifndef __U_BOOT__
    	ctx->pending_redirect=NULL;
    #endif
    	ctx->child=NULL;
    	ctx->list_head=new_pipe();
    	ctx->pipe=ctx->list_head;
    	ctx->w=RES_NONE;
    	ctx->stack=NULL;
    #ifdef __U_BOOT__
    	ctx->old_flag=0;
    #endif
    	done_command(ctx);   /* creates the memory for working child */
    }
    
    /* normal return is 0
     * if a reserved word is found, and processed, return 1
     * should handle if, then, elif, else, fi, for, while, until, do, done.
     * case, function, and select are obnoxious, save those for later.
     */
    
    struct reserved_combo {
    	char *literal;
    	int code;
    	long flag;
    };
    /* Mostly a list of accepted follow-up reserved words.
     * FLAG_END means we are done with the sequence, and are ready
     * to turn the compound list into a command.
     * FLAG_START means the word must start a new compound list.
     */
    static struct reserved_combo reserved_list[] = {
    	{ "if",    RES_IF,    FLAG_THEN | FLAG_START },
    	{ "then",  RES_THEN,  FLAG_ELIF | FLAG_ELSE | FLAG_FI },
    	{ "elif",  RES_ELIF,  FLAG_THEN },
    	{ "else",  RES_ELSE,  FLAG_FI   },
    	{ "fi",    RES_FI,    FLAG_END  },
    	{ "for",   RES_FOR,   FLAG_IN   | FLAG_START },
    	{ "while", RES_WHILE, FLAG_DO   | FLAG_START },
    	{ "until", RES_UNTIL, FLAG_DO   | FLAG_START },
    	{ "in",    RES_IN,    FLAG_DO   },
    	{ "do",    RES_DO,    FLAG_DONE },
    	{ "done",  RES_DONE,  FLAG_END  }
    };
    #define NRES (sizeof(reserved_list)/sizeof(struct reserved_combo))
    
    
    Kim Phillips's avatar
    Kim Phillips committed
    static int reserved_word(o_string *dest, struct p_context *ctx)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	struct reserved_combo *r;
    	for (r=reserved_list;
    		r<reserved_list+NRES; r++) {
    		if (strcmp(dest->data, r->literal) == 0) {
    			debug_printf("found reserved word %s, code %d\n",r->literal,r->code);
    			if (r->flag & FLAG_START) {
    				struct p_context *new = xmalloc(sizeof(struct p_context));
    				debug_printf("push stack\n");
    				if (ctx->w == RES_IN || ctx->w == RES_FOR) {
    					syntax();
    					free(new);
    					ctx->w = RES_SNTX;
    					b_reset(dest);
    					return 1;
    				}
    				*new = *ctx;   /* physical copy */
    				initialize_context(ctx);
    				ctx->stack=new;
    			} else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<<r->code))) {
    				syntax();
    				ctx->w = RES_SNTX;
    				b_reset(dest);
    				return 1;
    			}
    			ctx->w=r->code;
    			ctx->old_flag = r->flag;
    			if (ctx->old_flag & FLAG_END) {
    				struct p_context *old;
    				debug_printf("pop stack\n");
    				done_pipe(ctx,PIPE_SEQ);
    				old = ctx->stack;
    				old->child->group = ctx->list_head;
    #ifndef __U_BOOT__
    				old->child->subshell = 0;
    #endif
    				*ctx = *old;   /* physical copy */
    				free(old);
    			}
    			b_reset (dest);
    			return 1;
    		}
    	}
    	return 0;
    }
    
    /* normal return is 0.
     * Syntax or xglob errors return 1. */
    static int done_word(o_string *dest, struct p_context *ctx)
    {
    	struct child_prog *child=ctx->child;
    #ifndef __U_BOOT__
    	glob_t *glob_target;
    	int gr, flags = 0;
    #else
    	char *str, *s;
    	int argc, cnt;
    #endif
    
    	debug_printf("done_word: %s %p\n", dest->data, child);
    	if (dest->length == 0 && !dest->nonnull) {
    		debug_printf("  true null, ignored\n");
    		return 0;
    	}
    #ifndef __U_BOOT__
    	if (ctx->pending_redirect) {
    		glob_target = &ctx->pending_redirect->word;
    	} else {
    #endif
    		if (child->group) {
    			syntax();
    			return 1;  /* syntax error, groups and arglists don't mix */
    		}
    		if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) {
    			debug_printf("checking %s for reserved-ness\n",dest->data);
    			if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX;
    		}
    #ifndef __U_BOOT__
    		glob_target = &child->glob_result;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (child->argv) flags |= GLOB_APPEND;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #else
    		for (cnt = 1, s = dest->data; s && *s; s++) {
    			if (*s == '\\') s++;
    			cnt++;
    		}
    		str = malloc(cnt);
    		if (!str) return 1;
    		if ( child->argv == NULL) {
    			child->argc=0;
    		}
    		argc = ++child->argc;
    		child->argv = realloc(child->argv, (argc+1)*sizeof(*child->argv));
    
    		if (child->argv == NULL) {
    			free(str);
    			return 1;
    		}
    
    		child->argv_nonnull = realloc(child->argv_nonnull,
    					(argc+1)*sizeof(*child->argv_nonnull));
    
    		if (child->argv_nonnull == NULL) {
    			free(str);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		child->argv[argc-1]=str;
    
    		child->argv_nonnull[argc-1] = dest->nonnull;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		child->argv[argc]=NULL;
    
    		child->argv_nonnull[argc] = 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		for (s = dest->data; s && *s; s++,str++) {
    			if (*s == '\\') s++;
    			*str = *s;
    		}
    		*str = '\0';
    #endif
    #ifndef __U_BOOT__
    	}
    	gr = xglob(dest, flags, glob_target);
    	if (gr != 0) return 1;
    #endif
    
    	b_reset(dest);
    #ifndef __U_BOOT__
    	if (ctx->pending_redirect) {
    		ctx->pending_redirect=NULL;
    		if (glob_target->gl_pathc != 1) {
    			error_msg("ambiguous redirect");
    			return 1;
    		}
    	} else {
    		child->argv = glob_target->gl_pathv;
    	}
    #endif
    	if (ctx->w == RES_FOR) {
    		done_word(dest,ctx);
    		done_pipe(ctx,PIPE_SEQ);
    	}
    	return 0;
    }
    
    /* The only possible error here is out of memory, in which case
     * xmalloc exits. */
    static int done_command(struct p_context *ctx)
    {
    	/* The child is really already in the pipe structure, so
    	 * advance the pipe counter and make a new, null child.
    	 * Only real trickiness here is that the uncommitted
    	 * child structure, to which ctx->child points, is not
    	 * counted in pi->num_progs. */
    	struct pipe *pi=ctx->pipe;
    	struct child_prog *prog=ctx->child;
    
    	if (prog && prog->group == NULL
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		 && prog->argv == NULL
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #ifndef __U_BOOT__
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		 && prog->redirects == NULL) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #else
    										) {
    #endif
    		debug_printf("done_command: skipping null command\n");
    		return 0;
    	} else if (prog) {
    		pi->num_progs++;
    		debug_printf("done_command: num_progs incremented to %d\n",pi->num_progs);
    	} else {
    		debug_printf("done_command: initializing\n");
    	}
    	pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
    
    	prog = pi->progs + pi->num_progs;
    #ifndef __U_BOOT__
    	prog->redirects = NULL;
    #endif
    	prog->argv = NULL;
    
    	prog->argv_nonnull = NULL;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #ifndef __U_BOOT__
    	prog->is_stopped = 0;
    #endif
    	prog->group = NULL;
    #ifndef __U_BOOT__
    	prog->glob_result.gl_pathv = NULL;
    	prog->family = pi;
    #endif
    	prog->sp = 0;
    	ctx->child = prog;
    	prog->type = ctx->type;
    
    	/* but ctx->pipe and ctx->list_head remain unchanged */
    	return 0;
    }
    
    static int done_pipe(struct p_context *ctx, pipe_style type)
    {
    	struct pipe *new_p;
    	done_command(ctx);  /* implicit closure of previous command */
    	debug_printf("done_pipe, type %d\n", type);
    	ctx->pipe->followup = type;
    	ctx->pipe->r_mode = ctx->w;
    	new_p=new_pipe();
    	ctx->pipe->next = new_p;
    	ctx->pipe = new_p;
    	ctx->child = NULL;
    	done_command(ctx);  /* set up new pipe to accept commands */
    	return 0;
    }
    
    #ifndef __U_BOOT__
    /* peek ahead in the in_str to find out if we have a "&n" construct,
     * as in "2>&1", that represents duplicating a file descriptor.
     * returns either -2 (syntax error), -1 (no &), or the number found.
     */
    static int redirect_dup_num(struct in_str *input)
    {
    	int ch, d=0, ok=0;
    	ch = b_peek(input);
    	if (ch != '&') return -1;
    
    	b_getch(input);  /* get the & */
    	ch=b_peek(input);
    	if (ch == '-') {
    		b_getch(input);
    		return -3;  /* "-" represents "close me" */
    	}
    	while (isdigit(ch)) {
    		d = d*10+(ch-'0');
    		ok=1;
    		b_getch(input);
    		ch = b_peek(input);
    	}
    	if (ok) return d;
    
    	error_msg("ambiguous redirect");
    	return -2;
    }
    
    /* If a redirect is immediately preceded by a number, that number is
     * supposed to tell which file descriptor to redirect.  This routine
     * looks for such preceding numbers.  In an ideal world this routine
     * needs to handle all the following classes of redirects...
     *     echo 2>foo     # redirects fd  2 to file "foo", nothing passed to echo
     *     echo 49>foo    # redirects fd 49 to file "foo", nothing passed to echo
     *     echo -2>foo    # redirects fd  1 to file "foo",    "-2" passed to echo
     *     echo 49x>foo   # redirects fd  1 to file "foo",   "49x" passed to echo
     * A -1 output from this program means no valid number was found, so the
     * caller should use the appropriate default for this redirection.
     */
    static int redirect_opt_num(o_string *o)
    {
    	int num;
    
    	if (o->length==0) return -1;
    	for(num=0; num<o->length; num++) {
    		if (!isdigit(*(o->data+num))) {
    			return -1;
    		}
    	}
    	/* reuse num (and save an int) */
    	num=atoi(o->data);
    	b_reset(o);
    	return num;
    }
    
    FILE *generate_stream_from_list(struct pipe *head)
    {
    	FILE *pf;
    #if 1
    	int pid, channel[2];
    	if (pipe(channel)<0) perror_msg_and_die("pipe");
    	pid=fork();
    	if (pid<0) {
    		perror_msg_and_die("fork");
    	} else if (pid==0) {
    		close(channel[0]);
    		if (channel[1] != 1) {
    			dup2(channel[1],1);
    			close(channel[1]);
    		}
    #if 0
    #define SURROGATE "surrogate response"
    		write(1,SURROGATE,sizeof(SURROGATE));
    		_exit(run_list(head));
    #else
    		_exit(run_list_real(head));   /* leaks memory */
    #endif
    	}
    	debug_printf("forked child %d\n",pid);
    	close(channel[1]);
    	pf = fdopen(channel[0],"r");
    	debug_printf("pipe on FILE *%p\n",pf);
    #else
    	free_pipe_list(head,0);
    	pf=popen("echo surrogate response","r");
    	debug_printf("started fake pipe on FILE *%p\n",pf);
    #endif
    	return pf;
    }
    
    /* this version hacked for testing purposes */
    /* return code is exit status of the process that is run. */
    static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end)
    {
    	int retcode;
    	o_string result=NULL_O_STRING;
    	struct p_context inner;
    	FILE *p;
    	struct in_str pipe_str;
    	initialize_context(&inner);
    
    	/* recursion to generate command */
    	retcode = parse_stream(&result, &inner, input, subst_end);
    	if (retcode != 0) return retcode;  /* syntax error or EOF */
    	done_word(&result, &inner);
    	done_pipe(&inner, PIPE_SEQ);
    	b_free(&result);
    
    	p=generate_stream_from_list(inner.list_head);
    	if (p==NULL) return 1;
    	mark_open(fileno(p));
    	setup_file_in_str(&pipe_str, p);
    
    	/* now send results of command back into original context */
    	retcode = parse_stream(dest, ctx, &pipe_str, '\0');
    	/* XXX In case of a syntax error, should we try to kill the child?
    	 * That would be tough to do right, so just read until EOF. */
    	if (retcode == 1) {
    		while (b_getch(&pipe_str)!=EOF) { /* discard */ };
    	}
    
    	debug_printf("done reading from pipe, pclose()ing\n");
    	/* This is the step that wait()s for the child.  Should be pretty
    	 * safe, since we just read an EOF from its stdout.  We could try
    	 * to better, by using wait(), and keeping track of background jobs
    	 * at the same time.  That would be a lot of work, and contrary
    	 * to the KISS philosophy of this program. */
    	mark_closed(fileno(p));
    	retcode=pclose(p);
    	free_pipe_list(inner.list_head,0);
    	debug_printf("pclosed, retcode=%d\n",retcode);
    	/* XXX this process fails to trim a single trailing newline */
    	return retcode;
    }
    
    static int parse_group(o_string *dest, struct p_context *ctx,
    	struct in_str *input, int ch)
    {
    	int rcode, endch=0;
    	struct p_context sub;
    	struct child_prog *child = ctx->child;
    	if (child->argv) {
    		syntax();
    		return 1;  /* syntax error, groups and arglists don't mix */
    	}
    	initialize_context(&sub);
    	switch(ch) {
    		case '(': endch=')'; child->subshell=1; break;
    		case '{': endch='}'; break;
    		default: syntax();   /* really logic error */
    	}
    	rcode=parse_stream(dest,&sub,input,endch);
    	done_word(dest,&sub); /* finish off the final word in the subcontext */
    	done_pipe(&sub, PIPE_SEQ);  /* and the final command there, too */
    	child->group = sub.list_head;
    	return rcode;
    	/* child remains "open", available for possible redirects */
    }
    #endif
    
    /* basically useful version until someone wants to get fancier,
     * see the bash man page under "Parameter Expansion" */
    static char *lookup_param(char *src)
    {
    
    	char *sep;
    	char *default_val = NULL;
    	int assign = 0;
    	int expand_empty = 0;
    
    	sep = strchr(src, ':');
    
    	if (sep) {
    		*sep = '\0';
    		if (*(sep + 1) == '-')
    			default_val = sep+2;
    		if (*(sep + 1) == '=') {
    			default_val = sep+2;
    			assign = 1;
    		}
    		if (*(sep + 1) == '+') {
    			default_val = sep+2;
    			expand_empty = 1;
    		}
    	}
    
    	p = getenv(src);
    	if (!p)
    		p = get_local_var(src);
    
    	if (!p || strlen(p) == 0) {
    		p = default_val;
    		if (assign) {
    			char *var = malloc(strlen(src)+strlen(default_val)+2);
    			if (var) {
    				sprintf(var, "%s=%s", src, default_val);
    				set_local_var(var, 0);
    			}
    			free(var);
    		}
    	} else if (expand_empty) {
    		p += strlen(p);
    	}
    
    	if (sep)
    		*sep = ':';
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	return p;
    }
    
    
    #ifdef __U_BOOT__
    static char *get_dollar_var(char ch)
    {
    	static char buf[40];
    
    	buf[0] = '\0';
    	switch (ch) {
    		case '?':
    			sprintf(buf, "%u", (unsigned int)last_return_code);
    			break;
    		default:
    			return NULL;
    	}
    	return buf;
    }
    #endif
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /* return code: 0 for OK, 1 for syntax error */
    static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
    {
    #ifndef __U_BOOT__
    	int i, advance=0;
    #else
    	int advance=0;
    #endif
    #ifndef __U_BOOT__
    	char sep[]=" ";
    #endif
    	int ch = input->peek(input);  /* first character after the $ */
    	debug_printf("handle_dollar: ch=%c\n",ch);
    	if (isalpha(ch)) {
    		b_addchr(dest, SPECIAL_VAR_SYMBOL);
    		ctx->child->sp++;
    		while(ch=b_peek(input),isalnum(ch) || ch=='_') {
    			b_getch(input);
    			b_addchr(dest,ch);
    		}
    		b_addchr(dest, SPECIAL_VAR_SYMBOL);
    #ifndef __U_BOOT__
    	} else if (isdigit(ch)) {
    		i = ch-'0';  /* XXX is $0 special? */
    		if (i<global_argc) {
    			parse_string(dest, ctx, global_argv[i]); /* recursion */
    		}
    		advance = 1;
    #endif
    	} else switch (ch) {
    #ifndef __U_BOOT__
    		case '$':
    			b_adduint(dest,getpid());
    			advance = 1;
    			break;
    		case '!':
    			if (last_bg_pid > 0) b_adduint(dest, last_bg_pid);
    			advance = 1;
    			break;
    #endif
    		case '?':
    
    #ifndef __U_BOOT__
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			b_adduint(dest,last_return_code);
    
    #else
    			ctx->child->sp++;
    			b_addchr(dest, SPECIAL_VAR_SYMBOL);
    			b_addchr(dest, '$');
    			b_addchr(dest, '?');
    			b_addchr(dest, SPECIAL_VAR_SYMBOL);
    #endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			advance = 1;
    			break;
    #ifndef __U_BOOT__
    		case '#':
    			b_adduint(dest,global_argc ? global_argc-1 : 0);
    			advance = 1;
    			break;
    #endif
    		case '{':
    			b_addchr(dest, SPECIAL_VAR_SYMBOL);
    			ctx->child->sp++;
    			b_getch(input);
    			/* XXX maybe someone will try to escape the '}' */
    			while(ch=b_getch(input),ch!=EOF && ch!='}') {
    				b_addchr(dest,ch);
    			}
    			if (ch != '}') {
    				syntax();
    				return 1;
    			}
    			b_addchr(dest, SPECIAL_VAR_SYMBOL);
    			break;
    #ifndef __U_BOOT__
    		case '(':
    			b_getch(input);
    			process_command_subs(dest, ctx, input, ')');
    			break;
    		case '*':
    			sep[0]=ifs[0];
    			for (i=1; i<global_argc; i++) {
    				parse_string(dest, ctx, global_argv[i]);
    				if (i+1 < global_argc) parse_string(dest, ctx, sep);
    			}
    			break;
    		case '@':
    		case '-':
    		case '_':
    			/* still unhandled, but should be eventually */
    			error_msg("unhandled syntax: $%c",ch);
    			return 1;
    			break;
    #endif
    		default:
    			b_addqchr(dest,'$',dest->quote);
    	}
    	/* Eat the character if the flag was set.  If the compiler
    	 * is smart enough, we could substitute "b_getch(input);"
    	 * for all the "advance = 1;" above, and also end up with
    	 * a nice size-optimized program.  Hah!  That'll be the day.
    	 */
    	if (advance) b_getch(input);
    	return 0;
    }
    
    #ifndef __U_BOOT__
    int parse_string(o_string *dest, struct p_context *ctx, const char *src)
    {
    	struct in_str foo;
    	setup_string_in_str(&foo, src);
    	return parse_stream(dest, ctx, &foo, '\0');
    }
    #endif
    
    /* return code is 0 for normal exit, 1 for syntax error */
    
    Kim Phillips's avatar
    Kim Phillips committed
    static int parse_stream(o_string *dest, struct p_context *ctx,
    			struct in_str *input, int end_trigger)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	unsigned int ch, m;
    #ifndef __U_BOOT__
    	int redir_fd;
    	redir_type redir_style;
    #endif
    	int next;
    
    	/* Only double-quote state is handled in the state variable dest->quote.
    	 * A single-quote triggers a bypass of the main loop until its mate is
    	 * found.  When recursing, quote state is passed in via dest->quote. */
    
    	debug_printf("parse_stream, end_trigger=%d\n",end_trigger);
    	while ((ch=b_getch(input))!=EOF) {
    		m = map[ch];
    #ifdef __U_BOOT__
    		if (input->__promptme == 0) return 1;
    #endif
    		next = (ch == '\n') ? 0 : b_peek(input);
    
    
    		debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d - %c\n",
    			ch >= ' ' ? ch : '.', ch, m,
    			dest->quote, ctx->stack == NULL ? '*' : '.');
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (m==0 || ((m==1 || m==2) && dest->quote)) {
    			b_addqchr(dest, ch, dest->quote);
    		} else {
    			if (m==2) {  /* unquoted IFS */
    				if (done_word(dest, ctx)) {
    					return 1;
    				}
    				/* If we aren't performing a substitution, treat a newline as a
    				 * command separator.  */
    				if (end_trigger != '\0' && ch=='\n')
    					done_pipe(ctx,PIPE_SEQ);
    			}
    			if (ch == end_trigger && !dest->quote && ctx->w==RES_NONE) {
    				debug_printf("leaving parse_stream (triggered)\n");
    				return 0;
    			}
    #if 0
    			if (ch=='\n') {
    				/* Yahoo!  Time to run with it! */
    				done_pipe(ctx,PIPE_SEQ);
    				run_list(ctx->list_head);
    				initialize_context(ctx);
    			}
    #endif