1 /*
2  * Copyright (c) 2001-2007, Eric M. Johnston <emj@postal.net>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Eric M. Johnston.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: exiftags.c,v 1.28 2007/12/16 02:12:01 ejohnst Exp $
33  */
34 
35 /*
36  * exiftags: dump Exif information embedded in JPEG images.
37  *
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <errno.h>
44 
45 /* For getopt(). */
46 
47 #ifndef WIN32
48 #include <unistd.h>
49 #else
50 extern char *optarg;
51 extern int optind, opterr, optopt;
52 int getopt(int, char * const [], const char *);
53 #endif
54 
55 #include "jpeg.h"
56 #include "exif.h"
57 
58 
59 int quiet;
60 static const char *version = "1.01";
61 static int fnum;
62 static const char *delim = ": ";
63 
64 
65 static void
printprops(struct exifprop * list,unsigned short lvl,int pas)66 printprops(struct exifprop *list, unsigned short lvl, int pas)
67 {
68 	static int prevf = -1;
69 	const char *n;
70 
71 	if (!quiet) {
72 		if (prevf == fnum)
73 			printf("\n");
74 		else
75 			prevf = fnum;
76 
77 		switch (lvl) {
78 		case ED_UNK:
79 			printf("Unsupported Properties:\n\n");
80 			break;
81 		case ED_CAM:
82 			printf("Camera-Specific Properties:\n\n");
83 			break;
84 		case ED_IMG:
85 			printf("Image-Specific Properties:\n\n");
86 			break;
87 		case ED_VRB:
88 			printf("Other Properties:\n\n");
89 			break;
90 		case ED_BAD:
91 			printf("Invalid Properties:\n\n");
92 			break;
93 		}
94 	}
95 
96 	while (list) {
97 
98 		/* Take care of point-and-shoot values. */
99 
100 		if (list->lvl == ED_PAS)
101 			list->lvl = pas ? ED_CAM : ED_IMG;
102 
103 		/* For now, just treat overridden values as verbose. */
104 
105 		if (list->lvl == ED_OVR)
106 			list->lvl = ED_VRB;
107 
108 		if (list->lvl == lvl) {
109 			n = list->descr ? list->descr : list->name;
110 			if (list->str)
111 				printf("%s%s%s\n", n, delim, list->str);
112 			else
113 				printf("%s%s%d\n", n, delim, list->value);
114 		}
115 
116 		list = list->next;
117 	}
118 }
119 
120 
121 static int
doit(FILE * fp,int dumplvl,int pas)122 doit(FILE *fp, int dumplvl, int pas)
123 {
124 	int mark, gotexif, first;
125 	unsigned int len, rlen;
126 	unsigned char *exifbuf;
127 	struct exiftags *t;
128 
129 	gotexif = FALSE;
130 	first = 0;
131 	exifbuf = NULL;
132 
133 	while (jpegscan(fp, &mark, &len, !(first++))) {
134 
135 		if (mark != JPEG_M_APP1) {
136 			if (fseek(fp, len, SEEK_CUR))
137 				exifdie((const char *)strerror(errno));
138 			continue;
139 		}
140 
141 		exifbuf = (unsigned char *)malloc(len);
142 		if (!exifbuf)
143 			exifdie((const char *)strerror(errno));
144 
145 		rlen = fread(exifbuf, 1, len, fp);
146 		if (rlen != len) {
147 			exifwarn("error reading JPEG (length mismatch)");
148 			free(exifbuf);
149 			return (1);
150 		}
151 
152 		t = exifparse(exifbuf, len);
153 
154 		if (t && t->props) {
155 			gotexif = TRUE;
156 
157 			if (dumplvl & ED_CAM)
158 				printprops(t->props, ED_CAM, pas);
159 			if (dumplvl & ED_IMG)
160 				printprops(t->props, ED_IMG, pas);
161 			if (dumplvl & ED_VRB)
162 				printprops(t->props, ED_VRB, pas);
163 			if (dumplvl & ED_UNK)
164 				printprops(t->props, ED_UNK, pas);
165 			if (dumplvl & ED_BAD)
166 				printprops(t->props, ED_BAD, pas);
167 		}
168 		exiffree(t);
169 		free(exifbuf);
170 	}
171 
172 	if (!gotexif) {
173 		exifwarn("couldn't find Exif data");
174 		return (1);
175 	}
176 
177 	return (0);
178 }
179 
180 
181 static
usage()182 void usage()
183 {
184 	fprintf(stderr, "Usage: %s [options] [files]\nDisplays Exif data "
185 	    "from the specified files or standard input.\n", progname);
186 	fprintf(stderr, "Version: %s\n\n", version);
187 	fprintf(stderr, "Available options:\n");
188 	fprintf(stderr, "  -a\tDisplay camera-specific, image-specific, "
189 	    "and verbose properties.\n");
190 	fprintf(stderr, "  -c\tDisplay camera-specific properties.\n");
191 	fprintf(stderr, "  -i\tDisplay image-specific properties.\n");
192 	fprintf(stderr, "  -v\tDisplay verbose properties.\n");
193 	fprintf(stderr, "  -u\tDisplay unknown/unsupported properties (also "
194 	    "invalid props w/debug).\n");
195 	fprintf(stderr, "  -l\tCamera has a removable lens.\n");
196 	fprintf(stderr, "  -d\tDisplay parse debug information.\n");
197 	fprintf(stderr, "  -q\tSuppress section headers.\n");
198 	fprintf(stderr, "  -s\tSet delimiter to provided string "
199 	    "(default: \": \").\n");
200 
201 	exit(1);
202 }
203 
204 
205 int
main(int argc,char ** argv)206 main(int argc, char **argv)
207 {
208 	register int ch;
209 	int dumplvl, pas, eval;
210 	char *mode;
211 	FILE *fp;
212 
213 	progname = argv[0];
214 	dumplvl = eval = 0;
215 	debug = quiet = FALSE;
216 	pas = TRUE;
217 #ifdef WIN32
218 	mode = "rb";
219 #else
220 	mode = "r";
221 #endif
222 
223 	while ((ch = getopt(argc, argv, "acivuldqs:")) != -1)
224 		switch (ch) {
225 		case 'a':
226 			dumplvl |= (ED_CAM | ED_IMG | ED_VRB);
227 			break;
228 		case 'c':
229 			dumplvl |= ED_CAM;
230 			break;
231 		case 'i':
232 			dumplvl |= ED_IMG;
233 			break;
234 		case 'v':
235 			dumplvl |= ED_VRB;
236 			break;
237 		case 'u':
238 			dumplvl |= ED_UNK;
239 			break;
240 		case 'l':
241 			pas = FALSE;
242 			break;
243 		case 'd':
244 			debug = TRUE;
245 			break;
246 		case 'q':
247 			quiet = TRUE;
248 			break;
249 		case 's':
250 			delim = optarg;
251 			break;
252 		case '?':
253 		default:
254 			usage();
255 		}
256 	argc -= optind;
257 	argv += optind;
258 
259 	if (!dumplvl && !debug)
260 		dumplvl |= (ED_CAM | ED_IMG);
261 
262 	if (debug && (dumplvl & ED_UNK))
263 		dumplvl |= ED_BAD;
264 
265 	if (*argv) {
266 		for (fnum = 0; *argv; ++argv) {
267 			if ((fp = fopen(*argv, mode)) == NULL) {
268 				exifwarn2(strerror(errno), *argv);
269 				eval = 1;
270 				continue;
271 			}
272 
273 			fnum++;
274 
275 			/* Print filenames if more than one. */
276 
277 			if (argc > 1)
278 				printf("%s%s:\n", fnum == 1 ? "" : "\n", *argv);
279 
280 			if (doit(fp, dumplvl, pas))
281 				eval = 1;
282 			fclose(fp);
283 		}
284         } else {
285 		if (doit(stdin, dumplvl, pas))
286 			eval = 1;
287 	}
288 
289 	exit(eval);
290 }
291