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