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