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