Fix warnings from byacc import.
[dragonfly.git] / usr.bin / bc / bc.y
CommitLineData
f2d37758
MD
1%{
2/*
db555d9a 3 * $OpenBSD: bc.y,v 1.32 2006/05/18 05:49:53 otto Exp $
f2d37758
MD
4 */
5
6/*
7 * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This implementation of bc(1) uses concepts from the original 4.4
24 * BSD bc(1). The code itself is a complete rewrite, based on the
25 * Posix defined bc(1) grammar. Other differences include type safe
26 * usage of pointers to build the tree of emitted code, typed yacc
27 * rule values, dynamic allocation of all data structures and a
28 * completely rewritten lexical analyzer using lex(1).
29 *
30 * Some effort has been made to make sure that the generated code is
31 * the same as the code generated by the older version, to provide
32 * easy regression testing.
33 */
34
db555d9a
PA
35#include <sys/types.h>
36#include <sys/wait.h>
37
f2d37758
MD
38#include <ctype.h>
39#include <err.h>
db555d9a 40#include <errno.h>
ac3cc18c 41#include <histedit.h>
f2d37758
MD
42#include <limits.h>
43#include <search.h>
44#include <signal.h>
45#include <stdarg.h>
46#include <stdbool.h>
52e9aa73 47#include <stdlib.h>
f2d37758
MD
48#include <string.h>
49#include <unistd.h>
50
51#include "extern.h"
52#include "pathnames.h"
53
54#define END_NODE ((ssize_t) -1)
55#define CONST_STRING ((ssize_t) -2)
56#define ALLOC_STRING ((ssize_t) -3)
57
58struct tree {
59 ssize_t index;
60 union {
61 char *astr;
62 const char *cstr;
63 } u;
64};
65
66int yyparse(void);
67int yywrap(void);
68
b6d9cda5
SW
69int fileindex;
70int sargc;
71char **sargv;
72char *filename;
73char *cmdexpr;
74
f2d37758
MD
75static void grow(void);
76static ssize_t cs(const char *);
77static ssize_t as(const char *);
78static ssize_t node(ssize_t, ...);
79static void emit(ssize_t);
80static void emit_macro(int, ssize_t);
81static void free_tree(void);
82static ssize_t numnode(int);
83static ssize_t lookup(char *, size_t, char);
84static ssize_t letter_node(char *);
85static ssize_t array_node(char *);
86static ssize_t function_node(char *);
87
88static void add_par(ssize_t);
89static void add_local(ssize_t);
90static void warning(const char *);
91static void init(void);
b6d9cda5 92static __dead2 void usage(void);
f2d37758
MD
93static char *escape(const char *);
94
db555d9a 95static ssize_t instr_sz = 0;
f2d37758
MD
96static struct tree *instructions = NULL;
97static ssize_t current = 0;
98static int macro_char = '0';
99static int reset_macro_char = '0';
100static int nesting = 0;
101static int breakstack[16];
102static int breaksp = 0;
103static ssize_t prologue;
104static ssize_t epilogue;
105static bool st_has_continue;
106static char str_table[UCHAR_MAX][2];
f2d37758
MD
107static bool do_fork = true;
108static u_short var_count;
db555d9a 109static pid_t dc;
f2d37758
MD
110
111extern char *__progname;
112
113#define BREAKSTACK_SZ (sizeof(breakstack)/sizeof(breakstack[0]))
114
115/* These values are 4.4BSD bc compatible */
116#define FUNC_CHAR 0x01
117#define ARRAY_CHAR 0xa1
118
119/* Skip '\0', [, \ and ] */
120#define ENCODE(c) ((c) < '[' ? (c) : (c) + 3);
121#define VAR_BASE (256-4)
122#define MAX_VARIABLES (VAR_BASE * VAR_BASE)
123
124%}
125
126%start program
127
128%union {
129 ssize_t node;
130 struct lvalue lvalue;
131 const char *str;
132 char *astr;
133}
134
135%token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT
136%token NEWLINE
137%token <astr> LETTER
138%token <str> NUMBER STRING
139%token DEFINE BREAK QUIT LENGTH
140%token RETURN FOR IF WHILE SQRT
141%token SCALE IBASE OBASE AUTO
142%token CONTINUE ELSE PRINT
143
144%left BOOL_OR
145%left BOOL_AND
146%nonassoc BOOL_NOT
147%nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER
148%right <str> ASSIGN_OP
149%left PLUS MINUS
150%left MULTIPLY DIVIDE REMAINDER
151%right EXPONENT
152%nonassoc UMINUS
153%nonassoc INCR DECR
154
155%type <lvalue> named_expression
156%type <node> argument_list
157%type <node> alloc_macro
158%type <node> expression
159%type <node> function
160%type <node> function_header
161%type <node> input_item
162%type <node> opt_argument_list
163%type <node> opt_expression
164%type <node> opt_relational_expression
165%type <node> opt_statement
166%type <node> print_expression
167%type <node> print_expression_list
168%type <node> relational_expression
169%type <node> return_expression
170%type <node> semicolon_list
171%type <node> statement
172%type <node> statement_list
173
174%%
175
176program : /* empty */
177 | program input_item
178 ;
179
180input_item : semicolon_list NEWLINE
181 {
182 emit($1);
183 macro_char = reset_macro_char;
184 putchar('\n');
185 free_tree();
186 st_has_continue = false;
187 }
188 | function
189 {
190 putchar('\n');
191 free_tree();
192 st_has_continue = false;
193 }
194 | error NEWLINE
195 {
196 yyerrok;
197 }
b6d9cda5
SW
198 | error QUIT
199 {
200 yyerrok;
201 }
f2d37758
MD
202 ;
203
204semicolon_list : /* empty */
205 {
206 $$ = cs("");
207 }
208 | statement
209 | semicolon_list SEMICOLON statement
210 {
211 $$ = node($1, $3, END_NODE);
212 }
213 | semicolon_list SEMICOLON
214 ;
215
216statement_list : /* empty */
217 {
218 $$ = cs("");
219 }
220 | statement
221 | statement_list NEWLINE
222 | statement_list NEWLINE statement
223 {
224 $$ = node($1, $3, END_NODE);
225 }
226 | statement_list SEMICOLON
227 | statement_list SEMICOLON statement
228 {
229 $$ = node($1, $3, END_NODE);
230 }
231 ;
232
233
234opt_statement : /* empty */
235 {
236 $$ = cs("");
237 }
238 | statement
239 ;
240
241statement : expression
242 {
243 $$ = node($1, cs("ps."), END_NODE);
244 }
245 | named_expression ASSIGN_OP expression
246 {
247 if ($2[0] == '\0')
248 $$ = node($3, cs($2), $1.store,
249 END_NODE);
250 else
251 $$ = node($1.load, $3, cs($2), $1.store,
252 END_NODE);
253 }
254 | STRING
255 {
256 $$ = node(cs("["), as($1),
257 cs("]P"), END_NODE);
258 }
259 | BREAK
260 {
261 if (breaksp == 0) {
262 warning("break not in for or while");
263 YYERROR;
264 } else {
265 $$ = node(
266 numnode(nesting -
267 breakstack[breaksp-1]),
268 cs("Q"), END_NODE);
269 }
270 }
271 | CONTINUE
272 {
273 if (breaksp == 0) {
274 warning("continue not in for or while");
275 YYERROR;
276 } else {
277 st_has_continue = true;
278 $$ = node(numnode(nesting -
279 breakstack[breaksp-1] - 1),
280 cs("J"), END_NODE);
281 }
282 }
283 | QUIT
284 {
db555d9a
PA
285 sigset_t mask;
286
f2d37758
MD
287 putchar('q');
288 fflush(stdout);
db555d9a
PA
289 if (dc) {
290 sigprocmask(SIG_BLOCK, NULL, &mask);
291 sigsuspend(&mask);
292 } else
293 exit(0);
f2d37758
MD
294 }
295 | RETURN return_expression
296 {
297 if (nesting == 0) {
298 warning("return must be in a function");
299 YYERROR;
300 }
301 $$ = $2;
302 }
303 | FOR LPAR alloc_macro opt_expression SEMICOLON
304 opt_relational_expression SEMICOLON
305 opt_expression RPAR opt_statement pop_nesting
306 {
307 ssize_t n;
308
309 if (st_has_continue)
310 n = node($10, cs("M"), $8, cs("s."),
311 $6, $3, END_NODE);
312 else
313 n = node($10, $8, cs("s."), $6, $3,
314 END_NODE);
315
316 emit_macro($3, n);
317 $$ = node($4, cs("s."), $6, $3, cs(" "),
318 END_NODE);
319 }
320 | IF LPAR alloc_macro pop_nesting relational_expression RPAR
321 opt_statement
322 {
323 emit_macro($3, $7);
324 $$ = node($5, $3, cs(" "), END_NODE);
325 }
326 | IF LPAR alloc_macro pop_nesting relational_expression RPAR
327 opt_statement ELSE alloc_macro pop_nesting opt_statement
328 {
329 emit_macro($3, $7);
330 emit_macro($9, $11);
331 $$ = node($5, $3, cs("e"), $9, cs(" "),
332 END_NODE);
333 }
334 | WHILE LPAR alloc_macro relational_expression RPAR
335 opt_statement pop_nesting
336 {
337 ssize_t n;
338
339 if (st_has_continue)
340 n = node($6, cs("M"), $4, $3, END_NODE);
341 else
342 n = node($6, $4, $3, END_NODE);
343 emit_macro($3, n);
344 $$ = node($4, $3, cs(" "), END_NODE);
345 }
346 | LBRACE statement_list RBRACE
347 {
348 $$ = $2;
349 }
350 | PRINT print_expression_list
351 {
352 $$ = $2;
353 }
354 ;
355
356alloc_macro : /* empty */
357 {
358 $$ = cs(str_table[macro_char]);
359 macro_char++;
360 /* Do not use [, \ and ] */
361 if (macro_char == '[')
362 macro_char += 3;
363 /* skip letters */
364 else if (macro_char == 'a')
365 macro_char = '{';
366 else if (macro_char == ARRAY_CHAR)
367 macro_char += 26;
368 else if (macro_char == 255)
369 fatal("program too big");
370 if (breaksp == BREAKSTACK_SZ)
371 fatal("nesting too deep");
372 breakstack[breaksp++] = nesting++;
373 }
374 ;
375
376pop_nesting : /* empty */
377 {
378 breaksp--;
379 }
380 ;
381
382function : function_header opt_parameter_list RPAR opt_newline
383 LBRACE NEWLINE opt_auto_define_list
384 statement_list RBRACE
385 {
386 int n = node(prologue, $8, epilogue,
387 cs("0"), numnode(nesting),
388 cs("Q"), END_NODE);
389 emit_macro($1, n);
390 reset_macro_char = macro_char;
391 nesting = 0;
392 breaksp = 0;
393 }
394 ;
395
396function_header : DEFINE LETTER LPAR
397 {
398 $$ = function_node($2);
399 free($2);
400 prologue = cs("");
401 epilogue = cs("");
402 nesting = 1;
403 breaksp = 0;
404 breakstack[breaksp] = 0;
405 }
406 ;
407
408opt_newline : /* empty */
409 | NEWLINE
410 ;
411
412opt_parameter_list
413 : /* empty */
414 | parameter_list
415 ;
416
417
418parameter_list : LETTER
419 {
420 add_par(letter_node($1));
421 free($1);
422 }
423 | LETTER LBRACKET RBRACKET
424 {
425 add_par(array_node($1));
426 free($1);
427 }
428 | parameter_list COMMA LETTER
429 {
430 add_par(letter_node($3));
431 free($3);
432 }
433 | parameter_list COMMA LETTER LBRACKET RBRACKET
434 {
435 add_par(array_node($3));
436 free($3);
437 }
438 ;
439
440
441
442opt_auto_define_list
443 : /* empty */
444 | AUTO define_list NEWLINE
445 | AUTO define_list SEMICOLON
446 ;
447
448
449define_list : LETTER
450 {
451 add_local(letter_node($1));
452 free($1);
453 }
454 | LETTER LBRACKET RBRACKET
455 {
456 add_local(array_node($1));
457 free($1);
458 }
459 | define_list COMMA LETTER
460 {
461 add_local(letter_node($3));
462 free($3);
463 }
464 | define_list COMMA LETTER LBRACKET RBRACKET
465 {
466 add_local(array_node($3));
467 free($3);
468 }
469 ;
470
471
472opt_argument_list
473 : /* empty */
474 {
475 $$ = cs("");
476 }
477 | argument_list
478 ;
479
480
481argument_list : expression
482 | argument_list COMMA expression
483 {
484 $$ = node($1, $3, END_NODE);
485 }
486 | argument_list COMMA LETTER LBRACKET RBRACKET
487 {
488 $$ = node($1, cs("l"), array_node($3),
489 END_NODE);
490 free($3);
491 }
492 ;
493
494opt_relational_expression
495 : /* empty */
496 {
497 $$ = cs(" 0 0=");
498 }
499 | relational_expression
500 ;
501
502relational_expression
503 : expression EQUALS expression
504 {
505 $$ = node($1, $3, cs("="), END_NODE);
506 }
507 | expression UNEQUALS expression
508 {
509 $$ = node($1, $3, cs("!="), END_NODE);
510 }
511 | expression LESS expression
512 {
513 $$ = node($1, $3, cs(">"), END_NODE);
514 }
515 | expression LESS_EQ expression
516 {
517 $$ = node($1, $3, cs("!<"), END_NODE);
518 }
519 | expression GREATER expression
520 {
521 $$ = node($1, $3, cs("<"), END_NODE);
522 }
523 | expression GREATER_EQ expression
524 {
525 $$ = node($1, $3, cs("!>"), END_NODE);
526 }
527 | expression
528 {
529 $$ = node($1, cs(" 0!="), END_NODE);
530 }
531 ;
532
533
534return_expression
535 : /* empty */
536 {
537 $$ = node(cs("0"), epilogue,
538 numnode(nesting), cs("Q"), END_NODE);
539 }
540 | expression
541 {
542 $$ = node($1, epilogue,
543 numnode(nesting), cs("Q"), END_NODE);
544 }
545 | LPAR RPAR
546 {
547 $$ = node(cs("0"), epilogue,
548 numnode(nesting), cs("Q"), END_NODE);
549 }
550 ;
551
552
553opt_expression : /* empty */
554 {
555 $$ = cs(" 0");
556 }
557 | expression
558 ;
559
560expression : named_expression
561 {
562 $$ = node($1.load, END_NODE);
563 }
564 | DOT {
565 $$ = node(cs("l."), END_NODE);
566 }
567 | NUMBER
568 {
569 $$ = node(cs(" "), as($1), END_NODE);
570 }
571 | LPAR expression RPAR
572 {
573 $$ = $2;
574 }
575 | LETTER LPAR opt_argument_list RPAR
576 {
577 $$ = node($3, cs("l"),
578 function_node($1), cs("x"),
579 END_NODE);
580 free($1);
581 }
582 | MINUS expression %prec UMINUS
583 {
584 $$ = node(cs(" 0"), $2, cs("-"),
585 END_NODE);
586 }
587 | expression PLUS expression
588 {
589 $$ = node($1, $3, cs("+"), END_NODE);
590 }
591 | expression MINUS expression
592 {
593 $$ = node($1, $3, cs("-"), END_NODE);
594 }
595 | expression MULTIPLY expression
596 {
597 $$ = node($1, $3, cs("*"), END_NODE);
598 }
599 | expression DIVIDE expression
600 {
601 $$ = node($1, $3, cs("/"), END_NODE);
602 }
603 | expression REMAINDER expression
604 {
605 $$ = node($1, $3, cs("%"), END_NODE);
606 }
607 | expression EXPONENT expression
608 {
609 $$ = node($1, $3, cs("^"), END_NODE);
610 }
611 | INCR named_expression
612 {
613 $$ = node($2.load, cs("1+d"), $2.store,
614 END_NODE);
615 }
616 | DECR named_expression
617 {
618 $$ = node($2.load, cs("1-d"),
619 $2.store, END_NODE);
620 }
621 | named_expression INCR
622 {
623 $$ = node($1.load, cs("d1+"),
624 $1.store, END_NODE);
625 }
626 | named_expression DECR
627 {
628 $$ = node($1.load, cs("d1-"),
629 $1.store, END_NODE);
630 }
631 | named_expression ASSIGN_OP expression
632 {
633 if ($2[0] == '\0')
634 $$ = node($3, cs($2), cs("d"), $1.store,
635 END_NODE);
636 else
637 $$ = node($1.load, $3, cs($2), cs("d"),
638 $1.store, END_NODE);
639 }
640 | LENGTH LPAR expression RPAR
641 {
642 $$ = node($3, cs("Z"), END_NODE);
643 }
644 | SQRT LPAR expression RPAR
645 {
646 $$ = node($3, cs("v"), END_NODE);
647 }
648 | SCALE LPAR expression RPAR
649 {
650 $$ = node($3, cs("X"), END_NODE);
651 }
652 | BOOL_NOT expression
653 {
654 $$ = node($2, cs("N"), END_NODE);
655 }
656 | expression BOOL_AND alloc_macro pop_nesting expression
657 {
658 ssize_t n = node(cs("R"), $5, END_NODE);
659 emit_macro($3, n);
660 $$ = node($1, cs("d0!="), $3, END_NODE);
661 }
662 | expression BOOL_OR alloc_macro pop_nesting expression
663 {
664 ssize_t n = node(cs("R"), $5, END_NODE);
665 emit_macro($3, n);
666 $$ = node($1, cs("d0="), $3, END_NODE);
667 }
668 | expression EQUALS expression
669 {
670 $$ = node($1, $3, cs("G"), END_NODE);
671 }
672 | expression UNEQUALS expression
673 {
674 $$ = node($1, $3, cs("GN"), END_NODE);
675 }
676 | expression LESS expression
677 {
678 $$ = node($3, $1, cs("("), END_NODE);
679 }
680 | expression LESS_EQ expression
681 {
682 $$ = node($3, $1, cs("{"), END_NODE);
683 }
684 | expression GREATER expression
685 {
686 $$ = node($1, $3, cs("("), END_NODE);
687 }
688 | expression GREATER_EQ expression
689 {
690 $$ = node($1, $3, cs("{"), END_NODE);
691 }
692 ;
693
694named_expression
695 : LETTER
696 {
697 $$.load = node(cs("l"), letter_node($1),
698 END_NODE);
699 $$.store = node(cs("s"), letter_node($1),
700 END_NODE);
701 free($1);
702 }
703 | LETTER LBRACKET expression RBRACKET
704 {
705 $$.load = node($3, cs(";"),
706 array_node($1), END_NODE);
707 $$.store = node($3, cs(":"),
708 array_node($1), END_NODE);
709 free($1);
710 }
711 | SCALE
712 {
713 $$.load = cs("K");
714 $$.store = cs("k");
715 }
716 | IBASE
717 {
718 $$.load = cs("I");
719 $$.store = cs("i");
720 }
721 | OBASE
722 {
723 $$.load = cs("O");
724 $$.store = cs("o");
725 }
726 ;
727
728print_expression_list
729 : print_expression
730 | print_expression_list COMMA print_expression
731 {
732 $$ = node($1, $3, END_NODE);
733 }
734
735print_expression
736 : expression
737 {
738 $$ = node($1, cs("ds.n"), END_NODE);
739 }
740 | STRING
741 {
742 char *p = escape($1);
743 $$ = node(cs("["), as(p), cs("]n"), END_NODE);
744 free(p);
745 }
746%%
747
748
749static void
750grow(void)
751{
752 struct tree *p;
db555d9a 753 size_t newsize;
f2d37758
MD
754
755 if (current == instr_sz) {
756 newsize = instr_sz * 2 + 1;
757 p = realloc(instructions, newsize * sizeof(*p));
758 if (p == NULL) {
759 free(instructions);
760 err(1, NULL);
761 }
762 instructions = p;
763 instr_sz = newsize;
764 }
765}
766
767static ssize_t
768cs(const char *str)
769{
770 grow();
771 instructions[current].index = CONST_STRING;
772 instructions[current].u.cstr = str;
773 return current++;
774}
775
776static ssize_t
777as(const char *str)
778{
779 grow();
780 instructions[current].index = ALLOC_STRING;
781 instructions[current].u.astr = strdup(str);
782 if (instructions[current].u.astr == NULL)
783 err(1, NULL);
784 return current++;
785}
786
787static ssize_t
788node(ssize_t arg, ...)
789{
790 va_list ap;
791 ssize_t ret;
792
793 va_start(ap, arg);
794
795 ret = current;
796 grow();
797 instructions[current++].index = arg;
798
799 do {
800 arg = va_arg(ap, ssize_t);
801 grow();
802 instructions[current++].index = arg;
803 } while (arg != END_NODE);
804
805 va_end(ap);
806 return ret;
807}
808
809static void
810emit(ssize_t i)
811{
812 if (instructions[i].index >= 0)
813 while (instructions[i].index != END_NODE)
814 emit(instructions[i++].index);
815 else
816 fputs(instructions[i].u.cstr, stdout);
817}
818
819static void
820emit_macro(int node, ssize_t code)
821{
822 putchar('[');
823 emit(code);
824 printf("]s%s\n", instructions[node].u.cstr);
825 nesting--;
826}
827
828static void
829free_tree(void)
830{
db555d9a 831 ssize_t i;
f2d37758
MD
832
833 for (i = 0; i < current; i++)
834 if (instructions[i].index == ALLOC_STRING)
835 free(instructions[i].u.astr);
836 current = 0;
837}
838
839static ssize_t
840numnode(int num)
841{
842 const char *p;
843
844 if (num < 10)
845 p = str_table['0' + num];
846 else if (num < 16)
847 p = str_table['A' - 10 + num];
848 else
849 errx(1, "internal error: break num > 15");
850 return node(cs(" "), cs(p), END_NODE);
851}
852
853
854static ssize_t
855lookup(char * str, size_t len, char type)
856{
857 ENTRY entry, *found;
858 u_short num;
859 u_char *p;
860
861 /* The scanner allocated an extra byte already */
862 if (str[len-1] != type) {
863 str[len] = type;
864 str[len+1] = '\0';
865 }
866 entry.key = str;
867 found = hsearch(entry, FIND);
868 if (found == NULL) {
869 if (var_count == MAX_VARIABLES)
870 errx(1, "too many variables");
871 p = malloc(4);
872 if (p == NULL)
873 err(1, NULL);
874 num = var_count++;
875 p[0] = 255;
876 p[1] = ENCODE(num / VAR_BASE + 1);
877 p[2] = ENCODE(num % VAR_BASE + 1);
878 p[3] = '\0';
879
880 entry.data = (char *)p;
881 entry.key = strdup(str);
882 if (entry.key == NULL)
883 err(1, NULL);
884 found = hsearch(entry, ENTER);
885 if (found == NULL)
886 err(1, NULL);
887 }
888 return cs(found->data);
889}
890
891static ssize_t
892letter_node(char *str)
893{
894 size_t len;
895
896 len = strlen(str);
897 if (len == 1 && str[0] != '_')
898 return cs(str_table[(int)str[0]]);
899 else
900 return lookup(str, len, 'L');
901}
902
903static ssize_t
904array_node(char *str)
905{
906 size_t len;
907
908 len = strlen(str);
909 if (len == 1 && str[0] != '_')
910 return cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR]);
911 else
912 return lookup(str, len, 'A');
913}
914
915static ssize_t
916function_node(char *str)
917{
918 size_t len;
919
920 len = strlen(str);
921 if (len == 1 && str[0] != '_')
922 return cs(str_table[(int)str[0] - 'a' + FUNC_CHAR]);
923 else
924 return lookup(str, len, 'F');
925}
926
927static void
928add_par(ssize_t n)
929{
930 prologue = node(cs("S"), n, prologue, END_NODE);
931 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
932}
933
934static void
935add_local(ssize_t n)
936{
937 prologue = node(cs("0S"), n, prologue, END_NODE);
938 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
939}
940
f2d37758
MD
941void
942yyerror(char *s)
943{
944 char *str, *p;
db555d9a 945 int n;
f2d37758 946
db555d9a
PA
947 if (yyin != NULL && feof(yyin))
948 n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF",
b6d9cda5
SW
949 __progname, filename, lineno, s);
950 else if (isspace(yytext[0]) || !isprint(yytext[0]))
db555d9a
PA
951 n = asprintf(&str,
952 "%s: %s:%d: %s: ascii char 0x%02x unexpected",
f2d37758
MD
953 __progname, filename, lineno, s, yytext[0]);
954 else
db555d9a 955 n = asprintf(&str, "%s: %s:%d: %s: %s unexpected",
f2d37758 956 __progname, filename, lineno, s, yytext);
db555d9a 957 if (n == -1)
f2d37758
MD
958 err(1, NULL);
959
960 fputs("c[", stdout);
961 for (p = str; *p != '\0'; p++) {
962 if (*p == '[' || *p == ']' || *p =='\\')
963 putchar('\\');
964 putchar(*p);
965 }
966 fputs("]pc\n", stdout);
967 free(str);
968}
969
970void
971fatal(const char *s)
972{
973 errx(1, "%s:%d: %s", filename, lineno, s);
974}
975
976static void
977warning(const char *s)
978{
979 warnx("%s:%d: %s", filename, lineno, s);
980}
981
982static void
983init(void)
984{
985 int i;
986
987 for (i = 0; i < UCHAR_MAX; i++) {
988 str_table[i][0] = i;
989 str_table[i][1] = '\0';
990 }
991 if (hcreate(1 << 16) == 0)
992 err(1, NULL);
993}
994
995
b6d9cda5 996static __dead2 void
f2d37758
MD
997usage(void)
998{
db555d9a 999 fprintf(stderr, "usage: %s [-cl] [-e expression] [file ...]\n",
b6d9cda5 1000 __progname);
f2d37758
MD
1001 exit(1);
1002}
1003
1004static char *
1005escape(const char *str)
1006{
1007 char *ret, *p;
1008
1009 ret = malloc(strlen(str) + 1);
1010 if (ret == NULL)
1011 err(1, NULL);
1012
1013 p = ret;
1014 while (*str != '\0') {
1015 /*
1016 * We get _escaped_ strings here. Single backslashes are
1017 * already converted to double backslashes
1018 */
1019 if (*str == '\\') {
1020 if (*++str == '\\') {
1021 switch (*++str) {
1022 case 'a':
1023 *p++ = '\a';
1024 break;
1025 case 'b':
1026 *p++ = '\b';
1027 break;
1028 case 'f':
1029 *p++ = '\f';
1030 break;
1031 case 'n':
1032 *p++ = '\n';
1033 break;
1034 case 'q':
1035 *p++ = '"';
1036 break;
1037 case 'r':
1038 *p++ = '\r';
1039 break;
1040 case 't':
1041 *p++ = '\t';
1042 break;
1043 case '\\':
1044 *p++ = '\\';
1045 break;
1046 }
1047 str++;
1048 } else {
1049 *p++ = '\\';
1050 *p++ = *str++;
1051 }
1052 } else
1053 *p++ = *str++;
1054 }
1055 *p = '\0';
1056 return ret;
1057}
1058
db555d9a 1059/* ARGSUSED */
b4d68f20 1060static void
db555d9a
PA
1061sigchld(int signo)
1062{
1063 pid_t pid;
1064 int status;
1065
1066 for (;;) {
1067 pid = waitpid(dc, &status, WCONTINUED);
1068 if (pid == -1) {
1069 if (errno == EINTR)
1070 continue;
1071 _exit(0);
1072 }
1073 if (WIFEXITED(status) || WIFSIGNALED(status))
1074 _exit(0);
1075 else
1076 break;
1077 }
1078}
1079
ac3cc18c
SW
1080static const char *
1081dummy_prompt(void)
1082{
1083 return ("");
1084}
1085
f2d37758
MD
1086int
1087main(int argc, char *argv[])
1088{
db555d9a 1089 int i, ch;
f2d37758 1090 int p[2];
b6d9cda5 1091 char *q;
f2d37758
MD
1092
1093 init();
1094 setlinebuf(stdout);
1095
1096 sargv = malloc(argc * sizeof(char *));
1097 if (sargv == NULL)
1098 err(1, NULL);
1099
b6d9cda5
SW
1100 if ((cmdexpr = strdup("")) == NULL)
1101 err(1, NULL);
1102 /* The d debug option is 4.4 BSD bc(1) compatible */
1103 while ((ch = getopt(argc, argv, "cde:l")) != -1) {
f2d37758
MD
1104 switch (ch) {
1105 case 'c':
1106 case 'd':
1107 do_fork = false;
1108 break;
b6d9cda5
SW
1109 case 'e':
1110 q = cmdexpr;
1111 if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1)
1112 err(1, NULL);
1113 free(q);
1114 break;
f2d37758
MD
1115 case 'l':
1116 sargv[sargc++] = _PATH_LIBB;
1117 break;
1118 default:
1119 usage();
1120 }
1121 }
1122
1123 argc -= optind;
1124 argv += optind;
1125
db555d9a 1126 interactive = isatty(STDIN_FILENO);
f2d37758
MD
1127 for (i = 0; i < argc; i++)
1128 sargv[sargc++] = argv[i];
1129
1130 if (do_fork) {
1131 if (pipe(p) == -1)
1132 err(1, "cannot create pipe");
db555d9a
PA
1133 dc = fork();
1134 if (dc == -1)
f2d37758 1135 err(1, "cannot fork");
db555d9a
PA
1136 else if (dc != 0) {
1137 signal(SIGCHLD, sigchld);
f2d37758
MD
1138 close(STDOUT_FILENO);
1139 dup(p[1]);
1140 close(p[0]);
1141 close(p[1]);
1142 } else {
1143 close(STDIN_FILENO);
1144 dup(p[0]);
1145 close(p[0]);
1146 close(p[1]);
2038fb68 1147 execl(_PATH_DC, "dc", "-x", NULL);
f2d37758
MD
1148 err(1, "cannot find dc");
1149 }
1150 }
ac3cc18c
SW
1151 if (interactive) {
1152 el = el_init("bc", stdin, stderr, stderr);
1153 hist = history_init();
1154 history(hist, &he, H_SETSIZE, 100);
1155 el_set(el, EL_HIST, history, hist);
1156 el_set(el, EL_EDITOR, "emacs");
1157 el_set(el, EL_SIGNAL, 1);
1158 el_set(el, EL_PROMPT, dummy_prompt);
1159 el_source(el, NULL);
1160 }
f2d37758
MD
1161 yywrap();
1162 return yyparse();
1163}