kernel: Fix buildkernel without INVARIANTS.
[dragonfly.git] / usr.bin / bc / bc.y
blob57b771afa0577d9b1f3e48bf0c3d92eb6d78430f
1 %{
2 /*
3 * $OpenBSD: bc.y,v 1.32 2006/05/18 05:49:53 otto Exp $
4 */
6 /*
7 * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
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.
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.
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).
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.
35 #include <sys/types.h>
36 #include <sys/wait.h>
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <histedit.h>
42 #include <limits.h>
43 #include <search.h>
44 #include <signal.h>
45 #include <stdarg.h>
46 #include <stdbool.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
51 #include "extern.h"
52 #include "pathnames.h"
54 #define END_NODE ((ssize_t) -1)
55 #define CONST_STRING ((ssize_t) -2)
56 #define ALLOC_STRING ((ssize_t) -3)
58 struct tree {
59 ssize_t index;
60 union {
61 char *astr;
62 const char *cstr;
63 } u;
66 int yyparse(void);
67 int yywrap(void);
69 int fileindex;
70 int sargc;
71 char **sargv;
72 char *filename;
73 char *cmdexpr;
75 static void grow(void);
76 static ssize_t cs(const char *);
77 static ssize_t as(const char *);
78 static ssize_t node(ssize_t, ...);
79 static void emit(ssize_t);
80 static void emit_macro(int, ssize_t);
81 static void free_tree(void);
82 static ssize_t numnode(int);
83 static ssize_t lookup(char *, size_t, char);
84 static ssize_t letter_node(char *);
85 static ssize_t array_node(char *);
86 static ssize_t function_node(char *);
88 static void add_par(ssize_t);
89 static void add_local(ssize_t);
90 static void warning(const char *);
91 static void init(void);
92 static __dead2 void usage(void);
93 static char *escape(const char *);
95 static ssize_t instr_sz = 0;
96 static struct tree *instructions = NULL;
97 static ssize_t current = 0;
98 static int macro_char = '0';
99 static int reset_macro_char = '0';
100 static int nesting = 0;
101 static int breakstack[16];
102 static int breaksp = 0;
103 static ssize_t prologue;
104 static ssize_t epilogue;
105 static bool st_has_continue;
106 static char str_table[UCHAR_MAX][2];
107 static bool do_fork = true;
108 static u_short var_count;
109 static pid_t dc;
111 extern char *__progname;
113 #define BREAKSTACK_SZ (sizeof(breakstack)/sizeof(breakstack[0]))
115 /* These values are 4.4BSD bc compatible */
116 #define FUNC_CHAR 0x01
117 #define ARRAY_CHAR 0xa1
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)
126 %start program
128 %union {
129 ssize_t node;
130 struct lvalue lvalue;
131 const char *str;
132 char *astr;
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
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
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
176 program : /* empty */
177 | program input_item
180 input_item : semicolon_list NEWLINE
182 emit($1);
183 macro_char = reset_macro_char;
184 putchar('\n');
185 free_tree();
186 st_has_continue = false;
188 | function
190 putchar('\n');
191 free_tree();
192 st_has_continue = false;
194 | error NEWLINE
196 yyerrok;
198 | error QUIT
200 yyerrok;
204 semicolon_list : /* empty */
206 $$ = cs("");
208 | statement
209 | semicolon_list SEMICOLON statement
211 $$ = node($1, $3, END_NODE);
213 | semicolon_list SEMICOLON
216 statement_list : /* empty */
218 $$ = cs("");
220 | statement
221 | statement_list NEWLINE
222 | statement_list NEWLINE statement
224 $$ = node($1, $3, END_NODE);
226 | statement_list SEMICOLON
227 | statement_list SEMICOLON statement
229 $$ = node($1, $3, END_NODE);
234 opt_statement : /* empty */
236 $$ = cs("");
238 | statement
241 statement : expression
243 $$ = node($1, cs("ps."), END_NODE);
245 | named_expression ASSIGN_OP expression
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);
254 | STRING
256 $$ = node(cs("["), as($1),
257 cs("]P"), END_NODE);
259 | BREAK
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);
271 | CONTINUE
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);
283 | QUIT
285 sigset_t mask;
287 putchar('q');
288 fflush(stdout);
289 if (dc) {
290 sigprocmask(SIG_BLOCK, NULL, &mask);
291 sigsuspend(&mask);
292 } else
293 exit(0);
295 | RETURN return_expression
297 if (nesting == 0) {
298 warning("return must be in a function");
299 YYERROR;
301 $$ = $2;
303 | FOR LPAR alloc_macro opt_expression SEMICOLON
304 opt_relational_expression SEMICOLON
305 opt_expression RPAR opt_statement pop_nesting
307 ssize_t n;
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);
316 emit_macro($3, n);
317 $$ = node($4, cs("s."), $6, $3, cs(" "),
318 END_NODE);
320 | IF LPAR alloc_macro pop_nesting relational_expression RPAR
321 opt_statement
323 emit_macro($3, $7);
324 $$ = node($5, $3, cs(" "), END_NODE);
326 | IF LPAR alloc_macro pop_nesting relational_expression RPAR
327 opt_statement ELSE alloc_macro pop_nesting opt_statement
329 emit_macro($3, $7);
330 emit_macro($9, $11);
331 $$ = node($5, $3, cs("e"), $9, cs(" "),
332 END_NODE);
334 | WHILE LPAR alloc_macro relational_expression RPAR
335 opt_statement pop_nesting
337 ssize_t n;
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);
346 | LBRACE statement_list RBRACE
348 $$ = $2;
350 | PRINT print_expression_list
352 $$ = $2;
356 alloc_macro : /* empty */
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++;
376 pop_nesting : /* empty */
378 breaksp--;
382 function : function_header opt_parameter_list RPAR opt_newline
383 LBRACE NEWLINE opt_auto_define_list
384 statement_list RBRACE
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;
396 function_header : DEFINE LETTER LPAR
398 $$ = function_node($2);
399 free($2);
400 prologue = cs("");
401 epilogue = cs("");
402 nesting = 1;
403 breaksp = 0;
404 breakstack[breaksp] = 0;
408 opt_newline : /* empty */
409 | NEWLINE
412 opt_parameter_list
413 : /* empty */
414 | parameter_list
418 parameter_list : LETTER
420 add_par(letter_node($1));
421 free($1);
423 | LETTER LBRACKET RBRACKET
425 add_par(array_node($1));
426 free($1);
428 | parameter_list COMMA LETTER
430 add_par(letter_node($3));
431 free($3);
433 | parameter_list COMMA LETTER LBRACKET RBRACKET
435 add_par(array_node($3));
436 free($3);
442 opt_auto_define_list
443 : /* empty */
444 | AUTO define_list NEWLINE
445 | AUTO define_list SEMICOLON
449 define_list : LETTER
451 add_local(letter_node($1));
452 free($1);
454 | LETTER LBRACKET RBRACKET
456 add_local(array_node($1));
457 free($1);
459 | define_list COMMA LETTER
461 add_local(letter_node($3));
462 free($3);
464 | define_list COMMA LETTER LBRACKET RBRACKET
466 add_local(array_node($3));
467 free($3);
472 opt_argument_list
473 : /* empty */
475 $$ = cs("");
477 | argument_list
481 argument_list : expression
482 | argument_list COMMA expression
484 $$ = node($1, $3, END_NODE);
486 | argument_list COMMA LETTER LBRACKET RBRACKET
488 $$ = node($1, cs("l"), array_node($3),
489 END_NODE);
490 free($3);
494 opt_relational_expression
495 : /* empty */
497 $$ = cs(" 0 0=");
499 | relational_expression
502 relational_expression
503 : expression EQUALS expression
505 $$ = node($1, $3, cs("="), END_NODE);
507 | expression UNEQUALS expression
509 $$ = node($1, $3, cs("!="), END_NODE);
511 | expression LESS expression
513 $$ = node($1, $3, cs(">"), END_NODE);
515 | expression LESS_EQ expression
517 $$ = node($1, $3, cs("!<"), END_NODE);
519 | expression GREATER expression
521 $$ = node($1, $3, cs("<"), END_NODE);
523 | expression GREATER_EQ expression
525 $$ = node($1, $3, cs("!>"), END_NODE);
527 | expression
529 $$ = node($1, cs(" 0!="), END_NODE);
534 return_expression
535 : /* empty */
537 $$ = node(cs("0"), epilogue,
538 numnode(nesting), cs("Q"), END_NODE);
540 | expression
542 $$ = node($1, epilogue,
543 numnode(nesting), cs("Q"), END_NODE);
545 | LPAR RPAR
547 $$ = node(cs("0"), epilogue,
548 numnode(nesting), cs("Q"), END_NODE);
553 opt_expression : /* empty */
555 $$ = cs(" 0");
557 | expression
560 expression : named_expression
562 $$ = node($1.load, END_NODE);
564 | DOT {
565 $$ = node(cs("l."), END_NODE);
567 | NUMBER
569 $$ = node(cs(" "), as($1), END_NODE);
571 | LPAR expression RPAR
573 $$ = $2;
575 | LETTER LPAR opt_argument_list RPAR
577 $$ = node($3, cs("l"),
578 function_node($1), cs("x"),
579 END_NODE);
580 free($1);
582 | MINUS expression %prec UMINUS
584 $$ = node(cs(" 0"), $2, cs("-"),
585 END_NODE);
587 | expression PLUS expression
589 $$ = node($1, $3, cs("+"), END_NODE);
591 | expression MINUS expression
593 $$ = node($1, $3, cs("-"), END_NODE);
595 | expression MULTIPLY expression
597 $$ = node($1, $3, cs("*"), END_NODE);
599 | expression DIVIDE expression
601 $$ = node($1, $3, cs("/"), END_NODE);
603 | expression REMAINDER expression
605 $$ = node($1, $3, cs("%"), END_NODE);
607 | expression EXPONENT expression
609 $$ = node($1, $3, cs("^"), END_NODE);
611 | INCR named_expression
613 $$ = node($2.load, cs("1+d"), $2.store,
614 END_NODE);
616 | DECR named_expression
618 $$ = node($2.load, cs("1-d"),
619 $2.store, END_NODE);
621 | named_expression INCR
623 $$ = node($1.load, cs("d1+"),
624 $1.store, END_NODE);
626 | named_expression DECR
628 $$ = node($1.load, cs("d1-"),
629 $1.store, END_NODE);
631 | named_expression ASSIGN_OP expression
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);
640 | LENGTH LPAR expression RPAR
642 $$ = node($3, cs("Z"), END_NODE);
644 | SQRT LPAR expression RPAR
646 $$ = node($3, cs("v"), END_NODE);
648 | SCALE LPAR expression RPAR
650 $$ = node($3, cs("X"), END_NODE);
652 | BOOL_NOT expression
654 $$ = node($2, cs("N"), END_NODE);
656 | expression BOOL_AND alloc_macro pop_nesting expression
658 ssize_t n = node(cs("R"), $5, END_NODE);
659 emit_macro($3, n);
660 $$ = node($1, cs("d0!="), $3, END_NODE);
662 | expression BOOL_OR alloc_macro pop_nesting expression
664 ssize_t n = node(cs("R"), $5, END_NODE);
665 emit_macro($3, n);
666 $$ = node($1, cs("d0="), $3, END_NODE);
668 | expression EQUALS expression
670 $$ = node($1, $3, cs("G"), END_NODE);
672 | expression UNEQUALS expression
674 $$ = node($1, $3, cs("GN"), END_NODE);
676 | expression LESS expression
678 $$ = node($3, $1, cs("("), END_NODE);
680 | expression LESS_EQ expression
682 $$ = node($3, $1, cs("{"), END_NODE);
684 | expression GREATER expression
686 $$ = node($1, $3, cs("("), END_NODE);
688 | expression GREATER_EQ expression
690 $$ = node($1, $3, cs("{"), END_NODE);
694 named_expression
695 : LETTER
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);
703 | LETTER LBRACKET expression RBRACKET
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);
711 | SCALE
713 $$.load = cs("K");
714 $$.store = cs("k");
716 | IBASE
718 $$.load = cs("I");
719 $$.store = cs("i");
721 | OBASE
723 $$.load = cs("O");
724 $$.store = cs("o");
728 print_expression_list
729 : print_expression
730 | print_expression_list COMMA print_expression
732 $$ = node($1, $3, END_NODE);
735 print_expression
736 : expression
738 $$ = node($1, cs("ds.n"), END_NODE);
740 | STRING
742 char *p = escape($1);
743 $$ = node(cs("["), as(p), cs("]n"), END_NODE);
744 free(p);
749 static void
750 grow(void)
752 struct tree *p;
753 size_t newsize;
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);
762 instructions = p;
763 instr_sz = newsize;
767 static ssize_t
768 cs(const char *str)
770 grow();
771 instructions[current].index = CONST_STRING;
772 instructions[current].u.cstr = str;
773 return current++;
776 static ssize_t
777 as(const char *str)
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++;
787 static ssize_t
788 node(ssize_t arg, ...)
790 va_list ap;
791 ssize_t ret;
793 va_start(ap, arg);
795 ret = current;
796 grow();
797 instructions[current++].index = arg;
799 do {
800 arg = va_arg(ap, ssize_t);
801 grow();
802 instructions[current++].index = arg;
803 } while (arg != END_NODE);
805 va_end(ap);
806 return ret;
809 static void
810 emit(ssize_t i)
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);
819 static void
820 emit_macro(int node, ssize_t code)
822 putchar('[');
823 emit(code);
824 printf("]s%s\n", instructions[node].u.cstr);
825 nesting--;
828 static void
829 free_tree(void)
831 ssize_t i;
833 for (i = 0; i < current; i++)
834 if (instructions[i].index == ALLOC_STRING)
835 free(instructions[i].u.astr);
836 current = 0;
839 static ssize_t
840 numnode(int num)
842 const char *p;
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);
854 static ssize_t
855 lookup(char * str, size_t len, char type)
857 ENTRY entry, *found;
858 u_short num;
859 u_char *p;
861 /* The scanner allocated an extra byte already */
862 if (str[len-1] != type) {
863 str[len] = type;
864 str[len+1] = '\0';
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';
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);
888 return cs(found->data);
891 static ssize_t
892 letter_node(char *str)
894 size_t len;
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');
903 static ssize_t
904 array_node(char *str)
906 size_t len;
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');
915 static ssize_t
916 function_node(char *str)
918 size_t len;
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');
927 static void
928 add_par(ssize_t n)
930 prologue = node(cs("S"), n, prologue, END_NODE);
931 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
934 static void
935 add_local(ssize_t n)
937 prologue = node(cs("0S"), n, prologue, END_NODE);
938 epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
941 void
942 yyerror(char *s)
944 char *str, *p;
945 int n;
947 if (yyin != NULL && feof(yyin))
948 n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF",
949 __progname, filename, lineno, s);
950 else if (isspace(yytext[0]) || !isprint(yytext[0]))
951 n = asprintf(&str,
952 "%s: %s:%d: %s: ascii char 0x%02x unexpected",
953 __progname, filename, lineno, s, yytext[0]);
954 else
955 n = asprintf(&str, "%s: %s:%d: %s: %s unexpected",
956 __progname, filename, lineno, s, yytext);
957 if (n == -1)
958 err(1, NULL);
960 fputs("c[", stdout);
961 for (p = str; *p != '\0'; p++) {
962 if (*p == '[' || *p == ']' || *p =='\\')
963 putchar('\\');
964 putchar(*p);
966 fputs("]pc\n", stdout);
967 free(str);
970 void
971 fatal(const char *s)
973 errx(1, "%s:%d: %s", filename, lineno, s);
976 static void
977 warning(const char *s)
979 warnx("%s:%d: %s", filename, lineno, s);
982 static void
983 init(void)
985 int i;
987 for (i = 0; i < UCHAR_MAX; i++) {
988 str_table[i][0] = i;
989 str_table[i][1] = '\0';
991 if (hcreate(1 << 16) == 0)
992 err(1, NULL);
996 static __dead2 void
997 usage(void)
999 fprintf(stderr, "usage: %s [-cl] [-e expression] [file ...]\n",
1000 __progname);
1001 exit(1);
1004 static char *
1005 escape(const char *str)
1007 char *ret, *p;
1009 ret = malloc(strlen(str) + 1);
1010 if (ret == NULL)
1011 err(1, NULL);
1013 p = ret;
1014 while (*str != '\0') {
1016 * We get _escaped_ strings here. Single backslashes are
1017 * already converted to double backslashes
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;
1047 str++;
1048 } else {
1049 *p++ = '\\';
1050 *p++ = *str++;
1052 } else
1053 *p++ = *str++;
1055 *p = '\0';
1056 return ret;
1059 /* ARGSUSED */
1060 static void
1061 sigchld(int signo)
1063 pid_t pid;
1064 int status;
1066 for (;;) {
1067 pid = waitpid(dc, &status, WCONTINUED);
1068 if (pid == -1) {
1069 if (errno == EINTR)
1070 continue;
1071 _exit(0);
1073 if (WIFEXITED(status) || WIFSIGNALED(status))
1074 _exit(0);
1075 else
1076 break;
1080 static const char *
1081 dummy_prompt(void)
1083 return ("");
1087 main(int argc, char *argv[])
1089 int i, ch;
1090 int p[2];
1091 char *q;
1093 init();
1094 setlinebuf(stdout);
1096 sargv = malloc(argc * sizeof(char *));
1097 if (sargv == NULL)
1098 err(1, NULL);
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) {
1104 switch (ch) {
1105 case 'c':
1106 case 'd':
1107 do_fork = false;
1108 break;
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;
1115 case 'l':
1116 sargv[sargc++] = _PATH_LIBB;
1117 break;
1118 default:
1119 usage();
1123 argc -= optind;
1124 argv += optind;
1126 interactive = isatty(STDIN_FILENO);
1127 for (i = 0; i < argc; i++)
1128 sargv[sargc++] = argv[i];
1130 if (do_fork) {
1131 if (pipe(p) == -1)
1132 err(1, "cannot create pipe");
1133 dc = fork();
1134 if (dc == -1)
1135 err(1, "cannot fork");
1136 else if (dc != 0) {
1137 signal(SIGCHLD, sigchld);
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]);
1147 execl(_PATH_DC, "dc", "-x", NULL);
1148 err(1, "cannot find dc");
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);
1161 yywrap();
1162 return yyparse();