2 /* $OpenBSD: bc.y,v 1.46 2014/10/14 15:35:18 deraadt Exp $ */
5 * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * This implementation of bc(1) uses concepts from the original 4.4
22 * BSD bc(1). The code itself is a complete rewrite, based on the
23 * Posix defined bc(1) grammar. Other differences include type safe
24 * usage of pointers to build the tree of emitted code, typed yacc
25 * rule values, dynamic allocation of all data structures and a
26 * completely rewritten lexical analyzer using lex(1).
28 * Some effort has been made to make sure that the generated code is
29 * the same as the code generated by the older version, to provide
30 * easy regression testing.
33 #include <sys/types.h>
50 #include "pathnames.h"
52 #define BC_VER "1.1-FreeBSD"
53 #define END_NODE ((ssize_t) -1)
54 #define CONST_STRING ((ssize_t) -2)
55 #define ALLOC_STRING ((ssize_t) -3)
76 static void grow(void);
77 static ssize_t cs(const char *);
78 static ssize_t as(const char *);
79 static ssize_t node(ssize_t, ...);
80 static void emit(ssize_t, int);
81 static void emit_macro(int, ssize_t);
82 static void free_tree(void);
83 static ssize_t numnode(int);
84 static ssize_t lookup(char *, size_t, char);
85 static ssize_t letter_node(char *);
86 static ssize_t array_node(char *);
87 static ssize_t function_node(char *);
89 static void add_par(ssize_t);
90 static void add_local(ssize_t);
91 static void warning(const char *);
92 static void init(void);
93 static void usage(void);
94 static char *escape(const char *);
96 static ssize_t instr_sz = 0;
97 static struct tree *instructions = NULL;
98 static ssize_t current = 0;
99 static int macro_char = '0';
100 static int reset_macro_char = '0';
101 static int nesting = 0;
102 static int breakstack[16];
103 static int breaksp = 0;
104 static ssize_t prologue;
105 static ssize_t epilogue;
106 static bool st_has_continue;
107 static char str_table[UCHAR_MAX][2];
108 static bool do_fork = true;
109 static u_short var_count;
112 static void sigchld(int);
114 extern char *__progname;
116 #define BREAKSTACK_SZ (sizeof(breakstack)/sizeof(breakstack[0]))
118 /* These values are 4.4BSD bc compatible */
119 #define FUNC_CHAR 0x01
120 #define ARRAY_CHAR 0xa1
122 /* Skip '\0', [, \ and ] */
123 #define ENCODE(c) ((c) < '[' ? (c) : (c) + 3);
124 #define VAR_BASE (256-4)
125 #define MAX_VARIABLES (VAR_BASE * VAR_BASE)
127 const struct option long_options[] =
129 {"expression", required_argument, NULL, 'e'},
130 {"help", no_argument, NULL, 'h'},
131 {"mathlib", no_argument, NULL, 'l'},
132 /* compatibility option */
133 {"quiet", no_argument, NULL, 'q'},
134 {"version", no_argument, NULL, 'v'},
135 {NULL, no_argument, NULL, 0}
143 struct lvalue lvalue;
149 %token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT
152 %token <str> NUMBER STRING
153 %token DEFINE BREAK QUIT LENGTH
154 %token RETURN FOR IF WHILE SQRT
155 %token SCALE IBASE OBASE AUTO
156 %token CONTINUE ELSE PRINT
161 %nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER
162 %right <str> ASSIGN_OP
164 %left MULTIPLY DIVIDE REMAINDER
169 %type <lvalue> named_expression
170 %type <node> argument_list
171 %type <node> alloc_macro
172 %type <node> expression
173 %type <node> function
174 %type <node> function_header
175 %type <node> input_item
176 %type <node> opt_argument_list
177 %type <node> opt_expression
178 %type <node> opt_relational_expression
179 %type <node> opt_statement
180 %type <node> print_expression
181 %type <node> print_expression_list
182 %type <node> relational_expression
183 %type <node> return_expression
184 %type <node> semicolon_list
185 %type <node> statement
186 %type <node> statement_list
190 program : /* empty */
194 input_item : semicolon_list NEWLINE
197 macro_char = reset_macro_char;
200 st_has_continue = false;
206 st_has_continue = false;
218 semicolon_list : /* empty */
223 | semicolon_list SEMICOLON statement
225 $$ = node($1, $3, END_NODE);
227 | semicolon_list SEMICOLON
230 statement_list : /* empty */
235 | statement_list NEWLINE
236 | statement_list NEWLINE statement
238 $$ = node($1, $3, END_NODE);
240 | statement_list SEMICOLON
241 | statement_list SEMICOLON statement
243 $$ = node($1, $3, END_NODE);
248 opt_statement : /* empty */
255 statement : expression
257 $$ = node($1, cs("ps."), END_NODE);
259 | named_expression ASSIGN_OP expression
262 $$ = node($3, cs($2), $1.store,
265 $$ = node($1.load, $3, cs($2), $1.store,
270 $$ = node(cs("["), as($1),
276 warning("break not in for or while");
281 breakstack[breaksp-1]),
288 warning("continue not in for or while");
291 st_has_continue = true;
292 $$ = node(numnode(nesting -
293 breakstack[breaksp-1] - 1),
304 sigprocmask(SIG_BLOCK, NULL, &mask);
309 | RETURN return_expression
312 warning("return must be in a function");
317 | FOR LPAR alloc_macro opt_expression SEMICOLON
318 opt_relational_expression SEMICOLON
319 opt_expression RPAR opt_statement pop_nesting
324 n = node($10, cs("M"), $8, cs("s."),
327 n = node($10, $8, cs("s."), $6, $3,
331 $$ = node($4, cs("s."), $6, $3, cs(" "),
334 | IF LPAR alloc_macro pop_nesting relational_expression RPAR
338 $$ = node($5, $3, cs(" "), END_NODE);
340 | IF LPAR alloc_macro pop_nesting relational_expression RPAR
341 opt_statement ELSE alloc_macro pop_nesting opt_statement
345 $$ = node($5, $3, cs("e"), $9, cs(" "),
348 | WHILE LPAR alloc_macro relational_expression RPAR
349 opt_statement pop_nesting
354 n = node($6, cs("M"), $4, $3, END_NODE);
356 n = node($6, $4, $3, END_NODE);
358 $$ = node($4, $3, cs(" "), END_NODE);
360 | LBRACE statement_list RBRACE
364 | PRINT print_expression_list
370 alloc_macro : /* empty */
372 $$ = cs(str_table[macro_char]);
374 /* Do not use [, \ and ] */
375 if (macro_char == '[')
378 else if (macro_char == 'a')
380 else if (macro_char == ARRAY_CHAR)
382 else if (macro_char == 255)
383 fatal("program too big");
384 if (breaksp == BREAKSTACK_SZ)
385 fatal("nesting too deep");
386 breakstack[breaksp++] = nesting++;
390 pop_nesting : /* empty */
396 function : function_header opt_parameter_list RPAR opt_newline
397 LBRACE NEWLINE opt_auto_define_list
398 statement_list RBRACE
400 int n = node(prologue, $8, epilogue,
401 cs("0"), numnode(nesting),
404 reset_macro_char = macro_char;
410 function_header : DEFINE LETTER LPAR
412 $$ = function_node($2);
418 breakstack[breaksp] = 0;
422 opt_newline : /* empty */
432 parameter_list : LETTER
434 add_par(letter_node($1));
437 | LETTER LBRACKET RBRACKET
439 add_par(array_node($1));
442 | parameter_list COMMA LETTER
444 add_par(letter_node($3));
447 | parameter_list COMMA LETTER LBRACKET RBRACKET
449 add_par(array_node($3));
458 | AUTO define_list NEWLINE
459 | AUTO define_list SEMICOLON
465 add_local(letter_node($1));
468 | LETTER LBRACKET RBRACKET
470 add_local(array_node($1));
473 | define_list COMMA LETTER
475 add_local(letter_node($3));
478 | define_list COMMA LETTER LBRACKET RBRACKET
480 add_local(array_node($3));
495 argument_list : expression
496 | argument_list COMMA expression
498 $$ = node($1, $3, END_NODE);
500 | argument_list COMMA LETTER LBRACKET RBRACKET
502 $$ = node($1, cs("l"), array_node($3),
508 opt_relational_expression
513 | relational_expression
516 relational_expression
517 : expression EQUALS expression
519 $$ = node($1, $3, cs("="), END_NODE);
521 | expression UNEQUALS expression
523 $$ = node($1, $3, cs("!="), END_NODE);
525 | expression LESS expression
527 $$ = node($1, $3, cs(">"), END_NODE);
529 | expression LESS_EQ expression
531 $$ = node($1, $3, cs("!<"), END_NODE);
533 | expression GREATER expression
535 $$ = node($1, $3, cs("<"), END_NODE);
537 | expression GREATER_EQ expression
539 $$ = node($1, $3, cs("!>"), END_NODE);
543 $$ = node($1, cs(" 0!="), END_NODE);
551 $$ = node(cs("0"), epilogue,
552 numnode(nesting), cs("Q"), END_NODE);
556 $$ = node($1, epilogue,
557 numnode(nesting), cs("Q"), END_NODE);
561 $$ = node(cs("0"), epilogue,
562 numnode(nesting), cs("Q"), END_NODE);
567 opt_expression : /* empty */
574 expression : named_expression
576 $$ = node($1.load, END_NODE);
579 $$ = node(cs("l."), END_NODE);
583 $$ = node(cs(" "), as($1), END_NODE);
585 | LPAR expression RPAR
589 | LETTER LPAR opt_argument_list RPAR
591 $$ = node($3, cs("l"),
592 function_node($1), cs("x"),
596 | MINUS expression %prec UMINUS
598 $$ = node(cs(" 0"), $2, cs("-"),
601 | expression PLUS expression
603 $$ = node($1, $3, cs("+"), END_NODE);
605 | expression MINUS expression
607 $$ = node($1, $3, cs("-"), END_NODE);
609 | expression MULTIPLY expression
611 $$ = node($1, $3, cs("*"), END_NODE);
613 | expression DIVIDE expression
615 $$ = node($1, $3, cs("/"), END_NODE);
617 | expression REMAINDER expression
619 $$ = node($1, $3, cs("%"), END_NODE);
621 | expression EXPONENT expression
623 $$ = node($1, $3, cs("^"), END_NODE);
625 | INCR named_expression
627 $$ = node($2.load, cs("1+d"), $2.store,
630 | DECR named_expression
632 $$ = node($2.load, cs("1-d"),
635 | named_expression INCR
637 $$ = node($1.load, cs("d1+"),
640 | named_expression DECR
642 $$ = node($1.load, cs("d1-"),
645 | named_expression ASSIGN_OP expression
648 $$ = node($3, cs($2), cs("d"), $1.store,
651 $$ = node($1.load, $3, cs($2), cs("d"),
654 | LENGTH LPAR expression RPAR
656 $$ = node($3, cs("Z"), END_NODE);
658 | SQRT LPAR expression RPAR
660 $$ = node($3, cs("v"), END_NODE);
662 | SCALE LPAR expression RPAR
664 $$ = node($3, cs("X"), END_NODE);
666 | BOOL_NOT expression
668 $$ = node($2, cs("N"), END_NODE);
670 | expression BOOL_AND alloc_macro pop_nesting expression
672 ssize_t n = node(cs("R"), $5, END_NODE);
674 $$ = node($1, cs("d0!="), $3, END_NODE);
676 | expression BOOL_OR alloc_macro pop_nesting expression
678 ssize_t n = node(cs("R"), $5, END_NODE);
680 $$ = node($1, cs("d0="), $3, END_NODE);
682 | expression EQUALS expression
684 $$ = node($1, $3, cs("G"), END_NODE);
686 | expression UNEQUALS expression
688 $$ = node($1, $3, cs("GN"), END_NODE);
690 | expression LESS expression
692 $$ = node($3, $1, cs("("), END_NODE);
694 | expression LESS_EQ expression
696 $$ = node($3, $1, cs("{"), END_NODE);
698 | expression GREATER expression
700 $$ = node($1, $3, cs("("), END_NODE);
702 | expression GREATER_EQ expression
704 $$ = node($1, $3, cs("{"), END_NODE);
711 $$.load = node(cs("l"), letter_node($1),
713 $$.store = node(cs("s"), letter_node($1),
717 | LETTER LBRACKET expression RBRACKET
719 $$.load = node($3, cs(";"),
720 array_node($1), END_NODE);
721 $$.store = node($3, cs(":"),
722 array_node($1), END_NODE);
742 print_expression_list
744 | print_expression_list COMMA print_expression
746 $$ = node($1, $3, END_NODE);
752 $$ = node($1, cs("ds.n"), END_NODE);
756 char *p = escape($1);
757 $$ = node(cs("["), as(p), cs("]n"), END_NODE);
769 if (current == instr_sz) {
770 newsize = instr_sz * 2 + 1;
771 p = reallocarray(instructions, newsize, sizeof(*p));
786 instructions[current].index = CONST_STRING;
787 instructions[current].u.cstr = str;
796 instructions[current].index = ALLOC_STRING;
797 instructions[current].u.astr = strdup(str);
798 if (instructions[current].u.astr == NULL)
804 node(ssize_t arg, ...)
813 instructions[current++].index = arg;
816 arg = va_arg(ap, ssize_t);
818 instructions[current++].index = arg;
819 } while (arg != END_NODE);
826 emit(ssize_t i, int level)
830 errx(1, "internal error: tree level > 1000");
831 if (instructions[i].index >= 0) {
832 while (instructions[i].index != END_NODE &&
833 instructions[i].index != i) {
834 emit(instructions[i].index, level + 1);
837 } else if (instructions[i].index != END_NODE)
838 fputs(instructions[i].u.cstr, stdout);
842 emit_macro(int nodeidx, ssize_t code)
847 printf("]s%s\n", instructions[nodeidx].u.cstr);
856 for (i = 0; i < current; i++)
857 if (instructions[i].index == ALLOC_STRING)
858 free(instructions[i].u.astr);
868 p = str_table['0' + num];
870 p = str_table['A' - 10 + num];
872 errx(1, "internal error: break num > 15");
873 return (node(cs(" "), cs(p), END_NODE));
878 lookup(char * str, size_t len, char type)
884 /* The scanner allocated an extra byte already */
885 if (str[len-1] != type) {
890 found = hsearch(entry, FIND);
892 if (var_count == MAX_VARIABLES)
893 errx(1, "too many variables");
899 p[1] = ENCODE(num / VAR_BASE + 1);
900 p[2] = ENCODE(num % VAR_BASE + 1);
903 entry.data = (char *)p;
904 entry.key = strdup(str);
905 if (entry.key == NULL)
907 found = hsearch(entry, ENTER);
911 return (cs(found->data));
915 letter_node(char *str)
920 if (len == 1 && str[0] != '_')
921 return (cs(str_table[(int)str[0]]));
923 return (lookup(str, len, 'L'));
927 array_node(char *str)
932 if (len == 1 && str[0] != '_')
933 return (cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR]));
935 return (lookup(str, len, 'A'));
939 function_node(char *str)
944 if (len == 1 && str[0] != '_')
945 return (cs(str_table[(int)str[0] - 'a' + FUNC_CHAR]));
947 return (lookup(str, len, 'F'));
954 prologue = node(cs("S"), n, prologue, END_NODE);
955 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
962 prologue = node(cs("0S"), n, prologue, END_NODE);
963 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
967 yyerror(const char *s)
972 if (yyin != NULL && feof(yyin))
973 n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF",
974 __progname, filename, lineno, s);
975 else if (yytext[0] == '\n')
977 "%s: %s:%d: %s: newline unexpected",
978 __progname, filename, lineno, s);
979 else if (isspace((unsigned char)yytext[0]) ||
980 !isprint((unsigned char)yytext[0]))
982 "%s: %s:%d: %s: ascii char 0x%02x unexpected",
983 __progname, filename, lineno, s, yytext[0] & 0xff);
985 n = asprintf(&str, "%s: %s:%d: %s: %s unexpected",
986 __progname, filename, lineno, s, yytext);
991 for (p = str; *p != '\0'; p++) {
992 if (*p == '[' || *p == ']' || *p =='\\')
996 fputs("]ec\n", stdout);
1001 fatal(const char *s)
1004 errx(1, "%s:%d: %s", filename, lineno, s);
1008 warning(const char *s)
1011 warnx("%s:%d: %s", filename, lineno, s);
1019 for (i = 0; i < UCHAR_MAX; i++) {
1020 str_table[i][0] = i;
1021 str_table[i][1] = '\0';
1023 if (hcreate(1 << 16) == 0)
1032 fprintf(stderr, "usage: %s [-chlv] [-e expression] [file ...]\n",
1038 escape(const char *str)
1042 ret = malloc(strlen(str) + 1);
1047 while (*str != '\0') {
1049 * We get _escaped_ strings here. Single backslashes are
1050 * already converted to double backslashes
1053 if (*++str == '\\') {
1094 sigchld(int signo __unused)
1097 int status, save_errno = errno;
1100 pid = waitpid(dc, &status, WCONTINUED | WNOHANG);
1105 } else if (pid == 0)
1107 if (WIFEXITED(status) || WIFSIGNALED(status))
1123 main(int argc, char *argv[])
1130 setvbuf(stdout, NULL, _IOLBF, 0);
1132 sargv = reallocarray(NULL, argc, sizeof(char *));
1136 if ((cmdexpr = strdup("")) == NULL)
1138 /* The d debug option is 4.4 BSD bc(1) compatible */
1139 while ((ch = getopt_long(argc, argv, "cde:hlqv",
1140 long_options, NULL)) != -1) {
1148 if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1)
1156 sargv[sargc++] = _PATH_LIBB;
1159 /* compatibility option */
1162 fprintf(stderr, "%s (BSD bc) %s\n", __progname, BC_VER);
1173 interactive = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) &&
1174 isatty(STDERR_FILENO);
1175 for (i = 0; i < argc; i++)
1176 sargv[sargc++] = argv[i];
1180 err(1, "cannot create pipe");
1183 err(1, "cannot fork");
1185 signal(SIGCHLD, sigchld);
1186 close(STDOUT_FILENO);
1191 close(STDIN_FILENO);
1195 execl(_PATH_DC, "dc", "-x", (char *)NULL);
1196 err(1, "cannot find dc");
1201 el = el_init("bc", stdin, stderr, stderr);
1202 hist = history_init();
1203 history(hist, &he, H_SETSIZE, 100);
1204 el_set(el, EL_HIST, history, hist);
1205 el_set(el, EL_EDITOR, "emacs");
1206 el_set(el, EL_SIGNAL, 1);
1207 el_set(el, EL_PROMPT, dummy_prompt);
1208 el_set(el, EL_ADDFN, "bc_eof", "", bc_eof);
1209 el_set(el, EL_BIND, "^D", "bc_eof", NULL);
1210 el_source(el, NULL);