/src/libexif/libexif/exif-content.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* exif-content.c |
2 | | * |
3 | | * Copyright (c) 2001 Lutz Mueller <lutz@users.sourceforge.net> |
4 | | * |
5 | | * This library is free software; you can redistribute it and/or |
6 | | * modify it under the terms of the GNU Lesser General Public |
7 | | * License as published by the Free Software Foundation; either |
8 | | * version 2 of the License, or (at your option) any later version. |
9 | | * |
10 | | * This library is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | * Lesser General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU Lesser General Public |
16 | | * License along with this library; if not, write to the |
17 | | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | | * Boston, MA 02110-1301 USA. |
19 | | * |
20 | | * SPDX-License-Identifier: LGPL-2.0-or-later |
21 | | */ |
22 | | |
23 | | #include <config.h> |
24 | | |
25 | | #include <libexif/exif-content.h> |
26 | | #include <libexif/exif-system.h> |
27 | | |
28 | | #include <stdlib.h> |
29 | | #include <stdio.h> |
30 | | #include <string.h> |
31 | | |
32 | | /* unused constant |
33 | | * static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; |
34 | | */ |
35 | | |
36 | | struct _ExifContentPrivate |
37 | | { |
38 | | unsigned int ref_count; |
39 | | |
40 | | ExifMem *mem; |
41 | | ExifLog *log; |
42 | | }; |
43 | | |
44 | | ExifContent * |
45 | | exif_content_new (void) |
46 | 0 | { |
47 | 0 | ExifMem *mem = exif_mem_new_default (); |
48 | 0 | ExifContent *content = exif_content_new_mem (mem); |
49 | |
|
50 | 0 | exif_mem_unref (mem); |
51 | |
|
52 | 0 | return content; |
53 | 0 | } |
54 | | |
55 | | ExifContent * |
56 | | exif_content_new_mem (ExifMem *mem) |
57 | 100k | { |
58 | 100k | ExifContent *content; |
59 | | |
60 | 100k | if (!mem) return NULL; |
61 | | |
62 | 100k | content = exif_mem_alloc (mem, (ExifLong) sizeof (ExifContent)); |
63 | 100k | if (!content) |
64 | 0 | return NULL; |
65 | 100k | content->priv = exif_mem_alloc (mem, |
66 | 100k | (ExifLong) sizeof (ExifContentPrivate)); |
67 | 100k | if (!content->priv) { |
68 | 0 | exif_mem_free (mem, content); |
69 | 0 | return NULL; |
70 | 0 | } |
71 | | |
72 | 100k | content->priv->ref_count = 1; |
73 | | |
74 | 100k | content->priv->mem = mem; |
75 | 100k | exif_mem_ref (mem); |
76 | | |
77 | 100k | return content; |
78 | 100k | } |
79 | | |
80 | | void |
81 | | exif_content_ref (ExifContent *content) |
82 | 0 | { |
83 | 0 | if (!content) |
84 | 0 | return; |
85 | | |
86 | 0 | content->priv->ref_count++; |
87 | 0 | } |
88 | | |
89 | | void |
90 | | exif_content_unref (ExifContent *content) |
91 | 100k | { |
92 | 100k | if (!content) |
93 | 0 | return; |
94 | | |
95 | 100k | content->priv->ref_count--; |
96 | 100k | if (!content->priv->ref_count) |
97 | 100k | exif_content_free (content); |
98 | 100k | } |
99 | | |
100 | | void |
101 | | exif_content_free (ExifContent *content) |
102 | 100k | { |
103 | 100k | ExifMem *mem = (content && content->priv) ? content->priv->mem : NULL; |
104 | 100k | unsigned int i; |
105 | | |
106 | 100k | if (!content) return; |
107 | | |
108 | 250k | for (i = 0; i < content->count; i++) |
109 | 149k | exif_entry_unref (content->entries[i]); |
110 | 100k | exif_mem_free (mem, content->entries); |
111 | | |
112 | 100k | if (content->priv) { |
113 | 100k | exif_log_unref (content->priv->log); |
114 | 100k | } |
115 | | |
116 | 100k | exif_mem_free (mem, content->priv); |
117 | 100k | exif_mem_free (mem, content); |
118 | 100k | exif_mem_unref (mem); |
119 | 100k | } |
120 | | |
121 | | void |
122 | | exif_content_dump (ExifContent *content, unsigned int indent) |
123 | 0 | { |
124 | 0 | char buf[1024]; |
125 | 0 | unsigned int i, l; |
126 | |
|
127 | 0 | if (!content) |
128 | 0 | return; |
129 | | |
130 | 0 | l = MIN(sizeof(buf)-1, 2*indent); |
131 | 0 | memset(buf, ' ', l); |
132 | 0 | buf[l] = '\0'; |
133 | |
|
134 | 0 | printf ("%sDumping exif content (%u entries)...\n", buf, |
135 | 0 | content->count); |
136 | 0 | for (i = 0; i < content->count; i++) |
137 | 0 | exif_entry_dump (content->entries[i], indent + 1); |
138 | 0 | } |
139 | | |
140 | | void |
141 | | exif_content_add_entry (ExifContent *c, ExifEntry *entry) |
142 | 181k | { |
143 | 181k | ExifEntry **entries; |
144 | 181k | if (!c || !c->priv || !entry || entry->parent) return; |
145 | | |
146 | | /* One tag can only be added once to an IFD. */ |
147 | 181k | if (exif_content_get_entry (c, entry->tag)) { |
148 | 27.0k | exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "ExifContent", |
149 | 27.0k | "An attempt has been made to add " |
150 | 27.0k | "the tag '%s' twice to an IFD. This is against " |
151 | 27.0k | "specification.", exif_tag_get_name (entry->tag)); |
152 | 27.0k | return; |
153 | 27.0k | } |
154 | | |
155 | 154k | entries = exif_mem_realloc (c->priv->mem, |
156 | 154k | c->entries, sizeof (ExifEntry*) * (c->count + 1)); |
157 | 154k | if (!entries) return; |
158 | 154k | entry->parent = c; |
159 | 154k | entries[c->count++] = entry; |
160 | 154k | c->entries = entries; |
161 | 154k | exif_entry_ref (entry); |
162 | 154k | } |
163 | | |
164 | | void |
165 | | exif_content_remove_entry (ExifContent *c, ExifEntry *e) |
166 | 4.71k | { |
167 | 4.71k | unsigned int i; |
168 | 4.71k | ExifEntry **t, *temp; |
169 | | |
170 | 4.71k | if (!c || !c->priv || !e || (e->parent != c)) return; |
171 | | |
172 | | /* Search the entry */ |
173 | 19.3k | for (i = 0; i < c->count; i++) |
174 | 19.3k | if (c->entries[i] == e) |
175 | 4.71k | break; |
176 | | |
177 | 4.71k | if (i == c->count) |
178 | 0 | return; |
179 | | |
180 | | /* Remove the entry */ |
181 | 4.71k | temp = c->entries[c->count-1]; |
182 | 4.71k | if (c->count > 1) { |
183 | 2.59k | t = exif_mem_realloc (c->priv->mem, c->entries, |
184 | 2.59k | sizeof(ExifEntry*) * (c->count - 1)); |
185 | 2.59k | if (!t) { |
186 | 0 | return; |
187 | 0 | } |
188 | 2.59k | c->entries = t; |
189 | 2.59k | c->count--; |
190 | 2.59k | if (i != c->count) { /* we deallocated the last slot already */ |
191 | 0 | memmove (&t[i], &t[i + 1], sizeof (ExifEntry*) * (c->count - i - 1)); |
192 | 0 | t[c->count-1] = temp; |
193 | 0 | } |
194 | 2.59k | } else { |
195 | 2.11k | exif_mem_free (c->priv->mem, c->entries); |
196 | 2.11k | c->entries = NULL; |
197 | 2.11k | c->count = 0; |
198 | 2.11k | } |
199 | 4.71k | e->parent = NULL; |
200 | 4.71k | exif_entry_unref (e); |
201 | 4.71k | } |
202 | | |
203 | | ExifEntry * |
204 | | exif_content_get_entry (ExifContent *content, ExifTag tag) |
205 | 433k | { |
206 | 433k | unsigned int i; |
207 | | |
208 | 433k | if (!content) |
209 | 0 | return (NULL); |
210 | | |
211 | 1.15M | for (i = 0; i < content->count; i++) |
212 | 826k | if (content->entries[i]->tag == tag) |
213 | 110k | return (content->entries[i]); |
214 | 323k | return (NULL); |
215 | 433k | } |
216 | | |
217 | | void |
218 | | exif_content_foreach_entry (ExifContent *content, |
219 | | ExifContentForeachEntryFunc func, void *data) |
220 | 306k | { |
221 | 306k | unsigned int i; |
222 | | |
223 | 306k | if (!content || !func) |
224 | 0 | return; |
225 | | |
226 | 652k | for (i = 0; i < content->count; i++) |
227 | 345k | func (content->entries[i], data); |
228 | 306k | } |
229 | | |
230 | | void |
231 | | exif_content_log (ExifContent *content, ExifLog *log) |
232 | 49.1k | { |
233 | 49.1k | if (!content || !content->priv || !log || content->priv->log == log) |
234 | 49.1k | return; |
235 | | |
236 | 0 | if (content->priv->log) exif_log_unref (content->priv->log); |
237 | 0 | content->priv->log = log; |
238 | 0 | exif_log_ref (log); |
239 | 0 | } |
240 | | |
241 | | ExifIfd |
242 | | exif_content_get_ifd (ExifContent *c) |
243 | 445k | { |
244 | 445k | if (!c || !c->parent) return EXIF_IFD_COUNT; |
245 | | |
246 | 445k | return |
247 | 445k | ((c)->parent->ifd[EXIF_IFD_EXIF] == (c)) ? EXIF_IFD_EXIF : |
248 | 445k | ((c)->parent->ifd[EXIF_IFD_0] == (c)) ? EXIF_IFD_0 : |
249 | 283k | ((c)->parent->ifd[EXIF_IFD_1] == (c)) ? EXIF_IFD_1 : |
250 | 150k | ((c)->parent->ifd[EXIF_IFD_GPS] == (c)) ? EXIF_IFD_GPS : |
251 | 114k | ((c)->parent->ifd[EXIF_IFD_INTEROPERABILITY] == (c)) ? EXIF_IFD_INTEROPERABILITY : |
252 | 54.9k | EXIF_IFD_COUNT; |
253 | 445k | } |
254 | | |
255 | | static void |
256 | | fix_func (ExifEntry *e, void *UNUSED(data)) |
257 | 105k | { |
258 | 105k | exif_entry_fix (e); |
259 | 105k | } |
260 | | |
261 | | /*! |
262 | | * Check if this entry is unknown and if so, delete it. |
263 | | * \note Be careful calling this function in a loop. Deleting an entry from |
264 | | * an ExifContent changes the index of subsequent entries, as well as the |
265 | | * total size of the entries array. |
266 | | */ |
267 | | static void |
268 | | remove_not_recorded (ExifEntry *e, void *UNUSED(data)) |
269 | 105k | { |
270 | 105k | ExifIfd ifd = exif_entry_get_ifd(e) ; |
271 | 105k | ExifContent *c = e->parent; |
272 | 105k | ExifDataType dt = exif_data_get_data_type (c->parent); |
273 | 105k | ExifTag t = e->tag; |
274 | | |
275 | 105k | if (exif_tag_get_support_level_in_ifd (t, ifd, dt) == |
276 | 105k | EXIF_SUPPORT_LEVEL_NOT_RECORDED) { |
277 | 0 | exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "exif-content", |
278 | 0 | "Tag 0x%04x is not recorded in IFD '%s' and has therefore been " |
279 | 0 | "removed.", t, exif_ifd_get_name (ifd)); |
280 | 0 | exif_content_remove_entry (c, e); |
281 | 0 | } |
282 | | |
283 | 105k | } |
284 | | |
285 | | void |
286 | | exif_content_fix (ExifContent *c) |
287 | 103k | { |
288 | 103k | ExifIfd ifd = exif_content_get_ifd (c); |
289 | 103k | ExifDataType dt; |
290 | 103k | ExifEntry *e; |
291 | 103k | unsigned int i, num; |
292 | | |
293 | 103k | if (!c) |
294 | 0 | return; |
295 | | |
296 | 103k | dt = exif_data_get_data_type (c->parent); |
297 | | |
298 | | /* |
299 | | * First of all, fix all existing entries. |
300 | | */ |
301 | 103k | exif_content_foreach_entry (c, fix_func, NULL); |
302 | | |
303 | | /* |
304 | | * Go through each tag and if it's not recorded, remove it. If one |
305 | | * is removed, exif_content_foreach_entry() will skip the next entry, |
306 | | * so if this happens do the loop again from the beginning to ensure |
307 | | * they're all checked. This could be avoided if we stop relying on |
308 | | * exif_content_foreach_entry but loop intelligently here. |
309 | | */ |
310 | 103k | do { |
311 | 103k | num = c->count; |
312 | 103k | exif_content_foreach_entry (c, remove_not_recorded, NULL); |
313 | 103k | } while (num != c->count); |
314 | | |
315 | | /* |
316 | | * Then check for non-existing mandatory tags and create them if needed |
317 | | */ |
318 | 103k | num = exif_tag_table_count(); |
319 | 17.4M | for (i = 0; i < num; ++i) { |
320 | 17.3M | const ExifTag t = exif_tag_table_get_tag (i); |
321 | 17.3M | if (exif_tag_get_support_level_in_ifd (t, ifd, dt) == |
322 | 17.3M | EXIF_SUPPORT_LEVEL_MANDATORY) { |
323 | 157k | if (exif_content_get_entry (c, t)) |
324 | | /* This tag already exists */ |
325 | 49.1k | continue; |
326 | 107k | exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "exif-content", |
327 | 107k | "Tag '%s' is mandatory in IFD '%s' and has therefore been added.", |
328 | 107k | exif_tag_get_name_in_ifd (t, ifd), exif_ifd_get_name (ifd)); |
329 | 107k | e = exif_entry_new (); |
330 | 107k | exif_content_add_entry (c, e); |
331 | 107k | exif_entry_initialize (e, t); |
332 | 107k | exif_entry_unref (e); |
333 | 107k | } |
334 | 17.3M | } |
335 | 103k | } |