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