| OLD | NEW |
| (Empty) |
| 1 /* -*- mode: C; c-file-style: "gnu" -*- */ | |
| 2 /* xdgmime.c: XDG Mime Spec mime resolver. Based on version 0.11 of the spec. | |
| 3 * | |
| 4 * More info can be found at http://www.freedesktop.org/standards/ | |
| 5 * | |
| 6 * Copyright (C) 2003,2004 Red Hat, Inc. | |
| 7 * Copyright (C) 2003,2004 Jonathan Blandford <jrb@alum.mit.edu> | |
| 8 * | |
| 9 * Licensed under the Academic Free License version 2.0 | |
| 10 * Or under the following terms: | |
| 11 * | |
| 12 * This library is free software; you can redistribute it and/or | |
| 13 * modify it under the terms of the GNU Lesser General Public | |
| 14 * License as published by the Free Software Foundation; either | |
| 15 * version 2 of the License, or (at your option) any later version. | |
| 16 * | |
| 17 * This library is distributed in the hope that it will be useful, | |
| 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 20 * Lesser General Public License for more details. | |
| 21 * | |
| 22 * You should have received a copy of the GNU Lesser General Public | |
| 23 * License along with this library; if not, write to the | |
| 24 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
| 25 * Boston, MA 02111-1307, USA. | |
| 26 */ | |
| 27 | |
| 28 #ifdef HAVE_CONFIG_H | |
| 29 #include <config.h> | |
| 30 #endif | |
| 31 | |
| 32 #include "xdgmime.h" | |
| 33 #include "xdgmimeint.h" | |
| 34 #include "xdgmimeglob.h" | |
| 35 #include "xdgmimemagic.h" | |
| 36 #include "xdgmimealias.h" | |
| 37 #include "xdgmimeicon.h" | |
| 38 #include "xdgmimeparent.h" | |
| 39 #include "xdgmimecache.h" | |
| 40 #include <stdio.h> | |
| 41 #include <string.h> | |
| 42 #include <sys/stat.h> | |
| 43 #include <sys/types.h> | |
| 44 #include <sys/time.h> | |
| 45 #include <unistd.h> | |
| 46 #include <assert.h> | |
| 47 | |
| 48 typedef struct XdgDirTimeList XdgDirTimeList; | |
| 49 typedef struct XdgCallbackList XdgCallbackList; | |
| 50 | |
| 51 static int need_reread = TRUE; | |
| 52 static time_t last_stat_time = 0; | |
| 53 | |
| 54 static XdgGlobHash *global_hash = NULL; | |
| 55 static XdgMimeMagic *global_magic = NULL; | |
| 56 static XdgAliasList *alias_list = NULL; | |
| 57 static XdgParentList *parent_list = NULL; | |
| 58 static XdgDirTimeList *dir_time_list = NULL; | |
| 59 static XdgCallbackList *callback_list = NULL; | |
| 60 static XdgIconList *icon_list = NULL; | |
| 61 static XdgIconList *generic_icon_list = NULL; | |
| 62 | |
| 63 XdgMimeCache **_caches = NULL; | |
| 64 static int n_caches = 0; | |
| 65 | |
| 66 const char xdg_mime_type_unknown[] = "application/octet-stream"; | |
| 67 const char xdg_mime_type_empty[] = "application/x-zerosize"; | |
| 68 const char xdg_mime_type_textplain[] = "text/plain"; | |
| 69 | |
| 70 | |
| 71 enum | |
| 72 { | |
| 73 XDG_CHECKED_UNCHECKED, | |
| 74 XDG_CHECKED_VALID, | |
| 75 XDG_CHECKED_INVALID | |
| 76 }; | |
| 77 | |
| 78 struct XdgDirTimeList | |
| 79 { | |
| 80 time_t mtime; | |
| 81 char *directory_name; | |
| 82 int checked; | |
| 83 XdgDirTimeList *next; | |
| 84 }; | |
| 85 | |
| 86 struct XdgCallbackList | |
| 87 { | |
| 88 XdgCallbackList *next; | |
| 89 XdgCallbackList *prev; | |
| 90 int callback_id; | |
| 91 XdgMimeCallback callback; | |
| 92 void *data; | |
| 93 XdgMimeDestroy destroy; | |
| 94 }; | |
| 95 | |
| 96 /* Function called by xdg_run_command_on_dirs. If it returns TRUE, further | |
| 97 * directories aren't looked at */ | |
| 98 typedef int (*XdgDirectoryFunc) (const char *directory, | |
| 99 void *user_data); | |
| 100 | |
| 101 static void | |
| 102 xdg_dir_time_list_add (char *file_name, | |
| 103 time_t mtime) | |
| 104 { | |
| 105 XdgDirTimeList *list; | |
| 106 | |
| 107 for (list = dir_time_list; list; list = list->next) | |
| 108 { | |
| 109 if (strcmp (list->directory_name, file_name) == 0) | |
| 110 { | |
| 111 free (file_name); | |
| 112 return; | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 list = calloc (1, sizeof (XdgDirTimeList)); | |
| 117 list->checked = XDG_CHECKED_UNCHECKED; | |
| 118 list->directory_name = file_name; | |
| 119 list->mtime = mtime; | |
| 120 list->next = dir_time_list; | |
| 121 dir_time_list = list; | |
| 122 } | |
| 123 | |
| 124 static void | |
| 125 xdg_dir_time_list_free (XdgDirTimeList *list) | |
| 126 { | |
| 127 XdgDirTimeList *next; | |
| 128 | |
| 129 while (list) | |
| 130 { | |
| 131 next = list->next; | |
| 132 free (list->directory_name); | |
| 133 free (list); | |
| 134 list = next; | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 static int | |
| 139 xdg_mime_init_from_directory (const char *directory) | |
| 140 { | |
| 141 char *file_name; | |
| 142 struct stat st; | |
| 143 | |
| 144 assert (directory != NULL); | |
| 145 | |
| 146 file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); | |
| 147 strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); | |
| 148 if (stat (file_name, &st) == 0) | |
| 149 { | |
| 150 XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name); | |
| 151 | |
| 152 if (cache != NULL) | |
| 153 { | |
| 154 xdg_dir_time_list_add (file_name, st.st_mtime); | |
| 155 | |
| 156 _caches = realloc (_caches, sizeof (XdgMimeCache *) * (n_caches + 2)); | |
| 157 _caches[n_caches] = cache; | |
| 158 _caches[n_caches + 1] = NULL; | |
| 159 n_caches++; | |
| 160 | |
| 161 return FALSE; | |
| 162 } | |
| 163 } | |
| 164 free (file_name); | |
| 165 | |
| 166 file_name = malloc (strlen (directory) + strlen ("/mime/globs2") + 1); | |
| 167 strcpy (file_name, directory); strcat (file_name, "/mime/globs2"); | |
| 168 if (stat (file_name, &st) == 0) | |
| 169 { | |
| 170 _xdg_mime_glob_read_from_file (global_hash, file_name, TRUE); | |
| 171 xdg_dir_time_list_add (file_name, st.st_mtime); | |
| 172 } | |
| 173 else | |
| 174 { | |
| 175 free (file_name); | |
| 176 file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); | |
| 177 strcpy (file_name, directory); strcat (file_name, "/mime/globs"); | |
| 178 if (stat (file_name, &st) == 0) | |
| 179 { | |
| 180 _xdg_mime_glob_read_from_file (global_hash, file_name, FALSE); | |
| 181 xdg_dir_time_list_add (file_name, st.st_mtime); | |
| 182 } | |
| 183 else | |
| 184 { | |
| 185 free (file_name); | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); | |
| 190 strcpy (file_name, directory); strcat (file_name, "/mime/magic"); | |
| 191 if (stat (file_name, &st) == 0) | |
| 192 { | |
| 193 _xdg_mime_magic_read_from_file (global_magic, file_name); | |
| 194 xdg_dir_time_list_add (file_name, st.st_mtime); | |
| 195 } | |
| 196 else | |
| 197 { | |
| 198 free (file_name); | |
| 199 } | |
| 200 | |
| 201 file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1); | |
| 202 strcpy (file_name, directory); strcat (file_name, "/mime/aliases"); | |
| 203 _xdg_mime_alias_read_from_file (alias_list, file_name); | |
| 204 free (file_name); | |
| 205 | |
| 206 file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1); | |
| 207 strcpy (file_name, directory); strcat (file_name, "/mime/subclasses"); | |
| 208 _xdg_mime_parent_read_from_file (parent_list, file_name); | |
| 209 free (file_name); | |
| 210 | |
| 211 file_name = malloc (strlen (directory) + strlen ("/mime/icons") + 1); | |
| 212 strcpy (file_name, directory); strcat (file_name, "/mime/icons"); | |
| 213 _xdg_mime_icon_read_from_file (icon_list, file_name); | |
| 214 free (file_name); | |
| 215 | |
| 216 file_name = malloc (strlen (directory) + strlen ("/mime/generic-icons") + 1); | |
| 217 strcpy (file_name, directory); strcat (file_name, "/mime/generic-icons"); | |
| 218 _xdg_mime_icon_read_from_file (generic_icon_list, file_name); | |
| 219 free (file_name); | |
| 220 | |
| 221 return FALSE; /* Keep processing */ | |
| 222 } | |
| 223 | |
| 224 /* Runs a command on all the directories in the search path */ | |
| 225 static void | |
| 226 xdg_run_command_on_dirs (XdgDirectoryFunc func, | |
| 227 void *user_data) | |
| 228 { | |
| 229 const char *xdg_data_home; | |
| 230 const char *xdg_data_dirs; | |
| 231 const char *ptr; | |
| 232 | |
| 233 xdg_data_home = getenv ("XDG_DATA_HOME"); | |
| 234 if (xdg_data_home) | |
| 235 { | |
| 236 if ((func) (xdg_data_home, user_data)) | |
| 237 return; | |
| 238 } | |
| 239 else | |
| 240 { | |
| 241 const char *home; | |
| 242 | |
| 243 home = getenv ("HOME"); | |
| 244 if (home != NULL) | |
| 245 { | |
| 246 char *guessed_xdg_home; | |
| 247 int stop_processing; | |
| 248 | |
| 249 guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") +
1); | |
| 250 strcpy (guessed_xdg_home, home); | |
| 251 strcat (guessed_xdg_home, "/.local/share/"); | |
| 252 stop_processing = (func) (guessed_xdg_home, user_data); | |
| 253 free (guessed_xdg_home); | |
| 254 | |
| 255 if (stop_processing) | |
| 256 return; | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 xdg_data_dirs = getenv ("XDG_DATA_DIRS"); | |
| 261 if (xdg_data_dirs == NULL) | |
| 262 xdg_data_dirs = "/usr/local/share/:/usr/share/"; | |
| 263 | |
| 264 ptr = xdg_data_dirs; | |
| 265 | |
| 266 while (*ptr != '\000') | |
| 267 { | |
| 268 const char *end_ptr; | |
| 269 char *dir; | |
| 270 int len; | |
| 271 int stop_processing; | |
| 272 | |
| 273 end_ptr = ptr; | |
| 274 while (*end_ptr != ':' && *end_ptr != '\000') | |
| 275 end_ptr ++; | |
| 276 | |
| 277 if (end_ptr == ptr) | |
| 278 { | |
| 279 ptr++; | |
| 280 continue; | |
| 281 } | |
| 282 | |
| 283 if (*end_ptr == ':') | |
| 284 len = end_ptr - ptr; | |
| 285 else | |
| 286 len = end_ptr - ptr + 1; | |
| 287 dir = malloc (len + 1); | |
| 288 strncpy (dir, ptr, len); | |
| 289 dir[len] = '\0'; | |
| 290 stop_processing = (func) (dir, user_data); | |
| 291 free (dir); | |
| 292 | |
| 293 if (stop_processing) | |
| 294 return; | |
| 295 | |
| 296 ptr = end_ptr; | |
| 297 } | |
| 298 } | |
| 299 | |
| 300 /* Checks file_path to make sure it has the same mtime as last time it was | |
| 301 * checked. If it has a different mtime, or if the file doesn't exist, it | |
| 302 * returns FALSE. | |
| 303 * | |
| 304 * FIXME: This doesn't protect against permission changes. | |
| 305 */ | |
| 306 static int | |
| 307 xdg_check_file (const char *file_path, | |
| 308 int *exists) | |
| 309 { | |
| 310 struct stat st; | |
| 311 | |
| 312 /* If the file exists */ | |
| 313 if (stat (file_path, &st) == 0) | |
| 314 { | |
| 315 XdgDirTimeList *list; | |
| 316 | |
| 317 if (exists) | |
| 318 *exists = TRUE; | |
| 319 | |
| 320 for (list = dir_time_list; list; list = list->next) | |
| 321 { | |
| 322 if (! strcmp (list->directory_name, file_path)) | |
| 323 { | |
| 324 if (st.st_mtime == list->mtime) | |
| 325 list->checked = XDG_CHECKED_VALID; | |
| 326 else | |
| 327 list->checked = XDG_CHECKED_INVALID; | |
| 328 | |
| 329 return (list->checked != XDG_CHECKED_VALID); | |
| 330 } | |
| 331 } | |
| 332 return TRUE; | |
| 333 } | |
| 334 | |
| 335 if (exists) | |
| 336 *exists = FALSE; | |
| 337 | |
| 338 return FALSE; | |
| 339 } | |
| 340 | |
| 341 static int | |
| 342 xdg_check_dir (const char *directory, | |
| 343 int *invalid_dir_list) | |
| 344 { | |
| 345 int invalid, exists; | |
| 346 char *file_name; | |
| 347 | |
| 348 assert (directory != NULL); | |
| 349 | |
| 350 /* Check the mime.cache file */ | |
| 351 file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); | |
| 352 strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); | |
| 353 invalid = xdg_check_file (file_name, &exists); | |
| 354 free (file_name); | |
| 355 if (invalid) | |
| 356 { | |
| 357 *invalid_dir_list = TRUE; | |
| 358 return TRUE; | |
| 359 } | |
| 360 else if (exists) | |
| 361 { | |
| 362 return FALSE; | |
| 363 } | |
| 364 | |
| 365 /* Check the globs file */ | |
| 366 file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); | |
| 367 strcpy (file_name, directory); strcat (file_name, "/mime/globs"); | |
| 368 invalid = xdg_check_file (file_name, NULL); | |
| 369 free (file_name); | |
| 370 if (invalid) | |
| 371 { | |
| 372 *invalid_dir_list = TRUE; | |
| 373 return TRUE; | |
| 374 } | |
| 375 | |
| 376 /* Check the magic file */ | |
| 377 file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); | |
| 378 strcpy (file_name, directory); strcat (file_name, "/mime/magic"); | |
| 379 invalid = xdg_check_file (file_name, NULL); | |
| 380 free (file_name); | |
| 381 if (invalid) | |
| 382 { | |
| 383 *invalid_dir_list = TRUE; | |
| 384 return TRUE; | |
| 385 } | |
| 386 | |
| 387 return FALSE; /* Keep processing */ | |
| 388 } | |
| 389 | |
| 390 /* Walks through all the mime files stat()ing them to see if they've changed. | |
| 391 * Returns TRUE if they have. */ | |
| 392 static int | |
| 393 xdg_check_dirs (void) | |
| 394 { | |
| 395 XdgDirTimeList *list; | |
| 396 int invalid_dir_list = FALSE; | |
| 397 | |
| 398 for (list = dir_time_list; list; list = list->next) | |
| 399 list->checked = XDG_CHECKED_UNCHECKED; | |
| 400 | |
| 401 xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir, | |
| 402 &invalid_dir_list); | |
| 403 | |
| 404 if (invalid_dir_list) | |
| 405 return TRUE; | |
| 406 | |
| 407 for (list = dir_time_list; list; list = list->next) | |
| 408 { | |
| 409 if (list->checked != XDG_CHECKED_VALID) | |
| 410 return TRUE; | |
| 411 } | |
| 412 | |
| 413 return FALSE; | |
| 414 } | |
| 415 | |
| 416 /* We want to avoid stat()ing on every single mime call, so we only look for | |
| 417 * newer files every 5 seconds. This will return TRUE if we need to reread the | |
| 418 * mime data from disk. | |
| 419 */ | |
| 420 static int | |
| 421 xdg_check_time_and_dirs (void) | |
| 422 { | |
| 423 struct timeval tv; | |
| 424 time_t current_time; | |
| 425 int retval = FALSE; | |
| 426 | |
| 427 gettimeofday (&tv, NULL); | |
| 428 current_time = tv.tv_sec; | |
| 429 | |
| 430 if (current_time >= last_stat_time + 5) | |
| 431 { | |
| 432 retval = xdg_check_dirs (); | |
| 433 last_stat_time = current_time; | |
| 434 } | |
| 435 | |
| 436 return retval; | |
| 437 } | |
| 438 | |
| 439 /* Called in every public function. It reloads the hash function if need be. | |
| 440 */ | |
| 441 static void | |
| 442 xdg_mime_init (void) | |
| 443 { | |
| 444 if (xdg_check_time_and_dirs ()) | |
| 445 { | |
| 446 xdg_mime_shutdown (); | |
| 447 } | |
| 448 | |
| 449 if (need_reread) | |
| 450 { | |
| 451 global_hash = _xdg_glob_hash_new (); | |
| 452 global_magic = _xdg_mime_magic_new (); | |
| 453 alias_list = _xdg_mime_alias_list_new (); | |
| 454 parent_list = _xdg_mime_parent_list_new (); | |
| 455 icon_list = _xdg_mime_icon_list_new (); | |
| 456 generic_icon_list = _xdg_mime_icon_list_new (); | |
| 457 | |
| 458 xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory, | |
| 459 NULL); | |
| 460 | |
| 461 need_reread = FALSE; | |
| 462 } | |
| 463 } | |
| 464 | |
| 465 const char * | |
| 466 xdg_mime_get_mime_type_for_data (const void *data, | |
| 467 size_t len, | |
| 468 int *result_prio) | |
| 469 { | |
| 470 const char *mime_type; | |
| 471 | |
| 472 if (len == 0) | |
| 473 { | |
| 474 *result_prio = 100; | |
| 475 return XDG_MIME_TYPE_EMPTY; | |
| 476 } | |
| 477 | |
| 478 xdg_mime_init (); | |
| 479 | |
| 480 if (_caches) | |
| 481 mime_type = _xdg_mime_cache_get_mime_type_for_data (data, len, result_prio); | |
| 482 else | |
| 483 mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, result_pri
o, NULL, 0); | |
| 484 | |
| 485 if (mime_type) | |
| 486 return mime_type; | |
| 487 | |
| 488 return _xdg_binary_or_text_fallback(data, len); | |
| 489 } | |
| 490 | |
| 491 const char * | |
| 492 xdg_mime_get_mime_type_for_file (const char *file_name, | |
| 493 struct stat *statbuf) | |
| 494 { | |
| 495 const char *mime_type; | |
| 496 /* currently, only a few globs occur twice, and none | |
| 497 * more often, so 5 seems plenty. | |
| 498 */ | |
| 499 const char *mime_types[5]; | |
| 500 FILE *file; | |
| 501 unsigned char *data; | |
| 502 int max_extent; | |
| 503 int bytes_read; | |
| 504 struct stat buf; | |
| 505 const char *base_name; | |
| 506 int n; | |
| 507 | |
| 508 if (file_name == NULL) | |
| 509 return NULL; | |
| 510 if (! _xdg_utf8_validate (file_name)) | |
| 511 return NULL; | |
| 512 | |
| 513 xdg_mime_init (); | |
| 514 | |
| 515 if (_caches) | |
| 516 return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf); | |
| 517 | |
| 518 base_name = _xdg_get_base_name (file_name); | |
| 519 n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 5); | |
| 520 | |
| 521 if (n == 1) | |
| 522 return mime_types[0]; | |
| 523 | |
| 524 if (!statbuf) | |
| 525 { | |
| 526 if (stat (file_name, &buf) != 0) | |
| 527 return XDG_MIME_TYPE_UNKNOWN; | |
| 528 | |
| 529 statbuf = &buf; | |
| 530 } | |
| 531 | |
| 532 if (!S_ISREG (statbuf->st_mode)) | |
| 533 return XDG_MIME_TYPE_UNKNOWN; | |
| 534 | |
| 535 /* FIXME: Need to make sure that max_extent isn't totally broken. This could | |
| 536 * be large and need getting from a stream instead of just reading it all | |
| 537 * in. */ | |
| 538 max_extent = _xdg_mime_magic_get_buffer_extents (global_magic); | |
| 539 data = malloc (max_extent); | |
| 540 if (data == NULL) | |
| 541 return XDG_MIME_TYPE_UNKNOWN; | |
| 542 | |
| 543 file = fopen (file_name, "r"); | |
| 544 if (file == NULL) | |
| 545 { | |
| 546 free (data); | |
| 547 return XDG_MIME_TYPE_UNKNOWN; | |
| 548 } | |
| 549 | |
| 550 bytes_read = fread (data, 1, max_extent, file); | |
| 551 if (ferror (file)) | |
| 552 { | |
| 553 free (data); | |
| 554 fclose (file); | |
| 555 return XDG_MIME_TYPE_UNKNOWN; | |
| 556 } | |
| 557 | |
| 558 mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL, | |
| 559 mime_types, n); | |
| 560 | |
| 561 fclose (file); | |
| 562 | |
| 563 if (!mime_type) | |
| 564 mime_type = _xdg_binary_or_text_fallback(data, bytes_read); | |
| 565 | |
| 566 free (data); | |
| 567 return mime_type; | |
| 568 } | |
| 569 | |
| 570 const char * | |
| 571 xdg_mime_get_mime_type_from_file_name (const char *file_name) | |
| 572 { | |
| 573 const char *mime_type; | |
| 574 | |
| 575 xdg_mime_init (); | |
| 576 | |
| 577 if (_caches) | |
| 578 return _xdg_mime_cache_get_mime_type_from_file_name (file_name); | |
| 579 | |
| 580 if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, &mime_type, 1)) | |
| 581 return mime_type; | |
| 582 else | |
| 583 return XDG_MIME_TYPE_UNKNOWN; | |
| 584 } | |
| 585 | |
| 586 int | |
| 587 xdg_mime_get_mime_types_from_file_name (const char *file_name, | |
| 588 const char *mime_types[], | |
| 589 int n_mime_types) | |
| 590 { | |
| 591 xdg_mime_init (); | |
| 592 | |
| 593 if (_caches) | |
| 594 return _xdg_mime_cache_get_mime_types_from_file_name (file_name, mime_types,
n_mime_types); | |
| 595 | |
| 596 return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_
mime_types); | |
| 597 } | |
| 598 | |
| 599 int | |
| 600 xdg_mime_is_valid_mime_type (const char *mime_type) | |
| 601 { | |
| 602 /* FIXME: We should make this a better test | |
| 603 */ | |
| 604 return _xdg_utf8_validate (mime_type); | |
| 605 } | |
| 606 | |
| 607 void | |
| 608 xdg_mime_shutdown (void) | |
| 609 { | |
| 610 XdgCallbackList *list; | |
| 611 | |
| 612 /* FIXME: Need to make this (and the whole library) thread safe */ | |
| 613 if (dir_time_list) | |
| 614 { | |
| 615 xdg_dir_time_list_free (dir_time_list); | |
| 616 dir_time_list = NULL; | |
| 617 } | |
| 618 | |
| 619 if (global_hash) | |
| 620 { | |
| 621 _xdg_glob_hash_free (global_hash); | |
| 622 global_hash = NULL; | |
| 623 } | |
| 624 if (global_magic) | |
| 625 { | |
| 626 _xdg_mime_magic_free (global_magic); | |
| 627 global_magic = NULL; | |
| 628 } | |
| 629 | |
| 630 if (alias_list) | |
| 631 { | |
| 632 _xdg_mime_alias_list_free (alias_list); | |
| 633 alias_list = NULL; | |
| 634 } | |
| 635 | |
| 636 if (parent_list) | |
| 637 { | |
| 638 _xdg_mime_parent_list_free (parent_list); | |
| 639 parent_list = NULL; | |
| 640 } | |
| 641 | |
| 642 if (icon_list) | |
| 643 { | |
| 644 _xdg_mime_icon_list_free (icon_list); | |
| 645 icon_list = NULL; | |
| 646 } | |
| 647 | |
| 648 if (generic_icon_list) | |
| 649 { | |
| 650 _xdg_mime_icon_list_free (generic_icon_list); | |
| 651 generic_icon_list = NULL; | |
| 652 } | |
| 653 | |
| 654 if (_caches) | |
| 655 { | |
| 656 int i; | |
| 657 | |
| 658 for (i = 0; i < n_caches; i++) | |
| 659 _xdg_mime_cache_unref (_caches[i]); | |
| 660 free (_caches); | |
| 661 _caches = NULL; | |
| 662 n_caches = 0; | |
| 663 } | |
| 664 | |
| 665 for (list = callback_list; list; list = list->next) | |
| 666 (list->callback) (list->data); | |
| 667 | |
| 668 need_reread = TRUE; | |
| 669 } | |
| 670 | |
| 671 int | |
| 672 xdg_mime_get_max_buffer_extents (void) | |
| 673 { | |
| 674 xdg_mime_init (); | |
| 675 | |
| 676 if (_caches) | |
| 677 return _xdg_mime_cache_get_max_buffer_extents (); | |
| 678 | |
| 679 return _xdg_mime_magic_get_buffer_extents (global_magic); | |
| 680 } | |
| 681 | |
| 682 const char * | |
| 683 _xdg_mime_unalias_mime_type (const char *mime_type) | |
| 684 { | |
| 685 const char *lookup; | |
| 686 | |
| 687 if (_caches) | |
| 688 return _xdg_mime_cache_unalias_mime_type (mime_type); | |
| 689 | |
| 690 if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL) | |
| 691 return lookup; | |
| 692 | |
| 693 return mime_type; | |
| 694 } | |
| 695 | |
| 696 const char * | |
| 697 xdg_mime_unalias_mime_type (const char *mime_type) | |
| 698 { | |
| 699 xdg_mime_init (); | |
| 700 | |
| 701 return _xdg_mime_unalias_mime_type (mime_type); | |
| 702 } | |
| 703 | |
| 704 int | |
| 705 _xdg_mime_mime_type_equal (const char *mime_a, | |
| 706 const char *mime_b) | |
| 707 { | |
| 708 const char *unalias_a, *unalias_b; | |
| 709 | |
| 710 unalias_a = _xdg_mime_unalias_mime_type (mime_a); | |
| 711 unalias_b = _xdg_mime_unalias_mime_type (mime_b); | |
| 712 | |
| 713 if (strcmp (unalias_a, unalias_b) == 0) | |
| 714 return 1; | |
| 715 | |
| 716 return 0; | |
| 717 } | |
| 718 | |
| 719 int | |
| 720 xdg_mime_mime_type_equal (const char *mime_a, | |
| 721 const char *mime_b) | |
| 722 { | |
| 723 xdg_mime_init (); | |
| 724 | |
| 725 return _xdg_mime_mime_type_equal (mime_a, mime_b); | |
| 726 } | |
| 727 | |
| 728 int | |
| 729 xdg_mime_media_type_equal (const char *mime_a, | |
| 730 const char *mime_b) | |
| 731 { | |
| 732 char *sep; | |
| 733 | |
| 734 sep = strchr (mime_a, '/'); | |
| 735 | |
| 736 if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0) | |
| 737 return 1; | |
| 738 | |
| 739 return 0; | |
| 740 } | |
| 741 | |
| 742 #if 1 | |
| 743 static int | |
| 744 xdg_mime_is_super_type (const char *mime) | |
| 745 { | |
| 746 int length; | |
| 747 const char *type; | |
| 748 | |
| 749 length = strlen (mime); | |
| 750 type = &(mime[length - 2]); | |
| 751 | |
| 752 if (strcmp (type, "/*") == 0) | |
| 753 return 1; | |
| 754 | |
| 755 return 0; | |
| 756 } | |
| 757 #endif | |
| 758 | |
| 759 int | |
| 760 _xdg_mime_mime_type_subclass (const char *mime, | |
| 761 const char *base) | |
| 762 { | |
| 763 const char *umime, *ubase; | |
| 764 const char **parents; | |
| 765 | |
| 766 if (_caches) | |
| 767 return _xdg_mime_cache_mime_type_subclass (mime, base); | |
| 768 | |
| 769 umime = _xdg_mime_unalias_mime_type (mime); | |
| 770 ubase = _xdg_mime_unalias_mime_type (base); | |
| 771 | |
| 772 if (strcmp (umime, ubase) == 0) | |
| 773 return 1; | |
| 774 | |
| 775 #if 1 | |
| 776 /* Handle supertypes */ | |
| 777 if (xdg_mime_is_super_type (ubase) && | |
| 778 xdg_mime_media_type_equal (umime, ubase)) | |
| 779 return 1; | |
| 780 #endif | |
| 781 | |
| 782 /* Handle special cases text/plain and application/octet-stream */ | |
| 783 if (strcmp (ubase, "text/plain") == 0 && | |
| 784 strncmp (umime, "text/", 5) == 0) | |
| 785 return 1; | |
| 786 | |
| 787 if (strcmp (ubase, "application/octet-stream") == 0) | |
| 788 return 1; | |
| 789 | |
| 790 parents = _xdg_mime_parent_list_lookup (parent_list, umime); | |
| 791 for (; parents && *parents; parents++) | |
| 792 { | |
| 793 if (_xdg_mime_mime_type_subclass (*parents, ubase)) | |
| 794 return 1; | |
| 795 } | |
| 796 | |
| 797 return 0; | |
| 798 } | |
| 799 | |
| 800 int | |
| 801 xdg_mime_mime_type_subclass (const char *mime, | |
| 802 const char *base) | |
| 803 { | |
| 804 xdg_mime_init (); | |
| 805 | |
| 806 return _xdg_mime_mime_type_subclass (mime, base); | |
| 807 } | |
| 808 | |
| 809 char ** | |
| 810 xdg_mime_list_mime_parents (const char *mime) | |
| 811 { | |
| 812 const char **parents; | |
| 813 char **result; | |
| 814 int i, n; | |
| 815 | |
| 816 if (_caches) | |
| 817 return _xdg_mime_cache_list_mime_parents (mime); | |
| 818 | |
| 819 parents = xdg_mime_get_mime_parents (mime); | |
| 820 | |
| 821 if (!parents) | |
| 822 return NULL; | |
| 823 | |
| 824 for (i = 0; parents[i]; i++) ; | |
| 825 | |
| 826 n = (i + 1) * sizeof (char *); | |
| 827 result = (char **) malloc (n); | |
| 828 memcpy (result, parents, n); | |
| 829 | |
| 830 return result; | |
| 831 } | |
| 832 | |
| 833 const char ** | |
| 834 xdg_mime_get_mime_parents (const char *mime) | |
| 835 { | |
| 836 const char *umime; | |
| 837 | |
| 838 xdg_mime_init (); | |
| 839 | |
| 840 umime = _xdg_mime_unalias_mime_type (mime); | |
| 841 | |
| 842 return _xdg_mime_parent_list_lookup (parent_list, umime); | |
| 843 } | |
| 844 | |
| 845 void | |
| 846 xdg_mime_dump (void) | |
| 847 { | |
| 848 xdg_mime_init(); | |
| 849 | |
| 850 printf ("*** ALIASES ***\n\n"); | |
| 851 _xdg_mime_alias_list_dump (alias_list); | |
| 852 printf ("\n*** PARENTS ***\n\n"); | |
| 853 _xdg_mime_parent_list_dump (parent_list); | |
| 854 printf ("\n*** CACHE ***\n\n"); | |
| 855 _xdg_glob_hash_dump (global_hash); | |
| 856 printf ("\n*** GLOBS ***\n\n"); | |
| 857 _xdg_glob_hash_dump (global_hash); | |
| 858 printf ("\n*** GLOBS REVERSE TREE ***\n\n"); | |
| 859 _xdg_mime_cache_glob_dump (); | |
| 860 } | |
| 861 | |
| 862 | |
| 863 /* Registers a function to be called every time the mime database reloads its fi
les | |
| 864 */ | |
| 865 int | |
| 866 xdg_mime_register_reload_callback (XdgMimeCallback callback, | |
| 867 void *data, | |
| 868 XdgMimeDestroy destroy) | |
| 869 { | |
| 870 XdgCallbackList *list_el; | |
| 871 static int callback_id = 1; | |
| 872 | |
| 873 /* Make a new list element */ | |
| 874 list_el = calloc (1, sizeof (XdgCallbackList)); | |
| 875 list_el->callback_id = callback_id; | |
| 876 list_el->callback = callback; | |
| 877 list_el->data = data; | |
| 878 list_el->destroy = destroy; | |
| 879 list_el->next = callback_list; | |
| 880 if (list_el->next) | |
| 881 list_el->next->prev = list_el; | |
| 882 | |
| 883 callback_list = list_el; | |
| 884 callback_id ++; | |
| 885 | |
| 886 return callback_id - 1; | |
| 887 } | |
| 888 | |
| 889 void | |
| 890 xdg_mime_remove_callback (int callback_id) | |
| 891 { | |
| 892 XdgCallbackList *list; | |
| 893 | |
| 894 for (list = callback_list; list; list = list->next) | |
| 895 { | |
| 896 if (list->callback_id == callback_id) | |
| 897 { | |
| 898 if (list->next) | |
| 899 list->next = list->prev; | |
| 900 | |
| 901 if (list->prev) | |
| 902 list->prev->next = list->next; | |
| 903 else | |
| 904 callback_list = list->next; | |
| 905 | |
| 906 /* invoke the destroy handler */ | |
| 907 (list->destroy) (list->data); | |
| 908 free (list); | |
| 909 return; | |
| 910 } | |
| 911 } | |
| 912 } | |
| 913 | |
| 914 const char * | |
| 915 xdg_mime_get_icon (const char *mime) | |
| 916 { | |
| 917 xdg_mime_init (); | |
| 918 | |
| 919 if (_caches) | |
| 920 return _xdg_mime_cache_get_icon (mime); | |
| 921 | |
| 922 return _xdg_mime_icon_list_lookup (icon_list, mime); | |
| 923 } | |
| 924 | |
| 925 const char * | |
| 926 xdg_mime_get_generic_icon (const char *mime) | |
| 927 { | |
| 928 xdg_mime_init (); | |
| 929 | |
| 930 if (_caches) | |
| 931 return _xdg_mime_cache_get_generic_icon (mime); | |
| 932 | |
| 933 return _xdg_mime_icon_list_lookup (generic_icon_list, mime); | |
| 934 } | |
| OLD | NEW |