xref: /dragonfly/usr.bin/bc/bc.y (revision cb7e3b3c9433ef3b14752f26e3bfdca1094c47e2)
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