sbase

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

bc.y (17679B)


      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 10
     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
     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=", $3, $1);}
    198         | expr LE expr          {$$ = code("%s%s!>", $3, $1);}
    199         | expr GE expr          {$$ = code("%s%s!<", $3, $1);}
    200         | expr NE expr          {$$ = code("%s%s!=", $3, $1);}
    201         | expr '<' expr         {$$ = code("%s%s<", $3, $1);}
    202         | expr '>' expr         {$$ = code("%s%s>", $3, $1);}
    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 }
    335 
    336 static Macro *
    337 macro(int op)
    338 {
    339 	int preop;
    340 	Macro *d, *p;
    341 
    342 	if (nested == NESTED_MAX)
    343 		yyerror("too much nesting");
    344 
    345 	d = &macros[nested];
    346 	d->op = op;
    347 	d->nested = nested++;
    348 
    349 	switch (op) {
    350 	case HOME:
    351 		d->id = 0;
    352 		d->flowid = flowid;
    353 		inhome = 1;
    354 		break;
    355 	case DEF:
    356 		inhome = 0;
    357 		d->id = funid(yytext);
    358 		d->flowid = macros[0].flowid;
    359 		break;
    360 	default:
    361 		assert(nested > 1);
    362 		preop = d[-1].op;
    363 		d->flowid = d[-1].flowid;
    364 		if (preop != HOME && preop != DEF) {
    365 			if (d->flowid == 255)
    366 				eprintf("too many control flow structures");
    367 			d->flowid++;
    368 		}
    369 		d->id = d->flowid;
    370 		if (!inhome) {
    371 			/* populate reserved id */
    372 			flowid = d->flowid;
    373 			for (p = d; p != macros; --p)
    374 				p[-1].flowid++;
    375 		}
    376 		break;
    377 	}
    378 
    379 	return d;
    380 }
    381 
    382 static char *
    383 param(char *list, char *id)
    384 {
    385 	char *i1, *i2;
    386 
    387 	i1 = estrdup(id);
    388 	i2 = estrdup(id);
    389 	free(id);
    390 
    391 	unwind = code(unwind ? "L%ss.%s" : "L%ss.", i1, unwind);
    392 
    393 	return code(list ? "S%s%s" : "S%s" , i2, list);
    394 }
    395 
    396 static char *
    397 local(char *list, char *id)
    398 {
    399 	char *i1, *i2;
    400 
    401 	i1 = estrdup(id);
    402 	i2 = estrdup(id);
    403 	free(id);
    404 
    405 	unwind = code(unwind ? "L%ss.%s" : "L%ss.", i1, unwind);
    406 
    407 	return code(list ? "0S%s%s" : "0S%s" , i2, list);
    408 }
    409 
    410 static char *
    411 funcode(Macro *d, char *params, char *vars, char *body)
    412 {
    413 	char *s;
    414 
    415 	s = code("[%s%s%s%s]s%c",
    416 	         vars, params,
    417 	         body,
    418 	         retcode(code(" 0")),
    419 	         d->id);
    420 	free(unwind);
    421 	unwind = NULL;
    422 	nested--;
    423 	inhome = 0;
    424 
    425 
    426 	return s;
    427 }
    428 
    429 static char *
    430 brkcode(void)
    431 {
    432 	Macro *d;
    433 
    434 	for (d = &macros[nested-1]; d->op != HOME && d->op != LOOP; --d)
    435 		;
    436 	if (d->op == HOME)
    437 		yyerror("break not in for or while");
    438 	return code(" %dQ", nested  - d->nested);
    439 }
    440 
    441 static char *
    442 forcode(Macro *d, char *init, char *cmp, char *inc, char *body)
    443 {
    444 	char *s;
    445 
    446 	s = code("[%s%ss.%s%c]s%c",
    447 	         body,
    448 	         inc,
    449 	         estrdup(cmp),
    450 	         d->id, d->id);
    451 	writeout(s);
    452 
    453 	s = code("%ss.%s%c", init, cmp, d->id);
    454 	nested--;
    455 
    456 	return s;
    457 }
    458 
    459 static char *
    460 whilecode(Macro *d, char *cmp, char *body)
    461 {
    462 	char *s;
    463 
    464 	s = code("[%ss.%s%c]s%c",
    465 	         body,
    466 	         estrdup(cmp),
    467 	         d->id, d->id);
    468 	writeout(s);
    469 
    470 	s = code("%s%c", cmp, d->id);
    471 	nested--;
    472 
    473 	return s;
    474 }
    475 
    476 static char *
    477 ifcode(Macro *d, char *cmp, char *body)
    478 {
    479 	char *s;
    480 
    481 	s = code("[%s]s%c", body, d->id);
    482 	writeout(s);
    483 
    484 	s = code("%s%c", cmp, d->id);
    485 	nested--;
    486 
    487 	return s;
    488 }
    489 
    490 static char *
    491 retcode(char *expr)
    492 {
    493 	char *s;
    494 
    495 	if (nested < 2 || macros[1].op != DEF)
    496 		yyerror("return must be in a function");
    497 	return code("%s %s %dQ", expr, estrdup(unwind), nested - 1);
    498 }
    499 
    500 static char *
    501 ary(char *s)
    502 {
    503 	return code("%c", toupper(s[0]));
    504 }
    505 
    506 static char *
    507 ftn(char *s)
    508 {
    509 	return code("%c", funid(s));
    510 }
    511 
    512 static char *
    513 var(char *s)
    514 {
    515 	return code(s);
    516 }
    517 
    518 static void
    519 quit(void)
    520 {
    521 	exit(nerr > 0 ? 1 : 0);
    522 }
    523 
    524 static void
    525 skipspaces(void)
    526 {
    527 	int ch;
    528 
    529 	while (isspace(ch = getc(filep))) {
    530 		if (ch == '\n') {
    531 			lineno++;
    532 			break;
    533 		}
    534 	}
    535 	ungetc(ch, filep);
    536 }
    537 
    538 static int
    539 iden(int ch)
    540 {
    541 	static struct keyword {
    542 		char *str;
    543 		int token;
    544 	} keywords[] = {
    545 		{"define", DEF},
    546 		{"break", BREAK},
    547 		{"quit", QUIT},
    548 		{"length", LENGTH},
    549 		{"return", RETURN},
    550 		{"for", FOR},
    551 		{"if", IF},
    552 		{"while", WHILE},
    553 		{"sqrt", SQRT},
    554 		{"scale", SCALE},
    555 		{"ibase", IBASE},
    556 		{"obase", OBASE},
    557 		{"auto", AUTO},
    558 		{"print", PRINT},
    559 		{NULL}
    560 	};
    561 	struct keyword *p;
    562 	char *bp;
    563 
    564 	ungetc(ch, filep);
    565 	for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) {
    566 		ch = getc(filep);
    567 		if (!islower(ch))
    568 			break;
    569 		*bp = ch;
    570 	}
    571 
    572 	if (bp == &yytext[BUFSIZ])
    573 		yyerror("too long token");
    574 	*bp = '\0';
    575 	ungetc(ch, filep);
    576 
    577 	if (strlen(yytext) == 1) {
    578 		strcpy(yylval.id, yytext);
    579 		return ID;
    580 	}
    581 
    582 	for (p = keywords; p->str && strcmp(p->str, yytext); ++p)
    583 		;
    584 
    585 	if (!p->str)
    586 		yyerror("invalid keyword");
    587 
    588 	return p->token;
    589 }
    590 
    591 static char *
    592 digits(char *bp)
    593 {
    594 	int ch;
    595 	char *digits = DIGITS, *p;
    596 
    597 	while (bp < &yytext[BUFSIZ]) {
    598 		ch = getc(filep);
    599 		p = strchr(digits, ch);
    600 		if (!p)
    601 			break;
    602 		*bp++ = ch;
    603 	}
    604 
    605 	if (bp == &yytext[BUFSIZ])
    606 		return NULL;
    607 	ungetc(ch, filep);
    608 
    609 	return bp;
    610 }
    611 
    612 static int
    613 number(int ch)
    614 {
    615 	int d;
    616 	char *bp;
    617 
    618 	ungetc(ch, filep);
    619 	if ((bp = digits(yytext)) == NULL)
    620 		goto toolong;
    621 
    622 	if ((ch = getc(filep)) != '.') {
    623 		ungetc(ch, filep);
    624 		goto end;
    625 	}
    626 	*bp++ = '.';
    627 
    628 	if ((bp = digits(bp)) == NULL)
    629 		goto toolong;
    630 
    631 end:
    632 	if (bp ==  &yytext[BUFSIZ])
    633 		goto toolong;
    634 	*bp = '\0';
    635 	yylval.str = yytext;
    636 
    637 	return NUMBER;
    638 
    639 toolong:
    640 	yyerror("too long number");
    641 }
    642 
    643 static int
    644 string(int ch)
    645 {
    646 	char *bp;
    647 
    648 	for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) {
    649 		if ((ch = getc(filep)) == '"')
    650 			break;
    651 		*bp = ch;
    652 	}
    653 
    654 	if (bp == &yytext[BUFSIZ])
    655 		yyerror("too long string");
    656 	*bp = '\0';
    657 	yylval.str = estrdup(yytext);
    658 
    659 	return STRING;
    660 }
    661 
    662 static int
    663 follow(int next, int yes, int no)
    664 {
    665 	int ch;
    666 
    667 	ch = getc(filep);
    668 	if (ch == next)
    669 		return yes;
    670 	ungetc(ch, filep);
    671 	return no;
    672 }
    673 
    674 static int
    675 operand(int ch)
    676 {
    677 	int peekc;
    678 
    679 	switch (ch) {
    680 	case '\n':
    681 	case '{':
    682 	case '}':
    683 	case '[':
    684 	case ']':
    685 	case '(':
    686 	case ')':
    687 	case ',':
    688 	case ';':
    689 		return ch;
    690 	case '.':
    691 		peekc = ungetc(getc(filep), filep);
    692 		if (strchr(DIGITS, peekc))
    693 			return number(ch);
    694 		return DOT;
    695 	case '"':
    696 		return string(ch);
    697 	case '*':
    698 		yylval.str = "*";
    699 		return follow('=', EQOP, '*');
    700 	case '/':
    701 		yylval.str = "/";
    702 		return follow('=', EQOP, '/');
    703 	case '%':
    704 		yylval.str = "%";
    705 		return follow('=', EQOP, '%');
    706 	case '=':
    707 		return follow('=', EQ, '=');
    708 	case '+':
    709 	case '-':
    710 		yylval.str = (ch == '+') ? "+" : "-";
    711 		if (follow('=', EQOP, ch) != ch)
    712 			return EQOP;
    713 		return follow(ch, INCDEC, ch);
    714 	case '^':
    715 		yylval.str = "^";
    716 		return follow('=', EQOP, '^');
    717 	case '<':
    718 		return follow('=', LE, '<');
    719 	case '>':
    720 		return follow('=', GE, '>');
    721 	case '!':
    722 		if (getc(filep) == '=')
    723 			return NE;
    724 	default:
    725 		yyerror("invalid operand");
    726 	}
    727 }
    728 
    729 static void
    730 comment(void)
    731 {
    732 	int c;
    733 
    734 	for (;;) {
    735 		while ((c = getc(filep)) != '*') {
    736 			if (c == '\n')
    737 				lineno++;
    738 		}
    739 		if ((c = getc(filep)) == '/')
    740 			break;
    741 		ungetc(c, filep);
    742 	}
    743 }
    744 
    745 static int
    746 yylex(void)
    747 {
    748 	int peekc, ch;
    749 
    750 repeat:
    751 	skipspaces();
    752 
    753 	ch = getc(filep);
    754 	if (ch == EOF) {
    755 		return EOF;
    756 	} else if (!isascii(ch)) {
    757 		yyerror("invalid input character");
    758 	} else if (islower(ch)) {
    759 		return iden(ch);
    760 	} else if (strchr(DIGITS, ch)) {
    761 		return number(ch);
    762 	} else {
    763 		if (ch == '/') {
    764 			peekc = getc(filep);
    765 			if (peekc == '*') {
    766 				comment();
    767 				goto repeat;
    768 			}
    769 			ungetc(peekc, filep);
    770 		}
    771 		return operand(ch);
    772 	}
    773 }
    774 
    775 static void
    776 spawn(void)
    777 {
    778 	int fds[2];
    779 	char errmsg[] = "bc:error execing dc\n";
    780 
    781 	if (pipe(fds) < 0)
    782 		eprintf("creating pipe:");
    783 
    784 	switch (fork()) {
    785 	case -1:
    786 		eprintf("forking dc:");
    787 	case 0:
    788 		close(1);
    789 		dup(fds[1]);
    790 		close(fds[0]);
    791 		close(fds[1]);
    792 		break;
    793 	default:
    794 		close(0);
    795 		dup(fds[0]);
    796 		close(fds[0]);
    797 		close(fds[1]);
    798 		execlp("dc", "dc", (char *) NULL);
    799 
    800 		/* it shouldn't happen */
    801 		write(3, errmsg, sizeof(errmsg)-1);
    802 		_Exit(2);
    803 	}
    804 }
    805 
    806 static void
    807 run(void)
    808 {
    809 	if (setjmp(recover)) {
    810 		if (ferror(filep))
    811 			eprintf("%s:", filename);
    812 		if (feof(filep))
    813 			return;
    814 	}
    815 	yyparse();
    816 }
    817 
    818 static void
    819 bc(char *fname)
    820 {
    821 	Macro *d;
    822 
    823 	lineno = 1;
    824 	nested = 0;
    825 
    826 	macro(HOME);
    827 	if (!fname) {
    828 		filename = "<stdin>";
    829 		filep = stdin;
    830 	} else {
    831 		filename = fname;
    832 		if ((filep = fopen(fname, "r")) == NULL)
    833 			eprintf("%s:", fname);
    834 	}
    835 
    836 	run();
    837 	fclose(filep);
    838 }
    839 
    840 static void
    841 loadlib(void)
    842 {
    843 	int r;
    844 	size_t len;
    845 	char bin[FILENAME_MAX], fname[FILENAME_MAX];
    846 	static char bclib[] = "bc.library";
    847 
    848 	/*
    849 	 * try first to load the library from the same directory than
    850 	 * the executable, because that can makes easier the tests
    851 	 * because it does not require to install to test the last version
    852 	 */
    853 	len = strlen(argv0);
    854 	if (len >= FILENAME_MAX)
    855 		goto share;
    856 	memcpy(bin, argv0, len + 1);
    857 
    858 	r = snprintf(fname, sizeof(fname), "%s/%s", dirname(bin), bclib);
    859 	if (r < 0 || r >= sizeof(fname))
    860 		goto share;
    861 
    862 	if (access(fname, F_OK) < 0)
    863 		goto share;
    864 
    865 	bc(fname);
    866 	return;
    867 
    868 share:
    869 	r = snprintf(fname, sizeof(fname), "%s/share/misc/%s", PREFIX, bclib);
    870 	if (r < 0 || r >= sizeof(fname))
    871 		eprintf("invalid path name for bc.library\n");
    872 	bc(fname);
    873 }
    874 
    875 static void
    876 usage(void)
    877 {
    878 	eprintf("usage: %s [-cdls]\n", argv0);
    879 }
    880 
    881 int
    882 main(int argc, char *argv[])
    883 {
    884 	ARGBEGIN {
    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 		loadlib();
    912 
    913 	while (*argv)
    914 		bc(*argv++);
    915 	bc(NULL);
    916 
    917 	quit();
    918 }