/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 | 49.7k | { |
58 | 49.7k | ExifContent *content; |
59 | | |
60 | 49.7k | if (!mem) return NULL; |
61 | | |
62 | 49.7k | content = exif_mem_alloc (mem, (ExifLong) sizeof (ExifContent)); |
63 | 49.7k | if (!content) |
64 | 0 | return NULL; |
65 | 49.7k | content->priv = exif_mem_alloc (mem, |
66 | 49.7k | (ExifLong) sizeof (ExifContentPrivate)); |
67 | 49.7k | if (!content->priv) { |
68 | 0 | exif_mem_free (mem, content); |
69 | 0 | return NULL; |
70 | 0 | } |
71 | | |
72 | 49.7k | content->priv->ref_count = 1; |
73 | | |
74 | 49.7k | content->priv->mem = mem; |
75 | 49.7k | exif_mem_ref (mem); |
76 | | |
77 | 49.7k | return content; |
78 | 49.7k | } |
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 | 49.7k | { |
92 | 49.7k | if (!content) |
93 | 0 | return; |
94 | | |
95 | 49.7k | content->priv->ref_count--; |
96 | 49.7k | if (!content->priv->ref_count) |
97 | 49.7k | exif_content_free (content); |
98 | 49.7k | } |
99 | | |
100 | | void |
101 | | exif_content_free (ExifContent *content) |
102 | 49.7k | { |
103 | 49.7k | ExifMem *mem = (content && content->priv) ? content->priv->mem : NULL; |
104 | 49.7k | unsigned int i; |
105 | | |
106 | 49.7k | if (!content) return; |
107 | | |
108 | 117k | for (i = 0; i < content->count; i++) |
109 | 67.4k | exif_entry_unref (content->entries[i]); |
110 | 49.7k | exif_mem_free (mem, content->entries); |
111 | | |
112 | 49.7k | if (content->priv) { |
113 | 49.7k | exif_log_unref (content->priv->log); |
114 | 49.7k | } |
115 | | |
116 | 49.7k | exif_mem_free (mem, content->priv); |
117 | 49.7k | exif_mem_free (mem, content); |
118 | 49.7k | exif_mem_unref (mem); |
119 | 49.7k | } |
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 | 76.6k | { |
143 | 76.6k | ExifEntry **entries; |
144 | 76.6k | if (!c || !c->priv || !entry || entry->parent) return; |
145 | | |
146 | | /* One tag can only be added once to an IFD. */ |
147 | 76.6k | if (exif_content_get_entry (c, entry->tag)) { |
148 | 7.33k | exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "ExifContent", |
149 | 7.33k | "An attempt has been made to add " |
150 | 7.33k | "the tag '%s' twice to an IFD. This is against " |
151 | 7.33k | "specification.", exif_tag_get_name (entry->tag)); |
152 | 7.33k | return; |
153 | 7.33k | } |
154 | | |
155 | 69.2k | entries = exif_mem_realloc (c->priv->mem, |
156 | 69.2k | c->entries, sizeof (ExifEntry*) * (c->count + 1)); |
157 | 69.2k | if (!entries) return; |
158 | 69.2k | entry->parent = c; |
159 | 69.2k | entries[c->count++] = entry; |
160 | 69.2k | c->entries = entries; |
161 | 69.2k | exif_entry_ref (entry); |
162 | 69.2k | } |
163 | | |
164 | | void |
165 | | exif_content_remove_entry (ExifContent *c, ExifEntry *e) |
166 | 1.79k | { |
167 | 1.79k | unsigned int i; |
168 | 1.79k | ExifEntry **t, *temp; |
169 | | |
170 | 1.79k | if (!c || !c->priv || !e || (e->parent != c)) return; |
171 | | |
172 | | /* Search the entry */ |
173 | 9.75k | for (i = 0; i < c->count; i++) |
174 | 9.75k | if (c->entries[i] == e) |
175 | 1.79k | break; |
176 | | |
177 | 1.79k | if (i == c->count) |
178 | 0 | return; |
179 | | |
180 | | /* Remove the entry */ |
181 | 1.79k | temp = c->entries[c->count-1]; |
182 | 1.79k | if (c->count > 1) { |
183 | 1.16k | t = exif_mem_realloc (c->priv->mem, c->entries, |
184 | 1.16k | sizeof(ExifEntry*) * (c->count - 1)); |
185 | 1.16k | if (!t) { |
186 | 0 | return; |
187 | 0 | } |
188 | 1.16k | c->entries = t; |
189 | 1.16k | c->count--; |
190 | 1.16k | 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 | 1.16k | } else { |
195 | 628 | exif_mem_free (c->priv->mem, c->entries); |
196 | 628 | c->entries = NULL; |
197 | 628 | c->count = 0; |
198 | 628 | } |
199 | 1.79k | e->parent = NULL; |
200 | 1.79k | exif_entry_unref (e); |
201 | 1.79k | } |
202 | | |
203 | | ExifEntry * |
204 | | exif_content_get_entry (ExifContent *content, ExifTag tag) |
205 | 170k | { |
206 | 170k | unsigned int i; |
207 | | |
208 | 170k | if (!content) |
209 | 0 | return (NULL); |
210 | | |
211 | 459k | for (i = 0; i < content->count; i++) |
212 | 312k | if (content->entries[i]->tag == tag) |
213 | 24.0k | return (content->entries[i]); |
214 | 146k | return (NULL); |
215 | 170k | } |
216 | | |
217 | | void |
218 | | exif_content_foreach_entry (ExifContent *content, |
219 | | ExifContentForeachEntryFunc func, void *data) |
220 | 111k | { |
221 | 111k | unsigned int i; |
222 | | |
223 | 111k | if (!content || !func) |
224 | 0 | return; |
225 | | |
226 | 214k | for (i = 0; i < content->count; i++) |
227 | 103k | func (content->entries[i], data); |
228 | 111k | } |
229 | | |
230 | | void |
231 | | exif_content_log (ExifContent *content, ExifLog *log) |
232 | 49.7k | { |
233 | 49.7k | if (!content || !content->priv || !log || content->priv->log == log) |
234 | 49.7k | 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 | 134k | { |
244 | 134k | if (!c || !c->parent) return EXIF_IFD_COUNT; |
245 | | |
246 | 134k | return |
247 | 134k | ((c)->parent->ifd[EXIF_IFD_EXIF] == (c)) ? EXIF_IFD_EXIF : |
248 | 134k | ((c)->parent->ifd[EXIF_IFD_0] == (c)) ? EXIF_IFD_0 : |
249 | 85.0k | ((c)->parent->ifd[EXIF_IFD_1] == (c)) ? EXIF_IFD_1 : |
250 | 44.1k | ((c)->parent->ifd[EXIF_IFD_GPS] == (c)) ? EXIF_IFD_GPS : |
251 | 34.1k | ((c)->parent->ifd[EXIF_IFD_INTEROPERABILITY] == (c)) ? EXIF_IFD_INTEROPERABILITY : |
252 | 16.6k | EXIF_IFD_COUNT; |
253 | 134k | } |
254 | | |
255 | | static void |
256 | | fix_func (ExifEntry *e, void *UNUSED(data)) |
257 | 18.0k | { |
258 | 18.0k | exif_entry_fix (e); |
259 | 18.0k | } |
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 | 18.0k | { |
270 | 18.0k | ExifIfd ifd = exif_entry_get_ifd(e) ; |
271 | 18.0k | ExifContent *c = e->parent; |
272 | 18.0k | ExifDataType dt = exif_data_get_data_type (c->parent); |
273 | 18.0k | ExifTag t = e->tag; |
274 | | |
275 | 18.0k | if (exif_tag_get_support_level_in_ifd (t, ifd, dt) == |
276 | 18.0k | 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 | 18.0k | } |
284 | | |
285 | | void |
286 | | exif_content_fix (ExifContent *c) |
287 | 30.7k | { |
288 | 30.7k | ExifIfd ifd = exif_content_get_ifd (c); |
289 | 30.7k | ExifDataType dt; |
290 | 30.7k | ExifEntry *e; |
291 | 30.7k | unsigned int i, num; |
292 | | |
293 | 30.7k | if (!c) |
294 | 0 | return; |
295 | | |
296 | 30.7k | dt = exif_data_get_data_type (c->parent); |
297 | | |
298 | | /* |
299 | | * First of all, fix all existing entries. |
300 | | */ |
301 | 30.7k | 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 | 30.7k | do { |
311 | 30.7k | num = c->count; |
312 | 30.7k | exif_content_foreach_entry (c, remove_not_recorded, NULL); |
313 | 30.7k | } while (num != c->count); |
314 | | |
315 | | /* |
316 | | * Then check for non-existing mandatory tags and create them if needed |
317 | | */ |
318 | 30.7k | num = exif_tag_table_count(); |
319 | 5.20M | for (i = 0; i < num; ++i) { |
320 | 5.17M | const ExifTag t = exif_tag_table_get_tag (i); |
321 | 5.17M | if (exif_tag_get_support_level_in_ifd (t, ifd, dt) == |
322 | 5.17M | EXIF_SUPPORT_LEVEL_MANDATORY) { |
323 | 46.6k | if (exif_content_get_entry (c, t)) |
324 | | /* This tag already exists */ |
325 | 400 | continue; |
326 | 46.2k | exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "exif-content", |
327 | 46.2k | "Tag '%s' is mandatory in IFD '%s' and has therefore been added.", |
328 | 46.2k | exif_tag_get_name_in_ifd (t, ifd), exif_ifd_get_name (ifd)); |
329 | 46.2k | e = exif_entry_new (); |
330 | 46.2k | exif_content_add_entry (c, e); |
331 | 46.2k | exif_entry_initialize (e, t); |
332 | 46.2k | exif_entry_unref (e); |
333 | 46.2k | } |
334 | 5.17M | } |
335 | 30.7k | } |