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