%{ /* * Turns format specifications into internal list representation. * Formats are lists of items: * format := [count] item | [count] '(' format ')' | format format * where an item is: * item := [count] insize outform [ '>' c ] ... * | [count] insize [ '>' c ] * | [count] outform [ '>' c ] * | [count] '[' copyout_string ']' * | [count] !0|!1|!2 or seek0|seek1|seek2 (seeks) * | [count] `n' or `;' (adds newline) * | $c ({+-/*=} number)+ (does math on registers) * | $c P (prints register in %ld fmt) * and a count is: * count := positive decimal number | $c | $$ * Here, c is any non-null, non-'$' character; it names a register * insize := C | S | I | L | F | D | Z * | | | | | | zero-terminated string * | | | | | double * | | | | float * | | | long * | | int * | short * char * -or- * char short int integer long float real double string * outform := a | b | c | d | u | o | h | f | g | ~ | "(printf fmt string)" * | | | | | | | | | ignore (no output) * | | | | | | | | float * | | | | | | | float * | | | | | | hex (unsigned) * | | | | | octal (unsigned) * | | | | unsigned decimal * | | | signed decimal * | | octal for non-printing characters; ascii for printing * | binary (unsigned) * ascii characters * default pairings: C->a, S->o, I->d, L->d, [FD]->g, Z->a * a->C, b->C, c->C, [duohx]->I, g->F * illegal pairings: [CSILZ]-fg, [FD]-[abcduohx], [SILFD]-ac * Filters the named files; if there are no files, stdin is used. * Ascii formatting: * null -> \@ * percent -> \% * backspace -> \b * formfeed -> \f * newline -> \n * return -> \r * tab -> \t * backslash -> \\ * all other printing characters (octal 040 - 177): unchanged * all others -> \nnn * In addition, after transforming newline to \n, a true newline is printed. */ #include #include #include "string.h" #include #include #include "viz.h" extern int do_condense; static mathreg = 0; /* For handling multiple-term math expressions */ %} %token INCHAR OUTCHAR REG STORE LEXERROR NEWLINE EOF_COUNT SEEK %token NUMBER %token COPYOUT PCTSTRING %token MATHOP U_MATHOP %type count /* repetition count */ %type copyout /* An item with the copyout field filled */ %type seek /* Seek on input stream */ %type math /* Math operation on a register */ %type iocore /* An ioformat with the core of the iospec * filled: size + ichar + ochar are filled, * but fmt ptr & reg_no not filled in. */ %type iosingle /* iocore + reg_no info */ %type iounion /* iosingle | copyout | NEWLINE | math | SEEK; * type field filled, but not count field. */ %type iogroup /* [count] iounion */ %type list member /* pointers to list and member of list */ %type lexerr /* For handling lex-found errors */ %% list : member { if (debug) (void) fprintf(stderr, "list = member; making newlist"); rootlist = $$ = newlist($1); if (debug) { (void) fprintf(stderr, " ...list is now:\n"); printlist(0, $$); } } | list member { if (debug) (void) fprintf(stderr, "list = list member"); rootlist = $$ = addmember( $1, $2 ); if (debug) { (void) fprintf(stderr, " ...list is now:\n"); printlist(0, $$); } } | lexerr ; member : iogroup { if (debug) (void) fprintf(stderr, "member: iogroup\n"); $$ = $1; if ($$ == NIL) { (void) fprintf(stderr, "%s: malloc failed\n", prog); YYERROR; } } | '(' list ')' { if (debug) (void) fprintf(stderr, "member: ( list )\n"); $$ = $2; $$->count = 1; } | count '(' list ')' { if (debug) (void) fprintf(stderr, "member: %d ( list )\n", $1); $3->count = $1 ; $$ = $3; } ; iogroup : iounion { $$ = $1; $$->count = 1; $$->next = NIL; Fmt.item = Fmt.p; } | count iounion { $$ = $2; $$->count = $1; $$->next = NIL; Fmt.item = Fmt.p; } ; iounion : iosingle { $$ = (MEMBER *) malloc(sizeof(MEMBER)); if ($$ == (MEMP) NULL) { (void) fprintf(stderr, "%s: malloc failed.\n", prog); YYERROR; } $$->type = T_IOSPEC; $$->u = $1; mathreg = 0; } | seek { $$ = (MEMBER *) malloc(sizeof(MEMBER)); if ($$ == (MEMP) NULL) { (void) fprintf(stderr, "%s: malloc failed.\n", prog); YYERROR; } $$->type = T_SEEK; $$->u = $1; } | copyout { $$ = (MEMBER *) malloc(sizeof(MEMBER)); if ($$ == (MEMP) NULL) { (void) fprintf(stderr, "%s: malloc failed.\n", prog); YYERROR; } $$->type = T_COPYOUT; $$->u = $1; mathreg = 0; } | NEWLINE { $$ = (MEMBER *) malloc(sizeof(MEMBER)); if ($$ == (MEMP) NULL) { (void) fprintf(stderr, "%s: malloc failed.\n", prog); YYERROR; } $$->type = T_NEWLINE; mathreg = 0; } | math { $$ = (MEMBER *) malloc(sizeof(MEMBER)); if ($$ == (MEMP) NULL) { (void) fprintf(stderr, "%s: malloc failed.\n", prog); YYERROR; } $$->type = T_MATH; $$->u = $1; } ; copyout : COPYOUT { $$.copyout = malloc((unsigned) (strlen($1) + 1)); if ($$.copyout == NULL) { (void) fprintf(stderr, "%s: malloc failed.\n", prog); YYERROR; } (void) strcpy($$.copyout, $1); } ; seek : count SEEK count { $$.seek.count = $1; $$.seek.direction = $3; } | MATHOP count SEEK count { if ($1 != '-' && $1 != '+') { (void) fprintf(stderr, "%s: invalid seek offset: %c%d\n", prog, $1, $2); YYERROR; } else if ($1 == '-') { $$.seek.count = -$2; } else { $$.seek.count = $2; } $$.seek.direction = $4; } ; math : REG MATHOP count { $$.math.reg_no = mathreg = $1; $$.math.operator = $2; $$.math.operand = $3; } | MATHOP count { if (mathreg == 0) { (void) fprintf(stderr, "%s: Math operator outside math expression\n", prog); YYERROR; } $$.math.reg_no = mathreg; $$.math.operator = $1; $$.math.operand = $2; } | REG U_MATHOP { $$.math.reg_no = mathreg = $1; $$.math.operator = $2; $$.math.operand = 0; } | U_MATHOP { if (mathreg == 0) { (void) fprintf(stderr, "%s: Register print operator w/o register\n", prog); YYERROR; } $$.math.reg_no = mathreg; $$.math.operator = $1; $$.math.operand = 0; } | REG MATHOP INCHAR { $$.math.reg_no = mathreg = $1; $$.math.operator = $2; switch ($3) { case 'C': $$.math.operand = sizeof(char); break; case 'S': $$.math.operand = sizeof(short); break; case 'I': $$.math.operand = sizeof(int); break; case 'L': $$.math.operand = sizeof(long); break; case 'F': $$.math.operand = sizeof(float); break; case 'D': $$.math.operand = sizeof(double); break; default: (void) fprintf(stderr, "%s: illegal math sizing char %c\n", prog, $3); YYERROR; } } | MATHOP INCHAR { if (mathreg == 0) { (void) fprintf(stderr, "%s: Math operator outside math expression\n", prog); YYERROR; } $$.math.reg_no = mathreg; $$.math.operator = $1; switch ($2) { case 'C': $$.math.operand = sizeof(char); break; case 'S': $$.math.operand = sizeof(short); break; case 'I': $$.math.operand = sizeof(int); break; case 'L': $$.math.operand = sizeof(long); break; case 'F': $$.math.operand = sizeof(float); break; case 'D': $$.math.operand = sizeof(double); break; default: (void) fprintf(stderr, "%s: illegal math sizing char %c\n", prog, $2); YYERROR; } } ; iosingle : iocore STORE { $$ = $1; $$.iospec.reg_no = $2; if ( inval4reg($$.iospec.ochar) ) { (void) fprintf(stderr, "%s: Only integer types can be put in a $x register.\n", prog); YYERROR; } } | iocore { $$ = $1; $$.iospec.reg_no = 0; } ; iocore : INCHAR OUTCHAR { $$.iospec = makecore($1, $2); if (badiopair($$.iospec.ichar, $$.iospec.ochar)) { (void) fprintf(stderr, "%s: input size `%c' and output format `%c' cannot go together.\n", prog, $$.iospec.ichar, $$.iospec.ochar); yyerror(""); YYERROR; } } | INCHAR { $$.iospec = makecore($1, 0); if (badiopair($$.iospec.ichar, $$.iospec.ochar)) { (void) fprintf(stderr, "%s: input size `%c' and output format `%c' cannot go together.\n", prog, $$.iospec.ichar, $$.iospec.ochar); yyerror(""); YYERROR; } } | OUTCHAR { $$.iospec = makecore(0, $1); if (badiopair($$.iospec.ichar, $$.iospec.ochar)) { (void) fprintf(stderr, "%s: input size `%c' and output format `%c' cannot go together.\n", prog, $$.iospec.ichar, $$.iospec.ochar); yyerror(""); YYERROR; } } | INCHAR PCTSTRING { $$.iospec = makecorepct($1, $2); } | PCTSTRING { $$.iospec = makecorepct(0, $1); } ; count : NUMBER { $$ = $1 ; } | REG { $$ = 0 - $1; } | EOF_COUNT { $$ = UPTO_EOF ; } ; lexerr : LEXERROR { YYERROR; } ; %% yyerror(s) char *s; { if (s && *s) (void) fprintf(stderr, "%s: %s", prog, s); if (Fmt.p) { int n0, n1, n2; char *nl = strchr(Fmt.line, '\n'); if (Fmt.file) { n0 = Fmt.item - Fmt.line; n1 = ((nl <= Fmt.p) ? nl : Fmt.p + 1) - Fmt.item; n2 = nl ? (nl - Fmt.line - n1 - n0) : 0; if (Fmt.item && Fmt.line) fprintf(stderr, " at line %d:\n\t%.*s >>>%.*s<<< %.*s\n", Fmt.linenum, n0, Fmt.line, n1, Fmt.item, n2, (nl < Fmt.p) ? nl : Fmt.p+1); else fprintf(stderr, " at line %d.\n", Fmt.linenum); } else { if (Fmt.item) fprintf(stderr, "\n%.*s >>>%.*s<<< %s\n", Fmt.item - Fmt.text, Fmt.text, Fmt.p + 1 - Fmt.item, Fmt.item, Fmt.p+1); else putc('\n', stderr); } } } /* Save our current location (assumes we are at a newline) */ void mark_line() { Fmt.linenum++; Fmt.line = Fmt.item = Fmt.p; } yylex() { static char *tmpbuf; /* temporary storage for copyout text */ char *b; char c; if (Fmt.p == NULL) { Fmt.p = Fmt.item = Fmt.line = Fmt.text; Fmt.linenum = 1; tmpbuf = malloc((unsigned) strlen(Fmt.text)); if (tmpbuf == NULL) { (void) fprintf(stderr, "%s: Can't malloc buffer space in yylex().\n", prog); exit(1); } /* Eat leading whitespace */ eatws1: while (isspace(*Fmt.p)) { if (*Fmt.p++ == '\n') mark_line(); } if (*Fmt.p == '#') { /* Comment embedded in format; discard text through newline */ while (*Fmt.p && *Fmt.p != '\n') Fmt.p++; goto eatws1; } /* If the first non-whitespace character is a digit, * then the count is specified; don't process to eof. */ if ( isdigit(*Fmt.p) ) process_to_eof = 0; else process_to_eof = 1; } /* Eat whitespace */ eatws2: while (isspace(*Fmt.p)) { if (*Fmt.p++ == '\n') mark_line(); } if (*Fmt.p == '#') { /* Comment embedded in format; discard text through newline */ while (*Fmt.p && *Fmt.p != '\n') Fmt.p++; goto eatws2; } /* Special processing: support {char short int integer long * float real double} for data sizes {C S I I L F F D}, by replacing the * word with the equivalent single-character form. * Likewise map {seek} to {!}. * Likewise allow "print" for P. */ { #define ifname(longname, shortname) \ if (strncmp(Fmt.p, longname, sizeof(longname)-1) == 0 && \ !isalpha(*(Fmt.p+sizeof(longname)-1))) { \ Fmt.p += sizeof(longname) - 2; \ c = shortname; \ } ifname("char", 'C') else ifname("short", 'S') else ifname("int", 'I') else ifname("long", 'L') else ifname("float", 'F') else ifname("real", 'F') else ifname("double", 'D') else ifname("seek", '!') else ifname("print", 'P') else c = *Fmt.p; } if (debug) fprintf(stderr, "(dbg) fmt = `%s'\n", Fmt.p); /* Now check for usual forms */ switch (c) { case '\0': return 0; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* require count base to be 10; if we allow strtol to interpret * hex, we'll have to interpret 10a (10 ascii) as count=266. */ yylval.lval = strtol(Fmt.p, &b, 10); if (b == Fmt.p) { fprintf(stderr, "%s: severe error: strtol(\"%s\",...) failed\n"); exit(1); } Fmt.p = b; return NUMBER; break; case 'C': case 'S': case 'I': case 'L': case 'F': case 'D': case 'Z': yylval.ival = c; *Fmt.p++; return INCHAR; break; case '~': case 'a': case 'b': case 'c': case 'd': case 'u': case 'o': case 'x': case 'f': case 'g': yylval.ival = *Fmt.p++; return OUTCHAR; break; case 'h': yylval.ival = 'x'; Fmt.p++; return OUTCHAR; break; case '[': for (b = tmpbuf, Fmt.p++; *Fmt.p && *Fmt.p != ']'; ) { if (*Fmt.p == '\n') { (void) fprintf(stderr, "%s: Unterminated output comment.\n", prog); return LEXERROR; } if (*Fmt.p == '\\' && *(Fmt.p+1) == ']') { *b++ = *++Fmt.p; Fmt.p++; } else *b++ = *Fmt.p++; } if (*Fmt.p++ != ']') { (void) fprintf(stderr, "%s: Unterminated output comment.\n", prog); return LEXERROR; } *b = '\0'; yylval.sval = tmpbuf; if (strstr(yylval.sval, "$(")) do_condense=0; /* don't condense list, so that $( gives * a count correct from user's perspective. */ return COPYOUT; break; case ';': ++Fmt.p; return NEWLINE; break; case '\\': ++Fmt.p; switch (*Fmt.p) { case 'n': ++Fmt.p; return NEWLINE; break; default: (void) fprintf(stderr, "%s: unrecognized backslash sequence.\n"); } case 'n': ++Fmt.p; return NEWLINE; break; case '(': case ')': return (*Fmt.p++); break; case '!': ++Fmt.p; return SEEK; break; case '$': yylval.ival = *++Fmt.p; if (*Fmt.p == '\0') { (void) fprintf(stderr, "%s: `$' cannot end the format.\n", prog); return LEXERROR; } Fmt.p++; if (yylval.ival == '$') return EOF_COUNT; /* $$ is special: a count that means up-to-eof */ if (yylval.ival == '(') do_condense=0; /* don't condense list, so that $( gives * a count correct from user's perspective. */ return REG; break; case '>': yylval.ival = *++Fmt.p; if (*Fmt.p == '\0') { (void) fprintf(stderr, "%s: `>' must precede a register character.\n", prog); return LEXERROR; } Fmt.p++; return STORE; break; case '+': case '-': case '*': case '/': case '%': case '=': yylval.ival = *Fmt.p++; return MATHOP; break; case '?': case ':': /* Used to form tests */ return *Fmt.p++; break; case 'P': yylval.ival = *Fmt.p++; return U_MATHOP; break; case '"': /* User-entered format. Terminate it at '"'. */ for (b = tmpbuf, Fmt.p++; *Fmt.p && *Fmt.p != '"'; ) { if (*Fmt.p == '\n') { (void) fprintf(stderr, "%s: Unterminated printf-fmt.\n", prog); return LEXERROR; } if (*Fmt.p == '\\' && *(Fmt.p+1) == '"') { *b++ = *++Fmt.p; Fmt.p++; } else *b++ = *Fmt.p++; } if (*Fmt.p++ != '"') { (void) fprintf(stderr, "%s: Unterminated printf-fmt.\n", prog); return LEXERROR; } *b = '\0'; Fmt.p++; yylval.sval = tmpbuf; return PCTSTRING; break; default: fprintf(stderr, "%s: Invalid character `%c' in format\n", prog, *Fmt.p); return LEXERROR; } /* NOTREACHED */ } int defaultichar(ochar) int ochar; /* Specifies the output character */ { switch (ochar) { case 'a': case 'b': case 'c': case '~': return 'C'; break; case 'd': case 'u': case 'o': case 'h': case 'x': return 'I'; break; case 'f': case 'g': return 'F'; break; default: return '\0'; break; } /* NOTREACHED */ } int defaultochar(ichar) int ichar; /* Specifies the input character */ { switch (ichar) { case 'C': return 'a'; break; case 'Z': return 'a'; break; case 'S': return 'o'; break; case 'I': return 'd'; break; case 'L': return 'd'; break; case 'F': case 'D': return 'g'; break; default: return '\0'; break; } /* NOTREACHED */ } int getsize(ichar) int ichar; { switch (ichar) { case ';': return 0; break; case 'C': case 'Z': return sizeof(char); break; case 'S': return sizeof(short); break; case 'I': return sizeof(int); break; case 'L': return sizeof(long); break; case 'F': return sizeof(float); break; case 'D': return sizeof(double); break; default: return 0; } /* NOTREACHED */ } badiopair(ic, oc) int ic, oc; { /* illegal pairings: [CSILZ]-fg, [FD]-[abduohx], [SILFD]-ac */ if (strchr("fg", oc) && strchr("CSILZ", ic)) return 1; else if (strchr("abduohx", oc) && strchr("FD", ic)) return 1; else if (strchr("ac", oc) && strchr("SILFD", ic)) return 1; else return 0; } inval4reg(oc) int oc; { /* Illegal to use ">x" notation if data are float types */ if (oc == 'g' || oc == 'f') return 1; else return 0; }