Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 1 | /* timepng.c |
| 2 | * |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 3 | * Copyright (c) 2013,2016 John Cunningham Bowler |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 4 | * |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 5 | * Last changed in libpng 1.6.22 [May 26, 2016] |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 6 | * |
| 7 | * This code is released under the libpng license. |
| 8 | * For conditions of distribution and use, see the disclaimer |
| 9 | * and license in png.h |
| 10 | * |
| 11 | * Load an arbitrary number of PNG files (from the command line, or, if there |
| 12 | * are no arguments on the command line, from stdin) then run a time test by |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 13 | * reading each file by row or by image (possibly with transforms in the latter |
| 14 | * case). The only output is a time as a floating point number of seconds with |
| 15 | * 9 decimal digits. |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 16 | */ |
| 17 | #define _POSIX_C_SOURCE 199309L /* for clock_gettime */ |
| 18 | |
| 19 | #include <stdlib.h> |
| 20 | #include <stdio.h> |
| 21 | #include <string.h> |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 22 | #include <errno.h> |
| 23 | #include <limits.h> |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 24 | |
| 25 | #include <time.h> |
| 26 | |
| 27 | #if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H) |
| 28 | # include <config.h> |
| 29 | #endif |
| 30 | |
| 31 | /* Define the following to use this test against your installed libpng, rather |
| 32 | * than the one being built here: |
| 33 | */ |
| 34 | #ifdef PNG_FREESTANDING_TESTS |
| 35 | # include <png.h> |
| 36 | #else |
| 37 | # include "../../png.h" |
| 38 | #endif |
| 39 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 40 | /* The following is to support direct compilation of this file as C++ */ |
| 41 | #ifdef __cplusplus |
| 42 | # define voidcast(type, value) static_cast<type>(value) |
| 43 | #else |
| 44 | # define voidcast(type, value) (value) |
| 45 | #endif /* __cplusplus */ |
| 46 | |
| 47 | /* 'CLOCK_PROCESS_CPUTIME_ID' is one of the clock timers for clock_gettime. It |
| 48 | * need not be supported even when clock_gettime is available. It returns the |
| 49 | * 'CPU' time the process has consumed. 'CPU' time is assumed to include time |
| 50 | * when the CPU is actually blocked by a pending cache fill but not time |
| 51 | * waiting for page faults. The attempt is to get a measure of the actual time |
| 52 | * the implementation takes to read a PNG ignoring the potentially very large IO |
| 53 | * overhead. |
| 54 | */ |
| 55 | #if defined (CLOCK_PROCESS_CPUTIME_ID) && defined(PNG_STDIO_SUPPORTED) &&\ |
| 56 | defined(PNG_EASY_ACCESS_SUPPORTED) &&\ |
| 57 | (PNG_LIBPNG_VER >= 10700 ? defined(PNG_READ_PNG_SUPPORTED) :\ |
| 58 | defined (PNG_SEQUENTIAL_READ_SUPPORTED) &&\ |
| 59 | defined(PNG_INFO_IMAGE_SUPPORTED)) |
| 60 | |
| 61 | typedef struct |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 62 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 63 | FILE *input; |
| 64 | FILE *output; |
| 65 | } io_data; |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 66 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 67 | static PNG_CALLBACK(void, read_and_copy, |
| 68 | (png_structp png_ptr, png_bytep buffer, png_size_t cb)) |
| 69 | { |
| 70 | io_data *io = (io_data*)png_get_io_ptr(png_ptr); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 71 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 72 | if (fread(buffer, cb, 1, io->input) != 1) |
| 73 | png_error(png_ptr, strerror(errno)); |
| 74 | |
| 75 | if (fwrite(buffer, cb, 1, io->output) != 1) |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 76 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 77 | perror("temporary file"); |
| 78 | fprintf(stderr, "temporary file PNG write failed\n"); |
| 79 | exit(1); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 80 | } |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 81 | } |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 82 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 83 | static void read_by_row(png_structp png_ptr, png_infop info_ptr, |
| 84 | FILE *write_ptr, FILE *read_ptr) |
| 85 | { |
| 86 | /* These don't get freed on error, this is fine; the program immediately |
| 87 | * exits. |
| 88 | */ |
| 89 | png_bytep row = NULL, display = NULL; |
| 90 | io_data io_copy; |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 91 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 92 | if (write_ptr != NULL) |
| 93 | { |
| 94 | /* Set up for a copy to the temporary file: */ |
| 95 | io_copy.input = read_ptr; |
| 96 | io_copy.output = write_ptr; |
| 97 | png_set_read_fn(png_ptr, &io_copy, read_and_copy); |
| 98 | } |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 99 | |
| 100 | png_read_info(png_ptr, info_ptr); |
| 101 | |
| 102 | { |
| 103 | png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); |
| 104 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 105 | row = voidcast(png_bytep,malloc(rowbytes)); |
| 106 | display = voidcast(png_bytep,malloc(rowbytes)); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 107 | |
| 108 | if (row == NULL || display == NULL) |
| 109 | png_error(png_ptr, "OOM allocating row buffers"); |
| 110 | |
| 111 | { |
| 112 | png_uint_32 height = png_get_image_height(png_ptr, info_ptr); |
| 113 | int passes = png_set_interlace_handling(png_ptr); |
| 114 | int pass; |
| 115 | |
| 116 | png_start_read_image(png_ptr); |
| 117 | |
| 118 | for (pass = 0; pass < passes; ++pass) |
| 119 | { |
| 120 | png_uint_32 y = height; |
| 121 | |
| 122 | /* NOTE: this trashes the row each time; interlace handling won't |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 123 | * work, but this avoids memory thrashing for speed testing and is |
| 124 | * somewhat representative of an application that works row-by-row. |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 125 | */ |
| 126 | while (y-- > 0) |
| 127 | png_read_row(png_ptr, row, display); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | /* Make sure to read to the end of the file: */ |
| 133 | png_read_end(png_ptr, info_ptr); |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 134 | |
| 135 | /* Free this up: */ |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 136 | free(row); |
| 137 | free(display); |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | static PNG_CALLBACK(void, no_warnings, (png_structp png_ptr, |
| 141 | png_const_charp warning)) |
| 142 | { |
| 143 | (void)png_ptr; |
| 144 | (void)warning; |
| 145 | } |
| 146 | |
| 147 | static int read_png(FILE *fp, png_int_32 transforms, FILE *write_file) |
| 148 | { |
| 149 | png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0, |
| 150 | no_warnings); |
| 151 | png_infop info_ptr = NULL; |
| 152 | |
| 153 | if (png_ptr == NULL) |
| 154 | return 0; |
| 155 | |
| 156 | if (setjmp(png_jmpbuf(png_ptr))) |
| 157 | { |
| 158 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); |
| 159 | return 0; |
| 160 | } |
| 161 | |
| 162 | # ifdef PNG_BENIGN_ERRORS_SUPPORTED |
| 163 | png_set_benign_errors(png_ptr, 1/*allowed*/); |
| 164 | # endif |
| 165 | png_init_io(png_ptr, fp); |
| 166 | |
| 167 | info_ptr = png_create_info_struct(png_ptr); |
| 168 | |
| 169 | if (info_ptr == NULL) |
| 170 | png_error(png_ptr, "OOM allocating info structure"); |
| 171 | |
| 172 | if (transforms < 0) |
| 173 | read_by_row(png_ptr, info_ptr, write_file, fp); |
| 174 | |
| 175 | else |
| 176 | png_read_png(png_ptr, info_ptr, transforms, NULL/*params*/); |
| 177 | |
| 178 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 179 | return 1; |
| 180 | } |
| 181 | |
| 182 | static int mytime(struct timespec *t) |
| 183 | { |
| 184 | /* Do the timing using clock_gettime and the per-process timer. */ |
| 185 | if (!clock_gettime(CLOCK_PROCESS_CPUTIME_ID, t)) |
| 186 | return 1; |
| 187 | |
| 188 | perror("CLOCK_PROCESS_CPUTIME_ID"); |
| 189 | fprintf(stderr, "timepng: could not get the time\n"); |
| 190 | return 0; |
| 191 | } |
| 192 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 193 | static int perform_one_test(FILE *fp, int nfiles, png_int_32 transforms) |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 194 | { |
| 195 | int i; |
| 196 | struct timespec before, after; |
| 197 | |
| 198 | /* Clear out all errors: */ |
| 199 | rewind(fp); |
| 200 | |
| 201 | if (mytime(&before)) |
| 202 | { |
| 203 | for (i=0; i<nfiles; ++i) |
| 204 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 205 | if (read_png(fp, transforms, NULL/*write*/)) |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 206 | { |
| 207 | if (ferror(fp)) |
| 208 | { |
| 209 | perror("temporary file"); |
| 210 | fprintf(stderr, "file %d: error reading PNG data\n", i); |
| 211 | return 0; |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | else |
| 216 | { |
| 217 | perror("temporary file"); |
| 218 | fprintf(stderr, "file %d: error from libpng\n", i); |
| 219 | return 0; |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | else |
| 225 | return 0; |
| 226 | |
| 227 | if (mytime(&after)) |
| 228 | { |
| 229 | /* Work out the time difference and print it - this is the only output, |
| 230 | * so flush it immediately. |
| 231 | */ |
| 232 | unsigned long s = after.tv_sec - before.tv_sec; |
| 233 | long ns = after.tv_nsec - before.tv_nsec; |
| 234 | |
| 235 | if (ns < 0) |
| 236 | { |
| 237 | --s; |
| 238 | ns += 1000000000; |
| 239 | |
| 240 | if (ns < 0) |
| 241 | { |
| 242 | fprintf(stderr, "timepng: bad clock from kernel\n"); |
| 243 | return 0; |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | printf("%lu.%.9ld\n", s, ns); |
| 248 | fflush(stdout); |
| 249 | if (ferror(stdout)) |
| 250 | { |
| 251 | fprintf(stderr, "timepng: error writing output\n"); |
| 252 | return 0; |
| 253 | } |
| 254 | |
| 255 | /* Successful return */ |
| 256 | return 1; |
| 257 | } |
| 258 | |
| 259 | else |
| 260 | return 0; |
| 261 | } |
| 262 | |
| 263 | static int add_one_file(FILE *fp, char *name) |
| 264 | { |
| 265 | FILE *ip = fopen(name, "rb"); |
| 266 | |
| 267 | if (ip != NULL) |
| 268 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 269 | /* Read the file using libpng; this detects errors and also deals with |
| 270 | * files which contain data beyond the end of the file. |
| 271 | */ |
| 272 | int ok = 0; |
| 273 | fpos_t pos; |
| 274 | |
| 275 | if (fgetpos(fp, &pos)) |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 276 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 277 | /* Fatal error reading the start: */ |
| 278 | perror("temporary file"); |
| 279 | fprintf(stderr, "temporary file fgetpos error\n"); |
| 280 | exit(1); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 281 | } |
| 282 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 283 | if (read_png(ip, -1/*by row*/, fp/*output*/)) |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 284 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 285 | if (ferror(ip)) |
| 286 | { |
| 287 | perror(name); |
| 288 | fprintf(stderr, "%s: read error\n", name); |
| 289 | } |
| 290 | |
| 291 | else |
| 292 | ok = 1; /* read ok */ |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 293 | } |
| 294 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 295 | else |
| 296 | fprintf(stderr, "%s: file not added\n", name); |
| 297 | |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 298 | (void)fclose(ip); |
| 299 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 300 | /* An error in the output is fatal; exit immediately: */ |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 301 | if (ferror(fp)) |
| 302 | { |
| 303 | perror("temporary file"); |
| 304 | fprintf(stderr, "temporary file write error\n"); |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 305 | exit(1); |
| 306 | } |
| 307 | |
| 308 | if (ok) |
| 309 | return 1; |
| 310 | |
| 311 | /* Did not read the file successfully, simply rewind the temporary |
| 312 | * file. This must happen after the ferror check above to avoid clearing |
| 313 | * the error. |
| 314 | */ |
| 315 | if (fsetpos(fp, &pos)) |
| 316 | { |
| 317 | perror("temporary file"); |
| 318 | fprintf(stderr, "temporary file fsetpos error\n"); |
| 319 | exit(1); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 320 | } |
| 321 | } |
| 322 | |
| 323 | else |
| 324 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 325 | /* file open error: */ |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 326 | perror(name); |
| 327 | fprintf(stderr, "%s: open failed\n", name); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 328 | } |
| 329 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 330 | return 0; /* file not added */ |
| 331 | } |
| 332 | |
| 333 | static void |
| 334 | usage(FILE *fp) |
| 335 | { |
| 336 | if (fp != NULL) fclose(fp); |
| 337 | |
| 338 | fprintf(stderr, |
| 339 | "Usage:\n" |
| 340 | " timepng --assemble <assembly> {files}\n" |
| 341 | " Read the files into <assembly>, output the count. Options are ignored.\n" |
| 342 | " timepng --dissemble <assembly> <count> [options]\n" |
| 343 | " Time <count> files from <assembly>, additional files may not be given.\n" |
| 344 | " Otherwise:\n" |
| 345 | " Read the files into a temporary file and time the decode\n" |
| 346 | "Transforms:\n" |
| 347 | " --by-image: read by image with png_read_png\n" |
| 348 | " --<transform>: implies by-image, use PNG_TRANSFORM_<transform>\n" |
| 349 | " Otherwise: read by row using png_read_row (to a single row buffer)\n" |
| 350 | /* ISO C90 string length max 509 */);fprintf(stderr, |
| 351 | "{files}:\n" |
| 352 | " PNG files to copy into the assembly and time. Invalid files are skipped\n" |
| 353 | " with appropriate error messages. If no files are given the list of files\n" |
| 354 | " is read from stdin with each file name terminated by a newline\n" |
| 355 | "Output:\n" |
| 356 | " For --assemble the output is the name of the assembly file followed by the\n" |
| 357 | " count of the files it contains; the arguments for --dissemble. Otherwise\n" |
| 358 | " the output is the total decode time in seconds.\n"); |
| 359 | |
| 360 | exit(99); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 361 | } |
| 362 | |
| 363 | int main(int argc, char **argv) |
| 364 | { |
| 365 | int ok = 0; |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 366 | int err = 0; |
| 367 | int nfiles = 0; |
| 368 | int transforms = -1; /* by row */ |
| 369 | const char *assembly = NULL; |
| 370 | FILE *fp; |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 371 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 372 | if (argc > 2 && strcmp(argv[1], "--assemble") == 0) |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 373 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 374 | /* Just build the test file, argv[2] is the file name. */ |
| 375 | assembly = argv[2]; |
| 376 | fp = fopen(assembly, "wb"); |
| 377 | if (fp == NULL) |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 378 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 379 | perror(assembly); |
| 380 | fprintf(stderr, "timepng --assemble %s: could not open for write\n", |
| 381 | assembly); |
| 382 | usage(NULL); |
| 383 | } |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 384 | |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 385 | argv += 2; |
| 386 | argc -= 2; |
| 387 | } |
| 388 | |
| 389 | else if (argc > 3 && strcmp(argv[1], "--dissemble") == 0) |
| 390 | { |
| 391 | fp = fopen(argv[2], "rb"); |
| 392 | |
| 393 | if (fp == NULL) |
| 394 | { |
| 395 | perror(argv[2]); |
| 396 | fprintf(stderr, "timepng --dissemble %s: could not open for read\n", |
| 397 | argv[2]); |
| 398 | usage(NULL); |
| 399 | } |
| 400 | |
| 401 | nfiles = atoi(argv[3]); |
| 402 | if (nfiles <= 0) |
| 403 | { |
| 404 | fprintf(stderr, |
| 405 | "timepng --dissemble <file> <count>: %s is not a count\n", |
| 406 | argv[3]); |
| 407 | exit(99); |
| 408 | } |
| 409 | #ifdef __COVERITY__ |
| 410 | else |
| 411 | { |
| 412 | nfiles &= PNG_UINT_31_MAX; |
| 413 | } |
| 414 | #endif |
| 415 | |
| 416 | argv += 3; |
| 417 | argc -= 3; |
| 418 | } |
| 419 | |
| 420 | else /* Else use a temporary file */ |
| 421 | { |
| 422 | #ifndef __COVERITY__ |
| 423 | fp = tmpfile(); |
| 424 | #else |
| 425 | /* Experimental. Coverity says tmpfile() is insecure because it |
| 426 | * generates predictable names. |
| 427 | * |
| 428 | * It is possible to satisfy Coverity by using mkstemp(); however, |
| 429 | * any platform supporting mkstemp() undoubtedly has a secure tmpfile() |
| 430 | * implementation as well, and doesn't need the fix. Note that |
| 431 | * the fix won't work on platforms that don't support mkstemp(). |
| 432 | * |
| 433 | * https://www.securecoding.cert.org/confluence/display/c/ |
| 434 | * FIO21-C.+Do+not+create+temporary+files+in+shared+directories |
| 435 | * says that most historic implementations of tmpfile() provide |
| 436 | * only a limited number of possible temporary file names |
| 437 | * (usually 26) before file names are recycled. That article also |
| 438 | * provides a secure solution that unfortunately depends upon mkstemp(). |
| 439 | */ |
| 440 | char tmpfile[] = "timepng-XXXXXX"; |
| 441 | int filedes; |
| 442 | umask(0177); |
| 443 | filedes = mkstemp(tmpfile); |
| 444 | if (filedes < 0) |
| 445 | fp = NULL; |
| 446 | else |
| 447 | { |
| 448 | fp = fdopen(filedes,"w+"); |
| 449 | /* Hide the filename immediately and ensure that the file does |
| 450 | * not exist after the program ends |
| 451 | */ |
| 452 | (void) unlink(tmpfile); |
| 453 | } |
| 454 | #endif |
| 455 | |
| 456 | if (fp == NULL) |
| 457 | { |
| 458 | perror("tmpfile"); |
| 459 | fprintf(stderr, "timepng: could not open the temporary file\n"); |
| 460 | exit(1); /* not a user error */ |
| 461 | } |
| 462 | } |
| 463 | |
| 464 | /* Handle the transforms: */ |
| 465 | while (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-') |
| 466 | { |
| 467 | const char *opt = *++argv + 2; |
| 468 | |
| 469 | --argc; |
| 470 | |
| 471 | /* Transforms turn on the by-image processing and maybe set some |
| 472 | * transforms: |
| 473 | */ |
| 474 | if (transforms == -1) |
| 475 | transforms = PNG_TRANSFORM_IDENTITY; |
| 476 | |
| 477 | if (strcmp(opt, "by-image") == 0) |
| 478 | { |
| 479 | /* handled above */ |
| 480 | } |
| 481 | |
| 482 | # define OPT(name) else if (strcmp(opt, #name) == 0)\ |
| 483 | transforms |= PNG_TRANSFORM_ ## name |
| 484 | |
| 485 | OPT(STRIP_16); |
| 486 | OPT(STRIP_ALPHA); |
| 487 | OPT(PACKING); |
| 488 | OPT(PACKSWAP); |
| 489 | OPT(EXPAND); |
| 490 | OPT(INVERT_MONO); |
| 491 | OPT(SHIFT); |
| 492 | OPT(BGR); |
| 493 | OPT(SWAP_ALPHA); |
| 494 | OPT(SWAP_ENDIAN); |
| 495 | OPT(INVERT_ALPHA); |
| 496 | OPT(STRIP_FILLER); |
| 497 | OPT(STRIP_FILLER_BEFORE); |
| 498 | OPT(STRIP_FILLER_AFTER); |
| 499 | OPT(GRAY_TO_RGB); |
| 500 | OPT(EXPAND_16); |
| 501 | OPT(SCALE_16); |
| 502 | |
| 503 | else |
| 504 | { |
| 505 | fprintf(stderr, "timepng %s: unrecognized transform\n", opt); |
| 506 | usage(fp); |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | /* Handle the files: */ |
| 511 | if (argc > 1 && nfiles > 0) |
| 512 | usage(fp); /* Additional files not valid with --dissemble */ |
| 513 | |
| 514 | else if (argc > 1) |
| 515 | { |
| 516 | int i; |
| 517 | |
| 518 | for (i=1; i<argc; ++i) |
| 519 | { |
| 520 | if (nfiles == INT_MAX) |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 521 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 522 | fprintf(stderr, "%s: skipped, too many files\n", argv[i]); |
| 523 | break; |
| 524 | } |
| 525 | |
| 526 | else if (add_one_file(fp, argv[i])) |
| 527 | ++nfiles; |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | else if (nfiles == 0) /* Read from stdin withoout --dissemble */ |
| 532 | { |
| 533 | char filename[FILENAME_MAX+1]; |
| 534 | |
| 535 | while (fgets(filename, FILENAME_MAX+1, stdin)) |
| 536 | { |
| 537 | size_t len = strlen(filename); |
| 538 | |
| 539 | if (filename[len-1] == '\n') |
| 540 | { |
| 541 | filename[len-1] = 0; |
| 542 | if (nfiles == INT_MAX) |
| 543 | { |
| 544 | fprintf(stderr, "%s: skipped, too many files\n", filename); |
| 545 | break; |
| 546 | } |
| 547 | |
| 548 | else if (add_one_file(fp, filename)) |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 549 | ++nfiles; |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 550 | } |
| 551 | |
| 552 | else |
| 553 | { |
| 554 | fprintf(stderr, "timepng: file name too long: ...%s\n", |
| 555 | filename+len-32); |
| 556 | err = 1; |
| 557 | break; |
| 558 | } |
| 559 | } |
| 560 | |
| 561 | if (ferror(stdin)) |
| 562 | { |
| 563 | fprintf(stderr, "timepng: stdin: read error\n"); |
| 564 | err = 1; |
| 565 | } |
| 566 | } |
| 567 | |
| 568 | /* Perform the test, or produce the --assemble output: */ |
| 569 | if (!err) |
| 570 | { |
| 571 | if (nfiles > 0) |
| 572 | { |
| 573 | if (assembly != NULL) |
| 574 | { |
| 575 | if (fflush(fp) && !ferror(fp) && fclose(fp)) |
| 576 | { |
| 577 | perror(assembly); |
| 578 | fprintf(stderr, "%s: close failed\n", assembly); |
| 579 | } |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 580 | |
| 581 | else |
| 582 | { |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 583 | printf("%s %d\n", assembly, nfiles); |
| 584 | fflush(stdout); |
| 585 | ok = !ferror(stdout); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 586 | } |
| 587 | } |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 588 | |
| 589 | else |
| 590 | { |
| 591 | ok = perform_one_test(fp, nfiles, transforms); |
| 592 | (void)fclose(fp); |
| 593 | } |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 594 | } |
| 595 | |
| 596 | else |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 597 | usage(fp); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 598 | } |
| 599 | |
| 600 | else |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 601 | (void)fclose(fp); |
Chris Craik | ca2bf81 | 2013-07-29 15:28:30 -0700 | [diff] [blame] | 602 | |
| 603 | /* Exit code 0 on success. */ |
| 604 | return ok == 0; |
| 605 | } |
Alex Naidis | 7a055fd | 2016-10-01 12:23:07 +0200 | [diff] [blame] | 606 | #else /* !sufficient support */ |
| 607 | int main(void) { return 77; } |
| 608 | #endif /* !sufficient support */ |