Coverage Report

Created: 2026-01-13 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jpegoptim/jpegoptim.c
Line
Count
Source
1
/*******************************************************************
2
 * JPEGoptim
3
 * Copyright (c) Timo Kokkonen, 1996-2025.
4
 * All Rights Reserved.
5
 *
6
 * requires libjpeg (Independent JPEG Group's JPEG software
7
 *                     release 6a or later...)
8
 *
9
 * SPDX-License-Identifier: GPL-3.0-or-later
10
 *
11
 * This file is part of JPEGoptim.
12
 *
13
 * JPEGoptim is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation, either version 3 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * JPEGoptim is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with JPEGoptim. If not, see <https://www.gnu.org/licenses/>.
25
 */
26
27
#ifdef HAVE_CONFIG_H
28
#include "config.h"
29
#endif
30
#include <stdio.h>
31
#include <stdlib.h>
32
#ifdef HAVE_UNISTD_H
33
#include <unistd.h>
34
#endif
35
#if HAVE_DIRENT_H
36
#include <dirent.h>
37
#endif
38
#if HAVE_FCNTL_H
39
#include <fcntl.h>
40
#endif
41
#if HAVE_SYS_STAT_H
42
#include <sys/stat.h>
43
#endif
44
#if HAVE_SYS_TYPES_H
45
#include <sys/types.h>
46
#endif
47
#if HAVE_SYS_WAIT_H
48
#include <sys/wait.h>
49
#endif
50
#if HAVE_GETOPT_H && HAVE_GETOPT_LONG
51
#include <getopt.h>
52
#else
53
#include "getopt.h"
54
#endif
55
#include <signal.h>
56
#include <string.h>
57
#include <jpeglib.h>
58
#include <jerror.h>
59
#include <setjmp.h>
60
#include <time.h>
61
#include <math.h>
62
63
#include "jpegmarker.h"
64
#include "jpegoptim.h"
65
66
67
0
#define VERSION "1.5.7beta"
68
0
#define COPYRIGHT  "Copyright (C) 1996-2025, Timo Kokkonen"
69
70
#if HAVE_WAIT && HAVE_FORK
71
#define PARALLEL_PROCESSING 1
72
0
#define MAX_WORKERS 256
73
#endif
74
75
1.73k
#define IN_BUF_SIZE (256 * 1024)
76
77
78
struct my_error_mgr {
79
  struct jpeg_error_mgr pub;
80
  jmp_buf setjmp_buffer;
81
  int     jump_set;
82
};
83
typedef struct my_error_mgr * my_error_ptr;
84
85
86
#ifdef PARALLEL_PROCESSING
87
struct worker {
88
  pid_t pid;
89
  int   read_pipe;
90
};
91
struct worker *workers;
92
int worker_count = 0;
93
#endif
94
95
96
int verbose_mode = 0;
97
int quiet_mode = 0;
98
int preserve_mode = 0;
99
int preserve_perms = 0;
100
int overwrite_mode = 0;
101
int retry_mode = 0;
102
int totals_mode = 0;
103
int stdin_mode = 0;
104
int stdout_mode = 0;
105
int noaction = 0;
106
int quality = -1;
107
int dest = 0;
108
int force = 0;
109
int save_extra = 0;
110
int save_exif = 1;
111
int save_iptc = 1;
112
int save_com = 1;
113
int save_icc = 1;
114
int save_xmp = 1;
115
int save_adobe = 0;
116
int save_jfxx = 0;
117
int save_jfif = 1;
118
int strip_none = 0;
119
double threshold = -1.0;
120
int csv = 0;
121
int auto_mode = 0;
122
int all_normal = 0;
123
int all_progressive = 0;
124
int target_size = 0;
125
#ifdef HAVE_ARITH_CODE
126
int arith_mode = -1;
127
#endif
128
int max_workers = 1;
129
int nofix_mode = 0;
130
int files_stdin = 0;
131
FILE *files_from = NULL;
132
133
int compress_err_count = 0;
134
int decompress_err_count = 0;
135
int global_error_counter = 0;
136
char last_error[JMSG_LENGTH_MAX+1];
137
FILE *jpeg_log_fh;
138
long average_count = 0;
139
double average_rate = 0.0;
140
double total_save = 0.0;
141
142
const struct option long_options[] = {
143
#ifdef HAVE_ARITH_CODE
144
  { "all-arith",          0, &arith_mode,          1 },
145
  { "all-huffman",        0, &arith_mode,          0 },
146
#endif
147
  { "auto-mode",          0, &auto_mode,          1 },
148
  { "all-normal",         0, &all_normal,          1 },
149
  { "all-progressive",    0, &all_progressive,     1 },
150
  { "csv",                0, 0,                    'b' },
151
  { "dest",               1, 0,                    'd' },
152
  { "files-stdin",        0, &files_stdin,         1 },
153
  { "files-from",         1, 0,                    'F' },
154
  { "force",              0, 0,                    'f' },
155
  { "help",               0, 0,                    'h' },
156
  { "keep-adobe",         0, &save_adobe,          1 },
157
  { "keep-all",           0, &strip_none,          1 },
158
  { "keep-com",           0, &save_com,            1 },
159
  { "keep-exif",          0, &save_exif,           1 },
160
  { "keep-iptc",          0, &save_iptc,           1 },
161
  { "keep-icc",           0, &save_icc,            1 },
162
  { "keep-jfif",          0, &save_jfif,           1 },
163
  { "keep-jfxx",          0, &save_jfxx,           1 },
164
  { "keep-xmp",           0, &save_xmp,            1 },
165
  { "max",                1, 0,                    'm' },
166
  { "noaction",           0, 0,                    'n' },
167
  { "nofix",              0, &nofix_mode,          1 },
168
  { "overwrite",          0, 0,                    'o' },
169
  { "preserve",           0, 0,                    'p' },
170
  { "preserve-perms",     0, 0,                    'P' },
171
  { "quiet",              0, 0,                    'q' },
172
  { "retry",              0, &retry_mode,          'r' },
173
  { "save-extra",         0, &save_extra,          1 },
174
  { "size",               1, 0,                    'S' },
175
  { "stdin",              0, &stdin_mode,          1 },
176
  { "stdout",             0, &stdout_mode,         1 },
177
  { "strip-all",          0, 0,                    's' },
178
  { "strip-none",         0, &strip_none,          1 },
179
  { "strip-com",          0, &save_com,            0 },
180
  { "strip-exif",         0, &save_exif,           0 },
181
  { "strip-iptc",         0, &save_iptc,           0 },
182
  { "strip-icc",          0, &save_icc,            0 },
183
  { "strip-xmp",          0, &save_xmp,            0 },
184
  { "strip-jfif",         0, &save_jfif,           0 },
185
  { "strip-jfxx",         0, &save_jfxx,           0 },
186
  { "strip-adobe",        0, &save_adobe,          0 },
187
  { "threshold",          1, 0,                    'T' },
188
  { "totals",             0, 0,                    't' },
189
  { "verbose",            0, 0,                    'v' },
190
  { "version",            0, 0,                    'V' },
191
#ifdef PARALLEL_PROCESSING
192
  { "workers",            1, &max_workers,         'w' },
193
#endif
194
  { 0, 0, 0, 0 }
195
};
196
197
198
/*****************************************************************/
199
200
201
void free_line_buf(JSAMPARRAY *buf, unsigned int lines)
202
532
{
203
532
  if (*buf == NULL)
204
239
    return;
205
206
861k
  for (unsigned int i = 0; i < lines; i++) {
207
861k
    if ((*buf)[i])
208
861k
      free((*buf)[i]);
209
861k
  }
210
293
  free(*buf);
211
293
  *buf = NULL;
212
293
}
213
214
215
METHODDEF(void) my_error_exit (j_common_ptr cinfo)
216
173
{
217
173
  my_error_ptr myerr = (my_error_ptr)cinfo->err;
218
219
173
  (*cinfo->err->output_message)(cinfo);
220
173
  if (myerr->jump_set)
221
173
    longjmp(myerr->setjmp_buffer, 1);
222
0
  else
223
0
    fatal("fatal error");
224
173
}
225
226
227
METHODDEF(void) my_output_message (j_common_ptr cinfo)
228
690
{
229
690
  char buffer[JMSG_LENGTH_MAX+1];
230
231
690
  (*cinfo->err->format_message)((j_common_ptr)cinfo, buffer);
232
690
  buffer[sizeof(buffer) - 1] = 0;
233
234
690
  if (verbose_mode)
235
0
    fprintf(jpeg_log_fh, " (%s) ", buffer);
236
237
690
  global_error_counter++;
238
690
  strncopy(last_error, buffer, sizeof(last_error));
239
690
}
240
241
242
void print_usage(void)
243
0
{
244
0
  fprintf(stdout,PROGRAMNAME " v" VERSION "  " COPYRIGHT "\n");
245
246
0
  fprintf(stdout,
247
0
    "Usage: " PROGRAMNAME " [options] <filenames> \n\n"
248
0
    "  -d<path>, --dest=<path>\n"
249
0
    "                    specify alternative destination directory for \n"
250
0
    "                    optimized files (default is to overwrite originals)\n"
251
0
    "  -f, --force       force optimization\n"
252
0
    "  -h, --help        display this help and exit\n"
253
0
    "  -m<quality>, --max=<quality>\n"
254
0
    "                    set maximum image quality factor (disables lossless\n"
255
0
    "                    optimization mode, which is by default on)\n"
256
0
    "                    Valid quality values: 0 - 100\n"
257
0
    "  -n, --noaction    don't really optimize files, just print results\n"
258
0
    "  -S<size>, --size=<size>\n"
259
0
    "                    Try to optimize file to given size (disables lossless\n"
260
0
    "                    optimization mode). Target size is specified either in\n"
261
0
    "                    kilo bytes (1 - n) or as percentage (1%% - 99%%)\n"
262
0
    "  -T<threshold>, --threshold=<threshold>\n"
263
0
    "                    keep old file if the gain is below a threshold (%%)\n"
264
0
#ifdef PARALLEL_PROCESSING
265
0
    "  -w<max>, --workers=<max>\n"
266
0
    "                    set maximum number of parallel threads (default is 1)\n"
267
0
#endif
268
0
    "  -b, --csv         print progress info in CSV format\n"
269
0
    "  -o, --overwrite   overwrite target file even if it exists (meaningful\n"
270
0
    "                    only when used with -d, --dest option)\n"
271
0
    "  -p, --preserve    preserve file timestamps\n"
272
0
    "  -P, --preserve-perms\n"
273
0
    "                    preserve original file permissions by overwriting it\n"
274
0
    "  -q, --quiet       quiet mode\n"
275
0
    "  -r, --retry       try (recursively) optimize until file size does not change anymore\n"
276
0
    "  -t, --totals      print totals after processing all files\n"
277
0
    "  -v, --verbose     enable verbose mode (positively chatty)\n"
278
0
    "  -V, --version     print program version\n\n"
279
0
    "  -s, --strip-all   strip all markers from output file\n"
280
0
    "  --strip-none      do not strip any markers\n"
281
0
    "  --strip-adobe     strip Adobe (APP14) markers from output file\n"
282
0
    "  --strip-com       strip Comment markers from output file\n"
283
0
    "  --strip-exif      strip Exif markers from output file\n"
284
0
    "  --strip-iptc      strip IPTC/Photoshop (APP13) markers from output file\n"
285
0
    "  --strip-icc       strip ICC profile markers from output file\n"
286
0
    "  --strip-jfif      strip JFIF markers from output file\n"
287
0
    "  --strip-jfxx      strip JFXX (JFIF Extension) markers from output file\n"
288
0
    "  --strip-xmp       strip XMP markers markers from output file\n"
289
0
    "\n"
290
0
    "  --keep-all        do not strip any markers (same as --strip-none)\n"
291
0
    "  --keep-adobe      preserve any Adobe (APP14) markers\n"
292
0
    "  --keep-com        preserve any Comment markers\n"
293
0
    "  --keep-exif       preserve any Exif markers\n"
294
0
    "  --keep-iptc       preserve any IPTC/Photoshop (APP13) markers\n"
295
0
    "  --keep-icc        preserve any ICC profile markers\n"
296
0
    "  --keep-jfif       preserve any JFIF markers\n"
297
0
    "  --keep-jfxx       preserve any JFXX (JFIF Extension) markers\n"
298
0
    "  --keep-xmp        preserve any XMP markers markers\n"
299
0
    "\n"
300
0
    "  --all-normal      force all output files to be non-progressive\n"
301
0
    "  --all-progressive force all output files to be progressive\n"
302
0
    "  --auto-mode       select normal or progressive based on which produces\n"
303
0
    "                    smaller output file\n"
304
0
#ifdef HAVE_ARITH_CODE
305
0
    "  --all-arith       force all output files to use arithmetic coding\n"
306
0
    "  --all-huffman     force all output files to use Huffman coding\n"
307
0
#endif
308
0
    "  --stdout          send output to standard output (instead of a file)\n"
309
0
    "  --stdin           read input from standard input (instead of a file)\n"
310
0
    "  --files-stdin     Read names of files to process from stdin\n"
311
0
    "  --files-from=FILE Read names of files to process from a file\n"
312
0
    "  --nofix           skip processing of input files if they contain any errors\n"
313
0
    "  --save-extra      preserve extraneous data after the end of image\n"
314
0
    "\n\n");
315
0
}
316
317
318
void print_version()
319
0
{
320
0
  struct jpeg_error_mgr jerr;
321
322
0
#ifdef  __DATE__
323
0
  printf(PROGRAMNAME " v%s  %s (%s)\n",VERSION, HOST_TYPE, __DATE__);
324
#else
325
  printf(PROGRAMNAME " v%s  %s\n", VERSION, HOST_TYPE);
326
#endif
327
0
  printf(COPYRIGHT "\n\n");
328
0
  printf("This program comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
329
0
    "and you are welcome to redistribute it under certain conditions.\n"
330
0
    "See the GNU General Public License for more details.\n\n");
331
332
0
  if (!jpeg_std_error(&jerr))
333
0
    fatal("jpeg_std_error() failed");
334
335
0
  printf("\nlibjpeg version: %s\n%s\n",
336
0
    jerr.jpeg_message_table[JMSG_VERSION],
337
0
    jerr.jpeg_message_table[JMSG_COPYRIGHT]);
338
0
}
339
340
341
void parse_arguments(int argc, char **argv, char *dest_path, size_t dest_path_len)
342
0
{
343
0
  int opt_index;
344
0
  int i, c;
345
346
0
  while(1) {
347
0
    opt_index=0;
348
0
    if ((c = getopt_long(argc,argv,"d:hm:nstqvfVpProT:S:bw:",
349
0
            long_options, &opt_index)) == -1)
350
0
      break;
351
352
0
    switch (c) {
353
354
0
    case 'm':
355
0
            {
356
0
        int tmpvar;
357
358
0
        if (sscanf(optarg,"%d",&tmpvar) == 1) {
359
0
          quality=tmpvar;
360
0
          if (quality < 0) quality=0;
361
0
          if (quality > 100) quality=100;
362
0
        }
363
0
        else
364
0
          fatal("invalid argument for -m, --max");
365
0
      }
366
0
      break;
367
368
0
    case 'd':
369
0
      if (realpath(optarg,dest_path)==NULL)
370
0
        fatal("invalid destination directory: %s", optarg);
371
0
      if (!is_directory(dest_path))
372
0
        fatal("destination not a directory: %s", dest_path);
373
0
      strncatenate(dest_path, DIR_SEPARATOR_S, dest_path_len);
374
0
      if (verbose_mode)
375
0
        fprintf(stderr,"Destination directory: %s\n",dest_path);
376
0
      dest=1;
377
0
      break;
378
379
0
    case 'v':
380
0
      verbose_mode++;
381
0
      break;
382
383
0
    case 'h':
384
0
      print_usage();
385
0
      exit(0);
386
0
      break;
387
388
0
    case 'q':
389
0
      quiet_mode=1;
390
0
      break;
391
392
0
    case 'r':
393
0
      retry_mode=1;
394
0
      break;
395
396
0
    case 't':
397
0
      totals_mode=1;
398
0
      break;
399
400
0
    case 'n':
401
0
      noaction=1;
402
0
      break;
403
404
0
    case 'f':
405
0
      force=1;
406
0
      break;
407
408
0
    case 'b':
409
0
      csv=1;
410
0
      quiet_mode=1;
411
0
      break;
412
413
0
    case 'V':
414
0
      print_version();
415
0
      exit(0);
416
0
      break;
417
418
0
    case 'o':
419
0
      overwrite_mode=1;
420
0
      break;
421
422
0
    case 'p':
423
0
      preserve_mode=1;
424
0
      break;
425
426
0
    case 'P':
427
0
      preserve_perms=1;
428
0
      break;
429
430
0
    case 's':
431
0
      save_exif=0;
432
0
      save_iptc=0;
433
0
      save_com=0;
434
0
      save_icc=0;
435
0
      save_xmp=0;
436
0
      save_adobe=0;
437
0
      save_jfif=0;
438
0
      save_jfxx=0;
439
0
      break;
440
441
0
    case 'T':
442
0
            {
443
0
        double tmpvar;
444
0
        if (sscanf(optarg,"%lf", &tmpvar) == 1) {
445
0
          threshold=tmpvar;
446
0
          if (threshold < 0) threshold=0;
447
0
          if (threshold > 100) threshold=100;
448
0
        }
449
0
        else fatal("invalid argument for -T, --threshold");
450
0
      }
451
0
      break;
452
453
0
    case 'S':
454
0
            {
455
0
        unsigned int tmpvar;
456
0
        if (sscanf(optarg,"%u", &tmpvar) == 1) {
457
0
          if (tmpvar > 0 && tmpvar < 100 &&
458
0
            optarg[strlen(optarg)-1] == '%' ) {
459
0
            target_size=-tmpvar;
460
0
          } else {
461
0
            target_size=tmpvar;
462
0
          }
463
0
          quality=100;
464
0
        }
465
0
        else fatal("invalid argument for -S, --size");
466
0
      }
467
0
      break;
468
469
0
#ifdef PARALLEL_PROCESSING
470
0
    case 'w':
471
0
            {
472
0
        int tmpvar;
473
0
        if (sscanf(optarg, "%d", &tmpvar) == 1) {
474
0
          if (tmpvar > 0 && tmpvar <= MAX_WORKERS)
475
0
            max_workers = tmpvar;
476
0
        }
477
0
        else fatal("invalid argument for -w, --workers");
478
0
      }
479
0
      break;
480
0
#endif
481
482
0
    case 'F':
483
0
            {
484
0
        if (optarg[0] == '-' && optarg[1] == 0) {
485
0
          files_stdin = 1;
486
0
          break;
487
0
        }
488
0
        if (!is_file(optarg, NULL))
489
0
          fatal("argument for --files-from must be a file");
490
0
        if ((files_from = fopen(optarg, "r")) == NULL)
491
0
          fatal("cannot open file: '%s'", optarg);
492
0
      }
493
0
      break;
494
495
0
    case '?':
496
0
      exit(1);
497
498
0
    }
499
0
  }
500
501
502
  /* check for '-' option indicating input is from stdin... */
503
0
  i = optind;
504
0
  while (argv[i]) {
505
0
    if (argv[i][0]=='-' && argv[i][1]==0)
506
0
      stdin_mode=1;
507
0
    i++;
508
0
  }
509
510
0
  if (stdin_mode)
511
0
    stdout_mode=1;
512
0
  if (files_stdin)
513
0
    files_from = stdin;
514
0
  if (stdin_mode && files_from == stdin)
515
0
    fatal("cannot specify both --stdin and --files-stdin");
516
0
  if (all_normal && all_progressive)
517
0
    fatal("cannot specify both --all-normal and --all-progressive");
518
0
  if (auto_mode && (all_normal || all_progressive))
519
0
    fatal("cannot specify --all-normal or --all-progressive if using --auto-mode");
520
0
}
521
522
523
void own_signal_handler(int a)
524
0
{
525
0
  if (verbose_mode > 1)
526
0
    fprintf(stderr,PROGRAMNAME ": died from signal: %d\n",a);
527
0
  exit(2);
528
0
}
529
530
531
void write_markers(struct jpeg_decompress_struct *dinfo,
532
    struct jpeg_compress_struct *cinfo)
533
5.23k
{
534
5.23k
  jpeg_saved_marker_ptr mrk;
535
5.23k
  int write_marker;
536
5.23k
  const char *s_name;
537
538
5.23k
  if (!cinfo || !dinfo)
539
0
    fatal("invalid call to write_markers()");
540
541
5.23k
  mrk=dinfo->marker_list;
542
6.93M
  while (mrk) {
543
6.92M
    write_marker = 0;
544
6.92M
    s_name = jpeg_special_marker_name(mrk);
545
546
    /* Check for markers to save... */
547
548
6.92M
    if (save_com && mrk->marker == JPEG_COM)
549
73.0k
      write_marker++;
550
551
6.92M
    if (save_iptc && !strncmp(s_name, "IPTC", 5))
552
29.0k
      write_marker++;
553
554
6.92M
    if (save_exif && !strncmp(s_name, "Exif", 5))
555
5.97k
      write_marker++;
556
557
6.92M
    if (save_icc && !strncmp(s_name, "ICC", 4))
558
17.5k
      write_marker++;
559
560
6.92M
    if (save_xmp && !strncmp(s_name, "XMP", 4))
561
13.0k
      write_marker++;
562
563
6.92M
    if (save_jfxx && !strncmp(s_name, "JFXX", 5))
564
0
      write_marker++;
565
566
6.92M
    if (save_adobe && !strncmp(s_name, "Adobe", 6))
567
0
      write_marker++;
568
569
6.92M
    if (strip_none)
570
0
      write_marker++;
571
572
573
    /* libjpeg emits some markers automatically so skip these to avoid duplicates... */
574
575
6.92M
    if (!strncmp(s_name, "JFIF", 5))
576
699k
      write_marker=0;
577
578
579
6.92M
    if (verbose_mode > 2)
580
0
      fprintf(jpeg_log_fh, " (Marker %s [%s]: %s)", jpeg_marker_name(mrk->marker),
581
0
        s_name, (write_marker ? "Keep" : "Discard"));
582
6.92M
    if (write_marker)
583
138k
      jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length);
584
585
6.92M
    mrk=mrk->next;
586
6.92M
  }
587
5.23k
}
write_markers
Line
Count
Source
533
1.74k
{
534
1.74k
  jpeg_saved_marker_ptr mrk;
535
1.74k
  int write_marker;
536
1.74k
  const char *s_name;
537
538
1.74k
  if (!cinfo || !dinfo)
539
0
    fatal("invalid call to write_markers()");
540
541
1.74k
  mrk=dinfo->marker_list;
542
2.31M
  while (mrk) {
543
2.30M
    write_marker = 0;
544
2.30M
    s_name = jpeg_special_marker_name(mrk);
545
546
    /* Check for markers to save... */
547
548
2.30M
    if (save_com && mrk->marker == JPEG_COM)
549
24.3k
      write_marker++;
550
551
2.30M
    if (save_iptc && !strncmp(s_name, "IPTC", 5))
552
9.67k
      write_marker++;
553
554
2.30M
    if (save_exif && !strncmp(s_name, "Exif", 5))
555
1.99k
      write_marker++;
556
557
2.30M
    if (save_icc && !strncmp(s_name, "ICC", 4))
558
5.85k
      write_marker++;
559
560
2.30M
    if (save_xmp && !strncmp(s_name, "XMP", 4))
561
4.35k
      write_marker++;
562
563
2.30M
    if (save_jfxx && !strncmp(s_name, "JFXX", 5))
564
0
      write_marker++;
565
566
2.30M
    if (save_adobe && !strncmp(s_name, "Adobe", 6))
567
0
      write_marker++;
568
569
2.30M
    if (strip_none)
570
0
      write_marker++;
571
572
573
    /* libjpeg emits some markers automatically so skip these to avoid duplicates... */
574
575
2.30M
    if (!strncmp(s_name, "JFIF", 5))
576
233k
      write_marker=0;
577
578
579
2.30M
    if (verbose_mode > 2)
580
0
      fprintf(jpeg_log_fh, " (Marker %s [%s]: %s)", jpeg_marker_name(mrk->marker),
581
0
        s_name, (write_marker ? "Keep" : "Discard"));
582
2.30M
    if (write_marker)
583
46.2k
      jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length);
584
585
2.30M
    mrk=mrk->next;
586
2.30M
  }
587
1.74k
}
write_markers
Line
Count
Source
533
1.74k
{
534
1.74k
  jpeg_saved_marker_ptr mrk;
535
1.74k
  int write_marker;
536
1.74k
  const char *s_name;
537
538
1.74k
  if (!cinfo || !dinfo)
539
0
    fatal("invalid call to write_markers()");
540
541
1.74k
  mrk=dinfo->marker_list;
542
2.31M
  while (mrk) {
543
2.30M
    write_marker = 0;
544
2.30M
    s_name = jpeg_special_marker_name(mrk);
545
546
    /* Check for markers to save... */
547
548
2.30M
    if (save_com && mrk->marker == JPEG_COM)
549
24.3k
      write_marker++;
550
551
2.30M
    if (save_iptc && !strncmp(s_name, "IPTC", 5))
552
9.67k
      write_marker++;
553
554
2.30M
    if (save_exif && !strncmp(s_name, "Exif", 5))
555
1.99k
      write_marker++;
556
557
2.30M
    if (save_icc && !strncmp(s_name, "ICC", 4))
558
5.85k
      write_marker++;
559
560
2.30M
    if (save_xmp && !strncmp(s_name, "XMP", 4))
561
4.35k
      write_marker++;
562
563
2.30M
    if (save_jfxx && !strncmp(s_name, "JFXX", 5))
564
0
      write_marker++;
565
566
2.30M
    if (save_adobe && !strncmp(s_name, "Adobe", 6))
567
0
      write_marker++;
568
569
2.30M
    if (strip_none)
570
0
      write_marker++;
571
572
573
    /* libjpeg emits some markers automatically so skip these to avoid duplicates... */
574
575
2.30M
    if (!strncmp(s_name, "JFIF", 5))
576
233k
      write_marker=0;
577
578
579
2.30M
    if (verbose_mode > 2)
580
0
      fprintf(jpeg_log_fh, " (Marker %s [%s]: %s)", jpeg_marker_name(mrk->marker),
581
0
        s_name, (write_marker ? "Keep" : "Discard"));
582
2.30M
    if (write_marker)
583
46.2k
      jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length);
584
585
2.30M
    mrk=mrk->next;
586
2.30M
  }
587
1.74k
}
write_markers
Line
Count
Source
533
1.74k
{
534
1.74k
  jpeg_saved_marker_ptr mrk;
535
1.74k
  int write_marker;
536
1.74k
  const char *s_name;
537
538
1.74k
  if (!cinfo || !dinfo)
539
0
    fatal("invalid call to write_markers()");
540
541
1.74k
  mrk=dinfo->marker_list;
542
2.31M
  while (mrk) {
543
2.30M
    write_marker = 0;
544
2.30M
    s_name = jpeg_special_marker_name(mrk);
545
546
    /* Check for markers to save... */
547
548
2.30M
    if (save_com && mrk->marker == JPEG_COM)
549
24.3k
      write_marker++;
550
551
2.30M
    if (save_iptc && !strncmp(s_name, "IPTC", 5))
552
9.67k
      write_marker++;
553
554
2.30M
    if (save_exif && !strncmp(s_name, "Exif", 5))
555
1.99k
      write_marker++;
556
557
2.30M
    if (save_icc && !strncmp(s_name, "ICC", 4))
558
5.85k
      write_marker++;
559
560
2.30M
    if (save_xmp && !strncmp(s_name, "XMP", 4))
561
4.35k
      write_marker++;
562
563
2.30M
    if (save_jfxx && !strncmp(s_name, "JFXX", 5))
564
0
      write_marker++;
565
566
2.30M
    if (save_adobe && !strncmp(s_name, "Adobe", 6))
567
0
      write_marker++;
568
569
2.30M
    if (strip_none)
570
0
      write_marker++;
571
572
573
    /* libjpeg emits some markers automatically so skip these to avoid duplicates... */
574
575
2.30M
    if (!strncmp(s_name, "JFIF", 5))
576
233k
      write_marker=0;
577
578
579
2.30M
    if (verbose_mode > 2)
580
0
      fprintf(jpeg_log_fh, " (Marker %s [%s]: %s)", jpeg_marker_name(mrk->marker),
581
0
        s_name, (write_marker ? "Keep" : "Discard"));
582
2.30M
    if (write_marker)
583
46.2k
      jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length);
584
585
2.30M
    mrk=mrk->next;
586
2.30M
  }
587
1.74k
}
588
589
590
unsigned int parse_markers(const struct jpeg_decompress_struct *dinfo,
591
      char *str, unsigned int str_size, unsigned int *markers_total_size)
592
1.51k
{
593
1.51k
  jpeg_saved_marker_ptr m;
594
1.51k
  unsigned int count = 0;
595
1.51k
  char *seen;
596
1.51k
  size_t marker_types = jpeg_special_marker_types_count();
597
1.51k
  int com_seen = 0;
598
1.51k
  int special;
599
600
1.51k
  if ((seen = calloc(marker_types, 1)) == NULL)
601
0
    fatal("not enough of memory");
602
603
1.51k
  str[0] = 0;
604
1.51k
  *markers_total_size = 0;
605
606
1.51k
  m = dinfo->marker_list;
607
2.23M
  while (m) {
608
2.23M
    count++;
609
2.23M
    *markers_total_size += m->data_length;
610
611
2.23M
    if ((special = jpeg_special_marker(m)) >= 0) {
612
1.88M
      if (!seen[special])
613
11.1k
        str_add_list(str, str_size, jpeg_special_marker_types[special].name, ",");
614
1.88M
      seen[special]++;
615
1.88M
    }
616
617
2.23M
    if (m->marker == JPEG_COM && !com_seen) {
618
423
      str_add_list(str, str_size, "COM", ",");
619
423
      com_seen = 1;
620
423
    }
621
622
2.23M
    m = m->next;
623
2.23M
  }
624
625
1.51k
  free(seen);
626
627
1.51k
  return count;
628
1.51k
}
parse_markers
Line
Count
Source
592
505
{
593
505
  jpeg_saved_marker_ptr m;
594
505
  unsigned int count = 0;
595
505
  char *seen;
596
505
  size_t marker_types = jpeg_special_marker_types_count();
597
505
  int com_seen = 0;
598
505
  int special;
599
600
505
  if ((seen = calloc(marker_types, 1)) == NULL)
601
0
    fatal("not enough of memory");
602
603
505
  str[0] = 0;
604
505
  *markers_total_size = 0;
605
606
505
  m = dinfo->marker_list;
607
744k
  while (m) {
608
743k
    count++;
609
743k
    *markers_total_size += m->data_length;
610
611
743k
    if ((special = jpeg_special_marker(m)) >= 0) {
612
629k
      if (!seen[special])
613
3.71k
        str_add_list(str, str_size, jpeg_special_marker_types[special].name, ",");
614
629k
      seen[special]++;
615
629k
    }
616
617
743k
    if (m->marker == JPEG_COM && !com_seen) {
618
141
      str_add_list(str, str_size, "COM", ",");
619
141
      com_seen = 1;
620
141
    }
621
622
743k
    m = m->next;
623
743k
  }
624
625
505
  free(seen);
626
627
505
  return count;
628
505
}
parse_markers
Line
Count
Source
592
505
{
593
505
  jpeg_saved_marker_ptr m;
594
505
  unsigned int count = 0;
595
505
  char *seen;
596
505
  size_t marker_types = jpeg_special_marker_types_count();
597
505
  int com_seen = 0;
598
505
  int special;
599
600
505
  if ((seen = calloc(marker_types, 1)) == NULL)
601
0
    fatal("not enough of memory");
602
603
505
  str[0] = 0;
604
505
  *markers_total_size = 0;
605
606
505
  m = dinfo->marker_list;
607
744k
  while (m) {
608
743k
    count++;
609
743k
    *markers_total_size += m->data_length;
610
611
743k
    if ((special = jpeg_special_marker(m)) >= 0) {
612
629k
      if (!seen[special])
613
3.71k
        str_add_list(str, str_size, jpeg_special_marker_types[special].name, ",");
614
629k
      seen[special]++;
615
629k
    }
616
617
743k
    if (m->marker == JPEG_COM && !com_seen) {
618
141
      str_add_list(str, str_size, "COM", ",");
619
141
      com_seen = 1;
620
141
    }
621
622
743k
    m = m->next;
623
743k
  }
624
625
505
  free(seen);
626
627
505
  return count;
628
505
}
parse_markers
Line
Count
Source
592
505
{
593
505
  jpeg_saved_marker_ptr m;
594
505
  unsigned int count = 0;
595
505
  char *seen;
596
505
  size_t marker_types = jpeg_special_marker_types_count();
597
505
  int com_seen = 0;
598
505
  int special;
599
600
505
  if ((seen = calloc(marker_types, 1)) == NULL)
601
0
    fatal("not enough of memory");
602
603
505
  str[0] = 0;
604
505
  *markers_total_size = 0;
605
606
505
  m = dinfo->marker_list;
607
744k
  while (m) {
608
743k
    count++;
609
743k
    *markers_total_size += m->data_length;
610
611
743k
    if ((special = jpeg_special_marker(m)) >= 0) {
612
629k
      if (!seen[special])
613
3.71k
        str_add_list(str, str_size, jpeg_special_marker_types[special].name, ",");
614
629k
      seen[special]++;
615
629k
    }
616
617
743k
    if (m->marker == JPEG_COM && !com_seen) {
618
141
      str_add_list(str, str_size, "COM", ",");
619
141
      com_seen = 1;
620
141
    }
621
622
743k
    m = m->next;
623
743k
  }
624
625
505
  free(seen);
626
627
505
  return count;
628
505
}
629
630
631
632
int optimize(FILE *log_fh, const char *filename, const char *newname,
633
  const char *tmpdir, struct stat *file_stat,
634
  double *rate, double *saved)
635
603
{
636
603
  FILE *infile = NULL;
637
603
  FILE *outfile = NULL;
638
603
  const char *outfname = NULL;
639
603
  char tmpfilename[MAXPATHLEN];
640
603
  struct jpeg_decompress_struct dinfo;
641
603
  struct jpeg_compress_struct cinfo;
642
603
  struct my_error_mgr jcerr, jderr;
643
603
  JSAMPARRAY buf = NULL;
644
645
603
  unsigned char *outbuffer = NULL;
646
603
  size_t outbuffersize = 0;
647
603
  unsigned char *inbuffer = NULL;
648
603
  size_t inbuffersize = 0;
649
603
  size_t inbufferused = 0;
650
603
  unsigned char *tmpbuffer = NULL;
651
603
  size_t tmpbuffersize = 0;
652
603
  unsigned char *extrabuffer = NULL;
653
603
  size_t extrabuffersize = 0;
654
655
603
  jvirt_barray_ptr *coef_arrays = NULL;
656
603
  char marker_str[256];
657
603
  unsigned int marker_in_count, marker_in_size;
658
659
603
  long in_image_size = 0;
660
603
  long insize = 0, outsize = 0, lastsize = 0;
661
603
  int oldquality, searchdone;
662
603
  double ratio;
663
603
  size_t last_retry_size = 0;
664
603
  int retry_count = 0;
665
603
  int retry = 0;
666
603
  int res = -1;
667
668
603
  jpeg_log_fh = log_fh;
669
670
  /* Initialize decompression object */
671
603
  dinfo.err = jpeg_std_error(&jderr.pub);
672
603
  jpeg_create_decompress(&dinfo);
673
603
  jderr.pub.error_exit=my_error_exit;
674
603
  jderr.pub.output_message=my_output_message;
675
603
  jderr.jump_set = 0;
676
677
  /* Initialize compression object */
678
603
  cinfo.err = jpeg_std_error(&jcerr.pub);
679
603
  jpeg_create_compress(&cinfo);
680
603
  jcerr.pub.error_exit=my_error_exit;
681
603
  jcerr.pub.output_message=my_output_message;
682
603
  jcerr.jump_set = 0;
683
684
603
  if (rate)
685
603
    *rate = 0.0;
686
603
  if (saved)
687
603
    *saved = 0.0;
688
689
603
  if (filename) {
690
603
    if ((infile = fopen(filename, "rb")) == NULL) {
691
0
      warn("cannot open file: %s", filename);
692
0
      res = 1;
693
0
      goto exit_point;
694
0
    }
695
603
  } else {
696
0
    infile = stdin;
697
0
    set_filemode_binary(infile);
698
0
  }
699
700
806
retry_point:
701
702
806
  if (setjmp(jderr.setjmp_buffer)) {
703
    /* Error handler for decompress */
704
161
  abort_decompress:
705
161
    jpeg_abort_decompress(&dinfo);
706
161
    fclose(infile);
707
161
    free_line_buf(&buf, dinfo.output_height);
708
161
    if (!quiet_mode || csv)
709
161
      fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n");
710
161
    jderr.jump_set=0;
711
161
    res = 1;
712
161
    goto exit_point;
713
645
  } else {
714
645
    jderr.jump_set=1;
715
645
  }
716
717
718
  /* Prepare to decompress */
719
645
  if (!retry) {
720
603
    if (!quiet_mode || csv) {
721
603
      fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin"));
722
603
      fflush(log_fh);
723
603
    }
724
725
603
    if (stdin_mode || stdout_mode) {
726
0
      inbuffersize = IN_BUF_SIZE;
727
603
    } else {
728
603
      if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE)
729
524
        inbuffersize = IN_BUF_SIZE;
730
603
    }
731
603
    if (inbuffer)
732
0
      free(inbuffer);
733
603
    if (!(inbuffer=calloc(inbuffersize, 1)))
734
0
      fatal("not enough memory");
735
603
  }
736
645
  global_error_counter=0;
737
645
  jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
738
13.5k
  for (int i = 0; i < 16; i++) {
739
12.8k
    jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff);
740
12.8k
  }
741
645
  if (!retry) {
742
603
    jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE);
743
603
  } else {
744
42
    if (retry == 1)
745
203
      jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused);
746
18.4E
    else
747
18.4E
      jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize);
748
42
  }
749
645
  jpeg_read_header(&dinfo, TRUE);
750
751
752
  /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */
753
645
  marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str),
754
645
          &marker_in_size);
755
756
645
  if (!retry) {
757
563
    if (verbose_mode > 1) {
758
0
      fprintf(log_fh,"%d markers found in input file (total size %d bytes)\n",
759
0
        marker_in_count,marker_in_size);
760
0
      fprintf(log_fh,"coding: %s\n", (dinfo.arith_code == TRUE ? "Arithmetic" : "Huffman"));
761
0
    }
762
763
563
    if (!quiet_mode || csv) {
764
563
      fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width,
765
563
        (int)dinfo.image_height,(int)dinfo.num_components*8,
766
563
        (dinfo.progressive_mode?'P':'N'));
767
563
      if (!csv)
768
563
        fprintf(log_fh,"%s",marker_str);
769
563
      fflush(log_fh);
770
563
    }
771
563
  }
772
773
  /* Decompress the image */
774
761
  if (quality >= 0 && retry != 1) {
775
558
    jpeg_start_decompress(&dinfo);
776
777
    /* Allocate line buffer to store the decompressed image */
778
558
    if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW))))
779
0
      fatal("not enough memory");
780
1.70M
    for (int i = 0; i < dinfo.output_height; i++) {
781
1.70M
      if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components,
782
1.70M
              sizeof(JSAMPLE))))
783
0
        fatal("not enough memory");
784
1.70M
    }
785
786
658k
    while (dinfo.output_scanline < dinfo.output_height) {
787
657k
      jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
788
657k
          dinfo.output_height-dinfo.output_scanline);
789
657k
    }
790
558
  } else {
791
87
    coef_arrays = jpeg_read_coefficients(&dinfo);
792
87
    if (!coef_arrays) {
793
0
      if (!quiet_mode)
794
0
        fprintf(log_fh, " (failed to read coefficients) ");
795
0
      goto abort_decompress;
796
0
    }
797
87
  }
798
645
  if (!retry) {
799
535
    in_image_size = inbufferused - dinfo.src->bytes_in_buffer;
800
535
    if(verbose_mode > 2)
801
0
      fprintf(log_fh, " (input image size: %lu (%lu))",
802
0
        in_image_size, inbufferused);
803
535
    if (stdin_mode) {
804
0
      insize = in_image_size;
805
535
    } else {
806
535
      if ((insize = file_size(infile)) < 0)
807
0
        fatal("failed to stat() input file");
808
535
      if (in_image_size > 0 && in_image_size < insize) {
809
263
        if (!quiet_mode)
810
263
          fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ",
811
263
            insize - in_image_size);
812
263
        if (nofix_mode)
813
0
          global_error_counter++;
814
263
        if (save_extra) {
815
0
          extrabuffersize = insize - in_image_size;
816
0
          if (extrabuffer)
817
0
            free(extrabuffer);
818
0
          if (!(extrabuffer = calloc(extrabuffersize, 1)))
819
0
            fatal("not enough memory");
820
0
          if (fseek(infile, in_image_size, SEEK_SET))
821
0
            fatal("failed to seek input file");
822
0
          if (fread(extrabuffer, extrabuffersize, 1, infile) != 1)
823
0
            fatal("failed to read inputfile");
824
0
        }
825
263
      }
826
535
    }
827
535
    if (!quiet_mode) {
828
535
      fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] "));
829
535
      fflush(log_fh);
830
535
    }
831
832
535
    if (nofix_mode && global_error_counter != 0) {
833
      /* Skip files containing any errors (or warnings) */
834
0
      goto abort_decompress;
835
0
    }
836
837
535
    if (dest && !noaction) {
838
0
      if (file_exists(newname) && !overwrite_mode) {
839
0
        if (!quiet_mode)
840
0
          fprintf(log_fh, " (target file already exists) ");
841
0
        goto abort_decompress;
842
0
      }
843
0
    }
844
535
  }
845
846
847
  /* Prepare to compress... */
848
645
  if (setjmp(jcerr.setjmp_buffer)) {
849
    /* Error handler for compress failures */
850
104
    if (!quiet_mode)
851
104
      fprintf(log_fh," [Compress ERROR: %s]\n",last_error);
852
104
    jpeg_abort_compress(&cinfo);
853
104
    jpeg_abort_decompress(&dinfo);
854
104
    fclose(infile);
855
104
    free_line_buf(&buf, dinfo.output_height);
856
104
    jcerr.jump_set=0;
857
104
    res = 2;
858
104
    goto exit_point;
859
541
  } else {
860
541
    jcerr.jump_set=1;
861
541
  }
862
863
541
  lastsize = 0;
864
541
  searchdone = 0;
865
541
  if (!retry) {
866
535
    oldquality = 200;
867
535
    if (target_size != 0) {
868
      /* Always start with quality 100 if -S option specified... */
869
535
      quality = 100;
870
535
    }
871
535
  }
872
873
874
2.71k
binary_search_loop:
875
876
  /* Allocate memory buffer that should be large enough to store the output JPEG... */
877
2.71k
  if (outbuffer)
878
2.18k
    free(outbuffer);
879
2.71k
  outbuffersize = insize + 32768;
880
2.71k
  if (!(outbuffer=calloc(outbuffersize, 1)))
881
0
    fatal("not enough memory");
882
883
  /* setup custom "destination manager" for libjpeg to write to our buffer */
884
2.71k
  jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536);
885
886
887
2.71k
  if (quality >= 0 && retry != 1) {
888
    /* Lossy "optimization" ... */
889
890
2.51k
    cinfo.in_color_space=dinfo.out_color_space;
891
2.51k
    cinfo.input_components=dinfo.output_components;
892
2.51k
    cinfo.image_width=dinfo.image_width;
893
2.51k
    cinfo.image_height=dinfo.image_height;
894
2.51k
    jpeg_set_defaults(&cinfo);
895
2.51k
    jpeg_set_quality(&cinfo,quality,TRUE);
896
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
897
742
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
898
742
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
899
#endif
900
2.51k
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
901
      /* Explicitly disable progressive mode. */
902
1.92k
      cinfo.scan_info = NULL;
903
1.92k
      cinfo.num_scans = 0;
904
1.92k
    } else if (all_progressive || dinfo.progressive_mode) {
905
      /* Enable progressive mode. */
906
595
      jpeg_simple_progression(&cinfo);
907
595
    }
908
2.51k
    cinfo.optimize_coding = TRUE;
909
2.51k
#ifdef HAVE_ARITH_CODE
910
2.51k
    if (arith_mode >= 0)
911
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
912
2.51k
#endif
913
2.51k
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
914
1.34k
      cinfo.write_JFIF_header = TRUE;
915
1.34k
    } else {
916
1.17k
      cinfo.write_JFIF_header = FALSE;
917
1.17k
    }
918
2.51k
    if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
919
      /* If outputting Adobe marker, don't write JFIF marker... */
920
0
      cinfo.write_JFIF_header = FALSE;
921
0
    }
922
923
2.51k
    jpeg_start_compress(&cinfo,TRUE);
924
925
    /* Write markers */
926
2.51k
    write_markers(&dinfo,&cinfo);
927
928
    /* Write image */
929
5.02k
    while (cinfo.next_scanline < cinfo.image_height) {
930
2.51k
      jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
931
2.51k
          dinfo.output_height);
932
2.51k
    }
933
934
2.51k
  } else {
935
    /* Lossless optimization ... */
936
937
200
    jpeg_copy_critical_parameters(&dinfo, &cinfo);
938
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
939
72
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
940
61
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
941
#endif
942
200
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
943
      /* Explicitly disable progressive mode. */
944
136
      cinfo.scan_info = NULL;
945
136
      cinfo.num_scans = 0;
946
136
    } else if (all_progressive || dinfo.progressive_mode) {
947
      /* Enable progressive mode. */
948
39
      jpeg_simple_progression(&cinfo);
949
39
    }
950
200
    cinfo.optimize_coding = TRUE;
951
200
#ifdef HAVE_ARITH_CODE
952
200
    if (arith_mode >= 0)
953
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
954
200
#endif
955
200
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
956
88
      cinfo.write_JFIF_header = TRUE;
957
112
    } else {
958
112
      cinfo.write_JFIF_header = FALSE;
959
112
    }
960
200
    if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
961
      /* If outputting Adobe marker, don't write JFIF marker... */
962
0
      cinfo.write_JFIF_header = FALSE;
963
0
    }
964
965
    /* Write image */
966
200
    jpeg_write_coefficients(&cinfo, coef_arrays);
967
968
    /* Write markers */
969
200
    write_markers(&dinfo,&cinfo);
970
971
200
  }
972
973
2.71k
  jpeg_finish_compress(&cinfo);
974
2.71k
  outsize = outbuffersize + extrabuffersize;
975
2.71k
  if (verbose_mode > 2)
976
0
    fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize);
977
978
2.71k
  if (target_size != 0 && !retry) {
979
    /* Perform (binary) search to try to reach target file size... */
980
981
2.51k
    long osize = outsize/1024;
982
2.51k
    long isize = insize/1024;
983
2.51k
    long tsize = target_size;
984
985
2.51k
    if (verbose_mode > 1)
986
0
      fprintf(log_fh, "(size=%ld)",outsize);
987
2.51k
    if (tsize < 0) {
988
2.51k
      tsize=((-target_size)*insize/100)/1024;
989
2.51k
      if (tsize < 1)
990
586
        tsize = 1;
991
2.51k
    }
992
993
2.51k
    if (osize == tsize || searchdone || tsize > isize) {
994
570
      if (searchdone < 42 && lastsize > 0) {
995
458
        if (labs(osize-tsize) > labs(lastsize-tsize)) {
996
40
          if (verbose_mode)
997
0
            fprintf(log_fh,"(revert to %d)",oldquality);
998
40
          searchdone = 42;
999
40
          quality = oldquality;
1000
40
          goto binary_search_loop;
1001
40
        }
1002
458
      }
1003
530
      if (verbose_mode)
1004
0
        fprintf(log_fh," ");
1005
1006
1.94k
    } else {
1007
1.94k
      int newquality;
1008
1.94k
      double dif = abs(oldquality-quality) / 2.0;
1009
1010
1.94k
      if (osize > tsize)
1011
1.34k
        newquality = quality - dif;
1012
596
      else
1013
596
        newquality = quality + dif + 0.5;
1014
1015
1.94k
      if (dif < 1.0)
1016
61
        searchdone = 1;
1017
1.94k
      if (newquality < 0) {
1018
128
        newquality = 0;
1019
128
        searchdone = 1;
1020
128
      }
1021
1.94k
      if (newquality > 100) {
1022
156
        newquality = 100;
1023
156
        searchdone = 1;
1024
156
      }
1025
1026
1.94k
      oldquality = quality;
1027
1.94k
      quality = newquality;
1028
1.94k
      lastsize = osize;
1029
1.94k
      if (verbose_mode)
1030
0
        fprintf(log_fh,"(try %d)",quality);
1031
1.94k
      goto binary_search_loop;
1032
1.94k
    }
1033
2.51k
  }
1034
1035
735
  jpeg_finish_decompress(&dinfo);
1036
735
  free_line_buf(&buf, dinfo.output_height);
1037
1038
735
  if (retry_mode) {
1039
0
    if ((retry == 0 || retry == 2) && quality >= 0 && outsize <= insize) {
1040
      /* Retry compression until output file stops getting smaller
1041
         or we hit max limit of iterations (10)... */
1042
0
      if (retry_count == 0)
1043
0
        last_retry_size = outsize + 1;
1044
0
      if (++retry_count < 10 && outsize < last_retry_size) {
1045
0
        if (tmpbuffer)
1046
0
          free(tmpbuffer);
1047
0
        tmpbuffer = outbuffer;
1048
0
        tmpbuffersize = outbuffersize;
1049
0
        outbuffer = NULL;
1050
0
        last_retry_size = outsize;
1051
0
        retry = 2;
1052
0
        if (verbose_mode)
1053
0
          fprintf(log_fh, "(retry%d: %lu) ", retry_count, outsize);
1054
0
        goto retry_point;
1055
0
      }
1056
0
    }
1057
0
    if (retry == 2) {
1058
0
      if (verbose_mode)
1059
0
        fprintf(log_fh, "(retry done: %lu) ", outsize);
1060
0
      if (outsize > last_retry_size) {
1061
0
        if (outbuffer)
1062
0
          free(outbuffer);
1063
0
        outbuffer = tmpbuffer;
1064
0
        outbuffersize = tmpbuffersize;
1065
0
        outsize = outbuffersize + extrabuffersize;
1066
0
        tmpbuffer = NULL;
1067
0
      }
1068
0
    }
1069
0
  }
1070
1071
  /* If auto_mode, try both progressive and non-progressive... */
1072
735
  if (auto_mode) {
1073
0
    int newmode = (dinfo.progressive_mode ? 0 : 1);
1074
0
    if (retry != 3) {
1075
0
      if (newmode)
1076
0
        all_progressive = 1;
1077
0
      else
1078
0
        all_normal = 1;
1079
0
      if (tmpbuffer)
1080
0
        free(tmpbuffer);
1081
0
      tmpbuffer = outbuffer;
1082
0
      tmpbuffersize = outbuffersize;
1083
0
      outbuffer = NULL;
1084
0
      last_retry_size = outsize;
1085
0
      retry = 3;
1086
0
      if (verbose_mode)
1087
0
        fprintf(log_fh, "(retry w/%s) ", (newmode ? "progressive" : "normal"));
1088
0
      goto retry_point;
1089
0
    } else {
1090
0
      if (verbose_mode > 1)
1091
0
        fprintf(log_fh, "(automode done: %lu) ", outsize);
1092
0
      auto_mode = 0;
1093
0
      if (outsize > last_retry_size) {
1094
0
        if (verbose_mode)
1095
0
          fprintf(log_fh, "(revert to %s) ", (!newmode ? "progressive" : "normal"));
1096
0
        all_progressive = 0;
1097
0
        all_normal = 0;
1098
0
        if (outbuffer)
1099
0
          free(outbuffer);
1100
0
        outbuffer = tmpbuffer;
1101
0
        outbuffersize = tmpbuffersize;
1102
0
        outsize = outbuffersize + extrabuffersize;
1103
0
        tmpbuffer = NULL;
1104
0
      }
1105
0
    }
1106
0
  }
1107
1108
  /* In case "lossy" compression resulted larger file than original, retry with "lossless"... */
1109
735
  if (quality >= 0 && outsize >= insize && retry != 1) {
1110
203
    retry = 1;
1111
203
    if (verbose_mode)
1112
0
      fprintf(log_fh, "(retry w/lossless) ");
1113
203
    goto retry_point;
1114
203
  }
1115
1116
532
  fclose(infile);
1117
1118
532
  ratio = (insize - outsize) * 100.0 / insize;
1119
532
  if (!quiet_mode || csv)
1120
338
    fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio);
1121
532
  if (rate) {
1122
338
    *rate = (ratio < 0 ? 0.0 : ratio);
1123
338
  }
1124
1125
532
  if ((outsize < insize && ratio >= threshold) || force) {
1126
244
    if (saved) {
1127
244
      *saved = (insize - outsize) / 1024.0;
1128
244
    }
1129
244
    if (!quiet_mode || csv)
1130
244
      fprintf(log_fh,csv ? "optimized\n" : "optimized.\n");
1131
244
    if (noaction) {
1132
0
      res = 0;
1133
0
      goto exit_point;
1134
0
    }
1135
1136
244
    if (stdout_mode) {
1137
0
      outfname=NULL;
1138
0
      set_filemode_binary(stdout);
1139
0
      if (fwrite(outbuffer,outbuffersize,1,stdout) != 1)
1140
0
        fatal("%s, write failed to stdout",(stdin_mode ? "stdin" : filename));
1141
244
    } else {
1142
244
      if (preserve_perms && !dest) {
1143
        /* make backup of the original file */
1144
0
        int newlen = snprintf(tmpfilename, sizeof(tmpfilename),
1145
0
            "%s.jpegoptim.bak", newname);
1146
0
        if (newlen >= sizeof(tmpfilename))
1147
0
          fatal("temp filename too long: %s", tmpfilename);
1148
1149
0
        if (verbose_mode > 1)
1150
0
          fprintf(log_fh,"%s, creating backup as: %s\n",
1151
0
            (stdin_mode ? "stdin" : filename), tmpfilename);
1152
0
        if (file_exists(tmpfilename))
1153
0
          fatal("%s, backup file already exists: %s",
1154
0
            (stdin_mode ?" stdin" : filename), tmpfilename);
1155
0
        if (copy_file(newname,tmpfilename))
1156
0
          fatal("%s, failed to create backup: %s",
1157
0
            (stdin_mode ? "stdin" : filename), tmpfilename);
1158
0
        if ((outfile=create_file(newname))==NULL)
1159
0
          fatal("%s, error opening output file: %s",
1160
0
            (stdin_mode ? "stdin" : filename), newname);
1161
0
        outfname = newname;
1162
244
      } else {
1163
244
        if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename))))
1164
0
          fatal("error creating temporary file: %s", tmpfilename);
1165
244
        outfname = tmpfilename;
1166
244
      }
1167
1168
244
      if (verbose_mode > 1)
1169
0
        fprintf(log_fh,"writing %lu bytes to file: %s\n",
1170
0
          (long unsigned int)outbuffersize, outfname);
1171
244
      if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1)
1172
0
        fatal("write failed to file: %s", outfname);
1173
244
      if (save_extra && extrabuffersize > 0) {
1174
0
        if (verbose_mode > 1)
1175
0
          fprintf(log_fh,"writing %lu bytes to file: %s\n", extrabuffersize, outfname);
1176
0
        if (fwrite(extrabuffer, extrabuffersize, 1, outfile) != 1)
1177
0
          fatal("write failed to file: %s", outfname);
1178
0
      }
1179
244
      fclose(outfile);
1180
244
    }
1181
1182
244
    if (outfname) {
1183
244
      if (preserve_mode) {
1184
        /* preserve file modification time */
1185
0
        if (verbose_mode > 1)
1186
0
          fprintf(log_fh,"set file modification time same as in original: %s\n",
1187
0
            outfname);
1188
0
#if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM)
1189
0
        struct timespec time_save[2];
1190
0
        time_save[0].tv_sec = 0;
1191
0
        time_save[0].tv_nsec = UTIME_OMIT; /* omit atime */
1192
0
        time_save[1] = file_stat->st_mtim;
1193
0
        if (utimensat(AT_FDCWD,outfname,time_save,0) != 0)
1194
0
          warn("failed to reset output file time/date");
1195
#else
1196
        struct utimbuf time_save;
1197
        time_save.actime=file_stat->st_atime;
1198
        time_save.modtime=file_stat->st_mtime;
1199
        if (utime(outfname,&time_save) != 0)
1200
          warn("failed to reset output file time/date");
1201
#endif
1202
0
      }
1203
1204
244
      if (preserve_perms && !dest) {
1205
        /* original file was already replaced, remove backup... */
1206
0
        if (verbose_mode > 1)
1207
0
          fprintf(log_fh,"removing backup file: %s\n", tmpfilename);
1208
0
        if (delete_file(tmpfilename))
1209
0
          warn("failed to remove backup file: %s", tmpfilename);
1210
244
      } else {
1211
        /* make temp file to be the original file... */
1212
1213
        /* preserve file mode */
1214
244
        if (chmod(outfname,(file_stat->st_mode & 0777)) != 0)
1215
0
          warn("failed to set output file mode");
1216
1217
        /* preserve file group (and owner if run by root) */
1218
244
        if (chown(outfname,
1219
244
            (geteuid()==0 ? file_stat->st_uid : -1),
1220
244
            file_stat->st_gid) != 0)
1221
0
          warn("failed to reset output file group/owner");
1222
1223
244
        if (verbose_mode > 1)
1224
0
          fprintf(log_fh,"renaming: %s to %s\n", outfname, newname);
1225
244
        if (rename_file(outfname, newname))
1226
0
          fatal("cannot rename temp file");
1227
244
      }
1228
244
    }
1229
288
  } else {
1230
288
    if (!quiet_mode || csv)
1231
94
      fprintf(log_fh,csv ? "skipped\n" : "skipped.\n");
1232
288
    if (stdout_mode) {
1233
0
      set_filemode_binary(stdout);
1234
0
      if (fwrite(inbuffer, in_image_size, 1, stdout) != 1)
1235
0
        fatal("%s, write failed to stdout",
1236
0
          (stdin_mode ? "stdin" : filename));
1237
0
    }
1238
288
  }
1239
1240
532
  res = 0;
1241
1242
603
 exit_point:
1243
603
  if (inbuffer)
1244
603
    free(inbuffer);
1245
603
  if (outbuffer)
1246
535
    free(outbuffer);
1247
603
  if (tmpbuffer)
1248
0
    free(tmpbuffer);
1249
603
  if (extrabuffer)
1250
0
    free(extrabuffer);
1251
603
  jpeg_destroy_compress(&cinfo);
1252
603
  jpeg_destroy_decompress(&dinfo);
1253
1254
603
  return res;
1255
532
}
optimize
Line
Count
Source
635
209
{
636
209
  FILE *infile = NULL;
637
209
  FILE *outfile = NULL;
638
209
  const char *outfname = NULL;
639
209
  char tmpfilename[MAXPATHLEN];
640
209
  struct jpeg_decompress_struct dinfo;
641
209
  struct jpeg_compress_struct cinfo;
642
209
  struct my_error_mgr jcerr, jderr;
643
209
  JSAMPARRAY buf = NULL;
644
645
209
  unsigned char *outbuffer = NULL;
646
209
  size_t outbuffersize = 0;
647
209
  unsigned char *inbuffer = NULL;
648
209
  size_t inbuffersize = 0;
649
209
  size_t inbufferused = 0;
650
209
  unsigned char *tmpbuffer = NULL;
651
209
  size_t tmpbuffersize = 0;
652
209
  unsigned char *extrabuffer = NULL;
653
209
  size_t extrabuffersize = 0;
654
655
209
  jvirt_barray_ptr *coef_arrays = NULL;
656
209
  char marker_str[256];
657
209
  unsigned int marker_in_count, marker_in_size;
658
659
209
  long in_image_size = 0;
660
209
  long insize = 0, outsize = 0, lastsize = 0;
661
209
  int oldquality, searchdone;
662
209
  double ratio;
663
209
  size_t last_retry_size = 0;
664
209
  int retry_count = 0;
665
209
  int retry = 0;
666
209
  int res = -1;
667
668
209
  jpeg_log_fh = log_fh;
669
670
  /* Initialize decompression object */
671
209
  dinfo.err = jpeg_std_error(&jderr.pub);
672
209
  jpeg_create_decompress(&dinfo);
673
209
  jderr.pub.error_exit=my_error_exit;
674
209
  jderr.pub.output_message=my_output_message;
675
209
  jderr.jump_set = 0;
676
677
  /* Initialize compression object */
678
209
  cinfo.err = jpeg_std_error(&jcerr.pub);
679
209
  jpeg_create_compress(&cinfo);
680
209
  jcerr.pub.error_exit=my_error_exit;
681
209
  jcerr.pub.output_message=my_output_message;
682
209
  jcerr.jump_set = 0;
683
684
209
  if (rate)
685
209
    *rate = 0.0;
686
209
  if (saved)
687
209
    *saved = 0.0;
688
689
209
  if (filename) {
690
209
    if ((infile = fopen(filename, "rb")) == NULL) {
691
0
      warn("cannot open file: %s", filename);
692
0
      res = 1;
693
0
      goto exit_point;
694
0
    }
695
209
  } else {
696
0
    infile = stdin;
697
0
    set_filemode_binary(infile);
698
0
  }
699
700
274
retry_point:
701
702
274
  if (setjmp(jderr.setjmp_buffer)) {
703
    /* Error handler for decompress */
704
61
  abort_decompress:
705
61
    jpeg_abort_decompress(&dinfo);
706
61
    fclose(infile);
707
61
    free_line_buf(&buf, dinfo.output_height);
708
61
    if (!quiet_mode || csv)
709
61
      fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n");
710
61
    jderr.jump_set=0;
711
61
    res = 1;
712
61
    goto exit_point;
713
213
  } else {
714
213
    jderr.jump_set=1;
715
213
  }
716
717
718
  /* Prepare to decompress */
719
213
  if (!retry) {
720
209
    if (!quiet_mode || csv) {
721
209
      fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin"));
722
209
      fflush(log_fh);
723
209
    }
724
725
209
    if (stdin_mode || stdout_mode) {
726
0
      inbuffersize = IN_BUF_SIZE;
727
209
    } else {
728
209
      if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE)
729
176
        inbuffersize = IN_BUF_SIZE;
730
209
    }
731
209
    if (inbuffer)
732
0
      free(inbuffer);
733
209
    if (!(inbuffer=calloc(inbuffersize, 1)))
734
0
      fatal("not enough memory");
735
209
  }
736
213
  global_error_counter=0;
737
213
  jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
738
4.59k
  for (int i = 0; i < 16; i++) {
739
4.38k
    jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff);
740
4.38k
  }
741
213
  if (!retry) {
742
209
    jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE);
743
209
  } else {
744
4
    if (retry == 1)
745
65
      jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused);
746
18.4E
    else
747
18.4E
      jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize);
748
4
  }
749
213
  jpeg_read_header(&dinfo, TRUE);
750
751
752
  /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */
753
213
  marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str),
754
213
          &marker_in_size);
755
756
213
  if (!retry) {
757
196
    if (verbose_mode > 1) {
758
0
      fprintf(log_fh,"%d markers found in input file (total size %d bytes)\n",
759
0
        marker_in_count,marker_in_size);
760
0
      fprintf(log_fh,"coding: %s\n", (dinfo.arith_code == TRUE ? "Arithmetic" : "Huffman"));
761
0
    }
762
763
196
    if (!quiet_mode || csv) {
764
196
      fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width,
765
196
        (int)dinfo.image_height,(int)dinfo.num_components*8,
766
196
        (dinfo.progressive_mode?'P':'N'));
767
196
      if (!csv)
768
196
        fprintf(log_fh,"%s",marker_str);
769
196
      fflush(log_fh);
770
196
    }
771
196
  }
772
773
  /* Decompress the image */
774
259
  if (quality >= 0 && retry != 1) {
775
194
    jpeg_start_decompress(&dinfo);
776
777
    /* Allocate line buffer to store the decompressed image */
778
194
    if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW))))
779
0
      fatal("not enough memory");
780
661k
    for (int i = 0; i < dinfo.output_height; i++) {
781
661k
      if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components,
782
661k
              sizeof(JSAMPLE))))
783
0
        fatal("not enough memory");
784
661k
    }
785
786
241k
    while (dinfo.output_scanline < dinfo.output_height) {
787
240k
      jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
788
240k
          dinfo.output_height-dinfo.output_scanline);
789
240k
    }
790
194
  } else {
791
19
    coef_arrays = jpeg_read_coefficients(&dinfo);
792
19
    if (!coef_arrays) {
793
0
      if (!quiet_mode)
794
0
        fprintf(log_fh, " (failed to read coefficients) ");
795
0
      goto abort_decompress;
796
0
    }
797
19
  }
798
213
  if (!retry) {
799
186
    in_image_size = inbufferused - dinfo.src->bytes_in_buffer;
800
186
    if(verbose_mode > 2)
801
0
      fprintf(log_fh, " (input image size: %lu (%lu))",
802
0
        in_image_size, inbufferused);
803
186
    if (stdin_mode) {
804
0
      insize = in_image_size;
805
186
    } else {
806
186
      if ((insize = file_size(infile)) < 0)
807
0
        fatal("failed to stat() input file");
808
186
      if (in_image_size > 0 && in_image_size < insize) {
809
106
        if (!quiet_mode)
810
106
          fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ",
811
106
            insize - in_image_size);
812
106
        if (nofix_mode)
813
0
          global_error_counter++;
814
106
        if (save_extra) {
815
0
          extrabuffersize = insize - in_image_size;
816
0
          if (extrabuffer)
817
0
            free(extrabuffer);
818
0
          if (!(extrabuffer = calloc(extrabuffersize, 1)))
819
0
            fatal("not enough memory");
820
0
          if (fseek(infile, in_image_size, SEEK_SET))
821
0
            fatal("failed to seek input file");
822
0
          if (fread(extrabuffer, extrabuffersize, 1, infile) != 1)
823
0
            fatal("failed to read inputfile");
824
0
        }
825
106
      }
826
186
    }
827
186
    if (!quiet_mode) {
828
186
      fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] "));
829
186
      fflush(log_fh);
830
186
    }
831
832
186
    if (nofix_mode && global_error_counter != 0) {
833
      /* Skip files containing any errors (or warnings) */
834
0
      goto abort_decompress;
835
0
    }
836
837
186
    if (dest && !noaction) {
838
0
      if (file_exists(newname) && !overwrite_mode) {
839
0
        if (!quiet_mode)
840
0
          fprintf(log_fh, " (target file already exists) ");
841
0
        goto abort_decompress;
842
0
      }
843
0
    }
844
186
  }
845
846
847
  /* Prepare to compress... */
848
213
  if (setjmp(jcerr.setjmp_buffer)) {
849
    /* Error handler for compress failures */
850
31
    if (!quiet_mode)
851
31
      fprintf(log_fh," [Compress ERROR: %s]\n",last_error);
852
31
    jpeg_abort_compress(&cinfo);
853
31
    jpeg_abort_decompress(&dinfo);
854
31
    fclose(infile);
855
31
    free_line_buf(&buf, dinfo.output_height);
856
31
    jcerr.jump_set=0;
857
31
    res = 2;
858
31
    goto exit_point;
859
182
  } else {
860
182
    jcerr.jump_set=1;
861
182
  }
862
863
182
  lastsize = 0;
864
182
  searchdone = 0;
865
186
  if (!retry) {
866
186
    oldquality = 200;
867
186
    if (target_size != 0) {
868
      /* Always start with quality 100 if -S option specified... */
869
186
      quality = 100;
870
186
    }
871
186
  }
872
873
874
951
binary_search_loop:
875
876
  /* Allocate memory buffer that should be large enough to store the output JPEG... */
877
951
  if (outbuffer)
878
765
    free(outbuffer);
879
951
  outbuffersize = insize + 32768;
880
951
  if (!(outbuffer=calloc(outbuffersize, 1)))
881
0
    fatal("not enough memory");
882
883
  /* setup custom "destination manager" for libjpeg to write to our buffer */
884
951
  jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536);
885
886
887
951
  if (quality >= 0 && retry != 1) {
888
    /* Lossy "optimization" ... */
889
890
887
    cinfo.in_color_space=dinfo.out_color_space;
891
887
    cinfo.input_components=dinfo.output_components;
892
887
    cinfo.image_width=dinfo.image_width;
893
887
    cinfo.image_height=dinfo.image_height;
894
887
    jpeg_set_defaults(&cinfo);
895
887
    jpeg_set_quality(&cinfo,quality,TRUE);
896
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
897
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
898
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
899
#endif
900
887
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
901
      /* Explicitly disable progressive mode. */
902
768
      cinfo.scan_info = NULL;
903
768
      cinfo.num_scans = 0;
904
768
    } else if (all_progressive || dinfo.progressive_mode) {
905
      /* Enable progressive mode. */
906
119
      jpeg_simple_progression(&cinfo);
907
119
    }
908
887
    cinfo.optimize_coding = TRUE;
909
887
#ifdef HAVE_ARITH_CODE
910
887
    if (arith_mode >= 0)
911
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
912
887
#endif
913
887
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
914
490
      cinfo.write_JFIF_header = TRUE;
915
490
    } else {
916
397
      cinfo.write_JFIF_header = FALSE;
917
397
    }
918
887
    if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
919
      /* If outputting Adobe marker, don't write JFIF marker... */
920
0
      cinfo.write_JFIF_header = FALSE;
921
0
    }
922
923
887
    jpeg_start_compress(&cinfo,TRUE);
924
925
    /* Write markers */
926
887
    write_markers(&dinfo,&cinfo);
927
928
    /* Write image */
929
1.77k
    while (cinfo.next_scanline < cinfo.image_height) {
930
885
      jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
931
885
          dinfo.output_height);
932
885
    }
933
934
887
  } else {
935
    /* Lossless optimization ... */
936
937
64
    jpeg_copy_critical_parameters(&dinfo, &cinfo);
938
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
939
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
940
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
941
#endif
942
64
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
943
      /* Explicitly disable progressive mode. */
944
54
      cinfo.scan_info = NULL;
945
54
      cinfo.num_scans = 0;
946
54
    } else if (all_progressive || dinfo.progressive_mode) {
947
      /* Enable progressive mode. */
948
3
      jpeg_simple_progression(&cinfo);
949
3
    }
950
64
    cinfo.optimize_coding = TRUE;
951
64
#ifdef HAVE_ARITH_CODE
952
64
    if (arith_mode >= 0)
953
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
954
64
#endif
955
64
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
956
31
      cinfo.write_JFIF_header = TRUE;
957
33
    } else {
958
33
      cinfo.write_JFIF_header = FALSE;
959
33
    }
960
64
    if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
961
      /* If outputting Adobe marker, don't write JFIF marker... */
962
0
      cinfo.write_JFIF_header = FALSE;
963
0
    }
964
965
    /* Write image */
966
64
    jpeg_write_coefficients(&cinfo, coef_arrays);
967
968
    /* Write markers */
969
64
    write_markers(&dinfo,&cinfo);
970
971
64
  }
972
973
951
  jpeg_finish_compress(&cinfo);
974
951
  outsize = outbuffersize + extrabuffersize;
975
951
  if (verbose_mode > 2)
976
0
    fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize);
977
978
951
  if (target_size != 0 && !retry) {
979
    /* Perform (binary) search to try to reach target file size... */
980
981
885
    long osize = outsize/1024;
982
885
    long isize = insize/1024;
983
885
    long tsize = target_size;
984
985
885
    if (verbose_mode > 1)
986
0
      fprintf(log_fh, "(size=%ld)",outsize);
987
885
    if (tsize < 0) {
988
885
      tsize=((-target_size)*insize/100)/1024;
989
885
      if (tsize < 1)
990
182
        tsize = 1;
991
885
    }
992
993
885
    if (osize == tsize || searchdone || tsize > isize) {
994
198
      if (searchdone < 42 && lastsize > 0) {
995
161
        if (labs(osize-tsize) > labs(lastsize-tsize)) {
996
14
          if (verbose_mode)
997
0
            fprintf(log_fh,"(revert to %d)",oldquality);
998
14
          searchdone = 42;
999
14
          quality = oldquality;
1000
14
          goto binary_search_loop;
1001
14
        }
1002
161
      }
1003
184
      if (verbose_mode)
1004
0
        fprintf(log_fh," ");
1005
1006
687
    } else {
1007
687
      int newquality;
1008
687
      double dif = abs(oldquality-quality) / 2.0;
1009
1010
687
      if (osize > tsize)
1011
459
        newquality = quality - dif;
1012
228
      else
1013
228
        newquality = quality + dif + 0.5;
1014
1015
687
      if (dif < 1.0)
1016
26
        searchdone = 1;
1017
687
      if (newquality < 0) {
1018
38
        newquality = 0;
1019
38
        searchdone = 1;
1020
38
      }
1021
687
      if (newquality > 100) {
1022
48
        newquality = 100;
1023
48
        searchdone = 1;
1024
48
      }
1025
1026
687
      oldquality = quality;
1027
687
      quality = newquality;
1028
687
      lastsize = osize;
1029
687
      if (verbose_mode)
1030
0
        fprintf(log_fh,"(try %d)",quality);
1031
687
      goto binary_search_loop;
1032
687
    }
1033
885
  }
1034
1035
250
  jpeg_finish_decompress(&dinfo);
1036
250
  free_line_buf(&buf, dinfo.output_height);
1037
1038
250
  if (retry_mode) {
1039
0
    if ((retry == 0 || retry == 2) && quality >= 0 && outsize <= insize) {
1040
      /* Retry compression until output file stops getting smaller
1041
         or we hit max limit of iterations (10)... */
1042
0
      if (retry_count == 0)
1043
0
        last_retry_size = outsize + 1;
1044
0
      if (++retry_count < 10 && outsize < last_retry_size) {
1045
0
        if (tmpbuffer)
1046
0
          free(tmpbuffer);
1047
0
        tmpbuffer = outbuffer;
1048
0
        tmpbuffersize = outbuffersize;
1049
0
        outbuffer = NULL;
1050
0
        last_retry_size = outsize;
1051
0
        retry = 2;
1052
0
        if (verbose_mode)
1053
0
          fprintf(log_fh, "(retry%d: %lu) ", retry_count, outsize);
1054
0
        goto retry_point;
1055
0
      }
1056
0
    }
1057
0
    if (retry == 2) {
1058
0
      if (verbose_mode)
1059
0
        fprintf(log_fh, "(retry done: %lu) ", outsize);
1060
0
      if (outsize > last_retry_size) {
1061
0
        if (outbuffer)
1062
0
          free(outbuffer);
1063
0
        outbuffer = tmpbuffer;
1064
0
        outbuffersize = tmpbuffersize;
1065
0
        outsize = outbuffersize + extrabuffersize;
1066
0
        tmpbuffer = NULL;
1067
0
      }
1068
0
    }
1069
0
  }
1070
1071
  /* If auto_mode, try both progressive and non-progressive... */
1072
250
  if (auto_mode) {
1073
0
    int newmode = (dinfo.progressive_mode ? 0 : 1);
1074
0
    if (retry != 3) {
1075
0
      if (newmode)
1076
0
        all_progressive = 1;
1077
0
      else
1078
0
        all_normal = 1;
1079
0
      if (tmpbuffer)
1080
0
        free(tmpbuffer);
1081
0
      tmpbuffer = outbuffer;
1082
0
      tmpbuffersize = outbuffersize;
1083
0
      outbuffer = NULL;
1084
0
      last_retry_size = outsize;
1085
0
      retry = 3;
1086
0
      if (verbose_mode)
1087
0
        fprintf(log_fh, "(retry w/%s) ", (newmode ? "progressive" : "normal"));
1088
0
      goto retry_point;
1089
0
    } else {
1090
0
      if (verbose_mode > 1)
1091
0
        fprintf(log_fh, "(automode done: %lu) ", outsize);
1092
0
      auto_mode = 0;
1093
0
      if (outsize > last_retry_size) {
1094
0
        if (verbose_mode)
1095
0
          fprintf(log_fh, "(revert to %s) ", (!newmode ? "progressive" : "normal"));
1096
0
        all_progressive = 0;
1097
0
        all_normal = 0;
1098
0
        if (outbuffer)
1099
0
          free(outbuffer);
1100
0
        outbuffer = tmpbuffer;
1101
0
        outbuffersize = tmpbuffersize;
1102
0
        outsize = outbuffersize + extrabuffersize;
1103
0
        tmpbuffer = NULL;
1104
0
      }
1105
0
    }
1106
0
  }
1107
1108
  /* In case "lossy" compression resulted larger file than original, retry with "lossless"... */
1109
250
  if (quality >= 0 && outsize >= insize && retry != 1) {
1110
65
    retry = 1;
1111
65
    if (verbose_mode)
1112
0
      fprintf(log_fh, "(retry w/lossless) ");
1113
65
    goto retry_point;
1114
65
  }
1115
1116
185
  fclose(infile);
1117
1118
185
  ratio = (insize - outsize) * 100.0 / insize;
1119
185
  if (!quiet_mode || csv)
1120
117
    fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio);
1121
185
  if (rate) {
1122
117
    *rate = (ratio < 0 ? 0.0 : ratio);
1123
117
  }
1124
1125
185
  if ((outsize < insize && ratio >= threshold) || force) {
1126
84
    if (saved) {
1127
84
      *saved = (insize - outsize) / 1024.0;
1128
84
    }
1129
84
    if (!quiet_mode || csv)
1130
84
      fprintf(log_fh,csv ? "optimized\n" : "optimized.\n");
1131
84
    if (noaction) {
1132
0
      res = 0;
1133
0
      goto exit_point;
1134
0
    }
1135
1136
84
    if (stdout_mode) {
1137
0
      outfname=NULL;
1138
0
      set_filemode_binary(stdout);
1139
0
      if (fwrite(outbuffer,outbuffersize,1,stdout) != 1)
1140
0
        fatal("%s, write failed to stdout",(stdin_mode ? "stdin" : filename));
1141
84
    } else {
1142
84
      if (preserve_perms && !dest) {
1143
        /* make backup of the original file */
1144
0
        int newlen = snprintf(tmpfilename, sizeof(tmpfilename),
1145
0
            "%s.jpegoptim.bak", newname);
1146
0
        if (newlen >= sizeof(tmpfilename))
1147
0
          fatal("temp filename too long: %s", tmpfilename);
1148
1149
0
        if (verbose_mode > 1)
1150
0
          fprintf(log_fh,"%s, creating backup as: %s\n",
1151
0
            (stdin_mode ? "stdin" : filename), tmpfilename);
1152
0
        if (file_exists(tmpfilename))
1153
0
          fatal("%s, backup file already exists: %s",
1154
0
            (stdin_mode ?" stdin" : filename), tmpfilename);
1155
0
        if (copy_file(newname,tmpfilename))
1156
0
          fatal("%s, failed to create backup: %s",
1157
0
            (stdin_mode ? "stdin" : filename), tmpfilename);
1158
0
        if ((outfile=create_file(newname))==NULL)
1159
0
          fatal("%s, error opening output file: %s",
1160
0
            (stdin_mode ? "stdin" : filename), newname);
1161
0
        outfname = newname;
1162
84
      } else {
1163
84
        if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename))))
1164
0
          fatal("error creating temporary file: %s", tmpfilename);
1165
84
        outfname = tmpfilename;
1166
84
      }
1167
1168
84
      if (verbose_mode > 1)
1169
0
        fprintf(log_fh,"writing %lu bytes to file: %s\n",
1170
0
          (long unsigned int)outbuffersize, outfname);
1171
84
      if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1)
1172
0
        fatal("write failed to file: %s", outfname);
1173
84
      if (save_extra && extrabuffersize > 0) {
1174
0
        if (verbose_mode > 1)
1175
0
          fprintf(log_fh,"writing %lu bytes to file: %s\n", extrabuffersize, outfname);
1176
0
        if (fwrite(extrabuffer, extrabuffersize, 1, outfile) != 1)
1177
0
          fatal("write failed to file: %s", outfname);
1178
0
      }
1179
84
      fclose(outfile);
1180
84
    }
1181
1182
84
    if (outfname) {
1183
84
      if (preserve_mode) {
1184
        /* preserve file modification time */
1185
0
        if (verbose_mode > 1)
1186
0
          fprintf(log_fh,"set file modification time same as in original: %s\n",
1187
0
            outfname);
1188
0
#if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM)
1189
0
        struct timespec time_save[2];
1190
0
        time_save[0].tv_sec = 0;
1191
0
        time_save[0].tv_nsec = UTIME_OMIT; /* omit atime */
1192
0
        time_save[1] = file_stat->st_mtim;
1193
0
        if (utimensat(AT_FDCWD,outfname,time_save,0) != 0)
1194
0
          warn("failed to reset output file time/date");
1195
#else
1196
        struct utimbuf time_save;
1197
        time_save.actime=file_stat->st_atime;
1198
        time_save.modtime=file_stat->st_mtime;
1199
        if (utime(outfname,&time_save) != 0)
1200
          warn("failed to reset output file time/date");
1201
#endif
1202
0
      }
1203
1204
84
      if (preserve_perms && !dest) {
1205
        /* original file was already replaced, remove backup... */
1206
0
        if (verbose_mode > 1)
1207
0
          fprintf(log_fh,"removing backup file: %s\n", tmpfilename);
1208
0
        if (delete_file(tmpfilename))
1209
0
          warn("failed to remove backup file: %s", tmpfilename);
1210
84
      } else {
1211
        /* make temp file to be the original file... */
1212
1213
        /* preserve file mode */
1214
84
        if (chmod(outfname,(file_stat->st_mode & 0777)) != 0)
1215
0
          warn("failed to set output file mode");
1216
1217
        /* preserve file group (and owner if run by root) */
1218
84
        if (chown(outfname,
1219
84
            (geteuid()==0 ? file_stat->st_uid : -1),
1220
84
            file_stat->st_gid) != 0)
1221
0
          warn("failed to reset output file group/owner");
1222
1223
84
        if (verbose_mode > 1)
1224
0
          fprintf(log_fh,"renaming: %s to %s\n", outfname, newname);
1225
84
        if (rename_file(outfname, newname))
1226
0
          fatal("cannot rename temp file");
1227
84
      }
1228
84
    }
1229
101
  } else {
1230
101
    if (!quiet_mode || csv)
1231
33
      fprintf(log_fh,csv ? "skipped\n" : "skipped.\n");
1232
101
    if (stdout_mode) {
1233
0
      set_filemode_binary(stdout);
1234
0
      if (fwrite(inbuffer, in_image_size, 1, stdout) != 1)
1235
0
        fatal("%s, write failed to stdout",
1236
0
          (stdin_mode ? "stdin" : filename));
1237
0
    }
1238
101
  }
1239
1240
185
  res = 0;
1241
1242
209
 exit_point:
1243
209
  if (inbuffer)
1244
209
    free(inbuffer);
1245
209
  if (outbuffer)
1246
186
    free(outbuffer);
1247
209
  if (tmpbuffer)
1248
0
    free(tmpbuffer);
1249
209
  if (extrabuffer)
1250
0
    free(extrabuffer);
1251
209
  jpeg_destroy_compress(&cinfo);
1252
209
  jpeg_destroy_decompress(&dinfo);
1253
1254
209
  return res;
1255
185
}
optimize
Line
Count
Source
635
209
{
636
209
  FILE *infile = NULL;
637
209
  FILE *outfile = NULL;
638
209
  const char *outfname = NULL;
639
209
  char tmpfilename[MAXPATHLEN];
640
209
  struct jpeg_decompress_struct dinfo;
641
209
  struct jpeg_compress_struct cinfo;
642
209
  struct my_error_mgr jcerr, jderr;
643
209
  JSAMPARRAY buf = NULL;
644
645
209
  unsigned char *outbuffer = NULL;
646
209
  size_t outbuffersize = 0;
647
209
  unsigned char *inbuffer = NULL;
648
209
  size_t inbuffersize = 0;
649
209
  size_t inbufferused = 0;
650
209
  unsigned char *tmpbuffer = NULL;
651
209
  size_t tmpbuffersize = 0;
652
209
  unsigned char *extrabuffer = NULL;
653
209
  size_t extrabuffersize = 0;
654
655
209
  jvirt_barray_ptr *coef_arrays = NULL;
656
209
  char marker_str[256];
657
209
  unsigned int marker_in_count, marker_in_size;
658
659
209
  long in_image_size = 0;
660
209
  long insize = 0, outsize = 0, lastsize = 0;
661
209
  int oldquality, searchdone;
662
209
  double ratio;
663
209
  size_t last_retry_size = 0;
664
209
  int retry_count = 0;
665
209
  int retry = 0;
666
209
  int res = -1;
667
668
209
  jpeg_log_fh = log_fh;
669
670
  /* Initialize decompression object */
671
209
  dinfo.err = jpeg_std_error(&jderr.pub);
672
209
  jpeg_create_decompress(&dinfo);
673
209
  jderr.pub.error_exit=my_error_exit;
674
209
  jderr.pub.output_message=my_output_message;
675
209
  jderr.jump_set = 0;
676
677
  /* Initialize compression object */
678
209
  cinfo.err = jpeg_std_error(&jcerr.pub);
679
209
  jpeg_create_compress(&cinfo);
680
209
  jcerr.pub.error_exit=my_error_exit;
681
209
  jcerr.pub.output_message=my_output_message;
682
209
  jcerr.jump_set = 0;
683
684
209
  if (rate)
685
209
    *rate = 0.0;
686
209
  if (saved)
687
209
    *saved = 0.0;
688
689
209
  if (filename) {
690
209
    if ((infile = fopen(filename, "rb")) == NULL) {
691
0
      warn("cannot open file: %s", filename);
692
0
      res = 1;
693
0
      goto exit_point;
694
0
    }
695
209
  } else {
696
0
    infile = stdin;
697
0
    set_filemode_binary(infile);
698
0
  }
699
700
274
retry_point:
701
702
274
  if (setjmp(jderr.setjmp_buffer)) {
703
    /* Error handler for decompress */
704
61
  abort_decompress:
705
61
    jpeg_abort_decompress(&dinfo);
706
61
    fclose(infile);
707
61
    free_line_buf(&buf, dinfo.output_height);
708
61
    if (!quiet_mode || csv)
709
61
      fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n");
710
61
    jderr.jump_set=0;
711
61
    res = 1;
712
61
    goto exit_point;
713
213
  } else {
714
213
    jderr.jump_set=1;
715
213
  }
716
717
718
  /* Prepare to decompress */
719
213
  if (!retry) {
720
209
    if (!quiet_mode || csv) {
721
209
      fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin"));
722
209
      fflush(log_fh);
723
209
    }
724
725
209
    if (stdin_mode || stdout_mode) {
726
0
      inbuffersize = IN_BUF_SIZE;
727
209
    } else {
728
209
      if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE)
729
176
        inbuffersize = IN_BUF_SIZE;
730
209
    }
731
209
    if (inbuffer)
732
0
      free(inbuffer);
733
209
    if (!(inbuffer=calloc(inbuffersize, 1)))
734
0
      fatal("not enough memory");
735
209
  }
736
213
  global_error_counter=0;
737
213
  jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
738
4.59k
  for (int i = 0; i < 16; i++) {
739
4.38k
    jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff);
740
4.38k
  }
741
213
  if (!retry) {
742
209
    jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE);
743
209
  } else {
744
4
    if (retry == 1)
745
65
      jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused);
746
18.4E
    else
747
18.4E
      jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize);
748
4
  }
749
213
  jpeg_read_header(&dinfo, TRUE);
750
751
752
  /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */
753
213
  marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str),
754
213
          &marker_in_size);
755
756
213
  if (!retry) {
757
196
    if (verbose_mode > 1) {
758
0
      fprintf(log_fh,"%d markers found in input file (total size %d bytes)\n",
759
0
        marker_in_count,marker_in_size);
760
0
      fprintf(log_fh,"coding: %s\n", (dinfo.arith_code == TRUE ? "Arithmetic" : "Huffman"));
761
0
    }
762
763
196
    if (!quiet_mode || csv) {
764
196
      fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width,
765
196
        (int)dinfo.image_height,(int)dinfo.num_components*8,
766
196
        (dinfo.progressive_mode?'P':'N'));
767
196
      if (!csv)
768
196
        fprintf(log_fh,"%s",marker_str);
769
196
      fflush(log_fh);
770
196
    }
771
196
  }
772
773
  /* Decompress the image */
774
259
  if (quality >= 0 && retry != 1) {
775
194
    jpeg_start_decompress(&dinfo);
776
777
    /* Allocate line buffer to store the decompressed image */
778
194
    if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW))))
779
0
      fatal("not enough memory");
780
661k
    for (int i = 0; i < dinfo.output_height; i++) {
781
661k
      if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components,
782
661k
              sizeof(JSAMPLE))))
783
0
        fatal("not enough memory");
784
661k
    }
785
786
241k
    while (dinfo.output_scanline < dinfo.output_height) {
787
240k
      jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
788
240k
          dinfo.output_height-dinfo.output_scanline);
789
240k
    }
790
194
  } else {
791
19
    coef_arrays = jpeg_read_coefficients(&dinfo);
792
19
    if (!coef_arrays) {
793
0
      if (!quiet_mode)
794
0
        fprintf(log_fh, " (failed to read coefficients) ");
795
0
      goto abort_decompress;
796
0
    }
797
19
  }
798
213
  if (!retry) {
799
186
    in_image_size = inbufferused - dinfo.src->bytes_in_buffer;
800
186
    if(verbose_mode > 2)
801
0
      fprintf(log_fh, " (input image size: %lu (%lu))",
802
0
        in_image_size, inbufferused);
803
186
    if (stdin_mode) {
804
0
      insize = in_image_size;
805
186
    } else {
806
186
      if ((insize = file_size(infile)) < 0)
807
0
        fatal("failed to stat() input file");
808
186
      if (in_image_size > 0 && in_image_size < insize) {
809
106
        if (!quiet_mode)
810
106
          fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ",
811
106
            insize - in_image_size);
812
106
        if (nofix_mode)
813
0
          global_error_counter++;
814
106
        if (save_extra) {
815
0
          extrabuffersize = insize - in_image_size;
816
0
          if (extrabuffer)
817
0
            free(extrabuffer);
818
0
          if (!(extrabuffer = calloc(extrabuffersize, 1)))
819
0
            fatal("not enough memory");
820
0
          if (fseek(infile, in_image_size, SEEK_SET))
821
0
            fatal("failed to seek input file");
822
0
          if (fread(extrabuffer, extrabuffersize, 1, infile) != 1)
823
0
            fatal("failed to read inputfile");
824
0
        }
825
106
      }
826
186
    }
827
186
    if (!quiet_mode) {
828
186
      fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] "));
829
186
      fflush(log_fh);
830
186
    }
831
832
186
    if (nofix_mode && global_error_counter != 0) {
833
      /* Skip files containing any errors (or warnings) */
834
0
      goto abort_decompress;
835
0
    }
836
837
186
    if (dest && !noaction) {
838
0
      if (file_exists(newname) && !overwrite_mode) {
839
0
        if (!quiet_mode)
840
0
          fprintf(log_fh, " (target file already exists) ");
841
0
        goto abort_decompress;
842
0
      }
843
0
    }
844
186
  }
845
846
847
  /* Prepare to compress... */
848
213
  if (setjmp(jcerr.setjmp_buffer)) {
849
    /* Error handler for compress failures */
850
31
    if (!quiet_mode)
851
31
      fprintf(log_fh," [Compress ERROR: %s]\n",last_error);
852
31
    jpeg_abort_compress(&cinfo);
853
31
    jpeg_abort_decompress(&dinfo);
854
31
    fclose(infile);
855
31
    free_line_buf(&buf, dinfo.output_height);
856
31
    jcerr.jump_set=0;
857
31
    res = 2;
858
31
    goto exit_point;
859
182
  } else {
860
182
    jcerr.jump_set=1;
861
182
  }
862
863
182
  lastsize = 0;
864
182
  searchdone = 0;
865
186
  if (!retry) {
866
186
    oldquality = 200;
867
186
    if (target_size != 0) {
868
      /* Always start with quality 100 if -S option specified... */
869
186
      quality = 100;
870
186
    }
871
186
  }
872
873
874
951
binary_search_loop:
875
876
  /* Allocate memory buffer that should be large enough to store the output JPEG... */
877
951
  if (outbuffer)
878
765
    free(outbuffer);
879
951
  outbuffersize = insize + 32768;
880
951
  if (!(outbuffer=calloc(outbuffersize, 1)))
881
0
    fatal("not enough memory");
882
883
  /* setup custom "destination manager" for libjpeg to write to our buffer */
884
951
  jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536);
885
886
887
951
  if (quality >= 0 && retry != 1) {
888
    /* Lossy "optimization" ... */
889
890
887
    cinfo.in_color_space=dinfo.out_color_space;
891
887
    cinfo.input_components=dinfo.output_components;
892
887
    cinfo.image_width=dinfo.image_width;
893
887
    cinfo.image_height=dinfo.image_height;
894
887
    jpeg_set_defaults(&cinfo);
895
887
    jpeg_set_quality(&cinfo,quality,TRUE);
896
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
897
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
898
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
899
#endif
900
887
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
901
      /* Explicitly disable progressive mode. */
902
768
      cinfo.scan_info = NULL;
903
768
      cinfo.num_scans = 0;
904
768
    } else if (all_progressive || dinfo.progressive_mode) {
905
      /* Enable progressive mode. */
906
119
      jpeg_simple_progression(&cinfo);
907
119
    }
908
887
    cinfo.optimize_coding = TRUE;
909
887
#ifdef HAVE_ARITH_CODE
910
887
    if (arith_mode >= 0)
911
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
912
887
#endif
913
887
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
914
490
      cinfo.write_JFIF_header = TRUE;
915
490
    } else {
916
397
      cinfo.write_JFIF_header = FALSE;
917
397
    }
918
887
    if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
919
      /* If outputting Adobe marker, don't write JFIF marker... */
920
0
      cinfo.write_JFIF_header = FALSE;
921
0
    }
922
923
887
    jpeg_start_compress(&cinfo,TRUE);
924
925
    /* Write markers */
926
887
    write_markers(&dinfo,&cinfo);
927
928
    /* Write image */
929
1.77k
    while (cinfo.next_scanline < cinfo.image_height) {
930
885
      jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
931
885
          dinfo.output_height);
932
885
    }
933
934
887
  } else {
935
    /* Lossless optimization ... */
936
937
64
    jpeg_copy_critical_parameters(&dinfo, &cinfo);
938
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
939
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
940
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
941
#endif
942
64
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
943
      /* Explicitly disable progressive mode. */
944
54
      cinfo.scan_info = NULL;
945
54
      cinfo.num_scans = 0;
946
54
    } else if (all_progressive || dinfo.progressive_mode) {
947
      /* Enable progressive mode. */
948
3
      jpeg_simple_progression(&cinfo);
949
3
    }
950
64
    cinfo.optimize_coding = TRUE;
951
64
#ifdef HAVE_ARITH_CODE
952
64
    if (arith_mode >= 0)
953
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
954
64
#endif
955
64
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
956
31
      cinfo.write_JFIF_header = TRUE;
957
33
    } else {
958
33
      cinfo.write_JFIF_header = FALSE;
959
33
    }
960
64
    if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
961
      /* If outputting Adobe marker, don't write JFIF marker... */
962
0
      cinfo.write_JFIF_header = FALSE;
963
0
    }
964
965
    /* Write image */
966
64
    jpeg_write_coefficients(&cinfo, coef_arrays);
967
968
    /* Write markers */
969
64
    write_markers(&dinfo,&cinfo);
970
971
64
  }
972
973
951
  jpeg_finish_compress(&cinfo);
974
951
  outsize = outbuffersize + extrabuffersize;
975
951
  if (verbose_mode > 2)
976
0
    fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize);
977
978
951
  if (target_size != 0 && !retry) {
979
    /* Perform (binary) search to try to reach target file size... */
980
981
885
    long osize = outsize/1024;
982
885
    long isize = insize/1024;
983
885
    long tsize = target_size;
984
985
885
    if (verbose_mode > 1)
986
0
      fprintf(log_fh, "(size=%ld)",outsize);
987
885
    if (tsize < 0) {
988
885
      tsize=((-target_size)*insize/100)/1024;
989
885
      if (tsize < 1)
990
182
        tsize = 1;
991
885
    }
992
993
885
    if (osize == tsize || searchdone || tsize > isize) {
994
198
      if (searchdone < 42 && lastsize > 0) {
995
161
        if (labs(osize-tsize) > labs(lastsize-tsize)) {
996
14
          if (verbose_mode)
997
0
            fprintf(log_fh,"(revert to %d)",oldquality);
998
14
          searchdone = 42;
999
14
          quality = oldquality;
1000
14
          goto binary_search_loop;
1001
14
        }
1002
161
      }
1003
184
      if (verbose_mode)
1004
0
        fprintf(log_fh," ");
1005
1006
687
    } else {
1007
687
      int newquality;
1008
687
      double dif = abs(oldquality-quality) / 2.0;
1009
1010
687
      if (osize > tsize)
1011
459
        newquality = quality - dif;
1012
228
      else
1013
228
        newquality = quality + dif + 0.5;
1014
1015
687
      if (dif < 1.0)
1016
26
        searchdone = 1;
1017
687
      if (newquality < 0) {
1018
38
        newquality = 0;
1019
38
        searchdone = 1;
1020
38
      }
1021
687
      if (newquality > 100) {
1022
48
        newquality = 100;
1023
48
        searchdone = 1;
1024
48
      }
1025
1026
687
      oldquality = quality;
1027
687
      quality = newquality;
1028
687
      lastsize = osize;
1029
687
      if (verbose_mode)
1030
0
        fprintf(log_fh,"(try %d)",quality);
1031
687
      goto binary_search_loop;
1032
687
    }
1033
885
  }
1034
1035
250
  jpeg_finish_decompress(&dinfo);
1036
250
  free_line_buf(&buf, dinfo.output_height);
1037
1038
250
  if (retry_mode) {
1039
0
    if ((retry == 0 || retry == 2) && quality >= 0 && outsize <= insize) {
1040
      /* Retry compression until output file stops getting smaller
1041
         or we hit max limit of iterations (10)... */
1042
0
      if (retry_count == 0)
1043
0
        last_retry_size = outsize + 1;
1044
0
      if (++retry_count < 10 && outsize < last_retry_size) {
1045
0
        if (tmpbuffer)
1046
0
          free(tmpbuffer);
1047
0
        tmpbuffer = outbuffer;
1048
0
        tmpbuffersize = outbuffersize;
1049
0
        outbuffer = NULL;
1050
0
        last_retry_size = outsize;
1051
0
        retry = 2;
1052
0
        if (verbose_mode)
1053
0
          fprintf(log_fh, "(retry%d: %lu) ", retry_count, outsize);
1054
0
        goto retry_point;
1055
0
      }
1056
0
    }
1057
0
    if (retry == 2) {
1058
0
      if (verbose_mode)
1059
0
        fprintf(log_fh, "(retry done: %lu) ", outsize);
1060
0
      if (outsize > last_retry_size) {
1061
0
        if (outbuffer)
1062
0
          free(outbuffer);
1063
0
        outbuffer = tmpbuffer;
1064
0
        outbuffersize = tmpbuffersize;
1065
0
        outsize = outbuffersize + extrabuffersize;
1066
0
        tmpbuffer = NULL;
1067
0
      }
1068
0
    }
1069
0
  }
1070
1071
  /* If auto_mode, try both progressive and non-progressive... */
1072
250
  if (auto_mode) {
1073
0
    int newmode = (dinfo.progressive_mode ? 0 : 1);
1074
0
    if (retry != 3) {
1075
0
      if (newmode)
1076
0
        all_progressive = 1;
1077
0
      else
1078
0
        all_normal = 1;
1079
0
      if (tmpbuffer)
1080
0
        free(tmpbuffer);
1081
0
      tmpbuffer = outbuffer;
1082
0
      tmpbuffersize = outbuffersize;
1083
0
      outbuffer = NULL;
1084
0
      last_retry_size = outsize;
1085
0
      retry = 3;
1086
0
      if (verbose_mode)
1087
0
        fprintf(log_fh, "(retry w/%s) ", (newmode ? "progressive" : "normal"));
1088
0
      goto retry_point;
1089
0
    } else {
1090
0
      if (verbose_mode > 1)
1091
0
        fprintf(log_fh, "(automode done: %lu) ", outsize);
1092
0
      auto_mode = 0;
1093
0
      if (outsize > last_retry_size) {
1094
0
        if (verbose_mode)
1095
0
          fprintf(log_fh, "(revert to %s) ", (!newmode ? "progressive" : "normal"));
1096
0
        all_progressive = 0;
1097
0
        all_normal = 0;
1098
0
        if (outbuffer)
1099
0
          free(outbuffer);
1100
0
        outbuffer = tmpbuffer;
1101
0
        outbuffersize = tmpbuffersize;
1102
0
        outsize = outbuffersize + extrabuffersize;
1103
0
        tmpbuffer = NULL;
1104
0
      }
1105
0
    }
1106
0
  }
1107
1108
  /* In case "lossy" compression resulted larger file than original, retry with "lossless"... */
1109
250
  if (quality >= 0 && outsize >= insize && retry != 1) {
1110
65
    retry = 1;
1111
65
    if (verbose_mode)
1112
0
      fprintf(log_fh, "(retry w/lossless) ");
1113
65
    goto retry_point;
1114
65
  }
1115
1116
185
  fclose(infile);
1117
1118
185
  ratio = (insize - outsize) * 100.0 / insize;
1119
185
  if (!quiet_mode || csv)
1120
117
    fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio);
1121
185
  if (rate) {
1122
117
    *rate = (ratio < 0 ? 0.0 : ratio);
1123
117
  }
1124
1125
185
  if ((outsize < insize && ratio >= threshold) || force) {
1126
84
    if (saved) {
1127
84
      *saved = (insize - outsize) / 1024.0;
1128
84
    }
1129
84
    if (!quiet_mode || csv)
1130
84
      fprintf(log_fh,csv ? "optimized\n" : "optimized.\n");
1131
84
    if (noaction) {
1132
0
      res = 0;
1133
0
      goto exit_point;
1134
0
    }
1135
1136
84
    if (stdout_mode) {
1137
0
      outfname=NULL;
1138
0
      set_filemode_binary(stdout);
1139
0
      if (fwrite(outbuffer,outbuffersize,1,stdout) != 1)
1140
0
        fatal("%s, write failed to stdout",(stdin_mode ? "stdin" : filename));
1141
84
    } else {
1142
84
      if (preserve_perms && !dest) {
1143
        /* make backup of the original file */
1144
0
        int newlen = snprintf(tmpfilename, sizeof(tmpfilename),
1145
0
            "%s.jpegoptim.bak", newname);
1146
0
        if (newlen >= sizeof(tmpfilename))
1147
0
          fatal("temp filename too long: %s", tmpfilename);
1148
1149
0
        if (verbose_mode > 1)
1150
0
          fprintf(log_fh,"%s, creating backup as: %s\n",
1151
0
            (stdin_mode ? "stdin" : filename), tmpfilename);
1152
0
        if (file_exists(tmpfilename))
1153
0
          fatal("%s, backup file already exists: %s",
1154
0
            (stdin_mode ?" stdin" : filename), tmpfilename);
1155
0
        if (copy_file(newname,tmpfilename))
1156
0
          fatal("%s, failed to create backup: %s",
1157
0
            (stdin_mode ? "stdin" : filename), tmpfilename);
1158
0
        if ((outfile=create_file(newname))==NULL)
1159
0
          fatal("%s, error opening output file: %s",
1160
0
            (stdin_mode ? "stdin" : filename), newname);
1161
0
        outfname = newname;
1162
84
      } else {
1163
84
        if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename))))
1164
0
          fatal("error creating temporary file: %s", tmpfilename);
1165
84
        outfname = tmpfilename;
1166
84
      }
1167
1168
84
      if (verbose_mode > 1)
1169
0
        fprintf(log_fh,"writing %lu bytes to file: %s\n",
1170
0
          (long unsigned int)outbuffersize, outfname);
1171
84
      if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1)
1172
0
        fatal("write failed to file: %s", outfname);
1173
84
      if (save_extra && extrabuffersize > 0) {
1174
0
        if (verbose_mode > 1)
1175
0
          fprintf(log_fh,"writing %lu bytes to file: %s\n", extrabuffersize, outfname);
1176
0
        if (fwrite(extrabuffer, extrabuffersize, 1, outfile) != 1)
1177
0
          fatal("write failed to file: %s", outfname);
1178
0
      }
1179
84
      fclose(outfile);
1180
84
    }
1181
1182
84
    if (outfname) {
1183
84
      if (preserve_mode) {
1184
        /* preserve file modification time */
1185
0
        if (verbose_mode > 1)
1186
0
          fprintf(log_fh,"set file modification time same as in original: %s\n",
1187
0
            outfname);
1188
0
#if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM)
1189
0
        struct timespec time_save[2];
1190
0
        time_save[0].tv_sec = 0;
1191
0
        time_save[0].tv_nsec = UTIME_OMIT; /* omit atime */
1192
0
        time_save[1] = file_stat->st_mtim;
1193
0
        if (utimensat(AT_FDCWD,outfname,time_save,0) != 0)
1194
0
          warn("failed to reset output file time/date");
1195
#else
1196
        struct utimbuf time_save;
1197
        time_save.actime=file_stat->st_atime;
1198
        time_save.modtime=file_stat->st_mtime;
1199
        if (utime(outfname,&time_save) != 0)
1200
          warn("failed to reset output file time/date");
1201
#endif
1202
0
      }
1203
1204
84
      if (preserve_perms && !dest) {
1205
        /* original file was already replaced, remove backup... */
1206
0
        if (verbose_mode > 1)
1207
0
          fprintf(log_fh,"removing backup file: %s\n", tmpfilename);
1208
0
        if (delete_file(tmpfilename))
1209
0
          warn("failed to remove backup file: %s", tmpfilename);
1210
84
      } else {
1211
        /* make temp file to be the original file... */
1212
1213
        /* preserve file mode */
1214
84
        if (chmod(outfname,(file_stat->st_mode & 0777)) != 0)
1215
0
          warn("failed to set output file mode");
1216
1217
        /* preserve file group (and owner if run by root) */
1218
84
        if (chown(outfname,
1219
84
            (geteuid()==0 ? file_stat->st_uid : -1),
1220
84
            file_stat->st_gid) != 0)
1221
0
          warn("failed to reset output file group/owner");
1222
1223
84
        if (verbose_mode > 1)
1224
0
          fprintf(log_fh,"renaming: %s to %s\n", outfname, newname);
1225
84
        if (rename_file(outfname, newname))
1226
0
          fatal("cannot rename temp file");
1227
84
      }
1228
84
    }
1229
101
  } else {
1230
101
    if (!quiet_mode || csv)
1231
33
      fprintf(log_fh,csv ? "skipped\n" : "skipped.\n");
1232
101
    if (stdout_mode) {
1233
0
      set_filemode_binary(stdout);
1234
0
      if (fwrite(inbuffer, in_image_size, 1, stdout) != 1)
1235
0
        fatal("%s, write failed to stdout",
1236
0
          (stdin_mode ? "stdin" : filename));
1237
0
    }
1238
101
  }
1239
1240
185
  res = 0;
1241
1242
209
 exit_point:
1243
209
  if (inbuffer)
1244
209
    free(inbuffer);
1245
209
  if (outbuffer)
1246
186
    free(outbuffer);
1247
209
  if (tmpbuffer)
1248
0
    free(tmpbuffer);
1249
209
  if (extrabuffer)
1250
0
    free(extrabuffer);
1251
209
  jpeg_destroy_compress(&cinfo);
1252
209
  jpeg_destroy_decompress(&dinfo);
1253
1254
209
  return res;
1255
185
}
optimize
Line
Count
Source
635
185
{
636
185
  FILE *infile = NULL;
637
185
  FILE *outfile = NULL;
638
185
  const char *outfname = NULL;
639
185
  char tmpfilename[MAXPATHLEN];
640
185
  struct jpeg_decompress_struct dinfo;
641
185
  struct jpeg_compress_struct cinfo;
642
185
  struct my_error_mgr jcerr, jderr;
643
185
  JSAMPARRAY buf = NULL;
644
645
185
  unsigned char *outbuffer = NULL;
646
185
  size_t outbuffersize = 0;
647
185
  unsigned char *inbuffer = NULL;
648
185
  size_t inbuffersize = 0;
649
185
  size_t inbufferused = 0;
650
185
  unsigned char *tmpbuffer = NULL;
651
185
  size_t tmpbuffersize = 0;
652
185
  unsigned char *extrabuffer = NULL;
653
185
  size_t extrabuffersize = 0;
654
655
185
  jvirt_barray_ptr *coef_arrays = NULL;
656
185
  char marker_str[256];
657
185
  unsigned int marker_in_count, marker_in_size;
658
659
185
  long in_image_size = 0;
660
185
  long insize = 0, outsize = 0, lastsize = 0;
661
185
  int oldquality, searchdone;
662
185
  double ratio;
663
185
  size_t last_retry_size = 0;
664
185
  int retry_count = 0;
665
185
  int retry = 0;
666
185
  int res = -1;
667
668
185
  jpeg_log_fh = log_fh;
669
670
  /* Initialize decompression object */
671
185
  dinfo.err = jpeg_std_error(&jderr.pub);
672
185
  jpeg_create_decompress(&dinfo);
673
185
  jderr.pub.error_exit=my_error_exit;
674
185
  jderr.pub.output_message=my_output_message;
675
185
  jderr.jump_set = 0;
676
677
  /* Initialize compression object */
678
185
  cinfo.err = jpeg_std_error(&jcerr.pub);
679
185
  jpeg_create_compress(&cinfo);
680
185
  jcerr.pub.error_exit=my_error_exit;
681
185
  jcerr.pub.output_message=my_output_message;
682
185
  jcerr.jump_set = 0;
683
684
185
  if (rate)
685
185
    *rate = 0.0;
686
185
  if (saved)
687
185
    *saved = 0.0;
688
689
185
  if (filename) {
690
185
    if ((infile = fopen(filename, "rb")) == NULL) {
691
0
      warn("cannot open file: %s", filename);
692
0
      res = 1;
693
0
      goto exit_point;
694
0
    }
695
185
  } else {
696
0
    infile = stdin;
697
0
    set_filemode_binary(infile);
698
0
  }
699
700
258
retry_point:
701
702
258
  if (setjmp(jderr.setjmp_buffer)) {
703
    /* Error handler for decompress */
704
39
  abort_decompress:
705
39
    jpeg_abort_decompress(&dinfo);
706
39
    fclose(infile);
707
39
    free_line_buf(&buf, dinfo.output_height);
708
39
    if (!quiet_mode || csv)
709
39
      fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n");
710
39
    jderr.jump_set=0;
711
39
    res = 1;
712
39
    goto exit_point;
713
219
  } else {
714
219
    jderr.jump_set=1;
715
219
  }
716
717
718
  /* Prepare to decompress */
719
219
  if (!retry) {
720
185
    if (!quiet_mode || csv) {
721
185
      fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin"));
722
185
      fflush(log_fh);
723
185
    }
724
725
185
    if (stdin_mode || stdout_mode) {
726
0
      inbuffersize = IN_BUF_SIZE;
727
185
    } else {
728
185
      if ((inbuffersize = file_size(infile)) < IN_BUF_SIZE)
729
172
        inbuffersize = IN_BUF_SIZE;
730
185
    }
731
185
    if (inbuffer)
732
0
      free(inbuffer);
733
185
    if (!(inbuffer=calloc(inbuffersize, 1)))
734
0
      fatal("not enough memory");
735
185
  }
736
219
  global_error_counter=0;
737
219
  jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
738
4.34k
  for (int i = 0; i < 16; i++) {
739
4.12k
    jpeg_save_markers(&dinfo, JPEG_APP0 + i, 0xffff);
740
4.12k
  }
741
219
  if (!retry) {
742
185
    jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, IN_BUF_SIZE);
743
185
  } else {
744
34
    if (retry == 1)
745
73
      jpeg_custom_mem_src(&dinfo, inbuffer, inbufferused);
746
18.4E
    else
747
18.4E
      jpeg_custom_mem_src(&dinfo, tmpbuffer, tmpbuffersize);
748
34
  }
749
219
  jpeg_read_header(&dinfo, TRUE);
750
751
752
  /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */
753
219
  marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str),
754
219
          &marker_in_size);
755
756
219
  if (!retry) {
757
171
    if (verbose_mode > 1) {
758
0
      fprintf(log_fh,"%d markers found in input file (total size %d bytes)\n",
759
0
        marker_in_count,marker_in_size);
760
0
      fprintf(log_fh,"coding: %s\n", (dinfo.arith_code == TRUE ? "Arithmetic" : "Huffman"));
761
0
    }
762
763
171
    if (!quiet_mode || csv) {
764
171
      fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width,
765
171
        (int)dinfo.image_height,(int)dinfo.num_components*8,
766
171
        (dinfo.progressive_mode?'P':'N'));
767
171
      if (!csv)
768
171
        fprintf(log_fh,"%s",marker_str);
769
171
      fflush(log_fh);
770
171
    }
771
171
  }
772
773
  /* Decompress the image */
774
243
  if (quality >= 0 && retry != 1) {
775
170
    jpeg_start_decompress(&dinfo);
776
777
    /* Allocate line buffer to store the decompressed image */
778
170
    if (!(buf = calloc(dinfo.output_height, sizeof(JSAMPROW))))
779
0
      fatal("not enough memory");
780
380k
    for (int i = 0; i < dinfo.output_height; i++) {
781
380k
      if (!(buf[i]=calloc((size_t)dinfo.output_width * dinfo.out_color_components,
782
380k
              sizeof(JSAMPLE))))
783
0
        fatal("not enough memory");
784
380k
    }
785
786
175k
    while (dinfo.output_scanline < dinfo.output_height) {
787
175k
      jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
788
175k
          dinfo.output_height-dinfo.output_scanline);
789
175k
    }
790
170
  } else {
791
49
    coef_arrays = jpeg_read_coefficients(&dinfo);
792
49
    if (!coef_arrays) {
793
0
      if (!quiet_mode)
794
0
        fprintf(log_fh, " (failed to read coefficients) ");
795
0
      goto abort_decompress;
796
0
    }
797
49
  }
798
219
  if (!retry) {
799
163
    in_image_size = inbufferused - dinfo.src->bytes_in_buffer;
800
163
    if(verbose_mode > 2)
801
0
      fprintf(log_fh, " (input image size: %lu (%lu))",
802
0
        in_image_size, inbufferused);
803
163
    if (stdin_mode) {
804
0
      insize = in_image_size;
805
163
    } else {
806
163
      if ((insize = file_size(infile)) < 0)
807
0
        fatal("failed to stat() input file");
808
163
      if (in_image_size > 0 && in_image_size < insize) {
809
51
        if (!quiet_mode)
810
51
          fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ",
811
51
            insize - in_image_size);
812
51
        if (nofix_mode)
813
0
          global_error_counter++;
814
51
        if (save_extra) {
815
0
          extrabuffersize = insize - in_image_size;
816
0
          if (extrabuffer)
817
0
            free(extrabuffer);
818
0
          if (!(extrabuffer = calloc(extrabuffersize, 1)))
819
0
            fatal("not enough memory");
820
0
          if (fseek(infile, in_image_size, SEEK_SET))
821
0
            fatal("failed to seek input file");
822
0
          if (fread(extrabuffer, extrabuffersize, 1, infile) != 1)
823
0
            fatal("failed to read inputfile");
824
0
        }
825
51
      }
826
163
    }
827
163
    if (!quiet_mode) {
828
163
      fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] "));
829
163
      fflush(log_fh);
830
163
    }
831
832
163
    if (nofix_mode && global_error_counter != 0) {
833
      /* Skip files containing any errors (or warnings) */
834
0
      goto abort_decompress;
835
0
    }
836
837
163
    if (dest && !noaction) {
838
0
      if (file_exists(newname) && !overwrite_mode) {
839
0
        if (!quiet_mode)
840
0
          fprintf(log_fh, " (target file already exists) ");
841
0
        goto abort_decompress;
842
0
      }
843
0
    }
844
163
  }
845
846
847
  /* Prepare to compress... */
848
219
  if (setjmp(jcerr.setjmp_buffer)) {
849
    /* Error handler for compress failures */
850
42
    if (!quiet_mode)
851
42
      fprintf(log_fh," [Compress ERROR: %s]\n",last_error);
852
42
    jpeg_abort_compress(&cinfo);
853
42
    jpeg_abort_decompress(&dinfo);
854
42
    fclose(infile);
855
42
    free_line_buf(&buf, dinfo.output_height);
856
42
    jcerr.jump_set=0;
857
42
    res = 2;
858
42
    goto exit_point;
859
177
  } else {
860
177
    jcerr.jump_set=1;
861
177
  }
862
863
177
  lastsize = 0;
864
177
  searchdone = 0;
865
177
  if (!retry) {
866
163
    oldquality = 200;
867
163
    if (target_size != 0) {
868
      /* Always start with quality 100 if -S option specified... */
869
163
      quality = 100;
870
163
    }
871
163
  }
872
873
874
814
binary_search_loop:
875
876
  /* Allocate memory buffer that should be large enough to store the output JPEG... */
877
814
  if (outbuffer)
878
651
    free(outbuffer);
879
814
  outbuffersize = insize + 32768;
880
814
  if (!(outbuffer=calloc(outbuffersize, 1)))
881
0
    fatal("not enough memory");
882
883
  /* setup custom "destination manager" for libjpeg to write to our buffer */
884
814
  jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536);
885
886
887
814
  if (quality >= 0 && retry != 1) {
888
    /* Lossy "optimization" ... */
889
890
742
    cinfo.in_color_space=dinfo.out_color_space;
891
742
    cinfo.input_components=dinfo.output_components;
892
742
    cinfo.image_width=dinfo.image_width;
893
742
    cinfo.image_height=dinfo.image_height;
894
742
    jpeg_set_defaults(&cinfo);
895
742
    jpeg_set_quality(&cinfo,quality,TRUE);
896
742
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
897
742
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
898
742
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
899
742
#endif
900
742
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
901
      /* Explicitly disable progressive mode. */
902
385
      cinfo.scan_info = NULL;
903
385
      cinfo.num_scans = 0;
904
385
    } else if (all_progressive || dinfo.progressive_mode) {
905
      /* Enable progressive mode. */
906
357
      jpeg_simple_progression(&cinfo);
907
357
    }
908
742
    cinfo.optimize_coding = TRUE;
909
742
#ifdef HAVE_ARITH_CODE
910
742
    if (arith_mode >= 0)
911
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
912
742
#endif
913
742
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
914
362
      cinfo.write_JFIF_header = TRUE;
915
380
    } else {
916
380
      cinfo.write_JFIF_header = FALSE;
917
380
    }
918
742
    if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
919
      /* If outputting Adobe marker, don't write JFIF marker... */
920
0
      cinfo.write_JFIF_header = FALSE;
921
0
    }
922
923
742
    jpeg_start_compress(&cinfo,TRUE);
924
925
    /* Write markers */
926
742
    write_markers(&dinfo,&cinfo);
927
928
    /* Write image */
929
1.48k
    while (cinfo.next_scanline < cinfo.image_height) {
930
741
      jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
931
741
          dinfo.output_height);
932
741
    }
933
934
742
  } else {
935
    /* Lossless optimization ... */
936
937
72
    jpeg_copy_critical_parameters(&dinfo, &cinfo);
938
72
#ifdef HAVE_JINT_DC_SCAN_OPT_MODE
939
72
    if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
940
61
      jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
941
72
#endif
942
72
    if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
943
      /* Explicitly disable progressive mode. */
944
28
      cinfo.scan_info = NULL;
945
28
      cinfo.num_scans = 0;
946
44
    } else if (all_progressive || dinfo.progressive_mode) {
947
      /* Enable progressive mode. */
948
33
      jpeg_simple_progression(&cinfo);
949
33
    }
950
72
    cinfo.optimize_coding = TRUE;
951
72
#ifdef HAVE_ARITH_CODE
952
72
    if (arith_mode >= 0)
953
0
      cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
954
72
#endif
955
72
    if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
956
26
      cinfo.write_JFIF_header = TRUE;
957
46
    } else {
958
46
      cinfo.write_JFIF_header = FALSE;
959
46
    }
960
72
    if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
961
      /* If outputting Adobe marker, don't write JFIF marker... */
962
0
      cinfo.write_JFIF_header = FALSE;
963
0
    }
964
965
    /* Write image */
966
72
    jpeg_write_coefficients(&cinfo, coef_arrays);
967
968
    /* Write markers */
969
72
    write_markers(&dinfo,&cinfo);
970
971
72
  }
972
973
814
  jpeg_finish_compress(&cinfo);
974
814
  outsize = outbuffersize + extrabuffersize;
975
814
  if (verbose_mode > 2)
976
0
    fprintf(log_fh, " (output image size: %lu (%lu))", outsize,extrabuffersize);
977
978
814
  if (target_size != 0 && !retry) {
979
    /* Perform (binary) search to try to reach target file size... */
980
981
741
    long osize = outsize/1024;
982
741
    long isize = insize/1024;
983
741
    long tsize = target_size;
984
985
741
    if (verbose_mode > 1)
986
0
      fprintf(log_fh, "(size=%ld)",outsize);
987
741
    if (tsize < 0) {
988
741
      tsize=((-target_size)*insize/100)/1024;
989
741
      if (tsize < 1)
990
222
        tsize = 1;
991
741
    }
992
993
741
    if (osize == tsize || searchdone || tsize > isize) {
994
174
      if (searchdone < 42 && lastsize > 0) {
995
136
        if (labs(osize-tsize) > labs(lastsize-tsize)) {
996
12
          if (verbose_mode)
997
0
            fprintf(log_fh,"(revert to %d)",oldquality);
998
12
          searchdone = 42;
999
12
          quality = oldquality;
1000
12
          goto binary_search_loop;
1001
12
        }
1002
136
      }
1003
162
      if (verbose_mode)
1004
0
        fprintf(log_fh," ");
1005
1006
567
    } else {
1007
567
      int newquality;
1008
567
      double dif = abs(oldquality-quality) / 2.0;
1009
1010
567
      if (osize > tsize)
1011
427
        newquality = quality - dif;
1012
140
      else
1013
140
        newquality = quality + dif + 0.5;
1014
1015
567
      if (dif < 1.0)
1016
9
        searchdone = 1;
1017
567
      if (newquality < 0) {
1018
52
        newquality = 0;
1019
52
        searchdone = 1;
1020
52
      }
1021
567
      if (newquality > 100) {
1022
60
        newquality = 100;
1023
60
        searchdone = 1;
1024
60
      }
1025
1026
567
      oldquality = quality;
1027
567
      quality = newquality;
1028
567
      lastsize = osize;
1029
567
      if (verbose_mode)
1030
0
        fprintf(log_fh,"(try %d)",quality);
1031
567
      goto binary_search_loop;
1032
567
    }
1033
741
  }
1034
1035
235
  jpeg_finish_decompress(&dinfo);
1036
235
  free_line_buf(&buf, dinfo.output_height);
1037
1038
235
  if (retry_mode) {
1039
0
    if ((retry == 0 || retry == 2) && quality >= 0 && outsize <= insize) {
1040
      /* Retry compression until output file stops getting smaller
1041
         or we hit max limit of iterations (10)... */
1042
0
      if (retry_count == 0)
1043
0
        last_retry_size = outsize + 1;
1044
0
      if (++retry_count < 10 && outsize < last_retry_size) {
1045
0
        if (tmpbuffer)
1046
0
          free(tmpbuffer);
1047
0
        tmpbuffer = outbuffer;
1048
0
        tmpbuffersize = outbuffersize;
1049
0
        outbuffer = NULL;
1050
0
        last_retry_size = outsize;
1051
0
        retry = 2;
1052
0
        if (verbose_mode)
1053
0
          fprintf(log_fh, "(retry%d: %lu) ", retry_count, outsize);
1054
0
        goto retry_point;
1055
0
      }
1056
0
    }
1057
0
    if (retry == 2) {
1058
0
      if (verbose_mode)
1059
0
        fprintf(log_fh, "(retry done: %lu) ", outsize);
1060
0
      if (outsize > last_retry_size) {
1061
0
        if (outbuffer)
1062
0
          free(outbuffer);
1063
0
        outbuffer = tmpbuffer;
1064
0
        outbuffersize = tmpbuffersize;
1065
0
        outsize = outbuffersize + extrabuffersize;
1066
0
        tmpbuffer = NULL;
1067
0
      }
1068
0
    }
1069
0
  }
1070
1071
  /* If auto_mode, try both progressive and non-progressive... */
1072
235
  if (auto_mode) {
1073
0
    int newmode = (dinfo.progressive_mode ? 0 : 1);
1074
0
    if (retry != 3) {
1075
0
      if (newmode)
1076
0
        all_progressive = 1;
1077
0
      else
1078
0
        all_normal = 1;
1079
0
      if (tmpbuffer)
1080
0
        free(tmpbuffer);
1081
0
      tmpbuffer = outbuffer;
1082
0
      tmpbuffersize = outbuffersize;
1083
0
      outbuffer = NULL;
1084
0
      last_retry_size = outsize;
1085
0
      retry = 3;
1086
0
      if (verbose_mode)
1087
0
        fprintf(log_fh, "(retry w/%s) ", (newmode ? "progressive" : "normal"));
1088
0
      goto retry_point;
1089
0
    } else {
1090
0
      if (verbose_mode > 1)
1091
0
        fprintf(log_fh, "(automode done: %lu) ", outsize);
1092
0
      auto_mode = 0;
1093
0
      if (outsize > last_retry_size) {
1094
0
        if (verbose_mode)
1095
0
          fprintf(log_fh, "(revert to %s) ", (!newmode ? "progressive" : "normal"));
1096
0
        all_progressive = 0;
1097
0
        all_normal = 0;
1098
0
        if (outbuffer)
1099
0
          free(outbuffer);
1100
0
        outbuffer = tmpbuffer;
1101
0
        outbuffersize = tmpbuffersize;
1102
0
        outsize = outbuffersize + extrabuffersize;
1103
0
        tmpbuffer = NULL;
1104
0
      }
1105
0
    }
1106
0
  }
1107
1108
  /* In case "lossy" compression resulted larger file than original, retry with "lossless"... */
1109
235
  if (quality >= 0 && outsize >= insize && retry != 1) {
1110
73
    retry = 1;
1111
73
    if (verbose_mode)
1112
0
      fprintf(log_fh, "(retry w/lossless) ");
1113
73
    goto retry_point;
1114
73
  }
1115
1116
162
  fclose(infile);
1117
1118
162
  ratio = (insize - outsize) * 100.0 / insize;
1119
162
  if (!quiet_mode || csv)
1120
104
    fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio);
1121
162
  if (rate) {
1122
104
    *rate = (ratio < 0 ? 0.0 : ratio);
1123
104
  }
1124
1125
162
  if ((outsize < insize && ratio >= threshold) || force) {
1126
76
    if (saved) {
1127
76
      *saved = (insize - outsize) / 1024.0;
1128
76
    }
1129
76
    if (!quiet_mode || csv)
1130
76
      fprintf(log_fh,csv ? "optimized\n" : "optimized.\n");
1131
76
    if (noaction) {
1132
0
      res = 0;
1133
0
      goto exit_point;
1134
0
    }
1135
1136
76
    if (stdout_mode) {
1137
0
      outfname=NULL;
1138
0
      set_filemode_binary(stdout);
1139
0
      if (fwrite(outbuffer,outbuffersize,1,stdout) != 1)
1140
0
        fatal("%s, write failed to stdout",(stdin_mode ? "stdin" : filename));
1141
76
    } else {
1142
76
      if (preserve_perms && !dest) {
1143
        /* make backup of the original file */
1144
0
        int newlen = snprintf(tmpfilename, sizeof(tmpfilename),
1145
0
            "%s.jpegoptim.bak", newname);
1146
0
        if (newlen >= sizeof(tmpfilename))
1147
0
          fatal("temp filename too long: %s", tmpfilename);
1148
1149
0
        if (verbose_mode > 1)
1150
0
          fprintf(log_fh,"%s, creating backup as: %s\n",
1151
0
            (stdin_mode ? "stdin" : filename), tmpfilename);
1152
0
        if (file_exists(tmpfilename))
1153
0
          fatal("%s, backup file already exists: %s",
1154
0
            (stdin_mode ?" stdin" : filename), tmpfilename);
1155
0
        if (copy_file(newname,tmpfilename))
1156
0
          fatal("%s, failed to create backup: %s",
1157
0
            (stdin_mode ? "stdin" : filename), tmpfilename);
1158
0
        if ((outfile=create_file(newname))==NULL)
1159
0
          fatal("%s, error opening output file: %s",
1160
0
            (stdin_mode ? "stdin" : filename), newname);
1161
0
        outfname = newname;
1162
76
      } else {
1163
76
        if (!(outfile = create_temp_file(tmpdir, "jpegoptim", tmpfilename, sizeof(tmpfilename))))
1164
0
          fatal("error creating temporary file: %s", tmpfilename);
1165
76
        outfname = tmpfilename;
1166
76
      }
1167
1168
76
      if (verbose_mode > 1)
1169
0
        fprintf(log_fh,"writing %lu bytes to file: %s\n",
1170
0
          (long unsigned int)outbuffersize, outfname);
1171
76
      if (fwrite(outbuffer, outbuffersize, 1, outfile) != 1)
1172
0
        fatal("write failed to file: %s", outfname);
1173
76
      if (save_extra && extrabuffersize > 0) {
1174
0
        if (verbose_mode > 1)
1175
0
          fprintf(log_fh,"writing %lu bytes to file: %s\n", extrabuffersize, outfname);
1176
0
        if (fwrite(extrabuffer, extrabuffersize, 1, outfile) != 1)
1177
0
          fatal("write failed to file: %s", outfname);
1178
0
      }
1179
76
      fclose(outfile);
1180
76
    }
1181
1182
76
    if (outfname) {
1183
76
      if (preserve_mode) {
1184
        /* preserve file modification time */
1185
0
        if (verbose_mode > 1)
1186
0
          fprintf(log_fh,"set file modification time same as in original: %s\n",
1187
0
            outfname);
1188
0
#if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM)
1189
0
        struct timespec time_save[2];
1190
0
        time_save[0].tv_sec = 0;
1191
0
        time_save[0].tv_nsec = UTIME_OMIT; /* omit atime */
1192
0
        time_save[1] = file_stat->st_mtim;
1193
0
        if (utimensat(AT_FDCWD,outfname,time_save,0) != 0)
1194
0
          warn("failed to reset output file time/date");
1195
#else
1196
        struct utimbuf time_save;
1197
        time_save.actime=file_stat->st_atime;
1198
        time_save.modtime=file_stat->st_mtime;
1199
        if (utime(outfname,&time_save) != 0)
1200
          warn("failed to reset output file time/date");
1201
#endif
1202
0
      }
1203
1204
76
      if (preserve_perms && !dest) {
1205
        /* original file was already replaced, remove backup... */
1206
0
        if (verbose_mode > 1)
1207
0
          fprintf(log_fh,"removing backup file: %s\n", tmpfilename);
1208
0
        if (delete_file(tmpfilename))
1209
0
          warn("failed to remove backup file: %s", tmpfilename);
1210
76
      } else {
1211
        /* make temp file to be the original file... */
1212
1213
        /* preserve file mode */
1214
76
        if (chmod(outfname,(file_stat->st_mode & 0777)) != 0)
1215
0
          warn("failed to set output file mode");
1216
1217
        /* preserve file group (and owner if run by root) */
1218
76
        if (chown(outfname,
1219
76
            (geteuid()==0 ? file_stat->st_uid : -1),
1220
76
            file_stat->st_gid) != 0)
1221
0
          warn("failed to reset output file group/owner");
1222
1223
76
        if (verbose_mode > 1)
1224
0
          fprintf(log_fh,"renaming: %s to %s\n", outfname, newname);
1225
76
        if (rename_file(outfname, newname))
1226
0
          fatal("cannot rename temp file");
1227
76
      }
1228
76
    }
1229
86
  } else {
1230
86
    if (!quiet_mode || csv)
1231
28
      fprintf(log_fh,csv ? "skipped\n" : "skipped.\n");
1232
86
    if (stdout_mode) {
1233
0
      set_filemode_binary(stdout);
1234
0
      if (fwrite(inbuffer, in_image_size, 1, stdout) != 1)
1235
0
        fatal("%s, write failed to stdout",
1236
0
          (stdin_mode ? "stdin" : filename));
1237
0
    }
1238
86
  }
1239
1240
162
  res = 0;
1241
1242
185
 exit_point:
1243
185
  if (inbuffer)
1244
185
    free(inbuffer);
1245
185
  if (outbuffer)
1246
163
    free(outbuffer);
1247
185
  if (tmpbuffer)
1248
0
    free(tmpbuffer);
1249
185
  if (extrabuffer)
1250
0
    free(extrabuffer);
1251
185
  jpeg_destroy_compress(&cinfo);
1252
185
  jpeg_destroy_decompress(&dinfo);
1253
1254
185
  return res;
1255
162
}
1256
1257
1258
#ifdef PARALLEL_PROCESSING
1259
int wait_for_worker(FILE *log_fh)
1260
0
{
1261
0
  FILE *p;
1262
0
  struct worker *w;
1263
0
  char buf[1024];
1264
0
  int wstatus;
1265
0
  pid_t pid;
1266
0
  int j, e;
1267
0
  int state = 0;
1268
0
  double val;
1269
0
  double rate = 0.0;
1270
0
  double saved = 0.0;
1271
1272
1273
0
  if ((pid = wait(&wstatus)) < 0)
1274
0
    return pid;
1275
1276
0
  w = NULL;
1277
0
  for (j = 0; j < MAX_WORKERS; j++) {
1278
0
    if (workers[j].pid == pid) {
1279
0
      w = &workers[j];
1280
0
      break;
1281
0
    }
1282
0
  }
1283
0
  if (!w)
1284
0
    fatal("Unknown worker[%d] process found\n", pid);
1285
1286
0
  if (WIFEXITED(wstatus)) {
1287
0
    e = WEXITSTATUS(wstatus);
1288
0
    if (verbose_mode)
1289
0
      fprintf(log_fh, "worker[%d] [slot=%d] exited: %d\n",
1290
0
        pid, j, e);
1291
0
    if (e == 0) {
1292
      //average_count++;
1293
      //average_rate += rate;
1294
      //total_save += saved;
1295
0
    } else if (e == 1) {
1296
0
      decompress_err_count++;
1297
0
    } else if (e == 2) {
1298
0
      compress_err_count++;
1299
0
    }
1300
0
  } else {
1301
0
    fatal("worker[%d] killed", pid);
1302
0
  }
1303
1304
0
  p = fdopen(w->read_pipe, "r");
1305
0
  if (!p) fatal("fdopen failed()");
1306
0
  while (fgets(buf, sizeof(buf), p)) {
1307
0
    if (verbose_mode > 2)
1308
0
      fprintf(log_fh, "worker[%d] PIPE: %s", pid, buf);
1309
0
    if (state == 0 && buf[0] == '\n') {
1310
0
      state=1;
1311
0
      continue;
1312
0
    }
1313
0
    if (state == 1 && !strncmp(buf, "STAT", 4)) {
1314
0
      state=2;
1315
0
      continue;
1316
0
    }
1317
0
    if (state >= 2) {
1318
0
      if (sscanf(buf, "%lf", &val) == 1) {
1319
0
        if (state == 2) {
1320
0
          rate = val;
1321
0
        }
1322
0
        else if (state == 3) {
1323
0
          saved = val;
1324
0
          average_count++;
1325
0
          average_rate += rate;
1326
0
          total_save += saved;
1327
0
        }
1328
0
      }
1329
0
      state++;
1330
0
      continue;
1331
0
    }
1332
0
    if (state == 0)
1333
0
      fprintf(log_fh, "%s", buf);
1334
0
  }
1335
0
  close(w->read_pipe);
1336
0
  w->pid = -1;
1337
0
  w->read_pipe = -1;
1338
0
  worker_count --;
1339
1340
0
  return pid;
1341
0
}
1342
#endif
1343
1344
1345
#ifndef BUILD_FOR_OSS_FUZZ // Libfuzzer provides its own fuzzer
1346
/****************************************************************************/
1347
int main(int argc, char **argv)
1348
{
1349
  struct stat file_stat;
1350
  char tmpfilename[MAXPATHLEN + 1],tmpdir[MAXPATHLEN + 1];
1351
  char newname[MAXPATHLEN + 1], dest_path[MAXPATHLEN + 1];
1352
  char namebuf[MAXPATHLEN + 2];
1353
  const char *filename;
1354
  int arg_idx;
1355
  int res;
1356
  double rate, saved;
1357
  FILE *log_fh;
1358
#ifdef PARALLEL_PROCESSING
1359
  struct worker *w;
1360
  int pipe_fd[2];
1361
  pid_t pid;
1362
1363
1364
  /* Allocate table to keep track of child processes... */
1365
  if (!(workers = calloc(MAX_WORKERS, sizeof(struct worker))))
1366
    fatal("not enough memory");
1367
  for (int i = 0; i < MAX_WORKERS; i++) {
1368
    workers[i].pid = -1;
1369
    workers[i].read_pipe = -1;
1370
  }
1371
#endif
1372
1373
  umask(077);
1374
  signal(SIGINT,own_signal_handler);
1375
  signal(SIGTERM,own_signal_handler);
1376
1377
  /* Parse command line parameters */
1378
  parse_arguments(argc, argv, dest_path, sizeof(dest_path));
1379
  log_fh = (stdout_mode ? stderr : stdout);
1380
  if (quiet_mode)
1381
    verbose_mode = 0;
1382
1383
  if (verbose_mode) {
1384
    if (quality >= 0 && target_size == 0)
1385
      fprintf(log_fh, "Image quality limit set to: %d\n", quality);
1386
    if (threshold >= 0)
1387
      fprintf(log_fh, "Compression threshold (%%) set to: %0.1lf\n", threshold);
1388
    if (all_normal)
1389
      fprintf(log_fh, "All output files will be non-progressive\n");
1390
    if (all_progressive)
1391
      fprintf(log_fh, "All output files will be progressive\n");
1392
    if (target_size > 0)
1393
      fprintf(log_fh, "Target size for output files set to: %d Kbytes.\n",
1394
        target_size);
1395
    if (target_size < 0)
1396
      fprintf(log_fh, "Target size for output files set to: %d%%\n",
1397
        -target_size);
1398
#ifdef PARALLEL_PROCESSING
1399
    if (max_workers > 0)
1400
      fprintf(log_fh, "Using maximum of %d parallel threads\n", max_workers);
1401
#endif
1402
  }
1403
1404
1405
  if (stdin_mode) {
1406
    /* Process just one file, if source is stdin... */
1407
    res = optimize(stderr, NULL, NULL, NULL, &file_stat, NULL, NULL);
1408
    return (res == 0 ? 0 : 1);
1409
  }
1410
1411
  arg_idx = (optind > 0 ? optind : 1);
1412
  if (files_from == NULL && argc <= arg_idx) {
1413
    if (!quiet_mode)
1414
      fprintf(stderr, PROGRAMNAME ": file argument(s) missing\n"
1415
        "Try '" PROGRAMNAME " --help' for more information.\n");
1416
    exit(1);
1417
  }
1418
1419
1420
  /* Main loop to process input files */
1421
  do {
1422
    if (files_from) {
1423
      if (!fgetstr(namebuf, sizeof(namebuf), files_from))
1424
        break;
1425
      filename = namebuf;
1426
    } else {
1427
      filename = argv[arg_idx];
1428
    }
1429
1430
    if (*filename == 0)
1431
      continue;
1432
    if (verbose_mode > 1)
1433
      fprintf(log_fh, "processing file: %s\n", filename);
1434
    if (strnlen(filename, MAXPATHLEN + 1) > MAXPATHLEN) {
1435
      warn("skipping too long filename: %s", filename);
1436
      continue;
1437
    }
1438
1439
    if (!noaction) {
1440
      /* generate tmp dir & new filename */
1441
      if (dest) {
1442
        strncopy(tmpdir, dest_path, sizeof(tmpdir));
1443
        strncopy(newname, dest_path, sizeof(newname));
1444
        if (!splitname(filename, tmpfilename, sizeof(tmpfilename)))
1445
          fatal("splitname() failed for: %s", filename);
1446
        strncatenate(newname, tmpfilename, sizeof(newname));
1447
      } else {
1448
        if (!splitdir(filename, tmpdir, sizeof(tmpdir)))
1449
          fatal("splitdir() failed for: %s", filename);
1450
        strncopy(newname, filename, sizeof(newname));
1451
      }
1452
    }
1453
1454
    if (file_exists(filename)) {
1455
      if (!is_file(filename, &file_stat)) {
1456
        if (is_directory(filename))
1457
          warn("skipping directory: %s", filename);
1458
        else
1459
          warn("skipping special file: %s", filename);
1460
        continue;
1461
      }
1462
    } else {
1463
      warn("file not found: %s", filename);
1464
      continue;
1465
    }
1466
1467
#ifdef PARALLEL_PROCESSING
1468
    if (max_workers > 1) {
1469
      /* Multi process mode, run up to max_workers processes simultaneously... */
1470
1471
      if (worker_count >= max_workers) {
1472
        // wait for a worker to exit...
1473
        wait_for_worker(log_fh);
1474
      }
1475
      if (pipe(pipe_fd) < 0)
1476
        fatal("failed to open pipe");
1477
      pid = fork();
1478
      if (pid < 0)
1479
        fatal("fork() failed");
1480
      if (pid == 0) {
1481
        /* Child process starts here... */
1482
        if (files_from)
1483
          fclose(files_from);
1484
        close(pipe_fd[0]);
1485
        FILE *p;
1486
1487
        if (!(p = fdopen(pipe_fd[1],"w")))
1488
          fatal("worker: fdopen failed");
1489
1490
        res = optimize(p, filename, newname, tmpdir, &file_stat, &rate, &saved);
1491
        if (res == 0)
1492
          fprintf(p, "\n\nSTATS\n%lf\n%lf\n", rate, saved);
1493
        exit(res);
1494
      } else {
1495
        /* Parent continues here... */
1496
        int j;
1497
1498
        close(pipe_fd[1]);
1499
1500
        w = NULL;
1501
        for (j = 0; j < MAX_WORKERS; j++) {
1502
          if (workers[j].pid < 0) {
1503
            w = &workers[j];
1504
            break;
1505
          }
1506
        }
1507
        if (!w)
1508
          fatal("no space to start a new worker (%d)", worker_count);
1509
        w->pid = pid;
1510
        w->read_pipe = pipe_fd[0];
1511
        worker_count++;
1512
        if (verbose_mode > 0)
1513
          fprintf(log_fh, "worker[%d] [slot=%d] started\n", pid, j);;
1514
      }
1515
1516
    } else
1517
#endif
1518
    {
1519
      /* Single process mode, process one file at a time... */
1520
1521
      res = optimize(log_fh, filename, newname, tmpdir, &file_stat, &rate, &saved);
1522
      if (res == 0) {
1523
        average_count++;
1524
        average_rate += rate;
1525
        total_save += saved;
1526
      } else if (res == 1) {
1527
        decompress_err_count++;
1528
      } else if (res == 2) {
1529
        compress_err_count++;
1530
      }
1531
    }
1532
1533
  } while (files_from || ++arg_idx < argc);
1534
1535
1536
#ifdef PARALLEL_PROCESSING
1537
  /* Wait for any child processes to exit... */
1538
  if (max_workers > 1) {
1539
    if (verbose_mode) {
1540
      fprintf(log_fh, "Waiting for %d workers to finish...\n", worker_count);
1541
    }
1542
    while ((pid = wait_for_worker(log_fh)) > 0) {
1543
      if (verbose_mode > 2)
1544
        fprintf(log_fh, "worker[%d] done\n", pid);
1545
    }
1546
  }
1547
#endif
1548
1549
  if (totals_mode && !quiet_mode)
1550
    fprintf(log_fh, "Average ""compression"" (%ld files): %0.2f%% (total saved %0.0fk)\n",
1551
      average_count, average_rate/average_count, total_save);
1552
1553
1554
  return (decompress_err_count > 0 || compress_err_count > 0 ? 1 : 0);;
1555
}
1556
#else
1557
void fuzz_set_target_size(const int new_target_size)
1558
3
{
1559
3
  target_size = new_target_size;
1560
3
}
1561
#endif /* BUILD_FOR_OSS_FUZZ */
1562
/* eof :-) */