VirtualBox

source: vbox/trunk/src/libs/libpng-1.6.42/contrib/tools/pngcp.c@ 103980

最後變更 在這個檔案從103980是 103316,由 vboxsync 提交於 12 月 前

libpng-1.6.42: Applied and adjusted our libpng changes to 1.6.42. bugref:8515

  • 屬性 svn:eol-style 設為 native
檔案大小: 73.3 KB
 
1/* pngcp.c
2 *
3 * Copyright (c) 2016,2022,2024 John Cunningham Bowler
4 *
5 * This code is released under the libpng license.
6 * For conditions of distribution and use, see the disclaimer
7 * and license in png.h
8 *
9 * This is an example of copying a PNG without changes using the png_read_png
10 * and png_write_png interfaces. A considerable number of options are provided
11 * to manipulate the compression of the PNG data and other compressed chunks.
12 *
13 * For a more extensive example that uses the transforms see
14 * contrib/libtests/pngimage.c in the libpng distribution.
15 *
16 * This code is not intended for installation in a release system; the command
17 * line options are not documented and most of the behavior is intended for
18 * testing libpng performance, both speed and compression.
19 */
20
21#include "pnglibconf.h" /* To find how libpng was configured. */
22
23#ifdef PNG_PNGCP_TIMING_SUPPORTED
24 /* WARNING:
25 *
26 * This test is here to allow POSIX.1b extensions to be used if enabled in
27 * the compile; specifically the code requires_POSIX_C_SOURCE support of
28 * 199309L or later to enable clock_gettime use.
29 *
30 * IF this causes problems THEN compile with a strict ANSI C compiler and let
31 * this code turn on the POSIX features that it minimally requires.
32 *
33 * IF this does not work there is probably a bug in your ANSI C compiler or
34 * your POSIX implementation.
35 */
36# define _POSIX_C_SOURCE 199309L
37#else /* No timing support required */
38# define _POSIX_SOURCE 1
39#endif
40
41#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H)
42# include <config.h>
43#endif
44
45#include <stdio.h>
46
47/* Define the following to use this test against your installed libpng, rather
48 * than the one being built here:
49 */
50#ifdef PNG_FREESTANDING_TESTS
51# include <png.h>
52#else
53# include "../../png.h"
54#endif
55
56#if PNG_LIBPNG_VER < 10700
57 /* READ_PNG and WRITE_PNG were not defined, so: */
58# ifdef PNG_INFO_IMAGE_SUPPORTED
59# ifdef PNG_SEQUENTIAL_READ_SUPPORTED
60# define PNG_READ_PNG_SUPPORTED
61# endif /* SEQUENTIAL_READ */
62# ifdef PNG_WRITE_SUPPORTED
63# define PNG_WRITE_PNG_SUPPORTED
64# endif /* WRITE */
65# endif /* INFO_IMAGE */
66#endif /* pre 1.7.0 */
67
68#if (defined(PNG_READ_PNG_SUPPORTED)) && (defined(PNG_WRITE_PNG_SUPPORTED))
69#include <stdarg.h>
70#include <stdlib.h>
71#include <string.h>
72#include <errno.h>
73#include <limits.h>
74#include <assert.h>
75
76#include <unistd.h>
77#include <sys/stat.h>
78
79#include <zlib.h>
80
81#ifndef PNG_SETJMP_SUPPORTED
82# include <setjmp.h> /* because png.h did *not* include this */
83#endif
84
85#ifdef __cplusplus
86# define voidcast(type, value) static_cast<type>(value)
87#else
88# define voidcast(type, value) (value)
89#endif /* __cplusplus */
90
91/* 'CLOCK_PROCESS_CPUTIME_ID' is one of the clock timers for clock_gettime. It
92 * need not be supported even when clock_gettime is available. It returns the
93 * 'CPU' time the process has consumed. 'CPU' time is assumed to include time
94 * when the CPU is actually blocked by a pending cache fill but not time
95 * waiting for page faults. The attempt is to get a measure of the actual time
96 * the implementation takes to read a PNG ignoring the potentially very large IO
97 * overhead.
98 */
99#ifdef PNG_PNGCP_TIMING_SUPPORTED
100# include <time.h> /* clock_gettime and associated definitions */
101# ifndef CLOCK_PROCESS_CPUTIME_ID
102 /* Prevent inclusion of the spurious code: */
103# undef PNG_PNGCP_TIMING_SUPPORTED
104# endif
105#endif /* PNGCP_TIMING */
106
107/* So if the timing feature has been activated: */
108
109/* This structure is used to control the test of a single file. */
110typedef enum
111{
112 VERBOSE, /* switches on all messages */
113 INFORMATION,
114 WARNINGS, /* switches on warnings */
115 LIBPNG_WARNING,
116 APP_WARNING,
117 ERRORS, /* just errors */
118 APP_FAIL, /* continuable error - no need to longjmp */
119 LIBPNG_ERROR, /* this and higher cause a longjmp */
120 LIBPNG_BUG, /* erroneous behavior in libpng */
121 APP_ERROR, /* such as out-of-memory in a callback */
122 QUIET, /* no normal messages */
123 USER_ERROR, /* such as file-not-found */
124 INTERNAL_ERROR
125} error_level;
126#define LEVEL_MASK 0xf /* where the level is in 'options' */
127
128#define STRICT 0x010 /* Fail on warnings as well as errors */
129#define LOG 0x020 /* Log pass/fail to stdout */
130#define CONTINUE 0x040 /* Continue on APP_FAIL errors */
131#define SIZES 0x080 /* Report input and output sizes */
132#define SEARCH 0x100 /* Search IDAT compression options */
133#define NOWRITE 0x200 /* Do not write an output file */
134#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
135# define IGNORE_INDEX 0x400 /* Ignore out of range palette indices (BAD!) */
136# ifdef PNG_GET_PALETTE_MAX_SUPPORTED
137# define FIX_INDEX 0x800 /* 'Fix' out of range palette indices (OK) */
138# endif /* GET_PALETTE_MAX */
139#endif /* CHECK_FOR_INVALID_INDEX */
140#define OPTION 0x80000000 /* Used for handling options */
141#define LIST 0x80000001 /* Used for handling options */
142
143/* Result masks apply to the result bits in the 'results' field below; these
144 * bits are simple 1U<<error_level. A pass requires either nothing worse than
145 * warnings (--relaxes) or nothing worse than information (--strict)
146 */
147#define RESULT_STRICT(r) (((r) & ~((1U<<WARNINGS)-1)) == 0)
148#define RESULT_RELAXED(r) (((r) & ~((1U<<ERRORS)-1)) == 0)
149
150/* OPTION DEFINITIONS */
151static const char range_lo[] = "low";
152static const char range_hi[] = "high";
153static const char all[] = "all";
154#define RANGE(lo,hi) { range_lo, lo }, { range_hi, hi }
155typedef struct value_list
156{
157 const char *name; /* the command line name of the value */
158 int value; /* the actual value to use */
159} value_list;
160
161static const value_list
162#ifdef PNG_SW_COMPRESS_png_level
163vl_compression[] =
164{
165 /* Overall compression control. The order controls the search order for
166 * 'all'. Since the search is for the smallest the order used is low memory
167 * then high speed.
168 */
169 { "low-memory", PNG_COMPRESSION_LOW_MEMORY },
170 { "high-speed", PNG_COMPRESSION_HIGH_SPEED },
171 { "high-read-speed", PNG_COMPRESSION_HIGH_READ_SPEED },
172 { "low", PNG_COMPRESSION_LOW },
173 { "medium", PNG_COMPRESSION_MEDIUM },
174 { "old", PNG_COMPRESSION_COMPAT },
175 { "high", PNG_COMPRESSION_HIGH },
176 { all, 0 }
177},
178#endif /* SW_COMPRESS_png_level */
179
180#if defined(PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED) ||\
181 defined(PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED)
182vl_strategy[] =
183{
184 /* This controls the order of search. */
185 { "huffman", Z_HUFFMAN_ONLY },
186 { "RLE", Z_RLE },
187 { "fixed", Z_FIXED }, /* the remainder do window searches */
188 { "filtered", Z_FILTERED },
189 { "default", Z_DEFAULT_STRATEGY },
190 { all, 0 }
191},
192#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
193vl_windowBits_text[] =
194{
195 { "default", MAX_WBITS/*from zlib*/ },
196 { "minimum", 8 },
197 RANGE(8, MAX_WBITS/*from zlib*/),
198 { all, 0 }
199},
200#endif /* text compression */
201vl_level[] =
202{
203 { "default", Z_DEFAULT_COMPRESSION /* this is -1 */ },
204 { "none", Z_NO_COMPRESSION },
205 { "speed", Z_BEST_SPEED },
206 { "best", Z_BEST_COMPRESSION },
207 { "0", Z_NO_COMPRESSION },
208 RANGE(1, 9), /* this deliberately excludes '0' */
209 { all, 0 }
210},
211vl_memLevel[] =
212{
213 { "max", MAX_MEM_LEVEL }, /* zlib maximum */
214 { "1", 1 }, /* zlib minimum */
215 { "default", 8 }, /* zlib default */
216 { "2", 2 },
217 { "3", 3 },
218 { "4", 4 },
219 { "5", 5 }, /* for explicit testing */
220 RANGE(6, MAX_MEM_LEVEL/*zlib*/), /* exclude 5 and below: zlib bugs */
221 { all, 0 }
222},
223#endif /* WRITE_CUSTOMIZE_*COMPRESSION */
224#ifdef PNG_WRITE_FILTER_SUPPORTED
225vl_filter[] =
226{
227 { all, PNG_ALL_FILTERS },
228 { "off", PNG_NO_FILTERS },
229 { "none", PNG_FILTER_NONE },
230 { "sub", PNG_FILTER_SUB },
231 { "up", PNG_FILTER_UP },
232 { "avg", PNG_FILTER_AVG },
233 { "paeth", PNG_FILTER_PAETH }
234},
235#endif /* WRITE_FILTER */
236#ifdef PNG_PNGCP_TIMING_SUPPORTED
237# define PNGCP_TIME_READ 1
238# define PNGCP_TIME_WRITE 2
239vl_time[] =
240{
241 { "both", PNGCP_TIME_READ+PNGCP_TIME_WRITE },
242 { "off", 0 },
243 { "read", PNGCP_TIME_READ },
244 { "write", PNGCP_TIME_WRITE }
245},
246#endif /* PNGCP_TIMING */
247vl_IDAT_size[] = /* for png_set_IDAT_size */
248{
249 { "default", 0x7FFFFFFF },
250 { "minimal", 1 },
251 RANGE(1, 0x7FFFFFFF)
252},
253#ifndef PNG_SW_IDAT_size
254 /* Pre 1.7 API: */
255# define png_set_IDAT_size(p,v) png_set_compression_buffer_size(p, v)
256#endif /* !SW_IDAT_size */
257#define SL 8 /* stack limit in display, below */
258vl_log_depth[] = { { "on", 1 }, { "off", 0 }, RANGE(0, SL) },
259vl_on_off[] = { { "on", 1 }, { "off", 0 } };
260
261#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
262static value_list
263vl_windowBits_IDAT[] =
264{
265 { "default", MAX_WBITS },
266 { "small", 9 },
267 RANGE(8, MAX_WBITS), /* modified by set_windowBits_hi */
268 { all, 0 }
269};
270#endif /* IDAT compression */
271
272typedef struct option
273{
274 const char *name; /* name of the option */
275 png_uint_32 opt; /* an option, or OPTION or LIST */
276 png_byte search; /* Search on --search */
277 png_byte value_count; /* length of the list of values: */
278 const value_list *values; /* values for OPTION or LIST */
279} option;
280
281static const option options[] =
282{
283 /* struct display options, these are set when the command line is read */
284# define S(n,v) { #n, v, 0, 2, vl_on_off },
285 S(verbose, VERBOSE)
286 S(warnings, WARNINGS)
287 S(errors, ERRORS)
288 S(quiet, QUIET)
289 S(strict, STRICT)
290 S(log, LOG)
291 S(continue, CONTINUE)
292 S(sizes, SIZES)
293 S(search, SEARCH)
294 S(nowrite, NOWRITE)
295# ifdef IGNORE_INDEX
296 S(ignore-palette-index, IGNORE_INDEX)
297# endif /* IGNORE_INDEX */
298# ifdef FIX_INDEX
299 S(fix-palette-index, FIX_INDEX)
300# endif /* FIX_INDEX */
301# undef S
302
303 /* OPTION settings, these and LIST settings are read on demand */
304# define VLNAME(name) vl_ ## name
305# define VLSIZE(name) voidcast(png_byte,\
306 (sizeof VLNAME(name))/(sizeof VLNAME(name)[0]))
307# define VL(oname, name, type, search)\
308 { oname, type, search, VLSIZE(name), VLNAME(name) },
309# define VLO(oname, name, search) VL(oname, name, OPTION, search)
310
311# ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
312# define VLCIDAT(name) VLO(#name, name, 1/*search*/)
313# ifdef PNG_SW_COMPRESS_level
314# define VLCiCCP(name) VLO("ICC-profile-" #name, name, 0/*search*/)
315# else
316# define VLCiCCP(name)
317# endif
318# else
319# define VLCIDAT(name)
320# define VLCiCCP(name)
321# endif /* WRITE_CUSTOMIZE_COMPRESSION */
322
323# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
324# define VLCzTXt(name) VLO("text-" #name, name, 0/*search*/)
325# else
326# define VLCzTXt(name)
327# endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
328
329# define VLC(name) VLCIDAT(name) VLCiCCP(name) VLCzTXt(name)
330
331# ifdef PNG_SW_COMPRESS_png_level
332 /* The libpng compression level isn't searched because it just sets the
333 * other things that are searched!
334 */
335 VLO("compression", compression, 0)
336 VLO("text-compression", compression, 0)
337 VLO("ICC-profile-compression", compression, 0)
338# endif /* SW_COMPRESS_png_level */
339 VLC(strategy)
340 VLO("windowBits", windowBits_IDAT, 1)
341# ifdef PNG_SW_COMPRESS_windowBits
342 VLO("ICC-profile-windowBits", windowBits_text/*sic*/, 0)
343# endif
344 VLO("text-windowBits", windowBits_text, 0)
345 VLC(level)
346 VLC(memLevel)
347 VLO("IDAT-size", IDAT_size, 0)
348 VLO("log-depth", log_depth, 0)
349
350# undef VLO
351
352 /* LIST settings */
353# define VLL(name, search) VL(#name, name, LIST, search)
354#ifdef PNG_WRITE_FILTER_SUPPORTED
355 VLL(filter, 0)
356#endif /* WRITE_FILTER */
357#ifdef PNG_PNGCP_TIMING_SUPPORTED
358 VLL(time, 0)
359#endif /* PNGCP_TIMING */
360# undef VLL
361# undef VL
362};
363
364#ifdef __cplusplus
365 static const size_t option_count((sizeof options)/(sizeof options[0]));
366#else /* !__cplusplus */
367# define option_count ((sizeof options)/(sizeof options[0]))
368#endif /* !__cplusplus */
369
370static const char *
371cts(int ct)
372{
373 switch (ct)
374 {
375 case PNG_COLOR_TYPE_PALETTE: return "P";
376 case PNG_COLOR_TYPE_GRAY: return "G";
377 case PNG_COLOR_TYPE_GRAY_ALPHA: return "GA";
378 case PNG_COLOR_TYPE_RGB: return "RGB";
379 case PNG_COLOR_TYPE_RGB_ALPHA: return "RGBA";
380 default: return "INVALID";
381 }
382}
383
384struct display
385{
386 jmp_buf error_return; /* Where to go to on error */
387 unsigned int errset; /* error_return is set */
388 int errlevel; /* error level from longjmp */
389
390 const char *operation; /* What is happening */
391 const char *filename; /* The name of the original file */
392 const char *output_file; /* The name of the output file */
393
394 /* Used on both read and write: */
395 FILE *fp;
396
397 /* Used on a read, both the original read and when validating a written
398 * image.
399 */
400 png_alloc_size_t read_size;
401 png_structp read_pp;
402 png_infop ip;
403# if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
404 png_textp text_ptr; /* stash of text chunks */
405 int num_text;
406 int text_stashed;
407# endif /* pre 1.7 */
408
409# ifdef PNG_PNGCP_TIMING_SUPPORTED
410 struct timespec read_time;
411 struct timespec read_time_total;
412 struct timespec write_time;
413 struct timespec write_time_total;
414# endif /* PNGCP_TIMING */
415
416 /* Used to write a new image (the original info_ptr is used) */
417# define MAX_SIZE ((png_alloc_size_t)(-1))
418 png_alloc_size_t write_size;
419 png_alloc_size_t best_size;
420 png_structp write_pp;
421
422 /* Base file information */
423 png_alloc_size_t size;
424 png_uint_32 w;
425 png_uint_32 h;
426 int bpp;
427 png_byte ct;
428 int no_warnings; /* Do not output libpng warnings */
429 int min_windowBits; /* The windowBits range is 8..8 */
430
431 /* Options handling */
432 png_uint_32 results; /* A mask of errors seen */
433 png_uint_32 options; /* See display_log below */
434 png_byte entry[option_count]; /* The selected entry+1 of an option
435 * that appears on the command line, or
436 * 0 if it was not given. */
437 int value[option_count]; /* Corresponding value */
438
439 /* Compression exhaustive testing */
440 /* Temporary variables used only while testing a single collection of
441 * settings:
442 */
443 unsigned int csp; /* next stack entry to use */
444 unsigned int nsp; /* highest active entry+1 found so far */
445
446 /* Values used while iterating through all the combinations of settings for a
447 * single file:
448 */
449 unsigned int tsp; /* nsp from the last run; this is the
450 * index+1 of the highest active entry on
451 * this run; this entry will be advanced.
452 */
453 int opt_string_start; /* Position in buffer for the first
454 * searched option; non-zero if earlier
455 * options were set on the command line.
456 */
457 struct stack
458 {
459 png_alloc_size_t best_size; /* Best so far for this option */
460 png_alloc_size_t lo_size;
461 png_alloc_size_t hi_size;
462 int lo, hi; /* For binary chop of a range */
463 int best_val; /* Best value found so far */
464 int opt_string_end; /* End of the option string in 'curr' */
465 png_byte opt; /* The option being tested */
466 png_byte entry; /* The next value entry to be tested */
467 png_byte end; /* This is the last entry */
468 } stack[SL]; /* Stack of entries being tested */
469 char curr[32*SL]; /* current options being tested */
470 char best[32*SL]; /* best options */
471
472 char namebuf[FILENAME_MAX]; /* output file name */
473};
474
475static void
476display_init(struct display *dp)
477 /* Call this only once right at the start to initialize the control
478 * structure, the (struct buffer) lists are maintained across calls - the
479 * memory is not freed.
480 */
481{
482 memset(dp, 0, sizeof *dp);
483 dp->operation = "internal error";
484 dp->filename = "command line";
485 dp->output_file = "no output file";
486 dp->options = WARNINGS; /* default to !verbose, !quiet */
487 dp->fp = NULL;
488 dp->read_pp = NULL;
489 dp->ip = NULL;
490 dp->write_pp = NULL;
491 dp->min_windowBits = -1; /* this is an OPTIND, so -1 won't match anything */
492# if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
493 dp->text_ptr = NULL;
494 dp->num_text = 0;
495 dp->text_stashed = 0;
496# endif /* pre 1.7 */
497}
498
499static void
500display_clean_read(struct display *dp, int freeinfo)
501{
502 if (dp->read_pp != NULL)
503 png_destroy_read_struct(&dp->read_pp, freeinfo ? &dp->ip : NULL, NULL);
504
505 if (dp->fp != NULL)
506 {
507 FILE *fp = dp->fp;
508 dp->fp = NULL;
509 (void)fclose(fp);
510 }
511}
512
513static void
514display_clean_write(struct display *dp, int freeinfo)
515{
516 if (dp->fp != NULL)
517 {
518 FILE *fp = dp->fp;
519 dp->fp = NULL;
520 (void)fclose(fp);
521 }
522
523 if (dp->write_pp != NULL)
524 png_destroy_write_struct(&dp->write_pp, freeinfo ? &dp->ip : NULL);
525}
526
527static void
528display_clean(struct display *dp)
529{
530 display_clean_read(dp, 1/*freeinfo*/);
531 display_clean_write(dp, 1/*freeinfo*/);
532 dp->output_file = NULL;
533
534# if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
535 /* This is actually created and used by the write code, but only
536 * once; it has to be retained for subsequent writes of the same file.
537 */
538 if (dp->text_stashed)
539 {
540 dp->text_stashed = 0;
541 dp->num_text = 0;
542 free(dp->text_ptr);
543 dp->text_ptr = NULL;
544 }
545# endif /* pre 1.7 */
546
547 /* leave the filename for error detection */
548 dp->results = 0; /* reset for next time */
549}
550
551static void
552display_destroy(struct display *dp)
553{
554 /* Release any memory held in the display. */
555 display_clean(dp);
556}
557
558static struct display *
559get_dp(png_structp pp)
560 /* The display pointer is always stored in the png_struct error pointer */
561{
562 struct display *dp = (struct display*)png_get_error_ptr(pp);
563
564 if (dp == NULL)
565 {
566 fprintf(stderr, "pngcp: internal error (no display)\n");
567 exit(99); /* prevents a crash */
568 }
569
570 return dp;
571}
572
573/* error handling */
574#ifdef __GNUC__
575# define VGATTR __attribute__((__format__ (__printf__,3,4)))
576 /* Required to quiet GNUC warnings when the compiler sees a stdarg function
577 * that calls one of the stdio v APIs.
578 */
579#else
580# define VGATTR
581#endif
582static void VGATTR
583display_log(struct display *dp, error_level level, const char *fmt, ...)
584 /* 'level' is as above, fmt is a stdio style format string. This routine
585 * does not return if level is above LIBPNG_WARNING
586 */
587{
588 dp->results |= 1U << level;
589
590 if (level > (error_level)(dp->options & LEVEL_MASK))
591 {
592 const char *lp;
593 va_list ap;
594
595 switch (level)
596 {
597 case INFORMATION: lp = "information"; break;
598 case LIBPNG_WARNING: lp = "warning(libpng)"; break;
599 case APP_WARNING: lp = "warning(pngcp)"; break;
600 case APP_FAIL: lp = "error(continuable)"; break;
601 case LIBPNG_ERROR: lp = "error(libpng)"; break;
602 case LIBPNG_BUG: lp = "bug(libpng)"; break;
603 case APP_ERROR: lp = "error(pngcp)"; break;
604 case USER_ERROR: lp = "error(user)"; break;
605
606 case INTERNAL_ERROR: /* anything unexpected is an internal error: */
607 case VERBOSE: case WARNINGS: case ERRORS: case QUIET:
608 default: lp = "bug(pngcp)"; break;
609 }
610
611 fprintf(stderr, "%s: %s: %s",
612 dp->filename != NULL ? dp->filename : "<stdin>", lp, dp->operation);
613
614 fprintf(stderr, ": ");
615
616 va_start(ap, fmt);
617 vfprintf(stderr, fmt, ap);
618 va_end(ap);
619
620 fputc('\n', stderr);
621 }
622 /* else do not output any message */
623
624 /* Errors cause this routine to exit to the fail code */
625 if (level > APP_FAIL || (level > ERRORS && !(dp->options & CONTINUE)))
626 {
627 if (dp->errset)
628 {
629 dp->errlevel = level;
630 longjmp(dp->error_return, level);
631 }
632
633 else
634 exit(99);
635 }
636}
637
638#if PNG_LIBPNG_VER < 10700 && defined PNG_TEXT_SUPPORTED
639static void
640text_stash(struct display *dp)
641{
642 /* libpng 1.6 and earlier fixed a bug whereby text chunks were written
643 * multiple times by png_write_png; the issue was that png_write_png passed
644 * the same png_info to both png_write_info and png_write_end. Rather than
645 * fixing it by recording the information in the png_struct, or by recording
646 * where to write the chunks, the fix made was to change the 'compression'
647 * field of the chunk to invalid values, rendering the png_info somewhat
648 * useless.
649 *
650 * The only fix for this given that we use the png_info more than once is to
651 * make a copy of the text chunks and png_set_text it each time. This adds a
652 * text chunks, so they get replicated, but only the new set gets written
653 * each time. This uses memory like crazy but there is no way to delete the
654 * useless chunks from the png_info.
655 *
656 * To make this slightly more efficient only the top level structure is
657 * copied; since the old strings are actually preserved (in 1.6 and earlier)
658 * this happens to work.
659 */
660 png_textp chunks = NULL;
661
662 dp->num_text = png_get_text(dp->write_pp, dp->ip, &chunks, NULL);
663
664 if (dp->num_text > 0)
665 {
666 dp->text_ptr = voidcast(png_textp, malloc(dp->num_text * sizeof *chunks));
667
668 if (dp->text_ptr == NULL)
669 display_log(dp, APP_ERROR, "text chunks: stash malloc failed");
670
671 else
672 memcpy(dp->text_ptr, chunks, dp->num_text * sizeof *chunks);
673 }
674
675 dp->text_stashed = 1; /* regardless of whether there are chunks or not */
676}
677
678#define text_stash(dp) if (!dp->text_stashed) text_stash(dp)
679
680static void
681text_restore(struct display *dp)
682{
683 /* libpng makes a copy, so this is fine: */
684 if (dp->text_ptr != NULL)
685 png_set_text(dp->write_pp, dp->ip, dp->text_ptr, dp->num_text);
686}
687
688#define text_restore(dp) if (dp->text_stashed) text_restore(dp)
689
690#else
691#define text_stash(dp) ((void)0)
692#define text_restore(dp) ((void)0)
693#endif /* pre 1.7 */
694
695/* OPTIONS:
696 *
697 * The command handles options of the forms:
698 *
699 * --option
700 * Turn an option on (Option)
701 * --no-option
702 * Turn an option off (Option)
703 * --option=value
704 * Set an option to a value (Value)
705 * --option=val1,val2,val3
706 * Set an option to a bitmask constructed from the values (List)
707 */
708static png_byte
709option_index(struct display *dp, const char *opt, size_t len)
710 /* Return the index (in options[]) of the given option, outputs an error if
711 * it does not exist. Takes the name of the option and a length (number of
712 * characters in the name).
713 */
714{
715 png_byte j;
716
717 for (j=0; j<option_count; ++j)
718 if (strncmp(options[j].name, opt, len) == 0 && options[j].name[len] == 0)
719 return j;
720
721 /* If the setjmp buffer is set the code is asking for an option index; this
722 * is bad. Otherwise this is the command line option parsing.
723 */
724 display_log(dp, dp->errset ? INTERNAL_ERROR : USER_ERROR,
725 "%.*s: unknown option", (int)/*SAFE*/len, opt);
726 abort(); /* NOT REACHED */
727}
728
729/* This works for an option name (no quotes): */
730#define OPTIND(dp, name) option_index(dp, #name, (sizeof #name)-1)
731
732static int
733get_option(struct display *dp, const char *opt, int *value)
734{
735 png_byte i = option_index(dp, opt, strlen(opt));
736
737 if (dp->entry[i]) /* option was set on command line */
738 {
739 *value = dp->value[i];
740 return 1;
741 }
742
743 else
744 return 0;
745}
746
747static int
748set_opt_string_(struct display *dp, unsigned int sp, png_byte opt,
749 const char *entry_name)
750 /* Add the appropriate option string to dp->curr. */
751{
752 int offset, add;
753
754 if (sp > 0)
755 offset = dp->stack[sp-1].opt_string_end;
756
757 else
758 offset = dp->opt_string_start;
759
760 if (entry_name == range_lo)
761 add = sprintf(dp->curr+offset, " --%s=%d", options[opt].name,
762 dp->value[opt]);
763
764 else
765 add = sprintf(dp->curr+offset, " --%s=%s", options[opt].name, entry_name);
766
767 if (add < 0)
768 display_log(dp, INTERNAL_ERROR, "sprintf failed");
769
770 assert(offset+add < (int)/*SAFE*/sizeof dp->curr);
771 return offset+add;
772}
773
774static void
775set_opt_string(struct display *dp, unsigned int sp)
776 /* Add the appropriate option string to dp->curr. */
777{
778 dp->stack[sp].opt_string_end = set_opt_string_(dp, sp, dp->stack[sp].opt,
779 options[dp->stack[sp].opt].values[dp->stack[sp].entry].name);
780}
781
782static void
783record_opt(struct display *dp, png_byte opt, const char *entry_name)
784 /* Record this option in dp->curr; called for an option not being searched,
785 * the caller passes in the name of the value, or range_lo to use the
786 * numerical value.
787 */
788{
789 unsigned int sp = dp->csp; /* stack entry of next searched option */
790
791 if (sp >= dp->tsp)
792 {
793 /* At top of stack; add the opt string for this entry to the previous
794 * searched entry or the start of the dp->curr buffer if there is nothing
795 * on the stack yet (sp == 0).
796 */
797 int offset = set_opt_string_(dp, sp, opt, entry_name);
798
799 if (sp > 0)
800 dp->stack[sp-1].opt_string_end = offset;
801
802 else
803 dp->opt_string_start = offset;
804 }
805
806 /* else do nothing: option already recorded */
807}
808
809static int
810opt_list_end(struct display *dp, png_byte opt, png_byte entry)
811{
812 if (options[opt].values[entry].name == range_lo)
813 return entry+1U >= options[opt].value_count /* missing range_hi */ ||
814 options[opt].values[entry+1U].name != range_hi /* likewise */ ||
815 options[opt].values[entry+1U].value <= dp->value[opt] /* range end */;
816
817 else
818 return entry+1U >= options[opt].value_count /* missing 'all' */ ||
819 options[opt].values[entry+1U].name == all /* last entry */;
820}
821
822static void
823push_opt(struct display *dp, unsigned int sp, png_byte opt, int search)
824 /* Push a new option onto the stack, initializing the new stack entry
825 * appropriately; this does all the work of next_opt (setting end/nsp) for
826 * the first entry in the list.
827 */
828{
829 png_byte entry;
830 const char *entry_name;
831
832 assert(sp == dp->tsp && sp < SL);
833
834 /* The starting entry is entry 0 unless there is a range in which case it is
835 * the entry corresponding to range_lo:
836 */
837 entry = options[opt].value_count;
838 assert(entry > 0U);
839
840 do
841 {
842 entry_name = options[opt].values[--entry].name;
843 if (entry_name == range_lo)
844 break;
845 }
846 while (entry > 0U);
847
848 dp->tsp = sp+1U;
849 dp->stack[sp].best_size =
850 dp->stack[sp].lo_size =
851 dp->stack[sp].hi_size = MAX_SIZE;
852
853 if (search && entry_name == range_lo) /* search this range */
854 {
855 dp->stack[sp].lo = options[opt].values[entry].value;
856 /* check for a mal-formed RANGE above: */
857 assert(entry+1 < options[opt].value_count &&
858 options[opt].values[entry+1].name == range_hi);
859 dp->stack[sp].hi = options[opt].values[entry+1].value;
860 }
861
862 else
863 {
864 /* next_opt will just iterate over the range. */
865 dp->stack[sp].lo = INT_MAX;
866 dp->stack[sp].hi = INT_MIN; /* Prevent range chop */
867 }
868
869 dp->stack[sp].opt = opt;
870 dp->stack[sp].entry = entry;
871 dp->stack[sp].best_val = dp->value[opt] = options[opt].values[entry].value;
872
873 set_opt_string(dp, sp);
874
875 /* This works for the search case too; if the range has only one entry 'end'
876 * will be marked here.
877 */
878 if (opt_list_end(dp, opt, entry))
879 {
880 dp->stack[sp].end = 1;
881 /* Skip the warning if pngcp did this itself. See the code in
882 * set_windowBits_hi.
883 */
884 if (opt != dp->min_windowBits)
885 display_log(dp, APP_WARNING, "%s: only testing one value",
886 options[opt].name);
887 }
888
889 else
890 {
891 dp->stack[sp].end = 0;
892 dp->nsp = dp->tsp;
893 }
894
895 /* Do a lazy cache of the text chunks for libpng 1.6 and earlier; this is
896 * because they can only be written once(!) so if we are going to re-use the
897 * png_info we need a copy.
898 */
899 text_stash(dp);
900}
901
902static void
903next_opt(struct display *dp, unsigned int sp)
904 /* Return the next value for this option. When called 'sp' is expected to be
905 * the topmost stack entry - only the topmost entry changes each time round -
906 * and there must be a valid entry to return. next_opt will set dp->nsp to
907 * sp+1 if more entries are available, otherwise it will not change it and
908 * set dp->stack[s].end to true.
909 */
910{
911 int search = 0;
912 png_byte entry, opt;
913 const char *entry_name;
914
915 /* dp->stack[sp] must be the top stack entry and it must be active: */
916 assert(sp+1U == dp->tsp && !dp->stack[sp].end);
917
918 opt = dp->stack[sp].opt;
919 entry = dp->stack[sp].entry;
920 assert(entry+1U < options[opt].value_count);
921 entry_name = options[opt].values[entry].name;
922 assert(entry_name != NULL);
923
924 /* For ranges increment the value but don't change the entry, for all other
925 * cases move to the next entry and load its value:
926 */
927 if (entry_name == range_lo) /* a range */
928 {
929 /* A range can be iterated over or searched. The default iteration option
930 * is indicated by hi < lo on the stack, otherwise the range being search
931 * is [lo..hi] (inclusive).
932 */
933 if (dp->stack[sp].lo > dp->stack[sp].hi)
934 dp->value[opt]++;
935
936 else
937 {
938 /* This is the best size found for this option value: */
939 png_alloc_size_t best_size = dp->stack[sp].best_size;
940 int lo = dp->stack[sp].lo;
941 int hi = dp->stack[sp].hi;
942 int val = dp->value[opt];
943
944 search = 1; /* end is determined here */
945 assert(best_size < MAX_SIZE);
946
947 if (val == lo)
948 {
949 /* Finding the best for the low end of the range: */
950 dp->stack[sp].lo_size = best_size;
951 assert(hi > val);
952
953 if (hi == val+1) /* only 2 entries */
954 dp->stack[sp].end = 1;
955
956 val = hi;
957 }
958
959 else if (val == hi)
960 {
961 dp->stack[sp].hi_size = best_size;
962 assert(val > lo+1); /* else 'end' set above */
963
964 if (val == lo+2) /* only three entries to test */
965 dp->stack[sp].end = 1;
966
967 val = (lo + val)/2;
968 }
969
970 else
971 {
972 png_alloc_size_t lo_size = dp->stack[sp].lo_size;
973 png_alloc_size_t hi_size = dp->stack[sp].hi_size;
974
975 /* lo and hi should have been tested. */
976 assert(lo_size < MAX_SIZE && hi_size < MAX_SIZE);
977
978 /* These cases arise with the 'probe' handling below when there is a
979 * dip or peak in the size curve.
980 */
981 if (val < lo) /* probing a new lo */
982 {
983 /* Swap lo and val: */
984 dp->stack[sp].lo = val;
985 dp->stack[sp].lo_size = best_size;
986 val = lo;
987 best_size = lo_size;
988 lo = dp->stack[sp].lo;
989 lo_size = dp->stack[sp].lo_size;
990 }
991
992 else if (val > hi) /* probing a new hi */
993 {
994 /* Swap hi and val: */
995 dp->stack[sp].hi = val;
996 dp->stack[sp].hi_size = best_size;
997 val = hi;
998 best_size = hi_size;
999 hi = dp->stack[sp].hi;
1000 hi_size = dp->stack[sp].hi_size;
1001 }
1002
1003 /* The following should be true or something got messed up above. */
1004 assert(lo < val && val < hi);
1005
1006 /* If there are only four entries (lo, val, hi plus one more) just
1007 * test the remaining entry.
1008 */
1009 if (hi == lo+3)
1010 {
1011 /* Because of the 'probe' code val can either be lo+1 or hi-1; we
1012 * need to test the other.
1013 */
1014 val = lo + ((val == lo+1) ? 2 : 1);
1015 assert(lo < val && val < hi);
1016 dp->stack[sp].end = 1;
1017 }
1018
1019 else
1020 {
1021 /* There are at least 2 entries still untested between lo and hi,
1022 * i.e. hi >= lo+4. 'val' is the midpoint +/- 0.5
1023 *
1024 * Separate out the four easy cases when lo..val..hi are
1025 * monotonically decreased or (more weird) increasing:
1026 */
1027 assert(hi > lo+3);
1028
1029 if (lo_size <= best_size && best_size <= hi_size)
1030 {
1031 /* Select the low range; testing this first favours the low
1032 * range over the high range when everything comes out equal.
1033 * Because of the probing 'val' may be lo+1. In that case end
1034 * the search and set 'val' to lo+2.
1035 */
1036 if (val == lo+1)
1037 {
1038 ++val;
1039 dp->stack[sp].end = 1;
1040 }
1041
1042 else
1043 {
1044 dp->stack[sp].hi = hi = val;
1045 dp->stack[sp].hi_size = best_size;
1046 val = (lo + val) / 2;
1047 }
1048 }
1049
1050 else if (lo_size >= best_size && best_size >= hi_size)
1051 {
1052 /* Monotonically decreasing size; this is the expected case.
1053 * Select the high end of the range. As above, val may be
1054 * hi-1.
1055 */
1056 if (val == hi-1)
1057 {
1058 --val;
1059 dp->stack[sp].end = 1;
1060 }
1061
1062 else
1063 {
1064 dp->stack[sp].lo = lo = val;
1065 dp->stack[sp].lo_size = best_size;
1066 val = (val + hi) / 2;
1067 }
1068 }
1069
1070 /* If both those tests failed 'best_size' is either greater than
1071 * or less than both lo_size and hi_size. There is a peak or dip
1072 * in the curve of sizes from lo to hi and val is on the peak or
1073 * dip.
1074 *
1075 * Because the ranges being searched as so small (level is 1..9,
1076 * windowBits 8..15, memLevel 1..9) there will only be at most
1077 * three untested values between lo..val and val..hi, so solve
1078 * the problem by probing down from hi or up from lo, whichever
1079 * is the higher.
1080 *
1081 * This is the place where 'val' is set to outside the range
1082 * lo..hi, described as 'probing', though maybe 'narrowing' would
1083 * be more accurate.
1084 */
1085 else if (lo_size <= hi_size) /* down from hi */
1086 {
1087 dp->stack[sp].hi = val;
1088 dp->stack[sp].hi_size = best_size;
1089 val = --hi;
1090 }
1091
1092 else /* up from low */
1093 {
1094 dp->stack[sp].lo = val;
1095 dp->stack[sp].lo_size = best_size;
1096 val = ++lo;
1097 }
1098
1099 /* lo and hi are still the true range limits, check for the end
1100 * condition.
1101 */
1102 assert(hi > lo+1);
1103 if (hi <= lo+2)
1104 dp->stack[sp].end = 1;
1105 }
1106 }
1107
1108 assert(val != dp->stack[sp].best_val); /* should be a new value */
1109 dp->value[opt] = val;
1110 dp->stack[sp].best_size = MAX_SIZE;
1111 }
1112 }
1113
1114 else
1115 {
1116 /* Increment 'entry' */
1117 dp->value[opt] = options[opt].values[++entry].value;
1118 dp->stack[sp].entry = entry;
1119 }
1120
1121 set_opt_string(dp, sp);
1122
1123 if (!search && opt_list_end(dp, opt, entry)) /* end of list */
1124 dp->stack[sp].end = 1;
1125
1126 else if (!dp->stack[sp].end) /* still active after all these tests */
1127 dp->nsp = dp->tsp;
1128}
1129
1130static int
1131compare_option(const struct display *dp, unsigned int sp)
1132{
1133 int opt = dp->stack[sp].opt;
1134
1135 /* If the best so far is numerically less than the current value the
1136 * current set of options is invariably worse.
1137 */
1138 if (dp->stack[sp].best_val < dp->value[opt])
1139 return -1;
1140
1141 /* Lists of options are searched out of numerical order (currently only
1142 * strategy), so only return +1 here when a range is being searched.
1143 */
1144 else if (dp->stack[sp].best_val > dp->value[opt])
1145 {
1146 if (dp->stack[sp].lo <= dp->stack[sp].hi /*searching*/)
1147 return 1;
1148
1149 else
1150 return -1;
1151 }
1152
1153 else
1154 return 0; /* match; current value is the best one */
1155}
1156
1157static int
1158advance_opt(struct display *dp, png_byte opt, int search)
1159{
1160 unsigned int sp = dp->csp++; /* my stack entry */
1161
1162 assert(sp >= dp->nsp); /* nsp starts off zero */
1163
1164 /* If the entry was active in the previous run dp->stack[sp] is already
1165 * set up and dp->tsp will be greater than sp, otherwise a new entry
1166 * needs to be created.
1167 *
1168 * dp->nsp is handled this way:
1169 *
1170 * 1) When an option is pushed onto the stack dp->nsp and dp->tsp are
1171 * both set (by push_opt) to the next stack entry *unless* there is
1172 * only one entry in the new list, in which case dp->stack[sp].end
1173 * is set.
1174 *
1175 * 2) For the top stack entry next_opt is called. The entry must be
1176 * active (dp->stack[sp].end is not set) and either 'nsp' or 'end'
1177 * will be updated as appropriate.
1178 *
1179 * 3) For lower stack entries nsp is set unless the stack entry is
1180 * already at the end. This means that when all the higher entries
1181 * are popped this entry will be too.
1182 */
1183 if (sp >= dp->tsp)
1184 {
1185 push_opt(dp, sp, opt, search); /* This sets tsp to sp+1 */
1186 return 1; /* initialized */
1187 }
1188
1189 else
1190 {
1191 int ret = 0; /* unchanged */
1192
1193 /* An option that is already on the stack; update best_size and best_val
1194 * if appropriate. On the first run there are no previous values and
1195 * dp->write_size will be MAX_SIZE, however on the first run dp->tsp
1196 * starts off as 0.
1197 */
1198 assert(dp->write_size > 0U && dp->write_size < MAX_SIZE);
1199
1200 if (dp->stack[sp].best_size > dp->write_size ||
1201 (dp->stack[sp].best_size == dp->write_size &&
1202 compare_option(dp, sp) > 0))
1203 {
1204 dp->stack[sp].best_size = dp->write_size;
1205 dp->stack[sp].best_val = dp->value[opt];
1206 }
1207
1208 if (sp+1U >= dp->tsp)
1209 {
1210 next_opt(dp, sp);
1211 ret = 1; /* advanced */
1212 }
1213
1214 else if (!dp->stack[sp].end) /* Active, not at top of stack */
1215 dp->nsp = sp+1U;
1216
1217 return ret; /* advanced || unchanged */
1218 }
1219}
1220
1221static int
1222getallopts_(struct display *dp, png_byte opt, int *value, int record)
1223 /* Like getop but iterate over all the values if the option was set to "all".
1224 */
1225{
1226 if (dp->entry[opt]) /* option was set on command line */
1227 {
1228 /* Simple, single value, entries don't have a stack frame and have a fixed
1229 * value (it doesn't change once set on the command line). Otherwise the
1230 * value (entry) selected from the command line is 'all':
1231 */
1232 const char *entry_name = options[opt].values[dp->entry[opt]-1].name;
1233
1234 if (entry_name == all)
1235 (void)advance_opt(dp, opt, 0/*do not search; iterate*/);
1236
1237 else if (record)
1238 record_opt(dp, opt, entry_name);
1239
1240 *value = dp->value[opt];
1241 return 1; /* set */
1242 }
1243
1244 else
1245 return 0; /* not set */
1246}
1247
1248static int
1249getallopts(struct display *dp, const char *opt_str, int *value)
1250{
1251 return getallopts_(dp, option_index(dp, opt_str, strlen(opt_str)), value, 0);
1252}
1253
1254static int
1255getsearchopts(struct display *dp, const char *opt_str, int *value)
1256 /* As above except that if the option was not set try a search */
1257{
1258 png_byte istrat;
1259 png_byte opt = option_index(dp, opt_str, strlen(opt_str));
1260 int record = options[opt].search;
1261 const char *entry_name;
1262
1263 /* If it was set on the command line honour the setting, including 'all'
1264 * which will override the built in search:
1265 */
1266 if (getallopts_(dp, opt, value, record))
1267 return 1;
1268
1269 else if (!record) /* not a search option */
1270 return 0; /* unset and not searched */
1271
1272 /* Otherwise decide what to do here. */
1273 istrat = OPTIND(dp, strategy);
1274 entry_name = range_lo; /* record the value, not the name */
1275
1276 if (opt == istrat) /* search all strategies */
1277 (void)advance_opt(dp, opt, 0/*iterate*/), record=0;
1278
1279 else if (opt == OPTIND(dp, level))
1280 {
1281 /* Both RLE and HUFFMAN don't benefit from level increases */
1282 if (dp->value[istrat] == Z_RLE || dp->value[istrat] == Z_HUFFMAN_ONLY)
1283 dp->value[opt] = 1;
1284
1285 else /* fixed, filtered or default */
1286 (void)advance_opt(dp, opt, 1/*search*/), record=0;
1287 }
1288
1289 else if (opt == OPTIND(dp, windowBits))
1290 {
1291 /* Changing windowBits for strategies that do not search the window is
1292 * pointless. Huffman-only does not search, RLE only searches backwards
1293 * one byte, so given that the maximum string length is 258, a windowBits
1294 * of 9 is always sufficient.
1295 */
1296 if (dp->value[istrat] == Z_HUFFMAN_ONLY)
1297 dp->value[opt] = 8;
1298
1299 else if (dp->value[istrat] == Z_RLE)
1300 dp->value[opt] = 9;
1301
1302 else /* fixed, filtered or default */
1303 (void)advance_opt(dp, opt, 1/*search*/), record=0;
1304 }
1305
1306 else if (opt == OPTIND(dp, memLevel))
1307 {
1308# if 0
1309 (void)advance_opt(dp, opt, 0/*all*/), record=0;
1310# else
1311 dp->value[opt] = MAX_MEM_LEVEL;
1312# endif
1313 }
1314
1315 else /* something else */
1316 assert(0=="reached");
1317
1318 if (record)
1319 record_opt(dp, opt, entry_name);
1320
1321 /* One of the above searched options: */
1322 *value = dp->value[opt];
1323 return 1;
1324}
1325
1326static int
1327find_val(struct display *dp, png_byte opt, const char *str, size_t len)
1328 /* Like option_index but sets (index+i) of the entry in options[opt] that
1329 * matches str[0..len-1] into dp->entry[opt] as well as returning the actual
1330 * value.
1331 */
1332{
1333 int rlo = INT_MAX, rhi = INT_MIN;
1334 png_byte j, irange = 0;
1335
1336 for (j=1U; j<=options[opt].value_count; ++j)
1337 {
1338 if (strncmp(options[opt].values[j-1U].name, str, len) == 0 &&
1339 options[opt].values[j-1U].name[len] == 0)
1340 {
1341 dp->entry[opt] = j;
1342 return options[opt].values[j-1U].value;
1343 }
1344 else if (options[opt].values[j-1U].name == range_lo)
1345 rlo = options[opt].values[j-1U].value, irange = j;
1346 else if (options[opt].values[j-1U].name == range_hi)
1347 rhi = options[opt].values[j-1U].value;
1348 }
1349
1350 /* No match on the name, but there may be a range. */
1351 if (irange > 0)
1352 {
1353 char *ep = NULL;
1354 long l = strtol(str, &ep, 0);
1355
1356 if (ep == str+len && l >= rlo && l <= rhi)
1357 {
1358 dp->entry[opt] = irange; /* range_lo */
1359 return (int)/*SAFE*/l;
1360 }
1361 }
1362
1363 display_log(dp, dp->errset ? INTERNAL_ERROR : USER_ERROR,
1364 "%s: unknown value setting '%.*s'", options[opt].name,
1365 (int)/*SAFE*/len, str);
1366 abort(); /* NOT REACHED */
1367}
1368
1369static int
1370opt_check(struct display *dp, const char *arg)
1371{
1372 assert(dp->errset == 0);
1373
1374 if (arg != NULL && arg[0] == '-' && arg[1] == '-')
1375 {
1376 int i = 0, negate = (strncmp(arg+2, "no-", 3) == 0), val;
1377 png_byte j;
1378
1379 if (negate)
1380 arg += 5; /* --no- */
1381
1382 else
1383 arg += 2; /* -- */
1384
1385 /* Find the length (expect arg\0 or arg=) */
1386 while (arg[i] != 0 && arg[i] != '=') ++i;
1387
1388 /* So arg[0..i-1] is the argument name, this does not return if this isn't
1389 * a valid option name.
1390 */
1391 j = option_index(dp, arg, i);
1392
1393 /* It matcheth an option; check the remainder. */
1394 if (arg[i] == 0) /* no specified value, use the default */
1395 {
1396 val = options[j].values[negate].value;
1397 dp->entry[j] = (png_byte)/*SAFE*/(negate + 1U);
1398 }
1399
1400 else
1401 {
1402 const char *list = arg + (i+1);
1403
1404 /* Expect a single value here unless this is a list, in which case
1405 * multiple values are combined.
1406 */
1407 if (options[j].opt != LIST)
1408 {
1409 /* find_val sets 'dp->entry[j]' to a non-zero value: */
1410 val = find_val(dp, j, list, strlen(list));
1411
1412 if (negate)
1413 {
1414 if (options[j].opt < OPTION)
1415 val = !val;
1416
1417 else
1418 {
1419 display_log(dp, USER_ERROR,
1420 "%.*s: option=arg cannot be negated", i, arg);
1421 abort(); /* NOT REACHED */
1422 }
1423 }
1424 }
1425
1426 else /* multiple options separated by ',' characters */
1427 {
1428 /* --no-option negates list values from the default, which should
1429 * therefore be 'all'. Notice that if the option list is empty in
1430 * this case nothing will be removed and therefore --no-option= is
1431 * the same as --option.
1432 */
1433 if (negate)
1434 val = options[j].values[0].value;
1435
1436 else
1437 val = 0;
1438
1439 while (*list != 0) /* allows option= which sets 0 */
1440 {
1441 /* A value is terminated by the end of the list or a ','
1442 * character.
1443 */
1444 int v, iv;
1445
1446 iv = 0; /* an index into 'list' */
1447 while (list[++iv] != 0 && list[iv] != ',') {}
1448
1449 v = find_val(dp, j, list, iv);
1450
1451 if (negate)
1452 val &= ~v;
1453
1454 else
1455 val |= v;
1456
1457 list += iv;
1458 if (*list != 0)
1459 ++list; /* skip the ',' */
1460 }
1461 }
1462 }
1463
1464 /* 'val' is the new value, store it for use later and debugging: */
1465 dp->value[j] = val;
1466
1467 if (options[j].opt < LEVEL_MASK)
1468 {
1469 /* The handling for error levels is to set the level. */
1470 if (val) /* Set this level */
1471 dp->options = (dp->options & ~LEVEL_MASK) | options[j].opt;
1472
1473 else
1474 display_log(dp, USER_ERROR,
1475 "%.*s: messages cannot be turned off individually; set a message level",
1476 i, arg);
1477 }
1478
1479 else if (options[j].opt < OPTION)
1480 {
1481 if (val)
1482 dp->options |= options[j].opt;
1483
1484 else
1485 dp->options &= ~options[j].opt;
1486 }
1487
1488 return 1; /* this is an option */
1489 }
1490
1491 else
1492 return 0; /* not an option */
1493}
1494
1495#ifdef PNG_PNGCP_TIMING_SUPPORTED
1496static void
1497set_timer(struct display *dp, struct timespec *timer)
1498{
1499 /* Do the timing using clock_gettime and the per-process timer. */
1500 if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, timer))
1501 {
1502 display_log(dp, APP_ERROR,
1503 "CLOCK_PROCESS_CPUTIME_ID: %s: timing disabled\n", strerror(errno));
1504 dp->value[OPTIND(dp,time)] = 0; /* i.e. off */
1505 }
1506}
1507
1508static void
1509start_timer(struct display *dp, int what)
1510{
1511 if ((dp->value[OPTIND(dp,time)] & what) != 0)
1512 set_timer(dp, what == PNGCP_TIME_READ ? &dp->read_time : &dp->write_time);
1513}
1514
1515static void
1516end_timer(struct display *dp, int what)
1517{
1518 if ((dp->value[OPTIND(dp,time)] & what) != 0)
1519 {
1520 struct timespec t, tmp;
1521
1522 set_timer(dp, &t);
1523
1524 if (what == PNGCP_TIME_READ)
1525 tmp = dp->read_time;
1526
1527 else
1528 tmp = dp->write_time;
1529
1530 t.tv_sec -= tmp.tv_sec;
1531 t.tv_nsec -= tmp.tv_nsec;
1532
1533 if (t.tv_nsec < 0)
1534 {
1535 --(t.tv_sec);
1536 t.tv_nsec += 1000000000L;
1537 }
1538
1539 if (what == PNGCP_TIME_READ)
1540 dp->read_time = t, tmp = dp->read_time_total;
1541
1542 else
1543 dp->write_time = t, tmp = dp->write_time_total;
1544
1545 tmp.tv_sec += t.tv_sec;
1546 tmp.tv_nsec += t.tv_nsec;
1547
1548 if (tmp.tv_nsec >= 1000000000L)
1549 {
1550 ++(tmp.tv_sec);
1551 tmp.tv_nsec -= 1000000000L;
1552 }
1553
1554 if (what == PNGCP_TIME_READ)
1555 dp->read_time_total = tmp;
1556
1557 else
1558 dp->write_time_total = tmp;
1559 }
1560}
1561
1562static void
1563print_time(const char *what, struct timespec t)
1564{
1565 printf("%s %.2lu.%.9ld", what, (unsigned long)t.tv_sec, t.tv_nsec);
1566}
1567#else /* !PNGCP_TIMING */
1568#define start_timer(dp, what) ((void)0)
1569#define end_timer(dp, what) ((void)0)
1570#endif /* !PNGCP_TIMING */
1571
1572/* The following is used in main to verify that the final argument is a
1573 * directory:
1574 */
1575static int
1576checkdir(const char *pathname)
1577{
1578 struct stat buf;
1579 return stat(pathname, &buf) == 0 && S_ISDIR(buf.st_mode);
1580}
1581
1582/* Work out whether a path is valid (if not a display_log occurs), a directory
1583 * (1 is returned) or a file *or* non-existent (0 is returned).
1584 *
1585 * Used for a write path.
1586 */
1587static int
1588isdir(struct display *dp, const char *pathname)
1589{
1590 if (pathname == NULL)
1591 return 0; /* stdout */
1592
1593 else if (pathname[0] == 0)
1594 return 1; /* empty string */
1595
1596 else
1597 {
1598 struct stat buf;
1599 int ret = stat(pathname, &buf);
1600
1601 if (ret == 0) /* the entry exists */
1602 {
1603 if (S_ISDIR(buf.st_mode))
1604 return 1;
1605
1606 /* Else expect an object that exists and can be written: */
1607 if (access(pathname, W_OK) != 0)
1608 display_log(dp, USER_ERROR, "%s: cannot be written (%s)", pathname,
1609 strerror(errno));
1610
1611 return 0; /* file (exists, can be written) */
1612 }
1613
1614 else /* an error */
1615 {
1616 /* Non-existence is fine, other errors are not: */
1617 if (errno != ENOENT)
1618 display_log(dp, USER_ERROR, "%s: invalid output name (%s)",
1619 pathname, strerror(errno));
1620
1621 return 0; /* file (does not exist) */
1622 }
1623 }
1624}
1625
1626static void
1627makename(struct display *dp, const char *dir, const char *infile)
1628{
1629 /* Make a name for an output file (and check it). */
1630 dp->namebuf[0] = 0;
1631
1632 if (dir == NULL || infile == NULL)
1633 display_log(dp, INTERNAL_ERROR, "NULL name to makename");
1634
1635 else
1636 {
1637 size_t dsize = strlen(dir);
1638
1639 if (dsize <= (sizeof dp->namebuf)-2) /* Allow for name + '/' + '\0' */
1640 {
1641 size_t isize = strlen(infile);
1642 size_t istart = isize-1;
1643
1644 /* This should fail before here: */
1645 if (infile[istart] == '/')
1646 display_log(dp, INTERNAL_ERROR, "infile with trailing /");
1647
1648 memcpy(dp->namebuf, dir, dsize);
1649 if (dsize > 0 && dp->namebuf[dsize-1] != '/')
1650 dp->namebuf[dsize++] = '/';
1651
1652 /* Find the rightmost non-/ character: */
1653 while (istart > 0 && infile[istart-1] != '/')
1654 --istart;
1655
1656 isize -= istart;
1657 infile += istart;
1658
1659 if (dsize+isize < (sizeof dp->namebuf)) /* dsize + infile + '\0' */
1660 {
1661 memcpy(dp->namebuf+dsize, infile, isize+1);
1662
1663 if (isdir(dp, dp->namebuf))
1664 display_log(dp, USER_ERROR, "%s: output file is a directory",
1665 dp->namebuf);
1666 }
1667
1668 else
1669 {
1670 dp->namebuf[dsize] = 0; /* allowed for: -2 at start */
1671 display_log(dp, USER_ERROR, "%s%s: output file name too long",
1672 dp->namebuf, infile);
1673 }
1674 }
1675
1676 else
1677 display_log(dp, USER_ERROR, "%s: output directory name too long", dir);
1678 }
1679}
1680
1681/* error handler callbacks for libpng */
1682static void PNGCBAPI
1683display_warning(png_structp pp, png_const_charp warning)
1684{
1685 struct display *dp = get_dp(pp);
1686
1687 /* This is used to prevent repeated warnings while searching */
1688 if (!dp->no_warnings)
1689 display_log(get_dp(pp), LIBPNG_WARNING, "%s", warning);
1690}
1691
1692static void PNGCBAPI
1693display_error(png_structp pp, png_const_charp error)
1694{
1695 struct display *dp = get_dp(pp);
1696
1697 display_log(dp, LIBPNG_ERROR, "%s", error);
1698}
1699
1700static void
1701display_start_read(struct display *dp, const char *filename)
1702{
1703 if (filename != NULL)
1704 {
1705 dp->filename = filename;
1706 dp->fp = fopen(filename, "rb");
1707 }
1708
1709 else
1710 {
1711 dp->filename = "<stdin>";
1712 dp->fp = stdin;
1713 }
1714
1715 dp->w = dp->h = 0U;
1716 dp->bpp = 0U;
1717 dp->size = 0U;
1718 dp->read_size = 0U;
1719
1720 if (dp->fp == NULL)
1721 display_log(dp, USER_ERROR, "file open failed (%s)", strerror(errno));
1722}
1723
1724static void PNGCBAPI
1725read_function(png_structp pp, png_bytep data, size_t size)
1726{
1727 struct display *dp = get_dp(pp);
1728
1729 if (size == 0U || fread(data, size, 1U, dp->fp) == 1U)
1730 dp->read_size += size;
1731
1732 else
1733 {
1734 if (feof(dp->fp))
1735 display_log(dp, LIBPNG_ERROR, "PNG file truncated");
1736 else
1737 display_log(dp, LIBPNG_ERROR, "PNG file read failed (%s)",
1738 strerror(errno));
1739 }
1740}
1741
1742static void
1743read_png(struct display *dp, const char *filename)
1744{
1745 /* This is an assumption of the code; it may happen if a previous write fails
1746 * and there is a bug in the cleanup handling below (look for setjmp).
1747 * Passing freeinfo==1 to display_clean_read below avoids a second error
1748 * on dp->ip != NULL below.
1749 */
1750 if (dp->read_pp != NULL)
1751 {
1752 display_log(dp, APP_FAIL, "unexpected png_read_struct");
1753 display_clean_read(dp, 1/*freeinfo*/); /* recovery */
1754 }
1755
1756 display_start_read(dp, filename);
1757
1758 dp->read_pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, dp,
1759 display_error, display_warning);
1760 if (dp->read_pp == NULL)
1761 display_log(dp, LIBPNG_ERROR, "failed to create read struct");
1762
1763# ifdef PNG_BENIGN_ERRORS_SUPPORTED
1764 png_set_benign_errors(dp->read_pp, 1/*allowed*/);
1765# endif /* BENIGN_ERRORS */
1766
1767# ifdef FIX_INDEX
1768 if ((dp->options & FIX_INDEX) != 0)
1769 png_set_check_for_invalid_index(dp->read_pp, 1/*on, no warning*/);
1770# ifdef IGNORE_INDEX
1771 else
1772# endif /* IGNORE_INDEX */
1773# endif /* FIX_INDEX */
1774# ifdef IGNORE_INDEX
1775 if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */
1776 png_set_check_for_invalid_index(dp->read_pp, -1/*off completely*/);
1777# endif /* IGNORE_INDEX */
1778
1779 if (dp->ip != NULL)
1780 {
1781 /* UNEXPECTED: some problem in the display_clean function calls! */
1782 display_log(dp, APP_FAIL, "read_png: freeing old info struct");
1783 png_destroy_info_struct(dp->read_pp, &dp->ip);
1784 }
1785
1786 /* The png_read_png API requires us to make the info struct, but it does the
1787 * call to png_read_info.
1788 */
1789 dp->ip = png_create_info_struct(dp->read_pp);
1790 if (dp->ip == NULL)
1791 png_error(dp->read_pp, "failed to create info struct");
1792
1793 /* Set the IO handling */
1794 png_set_read_fn(dp->read_pp, dp, read_function);
1795
1796# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
1797 png_set_keep_unknown_chunks(dp->read_pp, PNG_HANDLE_CHUNK_ALWAYS, NULL,
1798 0);
1799# endif /* HANDLE_AS_UNKNOWN */
1800
1801# ifdef PNG_SET_USER_LIMITS_SUPPORTED
1802 /* Remove the user limits, if any */
1803 png_set_user_limits(dp->read_pp, 0x7fffffff, 0x7fffffff);
1804# endif /* SET_USER_LIMITS */
1805
1806 /* Now read the PNG. */
1807 start_timer(dp, PNGCP_TIME_READ);
1808 png_read_png(dp->read_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
1809 end_timer(dp, PNGCP_TIME_READ);
1810 dp->w = png_get_image_width(dp->read_pp, dp->ip);
1811 dp->h = png_get_image_height(dp->read_pp, dp->ip);
1812 dp->ct = png_get_color_type(dp->read_pp, dp->ip);
1813 dp->bpp = png_get_bit_depth(dp->read_pp, dp->ip) *
1814 png_get_channels(dp->read_pp, dp->ip);
1815 {
1816 /* png_get_rowbytes should never return 0 because the value is set by the
1817 * first call to png_set_IHDR, which should have happened by now, but just
1818 * in case:
1819 */
1820 png_alloc_size_t rb = png_get_rowbytes(dp->read_pp, dp->ip);
1821
1822 if (rb == 0)
1823 png_error(dp->read_pp, "invalid row byte count from libpng");
1824
1825 /* The size calc can overflow. */
1826 if ((MAX_SIZE-dp->h)/rb < dp->h)
1827 png_error(dp->read_pp, "image too large");
1828
1829 dp->size = rb * dp->h + dp->h/*filter byte*/;
1830 }
1831
1832#ifdef FIX_INDEX
1833 if (dp->ct == PNG_COLOR_TYPE_PALETTE && (dp->options & FIX_INDEX) != 0)
1834 {
1835 int max = png_get_palette_max(dp->read_pp, dp->ip);
1836 png_colorp palette = NULL;
1837 int num = -1;
1838
1839 if (png_get_PLTE(dp->read_pp, dp->ip, &palette, &num) != PNG_INFO_PLTE
1840 || max < 0 || num <= 0 || palette == NULL)
1841 display_log(dp, LIBPNG_ERROR, "invalid png_get_PLTE result");
1842
1843 if (max >= num)
1844 {
1845 /* 'Fix' the palette. */
1846 int i;
1847 png_color newpal[256];
1848
1849 for (i=0; i<num; ++i)
1850 newpal[i] = palette[i];
1851
1852 /* Fill in any remainder with a warning color: */
1853 for (; i<=max; ++i)
1854 {
1855 newpal[i].red = 0xbe;
1856 newpal[i].green = 0xad;
1857 newpal[i].blue = 0xed;
1858 }
1859
1860 png_set_PLTE(dp->read_pp, dp->ip, newpal, i);
1861 }
1862 }
1863#endif /* FIX_INDEX */
1864
1865 /* NOTE: dp->ip is where all the information about the PNG that was just read
1866 * is stored. It can be used to write and write again a single PNG file,
1867 * however be aware that prior to libpng 1.7 text chunks could only be
1868 * written once; this is a bug which would require a significant code rewrite
1869 * to fix, it has been there in several versions of libpng (it was introduced
1870 * to fix another bug involving duplicate writes of the text chunks.)
1871 */
1872 display_clean_read(dp, 0/*freeiinfo*/);
1873 dp->operation = "none";
1874}
1875
1876static void
1877display_start_write(struct display *dp, const char *filename)
1878{
1879 assert(dp->fp == NULL);
1880
1881 if ((dp->options & NOWRITE) != 0)
1882 dp->output_file = "<no write>";
1883
1884 else
1885 {
1886 if (filename != NULL)
1887 {
1888 dp->output_file = filename;
1889 dp->fp = fopen(filename, "wb");
1890 }
1891
1892 else
1893 {
1894 dp->output_file = "<stdout>";
1895 dp->fp = stdout;
1896 }
1897
1898 if (dp->fp == NULL)
1899 display_log(dp, USER_ERROR, "%s: file open failed (%s)",
1900 dp->output_file, strerror(errno));
1901 }
1902}
1903
1904static void PNGCBAPI
1905write_function(png_structp pp, png_bytep data, size_t size)
1906{
1907 struct display *dp = get_dp(pp);
1908
1909 /* The write fail is classed as a USER_ERROR, so --quiet does not turn it
1910 * off, this seems more likely to be correct.
1911 */
1912 if (dp->fp == NULL || fwrite(data, size, 1U, dp->fp) == 1U)
1913 {
1914 dp->write_size += size;
1915 if (dp->write_size < size || dp->write_size == MAX_SIZE)
1916 png_error(pp, "IDAT size overflow");
1917 }
1918
1919 else
1920 display_log(dp, USER_ERROR, "%s: PNG file write failed (%s)",
1921 dp->output_file, strerror(errno));
1922}
1923
1924/* Compression option, 'method' is never set: there is no choice.
1925 *
1926 * IMPORTANT: the order of the entries in this macro determines the preference
1927 * order when two different combos of two of these options produce an IDAT of
1928 * the same size. The logic here is to put the things that affect the decoding
1929 * of the PNG image ahead of those that are relevant only to the encoding.
1930 */
1931#define SET_COMPRESSION\
1932 SET(strategy, strategy);\
1933 SET(windowBits, window_bits);\
1934 SET(level, level);\
1935 SET(memLevel, mem_level);
1936
1937#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
1938static void
1939search_compression(struct display *dp)
1940{
1941 /* Like set_compression below but use a more restricted search than 'all' */
1942 int val;
1943
1944# define SET(name, func) if (getsearchopts(dp, #name, &val))\
1945 png_set_compression_ ## func(dp->write_pp, val);
1946 SET_COMPRESSION
1947# undef SET
1948}
1949
1950static void
1951set_compression(struct display *dp)
1952{
1953 int val;
1954
1955# define SET(name, func) if (getallopts(dp, #name, &val))\
1956 png_set_compression_ ## func(dp->write_pp, val);
1957 SET_COMPRESSION
1958# undef SET
1959}
1960
1961#ifdef PNG_SW_COMPRESS_level /* 1.7.0+ */
1962static void
1963set_ICC_profile_compression(struct display *dp)
1964{
1965 int val;
1966
1967# define SET(name, func) if (getallopts(dp, "ICC-profile-" #name, &val))\
1968 png_set_ICC_profile_compression_ ## func(dp->write_pp, val);
1969 SET_COMPRESSION
1970# undef SET
1971}
1972#else
1973# define set_ICC_profile_compression(dp) ((void)0)
1974#endif
1975#else
1976# define search_compression(dp) ((void)0)
1977# define set_compression(dp) ((void)0)
1978# define set_ICC_profile_compression(dp) ((void)0)
1979#endif /* WRITE_CUSTOMIZE_COMPRESSION */
1980
1981#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
1982static void
1983set_text_compression(struct display *dp)
1984{
1985 int val;
1986
1987# define SET(name, func) if (getallopts(dp, "text-" #name, &val))\
1988 png_set_text_compression_ ## func(dp->write_pp, val);
1989 SET_COMPRESSION
1990# undef SET
1991}
1992#else
1993# define set_text_compression(dp) ((void)0)
1994#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */
1995
1996static void
1997write_png(struct display *dp, const char *destname)
1998{
1999 /* If this test fails png_write_png would fail *silently* below; this
2000 * is not helpful, so catch the problem now and give up:
2001 */
2002 if (dp->ip == NULL)
2003 display_log(dp, INTERNAL_ERROR, "missing png_info");
2004
2005 /* This is an assumption of the code; it may happen if a previous
2006 * write fails and there is a bug in the cleanup handling below.
2007 */
2008 if (dp->write_pp != NULL)
2009 {
2010 display_log(dp, APP_FAIL, "unexpected png_write_struct");
2011 display_clean_write(dp, 0/*!freeinfo*/);
2012 }
2013
2014 display_start_write(dp, destname);
2015
2016 dp->write_pp = png_create_write_struct(PNG_LIBPNG_VER_STRING, dp,
2017 display_error, display_warning);
2018
2019 if (dp->write_pp == NULL)
2020 display_log(dp, LIBPNG_ERROR, "failed to create write png_struct");
2021
2022# ifdef PNG_BENIGN_ERRORS_SUPPORTED
2023 png_set_benign_errors(dp->write_pp, 1/*allowed*/);
2024# endif /* BENIGN_ERRORS */
2025
2026 png_set_write_fn(dp->write_pp, dp, write_function, NULL/*flush*/);
2027
2028#ifdef IGNORE_INDEX
2029 if ((dp->options & IGNORE_INDEX) != 0) /* DANGEROUS */
2030 png_set_check_for_invalid_index(dp->write_pp, -1/*off completely*/);
2031#endif /* IGNORE_INDEX */
2032
2033 /* Restore the text chunks when using libpng 1.6 or less; this is a macro
2034 * which expands to nothing in 1.7+ In earlier versions it tests
2035 * dp->text_stashed, which is only set (below) *after* the first write.
2036 */
2037 text_restore(dp);
2038
2039# ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
2040 png_set_keep_unknown_chunks(dp->write_pp, PNG_HANDLE_CHUNK_ALWAYS, NULL,
2041 0);
2042# endif /* HANDLE_AS_UNKNOWN */
2043
2044# ifdef PNG_SET_USER_LIMITS_SUPPORTED
2045 /* Remove the user limits, if any */
2046 png_set_user_limits(dp->write_pp, 0x7fffffff, 0x7fffffff);
2047# endif
2048
2049 /* OPTION HANDLING */
2050 /* compression outputs, IDAT and zTXt/iTXt: */
2051 dp->tsp = dp->nsp;
2052 dp->nsp = dp->csp = 0;
2053# ifdef PNG_SW_COMPRESS_png_level
2054 {
2055 int val;
2056
2057 /* This sets everything, but then the following options just override
2058 * the specific settings for ICC profiles and text.
2059 */
2060 if (getallopts(dp, "compression", &val))
2061 png_set_compression(dp->write_pp, val);
2062
2063 if (getallopts(dp, "ICC-profile-compression", &val))
2064 png_set_ICC_profile_compression(dp->write_pp, val);
2065
2066 if (getallopts(dp, "text-compression", &val))
2067 png_set_text_compression(dp->write_pp, val);
2068 }
2069# endif /* png_level support */
2070 if (dp->options & SEARCH)
2071 search_compression(dp);
2072 else
2073 set_compression(dp);
2074 set_ICC_profile_compression(dp);
2075 set_text_compression(dp);
2076
2077 {
2078 int val;
2079
2080 /* The permitted range is 1..0x7FFFFFFF, so the cast is safe */
2081 if (get_option(dp, "IDAT-size", &val))
2082 png_set_IDAT_size(dp->write_pp, val);
2083 }
2084
2085 /* filter handling */
2086# ifdef PNG_WRITE_FILTER_SUPPORTED
2087 {
2088 int val;
2089
2090 if (get_option(dp, "filter", &val))
2091 png_set_filter(dp->write_pp, PNG_FILTER_TYPE_BASE, val);
2092 }
2093# endif /* WRITE_FILTER */
2094
2095 /* This just uses the 'read' info_struct directly, it contains the image. */
2096 dp->write_size = 0U;
2097 start_timer(dp, PNGCP_TIME_WRITE);
2098 png_write_png(dp->write_pp, dp->ip, 0U/*transforms*/, NULL/*params*/);
2099 end_timer(dp, PNGCP_TIME_WRITE);
2100
2101 /* Make sure the file was written ok: */
2102 if (dp->fp != NULL)
2103 {
2104 FILE *fp = dp->fp;
2105 dp->fp = NULL;
2106 if (fclose(fp))
2107 display_log(dp, APP_ERROR, "%s: write failed (%s)",
2108 destname == NULL ? "stdout" : destname, strerror(errno));
2109 }
2110
2111 dp->operation = "none";
2112}
2113
2114static void
2115set_windowBits_hi(struct display *dp)
2116{
2117 /* windowBits is in the range 8..15 but zlib maps '8' to '9' so it is only
2118 * worth using if the data size is 256 byte or less.
2119 */
2120 int wb = MAX_WBITS; /* for large images */
2121 int i = VLSIZE(windowBits_IDAT);
2122
2123 while (wb > 8 && dp->size <= 1U<<(wb-1)) --wb;
2124
2125 while (--i >= 0) if (VLNAME(windowBits_IDAT)[i].name == range_hi) break;
2126
2127 assert(i > 1); /* vl_windowBits_IDAT always has a RANGE() */
2128 VLNAME(windowBits_IDAT)[i].value = wb;
2129
2130 assert(VLNAME(windowBits_IDAT)[--i].name == range_lo);
2131 VLNAME(windowBits_IDAT)[i].value = wb > 8 ? 9 : 8;
2132
2133 /* If wb == 8 then any search has been restricted to just one windowBits
2134 * entry. Record that here to avoid producing a spurious app-level warning
2135 * above.
2136 */
2137 if (wb == 8)
2138 dp->min_windowBits = OPTIND(dp, windowBits);
2139}
2140
2141static int
2142better_options(const struct display *dp)
2143{
2144 /* Are these options better than the best found so far? Normally the
2145 * options are tested in preference order, best first, however when doing a
2146 * search operation on a range the range values are tested out of order. In
2147 * that case preferable options will get tested later.
2148 *
2149 * This function looks through the stack from the bottom up looking for an
2150 * option that does not match the current best value. When it finds one it
2151 * checks to see if it is more or less desirable and returns true or false
2152 * as appropriate.
2153 *
2154 * Notice that this means that the order options are pushed onto the stack
2155 * conveys a priority; lower/earlier options are more important than later
2156 * ones.
2157 */
2158 unsigned int sp;
2159
2160 for (sp=0; sp<dp->csp; ++sp)
2161 {
2162 int c = compare_option(dp, sp);
2163
2164 if (c < 0)
2165 return 0; /* worse */
2166
2167 else if (c > 0)
2168 return 1; /* better */
2169 }
2170
2171 assert(0 && "unreached");
2172}
2173
2174static void
2175print_search_results(struct display *dp)
2176{
2177 assert(dp->filename != NULL);
2178 printf("%s [%ld x %ld %d bpp %s, %lu bytes] %lu -> %lu with '%s'\n",
2179 dp->filename, (unsigned long)dp->w, (unsigned long)dp->h, dp->bpp,
2180 cts(dp->ct), (unsigned long)dp->size, (unsigned long)dp->read_size,
2181 (unsigned long)dp->best_size, dp->best);
2182 fflush(stdout);
2183}
2184
2185static void
2186log_search(struct display *dp, unsigned int log_depth)
2187{
2188 /* Log, and reset, the search so far: */
2189 if (dp->nsp/*next entry to change*/ <= log_depth)
2190 {
2191 print_search_results(dp);
2192 /* Start again with this entry: */
2193 dp->best_size = MAX_SIZE;
2194 }
2195}
2196
2197static void
2198cp_one_file(struct display *dp, const char *filename, const char *destname)
2199{
2200 unsigned int log_depth;
2201
2202 dp->filename = filename;
2203 dp->operation = "read";
2204 dp->no_warnings = 0;
2205
2206 /* Read it then write it: */
2207 if (filename != NULL && access(filename, R_OK) != 0)
2208 display_log(dp, USER_ERROR, "%s: invalid file name (%s)",
2209 filename, strerror(errno));
2210
2211 read_png(dp, filename);
2212
2213 /* But 'destname' may be a directory. */
2214 dp->operation = "write";
2215
2216 /* Limit the upper end of the windowBits range for this file */
2217 set_windowBits_hi(dp);
2218
2219 /* For logging, depth to log: */
2220 {
2221 int val;
2222
2223 if (get_option(dp, "log-depth", &val) && val >= 0)
2224 log_depth = (unsigned int)/*SAFE*/val;
2225
2226 else
2227 log_depth = 0U;
2228 }
2229
2230 if (destname != NULL) /* else stdout */
2231 {
2232 if (isdir(dp, destname))
2233 {
2234 makename(dp, destname, filename);
2235 destname = dp->namebuf;
2236 }
2237
2238 else if (access(destname, W_OK) != 0 && errno != ENOENT)
2239 display_log(dp, USER_ERROR, "%s: invalid output name (%s)", destname,
2240 strerror(errno));
2241 }
2242
2243 dp->nsp = 0;
2244 dp->curr[0] = 0; /* acts as a flag for the caller */
2245 dp->opt_string_start = 0;
2246 dp->best[0] = 0; /* safety */
2247 dp->best_size = MAX_SIZE;
2248 write_png(dp, destname);
2249
2250 /* Initialize the 'best' fields: */
2251 strcpy(dp->best, dp->curr);
2252 dp->best_size = dp->write_size;
2253
2254 if (dp->nsp > 0) /* iterating over lists */
2255 {
2256 char *tmpname, tmpbuf[(sizeof dp->namebuf) + 4];
2257 assert(dp->curr[0] == ' ' && dp->tsp > 0);
2258
2259 /* Cancel warnings on subsequent writes */
2260 log_search(dp, log_depth);
2261 dp->no_warnings = 1;
2262
2263 /* Make a temporary name for the subsequent tests: */
2264 if (destname != NULL)
2265 {
2266 strcpy(tmpbuf, destname);
2267 strcat(tmpbuf, ".tmp"); /* space for .tmp allocated above */
2268 tmpname = tmpbuf;
2269 }
2270
2271 else
2272 tmpname = NULL; /* stdout */
2273
2274 /* Loop to find the best option. */
2275 do
2276 {
2277 /* Clean before each write_png; this just removes *dp->write_pp which
2278 * cannot be reused.
2279 */
2280 display_clean_write(dp, 0/*!freeinfo*/);
2281 write_png(dp, tmpname);
2282
2283 /* And compare the sizes (the write function makes sure write_size
2284 * doesn't overflow.)
2285 */
2286 assert(dp->csp > 0);
2287
2288 if (dp->write_size < dp->best_size ||
2289 (dp->write_size == dp->best_size && better_options(dp)))
2290 {
2291 if (destname != NULL && rename(tmpname, destname) != 0)
2292 display_log(dp, APP_ERROR, "rename %s %s failed (%s)", tmpname,
2293 destname, strerror(errno));
2294
2295 strcpy(dp->best, dp->curr);
2296 dp->best_size = dp->write_size;
2297 }
2298
2299 else if (tmpname != NULL && unlink(tmpname) != 0)
2300 display_log(dp, APP_WARNING, "unlink %s failed (%s)", tmpname,
2301 strerror(errno));
2302
2303 log_search(dp, log_depth);
2304 }
2305 while (dp->nsp > 0);
2306
2307 /* Do this for the 'sizes' option so that it reports the correct size. */
2308 dp->write_size = dp->best_size;
2309 }
2310
2311 display_clean_write(dp, 1/*freeinfo*/);
2312}
2313
2314static int
2315cppng(struct display *dp, const char *file, const char *dest)
2316{
2317 if (setjmp(dp->error_return) == 0)
2318 {
2319 dp->errset = 1;
2320 cp_one_file(dp, file, dest);
2321 dp->errset = 0;
2322 return 0;
2323 }
2324
2325 else
2326 {
2327 dp->errset = 0;
2328
2329 if (dp->errlevel < ERRORS) /* shouldn't longjmp on warnings */
2330 display_log(dp, INTERNAL_ERROR, "unexpected return code %d",
2331 dp->errlevel);
2332
2333 return dp->errlevel;
2334 }
2335}
2336
2337int
2338main(int argc, char **argv)
2339{
2340 /* For each file on the command line test it with a range of transforms */
2341 int option_end;
2342 struct display d;
2343
2344 display_init(&d);
2345
2346 d.operation = "options";
2347 for (option_end = 1;
2348 option_end < argc && opt_check(&d, argv[option_end]);
2349 ++option_end)
2350 {
2351 }
2352
2353 /* Do a quick check on the directory target case; when there are more than
2354 * two arguments the last one must be a directory.
2355 */
2356 if (!(d.options & NOWRITE) && option_end+2 < argc && !checkdir(argv[argc-1]))
2357 {
2358 fprintf(stderr,
2359 "pngcp: %s: directory required with more than two arguments\n",
2360 argv[argc-1]);
2361 return 99;
2362 }
2363
2364 {
2365 int errors = 0;
2366 int i = option_end;
2367
2368 /* Do this at least once; if there are no arguments stdin/stdout are used.
2369 */
2370 d.operation = "files";
2371 do
2372 {
2373 const char *infile = NULL;
2374 const char *outfile = NULL;
2375 int ret;
2376
2377 if (i < argc)
2378 {
2379 infile = argv[i++];
2380 if (!(d.options & NOWRITE) && i < argc)
2381 outfile = argv[argc-1];
2382 }
2383
2384 ret = cppng(&d, infile, outfile);
2385
2386 if (ret)
2387 {
2388 if (ret > QUIET) /* abort on user or internal error */
2389 return 99;
2390
2391 /* An error: the output is meaningless */
2392 }
2393
2394 else if (d.best[0] != 0)
2395 {
2396 /* This result may already have been output, in which case best_size
2397 * has been reset.
2398 */
2399 if (d.best_size < MAX_SIZE)
2400 print_search_results(&d);
2401 }
2402
2403 else if (d.options & SIZES)
2404 {
2405 printf("%s [%ld x %ld %d bpp %s, %lu bytes] %lu -> %lu [0x%lx]\n",
2406 infile, (unsigned long)d.w, (unsigned long)d.h, d.bpp,
2407 cts(d.ct), (unsigned long)d.size, (unsigned long)d.read_size,
2408 (unsigned long)d.write_size, (unsigned long)d.results);
2409 fflush(stdout);
2410 }
2411
2412 /* Here on any return, including failures, except user/internal issues
2413 */
2414 {
2415 int pass = (d.options & STRICT) ?
2416 RESULT_STRICT(d.results) : RESULT_RELAXED(d.results);
2417
2418 if (!pass)
2419 ++errors;
2420
2421 if (d.options & LOG)
2422 {
2423 int j;
2424
2425 printf("%s: pngcp", pass ? "PASS" : "FAIL");
2426
2427 for (j=1; j<option_end; ++j)
2428 printf(" %s", argv[j]);
2429
2430 if (infile != NULL)
2431 printf(" %s", infile);
2432
2433# ifdef PNG_PNGCP_TIMING_SUPPORTED
2434 /* When logging output the files for each file, if enabled. */
2435 if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_READ) != 0)
2436 print_time(" read", d.read_time);
2437
2438 if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_WRITE) != 0)
2439 print_time(" write", d.write_time);
2440# endif /* PNGCP_TIMING */
2441
2442 printf("\n");
2443 fflush(stdout);
2444 }
2445 }
2446
2447 display_clean(&d);
2448 }
2449 while (i+!(d.options & NOWRITE) < argc);
2450 /* I.e. for write cases after the first time through the loop require
2451 * there to be at least two arguments left and for the last one to be a
2452 * directory (this was checked above).
2453 */
2454
2455 /* Release allocated memory */
2456 display_destroy(&d);
2457
2458# ifdef PNG_PNGCP_TIMING_SUPPORTED
2459 {
2460 int output = 0;
2461
2462 if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_READ) != 0)
2463 print_time("read", d.read_time_total), output = 1;
2464
2465 if ((d.value[OPTIND(&d,time)] & PNGCP_TIME_WRITE) != 0)
2466 {
2467 if (output) putchar(' ');
2468 print_time("write", d.write_time_total);
2469 output = 1;
2470 }
2471
2472 if (output) putchar('\n');
2473 }
2474# endif /* PNGCP_TIMING */
2475
2476 return errors != 0;
2477 }
2478}
2479#else /* !READ_PNG || !WRITE_PNG */
2480int
2481main(void)
2482{
2483 fprintf(stderr, "pngcp: no support for png_read/write_image\n");
2484 return 77;
2485}
2486#endif /* !READ_PNG || !WRITE_PNG */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette