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