sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

bc.y (16980B)


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