parser.c revision 142845
1262629Sdelphij/*-
250276Speter * Copyright (c) 1991, 1993
3262629Sdelphij *	The Regents of the University of California.  All rights reserved.
450276Speter *
550276Speter * This code is derived from software contributed to Berkeley by
650276Speter * Kenneth Almquist.
750276Speter *
850276Speter * Redistribution and use in source and binary forms, with or without
950276Speter * modification, are permitted provided that the following conditions
1050276Speter * are met:
1150276Speter * 1. Redistributions of source code must retain the above copyright
1250276Speter *    notice, this list of conditions and the following disclaimer.
1350276Speter * 2. Redistributions in binary form must reproduce the above copyright
1450276Speter *    notice, this list of conditions and the following disclaimer in the
1550276Speter *    documentation and/or other materials provided with the distribution.
1650276Speter * 4. Neither the name of the University nor the names of its contributors
1750276Speter *    may be used to endorse or promote products derived from this software
1850276Speter *    without specific prior written permission.
1950276Speter *
2050276Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2150276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2250276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2350276Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2450276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2550276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2650276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2750276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2850276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2950276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3097049Speter * SUCH DAMAGE.
3150276Speter */
3250276Speter
3350276Speter#ifndef lint
3450276Speter#if 0
3550276Speterstatic char sccsid[] = "@(#)parser.c	8.7 (Berkeley) 5/16/95";
3650276Speter#endif
3750276Speter#endif /* not lint */
3850276Speter#include <sys/cdefs.h>
3950276Speter__FBSDID("$FreeBSD: head/bin/sh/parser.c 142845 2005-03-01 03:35:58Z obrien $");
4050276Speter
4150276Speter#include <stdlib.h>
4250276Speter
43262629Sdelphij#include "shell.h"
4450276Speter#include "parser.h"
4550276Speter#include "nodes.h"
4666963Speter#include "expand.h"	/* defines rmescapes() */
4750276Speter#include "syntax.h"
4850276Speter#include "options.h"
4950276Speter#include "input.h"
5050276Speter#include "output.h"
5150276Speter#include "var.h"
5250276Speter#include "error.h"
5350276Speter#include "memalloc.h"
5450276Speter#include "mystring.h"
5550276Speter#include "alias.h"
5650276Speter#include "show.h"
5750276Speter#include "eval.h"
58262629Sdelphij#ifndef NO_HISTORY
59262629Sdelphij#include "myhistedit.h"
60262629Sdelphij#endif
6150276Speter
6250276Speter/*
6397049Speter * Shell command parser.
6497049Speter */
6597049Speter
6697049Speter#define	EOFMARKLEN	79
6750276Speter#define	PROMPTLEN	128
6850276Speter
6950276Speter/* values returned by readtoken */
7097049Speter#include "token.h"
71262629Sdelphij
7250276Speter
7350276Speter
7450276Speterstruct heredoc {
7550276Speter	struct heredoc *next;	/* next here document in list */
7650276Speter	union node *here;		/* redirection node */
7797049Speter	char *eofmark;		/* string indicating end of input */
7850276Speter	int striptabs;		/* if set, strip leading tabs */
7966963Speter};
8050276Speter
81262629Sdelphij
8250276Speter
8350276SpeterSTATIC struct heredoc *heredoclist;	/* list of here documents to read */
8450276SpeterSTATIC int parsebackquote;	/* nonzero if we are inside backquotes */
8550276SpeterSTATIC int doprompt;		/* if set, prompt the user */
8662449SpeterSTATIC int needprompt;		/* true if interactive and at start of line */
8797049SpeterSTATIC int lasttoken;		/* last token read */
8850276SpeterMKINIT int tokpushback;		/* last token pushed back */
8950276SpeterSTATIC char *wordtext;		/* text of last word returned by readtoken */
9050276SpeterMKINIT int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */
9197049SpeterSTATIC struct nodelist *backquotelist;
9297049SpeterSTATIC union node *redirnode;
9397049SpeterSTATIC struct heredoc *heredoc;
9497049SpeterSTATIC int quoteflag;		/* set if (part of) last token was quoted */
9597049SpeterSTATIC int startlinno;		/* line # where last token started */
9697049Speter
9797049Speter/* XXX When 'noaliases' is set to one, no alias expansion takes place. */
9897049Speterstatic int noaliases = 0;
9997049Speter
10097049Speter#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */
10197049Speter#ifdef GDB_HACK
10250276Speterstatic const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};
10350276Speterstatic const char types[] = "}-+?=";
10497049Speter#endif
10597049Speter
10650276Speter
10766963SpeterSTATIC union node *list(int);
10866963SpeterSTATIC union node *andor(void);
10966963SpeterSTATIC union node *pipeline(void);
11097049SpeterSTATIC union node *command(void);
11197049SpeterSTATIC union node *simplecmd(union node **, union node *);
11250276SpeterSTATIC union node *makename(void);
113262629SdelphijSTATIC void parsefname(void);
114262629SdelphijSTATIC void parseheredoc(void);
115262629SdelphijSTATIC int peektoken(void);
116262629SdelphijSTATIC int readtoken(void);
11750276SpeterSTATIC int xxreadtoken(void);
118262629SdelphijSTATIC int readtoken1(int, char const *, char *, int);
11950276SpeterSTATIC int noexpand(char *);
12066963SpeterSTATIC void synexpect(int);
121262629SdelphijSTATIC void synerror(char *);
12250276SpeterSTATIC void setprompt(int);
12350276Speter
12497049Speter
12550276Speter/*
12650276Speter * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
12750276Speter * valid parse tree indicating a blank line.)
12850276Speter */
12950276Speter
130262629Sdelphijunion node *
13150276Speterparsecmd(int interact)
13250276Speter{
13350276Speter	int t;
13450276Speter
13550276Speter	tokpushback = 0;
13650276Speter	doprompt = interact;
137	if (doprompt)
138		setprompt(1);
139	else
140		setprompt(0);
141	needprompt = 0;
142	t = readtoken();
143	if (t == TEOF)
144		return NEOF;
145	if (t == TNL)
146		return NULL;
147	tokpushback++;
148	return list(1);
149}
150
151
152STATIC union node *
153list(int nlflag)
154{
155	union node *n1, *n2, *n3;
156	int tok;
157
158	checkkwd = 2;
159	if (nlflag == 0 && tokendlist[peektoken()])
160		return NULL;
161	n1 = NULL;
162	for (;;) {
163		n2 = andor();
164		tok = readtoken();
165		if (tok == TBACKGND) {
166			if (n2->type == NCMD || n2->type == NPIPE) {
167				n2->ncmd.backgnd = 1;
168			} else if (n2->type == NREDIR) {
169				n2->type = NBACKGND;
170			} else {
171				n3 = (union node *)stalloc(sizeof (struct nredir));
172				n3->type = NBACKGND;
173				n3->nredir.n = n2;
174				n3->nredir.redirect = NULL;
175				n2 = n3;
176			}
177		}
178		if (n1 == NULL) {
179			n1 = n2;
180		}
181		else {
182			n3 = (union node *)stalloc(sizeof (struct nbinary));
183			n3->type = NSEMI;
184			n3->nbinary.ch1 = n1;
185			n3->nbinary.ch2 = n2;
186			n1 = n3;
187		}
188		switch (tok) {
189		case TBACKGND:
190		case TSEMI:
191			tok = readtoken();
192			/* FALLTHROUGH */
193		case TNL:
194			if (tok == TNL) {
195				parseheredoc();
196				if (nlflag)
197					return n1;
198			} else {
199				tokpushback++;
200			}
201			checkkwd = 2;
202			if (tokendlist[peektoken()])
203				return n1;
204			break;
205		case TEOF:
206			if (heredoclist)
207				parseheredoc();
208			else
209				pungetc();		/* push back EOF on input */
210			return n1;
211		default:
212			if (nlflag)
213				synexpect(-1);
214			tokpushback++;
215			return n1;
216		}
217	}
218}
219
220
221
222STATIC union node *
223andor(void)
224{
225	union node *n1, *n2, *n3;
226	int t;
227
228	n1 = pipeline();
229	for (;;) {
230		if ((t = readtoken()) == TAND) {
231			t = NAND;
232		} else if (t == TOR) {
233			t = NOR;
234		} else {
235			tokpushback++;
236			return n1;
237		}
238		n2 = pipeline();
239		n3 = (union node *)stalloc(sizeof (struct nbinary));
240		n3->type = t;
241		n3->nbinary.ch1 = n1;
242		n3->nbinary.ch2 = n2;
243		n1 = n3;
244	}
245}
246
247
248
249STATIC union node *
250pipeline(void)
251{
252	union node *n1, *n2, *pipenode;
253	struct nodelist *lp, *prev;
254	int negate;
255
256	negate = 0;
257	TRACE(("pipeline: entered\n"));
258	while (readtoken() == TNOT)
259		negate = !negate;
260	tokpushback++;
261	n1 = command();
262	if (readtoken() == TPIPE) {
263		pipenode = (union node *)stalloc(sizeof (struct npipe));
264		pipenode->type = NPIPE;
265		pipenode->npipe.backgnd = 0;
266		lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
267		pipenode->npipe.cmdlist = lp;
268		lp->n = n1;
269		do {
270			prev = lp;
271			lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
272			lp->n = command();
273			prev->next = lp;
274		} while (readtoken() == TPIPE);
275		lp->next = NULL;
276		n1 = pipenode;
277	}
278	tokpushback++;
279	if (negate) {
280		n2 = (union node *)stalloc(sizeof (struct nnot));
281		n2->type = NNOT;
282		n2->nnot.com = n1;
283		return n2;
284	} else
285		return n1;
286}
287
288
289
290STATIC union node *
291command(void)
292{
293	union node *n1, *n2;
294	union node *ap, **app;
295	union node *cp, **cpp;
296	union node *redir, **rpp;
297	int t, negate = 0;
298
299	checkkwd = 2;
300	redir = NULL;
301	n1 = NULL;
302	rpp = &redir;
303
304	/* Check for redirection which may precede command */
305	while (readtoken() == TREDIR) {
306		*rpp = n2 = redirnode;
307		rpp = &n2->nfile.next;
308		parsefname();
309	}
310	tokpushback++;
311
312	while (readtoken() == TNOT) {
313		TRACE(("command: TNOT recognized\n"));
314		negate = !negate;
315	}
316	tokpushback++;
317
318	switch (readtoken()) {
319	case TIF:
320		n1 = (union node *)stalloc(sizeof (struct nif));
321		n1->type = NIF;
322		if ((n1->nif.test = list(0)) == NULL)
323			synexpect(-1);
324		if (readtoken() != TTHEN)
325			synexpect(TTHEN);
326		n1->nif.ifpart = list(0);
327		n2 = n1;
328		while (readtoken() == TELIF) {
329			n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
330			n2 = n2->nif.elsepart;
331			n2->type = NIF;
332			if ((n2->nif.test = list(0)) == NULL)
333				synexpect(-1);
334			if (readtoken() != TTHEN)
335				synexpect(TTHEN);
336			n2->nif.ifpart = list(0);
337		}
338		if (lasttoken == TELSE)
339			n2->nif.elsepart = list(0);
340		else {
341			n2->nif.elsepart = NULL;
342			tokpushback++;
343		}
344		if (readtoken() != TFI)
345			synexpect(TFI);
346		checkkwd = 1;
347		break;
348	case TWHILE:
349	case TUNTIL: {
350		int got;
351		n1 = (union node *)stalloc(sizeof (struct nbinary));
352		n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
353		if ((n1->nbinary.ch1 = list(0)) == NULL)
354			synexpect(-1);
355		if ((got=readtoken()) != TDO) {
356TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
357			synexpect(TDO);
358		}
359		n1->nbinary.ch2 = list(0);
360		if (readtoken() != TDONE)
361			synexpect(TDONE);
362		checkkwd = 1;
363		break;
364	}
365	case TFOR:
366		if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
367			synerror("Bad for loop variable");
368		n1 = (union node *)stalloc(sizeof (struct nfor));
369		n1->type = NFOR;
370		n1->nfor.var = wordtext;
371		if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
372			app = &ap;
373			while (readtoken() == TWORD) {
374				n2 = (union node *)stalloc(sizeof (struct narg));
375				n2->type = NARG;
376				n2->narg.text = wordtext;
377				n2->narg.backquote = backquotelist;
378				*app = n2;
379				app = &n2->narg.next;
380			}
381			*app = NULL;
382			n1->nfor.args = ap;
383			if (lasttoken != TNL && lasttoken != TSEMI)
384				synexpect(-1);
385		} else {
386#ifndef GDB_HACK
387			static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
388								   '@', '=', '\0'};
389#endif
390			n2 = (union node *)stalloc(sizeof (struct narg));
391			n2->type = NARG;
392			n2->narg.text = (char *)argvars;
393			n2->narg.backquote = NULL;
394			n2->narg.next = NULL;
395			n1->nfor.args = n2;
396			/*
397			 * Newline or semicolon here is optional (but note
398			 * that the original Bourne shell only allowed NL).
399			 */
400			if (lasttoken != TNL && lasttoken != TSEMI)
401				tokpushback++;
402		}
403		checkkwd = 2;
404		if ((t = readtoken()) == TDO)
405			t = TDONE;
406		else if (t == TBEGIN)
407			t = TEND;
408		else
409			synexpect(-1);
410		n1->nfor.body = list(0);
411		if (readtoken() != t)
412			synexpect(t);
413		checkkwd = 1;
414		break;
415	case TCASE:
416		n1 = (union node *)stalloc(sizeof (struct ncase));
417		n1->type = NCASE;
418		if (readtoken() != TWORD)
419			synexpect(TWORD);
420		n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
421		n2->type = NARG;
422		n2->narg.text = wordtext;
423		n2->narg.backquote = backquotelist;
424		n2->narg.next = NULL;
425		while (readtoken() == TNL);
426		if (lasttoken != TWORD || ! equal(wordtext, "in"))
427			synerror("expecting \"in\"");
428		cpp = &n1->ncase.cases;
429		noaliases = 1;	/* turn off alias expansion */
430		checkkwd = 2, readtoken();
431		while (lasttoken != TESAC) {
432			*cpp = cp = (union node *)stalloc(sizeof (struct nclist));
433			cp->type = NCLIST;
434			app = &cp->nclist.pattern;
435			if (lasttoken == TLP)
436				readtoken();
437			for (;;) {
438				*app = ap = (union node *)stalloc(sizeof (struct narg));
439				ap->type = NARG;
440				ap->narg.text = wordtext;
441				ap->narg.backquote = backquotelist;
442				if (checkkwd = 2, readtoken() != TPIPE)
443					break;
444				app = &ap->narg.next;
445				readtoken();
446			}
447			ap->narg.next = NULL;
448			if (lasttoken != TRP)
449				noaliases = 0, synexpect(TRP);
450			cp->nclist.body = list(0);
451
452			checkkwd = 2;
453			if ((t = readtoken()) != TESAC) {
454				if (t != TENDCASE)
455					noaliases = 0, synexpect(TENDCASE);
456				else
457					checkkwd = 2, readtoken();
458			}
459			cpp = &cp->nclist.next;
460		}
461		noaliases = 0;	/* reset alias expansion */
462		*cpp = NULL;
463		checkkwd = 1;
464		break;
465	case TLP:
466		n1 = (union node *)stalloc(sizeof (struct nredir));
467		n1->type = NSUBSHELL;
468		n1->nredir.n = list(0);
469		n1->nredir.redirect = NULL;
470		if (readtoken() != TRP)
471			synexpect(TRP);
472		checkkwd = 1;
473		break;
474	case TBEGIN:
475		n1 = list(0);
476		if (readtoken() != TEND)
477			synexpect(TEND);
478		checkkwd = 1;
479		break;
480	/* Handle an empty command like other simple commands.  */
481	case TSEMI:
482	case TAND:
483	case TOR:
484		/*
485		 * An empty command before a ; doesn't make much sense, and
486		 * should certainly be disallowed in the case of `if ;'.
487		 */
488		if (!redir)
489			synexpect(-1);
490	case TNL:
491	case TEOF:
492	case TWORD:
493	case TRP:
494		tokpushback++;
495		n1 = simplecmd(rpp, redir);
496		goto checkneg;
497	default:
498		synexpect(-1);
499	}
500
501	/* Now check for redirection which may follow command */
502	while (readtoken() == TREDIR) {
503		*rpp = n2 = redirnode;
504		rpp = &n2->nfile.next;
505		parsefname();
506	}
507	tokpushback++;
508	*rpp = NULL;
509	if (redir) {
510		if (n1->type != NSUBSHELL) {
511			n2 = (union node *)stalloc(sizeof (struct nredir));
512			n2->type = NREDIR;
513			n2->nredir.n = n1;
514			n1 = n2;
515		}
516		n1->nredir.redirect = redir;
517	}
518
519checkneg:
520	if (negate) {
521		n2 = (union node *)stalloc(sizeof (struct nnot));
522		n2->type = NNOT;
523		n2->nnot.com = n1;
524		return n2;
525	}
526	else
527		return n1;
528}
529
530
531STATIC union node *
532simplecmd(union node **rpp, union node *redir)
533{
534	union node *args, **app;
535	union node **orig_rpp = rpp;
536	union node *n = NULL, *n2;
537	int negate = 0;
538
539	/* If we don't have any redirections already, then we must reset */
540	/* rpp to be the address of the local redir variable.  */
541	if (redir == 0)
542		rpp = &redir;
543
544	args = NULL;
545	app = &args;
546	/*
547	 * We save the incoming value, because we need this for shell
548	 * functions.  There can not be a redirect or an argument between
549	 * the function name and the open parenthesis.
550	 */
551	orig_rpp = rpp;
552
553	while (readtoken() == TNOT) {
554		TRACE(("command: TNOT recognized\n"));
555		negate = !negate;
556	}
557	tokpushback++;
558
559	for (;;) {
560		if (readtoken() == TWORD) {
561			n = (union node *)stalloc(sizeof (struct narg));
562			n->type = NARG;
563			n->narg.text = wordtext;
564			n->narg.backquote = backquotelist;
565			*app = n;
566			app = &n->narg.next;
567		} else if (lasttoken == TREDIR) {
568			*rpp = n = redirnode;
569			rpp = &n->nfile.next;
570			parsefname();	/* read name of redirection file */
571		} else if (lasttoken == TLP && app == &args->narg.next
572					    && rpp == orig_rpp) {
573			/* We have a function */
574			if (readtoken() != TRP)
575				synexpect(TRP);
576#ifdef notdef
577			if (! goodname(n->narg.text))
578				synerror("Bad function name");
579#endif
580			n->type = NDEFUN;
581			n->narg.next = command();
582			goto checkneg;
583		} else {
584			tokpushback++;
585			break;
586		}
587	}
588	*app = NULL;
589	*rpp = NULL;
590	n = (union node *)stalloc(sizeof (struct ncmd));
591	n->type = NCMD;
592	n->ncmd.backgnd = 0;
593	n->ncmd.args = args;
594	n->ncmd.redirect = redir;
595
596checkneg:
597	if (negate) {
598		n2 = (union node *)stalloc(sizeof (struct nnot));
599		n2->type = NNOT;
600		n2->nnot.com = n;
601		return n2;
602	}
603	else
604		return n;
605}
606
607STATIC union node *
608makename(void)
609{
610	union node *n;
611
612	n = (union node *)stalloc(sizeof (struct narg));
613	n->type = NARG;
614	n->narg.next = NULL;
615	n->narg.text = wordtext;
616	n->narg.backquote = backquotelist;
617	return n;
618}
619
620void fixredir(union node *n, const char *text, int err)
621{
622	TRACE(("Fix redir %s %d\n", text, err));
623	if (!err)
624		n->ndup.vname = NULL;
625
626	if (is_digit(text[0]) && text[1] == '\0')
627		n->ndup.dupfd = digit_val(text[0]);
628	else if (text[0] == '-' && text[1] == '\0')
629		n->ndup.dupfd = -1;
630	else {
631
632		if (err)
633			synerror("Bad fd number");
634		else
635			n->ndup.vname = makename();
636	}
637}
638
639
640STATIC void
641parsefname(void)
642{
643	union node *n = redirnode;
644
645	if (readtoken() != TWORD)
646		synexpect(-1);
647	if (n->type == NHERE) {
648		struct heredoc *here = heredoc;
649		struct heredoc *p;
650		int i;
651
652		if (quoteflag == 0)
653			n->type = NXHERE;
654		TRACE(("Here document %d\n", n->type));
655		if (here->striptabs) {
656			while (*wordtext == '\t')
657				wordtext++;
658		}
659		if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
660			synerror("Illegal eof marker for << redirection");
661		rmescapes(wordtext);
662		here->eofmark = wordtext;
663		here->next = NULL;
664		if (heredoclist == NULL)
665			heredoclist = here;
666		else {
667			for (p = heredoclist ; p->next ; p = p->next);
668			p->next = here;
669		}
670	} else if (n->type == NTOFD || n->type == NFROMFD) {
671		fixredir(n, wordtext, 0);
672	} else {
673		n->nfile.fname = makename();
674	}
675}
676
677
678/*
679 * Input any here documents.
680 */
681
682STATIC void
683parseheredoc(void)
684{
685	struct heredoc *here;
686	union node *n;
687
688	while (heredoclist) {
689		here = heredoclist;
690		heredoclist = here->next;
691		if (needprompt) {
692			setprompt(2);
693			needprompt = 0;
694		}
695		readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
696				here->eofmark, here->striptabs);
697		n = (union node *)stalloc(sizeof (struct narg));
698		n->narg.type = NARG;
699		n->narg.next = NULL;
700		n->narg.text = wordtext;
701		n->narg.backquote = backquotelist;
702		here->here->nhere.doc = n;
703	}
704}
705
706STATIC int
707peektoken(void)
708{
709	int t;
710
711	t = readtoken();
712	tokpushback++;
713	return (t);
714}
715
716STATIC int
717readtoken(void)
718{
719	int t;
720	int savecheckkwd = checkkwd;
721	struct alias *ap;
722#ifdef DEBUG
723	int alreadyseen = tokpushback;
724#endif
725
726	top:
727	t = xxreadtoken();
728
729	if (checkkwd) {
730		/*
731		 * eat newlines
732		 */
733		if (checkkwd == 2) {
734			checkkwd = 0;
735			while (t == TNL) {
736				parseheredoc();
737				t = xxreadtoken();
738			}
739		} else
740			checkkwd = 0;
741		/*
742		 * check for keywords and aliases
743		 */
744		if (t == TWORD && !quoteflag)
745		{
746			const char * const *pp;
747
748			for (pp = parsekwd; *pp; pp++) {
749				if (**pp == *wordtext && equal(*pp, wordtext))
750				{
751					lasttoken = t = pp - parsekwd + KWDOFFSET;
752					TRACE(("keyword %s recognized\n", tokname[t]));
753					goto out;
754				}
755			}
756			if (noaliases == 0 &&
757			    (ap = lookupalias(wordtext, 1)) != NULL) {
758				pushstring(ap->val, strlen(ap->val), ap);
759				checkkwd = savecheckkwd;
760				goto top;
761			}
762		}
763out:
764		checkkwd = (t == TNOT) ? savecheckkwd : 0;
765	}
766#ifdef DEBUG
767	if (!alreadyseen)
768	    TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
769	else
770	    TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
771#endif
772	return (t);
773}
774
775
776/*
777 * Read the next input token.
778 * If the token is a word, we set backquotelist to the list of cmds in
779 *	backquotes.  We set quoteflag to true if any part of the word was
780 *	quoted.
781 * If the token is TREDIR, then we set redirnode to a structure containing
782 *	the redirection.
783 * In all cases, the variable startlinno is set to the number of the line
784 *	on which the token starts.
785 *
786 * [Change comment:  here documents and internal procedures]
787 * [Readtoken shouldn't have any arguments.  Perhaps we should make the
788 *  word parsing code into a separate routine.  In this case, readtoken
789 *  doesn't need to have any internal procedures, but parseword does.
790 *  We could also make parseoperator in essence the main routine, and
791 *  have parseword (readtoken1?) handle both words and redirection.]
792 */
793
794#define RETURN(token)	return lasttoken = token
795
796STATIC int
797xxreadtoken(void)
798{
799	int c;
800
801	if (tokpushback) {
802		tokpushback = 0;
803		return lasttoken;
804	}
805	if (needprompt) {
806		setprompt(2);
807		needprompt = 0;
808	}
809	startlinno = plinno;
810	for (;;) {	/* until token or start of word found */
811		c = pgetc_macro();
812		if (c == ' ' || c == '\t')
813			continue;		/* quick check for white space first */
814		switch (c) {
815		case ' ': case '\t':
816			continue;
817		case '#':
818			while ((c = pgetc()) != '\n' && c != PEOF);
819			pungetc();
820			continue;
821		case '\\':
822			if (pgetc() == '\n') {
823				startlinno = ++plinno;
824				if (doprompt)
825					setprompt(2);
826				else
827					setprompt(0);
828				continue;
829			}
830			pungetc();
831			goto breakloop;
832		case '\n':
833			plinno++;
834			needprompt = doprompt;
835			RETURN(TNL);
836		case PEOF:
837			RETURN(TEOF);
838		case '&':
839			if (pgetc() == '&')
840				RETURN(TAND);
841			pungetc();
842			RETURN(TBACKGND);
843		case '|':
844			if (pgetc() == '|')
845				RETURN(TOR);
846			pungetc();
847			RETURN(TPIPE);
848		case ';':
849			if (pgetc() == ';')
850				RETURN(TENDCASE);
851			pungetc();
852			RETURN(TSEMI);
853		case '(':
854			RETURN(TLP);
855		case ')':
856			RETURN(TRP);
857		default:
858			goto breakloop;
859		}
860	}
861breakloop:
862	return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
863#undef RETURN
864}
865
866
867
868/*
869 * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
870 * is not NULL, read a here document.  In the latter case, eofmark is the
871 * word which marks the end of the document and striptabs is true if
872 * leading tabs should be stripped from the document.  The argument firstc
873 * is the first character of the input token or document.
874 *
875 * Because C does not have internal subroutines, I have simulated them
876 * using goto's to implement the subroutine linkage.  The following macros
877 * will run code that appears at the end of readtoken1.
878 */
879
880#define CHECKEND()	{goto checkend; checkend_return:;}
881#define PARSEREDIR()	{goto parseredir; parseredir_return:;}
882#define PARSESUB()	{goto parsesub; parsesub_return:;}
883#define PARSEBACKQOLD()	{oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
884#define PARSEBACKQNEW()	{oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
885#define	PARSEARITH()	{goto parsearith; parsearith_return:;}
886
887STATIC int
888readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs)
889{
890	int c = firstc;
891	char *out;
892	int len;
893	char line[EOFMARKLEN + 1];
894	struct nodelist *bqlist;
895	int quotef;
896	int dblquote;
897	int varnest;	/* levels of variables expansion */
898	int arinest;	/* levels of arithmetic expansion */
899	int parenlevel;	/* levels of parens in arithmetic */
900	int oldstyle;
901	char const *prevsyntax;	/* syntax before arithmetic */
902	int synentry;
903#if __GNUC__
904	/* Avoid longjmp clobbering */
905	(void) &out;
906	(void) &quotef;
907	(void) &dblquote;
908	(void) &varnest;
909	(void) &arinest;
910	(void) &parenlevel;
911	(void) &oldstyle;
912	(void) &prevsyntax;
913	(void) &syntax;
914	(void) &synentry;
915#endif
916
917	startlinno = plinno;
918	dblquote = 0;
919	if (syntax == DQSYNTAX)
920		dblquote = 1;
921	quotef = 0;
922	bqlist = NULL;
923	varnest = 0;
924	arinest = 0;
925	parenlevel = 0;
926
927	STARTSTACKSTR(out);
928	loop: {	/* for each line, until end of word */
929		CHECKEND();	/* set c to PEOF if at end of here document */
930		for (;;) {	/* until end of line or end of word */
931			CHECKSTRSPACE(3, out);	/* permit 3 calls to USTPUTC */
932
933			synentry = syntax[c];
934
935			switch(synentry) {
936			case CNL:	/* '\n' */
937				if (syntax == BASESYNTAX)
938					goto endword;	/* exit outer loop */
939				USTPUTC(c, out);
940				plinno++;
941				if (doprompt)
942					setprompt(2);
943				else
944					setprompt(0);
945				c = pgetc();
946				goto loop;		/* continue outer loop */
947			case CWORD:
948				USTPUTC(c, out);
949				break;
950			case CCTL:
951				if (eofmark == NULL || dblquote)
952					USTPUTC(CTLESC, out);
953				USTPUTC(c, out);
954				break;
955			case CBACK:	/* backslash */
956				c = pgetc();
957				if (c == PEOF) {
958					USTPUTC('\\', out);
959					pungetc();
960				} else if (c == '\n') {
961					if (doprompt)
962						setprompt(2);
963					else
964						setprompt(0);
965				} else {
966					if (dblquote && c != '\\' &&
967					    c != '`' && c != '$' &&
968					    (c != '"' || eofmark != NULL))
969						USTPUTC('\\', out);
970					if (SQSYNTAX[c] == CCTL)
971						USTPUTC(CTLESC, out);
972					else if (eofmark == NULL)
973						USTPUTC(CTLQUOTEMARK, out);
974					USTPUTC(c, out);
975					quotef++;
976				}
977				break;
978			case CSQUOTE:
979				if (eofmark == NULL)
980					USTPUTC(CTLQUOTEMARK, out);
981				syntax = SQSYNTAX;
982				break;
983			case CDQUOTE:
984				if (eofmark == NULL)
985					USTPUTC(CTLQUOTEMARK, out);
986				syntax = DQSYNTAX;
987				dblquote = 1;
988				break;
989			case CENDQUOTE:
990				if (eofmark != NULL && arinest == 0 &&
991				    varnest == 0) {
992					USTPUTC(c, out);
993				} else {
994					if (arinest) {
995						syntax = ARISYNTAX;
996						dblquote = 0;
997					} else if (eofmark == NULL) {
998						syntax = BASESYNTAX;
999						dblquote = 0;
1000					}
1001					quotef++;
1002				}
1003				break;
1004			case CVAR:	/* '$' */
1005				PARSESUB();		/* parse substitution */
1006				break;
1007			case CENDVAR:	/* '}' */
1008				if (varnest > 0) {
1009					varnest--;
1010					USTPUTC(CTLENDVAR, out);
1011				} else {
1012					USTPUTC(c, out);
1013				}
1014				break;
1015			case CLP:	/* '(' in arithmetic */
1016				parenlevel++;
1017				USTPUTC(c, out);
1018				break;
1019			case CRP:	/* ')' in arithmetic */
1020				if (parenlevel > 0) {
1021					USTPUTC(c, out);
1022					--parenlevel;
1023				} else {
1024					if (pgetc() == ')') {
1025						if (--arinest == 0) {
1026							USTPUTC(CTLENDARI, out);
1027							syntax = prevsyntax;
1028							if (syntax == DQSYNTAX)
1029								dblquote = 1;
1030							else
1031								dblquote = 0;
1032						} else
1033							USTPUTC(')', out);
1034					} else {
1035						/*
1036						 * unbalanced parens
1037						 *  (don't 2nd guess - no error)
1038						 */
1039						pungetc();
1040						USTPUTC(')', out);
1041					}
1042				}
1043				break;
1044			case CBQUOTE:	/* '`' */
1045				PARSEBACKQOLD();
1046				break;
1047			case CEOF:
1048				goto endword;		/* exit outer loop */
1049			default:
1050				if (varnest == 0)
1051					goto endword;	/* exit outer loop */
1052				USTPUTC(c, out);
1053			}
1054			c = pgetc_macro();
1055		}
1056	}
1057endword:
1058	if (syntax == ARISYNTAX)
1059		synerror("Missing '))'");
1060	if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
1061		synerror("Unterminated quoted string");
1062	if (varnest != 0) {
1063		startlinno = plinno;
1064		synerror("Missing '}'");
1065	}
1066	USTPUTC('\0', out);
1067	len = out - stackblock();
1068	out = stackblock();
1069	if (eofmark == NULL) {
1070		if ((c == '>' || c == '<')
1071		 && quotef == 0
1072		 && len <= 2
1073		 && (*out == '\0' || is_digit(*out))) {
1074			PARSEREDIR();
1075			return lasttoken = TREDIR;
1076		} else {
1077			pungetc();
1078		}
1079	}
1080	quoteflag = quotef;
1081	backquotelist = bqlist;
1082	grabstackblock(len);
1083	wordtext = out;
1084	return lasttoken = TWORD;
1085/* end of readtoken routine */
1086
1087
1088
1089/*
1090 * Check to see whether we are at the end of the here document.  When this
1091 * is called, c is set to the first character of the next input line.  If
1092 * we are at the end of the here document, this routine sets the c to PEOF.
1093 */
1094
1095checkend: {
1096	if (eofmark) {
1097		if (striptabs) {
1098			while (c == '\t')
1099				c = pgetc();
1100		}
1101		if (c == *eofmark) {
1102			if (pfgets(line, sizeof line) != NULL) {
1103				char *p, *q;
1104
1105				p = line;
1106				for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
1107				if (*p == '\n' && *q == '\0') {
1108					c = PEOF;
1109					plinno++;
1110					needprompt = doprompt;
1111				} else {
1112					pushstring(line, strlen(line), NULL);
1113				}
1114			}
1115		}
1116	}
1117	goto checkend_return;
1118}
1119
1120
1121/*
1122 * Parse a redirection operator.  The variable "out" points to a string
1123 * specifying the fd to be redirected.  The variable "c" contains the
1124 * first character of the redirection operator.
1125 */
1126
1127parseredir: {
1128	char fd = *out;
1129	union node *np;
1130
1131	np = (union node *)stalloc(sizeof (struct nfile));
1132	if (c == '>') {
1133		np->nfile.fd = 1;
1134		c = pgetc();
1135		if (c == '>')
1136			np->type = NAPPEND;
1137		else if (c == '&')
1138			np->type = NTOFD;
1139		else if (c == '|')
1140			np->type = NCLOBBER;
1141		else {
1142			np->type = NTO;
1143			pungetc();
1144		}
1145	} else {	/* c == '<' */
1146		np->nfile.fd = 0;
1147		c = pgetc();
1148		if (c == '<') {
1149			if (sizeof (struct nfile) != sizeof (struct nhere)) {
1150				np = (union node *)stalloc(sizeof (struct nhere));
1151				np->nfile.fd = 0;
1152			}
1153			np->type = NHERE;
1154			heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
1155			heredoc->here = np;
1156			if ((c = pgetc()) == '-') {
1157				heredoc->striptabs = 1;
1158			} else {
1159				heredoc->striptabs = 0;
1160				pungetc();
1161			}
1162		} else if (c == '&')
1163			np->type = NFROMFD;
1164		else if (c == '>')
1165			np->type = NFROMTO;
1166		else {
1167			np->type = NFROM;
1168			pungetc();
1169		}
1170	}
1171	if (fd != '\0')
1172		np->nfile.fd = digit_val(fd);
1173	redirnode = np;
1174	goto parseredir_return;
1175}
1176
1177
1178/*
1179 * Parse a substitution.  At this point, we have read the dollar sign
1180 * and nothing else.
1181 */
1182
1183parsesub: {
1184	int subtype;
1185	int typeloc;
1186	int flags;
1187	char *p;
1188#ifndef GDB_HACK
1189	static const char types[] = "}-+?=";
1190#endif
1191       int bracketed_name = 0; /* used to handle ${[0-9]*} variables */
1192
1193	c = pgetc();
1194	if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) {
1195		USTPUTC('$', out);
1196		pungetc();
1197	} else if (c == '(') {	/* $(command) or $((arith)) */
1198		if (pgetc() == '(') {
1199			PARSEARITH();
1200		} else {
1201			pungetc();
1202			PARSEBACKQNEW();
1203		}
1204	} else {
1205		USTPUTC(CTLVAR, out);
1206		typeloc = out - stackblock();
1207		USTPUTC(VSNORMAL, out);
1208		subtype = VSNORMAL;
1209		if (c == '{') {
1210			bracketed_name = 1;
1211			c = pgetc();
1212			if (c == '#') {
1213				if ((c = pgetc()) == '}')
1214					c = '#';
1215				else
1216					subtype = VSLENGTH;
1217			}
1218			else
1219				subtype = 0;
1220		}
1221		if (is_name(c)) {
1222			do {
1223				STPUTC(c, out);
1224				c = pgetc();
1225			} while (is_in_name(c));
1226		} else if (is_digit(c)) {
1227			if (bracketed_name) {
1228				do {
1229					STPUTC(c, out);
1230					c = pgetc();
1231				} while (is_digit(c));
1232			} else {
1233				STPUTC(c, out);
1234				c = pgetc();
1235			}
1236		} else {
1237			if (! is_special(c))
1238badsub:				synerror("Bad substitution");
1239			USTPUTC(c, out);
1240			c = pgetc();
1241		}
1242		STPUTC('=', out);
1243		flags = 0;
1244		if (subtype == 0) {
1245			switch (c) {
1246			case ':':
1247				flags = VSNUL;
1248				c = pgetc();
1249				/*FALLTHROUGH*/
1250			default:
1251				p = strchr(types, c);
1252				if (p == NULL)
1253					goto badsub;
1254				subtype = p - types + VSNORMAL;
1255				break;
1256			case '%':
1257			case '#':
1258				{
1259					int cc = c;
1260					subtype = c == '#' ? VSTRIMLEFT :
1261							     VSTRIMRIGHT;
1262					c = pgetc();
1263					if (c == cc)
1264						subtype++;
1265					else
1266						pungetc();
1267					break;
1268				}
1269			}
1270		} else {
1271			pungetc();
1272		}
1273		if (subtype != VSLENGTH && (dblquote || arinest))
1274			flags |= VSQUOTE;
1275		*(stackblock() + typeloc) = subtype | flags;
1276		if (subtype != VSNORMAL)
1277			varnest++;
1278	}
1279	goto parsesub_return;
1280}
1281
1282
1283/*
1284 * Called to parse command substitutions.  Newstyle is set if the command
1285 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
1286 * list of commands (passed by reference), and savelen is the number of
1287 * characters on the top of the stack which must be preserved.
1288 */
1289
1290parsebackq: {
1291	struct nodelist **nlpp;
1292	int savepbq;
1293	union node *n;
1294	char *volatile str;
1295	struct jmploc jmploc;
1296	struct jmploc *volatile savehandler;
1297	int savelen;
1298	int saveprompt;
1299#if __GNUC__
1300	/* Avoid longjmp clobbering */
1301	(void) &saveprompt;
1302#endif
1303
1304	savepbq = parsebackquote;
1305	if (setjmp(jmploc.loc)) {
1306		if (str)
1307			ckfree(str);
1308		parsebackquote = 0;
1309		handler = savehandler;
1310		longjmp(handler->loc, 1);
1311	}
1312	INTOFF;
1313	str = NULL;
1314	savelen = out - stackblock();
1315	if (savelen > 0) {
1316		str = ckmalloc(savelen);
1317		memcpy(str, stackblock(), savelen);
1318	}
1319	savehandler = handler;
1320	handler = &jmploc;
1321	INTON;
1322        if (oldstyle) {
1323                /* We must read until the closing backquote, giving special
1324                   treatment to some slashes, and then push the string and
1325                   reread it as input, interpreting it normally.  */
1326                char *out;
1327                int c;
1328                int savelen;
1329                char *str;
1330
1331
1332                STARTSTACKSTR(out);
1333		for (;;) {
1334			if (needprompt) {
1335				setprompt(2);
1336				needprompt = 0;
1337			}
1338			switch (c = pgetc()) {
1339			case '`':
1340				goto done;
1341
1342			case '\\':
1343                                if ((c = pgetc()) == '\n') {
1344					plinno++;
1345					if (doprompt)
1346						setprompt(2);
1347					else
1348						setprompt(0);
1349					/*
1350					 * If eating a newline, avoid putting
1351					 * the newline into the new character
1352					 * stream (via the STPUTC after the
1353					 * switch).
1354					 */
1355					continue;
1356				}
1357                                if (c != '\\' && c != '`' && c != '$'
1358                                    && (!dblquote || c != '"'))
1359                                        STPUTC('\\', out);
1360				break;
1361
1362			case '\n':
1363				plinno++;
1364				needprompt = doprompt;
1365				break;
1366
1367			case PEOF:
1368			        startlinno = plinno;
1369				synerror("EOF in backquote substitution");
1370 				break;
1371
1372			default:
1373				break;
1374			}
1375			STPUTC(c, out);
1376                }
1377done:
1378                STPUTC('\0', out);
1379                savelen = out - stackblock();
1380                if (savelen > 0) {
1381                        str = ckmalloc(savelen);
1382                        memcpy(str, stackblock(), savelen);
1383			setinputstring(str, 1);
1384                }
1385        }
1386	nlpp = &bqlist;
1387	while (*nlpp)
1388		nlpp = &(*nlpp)->next;
1389	*nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
1390	(*nlpp)->next = NULL;
1391	parsebackquote = oldstyle;
1392
1393	if (oldstyle) {
1394		saveprompt = doprompt;
1395		doprompt = 0;
1396	}
1397
1398	n = list(0);
1399
1400	if (oldstyle)
1401		doprompt = saveprompt;
1402	else {
1403		if (readtoken() != TRP)
1404			synexpect(TRP);
1405	}
1406
1407	(*nlpp)->n = n;
1408        if (oldstyle) {
1409		/*
1410		 * Start reading from old file again, ignoring any pushed back
1411		 * tokens left from the backquote parsing
1412		 */
1413                popfile();
1414		tokpushback = 0;
1415	}
1416	while (stackblocksize() <= savelen)
1417		growstackblock();
1418	STARTSTACKSTR(out);
1419	if (str) {
1420		memcpy(out, str, savelen);
1421		STADJUST(savelen, out);
1422		INTOFF;
1423		ckfree(str);
1424		str = NULL;
1425		INTON;
1426	}
1427	parsebackquote = savepbq;
1428	handler = savehandler;
1429	if (arinest || dblquote)
1430		USTPUTC(CTLBACKQ | CTLQUOTE, out);
1431	else
1432		USTPUTC(CTLBACKQ, out);
1433	if (oldstyle)
1434		goto parsebackq_oldreturn;
1435	else
1436		goto parsebackq_newreturn;
1437}
1438
1439/*
1440 * Parse an arithmetic expansion (indicate start of one and set state)
1441 */
1442parsearith: {
1443
1444	if (++arinest == 1) {
1445		prevsyntax = syntax;
1446		syntax = ARISYNTAX;
1447		USTPUTC(CTLARI, out);
1448		if (dblquote)
1449			USTPUTC('"',out);
1450		else
1451			USTPUTC(' ',out);
1452	} else {
1453		/*
1454		 * we collapse embedded arithmetic expansion to
1455		 * parenthesis, which should be equivalent
1456		 */
1457		USTPUTC('(', out);
1458	}
1459	goto parsearith_return;
1460}
1461
1462} /* end of readtoken */
1463
1464
1465
1466#ifdef mkinit
1467RESET {
1468	tokpushback = 0;
1469	checkkwd = 0;
1470}
1471#endif
1472
1473/*
1474 * Returns true if the text contains nothing to expand (no dollar signs
1475 * or backquotes).
1476 */
1477
1478STATIC int
1479noexpand(char *text)
1480{
1481	char *p;
1482	char c;
1483
1484	p = text;
1485	while ((c = *p++) != '\0') {
1486		if ( c == CTLQUOTEMARK)
1487			continue;
1488		if (c == CTLESC)
1489			p++;
1490		else if (BASESYNTAX[(int)c] == CCTL)
1491			return 0;
1492	}
1493	return 1;
1494}
1495
1496
1497/*
1498 * Return true if the argument is a legal variable name (a letter or
1499 * underscore followed by zero or more letters, underscores, and digits).
1500 */
1501
1502int
1503goodname(char *name)
1504{
1505	char *p;
1506
1507	p = name;
1508	if (! is_name(*p))
1509		return 0;
1510	while (*++p) {
1511		if (! is_in_name(*p))
1512			return 0;
1513	}
1514	return 1;
1515}
1516
1517
1518/*
1519 * Called when an unexpected token is read during the parse.  The argument
1520 * is the token that is expected, or -1 if more than one type of token can
1521 * occur at this point.
1522 */
1523
1524STATIC void
1525synexpect(int token)
1526{
1527	char msg[64];
1528
1529	if (token >= 0) {
1530		fmtstr(msg, 64, "%s unexpected (expecting %s)",
1531			tokname[lasttoken], tokname[token]);
1532	} else {
1533		fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
1534	}
1535	synerror(msg);
1536}
1537
1538
1539STATIC void
1540synerror(char *msg)
1541{
1542	if (commandname)
1543		outfmt(&errout, "%s: %d: ", commandname, startlinno);
1544	outfmt(&errout, "Syntax error: %s\n", msg);
1545	error((char *)NULL);
1546}
1547
1548STATIC void
1549setprompt(int which)
1550{
1551	whichprompt = which;
1552
1553#ifndef NO_HISTORY
1554	if (!el)
1555#endif
1556		out2str(getprompt(NULL));
1557}
1558
1559/*
1560 * called by editline -- any expansions to the prompt
1561 *    should be added here.
1562 */
1563char *
1564getprompt(void *unused __unused)
1565{
1566	static char ps[PROMPTLEN];
1567	char *fmt;
1568	int i, j, trim;
1569
1570	/*
1571	 * Select prompt format.
1572	 */
1573	switch (whichprompt) {
1574	case 0:
1575		fmt = "";
1576		break;
1577	case 1:
1578		fmt = ps1val();
1579		break;
1580	case 2:
1581		fmt = ps2val();
1582		break;
1583	default:
1584		return "<internal prompt error>";
1585	}
1586
1587	/*
1588	 * Format prompt string.
1589	 */
1590	for (i = 0; (i < 127) && (*fmt != '\0'); i++, fmt++)
1591		if (*fmt == '\\')
1592			switch (*++fmt) {
1593
1594				/*
1595				 * Hostname.
1596				 *
1597				 * \h specifies just the local hostname,
1598				 * \H specifies fully-qualified hostname.
1599				 */
1600			case 'h':
1601			case 'H':
1602				ps[i] == '\0';
1603				gethostname(&ps[i], PROMPTLEN - i);
1604				/* Skip to end of hostname. */
1605				trim = (*fmt == 'h') ? '.' : '\0';
1606				while ((ps[i+1] != '\0') && (ps[i+1] != trim))
1607					i++;
1608				break;
1609
1610				/*
1611				 * Working directory.
1612				 *
1613				 * \W specifies just the final component,
1614				 * \w specifies the entire path.
1615				 */
1616			case 'W':
1617			case 'w':
1618				ps[i] == '\0';
1619				getcwd(&ps[i], PROMPTLEN - i);
1620				if (*fmt == 'W') {
1621					/* Final path component only. */
1622					trim = 1;
1623					for (j = i; ps[j] != '\0'; j++)
1624					  if (ps[j] == '/')
1625						trim = j + 1;
1626					memmove(&ps[i], &ps[trim],
1627					    j - trim + 1);
1628				}
1629				/* Skip to end of path. */
1630				while (ps[i + 1] != '\0')
1631					i++;
1632				break;
1633
1634				/*
1635				 * Superuser status.
1636				 *
1637				 * '$' for normal users, '#' for root.
1638				 */
1639			case '$':
1640				ps[i] = (geteuid() != 0) ? '$' : '#';
1641				break;
1642
1643				/*
1644				 * A literal \.
1645				 */
1646			case '\\':
1647				ps[i] = '\\';
1648				break;
1649
1650				/*
1651				 * Emit unrecognized formats verbatim.
1652				 */
1653			default:
1654				ps[i++] = '\\';
1655				ps[i] = *fmt;
1656				break;
1657			}
1658		else
1659			ps[i] = *fmt;
1660	ps[i] = '\0';
1661	return (ps);
1662}
1663