PageRenderTime 129ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/gio/xdgmime/xdgmimecache.c

https://gitlab.com/ImageMagick/glib
C | 1159 lines | 907 code | 213 blank | 39 comment | 173 complexity | 4ef3dc8d5e4a6fdc8c2d59bb7ca8aa56 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-3.0
  1. /* -*- mode: C; c-file-style: "gnu" -*- */
  2. /* xdgmimealias.c: Private file. mmappable caches for mime data
  3. *
  4. * More info can be found at http://www.freedesktop.org/standards/
  5. *
  6. * Copyright (C) 2005 Matthias Clasen <mclasen@redhat.com>
  7. *
  8. * Licensed under the Academic Free License version 2.0
  9. * Or under the following terms:
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation; either
  14. * version 2.1 of the License, or (at your option) any later version.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  23. */
  24. #ifdef HAVE_CONFIG_H
  25. #include "config.h"
  26. #endif
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <fcntl.h>
  31. #include <unistd.h>
  32. #include <errno.h>
  33. #include <fnmatch.h>
  34. #include <assert.h>
  35. #include <netinet/in.h> /* for ntohl/ntohs */
  36. #ifdef HAVE_MMAP
  37. #include <sys/mman.h>
  38. #else
  39. #warning Building xdgmime without MMAP support. Binary "mime.info" cache files will not be used.
  40. #endif
  41. #include <sys/stat.h>
  42. #include <sys/types.h>
  43. #include "xdgmimecache.h"
  44. #include "xdgmimeint.h"
  45. #ifndef MAX
  46. #define MAX(a,b) ((a) > (b) ? (a) : (b))
  47. #endif
  48. #ifndef FALSE
  49. #define FALSE (0)
  50. #endif
  51. #ifndef TRUE
  52. #define TRUE (!FALSE)
  53. #endif
  54. #ifndef _O_BINARY
  55. #define _O_BINARY 0
  56. #endif
  57. #ifndef MAP_FAILED
  58. #define MAP_FAILED ((void *) -1)
  59. #endif
  60. #define MAJOR_VERSION 1
  61. #define MINOR_VERSION_MIN 1
  62. #define MINOR_VERSION_MAX 2
  63. struct _XdgMimeCache
  64. {
  65. int ref_count;
  66. int minor;
  67. size_t size;
  68. char *buffer;
  69. };
  70. #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
  71. #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
  72. XdgMimeCache *
  73. _xdg_mime_cache_ref (XdgMimeCache *cache)
  74. {
  75. cache->ref_count++;
  76. return cache;
  77. }
  78. void
  79. _xdg_mime_cache_unref (XdgMimeCache *cache)
  80. {
  81. cache->ref_count--;
  82. if (cache->ref_count == 0)
  83. {
  84. #ifdef HAVE_MMAP
  85. munmap (cache->buffer, cache->size);
  86. #endif
  87. free (cache);
  88. }
  89. }
  90. XdgMimeCache *
  91. _xdg_mime_cache_new_from_file (const char *file_name)
  92. {
  93. XdgMimeCache *cache = NULL;
  94. #ifdef HAVE_MMAP
  95. int fd = -1;
  96. struct stat st;
  97. char *buffer = NULL;
  98. int minor;
  99. /* Open the file and map it into memory */
  100. do
  101. fd = open (file_name, O_RDONLY|_O_BINARY, 0);
  102. while (fd == -1 && errno == EINTR);
  103. if (fd < 0)
  104. return NULL;
  105. if (fstat (fd, &st) < 0 || st.st_size < 4)
  106. goto done;
  107. buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
  108. if (buffer == MAP_FAILED)
  109. goto done;
  110. minor = GET_UINT16 (buffer, 2);
  111. /* Verify version */
  112. if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
  113. (minor < MINOR_VERSION_MIN ||
  114. minor > MINOR_VERSION_MAX))
  115. {
  116. munmap (buffer, st.st_size);
  117. goto done;
  118. }
  119. cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
  120. cache->minor = minor;
  121. cache->ref_count = 1;
  122. cache->buffer = buffer;
  123. cache->size = st.st_size;
  124. done:
  125. if (fd != -1)
  126. close (fd);
  127. #else /* HAVE_MMAP */
  128. cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
  129. cache->minor = 0;
  130. cache->ref_count = 1;
  131. cache->buffer = NULL;
  132. cache->size = 0;
  133. #endif /* HAVE_MMAP */
  134. return cache;
  135. }
  136. static int
  137. cache_magic_matchlet_compare_to_data (XdgMimeCache *cache,
  138. xdg_uint32_t offset,
  139. const void *data,
  140. size_t len)
  141. {
  142. xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
  143. xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
  144. xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
  145. xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
  146. xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
  147. int i, j;
  148. for (i = range_start; i < range_start + range_length; i++)
  149. {
  150. int valid_matchlet = TRUE;
  151. if (i + data_length > len)
  152. return FALSE;
  153. if (mask_offset)
  154. {
  155. for (j = 0; j < data_length; j++)
  156. {
  157. if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) !=
  158. ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j]))
  159. {
  160. valid_matchlet = FALSE;
  161. break;
  162. }
  163. }
  164. }
  165. else
  166. {
  167. for (j = 0; j < data_length; j++)
  168. {
  169. if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i])
  170. {
  171. valid_matchlet = FALSE;
  172. break;
  173. }
  174. }
  175. }
  176. if (valid_matchlet)
  177. return TRUE;
  178. }
  179. return FALSE;
  180. }
  181. static int
  182. cache_magic_matchlet_compare (XdgMimeCache *cache,
  183. xdg_uint32_t offset,
  184. const void *data,
  185. size_t len)
  186. {
  187. xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
  188. xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
  189. int i;
  190. if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
  191. {
  192. if (n_children == 0)
  193. return TRUE;
  194. for (i = 0; i < n_children; i++)
  195. {
  196. if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
  197. data, len))
  198. return TRUE;
  199. }
  200. }
  201. return FALSE;
  202. }
  203. static const char *
  204. cache_magic_compare_to_data (XdgMimeCache *cache,
  205. xdg_uint32_t offset,
  206. const void *data,
  207. size_t len,
  208. int *prio)
  209. {
  210. xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
  211. xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
  212. xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
  213. xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
  214. int i;
  215. for (i = 0; i < n_matchlets; i++)
  216. {
  217. if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32,
  218. data, len))
  219. {
  220. *prio = priority;
  221. return cache->buffer + mimetype_offset;
  222. }
  223. }
  224. return NULL;
  225. }
  226. static const char *
  227. cache_magic_lookup_data (XdgMimeCache *cache,
  228. const void *data,
  229. size_t len,
  230. int *prio,
  231. const char *mime_types[],
  232. int n_mime_types)
  233. {
  234. xdg_uint32_t list_offset;
  235. xdg_uint32_t n_entries;
  236. xdg_uint32_t offset;
  237. int j, n;
  238. *prio = 0;
  239. list_offset = GET_UINT32 (cache->buffer, 24);
  240. n_entries = GET_UINT32 (cache->buffer, list_offset);
  241. offset = GET_UINT32 (cache->buffer, list_offset + 8);
  242. for (j = 0; j < n_entries; j++)
  243. {
  244. const char *match;
  245. match = cache_magic_compare_to_data (cache, offset + 16 * j,
  246. data, len, prio);
  247. if (match)
  248. return match;
  249. else
  250. {
  251. xdg_uint32_t mimetype_offset;
  252. const char *non_match;
  253. mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
  254. non_match = cache->buffer + mimetype_offset;
  255. for (n = 0; n < n_mime_types; n++)
  256. {
  257. if (mime_types[n] &&
  258. _xdg_mime_mime_type_equal (mime_types[n], non_match))
  259. mime_types[n] = NULL;
  260. }
  261. }
  262. }
  263. return NULL;
  264. }
  265. static const char *
  266. cache_alias_lookup (const char *alias)
  267. {
  268. const char *ptr;
  269. int i, min, max, mid, cmp;
  270. for (i = 0; _caches[i]; i++)
  271. {
  272. XdgMimeCache *cache = _caches[i];
  273. xdg_uint32_t list_offset;
  274. xdg_uint32_t n_entries;
  275. xdg_uint32_t offset;
  276. if (cache->buffer == NULL)
  277. continue;
  278. list_offset = GET_UINT32 (cache->buffer, 4);
  279. n_entries = GET_UINT32 (cache->buffer, list_offset);
  280. min = 0;
  281. max = n_entries - 1;
  282. while (max >= min)
  283. {
  284. mid = (min + max) / 2;
  285. offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
  286. ptr = cache->buffer + offset;
  287. cmp = strcmp (ptr, alias);
  288. if (cmp < 0)
  289. min = mid + 1;
  290. else if (cmp > 0)
  291. max = mid - 1;
  292. else
  293. {
  294. offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
  295. return cache->buffer + offset;
  296. }
  297. }
  298. }
  299. return NULL;
  300. }
  301. typedef struct {
  302. const char *mime;
  303. int weight;
  304. } MimeWeight;
  305. static int
  306. cache_glob_lookup_literal (const char *file_name,
  307. const char *mime_types[],
  308. int n_mime_types,
  309. int case_sensitive_check)
  310. {
  311. const char *ptr;
  312. int i, min, max, mid, cmp;
  313. for (i = 0; _caches[i]; i++)
  314. {
  315. XdgMimeCache *cache = _caches[i];
  316. xdg_uint32_t list_offset;
  317. xdg_uint32_t n_entries;
  318. xdg_uint32_t offset;
  319. if (cache->buffer == NULL)
  320. continue;
  321. list_offset = GET_UINT32 (cache->buffer, 12);
  322. n_entries = GET_UINT32 (cache->buffer, list_offset);
  323. min = 0;
  324. max = n_entries - 1;
  325. while (max >= min)
  326. {
  327. mid = (min + max) / 2;
  328. offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid);
  329. ptr = cache->buffer + offset;
  330. cmp = strcmp (ptr, file_name);
  331. if (cmp < 0)
  332. min = mid + 1;
  333. else if (cmp > 0)
  334. max = mid - 1;
  335. else
  336. {
  337. int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 8);
  338. int case_sensitive = weight & 0x100;
  339. weight = weight & 0xff;
  340. if (case_sensitive_check || !case_sensitive)
  341. {
  342. offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4);
  343. mime_types[0] = (const char *)(cache->buffer + offset);
  344. return 1;
  345. }
  346. return 0;
  347. }
  348. }
  349. }
  350. return 0;
  351. }
  352. static int
  353. cache_glob_lookup_fnmatch (const char *file_name,
  354. MimeWeight mime_types[],
  355. int n_mime_types)
  356. {
  357. const char *mime_type;
  358. const char *ptr;
  359. int i, j, n;
  360. n = 0;
  361. for (i = 0; _caches[i]; i++)
  362. {
  363. XdgMimeCache *cache = _caches[i];
  364. xdg_uint32_t list_offset;
  365. xdg_uint32_t n_entries;
  366. if (cache->buffer == NULL)
  367. continue;
  368. list_offset = GET_UINT32 (cache->buffer, 20);
  369. n_entries = GET_UINT32 (cache->buffer, list_offset);
  370. for (j = 0; j < n_entries && n < n_mime_types; j++)
  371. {
  372. xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j);
  373. xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4);
  374. int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8);
  375. weight = weight & 0xff;
  376. ptr = cache->buffer + offset;
  377. mime_type = cache->buffer + mimetype_offset;
  378. /* FIXME: Not UTF-8 safe */
  379. if (fnmatch (ptr, file_name, 0) == 0)
  380. {
  381. mime_types[n].mime = mime_type;
  382. mime_types[n].weight = weight;
  383. n++;
  384. }
  385. }
  386. if (n == n_mime_types)
  387. break;
  388. }
  389. return n;
  390. }
  391. static int
  392. cache_glob_node_lookup_suffix (XdgMimeCache *cache,
  393. xdg_uint32_t n_entries,
  394. xdg_uint32_t offset,
  395. const char *file_name,
  396. int len,
  397. int case_sensitive_check,
  398. MimeWeight mime_types[],
  399. int n_mime_types)
  400. {
  401. xdg_unichar_t character;
  402. xdg_unichar_t match_char;
  403. xdg_uint32_t mimetype_offset;
  404. xdg_uint32_t n_children;
  405. xdg_uint32_t child_offset;
  406. int weight;
  407. int case_sensitive;
  408. int min, max, mid, n, i;
  409. character = file_name[len - 1];
  410. assert (character != 0);
  411. min = 0;
  412. max = n_entries - 1;
  413. while (max >= min)
  414. {
  415. mid = (min + max) / 2;
  416. match_char = GET_UINT32 (cache->buffer, offset + 12 * mid);
  417. if (match_char < character)
  418. min = mid + 1;
  419. else if (match_char > character)
  420. max = mid - 1;
  421. else
  422. {
  423. len--;
  424. n = 0;
  425. n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4);
  426. child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8);
  427. if (len > 0)
  428. {
  429. n = cache_glob_node_lookup_suffix (cache,
  430. n_children, child_offset,
  431. file_name, len,
  432. case_sensitive_check,
  433. mime_types,
  434. n_mime_types);
  435. }
  436. if (n == 0)
  437. {
  438. i = 0;
  439. while (n < n_mime_types && i < n_children)
  440. {
  441. match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i);
  442. if (match_char != 0)
  443. break;
  444. mimetype_offset = GET_UINT32 (cache->buffer, child_offset + 12 * i + 4);
  445. weight = GET_UINT32 (cache->buffer, child_offset + 12 * i + 8);
  446. case_sensitive = weight & 0x100;
  447. weight = weight & 0xff;
  448. if (case_sensitive_check || !case_sensitive)
  449. {
  450. mime_types[n].mime = cache->buffer + mimetype_offset;
  451. mime_types[n].weight = weight;
  452. n++;
  453. }
  454. i++;
  455. }
  456. }
  457. return n;
  458. }
  459. }
  460. return 0;
  461. }
  462. static int
  463. cache_glob_lookup_suffix (const char *file_name,
  464. int len,
  465. int ignore_case,
  466. MimeWeight mime_types[],
  467. int n_mime_types)
  468. {
  469. int i, n;
  470. n = 0;
  471. for (i = 0; _caches[i]; i++)
  472. {
  473. XdgMimeCache *cache = _caches[i];
  474. xdg_uint32_t list_offset;
  475. xdg_uint32_t n_entries;
  476. xdg_uint32_t offset;
  477. if (cache->buffer == NULL)
  478. continue;
  479. list_offset = GET_UINT32 (cache->buffer, 16);
  480. n_entries = GET_UINT32 (cache->buffer, list_offset);
  481. offset = GET_UINT32 (cache->buffer, list_offset + 4);
  482. n += cache_glob_node_lookup_suffix (cache,
  483. n_entries, offset,
  484. file_name, len,
  485. ignore_case,
  486. mime_types + n,
  487. n_mime_types - n);
  488. if (n == n_mime_types)
  489. break;
  490. }
  491. return n;
  492. }
  493. static int compare_mime_weight (const void *a, const void *b)
  494. {
  495. const MimeWeight *aa = (const MimeWeight *)a;
  496. const MimeWeight *bb = (const MimeWeight *)b;
  497. return bb->weight - aa->weight;
  498. }
  499. #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
  500. static char *
  501. ascii_tolower (const char *str)
  502. {
  503. char *p, *lower;
  504. lower = strdup (str);
  505. p = lower;
  506. while (*p != 0)
  507. {
  508. char c = *p;
  509. *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
  510. }
  511. return lower;
  512. }
  513. static int
  514. filter_out_dupes (MimeWeight mimes[], int n_mimes)
  515. {
  516. int last;
  517. int i, j;
  518. last = n_mimes;
  519. for (i = 0; i < last; i++)
  520. {
  521. j = i + 1;
  522. while (j < last)
  523. {
  524. if (strcmp (mimes[i].mime, mimes[j].mime) == 0)
  525. {
  526. mimes[i].weight = MAX (mimes[i].weight, mimes[j].weight);
  527. last--;
  528. mimes[j].mime = mimes[last].mime;
  529. mimes[j].weight = mimes[last].weight;
  530. }
  531. else
  532. j++;
  533. }
  534. }
  535. return last;
  536. }
  537. static int
  538. cache_glob_lookup_file_name (const char *file_name,
  539. const char *mime_types[],
  540. int n_mime_types)
  541. {
  542. int n;
  543. MimeWeight mimes[10];
  544. int n_mimes = 10;
  545. int i;
  546. int len;
  547. char *lower_case;
  548. assert (file_name != NULL && n_mime_types > 0);
  549. /* First, check the literals */
  550. lower_case = ascii_tolower (file_name);
  551. n = cache_glob_lookup_literal (lower_case, mime_types, n_mime_types, FALSE);
  552. if (n > 0)
  553. {
  554. free (lower_case);
  555. return n;
  556. }
  557. n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types, TRUE);
  558. if (n > 0)
  559. {
  560. free (lower_case);
  561. return n;
  562. }
  563. len = strlen (file_name);
  564. n = cache_glob_lookup_suffix (lower_case, len, FALSE, mimes, n_mimes);
  565. if (n < 2)
  566. n += cache_glob_lookup_suffix (file_name, len, TRUE, mimes + n, n_mimes - n);
  567. free (lower_case);
  568. /* Last, try fnmatch */
  569. if (n < 2)
  570. n += cache_glob_lookup_fnmatch (file_name, mimes + n, n_mimes - n);
  571. n = filter_out_dupes (mimes, n);
  572. qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
  573. if (n_mime_types < n)
  574. n = n_mime_types;
  575. for (i = 0; i < n; i++)
  576. mime_types[i] = mimes[i].mime;
  577. return n;
  578. }
  579. int
  580. _xdg_mime_cache_get_max_buffer_extents (void)
  581. {
  582. xdg_uint32_t offset;
  583. xdg_uint32_t max_extent;
  584. int i;
  585. max_extent = 0;
  586. for (i = 0; _caches[i]; i++)
  587. {
  588. XdgMimeCache *cache = _caches[i];
  589. if (cache->buffer == NULL)
  590. continue;
  591. offset = GET_UINT32 (cache->buffer, 24);
  592. max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
  593. }
  594. return max_extent;
  595. }
  596. static const char *
  597. cache_get_mime_type_for_data (const void *data,
  598. size_t len,
  599. int *result_prio,
  600. const char *mime_types[],
  601. int n_mime_types)
  602. {
  603. const char *mime_type;
  604. int i, n, priority;
  605. priority = 0;
  606. mime_type = NULL;
  607. for (i = 0; _caches[i]; i++)
  608. {
  609. XdgMimeCache *cache = _caches[i];
  610. int prio;
  611. const char *match;
  612. if (cache->buffer == NULL)
  613. continue;
  614. match = cache_magic_lookup_data (cache, data, len, &prio,
  615. mime_types, n_mime_types);
  616. if (prio > priority)
  617. {
  618. priority = prio;
  619. mime_type = match;
  620. }
  621. }
  622. if (result_prio)
  623. *result_prio = priority;
  624. if (priority > 0)
  625. return mime_type;
  626. for (n = 0; n < n_mime_types; n++)
  627. {
  628. if (mime_types[n])
  629. return mime_types[n];
  630. }
  631. return XDG_MIME_TYPE_UNKNOWN;
  632. }
  633. const char *
  634. _xdg_mime_cache_get_mime_type_for_data (const void *data,
  635. size_t len,
  636. int *result_prio)
  637. {
  638. return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
  639. }
  640. #ifdef NOT_USED_IN_GIO
  641. const char *
  642. _xdg_mime_cache_get_mime_type_for_file (const char *file_name,
  643. struct stat *statbuf)
  644. {
  645. const char *mime_type;
  646. const char *mime_types[10];
  647. FILE *file;
  648. unsigned char *data;
  649. int max_extent;
  650. int bytes_read;
  651. struct stat buf;
  652. const char *base_name;
  653. int n;
  654. if (file_name == NULL)
  655. return NULL;
  656. if (! _xdg_utf8_validate (file_name))
  657. return NULL;
  658. base_name = _xdg_get_base_name (file_name);
  659. n = cache_glob_lookup_file_name (base_name, mime_types, 10);
  660. if (n == 1)
  661. return mime_types[0];
  662. if (!statbuf)
  663. {
  664. if (stat (file_name, &buf) != 0)
  665. return XDG_MIME_TYPE_UNKNOWN;
  666. statbuf = &buf;
  667. }
  668. if (!S_ISREG (statbuf->st_mode))
  669. return XDG_MIME_TYPE_UNKNOWN;
  670. /* FIXME: Need to make sure that max_extent isn't totally broken. This could
  671. * be large and need getting from a stream instead of just reading it all
  672. * in. */
  673. max_extent = _xdg_mime_cache_get_max_buffer_extents ();
  674. data = malloc (max_extent);
  675. if (data == NULL)
  676. return XDG_MIME_TYPE_UNKNOWN;
  677. file = fopen (file_name, "r");
  678. if (file == NULL)
  679. {
  680. free (data);
  681. return XDG_MIME_TYPE_UNKNOWN;
  682. }
  683. bytes_read = fread (data, 1, max_extent, file);
  684. if (ferror (file))
  685. {
  686. free (data);
  687. fclose (file);
  688. return XDG_MIME_TYPE_UNKNOWN;
  689. }
  690. mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
  691. mime_types, n);
  692. free (data);
  693. fclose (file);
  694. return mime_type;
  695. }
  696. const char *
  697. _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
  698. {
  699. const char *mime_type;
  700. if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
  701. return mime_type;
  702. else
  703. return XDG_MIME_TYPE_UNKNOWN;
  704. }
  705. #endif
  706. int
  707. _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
  708. const char *mime_types[],
  709. int n_mime_types)
  710. {
  711. return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
  712. }
  713. #if 1
  714. static int
  715. ends_with (const char *str,
  716. const char *suffix)
  717. {
  718. int length;
  719. int suffix_length;
  720. length = strlen (str);
  721. suffix_length = strlen (suffix);
  722. if (length < suffix_length)
  723. return 0;
  724. if (strcmp (str + length - suffix_length, suffix) == 0)
  725. return 1;
  726. return 0;
  727. }
  728. static int
  729. is_super_type (const char *mime)
  730. {
  731. return ends_with (mime, "/*");
  732. }
  733. #endif
  734. int
  735. _xdg_mime_cache_mime_type_subclass (const char *mime,
  736. const char *base)
  737. {
  738. const char *umime, *ubase;
  739. int i, j, min, max, med, cmp;
  740. umime = _xdg_mime_cache_unalias_mime_type (mime);
  741. ubase = _xdg_mime_cache_unalias_mime_type (base);
  742. if (strcmp (umime, ubase) == 0)
  743. return 1;
  744. /* We really want to handle text/ * in GtkFileFilter, so we just
  745. * turn on the supertype matching
  746. */
  747. #if 1
  748. /* Handle supertypes */
  749. if (is_super_type (ubase) &&
  750. xdg_mime_media_type_equal (umime, ubase))
  751. return 1;
  752. #endif
  753. /* Handle special cases text/plain and application/octet-stream */
  754. if (strcmp (ubase, "text/plain") == 0 &&
  755. strncmp (umime, "text/", 5) == 0)
  756. return 1;
  757. if (strcmp (ubase, "application/octet-stream") == 0 &&
  758. strncmp (umime, "inode/", 6) != 0)
  759. return 1;
  760. for (i = 0; _caches[i]; i++)
  761. {
  762. XdgMimeCache *cache = _caches[i];
  763. xdg_uint32_t list_offset;
  764. xdg_uint32_t n_entries;
  765. xdg_uint32_t offset, n_parents, parent_offset;
  766. if (cache->buffer == NULL)
  767. continue;
  768. list_offset = GET_UINT32 (cache->buffer, 8);
  769. n_entries = GET_UINT32 (cache->buffer, list_offset);
  770. min = 0;
  771. max = n_entries - 1;
  772. while (max >= min)
  773. {
  774. med = (min + max)/2;
  775. offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
  776. cmp = strcmp (cache->buffer + offset, umime);
  777. if (cmp < 0)
  778. min = med + 1;
  779. else if (cmp > 0)
  780. max = med - 1;
  781. else
  782. {
  783. offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
  784. n_parents = GET_UINT32 (cache->buffer, offset);
  785. for (j = 0; j < n_parents; j++)
  786. {
  787. parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
  788. if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
  789. return 1;
  790. }
  791. break;
  792. }
  793. }
  794. }
  795. return 0;
  796. }
  797. const char *
  798. _xdg_mime_cache_unalias_mime_type (const char *mime)
  799. {
  800. const char *lookup;
  801. lookup = cache_alias_lookup (mime);
  802. if (lookup)
  803. return lookup;
  804. return mime;
  805. }
  806. char **
  807. _xdg_mime_cache_list_mime_parents (const char *mime)
  808. {
  809. int i, j, k, l, p;
  810. char *all_parents[128]; /* we'll stop at 128 */
  811. char **result;
  812. mime = xdg_mime_unalias_mime_type (mime);
  813. p = 0;
  814. for (i = 0; _caches[i]; i++)
  815. {
  816. XdgMimeCache *cache = _caches[i];
  817. xdg_uint32_t list_offset;
  818. xdg_uint32_t n_entries;
  819. if (cache->buffer == NULL)
  820. continue;
  821. list_offset = GET_UINT32 (cache->buffer, 8);
  822. n_entries = GET_UINT32 (cache->buffer, list_offset);
  823. for (j = 0; j < n_entries; j++)
  824. {
  825. xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
  826. xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
  827. if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
  828. {
  829. xdg_uint32_t parent_mime_offset;
  830. xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
  831. for (k = 0; k < n_parents && p < 127; k++)
  832. {
  833. parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
  834. /* Don't add same parent multiple times.
  835. * This can happen for instance if the same type is listed in multiple directories
  836. */
  837. for (l = 0; l < p; l++)
  838. {
  839. if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0)
  840. break;
  841. }
  842. if (l == p)
  843. all_parents[p++] = cache->buffer + parent_mime_offset;
  844. }
  845. break;
  846. }
  847. }
  848. }
  849. all_parents[p++] = NULL;
  850. result = (char **) malloc (p * sizeof (char *));
  851. memcpy (result, all_parents, p * sizeof (char *));
  852. return result;
  853. }
  854. static const char *
  855. cache_lookup_icon (const char *mime, int header)
  856. {
  857. const char *ptr;
  858. int i, min, max, mid, cmp;
  859. for (i = 0; _caches[i]; i++)
  860. {
  861. XdgMimeCache *cache = _caches[i];
  862. xdg_uint32_t list_offset;
  863. xdg_uint32_t n_entries;
  864. xdg_uint32_t offset;
  865. if (cache->buffer == NULL)
  866. continue;
  867. list_offset = GET_UINT32 (cache->buffer, header);
  868. n_entries = GET_UINT32 (cache->buffer, list_offset);
  869. min = 0;
  870. max = n_entries - 1;
  871. while (max >= min)
  872. {
  873. mid = (min + max) / 2;
  874. offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
  875. ptr = cache->buffer + offset;
  876. cmp = strcmp (ptr, mime);
  877. if (cmp < 0)
  878. min = mid + 1;
  879. else if (cmp > 0)
  880. max = mid - 1;
  881. else
  882. {
  883. offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
  884. return cache->buffer + offset;
  885. }
  886. }
  887. }
  888. return NULL;
  889. }
  890. const char *
  891. _xdg_mime_cache_get_generic_icon (const char *mime)
  892. {
  893. return cache_lookup_icon (mime, 36);
  894. }
  895. const char *
  896. _xdg_mime_cache_get_icon (const char *mime)
  897. {
  898. return cache_lookup_icon (mime, 32);
  899. }
  900. #ifdef NOT_USED_IN_GIO
  901. static void
  902. dump_glob_node (XdgMimeCache *cache,
  903. xdg_uint32_t offset,
  904. int depth)
  905. {
  906. xdg_unichar_t character;
  907. xdg_uint32_t mime_offset;
  908. xdg_uint32_t n_children;
  909. xdg_uint32_t child_offset;
  910. int i;
  911. character = GET_UINT32 (cache->buffer, offset);
  912. mime_offset = GET_UINT32 (cache->buffer, offset + 4);
  913. n_children = GET_UINT32 (cache->buffer, offset + 8);
  914. child_offset = GET_UINT32 (cache->buffer, offset + 12);
  915. for (i = 0; i < depth; i++)
  916. printf (" ");
  917. printf ("%c", character);
  918. if (mime_offset)
  919. printf (" - %s", cache->buffer + mime_offset);
  920. printf ("\n");
  921. if (child_offset)
  922. {
  923. for (i = 0; i < n_children; i++)
  924. dump_glob_node (cache, child_offset + 20 * i, depth + 1);
  925. }
  926. }
  927. void
  928. _xdg_mime_cache_glob_dump (void)
  929. {
  930. int i, j;
  931. for (i = 0; _caches[i]; i++)
  932. {
  933. XdgMimeCache *cache = _caches[i];
  934. xdg_uint32_t list_offset;
  935. xdg_uint32_t n_entries;
  936. xdg_uint32_t offset;
  937. if (cache->buffer == NULL)
  938. continue;
  939. list_offset = GET_UINT32 (cache->buffer, 16);
  940. n_entries = GET_UINT32 (cache->buffer, list_offset);
  941. offset = GET_UINT32 (cache->buffer, list_offset + 4);
  942. for (j = 0; j < n_entries; j++)
  943. dump_glob_node (cache, offset + 20 * j, 0);
  944. }
  945. }
  946. #endif