1 /* sam2p_main.cpp
2  * by pts@fazekas.hu at Fri Mar 15 16:11:03 CET 2002
3  */
4 
5 #include "gensi.hpp"
6 #include "gensio.hpp"
7 #include "image.hpp"
8 #include "error.hpp"
9 #include "encoder.hpp"
10 #include "minips.hpp"
11 #include "rule.hpp"
12 #include "main.hpp"
13 #include "in_jai.hpp"
14 
15 #ifndef SAM2P_VERSION
16 #include "sam2p_version.h"
17 #endif
18 
19 /* Sat Jul  6 16:39:19 CEST 2002
20  * empirical checkerg++ helper routines for gcc version 2.95.2 20000220 (Debian GNU/Linux)
21  * see c_lgcc.cpp for more
22  */
23 #if !defined(OBJDEP) && defined(__CHECKER__) && !HAVE_PTS_C_LGCC_CPP_REQUIRED
24 #  include "c_lgcc.cpp"
25 #endif
26 
27 #if 0 /*#ifdef __CHECKER__*/
28 #include <stdlib.h>
29 void* __builtin_vec_new(unsigned len){ return malloc(len); }
30 void __builtin_vec_delete(void *p) { free(p); }
31 void* __builtin_new(unsigned len){ return malloc(len); }
32 void __builtin_delete(void *p) { free(p); }
33 void __rtti_user() { abort(); }
34 void __rtti_si() { abort(); }
35 void terminate() { abort(); }
36 extern "C" void __pure_virtual();
37 void __pure_virtual() { abort(); }
38 #endif
39 
40 #if OBJDEP
41 #  warning PROVIDES: sam2p_main
42 #  warning REQUIRES: gensi.o
43 #  warning REQUIRES: gensio.o
44 #  warning REQUIRES: image.o
45 #  warning REQUIRES: error.o
46 #  warning REQUIRES: encoder.o
47 #  warning REQUIRES: minips.o
48 #  warning REQUIRES: rule.o
49 #endif
50 
51 #include <stdio.h>
52 #include <string.h> /* memset() */
53 
54 #if OBJDEP
55 #  warning REQUIRES: in_tiff.o
56 #  warning REQUIRES: in_jpeg.o
57 #  warning REQUIRES: in_png.o
58 #  warning REQUIRES: in_jai.o
59 #  warning REQUIRES: in_pcx.o
60 #  warning REQUIRES: in_xpm.o
61 #  warning REQUIRES: in_lbm.o
62 #  warning REQUIRES: in_gif.o
63 #  warning REQUIRES: in_bmp.o
64 #  warning REQUIRES: in_pnm.o
65 #  warning REQUIRES: in_tga.o
66 /*#  warning REQUIRES: in_pdf.o Dat: no more, integrated to in_ps.o */
67 #  warning REQUIRES: in_ps.o
68 #endif
69 extern Image::Loader in_tiff_loader;
70 extern Image::Loader in_jpeg_loader;
71 extern Image::Loader in_png_loader;
72 extern Image::Loader in_jai_loader;
73 extern Image::Loader in_pcx_loader;
74 extern Image::Loader in_xpm_loader;
75 extern Image::Loader in_lbm_loader;
76 extern Image::Loader in_gif_loader;
77 extern Image::Loader in_bmp_loader;
78 extern Image::Loader in_pnm_loader;
79 extern Image::Loader in_tga_loader;
80 extern Image::Loader in_pdf_loader;
81 extern Image::Loader in_ps_loader;
init_loader()82 void init_loader() {
83   static bool had_init_loader=false;
84   if (had_init_loader) return;
85   Image::register0(&in_tga_loader); /* checker not sure; install early */
86   Image::register0(&in_pcx_loader);
87   Image::register0(&in_xpm_loader);
88   Image::register0(&in_lbm_loader);
89   Image::register0(&in_gif_loader);
90   Image::register0(&in_bmp_loader);
91   Image::register0(&in_pnm_loader);
92   Image::register0(&in_tiff_loader);
93   Image::register0(&in_jpeg_loader);
94   Image::register0(&in_png_loader);
95   Image::register0(&in_jai_loader);
96   Image::register0(&in_pdf_loader);
97   Image::register0(&in_ps_loader);
98   had_init_loader=true;
99 }
100 
101 #if OBJDEP
102 #  warning REQUIRES: appliers.o
103 #endif
104 extern Rule::Applier out_pnm_applier;
105 extern Rule::Applier out_jpeg_applier;
106 extern Rule::Applier out_jpegjai_applier;
107 extern Rule::Applier out_tiffjai_applier;
108 extern Rule::Applier out_tiff_applier;
109 extern Rule::Applier out_png_applier;
110 extern Rule::Applier out_bmp_applier;
111 extern Rule::Applier out_gif89a_applier;
112 extern Rule::Applier out_xpm_applier;
113 extern Rule::Applier out_l1c_applier;
114 // extern Rule::Applier out_l1op_applier;
115 extern Rule::Applier out_l1tr_applier;
116 extern Rule::Applier out_l23_applier;
117 // extern Rule::Applier out_l1fa85g_applier;
118 extern Rule::Applier out_l2jbin_applier;
119 // extern Rule::Applier out_p0jbin_applier;
120 extern Rule::Applier out_empty_applier;
121 extern Rule::Applier out_meta_applier;
122 extern Rule::Applier out_xwd_applier;
123 extern Rule::Applier out_x11_applier;
init_applier()124 void init_applier() {
125   static bool had_init_applier=false;
126   if (had_init_applier) return;
127   // Rule::register0(&out_l1op_applier);
128   Rule::register0(&out_l1tr_applier);
129   // Rule::register0(&out_l1fa85g_applier);
130   Rule::register0(&out_l2jbin_applier);
131   // Rule::register0(&out_p0jbin_applier);
132   Rule::register0(&out_l23_applier);
133   Rule::register0(&out_l1c_applier);
134   Rule::register0(&out_xpm_applier);
135   Rule::register0(&out_gif89a_applier);
136   Rule::register0(&out_pnm_applier);
137   Rule::register0(&out_jpeg_applier);
138   Rule::register0(&out_jpegjai_applier);
139   Rule::register0(&out_tiffjai_applier);
140   Rule::register0(&out_tiff_applier);
141   Rule::register0(&out_png_applier);
142   Rule::register0(&out_bmp_applier);
143   Rule::register0(&out_empty_applier);
144   Rule::register0(&out_meta_applier);
145   Rule::register0(&out_xwd_applier);
146   Rule::register0(&out_x11_applier);
147   had_init_applier=true;
148 }
149 
150 static char bts_ttt[] =
151 #include "bts2.tth"
152 ;
153 
154 /* --- One-liner mode */
155 
156 /** Dat: mod 16 does matter: expected # args etc. */
157 static const unsigned
158   OPT_unknown=0,
159   OPT_SampleFormat=0x02,
160   OPT_LoadHints=0x12,
161   OPT_Compression=0x22,
162   OPT_TransferEncoding=0x32,
163   OPT_TransferEncodingF=0x42,
164   // OPT_Asis=0x50,
165   OPT_PSL1=0x60,
166   OPT_PSLC=0x70,
167   OPT_PSL2=0x80,
168   OPT_PSL3=0x90,
169   OPT_PS=0xA1,
170   OPT_PDF=0xB1,
171   OPT_DisplayJobFile=0xC1,
172   OPT_Hints=0xD2,
173   // OPT_InputFile=0xE2,
174   OPT_OutputFile=0xF2,
175   OPT_Scale=0x101,
176   OPT_Margins=0x112,
177   OPT_Transparent=0x122,
178   OPT_TmpRemove=0x132;
179 
180 /** @param s an option (lower/upper case intact), without leading `-'s
181  * @param slen length of option
182  * @return          0  if invalid/unsupported/unknown option
183  *       | 16*(k+1)+0  if the option never accepts parameters
184  *       | 16*(k+2)+1  if the option may or may not have a parameter
185  *       | 16*(k+3)+2  if the option must have a parameter
186  */
sam2p_optval(char const * s,slen_t slen)187 static unsigned sam2p_optval(char const* s, slen_t slen) {
188   if (slen==1) {
189     switch (s[0]) {
190      case 's': return OPT_SampleFormat;
191      case 'l': return OPT_LoadHints;
192      case 'h': return OPT_Hints;
193      case 't': return OPT_TransferEncoding;
194      case 'f': return OPT_TransferEncodingF;
195      case 'c': return OPT_Compression;
196      // case 'a': return OPT_Asis; /* disabled, automatic! */
197      case '1': return OPT_PSL1;
198      case '2': return OPT_PSL2;
199      case '3': return OPT_PSL3;
200      case 'j': return OPT_DisplayJobFile;
201      case 'o': case 'O': return OPT_OutputFile;
202      case 'e': return OPT_Scale;
203      case 'm': return OPT_Margins;
204     }
205   } else {
206     slen_t len=slen; /* strlen(s); */
207     if (len>=32) return OPT_unknown;
208     char buf[32];
209     GenBuffer::tolower_memcpy(buf, s, len);
210     buf[len]='\0';
211     /* printf("buf=(%s)\n", buf); */
212     if (0==strcmp(buf, "sampleformat")) return OPT_SampleFormat;
213     if (0==strcmp(buf, "loadhints")) return OPT_LoadHints;
214     if (0==strcmp(buf, "tmpremove")) return OPT_TmpRemove;
215     if (0==strcmp(buf, "transparent")) return OPT_Transparent;
216     if (0==strcmp(buf, "hints")) return OPT_Hints;
217     if (0==strcmp(buf, "ps")
218      || 0==strcmp(buf, "eps")) return OPT_PS;
219     if (0==strcmp(buf, "pdf")) return OPT_PDF;
220     if (0==strcmp(buf, "1c")) return OPT_PSLC;
221     if (0==strcmp(buf, "scale")) return OPT_Scale;
222     if (0==strcmp(buf, "hints")) return OPT_Margins;
223   }
224   return OPT_unknown;
225 }
226 
displayJob(GenBuffer::Writable & sout,SimBuffer::Flat const & jobss)227 static void displayJob(GenBuffer::Writable &sout, SimBuffer::Flat const& jobss) {
228   fflush(stdout); fflush(stderr);
229   #if 0
230     puts("\n\n% begin sam2p job dump");
231     fwrite(stdout, 1, jobss.getLength(), jobss);
232     puts("% end sam2p job dump\n");
233   #else
234     /* vvv Dat: sout conflicts /OutputFile(-), but it is OK here */
235     sout << "\n% begin sam2p job dump\n"
236          << jobss
237          << "%)%)%)% end sam2p job dump\n\n";
238   #endif
239   fflush(stdout); fflush(stderr);
240 }
241 
protect_null(char const * s)242 static inline char const *protect_null(char const *s) {
243   return s==(char const*)NULLP ? "//" : s;
244 }
245 
246 /** @return the value for the parameter if matches key or the first letter of
247  * key; or NULLP if no match.
248  * @param key lower case
249  */
one_pabbr(char const * param,char const * key)250 static char const*one_pabbr(char const*param, char const *key) {
251   return GenBuffer::nocase_strbegins(param, key) ? param+strlen(key)
252        : (param[0]==key[0] || param[0]==key[0]+'A'-'a') && param[1]==':' ? param+2
253        : (char const*)NULLP;
254 }
255 
256 #if 0 /* cannot set badp... */
257 static inline void one_setdimen2(char const*&Dimen1, char const*&Dimen2, char const*p2) {
258   if (MiniPS::Real::isDimen(p2)) Dimen1=Dimen2=p2;
259   else Error::sev(Error::ERROR_CONT) << "one_liner: dimen expected" /*<< (SimBuffer::B().appendDumpC(p2, true))*/ << (Error*)0;
260 }
261 #endif
262 #if 0
263 #define one_setdimen2(Dimen1,Dimen2,p2) do { \
264   if (MiniPS::Real::isDimen(p2)) Dimen1=Dimen2=p2; \
265   else { badmsg="one_liner: dimen expected: "; goto bad_label; } \
266 } while(0)
267 #endif
268 
269 static Filter::UngetFILED *ufd;
270 static bool do_DisplayJobFile;
271 static bool buildProfile_quiet=false;
272 /** Creates an in-memory job file according to the command-line options.
273  * @param a argv+1
274  * @param job initially an empty string. On error, this function leaves it
275  * as-is, but on success, a valid job file is appened.
276  * @return true on syntax error
277  */
one_liner(SimBuffer::B & jobss,char const * const * a)278 static bool one_liner(SimBuffer::B &jobss, char const *const* a) {
279   /* Tue Jul  2 21:27:15 CEST 2002 */
280   char const *p, *pend;
281   bool no_option_term=true;
282   bool badp=false;
283   bool no_selector=true;
284   char const *badmsg;
285   // SimBuffer::B Profile /* MiniPS code */
286   SimBuffer::B Hints; /* MiniPS code */
287   /* ^^^ Imp: separate hint for each -c arg?? */
288   SimBuffer::B LoadHints;
289   bool TmpRemove_p=true;
290   Rule::Cache::pr_t Predictor=Rule::Cache::PR_None; /* Imp: separate for each Compression */
291   Rule::Cache::ff_t FileFormat=Rule::Cache::FF_default;
292   Rule::Cache::te_t TransferEncoding=Rule::Cache::TE_default;
293   do_DisplayJobFile=false;
294   bool do_stop_SampleFormat=false;
295   char const *Transparent=(char const*)NULLP; /* change 1 color to transparent unless NULL */
296   char const *OutputFile=(char const*)NULLP, *InputFile=(char const*)NULLP;
297   // SimBuffer::B tmp;
298   char const *TopMargin=(char const*)NULLP, *BottomMargin=(char const*)NULLP,
299              *LeftMargin=(char const*)NULLP, *RightMargin=(char const*)NULLP,
300              *LowerMargin=(char const*)NULLP, *ImageDPI=(char const*)NULLP;
301   bool negLowerMargin=false;
302   Rule::CacheHints::sc_t Scale=Rule::CacheHints::SC_default;
303   #define APPEND_sf(val) do { if (sfx[val]==0) { sfx[val]=1; sft[sflen++]=val; } } while (0)
304   Image::sf_t sft[Image::SF_max+1];       char sfx[Image::SF_max+1]; memset(sfx, 0, sizeof(sfx)); unsigned sflen=0;
305   #define APPEND_co(val) do { if (cox[val]==0) { cox[val]=1; cot[colen++]=val; } } while (0)
306   Rule::Cache::co_t cot[Rule::Cache::CO_max+1]; char cox[Image::SF_max+1]; memset(cox, 0, sizeof(cox)); unsigned colen=0;
307   /* ^^^ BUGFIX at 2002.12.02 */
308 
309   /** OPT_* value of current option */
310   unsigned opt;
311   /** Parameter for current option. May be NULL. */
312   char const *param=""; /* pacify VC6.0 */
313   char const *p2;
314   slen_t paramlen;
315   SimBuffer::B tmpnam; /* any dir -- keep on stack frame */
316 
317   buildProfile_quiet=true;
318 
319   for (; (p=*a)!=(char const*)NULLP; a++) {
320     if (p[0]=='-' && p[1]=='\0') { /* `-': Filename: STDIN or STDOUT */
321       #if 1
322         goto normal_label;
323       #else
324         Error::sev(Error::ERROR_CONT) << "one_liner: `-' (stdin|stdout) not allowed as filename" << (Error*)0;
325         badp=true;
326         continue;
327       #endif
328     } else if (p[0]=='-' && p[1]=='-' && p[2]=='\0') { /* `--': No more options */
329       if (a[1]==(char const*)NULLP && InputFile!=(char const*)NULLP) { /* `--' is last argument */
330         p=InputFile; /* OutputFile:=InputFile */
331         goto normal_label;
332       }
333       no_option_term=false;
334     } else if (p[0]=='-' && no_option_term) { /* an option */
335       while (*p=='-') p++; /* short options (-p) and long options (--pdf) are equivalent */
336       pend=p; while (*pend!='\0' && *pend!=':' && *pend!='=') pend++;
337       SimBuffer::Static optss(p, pend-p);
338       if (0==(opt=sam2p_optval(p, pend-p))) {
339         badmsg="one_liner: unknown option: ";
340        bad_label:
341         Error::sev(Error::ERROR_CONT) << badmsg << (SimBuffer::B().appendDumpC(optss, true)) << (Error*)0;
342         badp=true;
343         continue;
344       }
345       if ((opt&15)==0) { /* no parameters */
346         if (*pend!='\0') { badmsg="one_liner: don't give param to option: "; goto bad_label; }
347         param=(char const*)NULLP;
348       } else if ((opt&15)==1) { /* an optional parameter */
349         if (*pend!='\0') {
350           param=pend+1; while (*pend==*param) param++;
351         } else param=(char const*)NULLP;
352       } else if ((opt&15)==2) { /* a mandatory parameter */
353         if (*pend!='\0') {
354           param=pend+1; while (*pend==*param) param++;
355         } else if ((param=*++a)==(char const*)NULLP) { badmsg="one_liner: missing param for option: "; goto bad_label; }
356       } else assert(0);
357       paramlen=param==(char const*)NULLP ? 0 : strlen(param);
358 
359       /* Dat: now opt, paramlen and param are correct */
360       switch (opt) {
361        case OPT_LoadHints: LoadHints << ',' << param; break;
362        case OPT_TmpRemove: TmpRemove_p=GenBuffer::parseBool(param, paramlen); break;
363        case OPT_Transparent: Transparent=param; break; /* Imp: is this good memory management */
364        case OPT_Hints: Hints << '\n' << param; break;
365        case OPT_PSL1: FileFormat=Rule::Cache::FF_PSL1; break;
366        case OPT_PSLC: FileFormat=Rule::Cache::FF_PSLC; break;
367        case OPT_PSL2: FileFormat=Rule::Cache::FF_PSL2; break;
368        case OPT_PSL3: FileFormat=Rule::Cache::FF_PSL3; break;
369        case OPT_DisplayJobFile:
370         // fprintf(stderr, "param=(%s)\n", param);
371              if (paramlen==0 || 0==GenBuffer::nocase_strcmp(param, "job"))  do_DisplayJobFile=true;
372         else if (0==GenBuffer::nocase_strcmp(param, "warn")) buildProfile_quiet=false;
373         else if (GenBuffer::nocase_strbegins(param, "warn:")) buildProfile_quiet=!GenBuffer::parseBool(param+5, paramlen-5);
374         else if (0==GenBuffer::nocase_strcmp(param, "quiet")) { buildProfile_quiet=true; Error::setTopPrinted(Error::ERROR_CONT); } /* Dat: hide warnings, info etc. */ /* at Fri Aug 26 07:54:00 CEST 2005 */
375         else if (GenBuffer::nocase_strbegins(param, "job:")) do_DisplayJobFile=GenBuffer::parseBool(param+4, paramlen-4);
376         else do_DisplayJobFile=GenBuffer::parseBool(param, paramlen); /* Imp: better error report */
377         break;
378        case OPT_Margins:
379         /* Dat:abbreviation letters are distinct: [ahxvylrtubd] */
380              if (0!=(p2=one_pabbr(param,"all:"))) { all5:
381           if (MiniPS::Real::isDimen(p2)) LeftMargin=RightMargin=TopMargin=BottomMargin=LowerMargin=p2;
382           else { err_dimexp: badmsg="one_liner: dimen expected: "; goto bad_label; }
383         }
384         else if (0!=(p2=one_pabbr(param,"horiz:"))
385               || 0!=(p2=one_pabbr(param,"x:"))) { if (MiniPS::Real::isDimen(p2)) LeftMargin=RightMargin=p2; else goto err_dimexp; }
386         else if (0!=(p2=one_pabbr(param,"vert:"))
387               || 0!=(p2=one_pabbr(param,"y:"))) { if (MiniPS::Real::isDimen(p2)) TopMargin=BottomMargin=LowerMargin=p2; else goto err_dimexp; }
388         else if (0!=(p2=one_pabbr(param,"left:"))) { if (MiniPS::Real::isDimen(p2)) LeftMargin=p2; else goto err_dimexp; }
389         else if (0!=(p2=one_pabbr(param,"right:"))) { if (MiniPS::Real::isDimen(p2)) RightMargin=p2; else goto err_dimexp; }
390         else if (0!=(p2=one_pabbr(param,"dpi:"))) { if (MiniPS::Real::isDimen(p2)) ImageDPI=p2; else goto err_dimexp; }
391         else if (0!=(p2=one_pabbr(param,"top:"))
392               || 0!=(p2=one_pabbr(param,"up:"))) { if (MiniPS::Real::isDimen(p2)) TopMargin=p2; else goto err_dimexp; }
393         else if (0!=(p2=one_pabbr(param,"bottom:"))
394               || 0!=(p2=one_pabbr(param,"down:"))) { if (MiniPS::Real::isDimen(p2)) BottomMargin=LowerMargin=p2; else goto err_dimexp; }
395         else if (0!=(p2=one_pabbr(param,"raise:"))) { if (MiniPS::Real::isDimen(p2)) LowerMargin=p2; else goto err_dimexp; }
396         else if (0!=(p2=one_pabbr(param,"lower:"))) { negLowerMargin=true; if (MiniPS::Real::isDimen(p2)) LowerMargin=p2; else goto err_dimexp; }
397         else { p2=param; goto all5; }
398         break;
399        case OPT_Scale:
400              if (paramlen==0 || 0==GenBuffer::nocase_strcmp(param, "scale")) Scale=Rule::CacheHints::SC_OK;
401         else if (0==GenBuffer::nocase_strcmp(param, "none")) Scale=Rule::CacheHints::SC_None;
402         else if (0==GenBuffer::nocase_strcmp(param, "rot")) Scale=Rule::CacheHints::SC_RotateOK;
403         else if (0==GenBuffer::nocase_strcmp(param, "rotate")) Scale=Rule::CacheHints::SC_RotateOK;
404         else Scale=(Rule::CacheHints::sc_t)(GenBuffer::parseBool(param, paramlen) ? 0+Rule::CacheHints::SC_OK : 0+Rule::CacheHints::SC_None);
405         /* ^^^ Imp: better error report */
406         /* ^^^ +0: pacify g++-3.1 */
407         break;
408        case OPT_TransferEncoding:
409              if (0==GenBuffer::nocase_strcmp(param, "bin"))  TransferEncoding=Rule::Cache::TE_Binary;
410         else if (0==GenBuffer::nocase_strcmp(param, "hex"))  TransferEncoding=Rule::Cache::TE_Hex;
411         else if (0==GenBuffer::nocase_strcmp(param, "a85"))  TransferEncoding=Rule::Cache::TE_A85;
412         else if (0==GenBuffer::nocase_strcmp(param, "ascii"))TransferEncoding=Rule::Cache::TE_ASCII;
413         else if (0==GenBuffer::nocase_strcmp(param, "lsb1")) TransferEncoding=Rule::Cache::TE_LSBfirst;
414         else if (0==GenBuffer::nocase_strcmp(param, "msb1")) TransferEncoding=Rule::Cache::TE_MSBfirst;
415         else { inv_par: badmsg="one_liner: invalid param for option: "; goto bad_label; }
416         break;
417        case OPT_TransferEncodingF:
418              if (0==GenBuffer::nocase_strcmp(param, "lsb2msb")) TransferEncoding=Rule::Cache::TE_LSBfirst;
419         else if (0==GenBuffer::nocase_strcmp(param, "msb2lsb")) TransferEncoding=Rule::Cache::TE_MSBfirst;
420         else goto inv_par;
421         break;
422        case OPT_PS:
423              if (param==(char const*)NULLP || param[0]=='\0') FileFormat=Rule::Cache::FF_eps;
424         else if (0==GenBuffer::nocase_strcmp(param, "1")) FileFormat=Rule::Cache::FF_PSL1;
425         else if (0==GenBuffer::nocase_strcmp(param, "1c")
426               || 0==GenBuffer::nocase_strcmp(param, "c")) FileFormat=Rule::Cache::FF_PSLC;
427         else if (0==GenBuffer::nocase_strcmp(param, "2")) FileFormat=Rule::Cache::FF_PSL2;
428         else if (0==GenBuffer::nocase_strcmp(param, "3")) FileFormat=Rule::Cache::FF_PSL3;
429         else goto inv_par;
430         break;
431        case OPT_PDF:
432              if (param==(char const*)NULLP || param[0]=='\0') FileFormat=Rule::Cache::FF_pdfb;
433         else if (0==GenBuffer::nocase_strcmp(param, "b")) FileFormat=Rule::Cache::FF_pdfb; /* BI */
434         else if (0==GenBuffer::nocase_strcmp(param, "x")) FileFormat=Rule::Cache::FF_pdf; /* XObject */
435         else if (0==GenBuffer::nocase_strcmp(param, "b0")) FileFormat=Rule::Cache::FF_PDFB10;
436         else if (0==GenBuffer::nocase_strcmp(param, "b2")) FileFormat=Rule::Cache::FF_PDFB12;
437         else if (0==GenBuffer::nocase_strcmp(param, "0"))  FileFormat=Rule::Cache::FF_PDF10;
438         else if (0==GenBuffer::nocase_strcmp(param, "2"))  FileFormat=Rule::Cache::FF_PDF12;
439         else goto inv_par;
440         break;
441        case OPT_SampleFormat:
442         pend=param;
443         while (*pend!='\0') {
444           while (*pend==':') pend++;
445           if (do_stop_SampleFormat) goto inv_par; /* already stopped */
446           p=pend; while (*pend!='\0' && *pend!=':')  pend++;
447           paramlen=pend-p;
448           if (4==paramlen && 0==memcmp(p,"stop",4)) { do_stop_SampleFormat=true; buildProfile_quiet=false; break; }
449           if (5==paramlen && 0==memcmp(p,"stopq",5)) { do_stop_SampleFormat=true; break; }
450           if (2==paramlen && 0==memcmp(p,"tr",2)) {
451             APPEND_sf(Image::SF_Transparent);
452             APPEND_sf(Image::SF_Opaque);
453             APPEND_sf(Image::SF_Mask);
454             APPEND_sf(Image::SF_Transparent2);
455             APPEND_sf(Image::SF_Transparent4);
456             APPEND_sf(Image::SF_Transparent8);
457           } else {
458             Image::sf_t sf=Rule::Cache::parseSampleFormat(p, paramlen);
459             if (sf==Image::SF_max) goto inv_par;
460             APPEND_sf(sf);
461           }
462         }
463         break;
464        case OPT_Compression:
465         assert(param!=(char const*)NULLP);
466              if (0==GenBuffer::nocase_strcmp(param, "none")) APPEND_co(Rule::Cache::CO_None);
467         else if (0==GenBuffer::nocase_strcmp(param, "lzw"))  APPEND_co(Rule::Cache::CO_LZW);
468         else if (0==GenBuffer::nocase_strcmp(param, "zip"))  APPEND_co(Rule::Cache::CO_ZIP);
469         else if (0==GenBuffer::nocase_strcmp(param, "rle")
470               || 0==GenBuffer::nocase_strcmp(param, "packbits")) APPEND_co(Rule::Cache::CO_RLE);
471         else if (0==GenBuffer::nocase_strcmp(param, "dct"))  { APPEND_co(Rule::Cache::CO_DCT); Hints << "\n/DCT<<>>"; }
472         else if (0==GenBuffer::nocase_strcmp(param, "jpg")
473               || 0==GenBuffer::nocase_strcmp(param, "jpeg")) { APPEND_co(Rule::Cache::CO_JAI); APPEND_co(Rule::Cache::CO_IJG); }
474         else if (0==GenBuffer::nocase_strcmp(param, "ijg"))  APPEND_co(Rule::Cache::CO_IJG);
475         else if (0==GenBuffer::nocase_strcmp(param, "g4"))   { APPEND_co(Rule::Cache::CO_Fax); Hints << "\n/K -1"; }
476         else if (0==GenBuffer::nocase_strcmp(param, "g3")
477               || 0==GenBuffer::nocase_strcmp(param, "fax")
478               || 0==GenBuffer::nocase_strcmp(param, "g3:1d"))APPEND_co(Rule::Cache::CO_Fax);
479         else if (0==GenBuffer::nocase_strcmp(param, "g3:2d")){ APPEND_co(Rule::Cache::CO_Fax); Hints << "\n/K -2"; }
480         else if (0==GenBuffer::nocase_strcmp(param, "jai"))  APPEND_co(Rule::Cache::CO_JAI);
481         else if (GenBuffer::nocase_strbegins(param, "lzw:")) {
482           SimBuffer::Static s(param+4);
483           unsigned long i; /* toInteger() is quickest on long */
484           if (s.toInteger(i)) goto inv_par;
485           APPEND_co(Rule::Cache::CO_LZW); Predictor=(Rule::Cache::pr_t)i;
486         } else if (GenBuffer::nocase_strbegins(param, "rle:")) {
487           SimBuffer::Static s(param+4);
488           unsigned long i; /* toInteger() is quickest on long */
489           if (s.toInteger(i)) goto inv_par;
490           APPEND_co(Rule::Cache::CO_RLE); Hints << "\n/RecordSize " << i;
491         } else if (GenBuffer::nocase_strbegins(param, "packbits:")) {
492           SimBuffer::Static s(param+9);
493           unsigned long i; /* toInteger() is quickest on long */
494           if (s.toInteger(i)) goto inv_par;
495           APPEND_co(Rule::Cache::CO_RLE); Hints << "\n/RecordSize " << i;
496         } else if (GenBuffer::nocase_strbegins(param, "fax:")) {
497           SimBuffer::Static s(param+4);
498           long i; /* toInteger() is quickest on long */
499           if (s.toInteger(i)) goto inv_par;
500           APPEND_co(Rule::Cache::CO_Fax); Hints << "\n/K " << i;
501         } else if (GenBuffer::nocase_strbegins(param, "dct:")) {
502           APPEND_co(Rule::Cache::CO_DCT); Hints << "\n/DCT<< " << (param+4) << " >>";
503         } else if (GenBuffer::nocase_strbegins(param, "ijg:")) {
504           SimBuffer::Static s(param+4);
505           unsigned long i; /* toInteger() is quickest on long */
506           if (s.toInteger(i)) goto inv_par;
507           APPEND_co(Rule::Cache::CO_IJG); Hints << "\n/Quality " << i;
508         } else if (GenBuffer::nocase_strbegins(param, "jpeg:")) {
509           SimBuffer::Static s(param+5);
510           unsigned long i; /* toInteger() is quickest on long */
511           if (s.toInteger(i)) goto inv_par;
512           APPEND_co(Rule::Cache::CO_JAI);
513           APPEND_co(Rule::Cache::CO_IJG); Hints << "\n/Quality " << i;
514         } else if (GenBuffer::nocase_strbegins(param, "zip:")) {
515           pend=param+4; while (*pend!='\0' && *pend!=':') pend++;
516           SimBuffer::Static s(param+4, pend-param-4);
517           unsigned long i; /* toInteger() is quickest on long */
518           if (s.toInteger(i)) goto inv_par;
519           APPEND_co(Rule::Cache::CO_ZIP); Predictor=(Rule::Cache::pr_t)i;
520           if (*pend==':') {
521             pend++; while (*pend==':') pend++;
522             SimBuffer::Static s(pend);
523             if (s.toInteger(i)) goto inv_par;
524             // Error::sev(Error::FATAL) << "SUXX)" << s << ',' << i << '.' << (Error*)0;
525             APPEND_co(Rule::Cache::CO_ZIP); Hints << "\n/Effort " << i;
526             // Error::sev(Error::FATAL) << "SUXX(" << s << ',' << Hints << '.' << (Error*)0;
527           }
528         } else goto inv_par;
529         break;
530        default: assert(0);
531       }
532     } else { /* a selector or a normal argument */
533       if (!(no_selector && no_option_term)) goto normal_label;
534       pend=p; while (*pend!='\0' && *pend!=':') pend++;
535       if (pend-p>=2 && *pend==':') { /* an ImageMagick-style FileFormat selector */
536              if (GenBuffer::nocase_strbegins(p, "PSL2:")
537               || GenBuffer::nocase_strbegins(p, "EPS2:")) FileFormat=Rule::Cache::FF_PSL2;
538         else if (GenBuffer::nocase_strbegins(p, "EPS:"))  FileFormat=Rule::Cache::FF_eps;
539         else if (GenBuffer::nocase_strbegins(p, "PS2:"))  { FileFormat=Rule::Cache::FF_PSL2; if (Scale==Rule::CacheHints::SC_default) Scale=Rule::CacheHints::SC_RotateOK; }
540         else if (GenBuffer::nocase_strbegins(p, "PS:"))   { FileFormat=Rule::Cache::FF_eps;  if (Scale==Rule::CacheHints::SC_default) Scale=Rule::CacheHints::SC_RotateOK; }
541         else if (GenBuffer::nocase_strbegins(p, "PSL1:")) FileFormat=Rule::Cache::FF_PSL1;
542         else if (GenBuffer::nocase_strbegins(p, "PSLC:")) FileFormat=Rule::Cache::FF_PSLC;
543         else if (GenBuffer::nocase_strbegins(p, "PSL3:")) FileFormat=Rule::Cache::FF_PSL3;
544         else if (GenBuffer::nocase_strbegins(p, "PDF:")
545               || GenBuffer::nocase_strbegins(p, "PDF:"))  FileFormat=Rule::Cache::FF_pdfb;
546         else if (GenBuffer::nocase_strbegins(p, "PDFX:")) FileFormat=Rule::Cache::FF_pdf;
547         else if (GenBuffer::nocase_strbegins(p, "PDFB1.0:")) FileFormat=Rule::Cache::FF_PDFB10;
548         else if (GenBuffer::nocase_strbegins(p, "PDFB1.2:")) FileFormat=Rule::Cache::FF_PDFB12;
549         else if (GenBuffer::nocase_strbegins(p, "PDF1.0:"))  FileFormat=Rule::Cache::FF_PDF10;
550         else if (GenBuffer::nocase_strbegins(p, "PDF1.2:"))  FileFormat=Rule::Cache::FF_PDF12;
551         else if (GenBuffer::nocase_strbegins(p, "GIF89a:")
552               || GenBuffer::nocase_strbegins(p, "GIF:"))  FileFormat=Rule::Cache::FF_GIF89a;
553         /* vvv do_stop_SampleFormat BUGFIX at Thu Nov 21 23:44:20 CET 2002 */
554         else if (GenBuffer::nocase_strbegins(p, "PNM:"))  { FileFormat=Rule::Cache::FF_PNM; APPEND_sf(Image::SF_Gray1); APPEND_sf(Image::SF_Gray8); APPEND_sf(Image::SF_Rgb8); APPEND_sf(Image::SF_Transparent8); do_stop_SampleFormat=true; }
555         else if (GenBuffer::nocase_strbegins(p, "PBM:"))  { FileFormat=Rule::Cache::FF_PNM; APPEND_sf(Image::SF_Gray1); do_stop_SampleFormat=true; }
556         else if (GenBuffer::nocase_strbegins(p, "PGM:"))  { FileFormat=Rule::Cache::FF_PNM; APPEND_sf(Image::SF_Gray8); do_stop_SampleFormat=true; }
557         else if (GenBuffer::nocase_strbegins(p, "PPM:"))  { FileFormat=Rule::Cache::FF_PNM; APPEND_sf(Image::SF_Rgb8);  do_stop_SampleFormat=true; }
558         else if (GenBuffer::nocase_strbegins(p, "PAM:"))  FileFormat=Rule::Cache::FF_PAM;
559         else if (GenBuffer::nocase_strbegins(p, "PIP:"))  FileFormat=Rule::Cache::FF_PIP;
560         else if (GenBuffer::nocase_strbegins(p, "Empty:"))FileFormat=Rule::Cache::FF_Empty;
561         else if (GenBuffer::nocase_strbegins(p, "Meta:")) FileFormat=Rule::Cache::FF_Meta;
562         else if (GenBuffer::nocase_strbegins(p, "JPEG:")
563               || GenBuffer::nocase_strbegins(p, "JPG:"))  FileFormat=Rule::Cache::FF_JPEG;
564         else if (GenBuffer::nocase_strbegins(p, "TIFF:")
565               || GenBuffer::nocase_strbegins(p, "TIF:"))  FileFormat=Rule::Cache::FF_TIFF;
566         else if (GenBuffer::nocase_strbegins(p, "PNG:"))  FileFormat=Rule::Cache::FF_PNG;
567         else if (GenBuffer::nocase_strbegins(p, "XPM:"))  FileFormat=Rule::Cache::FF_XPM;
568         else if (GenBuffer::nocase_strbegins(p, "BMP:")
569               || GenBuffer::nocase_strbegins(p, "RLE:"))  { FileFormat=Rule::Cache::FF_BMP; /*APPEND_co(Rule::Cache::CO_RLE);*/ }
570         else if (GenBuffer::nocase_strbegins(p, "XWD:"))  FileFormat=Rule::Cache::FF_XWD;
571         else if (GenBuffer::nocase_strbegins(p, "X11:"))  FileFormat=Rule::Cache::FF_X11;
572         else {
573           Error::sev(Error::ERROR_CONT) << "one_liner: invalid FileFormat selector: " << (SimBuffer::B().appendDumpC(SimBuffer::Static(p, pend-p), true)) << (Error*)0;
574           badp=true;
575           continue;
576         }
577         no_selector=false;
578         while (*pend==':') pend++;
579         if (*pend=='\0') continue; /* this arg is just a selector */
580         /* normal argument is coming */
581         p=pend;
582       }
583      normal_label:
584       if (InputFile==(char const*)NULLP) {
585         InputFile=p; /* set it even on error */
586 	/* vvv since Sat Apr 19 13:37:57 CEST 2003:
587 	 * Possibly unseekable STDIN is handled by Filter::UngetFILED
588 	 */
589 	#if 0
590 	  if (p[0]=='-' && p[1]=='\0') { /* Filename: STDIN */
591 	    Files::set_binary_mode(0, true);
592 	    if (0!=fseek(stdin, 0L, 0)) { unseekable:
593 	      #if 0
594 		Error::sev(Error::ERROR_CONT) << "one_liner: `-' (stdin) not allowed as InputFile (stdin unseekable)" << (Error*)0;
595 		badp=true; continue;
596 	      #endif
597 	      FILE *f=Files::open_tmpnam(tmpnam);
598 	      if (!f) { Error::sev(Error::ERROR_CONT) << "one_liner: cannot open" << " temporary file for `-' (stdin)" << (Error*)0; badp=true; continue; }
599 	      tmpnam.term0();
600 	      Files::tmpRemoveCleanup(InputFile=tmpnam());
601 	      char *buf=new char[4096];
602 	      unsigned got;
603 	      while (
604 		   (0<(got=fread(buf, 1, sizeof(buf), stdin)))
605 		&& got==fwrite(buf, 1, sizeof(buf), f)) {}
606 	      delete [] buf;
607 	      if (ferror(f) || 0!=fclose(f) || ferror(stdin))
608 		{ Error::sev(Error::ERROR_CONT) << "one_liner: cannot write" << " temporary file for `-' (stdin)" << (Error*)0; badp=true; continue; }
609 	      // fprintf(stderr, "if=(%s)\n", InputFile);
610 	      continue;
611 	      /* Imp: report `-' as filename on errors etc. */
612 	    }
613 	    int c=MACRO_GETC(stdin);
614 	    if (0!=fseek(stdin, 0L, 0)) { if (c>=0) ungetc(c, stdin); goto unseekable; } /* active test */
615 	    /* ungetc() is not necessary because of fseek() */
616 	  }
617 	#endif
618       } else if (OutputFile==(char const*)NULLP) { /* Filename: STDOUT */
619         Files::set_binary_mode(1, true);
620         OutputFile=p;
621         if (FileFormat==Rule::Cache::FF_default) { /* OutputFile; determine FileFormat from extension */
622           pend=p+strlen(p);
623           while (pend!=p && *pend!='.') pend--;
624           /* ^^^ Dat: extra care later for /a/b.c/d */
625           if (pend!=p) { /* have extension */
626             assert(*pend=='.'); pend++;
627                  if (0==GenBuffer::nocase_strcmp(pend, "eps")
628                   || 0==GenBuffer::nocase_strcmp(pend, "epsi")
629                   || 0==GenBuffer::nocase_strcmp(pend, "epsf")) FileFormat=Rule::Cache::FF_eps;
630             else if (0==GenBuffer::nocase_strcmp(pend, "ps"))   { FileFormat=Rule::Cache::FF_eps; if (Scale==Rule::CacheHints::SC_default) Scale=Rule::CacheHints::SC_RotateOK; }
631             else if (0==GenBuffer::nocase_strcmp(pend, "pdf"))  FileFormat=Rule::Cache::FF_pdfb;
632             else if (0==GenBuffer::nocase_strcmp(pend, "gif"))  FileFormat=Rule::Cache::FF_GIF89a;
633             else if (0==GenBuffer::nocase_strcmp(pend, "pnm"))  { FileFormat=Rule::Cache::FF_PNM; APPEND_sf(Image::SF_Gray1); APPEND_sf(Image::SF_Gray8); APPEND_sf(Image::SF_Rgb8); APPEND_sf(Image::SF_Transparent8); do_stop_SampleFormat=true; }
634             else if (0==GenBuffer::nocase_strcmp(pend, "pbm"))  { FileFormat=Rule::Cache::FF_PNM; APPEND_sf(Image::SF_Gray1); do_stop_SampleFormat=true; }
635             else if (0==GenBuffer::nocase_strcmp(pend, "pgm"))  { FileFormat=Rule::Cache::FF_PNM; APPEND_sf(Image::SF_Gray8); do_stop_SampleFormat=true; }
636             else if (0==GenBuffer::nocase_strcmp(pend, "ppm"))  { FileFormat=Rule::Cache::FF_PNM; APPEND_sf(Image::SF_Rgb8);  do_stop_SampleFormat=true; }
637             else if (0==GenBuffer::nocase_strcmp(pend, "pam"))  FileFormat=Rule::Cache::FF_PAM;
638             else if (0==GenBuffer::nocase_strcmp(pend, "pip"))  FileFormat=Rule::Cache::FF_PIP;
639             else if (0==GenBuffer::nocase_strcmp(pend, "empty"))FileFormat=Rule::Cache::FF_Empty;
640             else if (0==GenBuffer::nocase_strcmp(pend, "meta")) FileFormat=Rule::Cache::FF_Meta;
641             else if (0==GenBuffer::nocase_strcmp(pend, "jpeg")
642                   || 0==GenBuffer::nocase_strcmp(pend, "jpg"))  FileFormat=Rule::Cache::FF_JPEG;
643             else if (0==GenBuffer::nocase_strcmp(pend, "tiff")
644                   || 0==GenBuffer::nocase_strcmp(pend, "tif"))  FileFormat=Rule::Cache::FF_TIFF;
645             else if (0==GenBuffer::nocase_strcmp(pend, "png"))  FileFormat=Rule::Cache::FF_PNG;
646             else if (0==GenBuffer::nocase_strcmp(pend, "xpm"))  FileFormat=Rule::Cache::FF_XPM;
647             else if (0==GenBuffer::nocase_strcmp(pend, "bmp")
648                   || 0==GenBuffer::nocase_strcmp(pend, "rle"))  { FileFormat=Rule::Cache::FF_BMP; /*APPEND_co(Rule::Cache::CO_RLE);*/ }
649             else if (0==GenBuffer::nocase_strcmp(pend, "xwd"))  FileFormat=Rule::Cache::FF_XWD;
650             // else if (0==GenBuffer::nocase_strcmp(pend, "x11"))  FileFormat=Rule::Cache::FF_X11;
651             /* ^^^ .x11 extension is useless */
652           }
653         }
654       } else {
655         Error::sev(Error::ERROR_CONT) << "one_liner: got too many (>2) filenames" << (Error*)0;
656         badp=true;
657         continue;
658       }
659     } /* IF */
660   } /* NEXT */
661 
662   if (InputFile==(char const*)NULLP) {
663     Error::sev(Error::ERROR_CONT) << "one_liner: InputFile unspecified" << (Error*)0;
664     badp=true;
665   }
666   if (OutputFile==(char const*)NULLP) {
667     Error::sev(Error::ERROR_CONT) << "one_liner: OutputFile unspecified" << (Error*)0;
668     badp=true;
669   }
670   if (FileFormat==Rule::Cache::FF_default) {
671     Error::sev(Error::ERROR_CONT) << "one_liner: FileFormat unspecified" << (Error*)0;
672     badp=true;
673   }
674   if (badp) return true;
675 
676   int prcc = (colen==0) ? 2 : 1;
677   if (colen==0) { /* apply default if Compression is unspecified */
678     // Error::sev(Error::FATAL) << "FileFormat=" << (unsigned)FileFormat << (Error*)0;
679     switch (FileFormat) {
680      case Rule::Cache::FF_TIFF:
681      case Rule::Cache::FF_eps: case Rule::Cache::FF_PSL2:
682      case Rule::Cache::FF_PDFB10: case Rule::Cache::FF_PDF10:
683       APPEND_co(Rule::Cache::CO_JAI);
684       #if HAVE_LZW
685        APPEND_co(Rule::Cache::CO_LZW);
686       #endif
687       APPEND_co(Rule::Cache::CO_RLE);
688       APPEND_co(Rule::Cache::CO_None); break;
689      case Rule::Cache::FF_BMP:
690      case Rule::Cache::FF_PSL1: case Rule::Cache::FF_PSLC:
691       /* assert(0); */
692       APPEND_co(Rule::Cache::CO_RLE);
693       APPEND_co(Rule::Cache::CO_None); break;
694      case Rule::Cache::FF_PSL3:
695      case Rule::Cache::FF_pdfb: /* BUGFIX at Sun Sep 22 14:57:03 CEST 2002 */
696      case Rule::Cache::FF_pdf:
697      case Rule::Cache::FF_PDFB12: case Rule::Cache::FF_PDF12:
698       APPEND_co(Rule::Cache::CO_JAI);
699       APPEND_co(Rule::Cache::CO_ZIP);
700       APPEND_co(Rule::Cache::CO_None); break;
701      case Rule::Cache::FF_GIF89a:
702       APPEND_co(Rule::Cache::CO_LZW); /* _not_ /LZWEncode filter */
703       APPEND_co(Rule::Cache::CO_None); break;
704      case Rule::Cache::FF_JPEG:
705       APPEND_co(Rule::Cache::CO_JAI);
706       APPEND_co(Rule::Cache::CO_IJG); break;
707      case Rule::Cache::FF_PNG:
708       APPEND_co(Rule::Cache::CO_ZIP); break;
709      default:
710       APPEND_co(Rule::Cache::CO_None); break;
711      /* for others FileFormats: CO_None, but that is _not_ appended anyway */
712     }
713   } else {
714     if ((int)Predictor == 55 || (int)Predictor == 45) {
715       Error::sev(Error::WARNING) << "predictor: /Predictor " << (int)Predictor
716           << " is inefficient; use /Predictor 15 instead" << (Error*)0;
717     }
718   }
719   /* Dat: don't append /Compression/None if the user has specified `-c'. The
720    *      user can append `-c none' manually.
721    */
722 
723   if (Scale==Rule::CacheHints::SC_default) Scale=Rule::CacheHints::SC_None;
724   if (TransferEncoding==Rule::Cache::TE_default) { /* apply default if TransferEncoding is unspecified */
725     switch (FileFormat) {
726      case Rule::Cache::FF_PSL1: case Rule::Cache::FF_PSLC:
727       TransferEncoding=(Rule::Cache::te_t)(cox[Rule::Cache::CO_ZIP]!=0 ||
728         cox[Rule::Cache::CO_LZW]!=0 ? 0+Rule::Cache::TE_A85 : 0+Rule::Cache::TE_Hex); break;
729       /* ^^^ Dat: /Hex is default for /RLE */
730      case Rule::Cache::FF_eps: case Rule::Cache::FF_PSL2:
731      case Rule::Cache::FF_PSL3:
732       TransferEncoding=Rule::Cache::TE_A85; break;
733     #if 0 /* useless bugfix */
734      case Rule::Cache::FF_XPM: /* BUGFIX at Thu Jul 11 21:53:56 CEST 2002 */
735       // assert(0);
736       TransferEncoding=Rule::Cache::TE_ASCII; break;
737     #endif
738      default:
739       TransferEncoding=Rule::Cache::TE_Binary; break;
740     }
741   }
742 
743   unsigned coc=colen;
744 
745   /* Smart verify whether /Compression/JAI is requested. If so, and the input
746    * file is JPEG, then load it as-is. Our heuristic is that if the user
747    * allowed /Compression/JAI among others, and the input file is a baseline
748    * JPEG, then /Compression/JAI will be the best (and only) compression
749    * method.
750    */
751   bool jaip=cox[Rule::Cache::CO_JAI]!=0;
752   if (jaip) {
753     if (ufd==NULLP) ufd=new Filter::UngetFILED(InputFile, stdin,
754       Filter::UngetFILED::CM_closep|Filter::UngetFILED::CM_keep_stdinp);
755     /* vvv Imp: no error handling */
756     if (1!=jai_is_baseline_jpeg(ufd)) { jaip=false; coc--; cox[Rule::Cache::CO_JAI]=0; }
757     ufd->seek(0);
758 #if 0
759     assert(ufd->vi_getcc()==255);
760     assert(ufd->vi_getcc()==0xd8);
761     assert(ufd->vi_getcc()==255+0);
762     assert(0);
763 #endif
764   }
765 
766   if (jaip) { /* Dat: might have changed to false */
767     APPEND_sf(Image::SF_Asis);
768     LoadHints << ",jpeg-asis,";
769     cot[0]=Rule::Cache::CO_JAI; colen=1; /* disable all other compression */
770   } else if (coc==1 && cox[Rule::Cache::CO_Fax]!=0) {
771     APPEND_sf(Image::SF_Opaque);
772     APPEND_sf(Image::SF_Transparent);
773     APPEND_sf(Image::SF_Gray1);
774     APPEND_sf(Image::SF_Indexed1);
775     APPEND_sf(Image::SF_Mask);
776   } else if (coc==1 && (cox[Rule::Cache::CO_DCT]!=0 || cox[Rule::Cache::CO_IJG]!=0)) {
777     APPEND_sf(Image::SF_Opaque);
778     APPEND_sf(Image::SF_Transparent);
779     APPEND_sf(Image::SF_Gray8);
780     APPEND_sf(Image::SF_Rgb8);
781   } else if (!do_stop_SampleFormat) {
782     /* JAI compression is automatically disabled since jaip==false */
783     /* Guess SampleFormat automatically. The order is significant, so most
784      * warnings emitted by Image::SampledInfo::setSampleFormat() are avoided.
785      */
786     APPEND_sf(Image::SF_Opaque);
787     APPEND_sf(Image::SF_Transparent);
788     APPEND_sf(Image::SF_Gray1);
789     APPEND_sf(Image::SF_Indexed1);
790     APPEND_sf(Image::SF_Mask);
791     APPEND_sf(Image::SF_Gray2);
792     APPEND_sf(Image::SF_Indexed2);
793     APPEND_sf(Image::SF_Rgb1);
794     APPEND_sf(Image::SF_Gray4);
795     APPEND_sf(Image::SF_Indexed4);
796     APPEND_sf(Image::SF_Rgb2);
797     APPEND_sf(Image::SF_Gray8);
798     APPEND_sf(Image::SF_Indexed8);
799     APPEND_sf(Image::SF_Rgb4);
800     APPEND_sf(Image::SF_Rgb8);
801     APPEND_sf(Image::SF_Transparent2); /* transparents are put last because of expensive color separation */
802     APPEND_sf(Image::SF_Transparent4);
803     APPEND_sf(Image::SF_Transparent8);
804   }
805 
806   /* Append more Hints */
807   if (Scale!=Rule::CacheHints::SC_None) Hints << "\n/Scale /" << protect_null(Rule::CacheHints::dumpScale(Scale));
808   if ((char const*)NULLP!=ImageDPI) Hints << "\n/ImageDPI " << ImageDPI;
809   if ((char const*)NULLP!=LeftMargin) Hints << "\n/LeftMargin " << LeftMargin;
810   if ((char const*)NULLP!=RightMargin) Hints << "\n/RightMargin " << RightMargin;
811   if ((char const*)NULLP!=TopMargin) Hints << "\n/TopMargin " << TopMargin;
812   if ((char const*)NULLP!=BottomMargin) Hints << "\n/BottomMargin " << BottomMargin;
813   if ((char const*)NULLP!=LowerMargin) {
814     Hints << "\n/LowerMargin ";
815     if (negLowerMargin) {
816       if (LowerMargin[0]=='-') Hints << (LowerMargin+1);
817                           else Hints << '-' << LowerMargin;
818     } else Hints << LowerMargin;
819   }
820 
821   /* Emit job file */
822   jobss << "<<%sam2p in-memory job file, autogenerated by " << Error::banner0
823         << "\n/InputFile ";
824   jobss.appendDumpPS(SimBuffer::Static(InputFile), true);
825   jobss << "\n/OutputFile ";
826   jobss.appendDumpPS(SimBuffer::Static(OutputFile), true);
827   jobss << "\n/LoadHints ";
828   jobss.appendDumpPS(LoadHints, true);
829   jobss << "\n/TmpRemove " << (TmpRemove_p ? "true" : "false");
830   jobss << "\n/Profile[\n";
831 
832   unsigned coi, sfi;
833   slen_t orc=0;
834   int prc;
835   bool is_predictor_recommended;
836   for (sfi=0;sfi<sflen;sfi++) { for (coi=0;coi<colen;coi++) {
837   // for (coi=0;coi<colen;coi++) for (sfi=0;sfi<sflen;sfi++)
838       Rule::Cache::co_t co=cot[coi];
839       Image::sf_t sf=sft[sfi];
840       if (co==Rule::Cache::CO_JAI) {
841         if (!jaip || sf!=Image::SF_Asis) continue;
842       }
843       for (prc=prcc; prc>0; --prc) {
844         /* Add an extra predictor only for LZW or ZIP, appropriate SampleFormat
845          * and if the `-c' command line option was not specified.
846          * So if the user specifies `-c zip', he won't get a predictor by
847          * default; but if he specifies no `-c', he might get a `-c:zip:15'.
848          * To get the smallest file size, he should specify `-c zip:15:9'.
849          *
850          * The value we assign to is_predictor_recommended comes from the
851          * do_filter = ... assignment in png_write_IHDR() in pngwutil.c of
852          * libpng-1.2.15 .
853          */
854         is_predictor_recommended = (
855             sf==Image::SF_Gray8 || sf==Image::SF_Rgb8);
856         if (prc>1 && ((co!=Rule::Cache::CO_LZW && co!=Rule::Cache::CO_ZIP) ||
857             !is_predictor_recommended)) continue;
858         jobss << "<<% OutputRule #" << orc++
859               << "\n  /FileFormat /" << protect_null(Rule::Cache::dumpFileFormat(FileFormat, co))
860               << "\n  /TransferEncoding /" << protect_null(Rule::Cache::dumpTransferEncoding(TransferEncoding))
861               << "\n  /SampleFormat /" << protect_null(Rule::Cache::dumpSampleFormat(sf))
862               << "\n  /Compression /"  << protect_null(Rule::Cache::dumpCompression (co))
863               << "\n  /Predictor " << (int)(co==Rule::Cache::CO_LZW || co==Rule::Cache::CO_ZIP ?
864                   /* If no compression (or predictor) specified, try predictor 15, then 1; otherwise try the specified predictor */
865                   /* +0 down there to avoid linking problems with g++ on Mac OS X 10.5.6 */
866                   (prc>1 ? Rule::Cache::PR_PNGAuto+0 : Predictor != Rule::Cache::PR_PNGAutoMaybe ? Predictor : is_predictor_recommended ? Rule::Cache::PR_PNGAuto+0 : Rule::Cache::PR_None+0) : Rule::Cache::PR_None+0)
867               << "\n  /Hints << " << Hints << " >>";
868         // jobss << "  /Transparent (\377\377\377)\n";
869         if (Transparent!=NULL) {
870           jobss << "\n  /Transparent ";
871           jobss.appendDumpPS(SimBuffer::Static(Transparent), true);
872         }
873         jobss  << "\n>>\n";
874       }
875     }
876   }
877   jobss << "]>>% __EOF__\n";
878 
879   /* Common keys not emited here:
880    * /TmpRemove true  /Templates pop
881    *    /ColorTransform pop
882    *    /EncoderColumns 0  /EncoderBPL 0 /EncoderRows 0  /EncoderColors 0
883    *    /PredictorColumns 0   /PredictorBPC 0  /PredictorColors 0
884    *  /Transparent null
885    *  /WarningOK true
886    *    /Comment  pop
887    *    /Title    pop
888    *    /Subject  pop
889    *    /Author   pop
890    *    /Creator  pop
891    *    /Producer pop
892    *    /Created  pop
893    *    /Produced pop
894    */
895   return false;
896   #undef APPEND_sf
897   #undef APPEND_co
898 } /* one_liner() */
899 
option_eq(char const * arg,char const * option)900 static bool option_eq(char const *arg, char const*option) {
901   if (option[0]=='-') { option++; while (arg[0]=='-') arg++; }
902   return 0==GenBuffer::nocase_strcmp(arg, option);
903 }
904 
init_sam2p_engine(char const * argv0)905 void init_sam2p_engine(char const*argv0) {
906   Error::long_argv0=argv0==(char const*)NULLP ? "sam2p" : argv0;
907   Error::argv0=Files::only_fext(Error::long_argv0);
908   Error::tmpargv0="_sam2p_";
909   Error::banner0="sam2p " SAM2P_VERSION;
910 }
911 
912 /* --- */
913 
914 /* Never returns. */
run_sam2p_engine(Files::FILEW & sout,Files::FILEW & serr,char const * const * argv1,bool helpp)915 int run_sam2p_engine(Files::FILEW &sout, Files::FILEW &serr, char const*const*argv1, bool helpp) {
916   class SerrResetter {
917    public:
918     SerrResetter(GenBuffer::Writable *old_serr_): old_serr(old_serr_) {}
919     ~SerrResetter() { Error::serr = old_serr; }
920    private:
921     GenBuffer::Writable *old_serr;
922   } serr_resetter(Error::serr);
923   Error::serr=&serr;
924 
925   /* --- Parse arguments, generate/read .job file */
926 
927   MiniPS::VALUE job=MiniPS::Qundef;
928   Filter::FlatD bts(bts_ttt);
929   ufd=(Filter::UngetFILED*)NULLP;
930   if (!helpp && argv1[0]!=(char const*)NULLP && argv1[1]==(char const*)NULLP) {
931     /* A single argument: must be the name of the .job file */
932     MiniPS::Parser p(argv1[0]);
933     p.addSpecRun("%bts", &bts); /* bts: Built-in TemplateS, the bts.ttt file in the sources */
934     job=p.parse1();
935     if (p.parse1(p.EOF_ALLOWED)!=MiniPS::Qundef)
936       Error::sev(Error::EERROR) << "job: the .job file should contain a single job" << (Error*)0;
937     /* ^^^ Dat: the result of the second p.parse1() doesn't get delete0(...)d */
938   } else if (helpp || argv1[0]==(char const*)NULLP) { help: /* help message */
939     sout << "Usage:   " << Error::long_argv0 << " <filename.job>\n" <<
940             "         " << Error::long_argv0 << " [options] <in.img> [OutputFormat:] <out.img>\n" <<
941             "Example: " << Error::long_argv0 << " test.gif EPS: test.eps\n";
942     if (helpp) Error::cexit(Error::runCleanups(0));
943     Error::sev(Error::EERROR) << "Incorrect command line" << (Error*)0;
944   } else { /* one_liner */
945     SimBuffer::B jobss;
946     if (one_liner(jobss, argv1)) goto help;
947     if (do_DisplayJobFile) displayJob(sout, jobss);
948     Filter::FlatD jobr(jobss(), jobss.getLength());
949     MiniPS::Parser p(&jobr);
950     p.addSpecRun("%bts", &bts); /* bts: Built-in TemplateS, the bts.ttt file in the sources */
951     if (MiniPS::Qerror==(job=p.parse1(p.EOF_ILLEGAL, Error::ERROR_CONT))
952      || p.parse1(p.EOF_ALLOWED)!=MiniPS::Qundef
953        ) {
954       displayJob(sout, jobss);
955       Error::sev(Error::EERROR) << "job: in-memory .job file corrupt (bug?" "?)" << (Error*)0;
956       /* ^^^ Dat: the result of the second p.parse1() doesn't get delete0(...)d */
957     }
958   }
959   Files::doSignalCleanup();
960 
961   /* --- Pre-parse .job file */
962 
963   /* Dat: memory storage for the MiniPS objects in the .job file are
964    *      allocated by p.parse1() and freed by MiniPS::delete0(job). Other
965    *      structures throughout this program hold pointers to inside `job',
966    *      but storage is not copied.
967    */
968 
969   MiniPS::String *InputFile, *OutputFile, *LoadHints;
970   MiniPS::Array *Profile;
971   MiniPS::VALUE TmpRemove;
972   MiniPS::scanf_dict(job, /*show_warnings:*/true,
973     "InputFile",  MiniPS::T_STRING, MiniPS::Qundef, &InputFile,
974     "OutputFile", MiniPS::T_STRING, MiniPS::Qundef, &OutputFile,
975     "LoadHints",  MiniPS::T_STRING, MiniPS::Qnull,  &LoadHints,
976     "Templates",  MiniPS::T_DICT,   MiniPS::Qnull,  &Rule::Templates,
977     "Profile",    MiniPS::T_ARRAY,  MiniPS::Qundef, &Profile,
978     "TmpRemove",  MiniPS::T_BOOLEAN,MiniPS::Qtrue,  &TmpRemove,   /* remove temporary files upon exit? */
979     NULLP
980   );
981   // MiniPS::dump(sout, job, 1);
982   Files::tmpRemove=TmpRemove==MiniPS::Qtrue;
983   if (MiniPS::Qnull==(MiniPS::VALUE)Rule::Templates) {
984     Filter::FlatD flatd("<< (%bts) run >>");
985     MiniPS::Parser p(&flatd);
986     p.addSpecRun("%bts", &bts);
987     Rule::Templates=(MiniPS::Dict*)p.parse1();
988     MiniPS::RDICT(job)->put("/Templates", (MiniPS::VALUE)Rule::Templates);  /* Takes ownership, deletes the previous value. */
989   }
990 
991   /* --- Preprocess Appliers */
992 
993   Rule::OutputRule *rule_list=Rule::buildProfile((MiniPS::VALUE)Profile, buildProfile_quiet);
994 
995   /* --- Read/open other files */
996 
997   if ((MiniPS::VALUE)LoadHints==MiniPS::Qnull) { /* smart default */
998     /* This isn't reached by default (sam2p t.png t.pdf), becuase `job'
999      * already contains /LoadHints ().
1000      */
1001 
1002     if (!rule_list[0].isEOL() && rule_list[0].cache.Compression==Rule::Cache::CO_JAI) {
1003       LoadHints=new MiniPS::String("jpeg-asis",4); /* used by in_jai.cpp and in_jpeg.cpp; even TIFF/JPEG */
1004     } else {
1005       LoadHints=new MiniPS::String("",0);
1006     }
1007     MiniPS::RDICT(job)->put("/LoadHints", (MiniPS::VALUE)LoadHints);  /* Takes ownership, deletes the previous value. */
1008   }
1009 
1010   /* vvv may raise tons of error messages */
1011   // InputFile->term0(); /* always term0() for MiniPS::String */
1012   if (ufd==NULLP) ufd=new Filter::UngetFILED(InputFile->begin_(), stdin,
1013     Filter::UngetFILED::CM_closep|Filter::UngetFILED::CM_keep_stdinp);
1014   // fprintf(stderr, "ufd=%p\n", ufd);
1015   Image::SampledInfo info(Image::load(
1016     #if 0
1017       InputFile->begin_(), /* knows about stdin `-' */
1018       SimBuffer::B(",",1, LoadHints->begin_(),LoadHints->getLength(), ",",1 ).term0() /* three-way concatentation */
1019     #else
1020       (Image::Loader::UFD*)ufd,
1021       SimBuffer::B(",",1, LoadHints->begin_(),LoadHints->getLength(), ",",1 ).term0(), /* three-way concatentation */
1022       /*format:*/(char const*)NULLP
1023     #endif
1024   ));
1025   Error::sev(Error::NOTICE) << "job: read InputFile: " <<
1026     FNQ2STDOK(InputFile->begin_(),InputFile->getLength()) << (Error*)0;
1027   delete ufd;
1028   // assert(0);
1029 
1030   bool overwrite=false;
1031   if (0==strcmp(InputFile->begin_(), OutputFile->begin_()) && 0!=strcmp("-", InputFile->begin_())) {
1032     // Error::sev(Error::WARNING) << "job: InputFile == OutputFile" << (Error*)0;
1033     OutputFile->replace(OutputFile->begin_(), OutputFile->getLength(), ".s2new", 6);
1034     overwrite=true;
1035   }
1036   FILE *of=stdout;
1037   if (OutputFile->getLength()!=1 || OutputFile->begin_()[0]!='-') {
1038     if ((of=fopen(OutputFile->begin_(), "wb"))==(FILE*)NULLP) {
1039       Error::sev(Error::EERROR) << "job: cannot rewrite OutputFile: " << FNQ2STDOK(OutputFile->begin_(),OutputFile->getLength()) << (Error*)0;
1040     }
1041     /*if (!overwrite)*/
1042     Files::tmpRemoveCleanup(OutputFile->begin_(), &of);
1043   } else Files::set_binary_mode(1, true);
1044 
1045   /* --- Find and apply best applier */
1046 
1047   Files::FILEW wf(of);
1048   Rule::applyProfile(wf, rule_list, &info);
1049 
1050   /* --- Done, flush files and clean up. */
1051 
1052   fflush(of);
1053   if (ferror(of)) {
1054     /* FILE *backup=of; of=(FILE*)NULLP; fclose(backup); */
1055     Error::sev(Error::EERROR) << "job: error writing OutputFile: " << FNQ2STDOK(OutputFile->begin_(),OutputFile->getLength()) << (Error*)0;
1056   }
1057   if (of!=stdout) { /* Don't interfere with tmpRemoveCleanup() */
1058     FILE *backup=of; of=(FILE*)NULLP; fclose(backup);
1059   }
1060   if (overwrite) {
1061     if (0!=rename(OutputFile->begin_(), InputFile->begin_())) {
1062       // remove(OutputFile->begin_()); /* tmpRemoveCleanup() does it */
1063       Error::sev(Error::EERROR) << "job: error renaming, InputFile left intact" << (Error*)0;
1064     }
1065   }
1066   Error::sev(Error::NOTICE) << "job: written OutputFile: " << FNQ2STDOK(OutputFile->begin_(),OutputFile->getLength()) << (Error*)0;
1067 
1068   /* Freeing memory may cause segfaults because of possibly
1069    * bad code design :-(. Luckily we have already saved the output file.
1070    */
1071   Rule::deleteProfile(rule_list);
1072   MiniPS::delete0(job); /* frees OutputFile etc. */
1073 
1074   bool successp = Error::getTopPrinted()+0<=Error::NOTICE+0;
1075   int exitCode = Error::runCleanups(0);
1076   fflush(stdout); fflush(stderr);
1077   if (successp && exitCode == 0)
1078     fputs("Success.\n", stderr);
1079   /* Don't do Error::cexit(exitCode);, it won't run destructors of
1080    * stack-allocated objects, and valgrind will complain about leaking memory.
1081    */
1082   return exitCode;
1083 }
1084 
1085 /** main: process entry point for the sam2p utility. */
main(int,char const * const * argv)1086 int main(int, char const*const* argv) {
1087   Files::FILEW sout(stdout);
1088   Files::FILEW serr(stderr);
1089   /* --- Initialize */
1090 
1091   bool helpp=argv[0]==(char const*)NULLP || (argv[0]!=(char const*)NULLP && argv[1]!=(char const*)NULLP && argv[2]==(char const*)NULLP && (
1092              option_eq(argv[1], "-help") ||
1093              option_eq(argv[1], "-h") ||
1094              option_eq(argv[1], "-?") ||
1095              option_eq(argv[1], "/h") ||
1096              option_eq(argv[1], "/?")));
1097   bool versionp=argv[0]!=(char const*)NULLP && argv[1]!=(char const*)NULLP && argv[2]==(char const*)NULLP && (
1098              option_eq(argv[1], "-version") ||
1099              option_eq(argv[1], "-v"));
1100   bool quietp=argv[0]!=(char const*)NULLP && argv[1]!=(char const*)NULLP &&
1101              option_eq(argv[1], "-j:quiet");
1102   init_sam2p_engine(argv[0]);
1103 
1104   if (versionp) {
1105     sout << "This is " << Error::banner0 << ".\n";
1106     return 0;
1107   }
1108 
1109   /* Don't print diagnostics to stdout, becuse it might be the OutputFile */
1110   if (!quietp) { serr << "This is " << Error::banner0 << ".\n"; }
1111   init_loader();
1112   if (!quietp) { serr << "Available Loaders:"; Image::printLoaders(serr); serr << ".\n"; }
1113   init_applier();
1114   if (!quietp) { serr << "Available Appliers:"; Rule::printAppliers(serr); serr << ".\n"; }
1115 
1116   return run_sam2p_engine(
1117       sout, serr, argv+(argv[0]!=(char const*)NULLP), helpp);
1118 }
1119