xdgmimecache.c 24 KB

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