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