jpegoptim.c 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427
  1. /*******************************************************************
  2. * JPEGoptim
  3. * Copyright (c) Timo Kokkonen, 1996-2023.
  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. #ifdef HAVE_CONFIG_H
  27. #include "config.h"
  28. #endif
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #ifdef HAVE_UNISTD_H
  32. #include <unistd.h>
  33. #endif
  34. #if HAVE_DIRENT_H
  35. #include <dirent.h>
  36. #endif
  37. #if HAVE_FCNTL_H
  38. #include <fcntl.h>
  39. #endif
  40. #if HAVE_SYS_STAT_H
  41. #include <sys/stat.h>
  42. #endif
  43. #if HAVE_SYS_TYPES_H
  44. #include <sys/types.h>
  45. #endif
  46. #if HAVE_SYS_WAIT_H
  47. #include <sys/wait.h>
  48. #endif
  49. #if HAVE_GETOPT_H && HAVE_GETOPT_LONG
  50. #include <getopt.h>
  51. #else
  52. #include "getopt.h"
  53. #endif
  54. #include <signal.h>
  55. #include <string.h>
  56. #include <jpeglib.h>
  57. #include <jerror.h>
  58. #include <setjmp.h>
  59. #include <time.h>
  60. #include <math.h>
  61. #include "jpegmarker.h"
  62. #include "jpegoptim.h"
  63. #define VERSION "1.5.5"
  64. #define COPYRIGHT "Copyright (C) 1996-2023, Timo Kokkonen"
  65. #if HAVE_WAIT && HAVE_FORK
  66. #define PARALLEL_PROCESSING 1
  67. #define MAX_WORKERS 256
  68. #endif
  69. #define FREE_LINE_BUF(buf,lines) { \
  70. int j; \
  71. for (j=0;j<lines;j++) free(buf[j]); \
  72. free(buf); \
  73. buf=NULL; \
  74. }
  75. struct my_error_mgr {
  76. struct jpeg_error_mgr pub;
  77. jmp_buf setjmp_buffer;
  78. int jump_set;
  79. };
  80. typedef struct my_error_mgr * my_error_ptr;
  81. #ifdef PARALLEL_PROCESSING
  82. struct worker {
  83. pid_t pid;
  84. int read_pipe;
  85. };
  86. struct worker *workers;
  87. int worker_count = 0;
  88. #endif
  89. int verbose_mode = 0;
  90. int quiet_mode = 0;
  91. int preserve_mode = 0;
  92. int preserve_perms = 0;
  93. int overwrite_mode = 0;
  94. int totals_mode = 0;
  95. int stdin_mode = 0;
  96. int stdout_mode = 0;
  97. int noaction = 0;
  98. int quality = -1;
  99. int retry = 0;
  100. int dest = 0;
  101. int force = 0;
  102. int save_exif = 1;
  103. int save_iptc = 1;
  104. int save_com = 1;
  105. int save_icc = 1;
  106. int save_xmp = 1;
  107. int save_adobe = 0;
  108. int save_jfxx = 0;
  109. int save_jfif = 1;
  110. int strip_none = 0;
  111. double threshold = -1.0;
  112. int csv = 0;
  113. int all_normal = 0;
  114. int all_progressive = 0;
  115. int target_size = 0;
  116. #ifdef HAVE_ARITH_CODE
  117. int arith_mode = -1;
  118. #endif
  119. int max_workers = 1;
  120. int nofix_mode = 0;
  121. int files_stdin = 0;
  122. FILE *files_from = NULL;
  123. int compress_err_count = 0;
  124. int decompress_err_count = 0;
  125. int global_error_counter = 0;
  126. char last_error[JMSG_LENGTH_MAX+1];
  127. FILE *jpeg_log_fh;
  128. long average_count = 0;
  129. double average_rate = 0.0;
  130. double total_save = 0.0;
  131. struct option long_options[] = {
  132. #ifdef HAVE_ARITH_CODE
  133. { "all-arith", 0, &arith_mode, 1 },
  134. { "all-huffman", 0, &arith_mode, 0 },
  135. #endif
  136. { "all-normal", 0, &all_normal, 1 },
  137. { "all-progressive", 0, &all_progressive, 1 },
  138. { "csv", 0, 0, 'b' },
  139. { "dest", 1, 0, 'd' },
  140. { "files-stdin", 0, &files_stdin, 1 },
  141. { "files-from", 1, 0, 'F' },
  142. { "force", 0, 0, 'f' },
  143. { "help", 0, 0, 'h' },
  144. { "keep-adobe", 0, &save_adobe, 1 },
  145. { "keep-all", 0, &strip_none, 1 },
  146. { "keep-com", 0, &save_com, 1 },
  147. { "keep-exif", 0, &save_exif, 1 },
  148. { "keep-iptc", 0, &save_iptc, 1 },
  149. { "keep-icc", 0, &save_icc, 1 },
  150. { "keep-jfif", 0, &save_jfif, 1 },
  151. { "keep-jfxx", 0, &save_jfxx, 1 },
  152. { "keep-xmp", 0, &save_xmp, 1 },
  153. { "max", 1, 0, 'm' },
  154. { "noaction", 0, 0, 'n' },
  155. { "nofix", 0, &nofix_mode, 1 },
  156. { "overwrite", 0, 0, 'o' },
  157. { "preserve", 0, 0, 'p' },
  158. { "preserve-perms", 0, 0, 'P' },
  159. { "quiet", 0, 0, 'q' },
  160. { "size", 1, 0, 'S' },
  161. { "stdin", 0, &stdin_mode, 1 },
  162. { "stdout", 0, &stdout_mode, 1 },
  163. { "strip-all", 0, 0, 's' },
  164. { "strip-none", 0, &strip_none, 1 },
  165. { "strip-com", 0, &save_com, 0 },
  166. { "strip-exif", 0, &save_exif, 0 },
  167. { "strip-iptc", 0, &save_iptc, 0 },
  168. { "strip-icc", 0, &save_icc, 0 },
  169. { "strip-xmp", 0, &save_xmp, 0 },
  170. { "strip-jfif", 0, &save_jfif, 0 },
  171. { "strip-jfxx", 0, &save_jfxx, 0 },
  172. { "strip-adobe", 0, &save_adobe, 0 },
  173. { "threshold", 1, 0, 'T' },
  174. { "totals", 0, 0, 't' },
  175. { "verbose", 0, 0, 'v' },
  176. { "version", 0, 0, 'V' },
  177. #ifdef PARALLEL_PROCESSING
  178. { "workers", 1, &max_workers, 'w' },
  179. #endif
  180. { 0, 0, 0, 0 }
  181. };
  182. /*****************************************************************/
  183. METHODDEF(void) my_error_exit (j_common_ptr cinfo)
  184. {
  185. my_error_ptr myerr = (my_error_ptr)cinfo->err;
  186. (*cinfo->err->output_message)(cinfo);
  187. if (myerr->jump_set)
  188. longjmp(myerr->setjmp_buffer, 1);
  189. else
  190. fatal("fatal error");
  191. }
  192. METHODDEF(void) my_output_message (j_common_ptr cinfo)
  193. {
  194. char buffer[JMSG_LENGTH_MAX+1];
  195. (*cinfo->err->format_message)((j_common_ptr)cinfo, buffer);
  196. buffer[sizeof(buffer) - 1] = 0;
  197. if (verbose_mode)
  198. fprintf(jpeg_log_fh, " (%s) ", buffer);
  199. global_error_counter++;
  200. strncopy(last_error, buffer, sizeof(last_error));
  201. }
  202. void print_usage(void)
  203. {
  204. fprintf(stderr,PROGRAMNAME " v" VERSION " " COPYRIGHT "\n");
  205. fprintf(stderr,
  206. "Usage: " PROGRAMNAME " [options] <filenames> \n\n"
  207. " -d<path>, --dest=<path>\n"
  208. " specify alternative destination directory for \n"
  209. " optimized files (default is to overwrite originals)\n"
  210. " -f, --force force optimization\n"
  211. " -h, --help display this help and exit\n"
  212. " -m<quality>, --max=<quality>\n"
  213. " set maximum image quality factor (disables lossless\n"
  214. " optimization mode, which is by default on)\n"
  215. " Valid quality values: 0 - 100\n"
  216. " -n, --noaction don't really optimize files, just print results\n"
  217. " -S<size>, --size=<size>\n"
  218. " Try to optimize file to given size (disables lossless\n"
  219. " optimization mode). Target size is specified either in\n"
  220. " kilo bytes (1 - n) or as percentage (1%% - 99%%)\n"
  221. " -T<threshold>, --threshold=<threshold>\n"
  222. " keep old file if the gain is below a threshold (%%)\n"
  223. #ifdef PARALLEL_PROCESSING
  224. " -w<max>, --workers=<max>\n"
  225. " set maximum number of parallel threads (default is 1)\n"
  226. #endif
  227. " -b, --csv print progress info in CSV format\n"
  228. " -o, --overwrite overwrite target file even if it exists (meaningful\n"
  229. " only when used with -d, --dest option)\n"
  230. " -p, --preserve preserve file timestamps\n"
  231. " -P, --preserve-perms\n"
  232. " preserve original file permissions by overwriting it\n"
  233. " -q, --quiet quiet mode\n"
  234. " -t, --totals print totals after processing all files\n"
  235. " -v, --verbose enable verbose mode (positively chatty)\n"
  236. " -V, --version print program version\n\n"
  237. " -s, --strip-all strip all markers from output file\n"
  238. " --strip-none do not strip any markers\n"
  239. " --strip-adobe strip Adobe (APP14) markers from output file\n"
  240. " --strip-com strip Comment markers from output file\n"
  241. " --strip-exif strip Exif markers from output file\n"
  242. " --strip-iptc strip IPTC/Photoshop (APP13) markers from output file\n"
  243. " --strip-icc strip ICC profile markers from output file\n"
  244. " --strip-jfif strip JFIF markers from output file\n"
  245. " --strip-jfxx strip JFXX (JFIF Extension) markers from output file\n"
  246. " --strip-xmp strip XMP markers markers from output file\n"
  247. "\n"
  248. " --keep-all do not strip any markers (same as --strip-none)\n"
  249. " --keep-adobe preserve any Adobe (APP14) markers\n"
  250. " --keep-com preserve any Comment markers\n"
  251. " --keep-exif preserve any Exif markers\n"
  252. " --keep-iptc preserve any IPTC/Photoshop (APP13) markers\n"
  253. " --keep-icc preserve any ICC profile markers\n"
  254. " --keep-jfif preserve any JFIF markers\n"
  255. " --keep-jfxx preserve any JFXX (JFIF Extension) markers\n"
  256. " --keep-xmp preserve any XMP markers markers\n"
  257. "\n"
  258. " --all-normal force all output files to be non-progressive\n"
  259. " --all-progressive force all output files to be progressive\n"
  260. #ifdef HAVE_ARITH_CODE
  261. " --all-arith force all output files to use arithmetic coding\n"
  262. " --all-huffman force all output files to use Huffman coding\n"
  263. #endif
  264. " --stdout send output to standard output (instead of a file)\n"
  265. " --stdin read input from standard input (instead of a file)\n"
  266. " --files-stdin Read names of files to process from stdin\n"
  267. " --files-from=FILE Read names of files to process from a file\n"
  268. " --nofix skip processing of input files if they contain any errors\n"
  269. "\n\n");
  270. }
  271. void print_version()
  272. {
  273. struct jpeg_error_mgr jerr;
  274. #ifdef __DATE__
  275. printf(PROGRAMNAME " v%s %s (%s)\n",VERSION,HOST_TYPE,__DATE__);
  276. #else
  277. printf(PROGRAMNAME " v%s %s\n",VERSION,HOST_TYPE);
  278. #endif
  279. printf(COPYRIGHT "\n\n");
  280. printf("This program comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
  281. "and you are welcome to redistribute it under certain conditions.\n"
  282. "See the GNU General Public License for more details.\n\n");
  283. if (!jpeg_std_error(&jerr))
  284. fatal("jpeg_std_error() failed");
  285. printf("\nlibjpeg version: %s\n%s\n",
  286. jerr.jpeg_message_table[JMSG_VERSION],
  287. jerr.jpeg_message_table[JMSG_COPYRIGHT]);
  288. }
  289. void parse_arguments(int argc, char **argv, char *dest_path, size_t dest_path_len)
  290. {
  291. int opt_index;
  292. int i, c;
  293. while(1) {
  294. opt_index=0;
  295. if ((c = getopt_long(argc,argv,"d:hm:nstqvfVpPoT:S:bw:",
  296. long_options, &opt_index)) == -1)
  297. break;
  298. switch (c) {
  299. case 'm':
  300. {
  301. int tmpvar;
  302. if (sscanf(optarg,"%d",&tmpvar) == 1) {
  303. quality=tmpvar;
  304. if (quality < 0) quality=0;
  305. if (quality > 100) quality=100;
  306. }
  307. else
  308. fatal("invalid argument for -m, --max");
  309. }
  310. break;
  311. case 'd':
  312. if (realpath(optarg,dest_path)==NULL)
  313. fatal("invalid destination directory: %s", optarg);
  314. if (!is_directory(dest_path))
  315. fatal("destination not a directory: %s", dest_path);
  316. strncatenate(dest_path, DIR_SEPARATOR_S, dest_path_len);
  317. if (verbose_mode)
  318. fprintf(stderr,"Destination directory: %s\n",dest_path);
  319. dest=1;
  320. break;
  321. case 'v':
  322. verbose_mode++;
  323. break;
  324. case 'h':
  325. print_usage();
  326. exit(0);
  327. break;
  328. case 'q':
  329. quiet_mode=1;
  330. break;
  331. case 't':
  332. totals_mode=1;
  333. break;
  334. case 'n':
  335. noaction=1;
  336. break;
  337. case 'f':
  338. force=1;
  339. break;
  340. case 'b':
  341. csv=1;
  342. quiet_mode=1;
  343. break;
  344. case 'V':
  345. print_version();
  346. exit(0);
  347. break;
  348. case 'o':
  349. overwrite_mode=1;
  350. break;
  351. case 'p':
  352. preserve_mode=1;
  353. break;
  354. case 'P':
  355. preserve_perms=1;
  356. break;
  357. case 's':
  358. save_exif=0;
  359. save_iptc=0;
  360. save_com=0;
  361. save_icc=0;
  362. save_xmp=0;
  363. save_adobe=0;
  364. save_jfif=0;
  365. save_jfxx=0;
  366. break;
  367. case 'T':
  368. {
  369. double tmpvar;
  370. if (sscanf(optarg,"%lf", &tmpvar) == 1) {
  371. threshold=tmpvar;
  372. if (threshold < 0) threshold=0;
  373. if (threshold > 100) threshold=100;
  374. }
  375. else fatal("invalid argument for -T, --threshold");
  376. }
  377. break;
  378. case 'S':
  379. {
  380. unsigned int tmpvar;
  381. if (sscanf(optarg,"%u", &tmpvar) == 1) {
  382. if (tmpvar > 0 && tmpvar < 100 &&
  383. optarg[strlen(optarg)-1] == '%' ) {
  384. target_size=-tmpvar;
  385. } else {
  386. target_size=tmpvar;
  387. }
  388. quality=100;
  389. }
  390. else fatal("invalid argument for -S, --size");
  391. }
  392. break;
  393. #ifdef PARALLEL_PROCESSING
  394. case 'w':
  395. {
  396. int tmpvar;
  397. if (sscanf(optarg, "%d", &tmpvar) == 1) {
  398. if (tmpvar > 0 && tmpvar <= MAX_WORKERS)
  399. max_workers = tmpvar;
  400. }
  401. else fatal("invalid argument for -w, --workers");
  402. }
  403. break;
  404. #endif
  405. case 'F':
  406. {
  407. if (optarg[0] == '-' && optarg[1] == 0) {
  408. files_stdin = 1;
  409. break;
  410. }
  411. if (!is_file(optarg, NULL))
  412. fatal("argument for --files-from must be a file");
  413. if ((files_from = fopen(optarg, "r")) == NULL)
  414. fatal("cannot open file: '%s'", optarg);
  415. }
  416. break;
  417. case '?':
  418. exit(1);
  419. }
  420. }
  421. /* check for '-' option indicating input is from stdin... */
  422. i = optind;
  423. while (argv[i]) {
  424. if (argv[i][0]=='-' && argv[i][1]==0)
  425. stdin_mode=1;
  426. i++;
  427. }
  428. if (stdin_mode)
  429. stdout_mode=1;
  430. if (all_normal && all_progressive)
  431. fatal("cannot specify both --all-normal and --all-progressive");
  432. if (files_stdin)
  433. files_from = stdin;
  434. if (stdin_mode && files_from == stdin)
  435. fatal("cannot specify both --stdin and --files-stdin");
  436. }
  437. void own_signal_handler(int a)
  438. {
  439. if (verbose_mode > 1)
  440. fprintf(stderr,PROGRAMNAME ": died from signal: %d\n",a);
  441. exit(2);
  442. }
  443. void write_markers(struct jpeg_decompress_struct *dinfo,
  444. struct jpeg_compress_struct *cinfo)
  445. {
  446. jpeg_saved_marker_ptr mrk;
  447. int write_marker;
  448. const char *s_name;
  449. if (!cinfo || !dinfo)
  450. fatal("invalid call to write_markers()");
  451. mrk=dinfo->marker_list;
  452. while (mrk) {
  453. write_marker = 0;
  454. s_name = jpeg_special_marker_name(mrk);
  455. /* Check for markers to save... */
  456. if (save_com && mrk->marker == JPEG_COM)
  457. write_marker++;
  458. if (save_iptc && !strncmp(s_name, "IPTC", 5))
  459. write_marker++;
  460. if (save_exif && !strncmp(s_name, "Exif", 5))
  461. write_marker++;
  462. if (save_icc && !strncmp(s_name, "ICC", 4))
  463. write_marker++;
  464. if (save_xmp && !strncmp(s_name, "XMP", 4))
  465. write_marker++;
  466. if (save_jfxx && !strncmp(s_name, "JFXX", 5))
  467. write_marker++;
  468. if (save_adobe && !strncmp(s_name, "Adobe", 6))
  469. write_marker++;
  470. if (strip_none)
  471. write_marker++;
  472. /* libjpeg emits some markers automatically so skip these to avoid duplicates... */
  473. if (!strncmp(s_name, "JFIF", 5))
  474. write_marker=0;
  475. if (verbose_mode > 2)
  476. fprintf(jpeg_log_fh, " (Marker %s [%s]: %s) ", jpeg_marker_name(mrk->marker),
  477. s_name, (write_marker ? "Keep" : "Discard"));
  478. if (write_marker)
  479. jpeg_write_marker(cinfo, mrk->marker, mrk->data, mrk->data_length);
  480. mrk=mrk->next;
  481. }
  482. }
  483. unsigned int parse_markers(const struct jpeg_decompress_struct *dinfo,
  484. char *str, unsigned int str_size, unsigned int *markers_total_size)
  485. {
  486. jpeg_saved_marker_ptr m;
  487. unsigned int count = 0;
  488. char *seen;
  489. size_t marker_types = jpeg_special_marker_types_count();
  490. int com_seen = 0;
  491. int special;
  492. if ((seen = malloc(marker_types)) == NULL)
  493. fatal("not enough of memory");
  494. memset(seen, 0, marker_types);
  495. str[0] = 0;
  496. *markers_total_size = 0;
  497. m = dinfo->marker_list;
  498. while (m) {
  499. count++;
  500. *markers_total_size += m->data_length;
  501. if ((special = jpeg_special_marker(m)) >= 0) {
  502. if (!seen[special])
  503. str_add_list(str, str_size, jpeg_special_marker_types[special].name, ",");
  504. seen[special]++;
  505. }
  506. if (m->marker == JPEG_COM && !com_seen) {
  507. str_add_list(str, str_size, "COM", ",");
  508. com_seen = 1;
  509. }
  510. m = m->next;
  511. }
  512. free(seen);
  513. return count;
  514. }
  515. int optimize(FILE *log_fh, const char *filename, const char *newname,
  516. const char *tmpdir, struct stat *file_stat,
  517. double *rate, double *saved)
  518. {
  519. FILE *infile = NULL;
  520. FILE *outfile = NULL;
  521. const char *outfname = NULL;
  522. char tmpfilename[MAXPATHLEN];
  523. struct jpeg_decompress_struct dinfo;
  524. struct jpeg_compress_struct cinfo;
  525. struct my_error_mgr jcerr, jderr;
  526. JSAMPARRAY buf = NULL;
  527. unsigned char *outbuffer = NULL;
  528. size_t outbuffersize = 0;
  529. unsigned char *inbuffer = NULL;
  530. size_t inbuffersize = 0;
  531. size_t inbufferused = 0;
  532. jvirt_barray_ptr *coef_arrays = NULL;
  533. char marker_str[256];
  534. unsigned int marker_in_count, marker_in_size;
  535. long insize = 0, outsize = 0, lastsize = 0;
  536. long inpos;
  537. int j;
  538. int oldquality, searchcount, searchdone;
  539. double ratio;
  540. int res = -1;
  541. jpeg_log_fh = log_fh;
  542. /* Initialize decompression object */
  543. dinfo.err = jpeg_std_error(&jderr.pub);
  544. jpeg_create_decompress(&dinfo);
  545. jderr.pub.error_exit=my_error_exit;
  546. jderr.pub.output_message=my_output_message;
  547. jderr.jump_set = 0;
  548. /* Initialize compression object */
  549. cinfo.err = jpeg_std_error(&jcerr.pub);
  550. jpeg_create_compress(&cinfo);
  551. jcerr.pub.error_exit=my_error_exit;
  552. jcerr.pub.output_message=my_output_message;
  553. jcerr.jump_set = 0;
  554. if (rate)
  555. *rate = 0.0;
  556. if (saved)
  557. *saved = 0.0;
  558. retry_point:
  559. if (filename) {
  560. if ((infile = fopen(filename, "rb")) == NULL) {
  561. warn("cannot open file: %s", filename);
  562. res = 1;
  563. goto exit_point;
  564. }
  565. } else {
  566. infile = stdin;
  567. set_filemode_binary(infile);
  568. }
  569. if (setjmp(jderr.setjmp_buffer)) {
  570. /* Error handler for decompress */
  571. abort_decompress:
  572. jpeg_abort_decompress(&dinfo);
  573. fclose(infile);
  574. if (buf)
  575. FREE_LINE_BUF(buf,dinfo.output_height);
  576. if (!quiet_mode || csv)
  577. fprintf(log_fh,csv ? ",,,,,error\n" : " [ERROR]\n");
  578. jderr.jump_set=0;
  579. res = 1;
  580. goto exit_point;
  581. } else {
  582. jderr.jump_set=1;
  583. }
  584. if (!retry && (!quiet_mode || csv)) {
  585. fprintf(log_fh,csv ? "%s," : "%s ",(filename ? filename:"stdin"));
  586. fflush(log_fh);
  587. }
  588. /* Prepare to decompress */
  589. if (stdin_mode || stdout_mode) {
  590. if (inbuffer)
  591. free(inbuffer);
  592. inbuffersize=65536;
  593. inbuffer=malloc(inbuffersize);
  594. if (!inbuffer)
  595. fatal("not enough memory");
  596. }
  597. global_error_counter=0;
  598. jpeg_save_markers(&dinfo, JPEG_COM, 0xffff);
  599. for (j=0;j<=15;j++) {
  600. jpeg_save_markers(&dinfo, JPEG_APP0+j, 0xffff);
  601. }
  602. jpeg_custom_src(&dinfo, infile, &inbuffer, &inbuffersize, &inbufferused, 65536);
  603. jpeg_read_header(&dinfo, TRUE);
  604. /* Check for known (Exif, IPTC, ICC , XMP, ...) markers */
  605. marker_in_count = parse_markers(&dinfo, marker_str, sizeof(marker_str),
  606. &marker_in_size);
  607. if (verbose_mode > 1) {
  608. fprintf(log_fh,"%d markers found in input file (total size %d bytes)\n",
  609. marker_in_count,marker_in_size);
  610. fprintf(log_fh,"coding: %s\n", (dinfo.arith_code == TRUE ? "Arithmetic" : "Huffman"));
  611. }
  612. if (!retry && (!quiet_mode || csv)) {
  613. fprintf(log_fh,csv ? "%dx%d,%dbit,%c," : "%dx%d %dbit %c ",(int)dinfo.image_width,
  614. (int)dinfo.image_height,(int)dinfo.num_components*8,
  615. (dinfo.progressive_mode?'P':'N'));
  616. if (!csv)
  617. fprintf(log_fh,"%s",marker_str);
  618. fflush(log_fh);
  619. }
  620. if ((insize=file_size(infile)) < 0)
  621. fatal("failed to stat() input file");
  622. /* Decompress the image */
  623. if (quality >= 0 && !retry) {
  624. jpeg_start_decompress(&dinfo);
  625. /* Allocate line buffer to store the decompressed image */
  626. buf = malloc(sizeof(JSAMPROW)*dinfo.output_height);
  627. if (!buf) fatal("not enough memory");
  628. for (j=0;j<dinfo.output_height;j++) {
  629. buf[j]=malloc(sizeof(JSAMPLE)*dinfo.output_width*
  630. dinfo.out_color_components);
  631. if (!buf[j])
  632. fatal("not enough memory");
  633. }
  634. while (dinfo.output_scanline < dinfo.output_height) {
  635. jpeg_read_scanlines(&dinfo,&buf[dinfo.output_scanline],
  636. dinfo.output_height-dinfo.output_scanline);
  637. }
  638. } else {
  639. coef_arrays = jpeg_read_coefficients(&dinfo);
  640. if (!coef_arrays) {
  641. if (!quiet_mode)
  642. fprintf(log_fh, " (failed to read coefficients) ");
  643. goto abort_decompress;
  644. }
  645. }
  646. inpos = ftell(infile);
  647. if (inpos > 0 && inpos < insize) {
  648. if (!quiet_mode)
  649. fprintf(log_fh, " (%lu bytes extraneous data found after end of image) ",
  650. insize - inpos);
  651. if (nofix_mode)
  652. global_error_counter++;
  653. }
  654. if (!retry && !quiet_mode) {
  655. fprintf(log_fh,(global_error_counter==0 ? " [OK] " : " [WARNING] "));
  656. fflush(log_fh);
  657. }
  658. if (stdin_mode)
  659. insize = inbufferused;
  660. if (nofix_mode && global_error_counter != 0) {
  661. /* Skip files containing any errors (or warnings) */
  662. goto abort_decompress;
  663. }
  664. if (dest && !noaction) {
  665. if (file_exists(newname) && !overwrite_mode) {
  666. if (!quiet_mode)
  667. fprintf(log_fh, " (target file already exists) ");
  668. goto abort_decompress;
  669. }
  670. }
  671. if (setjmp(jcerr.setjmp_buffer)) {
  672. /* Error handler for compress failures */
  673. jpeg_abort_compress(&cinfo);
  674. jpeg_abort_decompress(&dinfo);
  675. fclose(infile);
  676. if (!quiet_mode)
  677. fprintf(log_fh," [Compress ERROR: %s]\n",last_error);
  678. if (buf)
  679. FREE_LINE_BUF(buf,dinfo.output_height);
  680. jcerr.jump_set=0;
  681. res = 2;
  682. goto exit_point;
  683. } else {
  684. jcerr.jump_set=1;
  685. }
  686. lastsize = 0;
  687. searchcount = 0;
  688. searchdone = 0;
  689. oldquality = 200;
  690. if (target_size != 0) {
  691. /* Always start with quality 100 if -S option specified... */
  692. quality = 100;
  693. }
  694. binary_search_loop:
  695. /* Allocate memory buffer that should be large enough to store the output JPEG... */
  696. if (outbuffer)
  697. free(outbuffer);
  698. outbuffersize=insize + 32768;
  699. outbuffer=malloc(outbuffersize);
  700. if (!outbuffer)
  701. fatal("not enough memory");
  702. /* setup custom "destination manager" for libjpeg to write to our buffer */
  703. jpeg_memory_dest(&cinfo, &outbuffer, &outbuffersize, 65536);
  704. if (quality >= 0 && !retry) {
  705. /* Lossy "optimization" ... */
  706. cinfo.in_color_space=dinfo.out_color_space;
  707. cinfo.input_components=dinfo.output_components;
  708. cinfo.image_width=dinfo.image_width;
  709. cinfo.image_height=dinfo.image_height;
  710. jpeg_set_defaults(&cinfo);
  711. jpeg_set_quality(&cinfo,quality,TRUE);
  712. #ifdef HAVE_JINT_DC_SCAN_OPT_MODE
  713. if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
  714. jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
  715. #endif
  716. if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
  717. /* Explicitly disable progressive mode. */
  718. cinfo.scan_info = NULL;
  719. cinfo.num_scans = 0;
  720. } else if (all_progressive || dinfo.progressive_mode) {
  721. /* Enable progressive mode. */
  722. jpeg_simple_progression(&cinfo);
  723. }
  724. cinfo.optimize_coding = TRUE;
  725. #ifdef HAVE_ARITH_CODE
  726. if (arith_mode >= 0)
  727. cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
  728. #endif
  729. if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
  730. cinfo.write_JFIF_header = TRUE;
  731. } else {
  732. cinfo.write_JFIF_header = FALSE;
  733. }
  734. if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
  735. /* If outputting Adobe marker, don't write JFIF marker... */
  736. cinfo.write_JFIF_header = FALSE;
  737. }
  738. j=0;
  739. jpeg_start_compress(&cinfo,TRUE);
  740. /* Write markers */
  741. write_markers(&dinfo,&cinfo);
  742. /* Write image */
  743. while (cinfo.next_scanline < cinfo.image_height) {
  744. jpeg_write_scanlines(&cinfo,&buf[cinfo.next_scanline],
  745. dinfo.output_height);
  746. }
  747. } else {
  748. /* Lossless optimization ... */
  749. jpeg_copy_critical_parameters(&dinfo, &cinfo);
  750. #ifdef HAVE_JINT_DC_SCAN_OPT_MODE
  751. if (jpeg_c_int_param_supported(&cinfo, JINT_DC_SCAN_OPT_MODE))
  752. jpeg_c_set_int_param(&cinfo, JINT_DC_SCAN_OPT_MODE, 1);
  753. #endif
  754. if (all_normal || (!dinfo.progressive_mode && !all_progressive)) {
  755. /* Explicitly disable progressive mode. */
  756. cinfo.scan_info = NULL;
  757. cinfo.num_scans = 0;
  758. } else if (all_progressive || dinfo.progressive_mode) {
  759. /* Enable progressive mode. */
  760. jpeg_simple_progression(&cinfo);
  761. }
  762. cinfo.optimize_coding = TRUE;
  763. #ifdef HAVE_ARITH_CODE
  764. if (arith_mode >= 0)
  765. cinfo.arith_code = (arith_mode > 0 ? TRUE : FALSE);
  766. #endif
  767. if (dinfo.saw_JFIF_marker && (save_jfif || strip_none)) {
  768. cinfo.write_JFIF_header = TRUE;
  769. } else {
  770. cinfo.write_JFIF_header = FALSE;
  771. }
  772. if (dinfo.saw_Adobe_marker && (save_adobe || strip_none)) {
  773. /* If outputting Adobe marker, don't write JFIF marker... */
  774. cinfo.write_JFIF_header = FALSE;
  775. }
  776. /* Write image */
  777. jpeg_write_coefficients(&cinfo, coef_arrays);
  778. /* Write markers */
  779. write_markers(&dinfo,&cinfo);
  780. }
  781. jpeg_finish_compress(&cinfo);
  782. outsize=outbuffersize;
  783. if (target_size != 0 && !retry) {
  784. /* Perform (binary) search to try to reach target file size... */
  785. long osize = outsize/1024;
  786. long isize = insize/1024;
  787. long tsize = target_size;
  788. if (tsize < 0) {
  789. tsize=((-target_size)*insize/100)/1024;
  790. if (tsize < 1)
  791. tsize = 1;
  792. }
  793. if (osize == tsize || searchdone || searchcount >= 8 || tsize > isize) {
  794. if (searchdone < 42 && lastsize > 0) {
  795. if (labs(osize-tsize) > labs(lastsize-tsize)) {
  796. if (verbose_mode) fprintf(log_fh,"(revert to %d)",oldquality);
  797. searchdone=42;
  798. quality=oldquality;
  799. goto binary_search_loop;
  800. }
  801. }
  802. if (verbose_mode)
  803. fprintf(log_fh," ");
  804. } else {
  805. int newquality;
  806. int dif = floor((abs(oldquality-quality)/2.0)+0.5);
  807. if (osize > tsize) {
  808. newquality=quality-dif;
  809. if (dif < 1) { newquality--; searchdone=1; }
  810. if (newquality < 0) { newquality=0; searchdone=2; }
  811. } else {
  812. newquality=quality+dif;
  813. if (dif < 1) { newquality++; searchdone=3; }
  814. if (newquality > 100) { newquality=100; searchdone=4; }
  815. }
  816. oldquality=quality;
  817. quality=newquality;
  818. if (verbose_mode)
  819. fprintf(log_fh,"(try %d)",quality);
  820. lastsize=osize;
  821. searchcount++;
  822. goto binary_search_loop;
  823. }
  824. }
  825. if (buf)
  826. FREE_LINE_BUF(buf,dinfo.output_height);
  827. jpeg_finish_decompress(&dinfo);
  828. fclose(infile);
  829. if (quality >= 0 && outsize >= insize && !retry && !stdin_mode) {
  830. if (verbose_mode)
  831. fprintf(log_fh,"(retry w/lossless) ");
  832. retry=1;
  833. goto retry_point;
  834. }
  835. retry=0;
  836. ratio=(insize-outsize)*100.0/insize;
  837. if (!quiet_mode || csv)
  838. fprintf(log_fh,csv ? "%ld,%ld,%0.2f," : "%ld --> %ld bytes (%0.2f%%), ",insize,outsize,ratio);
  839. if (rate) {
  840. *rate = (ratio < 0 ? 0.0 : ratio);
  841. }
  842. if ((outsize < insize && ratio >= threshold) || force) {
  843. if (saved) {
  844. *saved = (insize - outsize) / 1024.0;
  845. }
  846. if (!quiet_mode || csv)
  847. fprintf(log_fh,csv ? "optimized\n" : "optimized.\n");
  848. if (noaction) {
  849. res = 0;
  850. goto exit_point;
  851. }
  852. if (stdout_mode) {
  853. outfname=NULL;
  854. set_filemode_binary(stdout);
  855. if (fwrite(outbuffer,outbuffersize,1,stdout) != 1)
  856. fatal("%s, write failed to stdout",(stdin_mode ? "stdin" : filename));
  857. } else {
  858. if (preserve_perms && !dest) {
  859. /* make backup of the original file */
  860. int newlen = snprintf(tmpfilename, sizeof(tmpfilename),
  861. "%s.jpegoptim.bak", newname);
  862. if (newlen >= sizeof(tmpfilename))
  863. warn("temp filename too long: %s", tmpfilename);
  864. if (verbose_mode > 1 && !quiet_mode)
  865. fprintf(log_fh,"%s, creating backup as: %s\n",
  866. (stdin_mode ? "stdin" : filename), tmpfilename);
  867. if (file_exists(tmpfilename))
  868. fatal("%s, backup file already exists: %s",
  869. (stdin_mode ?" stdin" : filename), tmpfilename);
  870. if (copy_file(newname,tmpfilename))
  871. fatal("%s, failed to create backup: %s",
  872. (stdin_mode ? "stdin" : filename), tmpfilename);
  873. if ((outfile=fopen(newname,"wb"))==NULL)
  874. fatal("%s, error opening output file: %s",
  875. (stdin_mode ? "stdin" : filename), newname);
  876. outfname=newname;
  877. } else {
  878. #ifdef HAVE_MKSTEMPS
  879. /* rely on mkstemps() to create us temporary file safely... */
  880. int newlen = snprintf(tmpfilename,sizeof(tmpfilename),
  881. "%sjpegoptim-%d-%d.XXXXXX.tmp",
  882. tmpdir, (int)getuid(), (int)getpid());
  883. if (newlen >= sizeof(tmpfilename))
  884. warn("temp filename too long: %s", tmpfilename);
  885. int tmpfd = mkstemps(tmpfilename,4);
  886. if (tmpfd < 0)
  887. fatal("%s, error creating temp file %s: mkstemps() failed",
  888. (stdin_mode ? "stdin" : filename), tmpfilename);
  889. if ((outfile = fdopen(tmpfd,"wb")) == NULL)
  890. #else
  891. /* if platform is missing mkstemps(), try to create
  892. at least somewhat "safe" temp file... */
  893. snprintf(tmpfilename,sizeof(tmpfilename),
  894. "%sjpegoptim-%d-%d.%ld.tmp", tmpdir,
  895. (int)getuid(), (int)getpid(), (long)time(NULL));
  896. if ((outfile = fopen(tmpfilename,"wb")) == NULL)
  897. #endif
  898. fatal("error opening temporary file: %s", tmpfilename);
  899. outfname=tmpfilename;
  900. }
  901. if (verbose_mode > 1 && !quiet_mode)
  902. fprintf(log_fh,"writing %lu bytes to file: %s\n",
  903. (long unsigned int)outbuffersize, outfname);
  904. if (fwrite(outbuffer,outbuffersize,1,outfile) != 1)
  905. fatal("write failed to file: %s", outfname);
  906. fclose(outfile);
  907. }
  908. if (outfname) {
  909. if (preserve_mode) {
  910. /* preserve file modification time */
  911. if (verbose_mode > 1 && !quiet_mode)
  912. fprintf(log_fh,"set file modification time same as in original: %s\n",
  913. outfname);
  914. #if defined(HAVE_UTIMENSAT) && defined(HAVE_STRUCT_STAT_ST_MTIM)
  915. struct timespec time_save[2];
  916. time_save[0].tv_sec = 0;
  917. time_save[0].tv_nsec = UTIME_OMIT; /* omit atime */
  918. time_save[1] = file_stat->st_mtim;
  919. if (utimensat(AT_FDCWD,outfname,time_save,0) != 0)
  920. warn("failed to reset output file time/date");
  921. #else
  922. struct utimbuf time_save;
  923. time_save.actime=file_stat->st_atime;
  924. time_save.modtime=file_stat->st_mtime;
  925. if (utime(outfname,&time_save) != 0)
  926. warn("failed to reset output file time/date");
  927. #endif
  928. }
  929. if (preserve_perms && !dest) {
  930. /* original file was already replaced, remove backup... */
  931. if (verbose_mode > 1 && !quiet_mode)
  932. fprintf(log_fh,"removing backup file: %s\n", tmpfilename);
  933. if (delete_file(tmpfilename))
  934. warn("failed to remove backup file: %s", tmpfilename);
  935. } else {
  936. /* make temp file to be the original file... */
  937. /* preserve file mode */
  938. if (chmod(outfname,(file_stat->st_mode & 0777)) != 0)
  939. warn("failed to set output file mode");
  940. /* preserve file group (and owner if run by root) */
  941. if (chown(outfname,
  942. (geteuid()==0 ? file_stat->st_uid : -1),
  943. file_stat->st_gid) != 0)
  944. warn("failed to reset output file group/owner");
  945. if (verbose_mode > 1 && !quiet_mode)
  946. fprintf(log_fh,"renaming: %s to %s\n", outfname, newname);
  947. if (rename_file(outfname, newname))
  948. fatal("cannot rename temp file");
  949. }
  950. }
  951. } else {
  952. if (!quiet_mode || csv)
  953. fprintf(log_fh,csv ? "skipped\n" : "skipped.\n");
  954. if (stdout_mode) {
  955. set_filemode_binary(stdout);
  956. if (fwrite(inbuffer, inbufferused, 1, stdout) != 1)
  957. fatal("%s, write failed to stdout",
  958. (stdin_mode ? "stdin" : filename));
  959. }
  960. }
  961. res = 0;
  962. exit_point:
  963. if (inbuffer)
  964. free(inbuffer);
  965. if (outbuffer)
  966. free(outbuffer);
  967. jpeg_destroy_compress(&cinfo);
  968. jpeg_destroy_decompress(&dinfo);
  969. return res;
  970. }
  971. #ifdef PARALLEL_PROCESSING
  972. int wait_for_worker(FILE *log_fh)
  973. {
  974. FILE *p;
  975. struct worker *w;
  976. char buf[1024];
  977. int wstatus;
  978. pid_t pid;
  979. int j, e;
  980. int state = 0;
  981. double val;
  982. double rate = 0.0;
  983. double saved = 0.0;
  984. if ((pid = wait(&wstatus)) < 0)
  985. return pid;
  986. w = NULL;
  987. for (j = 0; j < MAX_WORKERS; j++) {
  988. if (workers[j].pid == pid) {
  989. w = &workers[j];
  990. break;
  991. }
  992. }
  993. if (!w)
  994. fatal("Unknown worker[%d] process found\n", pid);
  995. if (WIFEXITED(wstatus)) {
  996. e = WEXITSTATUS(wstatus);
  997. if (verbose_mode)
  998. fprintf(log_fh, "worker[%d] [slot=%d] exited: %d\n",
  999. pid, j, e);
  1000. if (e == 0) {
  1001. //average_count++;
  1002. //average_rate += rate;
  1003. //total_save += saved;
  1004. } else if (e == 1) {
  1005. decompress_err_count++;
  1006. } else if (e == 2) {
  1007. compress_err_count++;
  1008. }
  1009. } else {
  1010. fatal("worker[%d] killed", pid);
  1011. }
  1012. p = fdopen(w->read_pipe, "r");
  1013. if (!p) fatal("fdopen failed()");
  1014. while (fgets(buf, sizeof(buf), p)) {
  1015. if (verbose_mode > 2)
  1016. fprintf(log_fh, "worker[%d] PIPE: %s", pid, buf);
  1017. if (state == 0 && buf[0] == '\n') {
  1018. state=1;
  1019. continue;
  1020. }
  1021. if (state == 1 && !strncmp(buf, "STAT", 4)) {
  1022. state=2;
  1023. continue;
  1024. }
  1025. if (state >= 2) {
  1026. if (sscanf(buf, "%lf", &val) == 1) {
  1027. if (state == 2) {
  1028. rate = val;
  1029. }
  1030. else if (state == 3) {
  1031. saved = val;
  1032. average_count++;
  1033. average_rate += rate;
  1034. total_save += saved;
  1035. }
  1036. }
  1037. state++;
  1038. continue;
  1039. }
  1040. if (state == 0)
  1041. fprintf(log_fh, "%s", buf);
  1042. }
  1043. close(w->read_pipe);
  1044. w->pid = -1;
  1045. w->read_pipe = -1;
  1046. worker_count --;
  1047. return pid;
  1048. }
  1049. #endif
  1050. /****************************************************************************/
  1051. int main(int argc, char **argv)
  1052. {
  1053. struct stat file_stat;
  1054. char tmpfilename[MAXPATHLEN + 1],tmpdir[MAXPATHLEN + 1];
  1055. char newname[MAXPATHLEN + 1], dest_path[MAXPATHLEN + 1];
  1056. char namebuf[MAXPATHLEN + 2];
  1057. const char *filename;
  1058. volatile int i;
  1059. int res;
  1060. double rate, saved;
  1061. FILE *log_fh;
  1062. #ifdef PARALLEL_PROCESSING
  1063. struct worker *w;
  1064. int pipe_fd[2];
  1065. pid_t pid;
  1066. int j;
  1067. /* Allocate table to keep track of child processes... */
  1068. if (!(workers = malloc(sizeof(struct worker) * MAX_WORKERS)))
  1069. fatal("not enough memory");
  1070. for (i = 0; i < MAX_WORKERS; i++) {
  1071. workers[i].pid = -1;
  1072. workers[i].read_pipe = -1;
  1073. }
  1074. #endif
  1075. umask(077);
  1076. signal(SIGINT,own_signal_handler);
  1077. signal(SIGTERM,own_signal_handler);
  1078. /* Parse command line parameters */
  1079. parse_arguments(argc, argv, dest_path, sizeof(dest_path));
  1080. log_fh = (stdout_mode ? stderr : stdout);
  1081. if (verbose_mode) {
  1082. if (quality >= 0 && target_size == 0)
  1083. fprintf(log_fh, "Image quality limit set to: %d\n", quality);
  1084. if (threshold >= 0)
  1085. fprintf(log_fh, "Compression threshold (%%) set to: %0.1lf\n", threshold);
  1086. if (all_normal)
  1087. fprintf(log_fh, "All output files will be non-progressive\n");
  1088. if (all_progressive)
  1089. fprintf(log_fh, "All output files will be progressive\n");
  1090. if (target_size > 0)
  1091. fprintf(log_fh, "Target size for output files set to: %d Kbytes.\n",
  1092. target_size);
  1093. if (target_size < 0)
  1094. fprintf(log_fh, "Target size for output files set to: %d%%\n",
  1095. -target_size);
  1096. #ifdef PARALLEL_PROCESSING
  1097. if (max_workers > 0)
  1098. fprintf(log_fh, "Using maximum of %d parallel threads\n", max_workers);
  1099. #endif
  1100. }
  1101. if (stdin_mode) {
  1102. /* Process just one file, if source is stdin... */
  1103. res = optimize(stderr, NULL, NULL, NULL, &file_stat, NULL, NULL);
  1104. return (res == 0 ? 0 : 1);
  1105. }
  1106. i=(optind > 0 ? optind : 1);
  1107. if (files_from == NULL && argc <= i) {
  1108. if (!quiet_mode)
  1109. fprintf(stderr, PROGRAMNAME ": file argument(s) missing\n"
  1110. "Try '" PROGRAMNAME " --help' for more information.\n");
  1111. exit(1);
  1112. }
  1113. /* Main loop to process input files */
  1114. do {
  1115. if (files_from) {
  1116. if (!fgetstr(namebuf, sizeof(namebuf), files_from))
  1117. break;
  1118. filename = namebuf;
  1119. } else {
  1120. filename = argv[i];
  1121. }
  1122. if (*filename == 0)
  1123. continue;
  1124. if (verbose_mode > 1)
  1125. fprintf(log_fh, "processing file: %s\n", filename);
  1126. if (strnlen(filename, MAXPATHLEN + 1) > MAXPATHLEN) {
  1127. warn("skipping too long filename: %s", filename);
  1128. continue;
  1129. }
  1130. if (!noaction) {
  1131. /* generate tmp dir & new filename */
  1132. if (dest) {
  1133. strncopy(tmpdir, dest_path, sizeof(tmpdir));
  1134. strncopy(newname, dest_path, sizeof(newname));
  1135. if (!splitname(filename, tmpfilename, sizeof(tmpfilename)))
  1136. fatal("splitname() failed for: %s", filename);
  1137. strncatenate(newname, tmpfilename, sizeof(newname));
  1138. } else {
  1139. if (!splitdir(filename, tmpdir, sizeof(tmpdir)))
  1140. fatal("splitdir() failed for: %s", filename);
  1141. strncopy(newname, filename, sizeof(newname));
  1142. }
  1143. }
  1144. if (file_exists(filename)) {
  1145. if (!is_file(filename, &file_stat)) {
  1146. if (is_directory(filename))
  1147. warn("skipping directory: %s", filename);
  1148. else
  1149. warn("skipping special file: %s", filename);
  1150. continue;
  1151. }
  1152. } else {
  1153. warn("file not found: %s", filename);
  1154. continue;
  1155. }
  1156. #ifdef PARALLEL_PROCESSING
  1157. if (max_workers > 1) {
  1158. /* Multi process mode, run up to max_workers processes simultaneously... */
  1159. if (worker_count >= max_workers) {
  1160. // wait for a worker to exit...
  1161. wait_for_worker(log_fh);
  1162. }
  1163. if (pipe(pipe_fd) < 0)
  1164. fatal("failed to open pipe");
  1165. pid = fork();
  1166. if (pid < 0)
  1167. fatal("fork() failed");
  1168. if (pid == 0) {
  1169. /* Child process starts here... */
  1170. if (files_from)
  1171. fclose(files_from);
  1172. close(pipe_fd[0]);
  1173. FILE *p;
  1174. if (!(p = fdopen(pipe_fd[1],"w")))
  1175. fatal("worker: fdopen failed");
  1176. res = optimize(p, filename, newname, tmpdir, &file_stat, &rate, &saved);
  1177. if (res == 0)
  1178. fprintf(p, "\n\nSTATS\n%lf\n%lf\n", rate, saved);
  1179. exit(res);
  1180. } else {
  1181. /* Parent continues here... */
  1182. close(pipe_fd[1]);
  1183. w = NULL;
  1184. for (j = 0; j < MAX_WORKERS; j++) {
  1185. if (workers[j].pid < 0) {
  1186. w = &workers[j];
  1187. break;
  1188. }
  1189. }
  1190. if (!w)
  1191. fatal("no space to start a new worker (%d)", worker_count);
  1192. w->pid = pid;
  1193. w->read_pipe = pipe_fd[0];
  1194. worker_count++;
  1195. if (verbose_mode > 0)
  1196. fprintf(log_fh, "worker[%d] [slot=%d] started\n", pid, j);;
  1197. }
  1198. } else
  1199. #endif
  1200. {
  1201. /* Single process mode, process one file at a time... */
  1202. res = optimize(log_fh, filename, newname, tmpdir, &file_stat, &rate, &saved);
  1203. if (res == 0) {
  1204. average_count++;
  1205. average_rate += rate;
  1206. total_save += saved;
  1207. } else if (res == 1) {
  1208. decompress_err_count++;
  1209. } else if (res == 2) {
  1210. compress_err_count++;
  1211. }
  1212. }
  1213. } while (files_from || ++i < argc);
  1214. #ifdef PARALLEL_PROCESSING
  1215. /* Wait for any child processes to exit... */
  1216. if (max_workers > 1) {
  1217. if (verbose_mode) {
  1218. fprintf(log_fh, "Waiting for %d workers to finish...\n", worker_count);
  1219. }
  1220. while ((pid = wait_for_worker(log_fh)) > 0) {
  1221. if (verbose_mode > 2)
  1222. fprintf(log_fh, "worker[%d] done\n", pid);
  1223. }
  1224. }
  1225. #endif
  1226. if (totals_mode && !quiet_mode)
  1227. fprintf(log_fh, "Average ""compression"" (%ld files): %0.2f%% (total saved %0.0fk)\n",
  1228. average_count, average_rate/average_count, total_save);
  1229. return (decompress_err_count > 0 || compress_err_count > 0 ? 1 : 0);;
  1230. }
  1231. /* eof :-) */