master xplshn/aruu / cmd / posix / bc.y
  1%{
  2#include <libgen.h>
  3#include <unistd.h>
  4
  5#include <assert.h>
  6#include <ctype.h>
  7#include <errno.h>
  8#include <setjmp.h>
  9#include <stdarg.h>
 10#include <stdio.h>
 11#include <stdlib.h>
 12#include <string.h>
 13
 14#include "arg.h"
 15#include "util.h"
 16
 17#define DIGITS   "0123456789ABCDEF"
 18#define NESTED_MAX 32
 19
 20#define funid(f) ((f)[0] - 'a' + 1)
 21
 22int yydebug;
 23
 24typedef struct macro Macro;
 25
 26struct macro {
 27	int op;
 28	int id;
 29	char *name;
 30	int flowid;
 31	int nested;
 32};
 33
 34static int yyerror(char *);
 35static int yylex(void);
 36
 37static void quit(void);
 38static char *code(char *, ...);
 39static char *forcode(Macro *, char *, char *, char *, char *);
 40static char *whilecode(Macro *, char *, char *);
 41static char *ifcode(Macro *, char *, char *);
 42static char *funcode(Macro *, char *, char *, char *);
 43static char *param(char *, char *), *local(char *, char *);
 44static char *retcode(char *);
 45static char *brkcode(void);
 46static Macro *macro(int);
 47
 48static char *ftn(char *);
 49static char *var(char *);
 50static char *ary(char *);
 51static void writeout(char *);
 52
 53static char *yytext, *buff, *unwind;
 54static char *filename;
 55static FILE *filep;
 56static int lineno, nerr, flowid;
 57static jmp_buf recover;
 58static int nested, inhome;
 59static Macro macros[NESTED_MAX];
 60int cflag, dflag, lflag, sflag;
 61
 62static char *dcprog = "dc";
 63
 64%}
 65
 66%union {
 67	char *str;
 68	char id[2];
 69	Macro *macro;
 70}
 71
 72%token <id> ID
 73%token <str> STRING NUMBER
 74%token <str> EQOP '+' '-' '*' '/' '%' '^' INCDEC
 75%token HOME LOOP
 76%token DOT
 77%token EQ
 78%token LE
 79%token GE
 80%token NE
 81%token DEF
 82%token BREAK
 83%token QUIT
 84%token LENGTH
 85%token RETURN
 86%token FOR
 87%token IF
 88%token WHILE
 89%token SQRT
 90%token SCALE
 91%token IBASE
 92%token OBASE
 93%token AUTO PARAM
 94%token PRINT
 95
 96%type <str> item statlst scolonlst
 97%type <str> function assign nexpr expr exprstat rel stat ary cond
 98%type <str> autolst arglst parlst
 99%type <str> params param locals local
100%type <macro> def if for while
101
102%right	'=' EQOP
103%left	'+' '-'
104%left	'*' '/' '%'
105%right	'^'
106
107%start    program
108
109%%
110
111program  :
112         | item program
113         ;
114
115item     : scolonlst '\n'       {writeout($1);}
116         | function             {writeout($1);}
117         ;
118
119function : def parlst '{' '\n' autolst statlst '}' {$$ = funcode($1, $2, $5, $6);}
120         ;
121
122scolonlst:                      {$$ = code("");}
123         | stat
124         | scolonlst ';' stat   {$$ = code("%s%s", $1, $3);}
125         | scolonlst ';'
126         ;
127
128statlst :                       {$$ = code("");}
129        | stat
130        | statlst '\n' stat     {$$ = code("%s%s", $1, $3);}
131        | statlst ';' stat      {$$ = code("%s%s", $1, $3);}
132        | statlst '\n'
133        | statlst ';'
134        ;
135
136stat    : exprstat
137        | PRINT expr            {$$ = code("%sps.", $2);}
138        | PRINT STRING          {$$ = code("[%s]P", $2);}
139        | PRINT STRING ',' expr {$$ = code("[%s]P%sps.", $2, $4);}
140        | STRING                {$$ = code("[%s]P", $1);}
141        | BREAK                 {$$ = brkcode();}
142        | QUIT                  {quit();}
143        | RETURN                {$$ = retcode(code(" 0"));}
144        | RETURN '(' expr ')'   {$$ = retcode($3);}
145        | RETURN '(' ')'        {$$ = retcode(code(" 0"));}
146        | while cond stat       {$$ = whilecode($1, $2, $3);}
147        | if cond stat          {$$ = ifcode($1, $2, $3);}
148        | '{' statlst '}'       {$$ = $2;}
149        | for '(' expr ';' rel ';' expr ')' stat  {$$ = forcode($1, $3, $5, $7, $9);}
150        ;
151
152while   : WHILE                 {$$ = macro(LOOP);}
153        ;
154
155if      : IF                    {$$ = macro(IF);}
156        ;
157
158for     : FOR                   {$$ = macro(LOOP);}
159        ;
160
161def     : DEF ID                {$$ = macro(DEF);}
162        ;
163
164parlst  : '(' ')'               {$$ = code("");}
165        | '(' params ')'        {$$ = $2;}
166        ;
167
168params  : param                 {$$ = param(NULL, $1);}
169        | params ',' param      {$$ = param($1, $3);}
170        ;
171
172param   : ID                    {$$ = var($1);}
173        | ID '[' ']'            {$$ = ary($1);}
174        ;
175
176autolst :                       {$$ = code("");}
177        | AUTO locals '\n'      {$$ = $2;}
178        | AUTO locals ';'       {$$ = $2;}
179        ;
180
181locals  : local                 {$$ = local(NULL, $1);}
182        | locals ',' local      {$$ = local($1, $3);}
183        ;
184
185local   : ID                    {$$ = var($1);}
186        | ID '[' ']'            {$$ = ary($1);}
187        ;
188
189arglst  : expr
190        | ID '[' ']'            {$$ = code("%s", ary($1));}
191        | expr ',' arglst       {$$ = code("%s%s", $1, $3);}
192        | ID '[' ']' ',' arglst {$$ = code("%s%s", ary($1), $5);}
193        ;
194
195cond    : '(' rel ')'           {$$ = $2;}
196        ;
197
198rel     : expr                  {$$ = code("%s 0!=", $1);}
199        | expr EQ expr          {$$ = code("%s%s=", $1, $3);}
200        | expr LE expr          {$$ = code("%s%s!<", $1, $3);}
201        | expr GE expr          {$$ = code("%s%s!>", $1, $3);}
202        | expr NE expr          {$$ = code("%s%s!=", $1, $3);}
203        | expr '<' expr         {$$ = code("%s%s>", $1, $3);}
204        | expr '>' expr         {$$ = code("%s%s<", $1, $3);}
205        ;
206
207exprstat: nexpr                 {$$ = code("%s%ss.", $1, code(sflag ? "" : "p"));}
208        | assign                {$$ = code("%ss.", $1);}
209        ;
210
211expr    : nexpr
212        | assign
213        ;
214
215nexpr   : NUMBER                {$$ = code(" %s", code($1));}
216        | ID                    {$$ = code("l%s", var($1));}
217        | DOT                   {$$ = code("l.");}
218        | SCALE                 {$$ = code("K");}
219        | IBASE                 {$$ = code("I");}
220        | OBASE                 {$$ = code("O");}
221        | ID ary                {$$ = code("%s;%s", $2, ary($1));}
222        | '(' expr ')'          {$$ = $2;}
223        | ID '(' arglst ')'     {$$ = code("%sl%sx", $3, ftn($1));}
224        | ID '(' ')'            {$$ = code("l%sx", ftn($1));}
225        | '-' expr              {$$ = code("0%s-", $2);}
226        | expr '+' expr         {$$ = code("%s%s+", $1, $3);}
227        | expr '-' expr         {$$ = code("%s%s-", $1, $3);}
228        | expr '*' expr         {$$ = code("%s%s*", $1, $3);}
229        | expr '/' expr         {$$ = code("%s%s/", $1, $3);}
230        | expr '%' expr         {$$ = code("%s%s%%", $1, $3);}
231        | expr '^' expr         {$$ = code("%s%s^", $1, $3);}
232        | LENGTH '(' expr ')'   {$$ = code("%sZ", $3);}
233        | SQRT '(' expr ')'     {$$ = code("%sv", $3);}
234        | SCALE '(' expr ')'    {$$ = code("%sX", $3);}
235        | INCDEC ID             {$$ = code("l%s1%sds%s", var($2), code($1), var($2));}
236        | INCDEC SCALE          {$$ = code("K1%sk", code($1));}
237        | INCDEC IBASE          {$$ = code("I1%sdi", code($1));}
238        | INCDEC OBASE          {$$ = code("O1%sdo", code($1));}
239        | INCDEC ID ary         {$$ = code("%sdS_;%s1%sdL_:%s", $3, ary($2), code($1), ary($2));}
240        | ID INCDEC             {$$ = code("l%sd1%ss%s", var($1), code($2), var($1));}
241        | SCALE INCDEC          {$$ = code("Kd1%sk", code($2));}
242        | IBASE INCDEC          {$$ = code("Id1%si", code($2));}
243        | OBASE INCDEC          {$$ = code("Od1%so", code($2));}
244        | ID ary INCDEC         {$$ = code("%sds.;%sd1%sl.:%s", $2, ary($1), code($3), ary($1));}
245        ;
246
247assign  : ID '=' expr           {$$ = code("%sds%s", $3, var($1));}
248        | SCALE '=' expr        {$$ = code("%sdk", $3);}
249        | IBASE '=' expr        {$$ = code("%sdi", $3);}
250        | OBASE '=' expr        {$$ = code("%sdo", $3);}
251        | ID ary '=' expr       {$$ = code("%sd%s:%s", $4, $2, ary($1));}
252        | ID EQOP expr          {$$ = code("%sl%s%sds%s", $3, var($1), code($2), var($1));}
253        | SCALE EQOP expr       {$$ = code("%sK%sdk", $3, code($2));}
254        | IBASE EQOP expr       {$$ = code("%sI%sdi", $3, code($2));}
255        | OBASE EQOP expr       {$$ = code("%sO%sdo", $3, code($2));}
256        | ID ary EQOP expr      {$$ = code("%s%sds.;%s%sdl.:s", $4, $2, ary($1), code($3), ary($1));}
257        ;
258
259ary     : '[' expr ']'          {$$ = $2;}
260        ;
261
262%%
263static int
264yyerror(char *s)
265{
266	fprintf(stderr, "bc: %s:%d: %s\n", filename, lineno, s);
267	nerr++;
268	longjmp(recover, 1);
269}
270
271static void
272writeout(char *s)
273{
274	if (write(1, s, strlen(s)) < 0)
275		goto err;
276	if (write(1, "\n", 1) < 0)
277		goto err;
278	free(s);
279	return;
280	
281err:
282	eprintf("writing to dc:");
283}
284
285static char *
286code(char *fmt, ...)
287{
288	char *s, *t;
289	va_list ap;
290	int c, len, room;
291
292	va_start(ap, fmt);
293	room = BUFSIZ;
294	for (s = buff; *fmt; s += len) {
295		len = 1;
296		if ((c = *fmt++) != '%')
297			goto append;
298
299		switch (*fmt++) {
300		case 'd':
301			c = va_arg(ap, int);
302			len = snprintf(s, room, "%d", c);
303			if (len < 0 || len >= room)
304				goto err;
305			break;
306		case 'c':
307			c = va_arg(ap, int);
308			goto append;
309		case 's':
310			t = va_arg(ap, void *);
311			len = strlen(t);
312			if (len >= room)
313				goto err;
314			memcpy(s, t, len);
315			free(t);
316			break;
317		case '%':
318		append:
319			if (room <= 1)
320				goto err;
321			*s = c;
322			break;
323		default:
324			abort();
325		}
326
327		room -= len;
328	}
329	va_end(ap);
330
331	*s = '\0';
332	return estrdup(buff);
333
334err:
335	eprintf("unable to code requested operation\n");
336	return NULL;
337}
338
339static Macro *
340macro(int op)
341{
342	int preop;
343	Macro *d, *p;
344
345	if (nested == NESTED_MAX)
346		yyerror("too much nesting");
347
348	d = &macros[nested];
349	d->op = op;
350	d->nested = nested++;
351	d->name = NULL;
352
353	switch (op) {
354	case HOME:
355		d->id = 0;
356		d->flowid = flowid;
357		inhome = 1;
358		break;
359	case DEF:
360		unwind = estrdup("");
361		inhome = 0;
362		d->id = funid(yytext);
363		d->name = estrdup(yytext);
364		d->flowid = macros[0].flowid;
365		break;
366	default:
367		assert(nested > 1);
368		preop = d[-1].op;
369		d->flowid = d[-1].flowid;
370		if (preop != HOME && preop != DEF) {
371			if (d->flowid == 255)
372				eprintf("too many control flow structures");
373			d->flowid++;
374		}
375		d->id = d->flowid;
376		if (!inhome) {
377			/* populate reserved id */
378			flowid = d->flowid;
379			for (p = d; p != macros; --p)
380				p[-1].flowid++;
381		}
382		break;
383	}
384
385	return d;
386}
387
388static char *
389decl(int type, char *list, char *id)
390{
391	char *i1, *i2;
392
393	i1 = estrdup(id);
394	i2 = estrdup(id);
395	free(id);
396
397	if (!list)
398		list = estrdup("");
399
400	unwind = code("%sL%ss.", unwind, i1);
401
402	return code((type == AUTO) ? "0S%s%s" : "S%s%s", i2, list);
403}
404
405static char *
406param(char *list, char *id)
407{
408	return decl(PARAM, list, id);
409}
410
411static char *
412local(char *list, char *id)
413{
414	return decl(AUTO, list, id);
415}
416
417static char *
418funcode(Macro *d, char *params, char *vars, char *body)
419{
420	char *s;
421
422	if (strlen(d->name) > 1) {
423		s = code("[%s%s%s%s]s\"()%s\"",
424			 vars, params,
425			 body,
426			 retcode(code(" 0")),
427			 d->name);
428	} else {
429		s = code(sflag ? "[%s%s%s%s]s<%d>" : "[%s%s%s%s]s%c",
430			 vars, params,
431			 body,
432			 retcode(code(" 0")),
433			 d->id);
434		free(d->name);
435	}
436
437	free(unwind);
438	unwind = NULL;
439	nested--;
440	inhome = 0;
441
442	return s;
443}
444
445static char *
446brkcode(void)
447{
448	Macro *d;
449
450	for (d = &macros[nested-1]; d->op != HOME && d->op != LOOP; --d)
451		;
452	if (d->op == HOME)
453		yyerror("break not in for or while");
454	return code(" %dQ", nested  - d->nested);
455}
456
457static char *
458forcode(Macro *d, char *init, char *cmp, char *inc, char *body)
459{
460	char *s;
461
462	s = code(sflag ? "[%s%ss.%s<%d>]s<%d>" : "[%s%ss.%s%c]s%c",
463	         body,
464	         inc,
465	         estrdup(cmp),
466	         d->id, d->id);
467	writeout(s);
468
469	s = code(sflag ? "%ss.%s<%d> " : "%ss.%s%c ",
470	         init,
471	         cmp,
472	         d->id);
473	nested--;
474
475	return s;
476}
477
478static char *
479whilecode(Macro *d, char *cmp, char *body)
480{
481	char *s;
482
483	s = code(sflag ? "[%s%s<%d>]s<%d>" : "[%s%s%c]s%c",
484	         body,
485	         estrdup(cmp),
486	         d->id, d->id);
487	writeout(s);
488
489	s = code(sflag ? "%s<%d> " : "%s%c ",
490	         cmp, d->id);
491	nested--;
492
493	return s;
494}
495
496static char *
497ifcode(Macro *d, char *cmp, char *body)
498{
499	char *s;
500
501	s = code(sflag ? "[%s]s<%d>" : "[%s]s%c",
502	         body, d->id);
503	writeout(s);
504
505	s = code(sflag ? "%s<%d> " : "%s%c ",
506	         cmp, d->id);
507	nested--;
508
509	return s;
510}
511
512static char *
513retcode(char *expr)
514{
515	if (nested < 2 || macros[1].op != DEF)
516		yyerror("return must be in a function");
517	return code("%s %s %dQ", expr, estrdup(unwind), nested - 1);
518}
519
520static char *
521ary(char *s)
522{
523	if (strlen(s) == 1)
524		return code("%c", toupper(s[0]));
525	return code("\"[]%s\"", estrdup(s));
526}
527
528static char *
529ftn(char *s)
530{
531	if (strlen(s) == 1)
532		return code(sflag ? "<%d>" : "%c", funid(s));
533	return code("\"()%s\"", estrdup(s));
534}
535
536static char *
537var(char *s)
538{
539	if (strlen(s) == 1)
540		return code(s);
541	return code("\"%s\"", estrdup(s));
542}
543
544static void
545quit(void)
546{
547	exit(nerr > 0 ? 1 : 0);
548}
549
550static void
551skipspaces(void)
552{
553	int ch;
554
555	while (isascii(ch = getc(filep)) && isspace(ch)) {
556		if (ch == '\n') {
557			lineno++;
558			break;
559		}
560	}
561	ungetc(ch, filep);
562}
563
564static int
565iden(int ch)
566{
567	static struct keyword {
568		char *str;
569		int token;
570	} keywords[] = {
571		{"define", DEF},
572		{"break", BREAK},
573		{"quit", QUIT},
574		{"length", LENGTH},
575		{"return", RETURN},
576		{"for", FOR},
577		{"if", IF},
578		{"while", WHILE},
579		{"sqrt", SQRT},
580		{"scale", SCALE},
581		{"ibase", IBASE},
582		{"obase", OBASE},
583		{"auto", AUTO},
584		{"print", PRINT},
585		{NULL}
586	};
587	struct keyword *p;
588	char *bp;
589
590	ungetc(ch, filep);
591	for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) {
592		ch = getc(filep);
593		if (!isascii(ch) || !islower(ch))
594			break;
595		*bp = ch;
596	}
597
598	if (bp == &yytext[BUFSIZ])
599		yyerror("too long token");
600	*bp = '\0';
601	ungetc(ch, filep);
602
603	if (strlen(yytext) == 1) {
604		strcpy(yylval.id, yytext);
605		return ID;
606	}
607
608	for (p = keywords; p->str && strcmp(p->str, yytext); ++p)
609		;
610	if (p->str)
611		return p->token;
612
613	if (!sflag)
614		yyerror("invalid keyword");
615	strcpy(yylval.id, yytext);
616	return ID;
617}
618
619static char *
620digits(char *bp)
621{
622	int ch;
623	char *digits = DIGITS, *p;
624
625	while (bp < &yytext[BUFSIZ]) {
626		ch = getc(filep);
627		p = strchr(digits, ch);
628		if (!p)
629			break;
630		*bp++ = ch;
631	}
632
633	if (bp == &yytext[BUFSIZ])
634		return NULL;
635	ungetc(ch, filep);
636
637	return bp;
638}
639
640static int
641number(int ch)
642{
643	char *bp;
644
645	ungetc(ch, filep);
646	if ((bp = digits(yytext)) == NULL)
647		goto toolong;
648
649	if ((ch = getc(filep)) != '.') {
650		ungetc(ch, filep);
651		goto end;
652	}
653	*bp++ = '.';
654
655	if ((bp = digits(bp)) == NULL)
656		goto toolong;
657
658end:
659	if (bp ==  &yytext[BUFSIZ])
660		goto toolong;
661	*bp = '\0';
662	yylval.str = yytext;
663
664	return NUMBER;
665
666toolong:
667	yyerror("too long number");
668	return 0;
669}
670
671static int
672string(int ch)
673{
674	char *bp;
675
676	for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) {
677		if ((ch = getc(filep)) == '"')
678			break;
679		*bp = ch;
680	}
681
682	if (bp == &yytext[BUFSIZ])
683		yyerror("too long string");
684	*bp = '\0';
685	yylval.str = estrdup(yytext);
686
687	return STRING;
688}
689
690static int
691follow(int next, int yes, int no)
692{
693	int ch;
694
695	ch = getc(filep);
696	if (ch == next)
697		return yes;
698	ungetc(ch, filep);
699	return no;
700}
701
702static int
703operand(int ch)
704{
705	int peekc;
706
707	switch (ch) {
708	case '\n':
709	case '{':
710	case '}':
711	case '[':
712	case ']':
713	case '(':
714	case ')':
715	case ',':
716	case ';':
717		return ch;
718	case '.':
719		peekc = ungetc(getc(filep), filep);
720		if (strchr(DIGITS, peekc))
721			return number(ch);
722		return DOT;
723	case '"':
724		return string(ch);
725	case '*':
726		yylval.str = "*";
727		return follow('=', EQOP, '*');
728	case '/':
729		yylval.str = "/";
730		return follow('=', EQOP, '/');
731	case '%':
732		yylval.str = "%";
733		return follow('=', EQOP, '%');
734	case '=':
735		return follow('=', EQ, '=');
736	case '+':
737	case '-':
738		yylval.str = (ch == '+') ? "+" : "-";
739		if (follow('=', EQOP, ch) != ch)
740			return EQOP;
741		return follow(ch, INCDEC, ch);
742	case '^':
743		yylval.str = "^";
744		return follow('=', EQOP, '^');
745	case '<':
746		return follow('=', LE, '<');
747	case '>':
748		return follow('=', GE, '>');
749	case '!':
750		if (getc(filep) == '=')
751			return NE;
752		/* fallthrough */
753	default:
754		yyerror("invalid operand");
755		return 0;
756	}
757}
758
759static void
760comment(void)
761{
762	int c;
763
764	for (;;) {
765		while ((c = getc(filep)) != '*') {
766			if (c == '\n')
767				lineno++;
768		}
769		if ((c = getc(filep)) == '/')
770			break;
771		ungetc(c, filep);
772	}
773}
774
775static int
776yylex(void)
777{
778	int peekc, ch;
779
780repeat:
781	skipspaces();
782
783	ch = getc(filep);
784	if (ch == EOF) {
785		return EOF;
786	} else if (!isascii(ch)) {
787		yyerror("invalid input character");
788	} else if (islower(ch)) {
789		return iden(ch);
790	} else if (strchr(DIGITS, ch)) {
791		return number(ch);
792	} else {
793		if (ch == '/') {
794			peekc = getc(filep);
795			if (peekc == '*') {
796				comment();
797				goto repeat;
798			}
799			ungetc(peekc, filep);
800		}
801		return operand(ch);
802	}
803
804	return 0;
805}
806
807static void
808spawn(void)
809{
810	int fds[2];
811	char *par = sflag ? "-i" : NULL;
812	char errmsg[] = "bc:error execing dc\n";
813
814	if (pipe(fds) < 0)
815		eprintf("creating pipe:");
816
817	switch (fork()) {
818	case -1:
819		eprintf("forking dc:");
820		/* fallthrough */
821	case 0:
822		close(1);
823		dup(fds[1]);
824		close(fds[0]);
825		close(fds[1]);
826		break;
827	default:
828		close(0);
829		dup(fds[0]);
830		close(fds[0]);
831		close(fds[1]);
832		execlp(dcprog, "dc", par, (char *) NULL);
833
834		/* it shouldn't happen */
835		write(3, errmsg, sizeof(errmsg)-1);
836		_Exit(2);
837	}
838}
839
840static void
841run(void)
842{
843	if (setjmp(recover)) {
844		if (ferror(filep))
845			eprintf("%s:", filename);
846		if (feof(filep))
847			return;
848	}
849	yyparse();
850}
851
852static void
853bc(char *fname)
854{
855	lineno = 1;
856	nested = 0;
857
858	macro(HOME);
859	if (!fname) {
860		filename = "<stdin>";
861		filep = stdin;
862	} else {
863		filename = fname;
864		if ((filep = fopen(fname, "r")) == NULL)
865			eprintf("%s:", fname);
866	}
867
868	run();
869	fclose(filep);
870}
871
872static void
873usage(void)
874{
875	eprintf("usage: %s [-p dc][-cdls]\n", argv0);
876}
877
878int
879main(int argc, char *argv[])
880{
881	ARGBEGIN {
882	case 'p':
883		dcprog = EARGF(usage());
884		break;
885	case 'c':
886		cflag = 1;
887		break;
888	case 'd':
889		dflag = 1;
890		yydebug = 3;
891		break;
892	case 'l':
893		lflag = 1;
894		break;
895	case 's':
896		sflag = 1;
897		break;
898	default:
899		usage();
900	} ARGEND
901
902	yytext = malloc(BUFSIZ);
903	buff = malloc(BUFSIZ);
904	if (!yytext || !buff)
905		eprintf("out of memory\n");
906	flowid = 128;
907
908	if (!cflag)
909		spawn();
910	if (lflag)
911		bc(PREFIX "/share/misc/bc.library");
912
913	while (*argv)
914		bc(*argv++);
915	bc(NULL);
916
917	quit();
918}