bc.y - sbase - suckless unix tools
HTML git clone git://git.suckless.org/sbase
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
bc.y (17788B)
---
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
22 int yydebug;
23
24 typedef struct macro Macro;
25
26 struct macro {
27 int op;
28 int id;
29 char *name;
30 int flowid;
31 int nested;
32 };
33
34 static int yyerror(char *);
35 static int yylex(void);
36
37 static void quit(void);
38 static char *code(char *, ...);
39 static char *forcode(Macro *, char *, char *, char *, char *);
40 static char *whilecode(Macro *, char *, char *);
41 static char *ifcode(Macro *, char *, char *);
42 static char *funcode(Macro *, char *, char *, char *);
43 static char *param(char *, char *), *local(char *, char *);
44 static Macro *define(char *, char *);
45 static char *retcode(char *);
46 static char *brkcode(void);
47 static Macro *macro(int);
48
49 static char *ftn(char *);
50 static char *var(char *);
51 static char *ary(char *);
52 static void writeout(char *);
53
54 static char *yytext, *buff, *unwind;
55 static char *filename;
56 static FILE *filep;
57 static int lineno, nerr, flowid;
58 static jmp_buf recover;
59 static int nested, inhome;
60 static Macro macros[NESTED_MAX];
61 int cflag, dflag, lflag, sflag;
62
63 static 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
112 program :
113 | item program
114 ;
115
116 item : scolonlst '\n' {writeout($1);}
117 | function {writeout($1);}
118 ;
119
120 function : def parlst '{' '\n' autolst statlst '}' {$$ = funcode($1, $2, $5, $6);}
121 ;
122
123 scolonlst: {$$ = code("");}
124 | stat
125 | scolonlst ';' stat {$$ = code("%s%s", $1, $3);}
126 | scolonlst ';'
127 ;
128
129 statlst : {$$ = 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
137 stat : 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
153 while : WHILE {$$ = macro(LOOP);}
154 ;
155
156 if : IF {$$ = macro(IF);}
157 ;
158
159 for : FOR {$$ = macro(LOOP);}
160 ;
161
162 def : DEF ID {$$ = macro(DEF);}
163 ;
164
165 parlst : '(' ')' {$$ = code("");}
166 | '(' params ')' {$$ = $2;}
167 ;
168
169 params : param {$$ = param(NULL, $1);}
170 | params ',' param {$$ = param($1, $3);}
171 ;
172
173 param : ID {$$ = var($1);}
174 | ID '[' ']' {$$ = ary($1);}
175 ;
176
177 autolst : {$$ = code("");}
178 | AUTO locals '\n' {$$ = $2;}
179 | AUTO locals ';' {$$ = $2;}
180 ;
181
182 locals : local {$$ = local(NULL, $1);}
183 | locals ',' local {$$ = local($1, $3);}
184 ;
185
186 local : ID {$$ = var($1);}
187 | ID '[' ']' {$$ = ary($1);}
188 ;
189
190 arglst : 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
196 cond : '(' rel ')' {$$ = $2;}
197 ;
198
199 rel : 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
208 exprstat: nexpr {$$ = code("%s%ss.", $1, code(sflag ? "" : "p"));}
209 | assign {$$ = code("%ss.", $1);}
210 ;
211
212 expr : nexpr
213 | assign
214 ;
215
216 nexpr : 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
248 assign : 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
260 ary : '[' expr ']' {$$ = $2;}
261 ;
262
263 %%
264 static int
265 yyerror(char *s)
266 {
267 fprintf(stderr, "bc: %s:%d: %s\n", filename, lineno, s);
268 nerr++;
269 longjmp(recover, 1);
270 }
271
272 static void
273 writeout(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
282 err:
283 eprintf("writing to dc:");
284 }
285
286 static char *
287 code(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
335 err:
336 eprintf("unable to code requested operation\n");
337 return NULL;
338 }
339
340 static Macro *
341 macro(int op)
342 {
343 int preop;
344 Macro *d, *p;
345
346 if (nested == NESTED_MAX)
347 yyerror("too much nesting");
348
349 d = ¯os[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
389 static char *
390 decl(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
406 static char *
407 param(char *list, char *id)
408 {
409 return decl(PARAM, list, id);
410 }
411
412 static char *
413 local(char *list, char *id)
414 {
415 return decl(AUTO, list, id);
416 }
417
418 static char *
419 funcode(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
446 static char *
447 brkcode(void)
448 {
449 Macro *d;
450
451 for (d = ¯os[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
458 static char *
459 forcode(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
479 static char *
480 whilecode(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
497 static char *
498 ifcode(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
513 static char *
514 retcode(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
523 static char *
524 ary(char *s)
525 {
526 if (strlen(s) == 1)
527 return code("%c", toupper(s[0]));
528 return code("\"[]%s\"", estrdup(s));
529 }
530
531 static char *
532 ftn(char *s)
533 {
534 if (strlen(s) == 1)
535 return code(sflag ? "<%d>" : "%c", funid(s));
536 return code("\"()%s\"", estrdup(s));
537 }
538
539 static char *
540 var(char *s)
541 {
542 if (strlen(s) == 1)
543 return code(s);
544 return code("\"%s\"", estrdup(s));
545 }
546
547 static void
548 quit(void)
549 {
550 exit(nerr > 0 ? 1 : 0);
551 }
552
553 static void
554 skipspaces(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
567 static int
568 iden(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
622 static char *
623 digits(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
643 static int
644 number(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
662 end:
663 if (bp == &yytext[BUFSIZ])
664 goto toolong;
665 *bp = '\0';
666 yylval.str = yytext;
667
668 return NUMBER;
669
670 toolong:
671 yyerror("too long number");
672 return 0;
673 }
674
675 static int
676 string(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
694 static int
695 follow(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
706 static int
707 operand(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
762 static void
763 comment(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
778 static int
779 yylex(void)
780 {
781 int peekc, ch;
782
783 repeat:
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
810 static void
811 spawn(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
842 static void
843 run(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
854 static void
855 bc(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
876 static void
877 usage(void)
878 {
879 eprintf("usage: %s [-p dc][-cdls]\n", argv0);
880 }
881
882 int
883 main(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 }