1 /*
2  * minips.cpp
3  * by pts@fazekas.hu at Sat Mar  9 21:33:35 CET 2002
4  */
5 
6 #ifdef __GNUC__
7 #ifndef __clang__
8 #pragma implementation
9 #endif
10 #endif
11 
12 #include "minips.hpp"
13 #include "error.hpp"
14 #include "gensio.hpp"
15 #if USE_DICT_MAPPING
16 #if OBJDEP
17 #  warning REQUIRES: mapping.o
18 #endif
19 #include "mapping.hpp"
20 #endif
21 #include <stdio.h> /* sscanf() */
22 #include <string.h> /* memset() */
23 
is_ps_white(char c)24 static inline bool is_ps_white(char c) {
25   return c=='\n' || c=='\r' || c=='\t' || c==' ' || c=='\f' || c=='\0';
26 }
27 
is_ps_name(char c)28 static inline bool is_ps_name(char c) {
29   /* Dat: we differ from PDF since we do not treat the hashmark (`#') special
30    *      in names.
31    * Dat: we differ from PostScript since we accept names =~ /[!-~]/
32    */
33   return c>='!' && c<='~'
34       && c!='/' && c!='%' && c!='{' && c!='}' && c!='<' && c!='>'
35       && c!='[' && c!=']' && c!='(' && c!=')';
36   /* Dat: PS avoids: /{}<>()[]% \n\r\t\000\f\040 */
37 }
38 
39 /** @param b: assume null-terminated @return true on erro
40  * @return false on error
41  */
toInteger(SimBuffer::Flat const & b,signed long & ret)42 static inline bool toInteger(SimBuffer::Flat const&b, signed long &ret) {
43   int n=0; /* BUGFIX?? found by __CHECKER__ */
44   // b.term0();
45   return sscanf(b(), "%li%n", &ret, &n)<1 || b[n]!='\0';
46 }
47 
toHex(char const * s,unsigned long & ret)48 static inline bool toHex(char const*s, unsigned long &ret) {
49   int n=0;
50   return sscanf(s, "%lx%n", &ret, &n)<1 || s[n]!='\0';
51 }
52 
toHex3(char const * s,char ret[3])53 static inline bool toHex3(char const*s, char ret[3]) {
54   unsigned long l;
55   if (toHex(s, l)) return true;
56   ret[0]=((l>>8)&15)*17; ret[1]=((l>>4)&15)*17; ret[2]=(l&15)*17;
57   return false;
58 }
59 
toHex6(char const * s,char ret[3])60 static inline bool toHex6(char const*s, char ret[3]) {
61   unsigned long l;
62   if (toHex(s, l)) return true;
63   ret[0]=(l>>16)&255; ret[1]=(l>>8)&255; ret[2]=l&255;
64   return false;
65 }
66 
67 /** @param b: assume null-terminated @return true on error */
toReal(SimBuffer::Flat const & b,double & ret)68 static inline bool toReal(SimBuffer::Flat const&b, double &ret) {
69   int n;
70   char c;
71   // b.term0();
72   /* Dat: glibc accepts "12e", "12E", "12e+" and "12E-" */
73   return sscanf(b(), "%lf%n", &ret, &n)<1
74       || (c=b[n-1])=='e' || c=='E' || c=='+' || c=='-' || b[n]!='\0';
75 }
76 
77 /** This is not correct if blen cuts the real number into two strings.
78  * @param b: assume null-terminated @return true on error
79  */
toReal(char const * b,slen_t blen,double & ret)80 static inline bool toReal(char const *b, slen_t blen, double &ret) {
81   int n;
82   char c;
83   // b.term0();
84   /* Dat: glibc accepts "12e", "12E", "12e+" and "12E-" */
85   return sscanf(b, "%lf%n", &ret, &n)<1
86       || (c=b[n-1])=='e' || c=='E' || c=='+' || c=='-' || (slen_t)n!=blen;
87 }
88 
Tokenizer(GenBuffer::Readable & in_)89 MiniPS::Tokenizer::Tokenizer(GenBuffer::Readable& in_): in(in_), ungot(NO_UNGOT) {
90 }
yylex()91 int MiniPS::Tokenizer::yylex() {
92   int c=0; /* dummy initialization */
93   bool hi;
94   unsigned hv;
95   slen_t nest, len;
96   signed long l;
97   double d;
98   Real::metric_t metric;
99   char saved;
100 
101   if (ungot==EOFF) return EOFF;
102   if (ungot!=NO_UNGOT) { c=ungot; ungot=NO_UNGOT; goto again; }
103  again_getcc:
104   c=in.vi_getcc();
105  again:
106   switch (c) {
107    case -1: eof:
108     return ungot=EOFF;
109    case '\n': case '\r': case '\t': case ' ': case '\f': case '\0':
110     goto again_getcc;
111    case '%': /* one-line comment */
112     while ((c=in.vi_getcc())!='\n' && c!='\r' && c!=-1) ;
113     if (c==-1) goto eof;
114     goto again_getcc;
115    case '{': case '[':
116     return '[';
117    case '}': case ']':
118     return ']';
119    case ')': goto err;
120    case '>':
121     if (in.vi_getcc()!='>') goto err;
122     return '>';
123    case '<':
124     if ((c=in.vi_getcc())==-1) { uf_hex: Error::sev(Error::EERROR) << "miniPS: unfinished hexstr" << (Error*)0; }
125     if (c=='<') return '<';
126     if (c=='~') Error::sev(Error::EERROR) << "miniPS: a85str unsupported" << (Error*)0;
127     tv.bb=&b; b.clear();
128     hi=true;
129     while (c!='>') {
130       if ((hv=b.hexc2n(c))!=16) {
131         if (hi) { b << (char)(hv<<4); hi=false; }
132            else { b.end_()[-1]|=hv; hi=true; }
133       } else if (!is_ps_white(c)) Error::sev(Error::EERROR) << "miniPS: syntax error in hexstr" << (Error*)0;
134       if ((c=in.vi_getcc())==-1) goto uf_hex;
135     }
136     /* This is correct even if an odd number of hex digits have arrived */
137     return '(';
138    case '(':
139     tv.bb=&b; b.clear();
140     nest=1;
141     while ((c=in.vi_getcc())!=-1) { redo:
142       if (c==')' && --nest==0) return '(';
143       if (c!='\\') { if (c=='(') nest++; b << (char)c; continue; }
144       /* read a backslash */
145       switch (c=in.vi_getcc()) {
146        case -1: goto uf_str;
147        case 'n': b << '\n'; break;
148        case 'r': b << '\r'; break;
149        case 't': b << '\t'; break;
150        case 'b': b << '\010'; break; /* \b and \a conflict between -ansi and -traditional */
151        case 'f': b << '\f'; break;
152        default:
153         if (c<'0' || c>'7') { b << (char)c; break; }
154         hv=c-'0'; /* read at most 3 octal chars */
155         if ((c=in.vi_getcc())==-1) goto uf_str;
156         if (c<'0' || c>'7') { b << (char)hv; goto redo; }
157         hv=8*hv+(c-'0');
158         if ((c=in.vi_getcc())==-1) goto uf_str;
159         if (c<'0' || c>'7') { b << (char)hv; goto redo; }
160         b << (char)(8*hv+(c-'0'));
161       } /* SWITCH */
162     } /* WHILE */
163     uf_str: Error::sev(Error::EERROR) << "miniPS: unfinished str" << (Error*)0;
164    case '/':
165     /* fall-through, b will begin with '/' */
166    default: /* /nametype, /integertype or /realtype */
167     tv.bb=&b; b.clear();
168     b.clear(); b << (char)c;
169     while ((c=in.vi_getcc())!=-1 && is_ps_name(c)) b << (char)c;
170     ungot=c==-1?EOFF:c;
171     if (b[0]=='/') return '/';
172     b.term0();
173     /* Dat: we don't support base-n number such as `16#100' == 256 in PostScript */
174     if (!toInteger(b, l)) { tv.i=l; return '1'; }
175     /* Dat: call toInteger _before_ toReal */
176     // if (!toReal(b, tv.d)) { fprintf(stderr,"%f;\n", tv.d); }
177     /* assert(tv.bb!=NULLP); */
178     len=b.getLength();
179     if (!toReal(b, d)) { /* tv.bb is also valid */
180       tv.r=new Real(d, b(), len);
181       return '.';
182     }
183     if (len>2 && (metric=Real::str2metric(b()+len-2))!=Real::ME_COUNT) {
184       saved=b[len-2];
185       b[len-2]='\0';
186       if (!toReal(b, d)) {
187         tv.r=new Real(d, b(), len-2);
188         tv.r->setMetric(metric);
189         return ':'; /* Real with metric */
190       }
191       b[len-2]=saved;
192     }
193     return 'E'; /* /nametype */
194   }
195  err:
196   Error::sev(Error::EERROR) << "miniPS: syntax error" << (Error*)0;
197   goto again_getcc; /* notreached */
198 }
199 
200 /* --- */
201 
202 #if 0
203 inline static unsigned typerr() { assert(0); return 0; }
204 #endif
205 
getType(VALUE v)206 unsigned MiniPS::getType(VALUE v) {
207   return (v&1)!=0 ? T_INTEGER
208        : v+0U>Qmax_+0U ? RVALUE(v)->getType()
209        : v==Qnull ? T_NULL+0/*avoid gcc-3.0 ld bug*/
210        : T_BOOLEAN;
211 }
getTypeStr(unsigned u)212 char const* MiniPS::getTypeStr(unsigned u) {
213   static char const* strs[]= { (char const*)NULLP, "null", "boolean", "integer", "real", "string", "array", "dict", "name", "Ename", "void" };
214   // return strs[getType(v)];
215   return strs[u];
216 }
delete0(VALUE v)217 void MiniPS::delete0(VALUE v) {
218   if (isDirect(v)) return;
219   Value *vp=RVALUE(v);
220   unsigned ty=vp->getType();
221   if (ty==T_DICT) RDICT(v)->free();
222   else if (ty==T_ARRAY) RARRAY(v)->free();
223   else if (ty==T_VOID) ;
224   else if (vp->hasPtr()) delete [] vp->begin_();
225   delete vp; /* BUGFIX at Sat Sep  7 12:50:13 CEST 2002 */
226 }
dump(VALUE v,unsigned indent)227 void MiniPS::dump(VALUE v, unsigned indent) {
228   Files::FILEW sout(stdout);
229   dump(sout, v, indent);
230 }
dump(GenBuffer::Writable & out_,VALUE v,unsigned indent)231 void MiniPS::dump(GenBuffer::Writable& out_, VALUE v, unsigned indent) {
232   if (v==Qnull) out_ << "null";
233   else if (v==Qtrue) out_ << "true";
234   else if (v==Qfalse) out_ << "false";
235   else if ((v&1)!=0) out_ << (v/2); /* prints a signed integer */
236   else {
237     Value *vp=RVALUE(v);
238     unsigned ty=vp->getType();
239     if (ty==T_STRING) {
240       SimBuffer::Static s((char*)vp->begin_(), vp->getLength());
241       SimBuffer::B b;
242       b.appendDumpPS(s, true);
243       out_ << b;
244     } else if (ty==T_SNAME || ty==T_ENAME) {
245       out_.vi_write((char*)vp->begin_(), vp->getLength());
246     } else if (ty==T_REAL) {
247       RREAL(v)->dump(out_);
248     } else if (ty==T_ARRAY) {
249       if (!vp->isDumping()) { /* Imp: thread-safe locking */
250         RARRAY(v)->dump(out_, indent);
251       } else out_ << "[...]";
252     } else if (ty==T_DICT) {
253       if (!vp->isDumping()) { /* Imp: thread-safe locking */
254         RDICT(v)->dump(out_, indent);
255       } else out_ << "<<...>>";
256     } else assert(0 && "unknown MiniPS type");
257   }
258 }
259 
260 /* --- */
261 
262 /* Sat Sep  7 12:30:19 CEST 2002 */
263 const double MiniPS::Real::me_factor[MiniPS::Real::ME_COUNT]={
264   1.0L, /* 1 bp = 1 bp (big point) */
265   72.0L, /* 1 in = 72 bp (inch) */
266   72.0L/72.27, /* 1 pt = 72/72.27 bp (point) */
267   12.0L*72.0/72.27, /* 1 pc = 12*72/72.27 bp (pica) */
268   1238.0L/1157.0*72.0/72.27, /* 1 dd = 1238/1157*72/72.27 bp (didot point) [about 1.06601110141206 bp] */
269   12.0L*1238.0/1157.0*72.0/72.27, /* 1 cc = 12*1238/1157*72/72.27 bp (cicero) */
270   72.0L/72.27/65536.0, /* 1 sp = 72/72.27/65536 bp (scaled point) */
271   72.0L/2.54, /* 1 cm = 72/2.54 bp (centimeter) */
272   7.2L/2.54, /* 1 mm = 7.2/2.54 bp (millimeter) */
273 };
274 /* Sat Sep  7 12:30:19 CEST 2002 */
275 char const* const MiniPS::Real::me_psfactor[MiniPS::Real::ME_COUNT]={
276   "", /* 1 bp = 1 bp (big point) */
277   " 72 mul", /* 1 in = 72 bp (inch) */
278   " 72 mul 72.27 div", /* 1 pt = 72/72.27 bp (point) */
279   " 864 mul 72.27 div", /* 1 pc = 12*72/72.27 bp (pica) */
280   " 891.36 mul 836.164 div", /* 1 dd = 1238/1157*72/72.27 bp (didot point) [about 1.06601110141206 bp] */
281   " 10696.32 mul 836.164 div", /* 1 cc = 12*1238/1157*72/72.27 bp (cicero) */
282   " 0.72 mul 47362.8672 div", /* 1 sp = 72/72.27/65536 bp (scaled point) */
283   " 72 mul 2.54 div", /* 1 cm = 72/2.54 bp (centimeter) */
284   " 720 mul 254 div", /* 1 mm = 7.2/2.54 bp (millimeter) */
285 };
Real(double d_,char const * ptr_,ii_t len_)286 MiniPS::Real::Real(double d_, char const*ptr_, ii_t len_): d(d_), metric(0), dumpPS(false) {
287   ty=T_REAL;
288   char *p=new char[len_+1];  /* Will be freed by MiniPS::delete0(). */
289   memcpy(ptr=p, ptr_, len=len_);
290   p[len_]='\0';
291 }
dump(GenBuffer::Writable & out_,bool dumpPS_force)292 void MiniPS::Real::dump(GenBuffer::Writable &out_, bool dumpPS_force) {
293   char buf[64]; /* Imp: should be enough?? */
294   if (metric!=0 && (dumpPS_force || dumpPS)) {
295     sprintf(buf, "%" PTS_CFG_PRINTFGLEN "g%s", d, me_psfactor[metric]);
296   } else {
297     sprintf(buf, "%" PTS_CFG_PRINTFGLEN "g", d*me_factor[metric]);
298   }
299   out_ << buf;
300 }
str2metric(char const str[2])301 MiniPS::Real::metric_t MiniPS::Real::str2metric(char const str[2]) {
302   switch (str[0]) {
303    case 'b': if (str[1]=='p') return ME_bp;  break;
304    case 'i': if (str[1]=='n') return ME_in;  break;
305    case 'p': if (str[1]=='t') return ME_pt;
306              if (str[1]=='c') return ME_pc;  break;
307    case 'd': if (str[1]=='d') return ME_dd;  break;
308    case 'c': if (str[1]=='c') return ME_cc;
309              if (str[1]=='m') return ME_cm;  break;
310    case 's': if (str[1]=='p') return ME_sp;  break;
311    case 'm': if (str[1]=='m') return ME_mm;  break;
312   }
313   return ME_COUNT;
314 }
isDimen(char const * str)315 bool MiniPS::Real::isDimen(char const *str) {
316   double d;
317   slen_t len=strlen(str);
318   if (!toReal(str, len, d)) return true;
319   return len>2 && str2metric(str+len-2)!=ME_COUNT && !toReal(str, len-2, d);
320 }
321 
String(char const * ptr_,ii_t len_)322 MiniPS::String::String(char const*ptr_, ii_t len_) {
323   char *p=new char[len_+1];  /* Will be freed by MiniPS::delete0(). */
324   memcpy(ptr=p, ptr_, len=len_);
325   p[len_]='\0';
326   ty=T_STRING;
327 }
replace(char const * ap,slen_t alen,char const * bp,slen_t blen)328 void MiniPS::String::replace(char const*ap, slen_t alen, char const*bp, slen_t blen) {
329   char *p=new char[alen+blen+1];  /* Will be freed by MiniPS::delete0(). */
330   memcpy(p,      ap, alen);
331   memcpy(p+alen, bp, blen);
332   p[alen+blen]='\0';
333   delete [] (char*)ptr;
334   ptr=p;
335 }
336 
Sname(char const * ptr_,ii_t len_)337 MiniPS::Sname::Sname(char const*ptr_, ii_t len_) {
338   param_assert(len_>=1 && ptr_[0]=='/');
339   char *p=new char[len_+1];  /* Will be freed by MiniPS::delete0(). */
340   memcpy(ptr=p, ptr_, len=len_);
341   p[len_]='\0';
342   ty=T_SNAME;
343 }
equals(Sname const & other)344 bool MiniPS::Sname::equals(Sname const&other) {
345   return len==other.len && 0==memcmp(ptr, other.ptr, len);
346 }
equals(char const * other)347 bool MiniPS::Sname::equals(char const*other) {
348   return 0==strcmp(1+(char*)ptr, other);
349 }
350 
Ename(char const * ptr_,ii_t len_)351 MiniPS::Ename::Ename(char const*ptr_, ii_t len_) {
352   param_assert(len_>=1 && ptr_[0]!='/');
353   char *p=new char[len_+1];  /* Will be freed by MiniPS::delete0(). */
354   memcpy(ptr=p, ptr_, len=len_);
355   p[len_]='\0';
356   ty=T_ENAME;
357 }
equals(Ename const & other)358 bool MiniPS::Ename::equals(Ename const&other) {
359   return len==other.len && 0==memcmp(ptr, other.ptr, len);
360 }
equals(char const * other,slen_t otherlen)361 bool MiniPS::Ename::equals(char const*other, slen_t otherlen) {
362   return (slen_t)len==otherlen && 0==memcmp(ptr, other, otherlen);
363 }
equals(char const * other)364 bool MiniPS::Ename::equals(char const*other) {
365   return 0==strcmp((char*)ptr, other);
366 }
367 
Array()368 MiniPS::Array::Array() {
369   alloced=16;
370   ptr=new VALUE[alloced=16];
371   len=0;
372   ty=T_ARRAY;
373 }
free()374 void MiniPS::Array::free() {
375   VALUE *p=(VALUE*)ptr, *pend=p+len;
376   while (p!=pend) MiniPS::delete0(*p++);
377   delete [] (VALUE*)ptr;
378 }
push(VALUE v)379 void MiniPS::Array::push(VALUE v) {
380   if (len==alloced) extend(len+1);
381   ((VALUE*)ptr)[len++]=v;
382 }
get(ii_t index)383 MiniPS::VALUE MiniPS::Array::get(ii_t index) {
384   return (index<0 || index>=len) ? Qundef : ((VALUE*)ptr)[index];
385 }
set(ii_t index,VALUE val)386 void MiniPS::Array::set(ii_t index, VALUE val) {
387   param_assert(index>=0 && index<len);
388   MiniPS::delete0(((VALUE*)ptr)[index]);
389   ((VALUE*)ptr)[index]=val;
390 }
dump(GenBuffer::Writable & out_,unsigned indent)391 void MiniPS::Array::dump(GenBuffer::Writable &out_, unsigned indent) {
392   dumping=true;
393   if (len==0) {
394     out_ << "[]";
395   } else if (len==1) {
396     out_ << "[ ";
397     MiniPS::dump(out_, ((VALUE*)ptr)[0], indent);
398     out_ << " ]";
399   } else {
400     indent+=2;
401     char *spaces=new char[indent];
402     memset(spaces, ' ', indent);
403     // spaces[indent]='\n';
404     out_ << "[ % " << len << " elements\n";
405     VALUE *p=(VALUE*)ptr, *pend=p+len;
406     while (p!=pend) {
407       out_.vi_write(spaces, indent);
408       MiniPS::dump(out_, *p++, indent);
409       /*if(p!=pend)*/ out_ << "\n";
410     }
411     out_.vi_write(spaces, indent-=2);
412     out_ << "]";
413     delete [] spaces;
414   }
415   dumping=false;
416 }
extend(ii_t newlen)417 void MiniPS::Array::extend(ii_t newlen) {
418   if (newlen<=alloced) return;
419   ii_t newalloced=alloced;
420   assert(alloced>=0);
421   while (newlen>newalloced) newalloced<<=1;
422   VALUE *newptr=new VALUE[newalloced];
423   memcpy(newptr, ptr, len*sizeof(VALUE));
424   delete [] (VALUE*)ptr;
425   ptr=newptr;
426   alloced=newalloced;
427   /* len remains unchanged */
428 }
getFirst(VALUE * & val)429 void MiniPS::Array::getFirst(VALUE *&val) {
430   if (len==0) { val=(VALUE*)NULLP; return; }
431   val=(VALUE*)ptr;
432 }
getNext(VALUE * & val)433 void MiniPS::Array::getNext(VALUE *&val) {
434   val++;
435   if (len+(VALUE*)ptr==val) val=(VALUE*)NULLP;
436 }
437 
438 #if USE_DICT_MAPPING
Dict()439 MiniPS::Dict::Dict() { /* Sun Mar 24 21:02:41 CET 2002 */
440   ptr=(void*)new Mapping::H(sizeof(VALUE)+1);
441   /* hash value format: a VALUE, and a flag (0 or 1) indicating touchedness */
442   len=0; /* meaningless */
443   ty=T_DICT;
444 }
free()445 void MiniPS::Dict::free() {
446   char const*const* keyy; slen_t keylen;
447   VALUE *val = 0;  /* pacify gcc-4.2.1 by giving initial value */
448   bool touched;
449   getFirst(keyy, keylen, val, touched);
450   while (keyy!=(char const*const*)NULLP) {
451     MiniPS::delete0(*val);
452     getNext(keyy, keylen, val, touched);
453   }
454   delete (Mapping::H*)ptr;
455 }
put(char const * key,VALUE val)456 void MiniPS::Dict::put(char const*key, VALUE val) {
457   put(key,strlen(key),val);
458 }
push(char const * keys,slen_t keylen,VALUE val)459 MiniPS::VALUE MiniPS::Dict::push(char const*keys, slen_t keylen, VALUE val) {
460   if (keys[0]=='/') { keys++; keylen--; }
461   char *has=((Mapping::H*)ptr)->get(keys,keylen);
462   VALUE ret=Qundef;
463   if (has!=(char const*)NULLP) {
464     memcpy(&ret, has, sizeof(VALUE));
465     // printf("found=/%s.\n", keys);
466     /* No MiniPS::delete0(); deliberately. */
467     memcpy(has, &val, sizeof(VALUE)); has[sizeof(VALUE)]=0;
468   } else {
469     char tmp[sizeof(VALUE)+1];
470     memcpy(tmp, &val, sizeof(VALUE));
471     tmp[sizeof(VALUE)]=0;
472     ((Mapping::H*)ptr)->set(keys,keylen,tmp);
473   }
474   return ret;
475 }
put(char const * keys,slen_t keylen,VALUE val)476 void MiniPS::Dict::put(char const*keys, slen_t keylen, VALUE val) {
477   if (keys[0]=='/') { keys++; keylen--; }
478   char *has=((Mapping::H*)ptr)->get(keys,keylen);
479   if (has!=NULLP) {
480     VALUE ret=Qundef; memcpy(&ret, has, sizeof(VALUE));
481     MiniPS::delete0(ret);
482     memcpy(has, &val, sizeof(VALUE)); has[sizeof(VALUE)]=0;
483   } else {
484     char tmp[sizeof(VALUE)+1];
485     memcpy(tmp, &val, sizeof(VALUE));
486     tmp[sizeof(VALUE)]=0;
487     ((Mapping::H*)ptr)->set(keys,keylen,tmp);
488   }
489 }
get(char const * keys,slen_t keylen)490 MiniPS::VALUE MiniPS::Dict::get(char const*keys, slen_t keylen) {
491   if (keys[0]=='/') { keys++; keylen--; }
492   char *has=((Mapping::H*)ptr)->get(keys,keylen);
493   VALUE ret=Qundef; if (has!=NULLP) memcpy(&ret, has, sizeof(VALUE));
494   return ret;
495 }
get1(char const * keys,slen_t keylen)496 MiniPS::VALUE MiniPS::Dict::get1(char const*keys, slen_t keylen) {
497   if (keys[0]=='/') { keys++; keylen--; }
498   char *has=((Mapping::H*)ptr)->get(keys,keylen);
499   VALUE ret=Qundef; if (has!=NULLP) { memcpy(&ret, has, sizeof(VALUE)); has[sizeof(VALUE)]=1; }
500   return ret;
501 }
untouch(char const * keys,slen_t keylen)502 void MiniPS::Dict::untouch(char const*keys, slen_t keylen) {
503   if (keys[0]=='/') { keys++; keylen--; }
504   char *has=((Mapping::H*)ptr)->get(keys,keylen);
505   if (has!=NULLP) has[sizeof(VALUE)]=0;
506 }
getFirst(char const * const * & key,slen_t & keylen,VALUE * & val,bool & touched)507 void MiniPS::Dict::getFirst(char const*const*& key, slen_t &keylen, VALUE *&val, bool &touched) {
508   char *has;
509   // key=(char const*const*)NULLP;return;
510   ((Mapping::H*)ptr)->getFirst(key, keylen, has);
511   if (key==(char const*const*)NULLP) return;
512   val=PTS_align_cast(VALUE*,has);
513   touched=has[sizeof(VALUE)]!=0;
514 }
getNext(char const * const * & key,slen_t & keylen,VALUE * & val,bool & touched)515 void MiniPS::Dict::getNext (char const*const*& key, slen_t &keylen, VALUE *&val, bool &touched) {
516   char *has;
517   ((Mapping::H*)ptr)->getNext(key, keylen, has);
518   if (key==(char const*const*)NULLP) return;
519   val=PTS_align_cast(VALUE*,has);
520   touched=has[sizeof(VALUE)]!=0;
521 }
dump(GenBuffer::Writable & out_,unsigned indent,bool dump_delimiters)522 void MiniPS::Dict::dump(GenBuffer::Writable &out_, unsigned indent, bool dump_delimiters) {
523   dumping=true;
524   slen_t len=((Mapping::H*)ptr)->getLength();
525   if (len==0) {
526     if (dump_delimiters) out_ << "<<>>";
527   } else {
528     char const*const* keyy; slen_t keylen;
529     VALUE *val = 0;  /* pacify gcc-4.2.1 by giving initial value */
530     bool touched;
531     indent+=2;
532     char *spaces=new char[indent];
533     memset(spaces, ' ', indent);
534     // spaces[indent]='\n';
535     if (dump_delimiters) out_ << "<< % " << len << " key(s)\n";
536     getFirst(keyy, keylen, val, touched);
537     while (keyy!=(char const*const*)NULLP) {
538       out_.vi_write(spaces, indent);
539       out_.vi_putcc('/');
540       out_.vi_write(*keyy, keylen); /* Imp: PDF #...-quoting */
541       out_ << "  ";
542       MiniPS::dump(out_, *val, indent);
543       out_.vi_putcc('\n');
544       getNext(keyy, keylen, val, touched);
545     }
546     if (dump_delimiters) { out_.vi_write(spaces, indent-=2); out_ << ">>"; }
547     delete [] spaces;
548   }
549   dumping=false;
550 }
extend(ii_t)551 void MiniPS::Dict::extend(ii_t) {}
552 
553 #else /* a MiniPS::Dict implementation with linear search */
Dict()554 MiniPS::Dict::Dict() {
555   alloced=16;
556   ptr=new VALUE[alloced=16];
557   len=0;
558   ty=T_DICT;
559 }
free()560 void MiniPS::Dict::free() {
561   VALUE *p=(VALUE*)ptr, *pend=p+len;
562   while (p!=pend) MiniPS::delete0(*p++);
563   delete [] (VALUE*)ptr;
564 }
put(char const * key,VALUE val)565 void MiniPS::Dict::put(char const*key, VALUE val) {
566   return put(key,strlen(key),val);
567 }
push(char const * keys,slen_t keylen,VALUE val)568 MiniPS::VALUE MiniPS::Dict::push(char const*keys, slen_t keylen, VALUE val) {
569   // param_assert(key[0]=='/');
570   if (keys[0]=='/') { keys++; keylen--; }
571   VALUE *p=(VALUE*)ptr, *pend=p+len;
572   while (p!=pend) {
573     if (MiniPS::RENAME(p[0]&~1)->equals(key,keylen)) {
574       VALUE v=p[1];
575       p[1]=val; return v;
576     }
577     p+=2;
578   }
579   if (len==alloced) extend(len+2);
580   ((VALUE*)ptr)[len++]=(MiniPS::VALUE)new Ename(keys,keylen);
581   ((VALUE*)ptr)[len++]=val;
582   return Qundef;
583 }
put(char const * keys,slen_t keylen,VALUE val)584 void MiniPS::Dict::put(char const*keys, slen_t keylen, VALUE val) {
585   // param_assert(key[0]=='/');
586   if (keys[0]=='/') { keys++; keylen--; }
587   //void MiniPS::Dict::put(VALUE key, VALUE val) {
588   //param_assert(MiniPS::getType(key)==T_ENAME);
589   VALUE *p=(VALUE*)ptr, *pend=p+len;
590   while (p!=pend) {
591     if (MiniPS::RENAME(p[0]&~1)->equals(keys,keylen)) {
592       MiniPS::delete0(p[1]);
593       p[1]=val;
594       return;
595     }
596     p+=2;
597   }
598   if (len==alloced) extend(len+2);
599   ((VALUE*)ptr)[len++]=(MiniPS::VALUE)new Ename(keys,keylen);
600   ((VALUE*)ptr)[len++]=val;
601 }
get(char const * key,slen_t keylen)602 MiniPS::VALUE MiniPS::Dict::get(char const*key, slen_t keylen) {
603   if (key[0]=='/') { key++; keylen--; }
604   VALUE *p=(VALUE*)ptr, *pend=p+len;
605   while (p!=pend) {
606     //printf("for=%s trying=%s.\n", key, MiniPS::RENAME(p[0]&~1)->begin_());
607     if (MiniPS::RENAME(p[0]&~1)->equals(key, keylen)) return p[1];
608     p+=2;
609   }
610   return Qundef;
611 }
get1(char const * key,slen_t keylen)612 MiniPS::VALUE MiniPS::Dict::get1(char const*key, slen_t keylen) {
613   if (key[0]=='/') { key++; keylen--; }
614   VALUE *p=(VALUE*)ptr, *pend=p+len;
615   while (p!=pend) {
616     //printf("for=%s trying=%s.\n", key, MiniPS::RENAME(p[0]&~1)->begin_());
617     if (MiniPS::RENAME(p[0]&~1)->equals(key,keylen)) {
618       /* dirty, black magic */ p[0]|=1;
619       return p[1];
620     }
621     p+=2;
622   }
623   return Qundef;
624 }
untouch(char const * key,slen_t keylen)625 void MiniPS::Dict::untouch(char const*key, slen_t keylen) {
626   if (key[0]=='/') { key++; keylen--; }
627   VALUE *p=(VALUE*)ptr, *pend=p+len;
628   while (p!=pend) {
629     if (MiniPS::RENAME(p[0]&~1)->equals(key,keylen)) { p[0]&=~1; return; }
630     p+=2;
631   }
632 }
getFirst(char const * const * & key,slen_t & keylen,VALUE * & val,bool & touched)633 void MiniPS::Dict::getFirst(char const*const*& key, slen_t &keylen, VALUE *&val, bool &touched) {
634   // assert(MiniPS::getType(((VALUE*)ptr)[0])==T_ENAME);
635   if (len==0) { key=(char const*const*)NULLP; return; }
636   assert(ptr!=NULLP);
637   Ename *skey=(Ename*)(((VALUE*)ptr)[0]&~1);
638   key=(char**)&skey->ptr;
639   keylen=skey->len;
640   val=((VALUE*)ptr)+1;
641   touched=(((VALUE*)ptr)[0]&1)!=0;
642 }
getNext(char const * const * & key,slen_t & keylen,VALUE * & val,bool & touched)643 void MiniPS::Dict::getNext (char const*const*& key, slen_t &keylen, VALUE *&val, bool &touched) {
644   val+=2;
645   if (len+(VALUE*)ptr==(VALUE*)val-1) { key=(char const*const*)NULLP; return; }
646   Ename *skey=RENAME(val[-1]&~1);
647   // assert(MiniPS::getType((VALUE)skey)==T_ENAME);
648   key=(char**)&skey->ptr;
649   keylen=skey->len;
650   touched=(val[-1]&1)!=0;
651 }
652 
653 #if 0 /* obsolete */
654 void MiniPS::Dict::getFirst(VALUE *&key, VALUE *&val) {
655   if (len==0) { key=val=(VALUE*)NULLP; return; }
656   assert(ptr!=NULLP);
657   key=(VALUE*)ptr;
658   val=key+1;
659 }
660 void MiniPS::Dict::getNext(VALUE *&key, VALUE *&val) {
661   key+=2;
662   if (len+(VALUE*)ptr==key) key=val=(VALUE*)NULLP;
663                        else val=key+1;
664 }
665 #endif
666 
dump(GenBuffer::Writable & out_,unsigned indent,bool dump_delimiters)667 void MiniPS::Dict::dump(GenBuffer::Writable &out_, unsigned indent, bool dump_delimiters) {
668   assert(len>=0 && (len&1)==0);
669   if (len==0) {
670     if (dump_delimiters) out_ << "<<>>";
671   } else {
672     indent+=2;
673     char *spaces=new char[indent];
674     memset(spaces, ' ', indent);
675     // spaces[indent]='\n';
676     if (dump_delimiters) out_ << "<< % " << (len/2) << " key(s)\n";
677     VALUE *p=(VALUE*)ptr, *pend=p+len;
678     while (p!=pend) {
679       out_.vi_write(spaces, indent);
680       MiniPS::dump(out_, *p++, indent);
681       out_ << "  ";
682       MiniPS::dump(out_, *p++, indent);
683       /*if(p!=pend)*/ out_.vi_putcc('\n'); // out_ << "\n";
684     }
685     if (dump_delimiters) { out_.vi_write(spaces, indent-=2); out_ << ">>"; }
686     delete [] spaces;
687   }
688 }
extend(ii_t newlen)689 void MiniPS::Dict::extend(ii_t newlen) {
690   if (newlen<=alloced) return;
691   ii_t newalloced=alloced;
692   assert(alloced>=0);
693   while (newlen>newalloced) newalloced<<=1;
694   VALUE *newptr=new VALUE[newalloced];
695   memcpy(newptr, ptr, len*sizeof(VALUE));
696   delete [] (VALUE*)ptr;
697   ptr=newptr;
698   alloced=newalloced;
699   /* len remains unchanged */
700 }
701 #endif
702 
703 /* --- */
704 
Parser(char const * filename_)705 MiniPS::Parser::Parser(char const *filename_) {
706   FILE *ff;
707   ff=(filename_[0]=='-' && filename_[1]=='\0')? stdin: fopen(filename_, "r"); /* not "rb" */
708   if (ff==NULLP) Error::sev(Error::EERROR) << "MiniPS::Parser: cannot open file: " << FNQ(filename_) << (Error*)0;
709   f=(FILEP)ff;
710   rd=new Files::FILER(ff);
711   tok=new Tokenizer(*rd);
712   master=(Parser*)NULLP;
713   free_level=4;
714   unread=Tokenizer::NO_UNGOT;
715   depth=0;
716   specRuns=(MiniPS::Dict*)NULLP;
717   specRunsDelete=false;
718 }
Parser(FILEP f_)719 MiniPS::Parser::Parser(FILEP f_) {
720   f=f_;
721   rd=new Files::FILER(PTS_align_cast(FILE*,f_));
722   tok=new Tokenizer(*rd);
723   master=(Parser*)NULLP;
724   free_level=3;
725   unread=Tokenizer::NO_UNGOT;
726   depth=0;
727   specRuns=(MiniPS::Dict*)NULLP;
728   specRunsDelete=false;
729 }
Parser(GenBuffer::Readable * rd_)730 MiniPS::Parser::Parser(GenBuffer::Readable *rd_) {
731   f=(FILEP)NULLP;
732   rd=rd_;
733   tok=new Tokenizer(*rd);
734   master=(Parser*)NULLP;
735   free_level=2;
736   unread=Tokenizer::NO_UNGOT;
737   depth=0;
738   specRuns=(MiniPS::Dict*)NULLP;
739   specRunsDelete=false;
740 }
Parser(Tokenizer * tok_)741 MiniPS::Parser::Parser(Tokenizer *tok_) {
742   master=(Parser*)NULLP;
743   f=(FILEP)NULLP;
744   rd=(GenBuffer::Readable*)NULLP;
745   tok=tok_;
746   master=(Parser*)NULLP;
747   free_level=0;
748   unread=Tokenizer::NO_UNGOT;
749   depth=0;
750   specRuns=(MiniPS::Dict*)NULLP;
751   specRunsDelete=false;
752 }
Parser(Parser * master_)753 MiniPS::Parser::Parser(Parser *master_) {
754   f=(FILEP)NULLP;
755   rd=(GenBuffer::Readable*)NULLP;
756   tok=(Tokenizer*)NULLP;
757   master=master_;
758   free_level=1;
759   unread=Tokenizer::NO_UNGOT;
760   depth=0;
761   specRuns=(MiniPS::Dict*)NULLP;
762   specRunsDelete=false;
763 }
~Parser()764 MiniPS::Parser::~Parser() {
765   /* We delete the master here! */
766   if (master!=NULLP) delete master; /* recursive ~Parser() call */
767   if (free_level>=2) delete tok;
768   if (free_level>=3) delete rd;
769   if (free_level>=4) fclose(PTS_align_cast(FILE*,f));
770   if (specRunsDelete) MiniPS::delete0((VALUE)specRuns);
771 }
addSpecRun(char const * filename_,GenBuffer::Readable * rd_)772 void MiniPS::Parser::addSpecRun(char const* filename_, GenBuffer::Readable *rd_) {
773   if (specRuns==NULLP) {
774     specRunsDelete=true;
775     specRuns=new MiniPS::Dict();
776   }
777   specRuns->put(filename_, (MiniPS::VALUE)new MiniPS::Void(rd_));
778 }
setSpecRuns(MiniPS::Dict * newSpecRuns)779 void MiniPS::Parser::setSpecRuns(MiniPS::Dict *newSpecRuns) {
780   if (newSpecRuns!=specRuns) {
781     if (specRunsDelete) MiniPS::delete0((VALUE)specRuns);
782     specRunsDelete=false;
783     specRuns=newSpecRuns;
784   }
785 }
setDepth(unsigned depth_)786 void MiniPS::Parser::setDepth(unsigned depth_) {
787   if (depth_>=MAX_DEPTH) Error::sev(Error::EERROR) << "MiniPS::Parser: `run' inclusion too deep" << (Error*)0;
788   depth=depth_;
789 }
parse1(int closer,int sev)790 MiniPS::VALUE MiniPS::Parser::parse1(int closer, int sev) {
791   char *beg=0; slen_t len=0; /* pacify g++-2.91 */
792   Real::metric_t metric;  Real *r=0; /* pacify g++-2.91 */
793   VALUE v, w;
794   if (master!=NULLP) {
795    from_master:
796     /* vvv EOF_ALLOWED means: the master cannot close our open '>' or ']' */
797     if ((v=master->parse1(EOF_ALLOWED, sev))!=Qundef) return v;
798     MiniPS::delete0(v);
799     delete master;
800     master=(Parser*)NULLP;
801     // fprintf(stderr, "closed master\n");
802   }
803   // return parse1_real(closer);
804 
805   int i=0;
806   if (unread!=Tokenizer::NO_UNGOT) {
807     i=unread;
808     unread=Tokenizer::NO_UNGOT;
809   } else i=tok->yylex();
810 
811   // fprintf(stderr, "i=%d i='%c'\n", i, i);
812 
813   switch (i) {
814    case Tokenizer::EOFF: case ']': case '>':
815     if (closer==i) return Qundef; /* EOF */
816     Error::sev((Error::level_t)sev) << "MiniPS::Parser: premature EOF (early closer: " << (int)i << ')' << (Error*)0;
817     return Qerror; /* parse error */
818    case '(': {
819      beg=tok->lastTokVal().bb->begin_(); len=tok->lastTokVal().bb->getLength();
820      VALUE v=(VALUE)new String(beg, len);
821      i=tok->yylex();
822      beg=tok->lastTokVal().bb->begin_(); len=tok->lastTokVal().bb->getLength();
823      if (i!='E' || len!=3 || 0!=memcmp(beg,"run",3)) { unread=i; return v; }
824      /* Process external file inclusion */
825      assert(master==NULLP);
826      /* Imp: prevent infinite recursion */
827      if (specRuns!=NULLP && Qundef!=(w=specRuns->get(RSTRING(v)->begin_(), RSTRING(v)->getLength()))) {
828        master=new Parser((GenBuffer::Readable*)RVOID(w)->getPtr());
829      } else {
830        master=new Parser(RSTRING(v)->getCstr());  /* Open external file. */
831      }
832      MiniPS::delete0(v);
833      master->setDepth(depth+1);
834      master->setSpecRuns(specRuns);
835      goto from_master;
836     }
837    case '/':
838     beg=tok->lastTokVal().bb->begin_(); len=tok->lastTokVal().bb->getLength();
839     return (VALUE)new Sname(beg, len);
840    case ':': /* Real with metric */
841     return (VALUE)tok->lastTokVal().r;
842    case '.':
843     // fprintf(stderr, "d=%g\n", tok->lastTokVal().d);
844     // fprintf(stderr, "b=(%s)\n", tok->lastTokVal().b());
845     // assert(tok->lastTokVal().bb!=NULLP);
846     // beg=tok->lastTokVal().bb->begin_(); len=tok->lastTokVal().bb->getLength();
847     // r=new Real(tok->lastTokVal().d, beg, len);
848     r=tok->lastTokVal().r;
849     i=tok->yylex();
850     beg=tok->lastTokVal().bb->begin_(); len=tok->lastTokVal().bb->getLength();
851     if (i!='E' || len!=2 || (metric=Real::str2metric(beg))==Real::ME_COUNT) {
852       unread=i;
853     } else {
854       r->setMetric(metric);
855     }
856     return (VALUE)r;
857    case '1':
858     i=tok->yylex();
859     beg=tok->lastTokVal().bb->begin_(); len=tok->lastTokVal().bb->getLength();
860     if (i!='E' || len!=2 || (metric=Real::str2metric(beg))==Real::ME_COUNT) {
861       unread=i;
862       return Qinteger(tok->lastTokVal().i);
863     } else { /* integer with metric is auto-converted to Real */
864       r=new Real(tok->lastTokVal().i, beg, len);
865       r->setMetric(metric);
866     }
867     return (VALUE)r;
868    case 'E': {
869     beg=tok->lastTokVal().bb->begin_(); len=tok->lastTokVal().bb->getLength();
870     // fprintf(stderr, "L=%d\n", bb->getLength());
871     // assert(0);
872     tok->lastTokVal().bb->term0();
873     if (0==strcmp(beg,"true")) return Qtrue;
874     if (0==strcmp(beg,"false")) return Qfalse;
875     if (0==strcmp(beg,"null")) return Qnull;
876     if (closer==EOF_ILLEGAL_POP && 0==strcmp(beg,"pop")) return Qpop;
877     Error::sev((Error::level_t)sev) << "MiniPS::Parser: unknown Ename: " << (*tok->lastTokVal().bb) << (Error*)0;
878     return Qerror;
879     }
880    case '[': {
881     Array *ap=new Array();
882     VALUE v;
883     while (Qundef!=(v=parse1(']', sev))) { if (v==Qerror) return Qerror; ap->push(v); }
884     return (VALUE)ap;
885     }
886    case '<': {
887     Dict *ap=new Dict();
888     VALUE key, val;
889     while (1) {
890       if (Qundef==(key=parse1('>', sev))) break;
891       if (key==Qerror) return Qerror;
892       if (getType(key)!=T_SNAME) {
893         MiniPS::delete0(key);
894         Error::sev(Error::EERROR) << "MiniPS::Parser: dict key must be a /name" << (Error*)0;
895         return Qerror;
896       }
897       val=parse1(EOF_ILLEGAL_POP, sev); /* No EOF allowed here */
898       if (val==Qerror) {
899         MiniPS::delete0(key);
900         return Qerror;
901       }
902       if (val!=Qpop) {
903         // if (Qundef!=ap->push(RSNAME(key)->begin_(),RSNAME(key)->getLength(),val)) Error::sev(Error::EERROR) << "MiniPS::Parser: duplicate dict key" << (Error*)0;
904         /* ^^^ should free if non-fatal error */
905         if (Qundef!=(v=ap->push(RSNAME(key)->begin_(),RSNAME(key)->getLength(),val))) {
906           Error::sev(Error::WARNING) << "MiniPS::Parser: overriding previous dict key: " << RSNAME(key)->begin_() << (Error*)0;
907           MiniPS::delete0(v);
908         }
909       }
910       MiniPS::delete0(key);
911     }
912     return (VALUE)ap;
913     }
914    default:
915     assert(0);
916   }
917   return Qerror; /* NOTREACHED */
918 }
919 
scanf_dict(VALUE job,bool show_warnings,...)920 void MiniPS::scanf_dict(VALUE job, bool show_warnings, ...) {
921   va_list ap;
922   Dict *dict=RDICT(job);
923   char *key;
924   unsigned ty;
925   char hex3[3];
926   VALUE default_, *dst, got;
927   if (getType(job)!=T_DICT) Error::sev(Error::EERROR) << "scanf_dict: dict expected" << (Error*)0;
928   PTS_va_start(ap, show_warnings);
929   //  "InputFile",  MiniPS::T_STRING, MiniPS::Qundef, &InputFile,
930   //  "OutputFile", MiniPS::T_STRING, MiniPS::Qundef, &OutputFile,
931   //  "Profile",    MiniPS::T_ARRAY,  MiniPS::Qundef, &Profiles,
932   //  NULLP
933   while (NULLP!=(key=va_arg(ap, char*))) {
934     slen_t keylen=strlen(key);
935     if (*key=='/') key++;
936     ty=va_arg(ap, unsigned);
937     default_=va_arg(ap, VALUE);
938     dst=va_arg(ap, VALUE*);
939     got=(show_warnings) ? dict->get1(key,keylen) : dict->get(key,keylen);
940     if (got==Qundef) {
941       got = (ty==S_SENUM) ? RDICT(default_)->get(" ",1) /* get the default value */
942           : (ty==S_FUNC) ? ((VALUE(*)(VALUE))default_)(Qundef)
943           : default_;
944       if (got==Qundef) Error::sev(Error::EERROR) << "scanf_dict: required key missing: /" << key << (Error*)0;
945       /* type of default value is unchecked deliberately */
946     } else switch (ty) {
947      case S_RGBSTR:
948       /* Dat: red is: (\377\0\0), (#f00), (#ff0000) */
949       if (getType(got)!=T_STRING || !(
950              RSTRING(got)->getLength()==3 /* Imp: `transparent -red' shouldn't work */
951           || (RSTRING(got)->getLength()==4 && RSTRING(got)->begin_()[0]=='#' && !toHex3(RSTRING(got)->begin_()+1, hex3) && (got=(VALUE)new String(hex3, 3), true))
952           || (RSTRING(got)->getLength()==7 && RSTRING(got)->begin_()[0]=='#' && !toHex6(RSTRING(got)->begin_()+1, hex3) && (got=(VALUE)new String(hex3, 3), true))
953           || (RSTRING(got)->getLength()==6 && !toHex6(RSTRING(got)->begin_(), hex3) && (got=(VALUE)new String(hex3, 3), true))
954          )) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " must be an RGB color triplet" << (Error*)0;
955       break;
956      case S_SENUM:
957       if (getType(got)!=T_SNAME) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " must be an enum value (name)" << (Error*)0;
958       got=RDICT(default_)->get(RSNAME(got)->begin_(),RSNAME(got)->getLength());
959       if (got==Qundef) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " must be a valid enum value" << (Error*)0;
960       break;
961      case S_FUNC:
962       got=((VALUE(*)(VALUE))default_)(got);
963       if (got==Qundef) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " has invalid value" << (Error*)0;
964       break;
965      case S_UINTEGER:
966       if ((got&1)==0 || got<Qinteger(0)) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " must be a non-negative integer" << (Error*)0;
967       break;
968      case S_ANY:
969       break;
970      case S_PINTEGER:
971       if ((got&1)==0 || got<=Qinteger(0)) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " must be a positive integer" << (Error*)0;
972       break;
973      case S_NUMBER:
974       if ((got&1)==0 && getType(got)!=T_REAL) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " must be real or integer" << (Error*)0;
975       break;
976      case S_PNUMBER:
977       if ((got&1)==0 && getType(got)!=T_REAL) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " must be real or integer" << (Error*)0;
978       if (((got&1)!=0 && got<=Qinteger(0))
979        || (getType(got)==T_REAL && RREAL(got)->getBp()<=0)
980          ) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " must be positive" << (Error*)0;
981       break;
982      default:
983       if (getType(got)!=ty) Error::sev(Error::EERROR) << "scanf_dict: key /" << key << " must have type " << getTypeStr(ty) << (Error*)0;
984     }
985     *dst=got;
986   }
987   va_end(ap);
988   if (show_warnings) {
989     // VALUE *keyy, *val;
990     char const*const* keyy; slen_t keylen; VALUE *val;
991     bool touched = false;  /* pacify gcc-4.2.1 by giving initial value */
992     dict->getFirst(keyy, keylen, val, touched);
993     // fprintf(stderr, "> %p\n", keyy);
994     PTS_va_start(ap, show_warnings);
995     while (keyy!=(char const*const*)NULLP) {
996       // fprintf(stderr, "untouch len=%u\n", keylen);
997       // fprintf(stderr, "untouching key=(%s)\n", *keyy);
998       if (!touched) Error::sev(Error::WARNING) << "scanf_dict: ignoring unknown key /" << SimBuffer::Static(*keyy,keylen) << (Error*)0;
999                else dict->untouch(*keyy, keylen); /* undo get1 */
1000       dict->getNext(keyy, keylen, val, touched);
1001     }
1002     va_end(ap);
1003   }
1004 }
1005 
setDumpPS(MiniPS::VALUE v,bool g)1006 void MiniPS::setDumpPS(MiniPS::VALUE v, bool g) {
1007   /* Sat Sep  7 13:18:35 CEST 2002 */
1008   if (getType(v)==T_REAL) RREAL(v)->setDumpPS(g);
1009 }
1010 
isZero(MiniPS::VALUE v)1011 bool MiniPS::isZero(MiniPS::VALUE v) {
1012   /* Sat Sep  7 15:12:54 CEST 2002 */
1013   switch (getType(v)) {
1014    case T_REAL: return RREAL(v)->getBp()==0;
1015    case T_INTEGER: return int2ii(v)==0;
1016   }
1017   Error::sev(Error::EERROR) << "isZero: number expected" << (Error*)0;
1018   return false; /* NOTREACHED */
1019 }
1020 
isEq(MiniPS::VALUE v,double d)1021 bool MiniPS::isEq(MiniPS::VALUE v, double d) {
1022   double dif=0;
1023   switch (getType(v)) {
1024    case T_REAL: dif=RREAL(v)->getBp()-d; break;
1025    case T_INTEGER: dif=int2ii(v)-d; break;
1026    default: Error::sev(Error::EERROR) << "isEq: number expected" << (Error*)0;
1027   }
1028   if (dif<0.0) dif=-dif;
1029   /* fprintf(stderr,"dif=%g g=%d\n", dif, (dif<0.000001)); */
1030   return (dif<0.000001); /* Imp: ... */
1031 }
1032 
dumpScale(GenBuffer::Writable & out,VALUE v)1033 void MiniPS::dumpScale(GenBuffer::Writable &out, VALUE v) {
1034   double d=0;
1035   ii_t ii;
1036   switch (getType(v)) {
1037    case T_REAL: d = RREAL(v)->getBp(); break;
1038    case T_INTEGER: d = int2ii(v); break;
1039    default: Error::sev(Error::EERROR) << "dumpScale: number expected" << (Error*)0;
1040   }
1041   if (d == -72.0) d = 72.0;
1042   ii = (ii_t)d / 72 * 72;
1043   if (d >= 0 && ii == d) {  /* accurate nonnegative integer divisible by 72 */
1044     out << (ii / 72);
1045   } else if (d < 0 && d == (ii_t)d && 72 % -(ii_t)d == 0) {
1046     out << (72 / -(ii_t)d);
1047   } else {
1048     d = d < 0 ? 72.0 / -d : d / 72.0;
1049     char buf[64]; /* Dat: enough */
1050     sprintf(buf, "%" PTS_CFG_PRINTFGLEN "g", d);
1051     out << buf;
1052   }
1053 }
1054 
dumpAdd3(GenBuffer::Writable & out,MiniPS::VALUE m,MiniPS::VALUE a,MiniPS::VALUE b,MiniPS::VALUE c,MiniPS::VALUE sub,unsigned rounding)1055 void MiniPS::dumpAdd3(GenBuffer::Writable &out, MiniPS::VALUE m, MiniPS::VALUE a, MiniPS::VALUE b, MiniPS::VALUE c, MiniPS::VALUE sub, unsigned rounding) {
1056   long ll;
1057   /* Sat Sep  7 15:30:28 CEST 2002 */
1058   bool no_real_real=true;
1059   double d=0, dd;
1060   long l=0;
1061   if ((getType(m)==T_REAL && (isEq(m, 72) || isEq(m, -72))) || /* Imp: not so exact comparison */
1062       (getType(m)==T_INTEGER && isEq(m, -72)))
1063     m = Qinteger(72);
1064   MiniPS::VALUE t[5], *tt;
1065   t[0]=a; t[1]=m; t[2]=b; t[3]=c; t[4]=sub;
1066   for (tt=t;tt<t+5;tt++) switch (getType(*tt)) {
1067    case T_REAL:
1068     dd=RREAL(*tt)->getBp();
1069    doadd:
1070     if (no_real_real) {
1071       d=l;
1072       no_real_real=false;
1073     }
1074     if (tt==t+1) { /* multiply by m/72 or 72/-m */
1075       if (dd==0.0 || d==0.0) { no_real_real=true; l=0; d=0.0; }
1076                         else d *= dd >= 0 ? dd / 72 : 72 / -dd;
1077     } else if (tt==t+4) d-=dd;
1078     else d+=dd;
1079     break;
1080    case T_INTEGER:
1081     ll=int2ii(*tt);
1082     if (tt==t+1) { /* multiply by m/72 or 72/-m */
1083       if (ll >= 0 && ll % 72 == 0) l *= ll / 72;
1084       else if (ll < 0 && 72 % -ll == 0) l *= 72 / -ll;
1085       else { dd=ll; goto doadd; }
1086     } else if (tt==t+4) l-=ll;
1087     else l+=ll;
1088     break;
1089    default: Error::sev(Error::EERROR) << "dumpAdd3: numbers expected" << (Error*)0;
1090   }
1091   if (no_real_real) { out << l; return; }
1092   if (rounding!=0) {
1093     ll=(long)d;
1094     if ((double)ll<d) ll++;
1095     assert((double)ll>=d); /* Imp: verify possible rounding errors */
1096     out << (rounding>=2 && ll<0 ? 0 : ll);
1097   } else {
1098     char buf[64]; /* Dat: enough */
1099     sprintf(buf, "%" PTS_CFG_PRINTFGLEN "g", d);
1100     out << buf;
1101   }
1102 }
1103 
1104 /* __END__ */
1105