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