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