1 /* ps_tiny.c -- read .pin files, write short PostScript code
2  * by pts@fazekas.hu at Sat Aug 24 12:46:14 CEST 2002
3  * -- Sat Aug 24 18:23:08 CEST 2002
4  * -- Sat Aug 24 23:47:02 CEST 2002
5  * -- Mon Sep  2 11:28:19 CEST 2002
6  * %<B added -- Thu Sep 19 09:23:00 CEST 2002
7  * v0.02 WORKS for flatedecode.psm -- Sun Sep 22 00:39:54 CEST 2002
8  * DEVEL
9  */
10 
11 /*
12  * Imp: extensive documentation
13  * Imp: verify acount + xcount
14  * Imp: uncompressed m�retellen�rz�s
15  */
16 
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h> /* exit() */
20 
21 #if NO_CONFIG
22   #include <assert.h>
23   #if SIZEOF_INT>=4
24     typedef unsigned slen_t;
25     typedef int slendiff_t;
26   #  define SLEN_P ""
27   #else
28     typedef unsigned long slen_t;
29     typedef long slendiff_t;
30   #  define SLEN_P "l"
31   #endif
32   typedef char sbool;
33   #if ';'!=59 || 'a'!=97
34   #  error ASCII system is required to compile this program.
35   #endif
36 #else
37   #include "config2.h" /* by sam2p... */
38   typedef bool sbool;
39 #endif
40 #if OBJDEP
41 #  warning PROVIDES: ps_tiny_main
42 #endif
43 
44 #define ULE(a,b) (((a)+0U)<=((b)+0U))
45 #define ISWSPACEE(i) ((i)==32 || ULE(i-9,13-9) || i==0)
46 #define ISWSPACE(i,j) ((i j)==32 || ULE(i-9,13-9) || i==0)
47 
48 #define PROGNAME "ps_tiny"
49 #define VERSION "0.02"
50 
51 /* --- Data */
52 
53 #define SBUFSIZE 4096
54 
55 /** Data area holding strings; */
56 char sbuf[SBUFSIZE], *sbufb;
57 
58 #define WORDSSIZE 127
59 
60 /** Pointers inside sbuf, indexed by chars, [0..31] are special */
61 char const* words[WORDSSIZE];
62 
sbuff(void)63 static void sbuff(void) {
64   unsigned i;
65   sbuf[0]='\0';
66   sbufb=sbuf+1;
67   for (i=0;i<WORDSSIZE;i++) words[i]=sbuf;
68 }
69 
70 /** @return -1 or the word found */
findword(char const * word)71 static int findword(char const*word) {
72   unsigned i=32;
73   while (i<WORDSSIZE && 0!=strcmp(word,words[i])) i++;
74   return i==WORDSSIZE ? -1 : (int)i;
75 }
76 
77 /** @return -1 or the word found */
findword_stripslash(char const * word)78 static int findword_stripslash(char const*word) {
79   unsigned i=32;
80   assert(word[0]!='/');
81   while (i<WORDSSIZE && 0!=strcmp(word,words[i]+(words[i][0]=='/'))) i++;
82   return i==WORDSSIZE ? -1 : (int)i;
83 }
84 
85 /* --- Reading */
86 
87 /** Current byte offset in input. (from 0) */
88 slen_t curofs;
89 
90 /** Current line in input. (from 1) */
91 slen_t curline;
92 
93 /** Offset of the leftmost char of current line in input. (from 0) */
94 slen_t leftofs;
95 
96 /** Current position in input override format string */
97 char const* ifmt;
98 
99 /** Input override string list */
100 char const* const* ilist;
101 
102 /** Current position in current item of ilist */
103 char const* iitem;
104 
105 /** Char unget */
106 int ilast;
107 
rewindd(void)108 static void rewindd(void) { ifmt=0; curofs=leftofs=0; curline=1; }
109 
setifmt(char const * ifmt_,char const * const * ilist_)110 static void setifmt(char const *ifmt_, char const* const* ilist_) {
111   ifmt=ifmt_;
112   ilist=ilist_;
113   iitem="";
114   ilast=-1;
115 }
116 
noifmt(void)117 static void noifmt(void) { ifmt=0; }
118 
getcc(void)119 static int getcc(void) {
120   int c;
121   if (ifmt!=0) {
122    nextitem:
123     if (ilast>=0) { c=ilast; ilast=-1; }
124     else if (iitem[0]!='\0') c=*iitem++;
125     else if (ifmt[0]=='$') { iitem=*ilist++; ifmt++; goto nextitem; }
126     else if (ifmt[0]=='\0') c=-1; /* EOF forever */
127     else c=*ifmt++;
128     /* fprintf(stderr, "igetcc()='%c'\n", c); */
129   } else {
130     if ((c=getchar())=='\n') { leftofs=curofs; curline++; }
131     curofs++;
132   }
133   return c;
134 }
135 
ungetcc(int c)136 static void ungetcc(int c) {
137   if (ifmt!=0) {
138     assert(ilast<0);
139     ilast=c;
140   } else {
141     if (c=='\n') curline--;
142     if (c>=0) { curofs--; ungetc(c,stdin); }
143   }
144 }
145 
erri(char const * msg1,char const * msg2)146 static void erri(char const*msg1, char const*msg2) {
147   fprintf(stderr, "%s: error at %" SLEN_P "u.%" SLEN_P "u.%" SLEN_P "u: %s%s\n",
148     PROGNAME, curline, curofs-leftofs+1, curofs, msg1, msg2?msg2:"");
149   exit(3);
150 }
151 
setword(int c,char const * word,int slash)152 static void setword(int c, char const*word, int slash) {
153   slen_t len;
154   assert(slash==0 || slash==1);
155   if (words[c][0]!='\0') erri("abbr letter already used",0);
156   if (sbuf+SBUFSIZE-sbufb+(slen_t)0<(len=strlen(word))+slash+1U) erri("words overflow",0);
157   /* fprintf(stderr,"setword(%d,\"%s\");\n", c, word); */
158   words[c]=sbufb;
159   if (slash) *sbufb++='/';
160   strcpy(sbufb, word);
161   sbufb+=len+1;
162   /* assert(c<3 || findword(word)==c); */
163 }
164 
165 /** So a long string from ps_tiny --copy <bts2.ttt will fit */
166 /* #define IBUFSIZE 512 */
167 #define IBUFSIZE 32000
168 
169 /** Input buffer for several operations. */
170 char ibuf[IBUFSIZE];
171 /** Position after last valid char in ibuf */
172 char *ibufb;
173 
174 typedef slendiff_t psint_t;
175 
176 #if 0
177 static psint_t getint(void) {
178   int c, sgn=1;
179   psint_t v, w;
180   while (ISWSPACE(c,=getcc())) ;
181   if (c=='-') { c=getcc(); sgn=-1; }
182   if (!ULE(c-'0','9'-'0')) erri("integer expected",0); /* Dat: EOF err */
183   v=sgn*(c-'0');
184   while (ULE((c=getcc())-'0','9'-'0')) {
185     if ((w=10*v+sgn*(c-'0'))/10!=v) erri("integer overflow",0);
186     /* ^^^ Dat: overflow check is ok between -MAX-1 .. MAX */
187     v=w;
188   }
189   ungetcc(c);
190   return v;
191 }
192 
193 static psint_t getuint(void) {
194   psint_t v=getint();
195   if (v<0) erri("nonnegative integer expected",0);
196   return v;
197 }
198 #endif
199 
is_ps_white(int c)200 static /*inline*/ sbool is_ps_white(int/*char*/ c) {
201   return c=='\n' || c=='\r' || c=='\t' || c==' ' || c=='\f' || c=='\0';
202 }
203 
is_ps_name(int c)204 static /*inline*/ sbool is_ps_name(int/*char*/ c) {
205   /* Dat: we differ from PDF since we do not treat the hashmark (`#') special
206    *      in names.
207    * Dat: we differ from PostScript since we accept names =~ /[!-~]/
208    */
209   return c>='!' && c<='~'
210       && c!='/' && c!='%' && c!='{' && c!='}' && c!='<' && c!='>'
211       && c!='[' && c!=']' && c!='(' && c!=')';
212   /* Dat: PS avoids: /{}<>()[]% \n\r\t\000\f\040 */
213 }
214 
215 /** Definition chosen rather arbitrarily by pts */
is_wordx(char const * s)216 static sbool is_wordx(char const *s) {
217   if (!ULE(*s-'A','Z'-'A') && !ULE(*s-'a','z'-'a') && *s!='.') return 0;
218    /* && !ULE(*s-'0','9'-'0') && *s!='-') return 0; */
219   while (*++s!='\0') if (!is_ps_name(*s)) return 0;
220   return 1;
221 }
222 
223 /** @param b: assume null-terminated @return true on error */
toInteger(char * s,psint_t * ret)224 static /*inline*/ sbool toInteger(char *s, psint_t *ret) {
225   int n=0; /* BUGFIX?? found by __CHECKER__ */
226   return sscanf(s, "%" SLEN_P "i%n", ret, &n)<1 || s[n]!='\0';
227 }
228 
229 /** @param b: assume null-terminated @return true on error */
toReal(char * s,double * ret)230 static /*inline*/ sbool toReal(char *s, double *ret) {
231   int n;
232   char c;
233   /* Dat: glibc accepts "12e", "12E", "12e+" and "12E-" */
234   return sscanf(s, "%lf%n", ret, &n)<1
235       || (c=s[n-1])=='e' || c=='E' || c=='+' || c=='-' || s[n]!='\0';
236 }
237 
238 /** Returns a PostScript token ID, puts token into buf */
gettok(void)239 static char gettok(void) {
240   /* Derived from MiniPS::Tokenizer::yylex() of sam2p-0.37 */
241   int c=0; /* dummy initialization */
242   sbool hi;
243   unsigned hv=0; /* =0: pacify G++ 2.91 */
244   slen_t nest;
245   char *ibufend=ibuf+IBUFSIZE;
246   ibufb=ibuf;
247 
248 #if 0
249   if (ungot==EOFF) return EOFF;
250   if (ungot!=NO_UNGOT) { c=ungot; ungot=NO_UNGOT; goto again; }
251 #endif
252  again_getcc:
253   c=getcc();
254  /* again: */
255   switch (c) {
256    case -1: eof:
257     return 0; /*ungot=EOFF */;
258    case '\n': case '\r': case '\t': case ' ': case '\f': case '\0':
259     goto again_getcc;
260    case '%': /* one-line comment */
261     if ((c=getcc())=='<') { /* XMLish tag */
262       char ret='<';
263       if ((c=getcc())=='/') { ret='>'; c=getcc(); } /* close tag */
264       if (!ULE(c-'A','Z'-'A')) erri("invalid tag",0); /* catch EOF */
265       (ibufb=ibuf)[0]=c; ibufb++;
266       while (ULE((c=getcc())-'A','Z'-'A') || ULE(c-'a','z'-'a')) {
267         if (ibufb==ibufend-1) erri("tag too long",0);
268         *ibufb++=c;
269       }
270       if (c<0) erri("unfinished tag",0);
271       *ibufb='\0';
272       ungetcc(c);
273       return ret;
274     }
275     while (c!='\n' && c!='\r' && c!=-1) c=getcc();
276     if (c==-1) goto eof;
277     goto again_getcc;
278    case '{': case '[':
279     *ibufb++=c;
280     return '[';
281    case '}': case ']':
282     *ibufb++=c;
283     return ']';
284    case '>':
285     if (getcc()!='>') goto err;
286     *ibufb++='>'; *ibufb++='>';
287     return ']';
288    case '<':
289     if ((c=getcc())==-1) { uf_hex: erri("unfinished hexstr",0); }
290     if (c=='<') {
291       *ibufb++='<'; *ibufb++='<';
292       return '[';
293     }
294     if (c=='~') erri("a85str unsupported",0);
295     hi=1;
296     while (c!='>') {
297            if (ULE(c-'0','9'-'0')) hv=c-'0';
298       else if (ULE(c-'a','f'-'a')) hv=c-'a'+10;
299       else if (ULE(c-'A','F'-'A')) hv=c-'A'+10;
300       else if (is_ps_white(c)) hv=16;
301       else erri("syntax error in hexstr",0);
302       if (hv==16) ;
303       else if (!hi) { ibufb[-1]|=hv; hi=1; }
304       else if (ibufb==ibufend) erri("hexstr literal too long",0);
305       else { *ibufb++=(char)(hv<<4); hi=0; }
306       if ((c=getcc())==-1) goto uf_hex;
307     }
308     /* This is correct even if an odd number of hex digits have arrived */
309     return '(';
310    case '(':
311     nest=1;
312     while ((c=getcc())!=-1) {
313       if (c==')' && --nest==0) return '(';
314       if (c!='\\') { if (c=='(') nest++; }
315       else switch (c=getcc()) { /* read a backslash escape */
316        case -1: goto uf_str;
317        case 'n': c='\n'; break;
318        case 'r': c='\r'; break;
319        case 't': c='\t'; break;
320        case 'b': c='\010'; break; /* \b and \a conflict between -ansi and -traditional */
321        case 'f': c='\f'; break;
322        default:
323         if (!ULE(c-'0','7'-'0')) break;
324         hv=c-'0'; /* read at most 3 octal chars */
325         if ((c=getcc())==-1) goto uf_str;
326         if (c<'0' || c>'7') { ungetcc(c); c=hv; }
327         hv=8*hv+(c-'0');
328         if ((c=getcc())==-1) goto uf_str;
329         if (c<'0' || c>'7') { ungetcc(c); c=hv; }
330         c=(char)(8*hv+(c-'0'));
331       } /* SWITCH */
332       if (ibufb==ibufend) erri("str literal too long",0);
333       /* putchar(c); */
334       *ibufb++=c;
335     } /* WHILE */
336     /* if (c==')') return '('; */
337     uf_str: erri("unfinished str",0);
338    case ')': goto err;
339    case '/':
340     *ibufb++='/';
341     while (ISWSPACE(c,=getcc())) ;
342     /* ^^^ `/ x' are two token in PostScript, but here we overcome the C
343      *     preprocessor's feature of including whitespace.
344      */
345     /* fall-through, b will begin with '/' */
346    default: /* /nametype, /integertype or /realtype */
347     *ibufb++=c;
348     while ((c=getcc())!=-1 && is_ps_name(c)) {
349       if (ibufb==ibufend) erri("token too long",0);
350       *ibufb++=c;
351     }
352     ungetcc(c);
353     if (ibuf[0]=='/') return '/';
354     /* Imp: optimise numbers?? */
355     if (ibufb!=ibufend) {
356       psint_t l;
357       double d;
358       *ibufb='\0';
359       /* Dat: we don't support base-n number such as `16#100' == 256 in real PostScript */
360       if (!toInteger(ibufb, &l)) return '1'; /* throw away integer value */
361       /* Dat: call toInteger _before_ toReal */
362       if (!toReal(ibufb, &d)) return '.';
363     }
364     return 'E'; /* executable /nametype */
365   }
366  err:
367   erri("syntax error, token expected",0);
368   goto again_getcc; /* notreached */
369 }
370 
getotag(char const * tag)371 static void getotag(char const*tag) {
372 #if 0 /* This code segment cannot ignore comments */
373   char const *p=tag;
374   int c;
375   while (ISWSPACE(c,=getcc())) ;
376   if (c!='%' || (c=getcc())!='<') erri("tag expected: ", tag);
377   while (ISWSPACE(c,=getcc())) ;
378   while (p[0]!='\0') {
379     if (c!=*p++) erri("this tag expected: ", tag);
380     c=getcc();
381   }
382   ungetcc(c);
383 #else
384   if (gettok()!='<' || 0!=strcmp(ibuf,tag)) erri("tag expected: ", tag);
385 #endif
386 }
387 
gettagbeg(void)388 static void gettagbeg(void) {
389   int c;
390   while (ISWSPACE(c,=getcc())) ;
391   if (c!='>') erri("`>' expected",0);
392 }
393 
gettagend(void)394 static void gettagend(void) {
395   int c;
396   while (ISWSPACE(c,=getcc())) ;
397   if (c!='/') erri("`/>' expected",0);
398   while (ISWSPACE(c,=getcc())) ;
399   if (c!='>') erri("`/>' expected",0);
400 }
401 
getkey(char const * key)402 static void getkey(char const *key) {
403   char const *p=key;
404   int c;
405   while (ISWSPACE(c,=getcc())) ;
406   while (p[0]!='\0') {
407     if (c!=*p++) erri("this key expected: ", key);
408     c=getcc();
409   }
410   while (ISWSPACEE(c)) c=getcc();
411   if (c!='=') erri("key `=' expected", 0);
412 }
413 
414 
415 /** Loads a value into ibuf, '\0'-terminated. */
getval(void)416 static void getval(void) {
417   sbool g=1;
418   char *ibufend1=ibuf+IBUFSIZE-1, c;
419   ibufb=ibuf;
420   while (ISWSPACE(c,=getcc())) ;
421   while (1) {
422     if (c=='"') g=!g;
423     else if (g && (ISWSPACEE(c) || c=='/' || c=='>')) { ungetcc(c); break; }
424     else if (c<0) erri("unfinished tag val",0);
425     else if (c==0) erri("\\0 disallowed in tag val",0);
426     else if (ibufb==ibufend1) erri("tag val too long",0);
427     else *ibufb++=c;
428     c=getcc();
429   } /* WHILE */
430   *ibufb='\0';
431 }
432 
getuintval(void)433 static psint_t getuintval(void) {
434   psint_t ret;
435   getval();
436   /* fprintf(stderr, "[%s]\n", ibuf); */
437   if (toInteger(ibuf, &ret) || ret<0) erri("tag val must be nonnegative integer",0);
438   return ret;
439 }
440 
441 /* --- Writing */
442 
443 /** Maximum number of characters in a line. */
444 #define MAXLINE 78
445 
446 /** Number of characters already written into this line. (from 0) */
447 slen_t wcolc;
448 
449 /** Last token was a self-closing one */
450 sbool wlastclosed;
451 
prepare(void)452 static void prepare(void) { wcolc=0; wlastclosed=1; }
453 
newline(void)454 static void newline(void) {
455   if (wcolc!=0) {
456     putchar('\n');
457     wcolc=0; wlastclosed=1;
458   } else assert(wlastclosed);
459 }
460 
461 /** 2: "\\n"; 1: "\n" */
462 static int pstrq_litn_pp=2;
463 
464 /** @return the byte length of a string as a quoted PostScript ASCII string
465  * literal
466  */
pstrqlen(register char const * p,char const * pend)467 static slen_t pstrqlen(register char const* p, char const* pend) {
468   slen_t olen=2; /* '(' and ')' */
469   char c;
470   p=ibuf; pend=ibufb;  while (p!=pend) {
471     if ((c=*(unsigned char const*)p++)=='\r' || c=='\t' || c=='\010' || c=='\f' || c=='(' || c==')') olen+=2;
472     else if (c=='\n') olen+=pstrq_litn_pp;
473     else if (c>=32 && c<=126) olen++;
474     else if (c>=64 || p==pend || ULE(*p-'0','7'-'0')) olen+=4;
475     else if (c>=8) olen+=3;
476     else olen+=2;
477   }
478   return olen;
479 }
480 
481 /** Prints the specified string as a quoted PostScript ASCII string literal.
482  * Does not modify wcolc etc.
483  */
pstrqput(register char const * p,char const * pend)484 static void pstrqput(register char const* p, char const* pend) {
485   char c;
486   putchar('(');
487   p=ibuf; pend=ibufb;  while (p!=pend) {
488     if ((ULE((c=*(unsigned char const*)p++)-32, 126-32) && c!='(' && c!=')')
489      || (c=='\n' && pstrq_litn_pp==1)
490        ) { putchar(c); continue; }
491     putchar('\\');
492          if (c=='\n')   putchar('n');
493     else if (c=='\r')   putchar('r');
494     else if (c=='\t')   putchar('t');
495     else if (c=='\010') putchar('b');
496     else if (c=='\f')   putchar('f');
497     else if (c=='(')    putchar('('); /* BUGFIX at Tue Feb 22 00:22:19 CET 2005 */
498     else if (c==')')    putchar(')');
499     else if (c>=64 || p==pend || ULE(*p-'0','7'-'0')) {
500       putchar((c>>6&7)+'0');
501       putchar((c>>3&7)+'0');
502       putchar((   c&7)+'0');
503     } else if (c>=8) {
504       putchar((c>>3)  +'0');
505       putchar((   c&7)+'0');
506     } else putchar(c+'0');
507   }
508   putchar(')');
509 }
510 
511 static int copy_longstr_warn_p=1;
512 
513 /** Copies PostScript code from input to output. Strips comments, superfluous
514  * whitespace etc., substitutes words etc.
515  * @param tag may be NULL, this signals: copy till EOF, doesn't substitute
516  *   words
517  */
copy(char const * tag)518 static void copy(char const*tag) {
519   int c;
520   slen_t len, olen;
521   while (1) switch (gettok()) {
522    case 0:
523     if (tag) erri("eof before close tag: ", tag);
524     return;
525    case '[': case ']':
526     if (wcolc+(len=ibufb-ibuf)>MAXLINE) newline();
527     wlastclosed=1;
528    write:
529     if (len>MAXLINE) fprintf(stderr, "%s: warning: output line too long\n", PROGNAME);
530     fwrite(ibuf, 1, len, stdout); wcolc+=len;
531     break;
532    case '/':
533     if ((len=ibufb-ibuf)<IBUFSIZE && tag && !(*ibufb='\0') && (c=findword(ibuf))>=0) {
534       ibuf[0]='/';
535       ibuf[1]=c;
536       ibufb=ibuf+2;
537       len=2;
538     }
539     if (wcolc+len>MAXLINE) newline();
540     wlastclosed=0;
541     goto write;
542    case '<': erri("tag unexpected",0);
543    case '>':
544     if (!tag) erri("close tag unexpected",0);
545     if (strlen(tag)!=(len=ibufb-ibuf) || 0!=memcmp(ibuf,tag,len)) erri("this close tag expected: ", tag);
546     /* wlastclosed is left unmodified */
547     gettagbeg();
548     return;
549    case '(':
550     olen=pstrqlen(ibuf,ibufb);
551     if (wcolc+olen>MAXLINE) newline();
552     if (olen>MAXLINE && copy_longstr_warn_p) fprintf(stderr, "%s: warning: output string too long\n", PROGNAME);
553     /* putchar(ibuf[1]); */
554     wcolc+=olen; pstrqput(ibuf,ibufb);
555     wlastclosed=1;
556     break;
557    default: /* case '1': case '.': case 'E': */
558     /* fprintf(stderr,"fw(%s) %c\n", ibuf, findword("32768")); */
559     if ((len=ibufb-ibuf)<IBUFSIZE && tag && !(*ibufb='\0') && (c=findword_stripslash(ibuf))>=0) {
560       ibuf[0]=c;
561       ibufb=ibuf+1;
562       len=1;
563     }
564     if (wcolc+len+!wlastclosed>MAXLINE) newline();
565     else if (!wlastclosed) { putchar(' '); wcolc++; }
566     wlastclosed=0;
567     goto write;
568   }
569 }
570 
571 /** Prefix chars: 32..41 ' '..')' */
cprefix(slen_t olen)572 static void cprefix(slen_t olen) {
573   slen_t plen;
574   if (0==olen) erri("empty token","");
575   if (olen>11) erri("compressed token too long (>11)","");
576   plen=olen+(olen>1)+(olen==7);
577   if (wcolc+(wcolc==0)+plen>MAXLINE) newline();
578   if (wcolc==0) { putchar('%'); wcolc=1; }
579   if (plen>=MAXLINE) fprintf(stderr, "%s: warning: output line too long\n", PROGNAME);
580   if (olen==7) { putchar(8+30); putchar(' '); wcolc+=2; } /* Dat: ASCII 37=='%' */
581   else if (olen>1) { putchar(olen+30); wcolc++; }
582 }
583 
584 /** Copies PostScript code from input to output. Strips comments, superfluous
585  * whitespace etc., substitutes words etc. Output is not valid PostScript code,
586  * but it needs decompression (i.e adding spaces to words). Ignores var
587  * `wlastclosed' (sets it to 1 by calling newline()).
588  */
compress0(char const * tag)589 static void compress0(char const*tag) {
590   int c;
591   slen_t len, olen;
592   newline();
593   while (1) switch (gettok()) {
594    case 0: erri("eof before close tag: ", tag);
595    case '[': case ']':
596     len=ibufb-ibuf;
597    write:
598     cprefix(len);
599     fwrite(ibuf, 1, len, stdout); wcolc+=len;
600     break;
601    case '/':
602     if ((len=ibufb-ibuf)<IBUFSIZE && !(*ibufb='\0') && (c=findword(ibuf))>=0) {
603       /* erri("possibly stupid redef: ", ibuf); */
604       ibuf[0]='/';
605       ibuf[1]=c;
606       ibufb=ibuf+2;
607       len=2;
608     }
609     goto write;
610    case '<': erri("tag unexpected",0);
611    case '>':
612     if (strlen(tag)!=(len=ibufb-ibuf) || 0!=memcmp(ibuf,tag,len)) erri("this close tag expected: ", tag);
613     /* wlastclosed is left unmodified */
614     fputs((wcolc+2>MAXLINE) ? "\n%   %%" : "%%", stdout);
615     newline();
616     gettagbeg();
617     return;
618    case '(':
619     cprefix(olen=pstrqlen(ibuf,ibufb));
620     wcolc+=olen; pstrqput(ibuf,ibufb);
621     break;
622    default: /* case '1': case '.': case 'E': */
623     if ((len=ibufb-ibuf)<IBUFSIZE && !(*ibufb='\0') && (c=findword_stripslash(ibuf))>=0) {
624       ibuf[0]=c;
625       ibufb=ibuf+1;
626       len=1;
627     }
628     goto write;
629   }
630 }
631 
632 /** by pts@fazekas.hu at Fri Sep  6 11:34:57 CEST 2002. Copies data from stdin
633  * to stdout till close tag %</tag>. (Note that it isn't safe to copy data of
634  * the ASCII85Encode filter this way
635  */
copydata(char const * tag)636 static void copydata(char const*tag) {
637   int c;
638   while (ISWSPACE(c,=getcc())) ;
639   /* ungetcc(c); */
640   while (1) {
641     if (c<0) erri("missing close tag: ", tag);
642     if (c!='%') { putchar(c); c=getcc(); continue; }
643     if ((c=getcc())=='<') {
644       if ((c=getcc())=='/') {
645         while (tag[0]!='\0') {
646           if ((c=getcc())!=*tag++) erri("this close tag expected: ", tag);
647         }
648         while (ISWSPACE(c,=getcc())) ;
649         if (c!='>') erri("`>' expected", 0);
650         break;
651       } else {
652         putchar('%');
653         putchar('<');
654       }
655     } else {
656       putchar('%');
657     }
658   }
659 }
660 
661 /* --- Main */
662 
main(int argc,char ** argv)663 int main(int argc, char**argv) {
664   slen_t acount, xcount, inlining;
665   char tmp[40];
666   (void)argc;
667 
668   /* freopen("t.pin","rb",stdin); */
669 
670   sbuff();
671   rewindd();
672   prepare();
673   if (argv[0] && argv[1] && 0==strcmp(argv[1],"--copy")) {
674     copy_longstr_warn_p=0;
675     pstrq_litn_pp=1;
676     copy((char const*)0);
677     return 0;
678   }
679 
680   { char tok=gettok();
681     slen_t len;
682     len=ibufb-ibuf;
683     if (tok=='<' && len==4 && 0==memcmp(ibuf, "Head", 4)) {
684       gettagbeg();
685       copydata("Head");
686       getotag("Open");
687     } else if (tok=='<' && len==4 && 0==memcmp(ibuf, "Open", 4)) {
688     } else erri("tag %<Head or %<Open expected",0);
689   }
690   gettagbeg();
691   copy("Open");
692 
693   getotag("Abbr");
694   getkey("acount");  acount=getuintval();
695   getkey("xcount");  xcount=getuintval();
696   sprintf(tmp,"%" SLEN_P "u dict%%</I>", acount+xcount);
697   setifmt(tmp,0); copy("I"); noifmt();
698   gettagbeg();
699 
700 #define E_RANGE 3
701 #define E_SYNTAX 4
702 #define E_COMPRESS 5
703   copy("Abbr");
704   setword(E_RANGE, ")range", 0);
705   setword(E_SYNTAX, ")syntax", 0);
706   setword(E_COMPRESS, ")compress", 0);
707   { int c;
708     for (c=7;c<32;c++) words[c]=words[E_RANGE];
709     for (c=32;c<42;c++) words[c]=words[E_COMPRESS];
710     for (c=47;c<58;c++) words[c]=words[E_SYNTAX]; /* Name:'/', Number:'0'..'9' */
711     words[0U+'<']=words[0U+'>']= /* Hex */
712       words[0U+'[']=words[0U+']']= /* Bracket */
713       words[0U+'{']=words[0U+'}']= /* Brace */
714       words[E_SYNTAX];
715     for (c=127;c<WORDSSIZE;c++) words[c]=words[E_RANGE];
716   }
717 
718   { char tok;
719     slen_t len;
720     int c;
721     char ct[2];
722     char const* ilist_[3];
723     psint_t i;
724     sbool g;
725     while (1) {
726       tok=gettok(); len=ibufb-ibuf;
727       if (tok=='<' && len==1 && 0==memcmp(ibuf, "A", 1)) {
728         while (ISWSPACE(c,=getcc())) ;
729         if (c<32 || c>=WORDSSIZE) erri("invalid %<A letter",0);
730         ct[0]=c; ct[1]='\0'; ilist_[0]=ct;
731         getval();
732         if (is_wordx(ibuf)) {
733           setword(c, ibuf, 0);
734           ilist_[1]=words[c]; words[c]=sbuf; /* temp hide */
735           ilist_[2]=words[0][0]=='\0' ? "load def" : words[0];
736           setifmt("/$ /$ $%</I>", ilist_);
737           if (words[1][0]=='\0' && 0==strcmp(ibuf,"def")) setword(1, ct, 0);
738         } else {
739           g=toInteger(ibuf, &i); /* need braces? */
740           if (g) { words[2]=sbuf; c=2; }
741           setword(c, ibuf, 0); /* temp save */
742           ilist_[1]=words[c]; words[c]=sbuf; /* temp hide */
743           ilist_[2]=words[1][0]=='\0' ? "def" : words[1];
744           setifmt(g ? "/$ { $\n} $%</I>" : "/$ $\n $%</I>", ilist_);
745           if (words[0][0]=='\0' && 0==strcmp(ibuf,"load def")) setword(0, ct, 0);
746         }
747         copy("I");
748         noifmt();
749         words[c]=ilist_[1];
750       } else if (tok=='<' && len==1 && 0==memcmp(ibuf, "D", 1)) {
751         /* define both /x and x */
752         while (ISWSPACE(c,=getcc())) ;
753         if (c<32 || c>=WORDSSIZE) erri("invalid %<D letter",0);
754         ct[0]=c; ct[1]='\0'; ilist_[0]=ct;
755         getval();
756         if (!is_wordx(ibuf)) erri("body for %<D must be a single word",0);
757         setword(c, ibuf, 1);
758       } else if (tok=='<' && len==7 && 0==memcmp(ibuf, "TokSubs", 7)) {
759         break;
760       } else erri("tag %<TokSubs or %<A or %<D expected",0);
761       gettagend();
762     }
763     words[2]=sbuf; /* Imp: free chars */
764   }
765 
766   getkey("name"); getval(); setword(2, ibuf, 0);
767   if (ibufb-ibuf==1 && words[(unsigned char)ibuf[0]]!=sbuf) erri("abbr used as %<TokSubs name= : ",ibuf);
768   getkey("inlining"); inlining=getuintval();
769   gettagend();
770   { char const* ilist_[2]; ilist_[0]=ilist_[1]=words[2];
771     setifmt(inlining!=0 ? "/${mark exch{dup xcheck{dup type/arraytype eq{$}{dup type/nametype eq{dup currentdict exch known{load dup dup type/arraytype eq exch xcheck and{$ aload pop}if}if}if}ifelse}if}forall counttomark array astore exch pop cvx}bind def%</I>"
772     :"/$ {\n"
773        "mark exch{\n"
774          "dup xcheck{\n"
775            "dup type/arraytype eq{$}{\n"
776              "dup type/nametype eq{\n"
777                "dup currentdict exch known {load}if\n"
778              "}if\n"
779            "}ifelse\n"
780          "}if\n"
781        "}forall\n"
782        "counttomark array astore exch pop cvx\n"
783      "} bind def%</I>", ilist_);
784     copy("I"); noifmt();
785   }
786 
787   getotag("Test");
788   gettagbeg();
789   copy("Test");
790 
791   getotag("S");
792   getkey("len");  (void)getuintval(); /* Imp: verify slen for overruns */
793   gettagbeg();
794   setifmt("{%</I>",0); copy("I"); noifmt(); /*}*/
795   copy("S");
796 
797   { char const* ilist_[2]; ilist_[0]=ilist_[1]=words[2];
798     setifmt(
799       "0\n"
800       "{ % Stack: <dst-str> <i>\n"
801       "  currentfile read pop\n"
802       "  dup 37 eq{% '%'\n"
803       "    pop currentfile read pop\n"
804       "    dup 37 eq{pop pop exit}if % exit when '%%'\n"
805       "  }if\n"
806       "  % Stack: <dst-str> <i> <char-read>\n"
807       "  dup 42 lt{\n"
808       "    30 sub\n"
809       "    % Stack: <dst-str> <i> <repeat-count>\n"
810       "    dup 0 gt{\n"
811       "      3 copy getinterval\n"
812       "      % Stack: <dst-str> <i> <repeat-count> <sub-dst-str>\n"
813       "      currentfile exch readstring pop pop\n"
814       "      % Stack: <dst-str> <i> <repeat-count>\n"
815       "      add 1 add\n"
816       "    }{pop}ifelse % skip newlines, tabs etc.\n"
817       "  }{\n"
818       "    3 copy put\n"
819       "    pop 2 add % \\0 as space\n"
820       "  }ifelse\n"
821       "  % Stack: <dst-str> <new-i>\n"
822       "}$ bind loop\n"
823       "% Stack: <dst-str>\n"
824       "cvx exec $ bind exec\n" /* CONST, VAR �s CODE futtat�sa */
825       "currentfile token pop pop\n" /* A %%BeginData ut�ni exec beolvas�sa */
826       "%</I>\n", ilist_);
827     copy("I"); noifmt();
828   }
829 
830   getotag("True");
831   gettagbeg();
832   copy("True");
833 
834   setifmt("}{currentfile token pop pop%</I>",0);
835   copy("I"); noifmt();
836 
837   getotag("False");
838   gettagbeg();
839   copy("False");
840 
841   setifmt("}ifelse%</I>",0);
842   copy("I"); noifmt();
843 
844   getotag("Defs");
845   gettagbeg();
846   compress0("Defs");
847 
848   { char tok=gettok();
849     slen_t len;
850     len=ibufb-ibuf;
851     if (tok=='<' && len==4 && 0==memcmp(ibuf, "Data", 4)) {
852       gettagbeg();
853       copydata("Data");
854     } else if (tok=='<' && len==4 && 0==memcmp(ibuf, "Fini", 4)) {
855       fputs("%%BeginData:\nexec\n`S\n%%EndData\n",stdout);
856       getotag("Fini");
857       gettagbeg();
858       copy("Fini");
859       newline();
860       fputs("%%Trailer\n%%EOF\n", stdout);
861     } else erri("tag %<Data or %<Fini expected",0);
862   }
863 
864   if (gettok()!=0) erri("premature EOF",0);
865 
866   return 0;
867 }
868