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