1 %{
2 /*
3 * Turns format specifications into internal list representation.
4
5 * Formats are lists of items:
6 * format := [count] item | [count] '(' format ')' | format format
7
8 * where an item is:
9 * item := [count] insize outform [ '>' c ] ...
10 * | [count] insize [ '>' c ]
11 * | [count] outform [ '>' c ]
12 * | [count] '[' copyout_string ']'
13 * | [count] !0|!1|!2 or seek0|seek1|seek2 (seeks)
14 * | [count] `n' or `;' (adds newline)
15 * | $c ({+-/*=} number)+ (does math on registers)
16 * | $c P (prints register in %ld fmt)
17
18 * and a count is:
19 * count := positive decimal number | $c | $$
20 * Here, c is any non-null, non-'$' character; it names a register
21
22
23 * insize := C | S | I | L | F | D | Z
24 * | | | | | | zero-terminated string
25 * | | | | | double
26 * | | | | float
27 * | | | long
28 * | | int
29 * | short
30 * char
31 * -or-
32 * char short int integer long float real double string
33
34 * outform := a | b | c | d | u | o | h | f | g | ~ | "(printf fmt string)"
35 * | | | | | | | | | ignore (no output)
36 * | | | | | | | | float
37 * | | | | | | | float
38 * | | | | | | hex (unsigned)
39 * | | | | | octal (unsigned)
40 * | | | | unsigned decimal
41 * | | | signed decimal
42 * | | octal for non-printing characters; ascii for printing
43 * | binary (unsigned)
44 * ascii characters
45
46 * default pairings: C->a, S->o, I->d, L->d, [FD]->g, Z->a
47 * a->C, b->C, c->C, [duohx]->I, g->F
48
49 * illegal pairings: [CSILZ]-fg, [FD]-[abcduohx], [SILFD]-ac
50
51
52 * Filters the named files; if there are no files, stdin is used.
53
54 * Ascii formatting:
55 * null -> \@
56 * percent -> \%
57 * backspace -> \b
58 * formfeed -> \f
59 * newline -> \n
60 * return -> \r
61 * tab -> \t
62 * backslash -> \\
63 * all other printing characters (octal 040 - 177): unchanged
64 * all others -> \nnn
65 * In addition, after transforming newline to \n, a true newline is printed.
66
67 */
68
69
70 #include <stdio.h>
71 #include <ctype.h>
72 #include "string.h"
73 #include <errno.h>
74 #include <stdlib.h>
75
76 #include "viz.h"
77
78 extern int do_condense;
79
80 static mathreg = 0; /* For handling multiple-term math expressions */
81
82 %}
83
84 %token <ival> INCHAR OUTCHAR REG STORE LEXERROR NEWLINE EOF_COUNT SEEK
85
86 %token <lval> NUMBER
87
88 %token <sval> COPYOUT PCTSTRING
89
90 %token <ival> MATHOP U_MATHOP
91
92 %type <lval> count /* repetition count */
93
94 %type <item> copyout /* An item with the copyout field filled */
95
96 %type <item> seek /* Seek on input stream */
97
98 %type <item> math /* Math operation on a register */
99
100 %type <item> iocore /* An ioformat with the core of the iospec
101 * filled: size + ichar + ochar are filled,
102 * but fmt ptr & reg_no not filled in.
103 */
104
105 %type <item> iosingle /* iocore + reg_no info */
106
107 %type <memp> iounion /* iosingle | copyout | NEWLINE | math | SEEK;
108 * type field filled, but not count field. */
109
110 %type <memp> iogroup /* [count] iounion */
111
112 %type <memp> list member /* pointers to list and member of list */
113
114 %type <memp> lexerr /* For handling lex-found errors */
115
116 %%
117
118 list : member
119 {
120 if (debug)
121 (void) fprintf(stderr, "list = member; making newlist");
122 rootlist = $$ = newlist($1);
123 if (debug) {
124 (void) fprintf(stderr, " ...list is now:\n");
125 printlist(0, $$);
126 }
127 }
128 | list member
129 {
130 if (debug)
131 (void) fprintf(stderr, "list = list member");
132 rootlist = $$ = addmember( $1, $2 );
133 if (debug) {
134 (void) fprintf(stderr, " ...list is now:\n");
135 printlist(0, $$);
136 }
137 }
138 | lexerr
139 ;
140
141 member : iogroup
142 {
143 if (debug)
144 (void) fprintf(stderr, "member: iogroup\n");
145 $$ = $1;
146 if ($$ == NIL) {
147 (void) fprintf(stderr, "%s: malloc failed\n", prog);
148 YYERROR;
149 }
150 }
151
152 | '(' list ')'
153 {
154 if (debug)
155 (void) fprintf(stderr, "member: ( list )\n");
156 $$ = $2;
157 $$->count = 1;
158 }
159 | count '(' list ')'
160 {
161 if (debug)
162 (void) fprintf(stderr, "member: %d ( list )\n", $1);
163 $3->count = $1 ;
164 $$ = $3;
165 }
166 ;
167
168 iogroup : iounion
169 {
170 $$ = $1;
171 $$->count = 1;
172 $$->next = NIL;
173 Fmt.item = Fmt.p;
174 }
175 | count iounion
176 {
177 $$ = $2;
178 $$->count = $1;
179 $$->next = NIL;
180 Fmt.item = Fmt.p;
181 }
182 ;
183
184 iounion : iosingle
185 {
186 $$ = (MEMBER *) malloc(sizeof(MEMBER));
187 if ($$ == (MEMP) NULL) {
188 (void) fprintf(stderr, "%s: malloc failed.\n", prog);
189 YYERROR;
190 }
191 $$->type = T_IOSPEC;
192 $$->u = $1;
193 mathreg = 0;
194 }
195 | seek
196 {
197 $$ = (MEMBER *) malloc(sizeof(MEMBER));
198 if ($$ == (MEMP) NULL) {
199 (void) fprintf(stderr, "%s: malloc failed.\n", prog);
200 YYERROR;
201 }
202 $$->type = T_SEEK;
203 $$->u = $1;
204 }
205 | copyout
206 {
207 $$ = (MEMBER *) malloc(sizeof(MEMBER));
208 if ($$ == (MEMP) NULL) {
209 (void) fprintf(stderr, "%s: malloc failed.\n", prog);
210 YYERROR;
211 }
212 $$->type = T_COPYOUT;
213 $$->u = $1;
214 mathreg = 0;
215 }
216 | NEWLINE
217 {
218 $$ = (MEMBER *) malloc(sizeof(MEMBER));
219 if ($$ == (MEMP) NULL) {
220 (void) fprintf(stderr, "%s: malloc failed.\n", prog);
221 YYERROR;
222 }
223 $$->type = T_NEWLINE;
224 mathreg = 0;
225 }
226 | math
227 {
228 $$ = (MEMBER *) malloc(sizeof(MEMBER));
229 if ($$ == (MEMP) NULL) {
230 (void) fprintf(stderr, "%s: malloc failed.\n", prog);
231 YYERROR;
232 }
233 $$->type = T_MATH;
234 $$->u = $1;
235 }
236 ;
237
238 copyout : COPYOUT
239 {
240 $$.copyout = malloc((unsigned) (strlen($1) + 1));
241 if ($$.copyout == NULL) {
242 (void) fprintf(stderr, "%s: malloc failed.\n", prog);
243 YYERROR;
244 }
245 (void) strcpy($$.copyout, $1);
246 }
247 ;
248
249 seek : count SEEK count
250 {
251 $$.seek.count = $1;
252 $$.seek.direction = $3;
253 }
254 | MATHOP count SEEK count
255 {
256 if ($1 != '-' && $1 != '+') {
257 (void) fprintf(stderr,
258 "%s: invalid seek offset: %c%d\n",
259 prog, $1, $2);
260 YYERROR;
261 } else if ($1 == '-') {
262 $$.seek.count = -$2;
263 } else {
264 $$.seek.count = $2;
265 }
266 $$.seek.direction = $4;
267 }
268 ;
269
270
271 math : REG MATHOP count
272 {
273 $$.math.reg_no = mathreg = $1;
274 $$.math.operator = $2;
275 $$.math.operand = $3;
276 }
277 | MATHOP count
278 {
279 if (mathreg == 0) {
280 (void) fprintf(stderr,
281 "%s: Math operator outside math expression\n",
282 prog);
283 YYERROR;
284 }
285 $$.math.reg_no = mathreg;
286 $$.math.operator = $1;
287 $$.math.operand = $2;
288 }
289 | REG U_MATHOP
290 {
291 $$.math.reg_no = mathreg = $1;
292 $$.math.operator = $2;
293 $$.math.operand = 0;
294 }
295 | U_MATHOP
296 {
297 if (mathreg == 0) {
298 (void) fprintf(stderr,
299 "%s: Register print operator w/o register\n",
300 prog);
301 YYERROR;
302 }
303 $$.math.reg_no = mathreg;
304 $$.math.operator = $1;
305 $$.math.operand = 0;
306 }
307 | REG MATHOP INCHAR
308 {
309 $$.math.reg_no = mathreg = $1;
310 $$.math.operator = $2;
311 switch ($3) {
312 case 'C':
313 $$.math.operand = sizeof(char);
314 break;
315 case 'S':
316 $$.math.operand = sizeof(short);
317 break;
318 case 'I':
319 $$.math.operand = sizeof(int);
320 break;
321 case 'L':
322 $$.math.operand = sizeof(long);
323 break;
324 case 'F':
325 $$.math.operand = sizeof(float);
326 break;
327 case 'D':
328 $$.math.operand = sizeof(double);
329 break;
330 default:
331 (void) fprintf(stderr,
332 "%s: illegal math sizing char %c\n", prog, $3);
333 YYERROR;
334 }
335 }
336 | MATHOP INCHAR
337 {
338 if (mathreg == 0) {
339 (void) fprintf(stderr,
340 "%s: Math operator outside math expression\n",
341 prog);
342 YYERROR;
343 }
344 $$.math.reg_no = mathreg;
345 $$.math.operator = $1;
346 switch ($2) {
347 case 'C':
348 $$.math.operand = sizeof(char);
349 break;
350 case 'S':
351 $$.math.operand = sizeof(short);
352 break;
353 case 'I':
354 $$.math.operand = sizeof(int);
355 break;
356 case 'L':
357 $$.math.operand = sizeof(long);
358 break;
359 case 'F':
360 $$.math.operand = sizeof(float);
361 break;
362 case 'D':
363 $$.math.operand = sizeof(double);
364 break;
365 default:
366 (void) fprintf(stderr,
367 "%s: illegal math sizing char %c\n", prog, $2);
368 YYERROR;
369 }
370 }
371 ;
372
373
374 iosingle : iocore STORE
375 {
376 $$ = $1;
377 $$.iospec.reg_no = $2;
378 if ( inval4reg($$.iospec.ochar) ) {
379 (void) fprintf(stderr,
380 "%s: Only integer types can be put in a $x register.\n",
381 prog);
382 YYERROR;
383 }
384 }
385 | iocore
386 {
387 $$ = $1;
388 $$.iospec.reg_no = 0;
389 }
390 ;
391
392 iocore : INCHAR OUTCHAR
393 {
394 $$.iospec = makecore($1, $2);
395 if (badiopair($$.iospec.ichar, $$.iospec.ochar)) {
396 (void) fprintf(stderr,
397 "%s: input size `%c' and output format `%c' cannot go together.\n",
398 prog, $$.iospec.ichar, $$.iospec.ochar);
399 yyerror("");
400 YYERROR;
401 }
402 }
403 | INCHAR
404 {
405 $$.iospec = makecore($1, 0);
406 if (badiopair($$.iospec.ichar, $$.iospec.ochar)) {
407 (void) fprintf(stderr,
408 "%s: input size `%c' and output format `%c' cannot go together.\n",
409 prog, $$.iospec.ichar, $$.iospec.ochar);
410 yyerror("");
411 YYERROR;
412 }
413 }
414 | OUTCHAR
415 {
416 $$.iospec = makecore(0, $1);
417 if (badiopair($$.iospec.ichar, $$.iospec.ochar)) {
418 (void) fprintf(stderr,
419 "%s: input size `%c' and output format `%c' cannot go together.\n",
420 prog, $$.iospec.ichar, $$.iospec.ochar);
421 yyerror("");
422 YYERROR;
423 }
424 }
425 | INCHAR PCTSTRING
426 {
427 $$.iospec = makecorepct($1, $2);
428 }
429 | PCTSTRING
430 {
431 $$.iospec = makecorepct(0, $1);
432 }
433 ;
434
435 count : NUMBER
436 { $$ = $1 ; }
437 | REG
438 { $$ = 0 - $1; }
439 | EOF_COUNT
440 { $$ = UPTO_EOF ; }
441 ;
442
443 lexerr : LEXERROR
444 { YYERROR; }
445 ;
446 %%
447
yyerror(s)448 yyerror(s)
449 char *s;
450 {
451 if (s && *s)
452 (void) fprintf(stderr, "%s: %s", prog, s);
453 if (Fmt.p) {
454 int n0, n1, n2;
455 char *nl = strchr(Fmt.line, '\n');
456 if (Fmt.file) {
457 n0 = Fmt.item - Fmt.line;
458 n1 = ((nl <= Fmt.p) ? nl : Fmt.p + 1) - Fmt.item;
459 n2 = nl ? (nl - Fmt.line - n1 - n0) : 0;
460 if (Fmt.item && Fmt.line)
461 fprintf(stderr, " at line %d:\n\t%.*s >>>%.*s<<< %.*s\n",
462 Fmt.linenum, n0, Fmt.line, n1, Fmt.item,
463 n2, (nl < Fmt.p) ? nl : Fmt.p+1);
464 else
465 fprintf(stderr, " at line %d.\n", Fmt.linenum);
466 } else {
467 if (Fmt.item)
468 fprintf(stderr, "\n%.*s >>>%.*s<<< %s\n",
469 Fmt.item - Fmt.text, Fmt.text,
470 Fmt.p + 1 - Fmt.item, Fmt.item,
471 Fmt.p+1);
472 else
473 putc('\n', stderr);
474 }
475 }
476 }
477 /* Save our current location (assumes we are at a newline) */
478 void
mark_line()479 mark_line() {
480 Fmt.linenum++;
481 Fmt.line = Fmt.item = Fmt.p;
482 }
483
yylex()484 yylex()
485 {
486 static char *tmpbuf; /* temporary storage for copyout text */
487
488 char *b;
489 char c;
490
491 if (Fmt.p == NULL) {
492 Fmt.p = Fmt.item = Fmt.line = Fmt.text;
493 Fmt.linenum = 1;
494 tmpbuf = malloc((unsigned) strlen(Fmt.text));
495 if (tmpbuf == NULL) {
496 (void) fprintf(stderr,
497 "%s: Can't malloc buffer space in yylex().\n", prog);
498 exit(1);
499 }
500 /* Eat leading whitespace */
501 eatws1: while (isspace(*Fmt.p)) {
502 if (*Fmt.p++ == '\n')
503 mark_line();
504 }
505 if (*Fmt.p == '#') {
506 /* Comment embedded in format; discard text through newline */
507 while (*Fmt.p && *Fmt.p != '\n')
508 Fmt.p++;
509 goto eatws1;
510 }
511 /* If the first non-whitespace character is a digit,
512 * then the count is specified; don't process to eof.
513 */
514 if ( isdigit(*Fmt.p) )
515 process_to_eof = 0;
516 else
517 process_to_eof = 1;
518 }
519
520 /* Eat whitespace */
521 eatws2: while (isspace(*Fmt.p)) {
522 if (*Fmt.p++ == '\n')
523 mark_line();
524 }
525
526 if (*Fmt.p == '#') {
527 /* Comment embedded in format; discard text through newline */
528 while (*Fmt.p && *Fmt.p != '\n')
529 Fmt.p++;
530 goto eatws2;
531 }
532
533
534 /* Special processing: support {char short int integer long
535 * float real double} for data sizes {C S I I L F F D}, by replacing the
536 * word with the equivalent single-character form.
537
538 * Likewise map {seek} to {!}.
539
540 * Likewise allow "print" for P.
541 */
542 {
543 #define ifname(longname, shortname) \
544 if (strncmp(Fmt.p, longname, sizeof(longname)-1) == 0 && \
545 !isalpha(*(Fmt.p+sizeof(longname)-1))) { \
546 Fmt.p += sizeof(longname) - 2; \
547 c = shortname; \
548 }
549
550 ifname("char", 'C')
551 else ifname("short", 'S')
552 else ifname("int", 'I')
553 else ifname("long", 'L')
554 else ifname("float", 'F')
555 else ifname("real", 'F')
556 else ifname("double", 'D')
557 else ifname("seek", '!')
558 else ifname("print", 'P')
559 else
560 c = *Fmt.p;
561
562 }
563 if (debug)
564 fprintf(stderr, "(dbg) fmt = `%s'\n", Fmt.p);
565
566 /* Now check for usual forms */
567 switch (c) {
568 case '\0':
569 return 0;
570 break;
571 case '0':
572 case '1':
573 case '2':
574 case '3':
575 case '4':
576 case '5':
577 case '6':
578 case '7':
579 case '8':
580 case '9':
581 /* require count base to be 10; if we allow strtol to interpret
582 * hex, we'll have to interpret 10a (10 ascii) as count=266.
583 */
584 yylval.lval = strtol(Fmt.p, &b, 10);
585 if (b == Fmt.p) {
586 fprintf(stderr, "%s: severe error: strtol(\"%s\",...) failed\n");
587 exit(1);
588 }
589 Fmt.p = b;
590 return NUMBER;
591 break;
592 case 'C':
593 case 'S':
594 case 'I':
595 case 'L':
596 case 'F':
597 case 'D':
598 case 'Z':
599 yylval.ival = c;
600 *Fmt.p++;
601 return INCHAR;
602 break;
603 case '~':
604 case 'a':
605 case 'b':
606 case 'c':
607 case 'd':
608 case 'u':
609 case 'o':
610 case 'x':
611 case 'f':
612 case 'g':
613 yylval.ival = *Fmt.p++;
614 return OUTCHAR;
615 break;
616 case 'h':
617 yylval.ival = 'x';
618 Fmt.p++;
619 return OUTCHAR;
620 break;
621 case '[':
622 for (b = tmpbuf, Fmt.p++; *Fmt.p && *Fmt.p != ']'; ) {
623 if (*Fmt.p == '\n') {
624 (void) fprintf(stderr, "%s: Unterminated output comment.\n", prog);
625 return LEXERROR;
626 }
627 if (*Fmt.p == '\\' && *(Fmt.p+1) == ']')
628 { *b++ = *++Fmt.p; Fmt.p++; }
629 else
630 *b++ = *Fmt.p++;
631 }
632 if (*Fmt.p++ != ']') {
633 (void) fprintf(stderr, "%s: Unterminated output comment.\n", prog);
634 return LEXERROR;
635 }
636 *b = '\0';
637 yylval.sval = tmpbuf;
638 if (strstr(yylval.sval, "$("))
639 do_condense=0; /* don't condense list, so that $( gives
640 * a count correct from user's perspective.
641 */
642 return COPYOUT;
643 break;
644 case ';':
645 ++Fmt.p;
646 return NEWLINE;
647 break;
648 case '\\':
649 ++Fmt.p;
650 switch (*Fmt.p) {
651 case 'n':
652 ++Fmt.p;
653 return NEWLINE;
654 break;
655 default:
656 (void) fprintf(stderr, "%s: unrecognized backslash sequence.\n");
657 }
658 case 'n':
659 ++Fmt.p;
660 return NEWLINE;
661 break;
662 case '(':
663 case ')':
664 return (*Fmt.p++);
665 break;
666 case '!':
667 ++Fmt.p;
668 return SEEK;
669 break;
670 case '$':
671 yylval.ival = *++Fmt.p;
672 if (*Fmt.p == '\0') {
673 (void) fprintf(stderr,
674 "%s: `$' cannot end the format.\n", prog);
675 return LEXERROR;
676 }
677 Fmt.p++;
678 if (yylval.ival == '$')
679 return EOF_COUNT; /* $$ is special: a count that means up-to-eof */
680
681 if (yylval.ival == '(')
682 do_condense=0; /* don't condense list, so that $( gives
683 * a count correct from user's perspective.
684 */
685 return REG;
686 break;
687 case '>':
688 yylval.ival = *++Fmt.p;
689 if (*Fmt.p == '\0') {
690 (void) fprintf(stderr,
691 "%s: `>' must precede a register character.\n", prog);
692 return LEXERROR;
693 }
694 Fmt.p++;
695 return STORE;
696 break;
697 case '+':
698 case '-':
699 case '*':
700 case '/':
701 case '%':
702 case '=':
703 yylval.ival = *Fmt.p++;
704 return MATHOP;
705 break;
706 case '?':
707 case ':':
708 /* Used to form tests */
709 return *Fmt.p++;
710 break;
711 case 'P':
712 yylval.ival = *Fmt.p++;
713 return U_MATHOP;
714 break;
715 case '"':
716 /* User-entered format. Terminate it at '"'. */
717 for (b = tmpbuf, Fmt.p++; *Fmt.p && *Fmt.p != '"'; ) {
718 if (*Fmt.p == '\n') {
719 (void) fprintf(stderr, "%s: Unterminated printf-fmt.\n", prog);
720 return LEXERROR;
721 }
722 if (*Fmt.p == '\\' && *(Fmt.p+1) == '"')
723 { *b++ = *++Fmt.p; Fmt.p++; }
724 else
725 *b++ = *Fmt.p++;
726 }
727 if (*Fmt.p++ != '"') {
728 (void) fprintf(stderr, "%s: Unterminated printf-fmt.\n", prog);
729 return LEXERROR;
730 }
731 *b = '\0';
732 Fmt.p++;
733 yylval.sval = tmpbuf;
734 return PCTSTRING;
735 break;
736 default:
737 fprintf(stderr, "%s: Invalid character `%c' in format\n", prog, *Fmt.p);
738 return LEXERROR;
739 }
740 /* NOTREACHED */
741 }
742
743 int
defaultichar(ochar)744 defaultichar(ochar)
745 int ochar; /* Specifies the output character */
746 {
747
748 switch (ochar) {
749 case 'a':
750 case 'b':
751 case 'c':
752 case '~':
753 return 'C';
754 break;
755 case 'd':
756 case 'u':
757 case 'o':
758 case 'h':
759 case 'x':
760 return 'I';
761 break;
762 case 'f':
763 case 'g':
764 return 'F';
765 break;
766 default:
767 return '\0';
768 break;
769 }
770 /* NOTREACHED */
771 }
772
773 int
defaultochar(ichar)774 defaultochar(ichar)
775 int ichar; /* Specifies the input character */
776 {
777 switch (ichar) {
778 case 'C':
779 return 'a';
780 break;
781 case 'Z':
782 return 'a';
783 break;
784 case 'S':
785 return 'o';
786 break;
787 case 'I':
788 return 'd';
789 break;
790 case 'L':
791 return 'd';
792 break;
793 case 'F':
794 case 'D':
795 return 'g';
796 break;
797 default:
798 return '\0';
799 break;
800 }
801 /* NOTREACHED */
802 }
803
804 int
getsize(ichar)805 getsize(ichar)
806 int ichar;
807 {
808
809 switch (ichar) {
810 case ';':
811 return 0;
812 break;
813 case 'C':
814 case 'Z':
815 return sizeof(char);
816 break;
817 case 'S':
818 return sizeof(short);
819 break;
820 case 'I':
821 return sizeof(int);
822 break;
823 case 'L':
824 return sizeof(long);
825 break;
826 case 'F':
827 return sizeof(float);
828 break;
829 case 'D':
830 return sizeof(double);
831 break;
832 default:
833 return 0;
834 }
835 /* NOTREACHED */
836 }
837
badiopair(ic,oc)838 badiopair(ic, oc)
839 int ic, oc;
840 {
841 /* illegal pairings: [CSILZ]-fg, [FD]-[abduohx], [SILFD]-ac */
842
843 if (strchr("fg", oc) && strchr("CSILZ", ic))
844 return 1;
845 else if (strchr("abduohx", oc) && strchr("FD", ic))
846 return 1;
847 else if (strchr("ac", oc) && strchr("SILFD", ic))
848 return 1;
849 else
850 return 0;
851 }
852
853
inval4reg(oc)854 inval4reg(oc)
855 int oc;
856 {
857 /* Illegal to use ">x" notation if data are float types */
858 if (oc == 'g' || oc == 'f')
859 return 1;
860 else
861 return 0;
862 }
863