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