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