1 /* This is a backward compatibility interface to Pamtogif.
2 
3    Pamtogif replaced Ppmtogif in Netpbm 10.37 (December 2006).
4 
5    The only significant ways Pamtogif are not backward compatible with
6    old Ppmtogif are:
7 
8      - Pamtogif does not have a -alpha option.
9 
10      - Pamtogif requires a user-specififed map file (-mapfile) to
11        match the input in depth.
12 */
13 #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
14 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
15 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
16 
17 #include <assert.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 
23 #include "pm_c_util.h"
24 #include "mallocvar.h"
25 #include "shhopt.h"
26 #include "nstring.h"
27 #include "pam.h"
28 
29 
30 
31 static const char *
dirname(const char * const fileName)32 dirname(const char * const fileName) {
33 
34     char * buffer;
35     char * slashPos;
36 
37     buffer = strdup(fileName);
38 
39     slashPos = strchr(buffer, '/');
40 
41     if (slashPos)
42         *slashPos = '\0';
43 
44     return buffer;
45 }
46 
47 
48 
49 struct cmdlineInfo {
50     /* All the information the user supplied in the command line,
51        in a form easy for the program to use.
52     */
53     const char *inputFileName;  /* Name of input file */
54     const char *alpha_filespec; /* Filespec of alpha file; NULL if none */
55     const char *alphacolor;     /* -alphacolor option value or default */
56     unsigned int interlace;     /* -interlace option value */
57     unsigned int sort;          /* -sort option value */
58     const char *mapfile;        /* -mapfile option value.  NULL if none. */
59     const char *transparent;    /* -transparent option value.  NULL if none. */
60     const char *comment;        /* -comment option value; NULL if none */
61     unsigned int nolzw;         /* -nolzw option */
62     unsigned int verbose;
63 };
64 
65 
66 static void
handleLatex2htmlHack(void)67 handleLatex2htmlHack(void) {
68 /*----------------------------------------------------------------------------
69   This program used to put out a "usage" message when it saw an option
70   it didn't understand.  Latex2html's configure program does a
71   ppmtogif -h (-h was never a valid option) to elicit that message and
72   then parses the message to see if it includes the strings
73   "-interlace" and "-transparent".  That way it knows if the
74   'ppmtogif' program it found has those options or not.  I don't think
75   any 'ppmtogif' you're likely to find today lacks those options, but
76   latex2html checks anyway, and we don't want it to conclude that we
77   don't have them.
78 
79   So we issue a special error message just to trick latex2html into
80   deciding that we have -interlace and -transparent options.  The function
81   is not documented in the man page.  We would like to see Latex2html
82   either stop checking or check like configure programs usually do --
83   try the option and see if you get success or failure.
84 
85   -Bryan 2001.11.14
86 -----------------------------------------------------------------------------*/
87      pm_error("latex2html, you should just try the -interlace and "
88               "-transparent options to see if they work instead of "
89               "expecting a 'usage' message from -h");
90 }
91 
92 
93 
94 static void
parseCommandLine(int argc,const char ** argv,struct cmdlineInfo * const cmdlineP)95 parseCommandLine(int argc, const char ** argv,
96                  struct cmdlineInfo * const cmdlineP) {
97 /*----------------------------------------------------------------------------
98    Parse the program arguments (given by argc and argv) into a form
99    the program can deal with more easily -- a cmdline_info structure.
100    If the syntax is invalid, issue a message and exit the program via
101    pm_error().
102 
103    Note that the file spec array we return is stored in the storage that
104    was passed to us as the argv array.
105 -----------------------------------------------------------------------------*/
106     optEntry * option_def;  /* malloc'ed */
107     optStruct3 opt;  /* set by OPTENT3 */
108     unsigned int option_def_index;
109 
110     unsigned int latex2htmlhack;
111 
112     MALLOCARRAY_NOFAIL(option_def, 100);
113 
114     option_def_index = 0;   /* incremented by OPTENT3 */
115     OPTENT3(0,   "interlace",   OPT_FLAG,
116             NULL,                       &cmdlineP->interlace, 0);
117     OPTENT3(0,   "sort",        OPT_FLAG,
118             NULL,                       &cmdlineP->sort, 0);
119     OPTENT3(0,   "nolzw",       OPT_FLAG,
120             NULL,                       &cmdlineP->nolzw, 0);
121     OPTENT3(0,   "mapfile",     OPT_STRING,
122             &cmdlineP->mapfile,        NULL, 0);
123     OPTENT3(0,   "transparent", OPT_STRING,
124             &cmdlineP->transparent,    NULL, 0);
125     OPTENT3(0,   "comment",     OPT_STRING,
126             &cmdlineP->comment,        NULL, 0);
127     OPTENT3(0,   "alpha",       OPT_STRING,
128             &cmdlineP->alpha_filespec, NULL, 0);
129     OPTENT3(0,   "alphacolor",  OPT_STRING,
130             &cmdlineP->alphacolor,     NULL, 0);
131     OPTENT3(0,   "h",           OPT_FLAG,
132             NULL,                       &latex2htmlhack, 0);
133     OPTENT3(0,   "verbose",     OPT_FLAG,
134             NULL,                       &cmdlineP->verbose, 0);
135 
136     /* Set the defaults */
137     cmdlineP->mapfile = NULL;
138     cmdlineP->transparent = NULL;  /* no transparency */
139     cmdlineP->comment = NULL;      /* no comment */
140     cmdlineP->alpha_filespec = NULL;      /* no alpha file */
141     cmdlineP->alphacolor = "rgb:0/0/0";
142         /* We could say "black" here, but then we depend on the color names
143            database existing.
144         */
145 
146     opt.opt_table = option_def;
147     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
148     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
149 
150     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
151         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
152 
153     if (latex2htmlhack)
154         handleLatex2htmlHack();
155 
156     if (argc-1 == 0)
157         cmdlineP->inputFileName = "-";
158     else if (argc-1 != 1)
159         pm_error("Program takes zero or one argument (filename).  You "
160                  "specified %d", argc-1);
161     else
162         cmdlineP->inputFileName = argv[1];
163 
164     if (cmdlineP->alpha_filespec && cmdlineP->transparent)
165         pm_error("You cannot specify both -alpha and -transparent.");
166 }
167 
168 
169 
170 static void
openPnmremapStream(const char * const inputFileName,const char * const mapFileName,bool const verbose,FILE ** const pnmremapPipeP)171 openPnmremapStream(const char * const inputFileName,
172                    const char * const mapFileName,
173                    bool         const verbose,
174                    FILE **      const pnmremapPipeP) {
175 /*----------------------------------------------------------------------------
176    Create a process to run the image in file inputFileName[] through
177    Pnmremap, remapping it to the colors in mapFileName[].  Have it
178    write its output to a pipe and return as *pnmremapPipeP the other
179    end of that pipe.
180 -----------------------------------------------------------------------------*/
181     FILE * pnmremapPipe;
182     const char * pnmremapCommand;
183 
184     assert(inputFileName != NULL);
185     assert(mapFileName != NULL);
186 
187     pm_asprintf(&pnmremapCommand, "pnmremap -mapfile='%s' %s",
188                 mapFileName, inputFileName);
189 
190     if (verbose)
191         pm_message("Preprocessing Pamtogif input with shell command '%s'",
192                    pnmremapCommand);
193 
194     pnmremapPipe = popen(pnmremapCommand, "r");
195 
196     if (pnmremapPipe == NULL)
197         pm_error("Shell command '%s', via popen(), to prepare the input "
198                  "for Pamtogif, failed.", pnmremapCommand);
199     else
200         *pnmremapPipeP = pnmremapPipe;
201 
202     pm_strfree(pnmremapCommand);
203 }
204 
205 
206 
207 static const char *
pamtogifCommand(const char * const arg0,struct cmdlineInfo const cmdline)208 pamtogifCommand(const char *       const arg0,
209                 struct cmdlineInfo const cmdline) {
210 
211     const char * const pamtogifName = "pamtogif";
212 
213     const char * retval;
214 
215     const char * commandVerb;
216     const char * transparentOpt;
217     const char * commentOpt;
218 
219     if (strchr(arg0, '/')) {
220         const char * const arg0DirName = dirname(arg0);
221         const char * progName;
222 
223         struct stat statbuf;
224 
225         pm_asprintf(&progName, "%s/%s", arg0DirName, pamtogifName);
226 
227         if (stat(progName, &statbuf) == 0)
228             commandVerb = progName;
229         else
230             commandVerb = strdup(pamtogifName);
231 
232         pm_strfree(arg0DirName);
233     } else
234         commandVerb = strdup(pamtogifName);
235 
236     if (cmdline.transparent)
237         pm_asprintf(&transparentOpt, "-transparent=%s", cmdline.transparent);
238     else
239         transparentOpt = strdup("");
240 
241     if (cmdline.comment)
242         pm_asprintf(&commentOpt, "-comment=%s", cmdline.comment);
243     else
244         commentOpt = strdup("");
245 
246     pm_asprintf(&retval, "%s - -alphacolor=%s %s %s %s %s %s %s",
247                 commandVerb,
248                 cmdline.alphacolor,
249                 cmdline.interlace ? "-interlace" : "",
250                 cmdline.sort ? "-sort" : "",
251                 transparentOpt,
252                 commentOpt,
253                 cmdline.nolzw ? "-nolzw" : "",
254                 cmdline.verbose ? "-verbose" : "");
255 
256     pm_strfree(transparentOpt);
257     pm_strfree(commentOpt);
258 
259     return retval;
260 }
261 
262 
263 
264 static void
feedPamtogifNoAlpha(struct pam * const inPamP,FILE * const pipeToPamtogif)265 feedPamtogifNoAlpha(struct pam * const inPamP,
266                     FILE *       const pipeToPamtogif) {
267 
268     unsigned int row;
269     struct pam outPam;
270     tuple * tuplerow;
271 
272     tuplerow = pnm_allocpamrow(inPamP);
273 
274     outPam = *inPamP;
275     outPam.file = pipeToPamtogif;
276 
277     pnm_writepaminit(&outPam);
278 
279     for (row = 0; row < inPamP->height; ++row) {
280         pnm_readpamrow(inPamP, tuplerow);
281 
282         pnm_writepamrow(&outPam, tuplerow);
283     }
284     pnm_freepamrow(tuplerow);
285 }
286 
287 
288 
289 static void
copyRasterWithAlpha(struct pam * const inPamP,struct pam * const alphaPamP,struct pam * const outPamP,unsigned int const alphaPlane)290 copyRasterWithAlpha(struct pam * const inPamP,
291                     struct pam * const alphaPamP,
292                     struct pam * const outPamP,
293                     unsigned int const alphaPlane) {
294 
295     tuple * tuplerow;
296     tuple * alpharow;
297     unsigned int row;
298 
299     inPamP->allocation_depth = outPamP->depth;
300 
301     tuplerow = pnm_allocpamrow(inPamP);
302     alpharow = pnm_allocpamrow(alphaPamP);
303 
304     for (row = 0; row < inPamP->height; ++row) {
305         unsigned int col;
306 
307         pnm_readpamrow(inPamP, tuplerow);
308         pnm_readpamrow(alphaPamP, alpharow);
309 
310         for (col = 0; col < inPamP->width; ++col) {
311             tuplerow[col][alphaPlane] = pnm_scalesample(alpharow[col][0],
312                                                         alphaPamP->maxval,
313                                                         inPamP->maxval);
314         }
315         pnm_writepamrow(outPamP, tuplerow);
316     }
317     pnm_freepamrow(alpharow);
318     pnm_freepamrow(tuplerow);
319 }
320 
321 
322 
323 static void
feedPamtogifWithAlpha(struct pam * const inPamP,struct pam * const alphaPamP,FILE * const pipeToPamtogif)324 feedPamtogifWithAlpha(struct pam * const inPamP,
325                       struct pam * const alphaPamP,
326                       FILE *       const pipeToPamtogif) {
327 
328     unsigned int alphaPlane;
329     struct pam outPam;
330 
331     if (inPamP->width != alphaPamP->width ||
332         inPamP->height != alphaPamP->height)
333         pm_error("-alpha image dimensions (%u w x %u h) do not match "
334                  "the input image dimensions (%u x %u)",
335                  alphaPamP->width, alphaPamP->height,
336                  inPamP->width, inPamP->height);
337 
338     outPam = *inPamP;
339     outPam.file        = pipeToPamtogif;
340     outPam.format      = PAM_FORMAT;
341     outPam.plainformat = 0;
342 
343     if (inPamP->depth == 1) {
344         alphaPlane = 1;
345         strcpy(outPam.tuple_type, "GRAYSCALE_ALPHA");
346     } else if (inPamP->depth == 3) {
347         alphaPlane = 3;
348         strcpy(outPam.tuple_type, "RGB_ALPHA");
349     }
350     outPam.depth = alphaPlane + 1;
351 
352     pnm_writepaminit(&outPam);
353 
354     copyRasterWithAlpha(inPamP, alphaPamP, &outPam, alphaPlane);
355 }
356 
357 
358 
359 static void
feedPamtogif(struct pam * const inPamP,const char * const alphaFilespec,FILE * const pipeToPamtogif)360 feedPamtogif(struct pam * const inPamP,
361              const char * const alphaFilespec,
362              FILE *       const pipeToPamtogif) {
363 
364     if (alphaFilespec) {
365         FILE * afP;
366         struct pam alphaPam;
367         afP = pm_openr(alphaFilespec);
368         pnm_readpaminit(afP, &alphaPam, PAM_STRUCT_SIZE(tuple_type));
369         feedPamtogifWithAlpha(inPamP, &alphaPam, pipeToPamtogif);
370         pm_close(afP);
371     } else
372         feedPamtogifNoAlpha(inPamP, pipeToPamtogif);
373 }
374 
375 
376 
377 int
main(int argc,const char ** argv)378 main(int           argc,
379      const char ** argv) {
380 
381     struct cmdlineInfo cmdline;
382     FILE * ifP;
383     struct pam inPam;
384     const char * command;
385     FILE * pipeToPamtogif;
386     int rc;
387 
388     pm_proginit(&argc, argv);
389 
390     parseCommandLine(argc, argv, &cmdline);
391 
392     if (cmdline.mapfile)
393         openPnmremapStream(cmdline.inputFileName, cmdline.mapfile,
394                            cmdline.verbose, &ifP);
395     else
396         ifP = pm_openr(cmdline.inputFileName);
397 
398     command = pamtogifCommand(argv[0], cmdline);
399 
400     if (cmdline.verbose)
401         pm_message("Executing shell command '%s'", command);
402 
403     pipeToPamtogif = popen(command, "w");
404 
405     if (pipeToPamtogif == NULL)
406         pm_error("Shell command '%s', via popen(), failed.", command);
407 
408     pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(allocation_depth));
409 
410     feedPamtogif(&inPam, cmdline.alpha_filespec, pipeToPamtogif);
411 
412     rc = pclose(pipeToPamtogif);
413 
414     if (rc != 0)
415         pm_error("Pamtogif process failed.  pclose() failed.");
416 
417     pm_strfree(command);
418 
419     pm_close(ifP);
420     pm_close(stdout);
421 
422     return 0;
423 }
424