1 /*
2  * djpeg.c
3  *
4  * Copyright (C) 1991-1994, Thomas G. Lane.
5  * This file is part of the Independent JPEG Group's software.
6  * For conditions of distribution and use, see the accompanying README file.
7  *
8  * This file contains a command-line user interface for the JPEG decompressor.
9  * It should work on any system with Unix- or MS-DOS-style command lines.
10  *
11  * Two different command line styles are permitted, depending on the
12  * compile-time switch TWO_FILE_COMMANDLINE:
13  *	djpeg [options]  inputfile outputfile
14  *	djpeg [options]  [inputfile]
15  * In the second style, output is always to standard output, which you'd
16  * normally redirect to a file or pipe to some other program.  Input is
17  * either from a named file or from standard input (typically redirected).
18  * The second style is convenient on Unix but is unhelpful on systems that
19  * don't support pipes.  Also, you MUST use the first style if your system
20  * doesn't do binary I/O to stdin/stdout.
21  * To simplify script writing, the "-outfile" switch is provided.  The syntax
22  *	djpeg [options]  -outfile outputfile  inputfile
23  * works regardless of which command line style is used.
24  */
25 
26 #include "cdjpeg.h"		/* Common decls for cjpeg/djpeg applications */
27 #define JMAKE_MSG_TABLE
28 #include "cderror.h"		/* create message string table */
29 #include "jversion.h"		/* for version message */
30 
31 #include <ctype.h>		/* to declare isupper(),tolower(),isprint() */
32 #ifdef NEED_SIGNAL_CATCHER
33 #include <signal.h>		/* to declare signal() */
34 #endif
35 #ifdef USE_SETMODE
36 #include <fcntl.h>		/* to declare setmode()'s parameter macros */
37 /* If you have setmode() but not <io.h>, just delete this line: */
38 #include <io.h>			/* to declare setmode() */
39 #endif
40 
41 #ifdef USE_CCOMMAND		/* command-line reader for Macintosh */
42 #ifdef __MWERKS__
43 #include <SIOUX.h>              /* Metrowerks declares it here */
44 #endif
45 #ifdef THINK_C
46 #include <console.h>		/* Think declares it here */
47 #endif
48 #endif
49 
50 #ifdef DONT_USE_B_MODE		/* define mode parameters for fopen() */
51 #define READ_BINARY	"r"
52 #define WRITE_BINARY	"w"
53 #else
54 #define READ_BINARY	"rb"
55 #define WRITE_BINARY	"wb"
56 #endif
57 
58 #ifndef EXIT_FAILURE		/* define exit() codes if not provided */
59 #define EXIT_FAILURE  1
60 #endif
61 #ifndef EXIT_SUCCESS
62 #ifdef VMS
63 #define EXIT_SUCCESS  1		/* VMS is very nonstandard */
64 #else
65 #define EXIT_SUCCESS  0
66 #endif
67 #endif
68 #ifndef EXIT_WARNING
69 #ifdef VMS
70 #define EXIT_WARNING  1		/* VMS is very nonstandard */
71 #else
72 #define EXIT_WARNING  2
73 #endif
74 #endif
75 
76 
77 /*
78  * This list defines the known output image formats
79  * (not all of which need be supported by a given version).
80  * You can change the default output format by defining DEFAULT_FMT;
81  * indeed, you had better do so if you undefine PPM_SUPPORTED.
82  */
83 
84 typedef enum {
85 	FMT_BMP,		/* BMP format (Windows flavor) */
86 	FMT_GIF,		/* GIF format */
87 	FMT_OS2,		/* BMP format (OS/2 flavor) */
88 	FMT_PPM,		/* PPM/PGM (PBMPLUS formats) */
89 	FMT_RLE,		/* RLE format */
90 	FMT_TARGA,		/* Targa format */
91 	FMT_TIFF		/* TIFF format */
92 } IMAGE_FORMATS;
93 
94 #ifndef DEFAULT_FMT		/* so can override from CFLAGS in Makefile */
95 #define DEFAULT_FMT	FMT_PPM
96 #endif
97 
98 static IMAGE_FORMATS requested_fmt;
99 
100 
101 /*
102  * Signal catcher to ensure that temporary files are removed before aborting.
103  * NB: for Amiga Manx C this is actually a global routine named _abort();
104  * we put "#define signal_catcher _abort" in jconfig.h.  Talk about bogus...
105  */
106 
107 #ifdef NEED_SIGNAL_CATCHER
108 
109 static j_common_ptr sig_cinfo;
110 
111 GLOBAL void
signal_catcher(int signum)112 signal_catcher (int signum)
113 {
114   if (sig_cinfo != NULL) {
115     if (sig_cinfo->err != NULL) /* turn off trace output */
116       sig_cinfo->err->trace_level = 0;
117     jpeg_destroy(sig_cinfo);	/* clean up memory allocation & temp files */
118   }
119   exit(EXIT_FAILURE);
120 }
121 
122 #endif
123 
124 
125 /*
126  * Optional routine to display a percent-done figure on stderr.
127  */
128 
129 #ifdef PROGRESS_REPORT
130 
131 METHODDEF void
progress_monitor(j_common_ptr cinfo)132 progress_monitor (j_common_ptr cinfo)
133 {
134   cd_progress_ptr prog = (cd_progress_ptr) cinfo->progress;
135   int total_passes = prog->pub.total_passes + prog->total_extra_passes;
136   int percent_done = (int) (prog->pub.pass_counter*100L/prog->pub.pass_limit);
137 
138   if (percent_done != prog->percent_done) {
139     prog->percent_done = percent_done;
140     if (total_passes > 1) {
141       fprintf(stderr, "\rPass %d/%d: %3d%% ",
142 	      prog->pub.completed_passes + prog->completed_extra_passes + 1,
143 	      total_passes, percent_done);
144     } else {
145       fprintf(stderr, "\r %3d%% ", percent_done);
146     }
147     fflush(stderr);
148   }
149 }
150 
151 #endif
152 
153 
154 /*
155  * Argument-parsing code.
156  * The switch parser is designed to be useful with DOS-style command line
157  * syntax, ie, intermixed switches and file names, where only the switches
158  * to the left of a given file name affect processing of that file.
159  * The main program in this file doesn't actually use this capability...
160  */
161 
162 
163 static const char * progname;	/* program name for error messages */
164 static char * outfilename;	/* for -outfile switch */
165 
166 
167 LOCAL void
usage(void)168 usage (void)
169 /* complain about bad command line */
170 {
171   fprintf(stderr, "usage: %s [switches] ", progname);
172 #ifdef TWO_FILE_COMMANDLINE
173   fprintf(stderr, "inputfile outputfile\n");
174 #else
175   fprintf(stderr, "[inputfile]\n");
176 #endif
177 
178   fprintf(stderr, "Switches (names may be abbreviated):\n");
179   fprintf(stderr, "  -colors N      Reduce image to no more than N colors\n");
180   fprintf(stderr, "  -fast          Fast, low-quality processing\n");
181   fprintf(stderr, "  -grayscale     Force grayscale output\n");
182 #ifdef IDCT_SCALING_SUPPORTED
183   fprintf(stderr, "  -scale M/N     Scale output image by fraction M/N, eg, 1/8\n");
184 #endif
185 #ifdef BMP_SUPPORTED
186   fprintf(stderr, "  -bmp           Select BMP output format (Windows style)%s\n",
187 	  (DEFAULT_FMT == FMT_BMP ? " (default)" : ""));
188 #endif
189 #ifdef GIF_SUPPORTED
190   fprintf(stderr, "  -gif           Select GIF output format%s\n",
191 	  (DEFAULT_FMT == FMT_GIF ? " (default)" : ""));
192 #endif
193 #ifdef BMP_SUPPORTED
194   fprintf(stderr, "  -os2           Select BMP output format (OS/2 style)%s\n",
195 	  (DEFAULT_FMT == FMT_OS2 ? " (default)" : ""));
196 #endif
197 #ifdef PPM_SUPPORTED
198   fprintf(stderr, "  -pnm           Select PBMPLUS (PPM/PGM) output format%s\n",
199 	  (DEFAULT_FMT == FMT_PPM ? " (default)" : ""));
200 #endif
201 #ifdef RLE_SUPPORTED
202   fprintf(stderr, "  -rle           Select Utah RLE output format%s\n",
203 	  (DEFAULT_FMT == FMT_RLE ? " (default)" : ""));
204 #endif
205 #ifdef TARGA_SUPPORTED
206   fprintf(stderr, "  -targa         Select Targa output format%s\n",
207 	  (DEFAULT_FMT == FMT_TARGA ? " (default)" : ""));
208 #endif
209   fprintf(stderr, "Switches for advanced users:\n");
210 #ifdef DCT_ISLOW_SUPPORTED
211   fprintf(stderr, "  -dct int       Use integer DCT method%s\n",
212 	  (JDCT_DEFAULT == JDCT_ISLOW ? " (default)" : ""));
213 #endif
214 #ifdef DCT_IFAST_SUPPORTED
215   fprintf(stderr, "  -dct fast      Use fast integer DCT (less accurate)%s\n",
216 	  (JDCT_DEFAULT == JDCT_IFAST ? " (default)" : ""));
217 #endif
218 #ifdef DCT_FLOAT_SUPPORTED
219   fprintf(stderr, "  -dct float     Use floating-point DCT method%s\n",
220 	  (JDCT_DEFAULT == JDCT_FLOAT ? " (default)" : ""));
221 #endif
222   fprintf(stderr, "  -dither fs     Use F-S dithering (default)\n");
223   fprintf(stderr, "  -dither none   Don't use dithering in quantization\n");
224   fprintf(stderr, "  -dither ordered  Use ordered dither (medium speed, quality)\n");
225 #ifdef QUANT_2PASS_SUPPORTED
226   fprintf(stderr, "  -map FILE      Map to colors used in named image file\n");
227 #endif
228   fprintf(stderr, "  -nosmooth      Don't use high-quality upsampling\n");
229 #ifdef QUANT_1PASS_SUPPORTED
230   fprintf(stderr, "  -onepass       Use 1-pass quantization (fast, low quality)\n");
231 #endif
232   fprintf(stderr, "  -maxmemory N   Maximum memory to use (in kbytes)\n");
233   fprintf(stderr, "  -outfile name  Specify name for output file\n");
234   fprintf(stderr, "  -verbose  or  -debug   Emit debug output\n");
235   exit(EXIT_FAILURE);
236 }
237 
238 
239 LOCAL boolean
keymatch(char * arg,const char * keyword,int minchars)240 keymatch (char * arg, const char * keyword, int minchars)
241 /* Case-insensitive matching of (possibly abbreviated) keyword switches. */
242 /* keyword is the constant keyword (must be lower case already), */
243 /* minchars is length of minimum legal abbreviation. */
244 {
245   register int ca, ck;
246   register int nmatched = 0;
247 
248   while ((ca = *arg++) != '\0') {
249     if ((ck = *keyword++) == '\0')
250       return FALSE;		/* arg longer than keyword, no good */
251     if (isupper(ca))		/* force arg to lcase (assume ck is already) */
252       ca = tolower(ca);
253     if (ca != ck)
254       return FALSE;		/* no good */
255     nmatched++;			/* count matched characters */
256   }
257   /* reached end of argument; fail if it's too short for unique abbrev */
258   if (nmatched < minchars)
259     return FALSE;
260   return TRUE;			/* A-OK */
261 }
262 
263 
264 LOCAL int
parse_switches(j_decompress_ptr cinfo,int argc,char ** argv,int last_file_arg_seen,boolean for_real)265 parse_switches (j_decompress_ptr cinfo, int argc, char **argv,
266 		int last_file_arg_seen, boolean for_real)
267 /* Parse optional switches.
268  * Returns argv[] index of first file-name argument (== argc if none).
269  * Any file names with indexes <= last_file_arg_seen are ignored;
270  * they have presumably been processed in a previous iteration.
271  * (Pass 0 for last_file_arg_seen on the first or only iteration.)
272  * for_real is FALSE on the first (dummy) pass; we may skip any expensive
273  * processing.
274  */
275 {
276   int argn;
277   char * arg;
278 
279   /* Set up default JPEG parameters. */
280   requested_fmt = DEFAULT_FMT;	/* set default output file format */
281   outfilename = NULL;
282   cinfo->err->trace_level = 0;
283 
284   /* Scan command line options, adjust parameters */
285 
286   for (argn = 1; argn < argc; argn++) {
287     arg = argv[argn];
288     if (*arg != '-') {
289       /* Not a switch, must be a file name argument */
290       if (argn <= last_file_arg_seen) {
291 	outfilename = NULL;	/* -outfile applies to just one input file */
292 	continue;		/* ignore this name if previously processed */
293       }
294       break;			/* else done parsing switches */
295     }
296     arg++;			/* advance past switch marker character */
297 
298     if (keymatch(arg, "bmp", 1)) {
299       /* BMP output format. */
300       requested_fmt = FMT_BMP;
301 
302     } else if (keymatch(arg, "colors", 1) || keymatch(arg, "colours", 1) ||
303 	       keymatch(arg, "quantize", 1) || keymatch(arg, "quantise", 1)) {
304       /* Do color quantization. */
305       int val;
306 
307       if (++argn >= argc)	/* advance to next argument */
308 	usage();
309       if (sscanf(argv[argn], "%d", &val) != 1)
310 	usage();
311       cinfo->desired_number_of_colors = val;
312       cinfo->quantize_colors = TRUE;
313 
314     } else if (keymatch(arg, "dct", 2)) {
315       /* Select IDCT algorithm. */
316       if (++argn >= argc)	/* advance to next argument */
317 	usage();
318       if (keymatch(argv[argn], "int", 1)) {
319 	cinfo->dct_method = JDCT_ISLOW;
320       } else if (keymatch(argv[argn], "fast", 2)) {
321 	cinfo->dct_method = JDCT_IFAST;
322       } else if (keymatch(argv[argn], "float", 2)) {
323 	cinfo->dct_method = JDCT_FLOAT;
324       } else
325 	usage();
326 
327     } else if (keymatch(arg, "dither", 2)) {
328       /* Select dithering algorithm. */
329       if (++argn >= argc)	/* advance to next argument */
330 	usage();
331       if (keymatch(argv[argn], "fs", 2)) {
332 	cinfo->dither_mode = JDITHER_FS;
333       } else if (keymatch(argv[argn], "none", 2)) {
334 	cinfo->dither_mode = JDITHER_NONE;
335       } else if (keymatch(argv[argn], "ordered", 2)) {
336 	cinfo->dither_mode = JDITHER_ORDERED;
337       } else
338 	usage();
339 
340     } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) {
341       /* Enable debug printouts. */
342       /* On first -d, print version identification */
343       static boolean printed_version = FALSE;
344 
345       if (! printed_version) {
346 	fprintf(stderr, "Independent JPEG Group's DJPEG, version %s\n%s\n",
347 		JVERSION, JCOPYRIGHT);
348 	printed_version = TRUE;
349       }
350       cinfo->err->trace_level++;
351 
352     } else if (keymatch(arg, "fast", 1)) {
353       /* Select recommended processing options for quick-and-dirty output. */
354       cinfo->two_pass_quantize = FALSE;
355       cinfo->dither_mode = JDITHER_ORDERED;
356       if (! cinfo->quantize_colors) /* don't override an earlier -colors */
357 	cinfo->desired_number_of_colors = 216;
358       cinfo->dct_method = JDCT_FASTEST;
359       cinfo->do_fancy_upsampling = FALSE;
360 
361     } else if (keymatch(arg, "gif", 1)) {
362       /* GIF output format. */
363       requested_fmt = FMT_GIF;
364 
365     } else if (keymatch(arg, "grayscale", 2) || keymatch(arg, "greyscale",2)) {
366       /* Force monochrome output. */
367       cinfo->out_color_space = JCS_GRAYSCALE;
368 
369     } else if (keymatch(arg, "map", 3)) {
370       /* Quantize to a color map taken from an input file. */
371       if (++argn >= argc)	/* advance to next argument */
372 	usage();
373       if (for_real) {		/* too expensive to do twice! */
374 #ifdef QUANT_2PASS_SUPPORTED	/* otherwise can't quantize to supplied map */
375 	FILE * mapfile;
376 
377 	if ((mapfile = fopen(argv[argn], READ_BINARY)) == NULL) {
378 	  fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]);
379 	  exit(EXIT_FAILURE);
380 	}
381 	read_color_map(cinfo, mapfile);
382 	fclose(mapfile);
383 	cinfo->quantize_colors = TRUE;
384 #else
385 	ERREXIT(cinfo, JERR_NOT_COMPILED);
386 #endif
387       }
388 
389     } else if (keymatch(arg, "maxmemory", 3)) {
390       /* Maximum memory in Kb (or Mb with 'm'). */
391       long lval;
392       char ch = 'x';
393 
394       if (++argn >= argc)	/* advance to next argument */
395 	usage();
396       if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1)
397 	usage();
398       if (ch == 'm' || ch == 'M')
399 	lval *= 1000L;
400       cinfo->mem->max_memory_to_use = lval * 1000L;
401 
402     } else if (keymatch(arg, "nosmooth", 3)) {
403       /* Suppress fancy upsampling */
404       cinfo->do_fancy_upsampling = FALSE;
405 
406     } else if (keymatch(arg, "onepass", 3)) {
407       /* Use fast one-pass quantization. */
408       cinfo->two_pass_quantize = FALSE;
409 
410     } else if (keymatch(arg, "os2", 3)) {
411       /* BMP output format (OS/2 flavor). */
412       requested_fmt = FMT_OS2;
413 
414     } else if (keymatch(arg, "outfile", 4)) {
415       /* Set output file name. */
416       if (++argn >= argc)	/* advance to next argument */
417 	usage();
418       outfilename = argv[argn];	/* save it away for later use */
419 
420     } else if (keymatch(arg, "pnm", 1) || keymatch(arg, "ppm", 1)) {
421       /* PPM/PGM output format. */
422       requested_fmt = FMT_PPM;
423 
424     } else if (keymatch(arg, "rle", 1)) {
425       /* RLE output format. */
426       requested_fmt = FMT_RLE;
427 
428     } else if (keymatch(arg, "scale", 1)) {
429       /* Scale the output image by a fraction M/N. */
430       if (++argn >= argc)	/* advance to next argument */
431 	usage();
432       if (sscanf(argv[argn], "%d/%d",
433 		 &cinfo->scale_num, &cinfo->scale_denom) != 2)
434 	usage();
435 
436     } else if (keymatch(arg, "targa", 1)) {
437       /* Targa output format. */
438       requested_fmt = FMT_TARGA;
439 
440     } else {
441       usage();			/* bogus switch */
442     }
443   }
444 
445   return argn;			/* return index of next arg (file name) */
446 }
447 
448 
449 /*
450  * Marker processor for COM markers.
451  * This replaces the library's built-in processor, which just skips the marker.
452  * We want to print out the marker as text, if possible.
453  * Note this code relies on a non-suspending data source.
454  */
455 
456 LOCAL unsigned int
jpeg_getc(j_decompress_ptr cinfo)457 jpeg_getc (j_decompress_ptr cinfo)
458 /* Read next byte */
459 {
460   struct jpeg_source_mgr * datasrc = cinfo->src;
461 
462   if (datasrc->bytes_in_buffer == 0) {
463     if (! (*datasrc->fill_input_buffer) (cinfo))
464       ERREXIT(cinfo, JERR_CANT_SUSPEND);
465   }
466   datasrc->bytes_in_buffer--;
467   return GETJOCTET(*datasrc->next_input_byte++);
468 }
469 
470 
471 METHODDEF boolean
COM_handler(j_decompress_ptr cinfo)472 COM_handler (j_decompress_ptr cinfo)
473 {
474   boolean traceit = (cinfo->err->trace_level >= 1);
475   INT32 length;
476   unsigned int ch;
477   unsigned int lastch = 0;
478 
479   length = jpeg_getc(cinfo) << 8;
480   length += jpeg_getc(cinfo);
481   length -= 2;			/* discount the length word itself */
482 
483   if (traceit)
484     fprintf(stderr, "Comment, length %ld:\n", (long) length);
485 
486   while (--length >= 0) {
487     ch = jpeg_getc(cinfo);
488     if (traceit) {
489       /* Emit the character in a readable form.
490        * Nonprintables are converted to \nnn form,
491        * while \ is converted to \\.
492        * Newlines in CR, CR/LF, or LF form will be printed as one newline.
493        */
494       if (ch == '\r') {
495 	fprintf(stderr, "\n");
496       } else if (ch == '\n') {
497 	if (lastch != '\r')
498 	  fprintf(stderr, "\n");
499       } else if (ch == '\\') {
500 	fprintf(stderr, "\\\\");
501       } else if (isprint(ch)) {
502 	putc(ch, stderr);
503       } else {
504 	fprintf(stderr, "\\%03o", ch);
505       }
506       lastch = ch;
507     }
508   }
509 
510   if (traceit)
511     fprintf(stderr, "\n");
512 
513   return TRUE;
514 }
515 
516 
517 /*
518  * The main program.
519  */
520 
521 GLOBAL int
main(int argc,char ** argv)522 main (int argc, char **argv)
523 {
524   struct jpeg_decompress_struct cinfo;
525   struct jpeg_error_mgr jerr;
526 #ifdef PROGRESS_REPORT
527   struct cdjpeg_progress_mgr progress;
528 #endif
529   int file_index;
530   djpeg_dest_ptr dest_mgr = NULL;
531   FILE * input_file;
532   FILE * output_file;
533   JDIMENSION num_scanlines;
534 
535   /* On Mac, fetch a command line. */
536 #ifdef USE_CCOMMAND
537   argc = ccommand(&argv);
538 #endif
539 
540   progname = argv[0];
541   if (progname == NULL || progname[0] == 0)
542     progname = "djpeg";		/* in case C library doesn't provide it */
543 
544   /* Initialize the JPEG decompression object with default error handling. */
545   cinfo.err = jpeg_std_error(&jerr);
546   jpeg_create_decompress(&cinfo);
547   /* Add some application-specific error messages (from cderror.h) */
548   jerr.addon_message_table = addon_message_table;
549   jerr.first_addon_message = JMSG_FIRSTADDONCODE;
550   jerr.last_addon_message = JMSG_LASTADDONCODE;
551   /* Insert custom COM marker processor. */
552   jpeg_set_marker_processor(&cinfo, JPEG_COM, COM_handler);
553 
554   /* Now safe to enable signal catcher. */
555 #ifdef NEED_SIGNAL_CATCHER
556   sig_cinfo = (j_common_ptr) &cinfo;
557   signal(SIGINT, signal_catcher);
558 #ifdef SIGTERM			/* not all systems have SIGTERM */
559   signal(SIGTERM, signal_catcher);
560 #endif
561 #endif
562 
563   /* Scan command line to find file names. */
564   /* It is convenient to use just one switch-parsing routine, but the switch
565    * values read here are ignored; we will rescan the switches after opening
566    * the input file.
567    * (Exception: tracing level set here controls verbosity for COM markers
568    * found during jpeg_read_header...)
569    */
570 
571   file_index = parse_switches(&cinfo, argc, argv, 0, FALSE);
572 
573 #ifdef TWO_FILE_COMMANDLINE
574   /* Must have either -outfile switch or explicit output file name */
575   if (outfilename == NULL) {
576     if (file_index != argc-2) {
577       fprintf(stderr, "%s: must name one input and one output file\n",
578 	      progname);
579       usage();
580     }
581     outfilename = argv[file_index+1];
582   } else {
583     if (file_index != argc-1) {
584       fprintf(stderr, "%s: must name one input and one output file\n",
585 	      progname);
586       usage();
587     }
588   }
589 #else
590   /* Unix style: expect zero or one file name */
591   if (file_index < argc-1) {
592     fprintf(stderr, "%s: only one input file\n", progname);
593     usage();
594   }
595 #endif /* TWO_FILE_COMMANDLINE */
596 
597   /* Open the input file. */
598   if (file_index < argc) {
599     if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) {
600       fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]);
601       exit(EXIT_FAILURE);
602     }
603   } else {
604     /* default input file is stdin */
605 #ifdef USE_SETMODE		/* need to hack file mode? */
606     setmode(fileno(stdin), O_BINARY);
607 #endif
608 #ifdef USE_FDOPEN		/* need to re-open in binary mode? */
609     if ((input_file = fdopen(fileno(stdin), READ_BINARY)) == NULL) {
610       fprintf(stderr, "%s: can't open stdin\n", progname);
611       exit(EXIT_FAILURE);
612     }
613 #else
614     input_file = stdin;
615 #endif
616   }
617 
618   /* Open the output file. */
619   if (outfilename != NULL) {
620     if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) {
621       fprintf(stderr, "%s: can't open %s\n", progname, outfilename);
622       exit(EXIT_FAILURE);
623     }
624   } else {
625     /* default output file is stdout */
626 #ifdef USE_SETMODE		/* need to hack file mode? */
627     setmode(fileno(stdout), O_BINARY);
628 #endif
629 #ifdef USE_FDOPEN		/* need to re-open in binary mode? */
630     if ((output_file = fdopen(fileno(stdout), WRITE_BINARY)) == NULL) {
631       fprintf(stderr, "%s: can't open stdout\n", progname);
632       exit(EXIT_FAILURE);
633     }
634 #else
635     output_file = stdout;
636 #endif
637   }
638 
639 #ifdef PROGRESS_REPORT
640   /* Enable progress display, unless trace output is on */
641   if (jerr.trace_level == 0) {
642     progress.pub.progress_monitor = progress_monitor;
643     progress.completed_extra_passes = 0;
644     progress.total_extra_passes = 0;
645     progress.percent_done = -1;
646     cinfo.progress = &progress.pub;
647   }
648 #endif
649 
650   /* Specify data source for decompression */
651   jpeg_stdio_src(&cinfo, input_file);
652 
653   /* Read file header, set default decompression parameters */
654   (void) jpeg_read_header(&cinfo, TRUE);
655 
656   /* Adjust default decompression parameters by re-parsing the options */
657   file_index = parse_switches(&cinfo, argc, argv, 0, TRUE);
658 
659   /* Initialize the output module now to let it override any crucial
660    * option settings (for instance, GIF wants to force color quantization).
661    */
662   switch (requested_fmt) {
663 #ifdef BMP_SUPPORTED
664   case FMT_BMP:
665     dest_mgr = jinit_write_bmp(&cinfo, FALSE);
666     break;
667   case FMT_OS2:
668     dest_mgr = jinit_write_bmp(&cinfo, TRUE);
669     break;
670 #endif
671 #ifdef GIF_SUPPORTED
672   case FMT_GIF:
673     dest_mgr = jinit_write_gif(&cinfo);
674     break;
675 #endif
676 #ifdef PPM_SUPPORTED
677   case FMT_PPM:
678     dest_mgr = jinit_write_ppm(&cinfo);
679     break;
680 #endif
681 #ifdef RLE_SUPPORTED
682   case FMT_RLE:
683     dest_mgr = jinit_write_rle(&cinfo);
684     break;
685 #endif
686 #ifdef TARGA_SUPPORTED
687   case FMT_TARGA:
688     dest_mgr = jinit_write_targa(&cinfo);
689     break;
690 #endif
691   default:
692     ERREXIT(&cinfo, JERR_UNSUPPORTED_FORMAT);
693     break;
694   }
695   dest_mgr->output_file = output_file;
696 
697   /* Start decompressor */
698   jpeg_start_decompress(&cinfo);
699 
700   /* Write output file header */
701   (*dest_mgr->start_output) (&cinfo, dest_mgr);
702 
703   /* Process data */
704   while (cinfo.output_scanline < cinfo.output_height) {
705     num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer,
706 					dest_mgr->buffer_height);
707     (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines);
708   }
709 
710 #ifdef PROGRESS_REPORT
711   /* Hack: count final pass as done in case finish_output does an extra pass.
712    * The library won't have updated completed_passes.
713    */
714   progress.pub.completed_passes = progress.pub.total_passes;
715 #endif
716 
717   /* Finish decompression and release memory.
718    * I must do it in this order because output module has allocated memory
719    * of lifespan JPOOL_IMAGE; it needs to finish before releasing memory.
720    */
721   (*dest_mgr->finish_output) (&cinfo, dest_mgr);
722   jpeg_finish_decompress(&cinfo);
723   jpeg_destroy_decompress(&cinfo);
724 
725 #ifdef PROGRESS_REPORT
726   /* Clear away progress display */
727   if (jerr.trace_level == 0) {
728     fprintf(stderr, "\r                \r");
729     fflush(stderr);
730   }
731 #endif
732 
733   /* All done. */
734   exit(jerr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS);
735   return 0;			/* suppress no-return-value warnings */
736 }
737