bc.y (17798B)
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, (char[]){'\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 }