11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
331556Srgrimes#ifndef lint
3436150Scharnier#if 0
3536150Scharnierstatic char sccsid[] = "@(#)input.c	8.3 (Berkeley) 6/9/95";
3636150Scharnier#endif
371556Srgrimes#endif /* not lint */
3899110Sobrien#include <sys/cdefs.h>
3999110Sobrien__FBSDID("$FreeBSD$");
401556Srgrimes
4117987Speter#include <stdio.h>	/* defines BUFSIZ */
4217987Speter#include <fcntl.h>
4317987Speter#include <errno.h>
4417987Speter#include <unistd.h>
4517987Speter#include <stdlib.h>
4617987Speter#include <string.h>
4717987Speter
481556Srgrimes/*
491556Srgrimes * This file implements the input routines used by the parser.
501556Srgrimes */
511556Srgrimes
521556Srgrimes#include "shell.h"
5317987Speter#include "redir.h"
541556Srgrimes#include "syntax.h"
551556Srgrimes#include "input.h"
561556Srgrimes#include "output.h"
571556Srgrimes#include "options.h"
581556Srgrimes#include "memalloc.h"
591556Srgrimes#include "error.h"
601556Srgrimes#include "alias.h"
611556Srgrimes#include "parser.h"
621556Srgrimes#include "myhistedit.h"
63100588Stjr#include "trap.h"
641556Srgrimes
651556Srgrimes#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
661556Srgrimes
671556Srgrimesstruct strpush {
681556Srgrimes	struct strpush *prev;	/* preceding string on stack */
69248980Sjilles	const char *prevstring;
701556Srgrimes	int prevnleft;
7112043Speter	int prevlleft;
721556Srgrimes	struct alias *ap;	/* if push was associated with an alias */
731556Srgrimes};
741556Srgrimes
751556Srgrimes/*
761556Srgrimes * The parsefile structure pointed to by the global variable parsefile
771556Srgrimes * contains information about the current file being read.
781556Srgrimes */
791556Srgrimes
801556Srgrimesstruct parsefile {
811556Srgrimes	struct parsefile *prev;	/* preceding file on stack */
821556Srgrimes	int linno;		/* current line */
831556Srgrimes	int fd;			/* file descriptor (or -1 if string) */
8420425Ssteve	int nleft;		/* number of chars left in this line */
8520425Ssteve	int lleft;		/* number of lines left in this buffer */
86248980Sjilles	const char *nextc;	/* next char in buffer */
871556Srgrimes	char *buf;		/* input buffer */
881556Srgrimes	struct strpush *strpush; /* for pushing strings at this level */
891556Srgrimes	struct strpush basestrpush; /* so pushing one is fast */
901556Srgrimes};
911556Srgrimes
921556Srgrimes
931556Srgrimesint plinno = 1;			/* input line number */
94201053Sjillesint parsenleft;			/* copy of parsefile->nleft */
95253658Sjillesstatic int parselleft;		/* copy of parsefile->lleft */
96248980Sjillesconst char *parsenextc;		/* copy of parsefile->nextc */
97245676Sjillesstatic char basebuf[BUFSIZ + 1];/* buffer for top level input file */
98245676Sjillesstatic struct parsefile basepf = {	/* top level input file */
99245676Sjilles	.nextc = basebuf,
100245676Sjilles	.buf = basebuf
101245676Sjilles};
102213760Sobrienstatic struct parsefile *parsefile = &basepf;	/* current input file */
1031556Srgrimesint whichprompt;		/* 1 == PS1, 2 == PS2 */
1041556Srgrimes
1051556SrgrimesEditLine *el;			/* cookie for editline package */
1061556Srgrimes
107213811Sobrienstatic void pushfile(void);
108213811Sobrienstatic int preadfd(void);
109229220Sjillesstatic void popstring(void);
1101556Srgrimes
111253650Sjillesvoid
112253650Sjillesresetinput(void)
113253650Sjilles{
114194406Sjilles	popallfiles();
115218306Sjilles	parselleft = parsenleft = 0;	/* clear input buffer */
1161556Srgrimes}
1171556Srgrimes
1181556Srgrimes
1191556Srgrimes/*
1201556Srgrimes * Read a line from the script.
1211556Srgrimes */
1221556Srgrimes
1231556Srgrimeschar *
12490111Simppfgets(char *line, int len)
12517987Speter{
12625225Ssteve	char *p = line;
1271556Srgrimes	int nleft = len;
1281556Srgrimes	int c;
1291556Srgrimes
1301556Srgrimes	while (--nleft > 0) {
1311556Srgrimes		c = pgetc_macro();
1321556Srgrimes		if (c == PEOF) {
1331556Srgrimes			if (p == line)
1341556Srgrimes				return NULL;
1351556Srgrimes			break;
1361556Srgrimes		}
1371556Srgrimes		*p++ = c;
1381556Srgrimes		if (c == '\n')
1391556Srgrimes			break;
1401556Srgrimes	}
1411556Srgrimes	*p = '\0';
1421556Srgrimes	return line;
1431556Srgrimes}
1441556Srgrimes
1451556Srgrimes
1461556Srgrimes
1471556Srgrimes/*
1481556Srgrimes * Read a character from the script, returning PEOF on end of file.
1491556Srgrimes * Nul characters in the input are silently discarded.
1501556Srgrimes */
1511556Srgrimes
1521556Srgrimesint
15390111Simppgetc(void)
15420425Ssteve{
1551556Srgrimes	return pgetc_macro();
1561556Srgrimes}
1571556Srgrimes
15820425Ssteve
159213811Sobrienstatic int
16090111Simppreadfd(void)
16112043Speter{
16212043Speter	int nr;
16320425Ssteve	parsenextc = parsefile->buf;
1641556Srgrimes
16512043Speterretry:
16625225Ssteve#ifndef NO_HISTORY
16712043Speter	if (parsefile->fd == 0 && el) {
168158143Sstefanf		static const char *rl_cp;
169158143Sstefanf		static int el_len;
17012043Speter
171262951Sjmmv		if (rl_cp == NULL) {
172262951Sjmmv			el_resize(el);
173158143Sstefanf			rl_cp = el_gets(el, &el_len);
174262951Sjmmv		}
175158143Sstefanf		if (rl_cp == NULL)
176238377Spfg			nr = el_len == 0 ? 0 : -1;
17712043Speter		else {
178158143Sstefanf			nr = el_len;
179230118Sjilles			if (nr > BUFSIZ)
180230118Sjilles				nr = BUFSIZ;
181248980Sjilles			memcpy(parsefile->buf, rl_cp, nr);
182158143Sstefanf			if (nr != el_len) {
183158143Sstefanf				el_len -= nr;
184158143Sstefanf				rl_cp += nr;
185158143Sstefanf			} else
186158143Sstefanf				rl_cp = NULL;
18712043Speter		}
18825225Ssteve	} else
18925225Ssteve#endif
190248980Sjilles		nr = read(parsefile->fd, parsefile->buf, BUFSIZ);
19112043Speter
19212043Speter	if (nr <= 0) {
19312043Speter                if (nr < 0) {
19412043Speter                        if (errno == EINTR)
19512043Speter                                goto retry;
19612043Speter                        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
19712043Speter                                int flags = fcntl(0, F_GETFL, 0);
19812043Speter                                if (flags >= 0 && flags & O_NONBLOCK) {
19912043Speter                                        flags &=~ O_NONBLOCK;
20012043Speter                                        if (fcntl(0, F_SETFL, flags) >= 0) {
201199629Sjilles						out2fmt_flush("sh: turning off NDELAY mode\n");
20212043Speter                                                goto retry;
20312043Speter                                        }
20412043Speter                                }
20512043Speter                        }
20612043Speter                }
20720425Ssteve                nr = -1;
20812043Speter	}
20912043Speter	return nr;
21012043Speter}
21112043Speter
2121556Srgrimes/*
2131556Srgrimes * Refill the input buffer and return the next input character:
2141556Srgrimes *
2151556Srgrimes * 1) If a string was pushed back on the input, pop it;
2161556Srgrimes * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
2171556Srgrimes *    from a string so we can't refill the buffer, return EOF.
21820425Ssteve * 3) If there is more in this buffer, use it else call read to fill it.
21920425Ssteve * 4) Process input up to the next newline, deleting nul characters.
2201556Srgrimes */
2211556Srgrimes
2221556Srgrimesint
22390111Simppreadbuffer(void)
22420425Ssteve{
22512043Speter	char *p, *q;
22612043Speter	int more;
22712043Speter	char savec;
2281556Srgrimes
229262951Sjmmv	while (parsefile->strpush) {
230262951Sjmmv		/*
231262951Sjmmv		 * Add a space to the end of an alias to ensure that the
232262951Sjmmv		 * alias remains in use while parsing its last word.
233262951Sjmmv		 * This avoids alias recursions.
234262951Sjmmv		 */
235262951Sjmmv		if (parsenleft == -1 && parsefile->strpush->ap != NULL)
236262951Sjmmv			return ' ';
2371556Srgrimes		popstring();
2381556Srgrimes		if (--parsenleft >= 0)
2391556Srgrimes			return (*parsenextc++);
2401556Srgrimes	}
2411556Srgrimes	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
2421556Srgrimes		return PEOF;
2431556Srgrimes	flushout(&output);
2441556Srgrimes	flushout(&errout);
2451556Srgrimes
24612043Speteragain:
24712043Speter	if (parselleft <= 0) {
24825225Ssteve		if ((parselleft = preadfd()) == -1) {
24912043Speter			parselleft = parsenleft = EOF_NLEFT;
25012043Speter			return PEOF;
2511556Srgrimes		}
2521556Srgrimes	}
2531556Srgrimes
254248980Sjilles	q = p = parsefile->buf + (parsenextc - parsefile->buf);
25512043Speter
2561556Srgrimes	/* delete nul characters */
25712043Speter	for (more = 1; more;) {
25812043Speter		switch (*p) {
25912043Speter		case '\0':
26012043Speter			p++;	/* Skip nul */
26112043Speter			goto check;
26212043Speter
26312043Speter		case '\n':
26412043Speter			parsenleft = q - parsenextc;
26512043Speter			more = 0; /* Stop processing here */
26612043Speter			break;
26712043Speter
26812043Speter		default:
26912043Speter			break;
2701556Srgrimes		}
27112043Speter
27212043Speter		*q++ = *p++;
27312043Spetercheck:
27412043Speter		if (--parselleft <= 0) {
27512043Speter			parsenleft = q - parsenextc - 1;
27612043Speter			if (parsenleft < 0)
27712043Speter				goto again;
27812043Speter			*q = '\0';
27912043Speter			more = 0;
28012043Speter		}
2811556Srgrimes	}
28212043Speter
28312043Speter	savec = *q;
2841556Srgrimes	*q = '\0';
2851556Srgrimes
28618018Speter#ifndef NO_HISTORY
287262951Sjmmv	if (parsefile->fd == 0 && hist &&
288262951Sjmmv	    parsenextc[strspn(parsenextc, " \t\n")] != '\0') {
28984261Sobrien		HistEvent he;
2901556Srgrimes		INTOFF;
29184261Sobrien		history(hist, &he, whichprompt == 1 ? H_ENTER : H_ADD,
29284261Sobrien		    parsenextc);
2931556Srgrimes		INTON;
2941556Srgrimes	}
29518018Speter#endif
29612043Speter
2971556Srgrimes	if (vflag) {
29812043Speter		out2str(parsenextc);
2991556Srgrimes		flushout(out2);
3001556Srgrimes	}
30112043Speter
30212043Speter	*q = savec;
30312043Speter
3041556Srgrimes	return *parsenextc++;
3051556Srgrimes}
3061556Srgrimes
3071556Srgrimes/*
308194128Sjilles * Returns if we are certain we are at EOF. Does not cause any more input
309194128Sjilles * to be read from the outside world.
310194128Sjilles */
311194128Sjilles
312194128Sjillesint
313194128Sjillespreadateof(void)
314194128Sjilles{
315194128Sjilles	if (parsenleft > 0)
316194128Sjilles		return 0;
317194128Sjilles	if (parsefile->strpush)
318194128Sjilles		return 0;
319194128Sjilles	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
320194128Sjilles		return 1;
321194128Sjilles	return 0;
322194128Sjilles}
323194128Sjilles
324194128Sjilles/*
3251556Srgrimes * Undo the last call to pgetc.  Only one character may be pushed back.
3261556Srgrimes * PEOF may be pushed back.
3271556Srgrimes */
3281556Srgrimes
3291556Srgrimesvoid
33090111Simppungetc(void)
33190111Simp{
3321556Srgrimes	parsenleft++;
3331556Srgrimes	parsenextc--;
3341556Srgrimes}
3351556Srgrimes
3361556Srgrimes/*
3371556Srgrimes * Push a string back onto the input at this current parsefile level.
3381556Srgrimes * We handle aliases this way.
3391556Srgrimes */
3401556Srgrimesvoid
341242895Sjillespushstring(char *s, int len, struct alias *ap)
34290111Simp{
3431556Srgrimes	struct strpush *sp;
3441556Srgrimes
3451556Srgrimes	INTOFF;
346199629Sjilles/*out2fmt_flush("*** calling pushstring: %s, %d\n", s, len);*/
3471556Srgrimes	if (parsefile->strpush) {
3481556Srgrimes		sp = ckmalloc(sizeof (struct strpush));
3491556Srgrimes		sp->prev = parsefile->strpush;
3501556Srgrimes		parsefile->strpush = sp;
3511556Srgrimes	} else
3521556Srgrimes		sp = parsefile->strpush = &(parsefile->basestrpush);
3531556Srgrimes	sp->prevstring = parsenextc;
3541556Srgrimes	sp->prevnleft = parsenleft;
35512043Speter	sp->prevlleft = parselleft;
356242895Sjilles	sp->ap = ap;
3571556Srgrimes	if (ap)
358242895Sjilles		ap->flag |= ALIASINUSE;
3591556Srgrimes	parsenextc = s;
3601556Srgrimes	parsenleft = len;
3611556Srgrimes	INTON;
3621556Srgrimes}
3631556Srgrimes
364229220Sjillesstatic void
36590111Simppopstring(void)
3661556Srgrimes{
3671556Srgrimes	struct strpush *sp = parsefile->strpush;
3681556Srgrimes
3691556Srgrimes	INTOFF;
370262951Sjmmv	if (sp->ap) {
371262951Sjmmv		if (parsenextc != sp->ap->val &&
372262951Sjmmv		    (parsenextc[-1] == ' ' || parsenextc[-1] == '\t'))
373262951Sjmmv			forcealias();
374262951Sjmmv		sp->ap->flag &= ~ALIASINUSE;
375262951Sjmmv	}
3761556Srgrimes	parsenextc = sp->prevstring;
3771556Srgrimes	parsenleft = sp->prevnleft;
37812043Speter	parselleft = sp->prevlleft;
379199629Sjilles/*out2fmt_flush("*** calling popstring: restoring to '%s'\n", parsenextc);*/
3801556Srgrimes	parsefile->strpush = sp->prev;
3811556Srgrimes	if (sp != &(parsefile->basestrpush))
3821556Srgrimes		ckfree(sp);
3831556Srgrimes	INTON;
3841556Srgrimes}
3851556Srgrimes
3861556Srgrimes/*
3871556Srgrimes * Set the input to take input from a file.  If push is set, push the
3881556Srgrimes * old input onto the stack first.
3891556Srgrimes */
3901556Srgrimes
3911556Srgrimesvoid
392200956Sjillessetinputfile(const char *fname, int push)
39317987Speter{
3941556Srgrimes	int fd;
3951556Srgrimes	int fd2;
3961556Srgrimes
3971556Srgrimes	INTOFF;
398250267Sjilles	if ((fd = open(fname, O_RDONLY | O_CLOEXEC)) < 0)
399222684Sjilles		error("cannot open %s: %s", fname, strerror(errno));
4001556Srgrimes	if (fd < 10) {
401250267Sjilles		fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 10);
4021556Srgrimes		close(fd);
4031556Srgrimes		if (fd2 < 0)
4041556Srgrimes			error("Out of file descriptors");
4051556Srgrimes		fd = fd2;
4061556Srgrimes	}
4071556Srgrimes	setinputfd(fd, push);
4081556Srgrimes	INTON;
4091556Srgrimes}
4101556Srgrimes
4111556Srgrimes
4121556Srgrimes/*
413250267Sjilles * Like setinputfile, but takes an open file descriptor (which should have
414250267Sjilles * its FD_CLOEXEC flag already set).  Call this with interrupts off.
4151556Srgrimes */
4161556Srgrimes
4171556Srgrimesvoid
41890111Simpsetinputfd(int fd, int push)
41917987Speter{
4201556Srgrimes	if (push) {
4211556Srgrimes		pushfile();
422230118Sjilles		parsefile->buf = ckmalloc(BUFSIZ + 1);
4231556Srgrimes	}
4241556Srgrimes	if (parsefile->fd > 0)
4251556Srgrimes		close(parsefile->fd);
4261556Srgrimes	parsefile->fd = fd;
4271556Srgrimes	if (parsefile->buf == NULL)
428230118Sjilles		parsefile->buf = ckmalloc(BUFSIZ + 1);
42912043Speter	parselleft = parsenleft = 0;
4301556Srgrimes	plinno = 1;
4311556Srgrimes}
4321556Srgrimes
4331556Srgrimes
4341556Srgrimes/*
4351556Srgrimes * Like setinputfile, but takes input from a string.
4361556Srgrimes */
4371556Srgrimes
4381556Srgrimesvoid
439248980Sjillessetinputstring(const char *string, int push)
44090111Simp{
4411556Srgrimes	INTOFF;
4421556Srgrimes	if (push)
4431556Srgrimes		pushfile();
4441556Srgrimes	parsenextc = string;
44512043Speter	parselleft = parsenleft = strlen(string);
4461556Srgrimes	parsefile->buf = NULL;
4471556Srgrimes	plinno = 1;
4481556Srgrimes	INTON;
4491556Srgrimes}
4501556Srgrimes
4511556Srgrimes
4521556Srgrimes
4531556Srgrimes/*
4541556Srgrimes * To handle the "." command, a stack of input files is used.  Pushfile
4551556Srgrimes * adds a new entry to the stack and popfile restores the previous level.
4561556Srgrimes */
4571556Srgrimes
458213811Sobrienstatic void
45990111Simppushfile(void)
46090111Simp{
4611556Srgrimes	struct parsefile *pf;
4621556Srgrimes
4631556Srgrimes	parsefile->nleft = parsenleft;
46412043Speter	parsefile->lleft = parselleft;
4651556Srgrimes	parsefile->nextc = parsenextc;
4661556Srgrimes	parsefile->linno = plinno;
4671556Srgrimes	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
4681556Srgrimes	pf->prev = parsefile;
4691556Srgrimes	pf->fd = -1;
4701556Srgrimes	pf->strpush = NULL;
4711556Srgrimes	pf->basestrpush.prev = NULL;
4721556Srgrimes	parsefile = pf;
4731556Srgrimes}
4741556Srgrimes
4751556Srgrimes
4761556Srgrimesvoid
47790111Simppopfile(void)
47890111Simp{
4791556Srgrimes	struct parsefile *pf = parsefile;
4801556Srgrimes
4811556Srgrimes	INTOFF;
4821556Srgrimes	if (pf->fd >= 0)
4831556Srgrimes		close(pf->fd);
4841556Srgrimes	if (pf->buf)
4851556Srgrimes		ckfree(pf->buf);
4861556Srgrimes	while (pf->strpush)
4871556Srgrimes		popstring();
4881556Srgrimes	parsefile = pf->prev;
4891556Srgrimes	ckfree(pf);
4901556Srgrimes	parsenleft = parsefile->nleft;
49112043Speter	parselleft = parsefile->lleft;
4921556Srgrimes	parsenextc = parsefile->nextc;
4931556Srgrimes	plinno = parsefile->linno;
4941556Srgrimes	INTON;
4951556Srgrimes}
4961556Srgrimes
4971556Srgrimes
4981556Srgrimes/*
499199647Sjilles * Return current file (to go back to it later using popfilesupto()).
500199647Sjilles */
501199647Sjilles
502199647Sjillesstruct parsefile *
503199647Sjillesgetcurrentfile(void)
504199647Sjilles{
505199647Sjilles	return parsefile;
506199647Sjilles}
507199647Sjilles
508199647Sjilles
509199647Sjilles/*
510199647Sjilles * Pop files until the given file is on top again. Useful for regular
511199647Sjilles * builtins that read shell commands from files or strings.
512199647Sjilles * If the given file is not an active file, an error is raised.
513199647Sjilles */
514199647Sjilles
515199647Sjillesvoid
516199647Sjillespopfilesupto(struct parsefile *file)
517199647Sjilles{
518199647Sjilles	while (parsefile != file && parsefile != &basepf)
519199647Sjilles		popfile();
520199647Sjilles	if (parsefile != file)
521199647Sjilles		error("popfilesupto() misused");
522199647Sjilles}
523199647Sjilles
524199647Sjilles/*
5251556Srgrimes * Return to top level.
5261556Srgrimes */
5271556Srgrimes
5281556Srgrimesvoid
52990111Simppopallfiles(void)
53090111Simp{
5311556Srgrimes	while (parsefile != &basepf)
5321556Srgrimes		popfile();
5331556Srgrimes}
5341556Srgrimes
5351556Srgrimes
5361556Srgrimes
5371556Srgrimes/*
5381556Srgrimes * Close the file(s) that the shell is reading commands from.  Called
5391556Srgrimes * after a fork is done.
5401556Srgrimes */
5411556Srgrimes
5421556Srgrimesvoid
54390111Simpclosescript(void)
54490111Simp{
5451556Srgrimes	popallfiles();
5461556Srgrimes	if (parsefile->fd > 0) {
5471556Srgrimes		close(parsefile->fd);
5481556Srgrimes		parsefile->fd = 0;
5491556Srgrimes	}
5501556Srgrimes}
551