1 /*******************************************************************
2 * $Id: jpeginfo.c,v 1.15 2009/10/12 04:23:40 tjko Exp $
3 *
4 * JPEGinfo
5 * Copyright (c) Timo Kokkonen, 1995-2009.
6 *
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include <stdio.h>
14 #if HAVE_GETOPT_H && HAVE_GETOPT_LONG
15 #include <getopt.h>
16 #else
17 #include "getopt.h"
18 #endif
19 #include <stdlib.h>
20 #include <string.h>
21 #include <setjmp.h>
22 #include <ctype.h>
23 #include <jpeglib.h>
24
25 #include "md5.h"
26 #include "jpeginfo.h"
27
28
29 #define VERSION "1.6.1"
30 #define BUF_LINES 255
31
32 #ifndef HOST_TYPE
33 #define HOST_TYPE ""
34 #endif
35
36 #ifdef BROKEN_METHODDEF
37 #undef METHODDEF
38 #define METHODDEF(x) static x
39 #endif
40
41 #define EXIF_JPEG_MARKER JPEG_APP0+1
42 #define EXIF_IDENT_STRING "Exif\000\000"
43
44
45 static char *rcsid = "$Id: jpeginfo.c,v 1.15 2009/10/12 04:23:40 tjko Exp $";
46
47 struct my_error_mgr {
48 struct jpeg_error_mgr pub;
49 jmp_buf setjmp_buffer;
50 };
51 typedef struct my_error_mgr * my_error_ptr;
52
53 static struct jpeg_decompress_struct cinfo;
54 static struct my_error_mgr jerr;
55
56 static struct option long_options[] = {
57 {"verbose",0,0,'v'},
58 {"delete",0,0,'d'},
59 {"mode",1,0,'m'},
60 {"file",1,0,'f'},
61 {"check",0,0,'c'},
62 {"help",0,0,'h'},
63 {"quiet",0,0,'q'},
64 {"lsstyle",0,0,'l'},
65 {"info",0,0,'i'},
66 {"md5",0,0,'5'},
67 {"version",0,0,'V'},
68 {"comments",0,0,'C'},
69 {0,0,0,0}
70 };
71
72 FILE *infile=NULL;
73 FILE *listfile=NULL;
74 int global_error_counter;
75 int global_total_errors;
76 int verbose_mode = 0;
77 int quiet_mode = 0;
78 int delete_mode = 0;
79 int check_mode = 0;
80 int com_mode = 0;
81 int del_mode = 0;
82 int opt_index = 0;
83 int list_mode = 0;
84 int longinfo_mode = 0;
85 int input_from_file = 0;
86 int md5_mode = 0;
87 char *current = NULL;
88
89 /*****************************************************************/
90
91 METHODDEF(void)
my_error_exit(j_common_ptr cinfo)92 my_error_exit (j_common_ptr cinfo)
93 {
94 my_error_ptr myerr = (my_error_ptr)cinfo->err;
95 (*cinfo->err->output_message) (cinfo);
96 longjmp(myerr->setjmp_buffer,1);
97 }
98
99
100 METHODDEF(void)
my_output_message(j_common_ptr cinfo)101 my_output_message (j_common_ptr cinfo)
102 {
103 char buffer[JMSG_LENGTH_MAX];
104
105 (*cinfo->err->format_message) (cinfo, buffer);
106 if (quiet_mode < 2) printf(" %s ",buffer);
107 global_error_counter++;
108 global_total_errors++;
109 }
110
111
no_memory(void)112 void no_memory(void)
113 {
114 fprintf(stderr,"jpeginfo: not enough memory!\n");
115 exit(3);
116 }
117
118
p_usage(void)119 void p_usage(void)
120 {
121 if (!quiet_mode) {
122 fprintf(stderr,"jpeginfo v" VERSION
123 " Copyright (c) Timo Kokkonen, 1995-2009.\n");
124
125 fprintf(stderr,
126 "Usage: jpeginfo [options] <filenames>\n\n"
127 " -c, --check check files also for errors\n"
128 " -C, --comments display comments (from COM markers)\n"
129 " -d, --delete delete files that have errors\n"
130 " -f<filename>, --file<filename>\n"
131 " read the filenames to process from given file\n"
132 " (for standard input use '-' as a filename)\n"
133 " -h, --help display this help and exit\n"
134 " -5, --md5 calculate MD5 checksum for each file\n"
135 " -i, --info display even more information about pictures\n"
136 " -l, --lsstyle use alternate listing format (ls -l style)\n"
137 " -v, --verbose enable verbose mode (positively chatty)\n"
138 " --version print program version and exit\n"
139 " -q, --quiet quiet mode, output just jpeg infos\n"
140 " -m<mode>, --mode=<mode>\n"
141 " defines which jpegs to remove (when using"
142 " the -d option).\n"
143 " Mode can be one of the following:\n"
144 " erronly only files with serious errrors\n"
145 " all files ontaining warnings or errors"
146 " (default)\n\n\n");
147 }
148
149 exit(0);
150 }
151
152
153 /*****************************************************************/
main(int argc,char ** argv)154 int main(int argc, char **argv)
155 {
156 JSAMPARRAY buf = malloc(sizeof(JSAMPROW)*BUF_LINES);
157 jpeg_saved_marker_ptr exif_marker, cmarker;
158 MD5_CTX *MD5 = malloc(sizeof(MD5_CTX));
159 volatile int i;
160 int c,j,lines_read, err_count;
161 unsigned char ch;
162 char namebuf[1024];
163 long fs;
164 char *md5buf,digest[16],digest_text[33];
165
166 global_total_errors=0;
167 if (rcsid); /* to keep compiler from not complaining about rcsid */
168
169 cinfo.err = jpeg_std_error(&jerr.pub);
170 jpeg_create_decompress(&cinfo);
171 jerr.pub.error_exit=my_error_exit;
172 jerr.pub.output_message=my_output_message;
173
174 if (!buf || !MD5) no_memory();
175 if (argc<2) {
176 if (quiet_mode < 2) fprintf(stderr,"jpeginfo: file arguments missing\n"
177 "Try 'jpeginfo "
178 "--help"
179 "' for more information.\n");
180 exit(1);
181 }
182
183 /* parse command line parameters */
184 while(1) {
185 opt_index=0;
186 if ( (c=getopt_long(argc,argv,"livVdcChqm:f:5",
187 long_options,&opt_index)) == -1)
188 break;
189 switch (c) {
190 case 'm':
191 if (!strcasecmp(optarg,"all")) del_mode=0;
192 else if (!strcasecmp(optarg,"erronly")) del_mode=1;
193 else if (!quiet_mode)
194 fprintf(stderr,"Unknown parameter for -m, --mode.\n");
195 break;
196 case 'f':
197 if (!strcmp(optarg,"-")) listfile=stdin;
198 else if ((listfile=fopen(optarg,"r"))==NULL) {
199 fprintf(stderr,"Cannot open file '%s'.\n",optarg);
200 exit(2);
201 }
202 input_from_file=1;
203 break;
204 case 'v':
205 verbose_mode=1;
206 break;
207 case 'V':
208 fprintf(stderr,"jpeginfo v" VERSION " " HOST_TYPE
209 "\nCopyright (c) Timo Kokkonen, 1995-2002.\n");
210 exit(0);
211 case 'd':
212 delete_mode=1;
213 break;
214 case 'c':
215 check_mode=1;
216 break;
217 case 'h':
218 p_usage();
219 break;
220 case 'q':
221 quiet_mode++;
222 break;
223 case 'l':
224 list_mode=1;
225 break;
226 case 'i':
227 longinfo_mode=1;
228 break;
229 case '5':
230 md5_mode=1;
231 break;
232 case 'C':
233 com_mode=1;
234 break;
235 case '?':
236 break;
237
238 default:
239 if (!quiet_mode)
240 fprintf(stderr,"jpeginfo: error parsing parameters.\n");
241 }
242 }
243
244 if (delete_mode && verbose_mode && !quiet_mode)
245 fprintf(stderr,"jpeginfo: delete mode enabled (%s)\n",
246 !del_mode?"normal":"errors only");
247
248 i=1;
249 do {
250 if (input_from_file) {
251 if (!fgetstr(namebuf,sizeof(namebuf),listfile)) break;
252 current=namebuf;
253 }
254 else current=argv[i];
255
256 if (current[0]==0) continue;
257 if (current[0]=='-' && !input_from_file) continue;
258
259 if (setjmp(jerr.setjmp_buffer)) {
260 jpeg_abort_decompress(&cinfo);
261 fclose(infile);
262 if (list_mode && quiet_mode < 2) printf(" %s",current);
263 if (quiet_mode < 2) printf(" [ERROR]\n");
264 if (delete_mode) delete_file(current,verbose_mode,quiet_mode);
265 continue;
266 }
267
268 if ((infile=fopen(current,"r"))==NULL) {
269 if (!quiet_mode) fprintf(stderr, "jpeginfo: can't open '%s'\n", current);
270 continue;
271 }
272 if (is_dir(infile)) {
273 fclose(infile);
274 if (verbose_mode) printf("directory: %s skipped\n",current);
275 continue;
276 }
277
278 fs=filesize(infile);
279
280 if (md5_mode) {
281 md5buf=malloc(fs);
282 if (!md5buf) no_memory();
283 fread(md5buf,1,fs,infile);
284 rewind(infile);
285
286 MD5Init(MD5);
287 MD5Update(MD5,md5buf,fs);
288 MD5Final(digest,MD5);
289 md2str(digest,digest_text);
290
291 free(md5buf);
292 }
293
294 if (!list_mode && quiet_mode < 2) printf("%s ",current);
295
296 global_error_counter=0;
297 err_count=jerr.pub.num_warnings;
298 if (com_mode) jpeg_save_markers(&cinfo, JPEG_COM, 0xffff);
299 jpeg_save_markers(&cinfo, EXIF_JPEG_MARKER, 0xffff);
300 jpeg_stdio_src(&cinfo, infile);
301 jpeg_read_header(&cinfo, TRUE);
302
303 /* check for Exif marker */
304 exif_marker=NULL;
305 cmarker=cinfo.marker_list;
306 while (cmarker) {
307 if (cmarker->marker == EXIF_JPEG_MARKER) {
308 if (!memcmp(cmarker->data,EXIF_IDENT_STRING,6)) exif_marker=cmarker;
309 }
310 cmarker=cmarker->next;
311 }
312
313 if (quiet_mode < 2) {
314 printf("%4d x %-4d %2dbit ",(int)cinfo.image_width,
315 (int)cinfo.image_height,(int)cinfo.num_components*8);
316
317 if (exif_marker) printf("Exif ");
318 else if (cinfo.saw_JFIF_marker) printf("JFIF ");
319 else if (cinfo.saw_Adobe_marker) printf("Adobe ");
320 else printf("n/a ");
321
322 if (longinfo_mode) {
323 printf("%s %s",(cinfo.progressive_mode?"Progressive":"Normal"),
324 (cinfo.arith_code?"Arithmetic":"Huffman") );
325
326 if (cinfo.density_unit==1||cinfo.density_unit==2)
327 printf(",%ddp%c",MIN(cinfo.X_density,cinfo.Y_density),
328 (cinfo.density_unit==1?'i':'c') );
329
330 if (cinfo.CCIR601_sampling) printf(",CCIR601");
331 printf(" %7ld ",fs);
332
333 } else printf("%c %7ld ",(cinfo.progressive_mode?'P':'N'),fs);
334
335 if (md5_mode) printf("%s ",digest_text);
336 if (list_mode) printf("%s ",current);
337
338 if (com_mode) {
339 cmarker=cinfo.marker_list;
340 while (cmarker) {
341 if (cmarker->marker == JPEG_COM) {
342 printf("\"");
343 for (j=0;j<cmarker->data_length;j++) {
344 ch = cmarker->data[j];
345 if (ch < 32 || iscntrl(ch)) continue;
346 printf("%c",cmarker->data[j]);
347 }
348 printf("\" ");
349 }
350 cmarker=cmarker->next;
351 }
352 }
353 }
354
355 if (check_mode) {
356 cinfo.out_color_space=JCS_GRAYSCALE; /* to speed up the process... */
357 cinfo.scale_denom = 8;
358 cinfo.scale_num = 1;
359 jpeg_start_decompress(&cinfo);
360
361 for (j=0;j<BUF_LINES;j++) {
362 buf[j]=malloc(sizeof(JSAMPLE)*cinfo.output_width*
363 cinfo.out_color_components);
364 if (!buf[j]) no_memory();
365 }
366
367 while (cinfo.output_scanline < cinfo.output_height) {
368 lines_read = jpeg_read_scanlines(&cinfo, buf,BUF_LINES);
369 }
370
371 jpeg_finish_decompress(&cinfo);
372 for(j=0;j<BUF_LINES;j++) free(buf[j]);
373
374 if (!global_error_counter) {
375 if (quiet_mode < 2) printf(" [OK]\n");
376 }
377 else {
378 if (quiet_mode < 2) printf(" [WARNING]\n");
379 if (delete_mode && !del_mode)
380 delete_file(current,verbose_mode,quiet_mode);
381 }
382 }
383 else { /* !check_mode */
384 if (quiet_mode < 2) printf("\n");
385 jpeg_abort_decompress(&cinfo);
386 }
387
388 fclose(infile);
389
390 } while (++i<argc || input_from_file);
391
392 jpeg_destroy_decompress(&cinfo);
393 free(buf);
394 free(MD5);
395
396 return (global_total_errors>0?1:0); /* return 1 if any errors found file(s)
397 we checked */
398 }
399
400 /* :-) */
401