parser.c

来自「Android 一些工具」· C语言 代码 · 共 1,652 行 · 第 1/3 页

C
1,652
字号
/*	$NetBSD: parser.c,v 1.57 2004/06/27 10:27:57 dsl Exp $	*//*- * Copyright (c) 1991, 1993 *	The Regents of the University of California.  All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include <sys/cdefs.h>#ifndef lint#if 0static char sccsid[] = "@(#)parser.c	8.7 (Berkeley) 5/16/95";#else__RCSID("$NetBSD: parser.c,v 1.57 2004/06/27 10:27:57 dsl Exp $");#endif#endif /* not lint */#include <stdlib.h>#include "shell.h"#include "parser.h"#include "nodes.h"#include "expand.h"	/* defines rmescapes() */#include "eval.h"	/* defines commandname */#include "redir.h"	/* defines copyfd() */#include "syntax.h"#include "options.h"#include "input.h"#include "output.h"#include "var.h"#include "error.h"#include "memalloc.h"#include "mystring.h"#include "alias.h"#include "show.h"#ifndef SMALL#include "myhistedit.h"#endif/* * Shell command parser. */#define EOFMARKLEN 79/* values returned by readtoken */#include "token.h"#define OPENBRACE '{'#define CLOSEBRACE '}'struct heredoc {	struct heredoc *next;	/* next here document in list */	union node *here;		/* redirection node */	char *eofmark;		/* string indicating end of input */	int striptabs;		/* if set, strip leading tabs */};static int noalias = 0;		/* when set, don't handle aliases */struct heredoc *heredoclist;	/* list of here documents to read */int parsebackquote;		/* nonzero if we are inside backquotes */int doprompt;			/* if set, prompt the user */int needprompt;			/* true if interactive and at start of line */int lasttoken;			/* last token read */MKINIT int tokpushback;		/* last token pushed back */char *wordtext;			/* text of last word returned by readtoken */MKINIT int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */struct nodelist *backquotelist;union node *redirnode;struct heredoc *heredoc;int quoteflag;			/* set if (part of) last token was quoted */int startlinno;			/* line # where last token started */STATIC union node *list(int);STATIC union node *andor(void);STATIC union node *pipeline(void);STATIC union node *command(void);STATIC union node *simplecmd(union node **, union node *);STATIC union node *makename(void);STATIC void parsefname(void);STATIC void parseheredoc(void);STATIC int peektoken(void);STATIC int readtoken(void);STATIC int xxreadtoken(void);STATIC int readtoken1(int, char const *, char *, int);STATIC int noexpand(char *);STATIC void synexpect(int) __attribute__((__noreturn__));STATIC void synerror(const char *) __attribute__((__noreturn__));STATIC void setprompt(int);/* * Read and parse a command.  Returns NEOF on end of file.  (NULL is a * valid parse tree indicating a blank line.) */union node *parsecmd(int interact){	int t;	tokpushback = 0;	doprompt = interact;	if (doprompt)		setprompt(1);	else		setprompt(0);	needprompt = 0;	t = readtoken();	if (t == TEOF)		return NEOF;	if (t == TNL)		return NULL;	tokpushback++;	return list(1);}STATIC union node *list(int nlflag){	union node *n1, *n2, *n3;	int tok;	checkkwd = 2;	if (nlflag == 0 && tokendlist[peektoken()])		return NULL;	n1 = NULL;	for (;;) {		n2 = andor();		tok = readtoken();		if (tok == TBACKGND) {			if (n2->type == NCMD || n2->type == NPIPE) {				n2->ncmd.backgnd = 1;			} else if (n2->type == NREDIR) {				n2->type = NBACKGND;			} else {				n3 = (union node *)stalloc(sizeof (struct nredir));				n3->type = NBACKGND;				n3->nredir.n = n2;				n3->nredir.redirect = NULL;				n2 = n3;			}		}		if (n1 == NULL) {			n1 = n2;		}		else {			n3 = (union node *)stalloc(sizeof (struct nbinary));			n3->type = NSEMI;			n3->nbinary.ch1 = n1;			n3->nbinary.ch2 = n2;			n1 = n3;		}		switch (tok) {		case TBACKGND:		case TSEMI:			tok = readtoken();			/* fall through */		case TNL:			if (tok == TNL) {				parseheredoc();				if (nlflag)					return n1;			} else {				tokpushback++;			}			checkkwd = 2;			if (tokendlist[peektoken()])				return n1;			break;		case TEOF:			if (heredoclist)				parseheredoc();			else				pungetc();		/* push back EOF on input */			return n1;		default:			if (nlflag)				synexpect(-1);			tokpushback++;			return n1;		}	}}STATIC union node *andor(void){	union node *n1, *n2, *n3;	int t;	n1 = pipeline();	for (;;) {		if ((t = readtoken()) == TAND) {			t = NAND;		} else if (t == TOR) {			t = NOR;		} else {			tokpushback++;			return n1;		}		n2 = pipeline();		n3 = (union node *)stalloc(sizeof (struct nbinary));		n3->type = t;		n3->nbinary.ch1 = n1;		n3->nbinary.ch2 = n2;		n1 = n3;	}}STATIC union node *pipeline(void){	union node *n1, *n2, *pipenode;	struct nodelist *lp, *prev;	int negate;	negate = 0;	TRACE(("pipeline: entered\n"));	while (readtoken() == TNOT)		negate = !negate;	tokpushback++;	n1 = command();	if (readtoken() == TPIPE) {		pipenode = (union node *)stalloc(sizeof (struct npipe));		pipenode->type = NPIPE;		pipenode->npipe.backgnd = 0;		lp = (struct nodelist *)stalloc(sizeof (struct nodelist));		pipenode->npipe.cmdlist = lp;		lp->n = n1;		do {			prev = lp;			lp = (struct nodelist *)stalloc(sizeof (struct nodelist));			lp->n = command();			prev->next = lp;		} while (readtoken() == TPIPE);		lp->next = NULL;		n1 = pipenode;	}	tokpushback++;	if (negate) {		n2 = (union node *)stalloc(sizeof (struct nnot));		n2->type = NNOT;		n2->nnot.com = n1;		return n2;	} else		return n1;}STATIC union node *command(void){	union node *n1, *n2;	union node *ap, **app;	union node *cp, **cpp;	union node *redir, **rpp;	int t, negate = 0;	checkkwd = 2;	redir = NULL;	n1 = NULL;	rpp = &redir;	/* Check for redirection which may precede command */	while (readtoken() == TREDIR) {		*rpp = n2 = redirnode;		rpp = &n2->nfile.next;		parsefname();	}	tokpushback++;	while (readtoken() == TNOT) {		TRACE(("command: TNOT recognized\n"));		negate = !negate;	}	tokpushback++;	switch (readtoken()) {	case TIF:		n1 = (union node *)stalloc(sizeof (struct nif));		n1->type = NIF;		n1->nif.test = list(0);		if (readtoken() != TTHEN)			synexpect(TTHEN);		n1->nif.ifpart = list(0);		n2 = n1;		while (readtoken() == TELIF) {			n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));			n2 = n2->nif.elsepart;			n2->type = NIF;			n2->nif.test = list(0);			if (readtoken() != TTHEN)				synexpect(TTHEN);			n2->nif.ifpart = list(0);		}		if (lasttoken == TELSE)			n2->nif.elsepart = list(0);		else {			n2->nif.elsepart = NULL;			tokpushback++;		}		if (readtoken() != TFI)			synexpect(TFI);		checkkwd = 1;		break;	case TWHILE:	case TUNTIL: {		int got;		n1 = (union node *)stalloc(sizeof (struct nbinary));		n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;		n1->nbinary.ch1 = list(0);		if ((got=readtoken()) != TDO) {TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));			synexpect(TDO);		}		n1->nbinary.ch2 = list(0);		if (readtoken() != TDONE)			synexpect(TDONE);		checkkwd = 1;		break;	}	case TFOR:		if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))			synerror("Bad for loop variable");		n1 = (union node *)stalloc(sizeof (struct nfor));		n1->type = NFOR;		n1->nfor.var = wordtext;		if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {			app = &ap;			while (readtoken() == TWORD) {				n2 = (union node *)stalloc(sizeof (struct narg));				n2->type = NARG;				n2->narg.text = wordtext;				n2->narg.backquote = backquotelist;				*app = n2;				app = &n2->narg.next;			}			*app = NULL;			n1->nfor.args = ap;			if (lasttoken != TNL && lasttoken != TSEMI)				synexpect(-1);		} else {			static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,								   '@', '=', '\0'};			n2 = (union node *)stalloc(sizeof (struct narg));			n2->type = NARG;			n2->narg.text = argvars;			n2->narg.backquote = NULL;			n2->narg.next = NULL;			n1->nfor.args = n2;			/*			 * Newline or semicolon here is optional (but note			 * that the original Bourne shell only allowed NL).			 */			if (lasttoken != TNL && lasttoken != TSEMI)				tokpushback++;		}		checkkwd = 2;		if ((t = readtoken()) == TDO)			t = TDONE;		else if (t == TBEGIN)			t = TEND;		else			synexpect(-1);		n1->nfor.body = list(0);		if (readtoken() != t)			synexpect(t);		checkkwd = 1;		break;	case TCASE:		n1 = (union node *)stalloc(sizeof (struct ncase));		n1->type = NCASE;		if (readtoken() != TWORD)			synexpect(TWORD);		n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));		n2->type = NARG;		n2->narg.text = wordtext;		n2->narg.backquote = backquotelist;		n2->narg.next = NULL;		while (readtoken() == TNL);		if (lasttoken != TWORD || ! equal(wordtext, "in"))			synerror("expecting \"in\"");		cpp = &n1->ncase.cases;		noalias = 1;		checkkwd = 2, readtoken();		do {			*cpp = cp = (union node *)stalloc(sizeof (struct nclist));			cp->type = NCLIST;			app = &cp->nclist.pattern;			for (;;) {				*app = ap = (union node *)stalloc(sizeof (struct narg));				ap->type = NARG;				ap->narg.text = wordtext;				ap->narg.backquote = backquotelist;				if (checkkwd = 2, readtoken() != TPIPE)					break;				app = &ap->narg.next;				readtoken();			}			ap->narg.next = NULL;			noalias = 0;			if (lasttoken != TRP) {				synexpect(TRP);			}			cp->nclist.body = list(0);			checkkwd = 2;			if ((t = readtoken()) != TESAC) {				if (t != TENDCASE) {					noalias = 0;					synexpect(TENDCASE);				} else {					noalias = 1;					checkkwd = 2;					readtoken();				}			}			cpp = &cp->nclist.next;		} while(lasttoken != TESAC);		noalias = 0;		*cpp = NULL;		checkkwd = 1;		break;	case TLP:		n1 = (union node *)stalloc(sizeof (struct nredir));		n1->type = NSUBSHELL;		n1->nredir.n = list(0);		n1->nredir.redirect = NULL;		if (readtoken() != TRP)			synexpect(TRP);		checkkwd = 1;		break;	case TBEGIN:		n1 = list(0);		if (readtoken() != TEND)			synexpect(TEND);		checkkwd = 1;		break;	/* Handle an empty command like other simple commands.  */	case TSEMI:		/*		 * An empty command before a ; doesn't make much sense, and		 * should certainly be disallowed in the case of `if ;'.		 */		if (!redir)			synexpect(-1);	case TAND:	case TOR:	case TNL:	case TEOF:	case TWORD:	case TRP:		tokpushback++;		n1 = simplecmd(rpp, redir);		goto checkneg;	default:		synexpect(-1);		/* NOTREACHED */	}	/* Now check for redirection which may follow command */	while (readtoken() == TREDIR) {		*rpp = n2 = redirnode;		rpp = &n2->nfile.next;		parsefname();	}	tokpushback++;	*rpp = NULL;	if (redir) {		if (n1->type != NSUBSHELL) {			n2 = (union node *)stalloc(sizeof (struct nredir));			n2->type = NREDIR;			n2->nredir.n = n1;			n1 = n2;		}		n1->nredir.redirect = redir;	}checkneg:	if (negate) {		n2 = (union node *)stalloc(sizeof (struct nnot));		n2->type = NNOT;		n2->nnot.com = n1;		return n2;	}	else		return n1;}STATIC union node *simplecmd(union node **rpp, union node *redir){	union node *args, **app;	union node **orig_rpp = rpp;	union node *n = NULL, *n2;	int negate = 0;	/* If we don't have any redirections already, then we must reset */	/* rpp to be the address of the local redir variable.  */	if (redir == 0)		rpp = &redir;	args = NULL;	app = &args;	/*	 * We save the incoming value, because we need this for shell	 * functions.  There can not be a redirect or an argument between	 * the function name and the open parenthesis.	 */	orig_rpp = rpp;	while (readtoken() == TNOT) {		TRACE(("command: TNOT recognized\n"));

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?