123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- /* -*- mode: C; c-file-style: "gnu" -*- */
- /* xdgmimeglob.c: Private file. Datastructure for storing the globs.
- *
- * More info can be found at http://www.freedesktop.org/standards/
- *
- * Copyright (C) 2003 Red Hat, Inc.
- * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
- *
- * Licensed under the Academic Free License version 2.0
- * Or under the following terms:
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include "xdgmimeglob.h"
- #include "xdgmimeint.h"
- #include <stdlib.h>
- #include <stdio.h>
- #include <assert.h>
- #include <string.h>
- #include <fnmatch.h>
- #ifndef FALSE
- #define FALSE (0)
- #endif
- #ifndef TRUE
- #define TRUE (!FALSE)
- #endif
- typedef struct XdgGlobHashNode XdgGlobHashNode;
- typedef struct XdgGlobList XdgGlobList;
- struct XdgGlobHashNode
- {
- xdg_unichar_t character;
- const char *mime_type;
- int weight;
- int case_sensitive;
- XdgGlobHashNode *next;
- XdgGlobHashNode *child;
- };
- struct XdgGlobList
- {
- const char *data;
- const char *mime_type;
- int weight;
- int case_sensitive;
- XdgGlobList *next;
- };
- struct XdgGlobHash
- {
- XdgGlobList *literal_list;
- XdgGlobHashNode *simple_node;
- XdgGlobList *full_list;
- };
- /* XdgGlobList
- */
- static XdgGlobList *
- _xdg_glob_list_new (void)
- {
- XdgGlobList *new_element;
- new_element = calloc (1, sizeof (XdgGlobList));
- return new_element;
- }
- /* Frees glob_list and all of its children */
- static void
- _xdg_glob_list_free (XdgGlobList *glob_list)
- {
- XdgGlobList *ptr, *next;
- ptr = glob_list;
- while (ptr != NULL)
- {
- next = ptr->next;
- if (ptr->data)
- free ((void *) ptr->data);
- if (ptr->mime_type)
- free ((void *) ptr->mime_type);
- free (ptr);
- ptr = next;
- }
- }
- static XdgGlobList *
- _xdg_glob_list_append (XdgGlobList *glob_list,
- void *data,
- const char *mime_type,
- int weight,
- int case_sensitive)
- {
- XdgGlobList *new_element;
- XdgGlobList *tmp_element;
- tmp_element = glob_list;
- while (tmp_element != NULL)
- {
- if (strcmp (tmp_element->data, data) == 0 &&
- strcmp (tmp_element->mime_type, mime_type) == 0)
- return glob_list;
- tmp_element = tmp_element->next;
- }
- new_element = _xdg_glob_list_new ();
- new_element->data = data;
- new_element->mime_type = mime_type;
- new_element->weight = weight;
- new_element->case_sensitive = case_sensitive;
- if (glob_list == NULL)
- return new_element;
- tmp_element = glob_list;
- while (tmp_element->next != NULL)
- tmp_element = tmp_element->next;
- tmp_element->next = new_element;
- return glob_list;
- }
- /* XdgGlobHashNode
- */
- static XdgGlobHashNode *
- _xdg_glob_hash_node_new (void)
- {
- XdgGlobHashNode *glob_hash_node;
- glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
- return glob_hash_node;
- }
- static void
- _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
- int depth)
- {
- int i;
- for (i = 0; i < depth; i++)
- printf (" ");
- printf ("%c", (char)glob_hash_node->character);
- if (glob_hash_node->mime_type)
- printf (" - %s %d\n", glob_hash_node->mime_type, glob_hash_node->weight);
- else
- printf ("\n");
- if (glob_hash_node->child)
- _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
- if (glob_hash_node->next)
- _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
- }
- static XdgGlobHashNode *
- _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
- xdg_unichar_t *text,
- const char *mime_type,
- int weight,
- int case_sensitive)
- {
- XdgGlobHashNode *node;
- xdg_unichar_t character;
- character = text[0];
- if ((glob_hash_node == NULL) ||
- (character < glob_hash_node->character))
- {
- node = _xdg_glob_hash_node_new ();
- node->character = character;
- node->next = glob_hash_node;
- glob_hash_node = node;
- }
- else if (character == glob_hash_node->character)
- {
- node = glob_hash_node;
- }
- else
- {
- XdgGlobHashNode *prev_node;
- int found_node = FALSE;
- /* Look for the first character of text in glob_hash_node, and insert it if we
- * have to.*/
- prev_node = glob_hash_node;
- node = prev_node->next;
- while (node != NULL)
- {
- if (character < node->character)
- {
- node = _xdg_glob_hash_node_new ();
- node->character = character;
- node->next = prev_node->next;
- prev_node->next = node;
- found_node = TRUE;
- break;
- }
- else if (character == node->character)
- {
- found_node = TRUE;
- break;
- }
- prev_node = node;
- node = node->next;
- }
- if (! found_node)
- {
- node = _xdg_glob_hash_node_new ();
- node->character = character;
- node->next = prev_node->next;
- prev_node->next = node;
- }
- }
- text++;
- if (*text == 0)
- {
- if (node->mime_type)
- {
- if (strcmp (node->mime_type, mime_type) != 0)
- {
- XdgGlobHashNode *child;
- int found_node = FALSE;
- child = node->child;
- while (child && child->character == 0)
- {
- if (strcmp (child->mime_type, mime_type) == 0)
- {
- found_node = TRUE;
- break;
- }
- child = child->next;
- }
- if (!found_node)
- {
- child = _xdg_glob_hash_node_new ();
- child->character = 0;
- child->mime_type = strdup (mime_type);
- child->weight = weight;
- child->case_sensitive = case_sensitive;
- child->child = NULL;
- child->next = node->child;
- node->child = child;
- }
- }
- }
- else
- {
- node->mime_type = strdup (mime_type);
- node->weight = weight;
- node->case_sensitive = case_sensitive;
- }
- }
- else
- {
- node->child = _xdg_glob_hash_insert_ucs4 (node->child, text, mime_type, weight, case_sensitive);
- }
- return glob_hash_node;
- }
- /* glob must be valid UTF-8 */
- static XdgGlobHashNode *
- _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
- const char *text,
- const char *mime_type,
- int weight,
- int case_sensitive)
- {
- XdgGlobHashNode *node;
- xdg_unichar_t *unitext;
- int len;
- unitext = _xdg_convert_to_ucs4 (text, &len);
- _xdg_reverse_ucs4 (unitext, len);
- node = _xdg_glob_hash_insert_ucs4 (glob_hash_node, unitext, mime_type, weight, case_sensitive);
- free (unitext);
- return node;
- }
- typedef struct {
- const char *mime;
- int weight;
- } MimeWeight;
- static int
- _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
- const char *file_name,
- int len,
- int case_sensitive_check,
- MimeWeight mime_types[],
- int n_mime_types)
- {
- int n;
- XdgGlobHashNode *node;
- xdg_unichar_t character;
- if (glob_hash_node == NULL)
- return 0;
- character = file_name[len - 1];
- for (node = glob_hash_node; node && character >= node->character; node = node->next)
- {
- if (character == node->character)
- {
- len--;
- n = 0;
- if (len > 0)
- {
- n = _xdg_glob_hash_node_lookup_file_name (node->child,
- file_name,
- len,
- case_sensitive_check,
- mime_types,
- n_mime_types);
- }
- if (n == 0)
- {
- if (node->mime_type &&
- (case_sensitive_check ||
- !node->case_sensitive))
- {
- mime_types[n].mime = node->mime_type;
- mime_types[n].weight = node->weight;
- n++;
- }
- node = node->child;
- while (n < n_mime_types && node && node->character == 0)
- {
- if (node->mime_type &&
- (case_sensitive_check ||
- !node->case_sensitive))
- {
- mime_types[n].mime = node->mime_type;
- mime_types[n].weight = node->weight;
- n++;
- }
- node = node->next;
- }
- }
- return n;
- }
- }
- return 0;
- }
- static int compare_mime_weight (const void *a, const void *b)
- {
- const MimeWeight *aa = (const MimeWeight *)a;
- const MimeWeight *bb = (const MimeWeight *)b;
- return bb->weight - aa->weight;
- }
- #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
- static char *
- ascii_tolower (const char *str)
- {
- char *p, *lower;
- lower = strdup (str);
- p = lower;
- while (*p != 0)
- {
- char c = *p;
- *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
- }
- return lower;
- }
- int
- _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
- const char *file_name,
- const char *mime_types[],
- int n_mime_types)
- {
- XdgGlobList *list;
- int i, n;
- MimeWeight mimes[10];
- int n_mimes = 10;
- int len;
- char *lower_case;
- /* First, check the literals */
- assert (file_name != NULL && n_mime_types > 0);
- n = 0;
- lower_case = ascii_tolower (file_name);
- for (list = glob_hash->literal_list; list; list = list->next)
- {
- if (strcmp ((const char *)list->data, file_name) == 0)
- {
- mime_types[0] = list->mime_type;
- free (lower_case);
- return 1;
- }
- }
- for (list = glob_hash->literal_list; list; list = list->next)
- {
- if (!list->case_sensitive &&
- strcmp ((const char *)list->data, lower_case) == 0)
- {
- mime_types[0] = list->mime_type;
- free (lower_case);
- return 1;
- }
- }
- len = strlen (file_name);
- n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, lower_case, len, FALSE,
- mimes, n_mimes);
- if (n == 0)
- n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, file_name, len, TRUE,
- mimes, n_mimes);
- if (n == 0)
- {
- for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
- {
- if (fnmatch ((const char *)list->data, file_name, 0) == 0)
- {
- mimes[n].mime = list->mime_type;
- mimes[n].weight = list->weight;
- n++;
- }
- }
- }
- free (lower_case);
- qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
- if (n_mime_types < n)
- n = n_mime_types;
- for (i = 0; i < n; i++)
- mime_types[i] = mimes[i].mime;
- return n;
- }
- /* XdgGlobHash
- */
- XdgGlobHash *
- _xdg_glob_hash_new (void)
- {
- XdgGlobHash *glob_hash;
- glob_hash = calloc (1, sizeof (XdgGlobHash));
- return glob_hash;
- }
- static void
- _xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
- {
- if (node)
- {
- if (node->child)
- _xdg_glob_hash_free_nodes (node->child);
- if (node->next)
- _xdg_glob_hash_free_nodes (node->next);
- if (node->mime_type)
- free ((void *) node->mime_type);
- free (node);
- }
- }
- void
- _xdg_glob_hash_free (XdgGlobHash *glob_hash)
- {
- _xdg_glob_list_free (glob_hash->literal_list);
- _xdg_glob_list_free (glob_hash->full_list);
- _xdg_glob_hash_free_nodes (glob_hash->simple_node);
- free (glob_hash);
- }
- XdgGlobType
- _xdg_glob_determine_type (const char *glob)
- {
- const char *ptr;
- int maybe_in_simple_glob = FALSE;
- int first_char = TRUE;
- ptr = glob;
- while (*ptr != '\0')
- {
- if (*ptr == '*' && first_char)
- maybe_in_simple_glob = TRUE;
- else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
- return XDG_GLOB_FULL;
- first_char = FALSE;
- ptr = _xdg_utf8_next_char (ptr);
- }
- if (maybe_in_simple_glob)
- return XDG_GLOB_SIMPLE;
- else
- return XDG_GLOB_LITERAL;
- }
- /* glob must be valid UTF-8 */
- void
- _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
- const char *glob,
- const char *mime_type,
- int weight,
- int case_sensitive)
- {
- XdgGlobType type;
- assert (glob_hash != NULL);
- assert (glob != NULL);
- type = _xdg_glob_determine_type (glob);
- switch (type)
- {
- case XDG_GLOB_LITERAL:
- glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type), weight, case_sensitive);
- break;
- case XDG_GLOB_SIMPLE:
- glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type, weight, case_sensitive);
- break;
- case XDG_GLOB_FULL:
- glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type), weight, case_sensitive);
- break;
- }
- }
- void
- _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
- {
- XdgGlobList *list;
- printf ("LITERAL STRINGS\n");
- if (!glob_hash || glob_hash->literal_list == NULL)
- {
- printf (" None\n");
- }
- else
- {
- for (list = glob_hash->literal_list; list; list = list->next)
- printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
- }
- printf ("\nSIMPLE GLOBS\n");
- if (!glob_hash || glob_hash->simple_node == NULL)
- {
- printf (" None\n");
- }
- else
- {
- _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
- }
- printf ("\nFULL GLOBS\n");
- if (!glob_hash || glob_hash->full_list == NULL)
- {
- printf (" None\n");
- }
- else
- {
- for (list = glob_hash->full_list; list; list = list->next)
- printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
- }
- }
- void
- _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
- const char *file_name,
- int version_two)
- {
- FILE *glob_file;
- char line[255];
- char *p;
- glob_file = fopen (file_name, "r");
- if (glob_file == NULL)
- return;
- /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
- * Blah */
- while (fgets (line, 255, glob_file) != NULL)
- {
- char *colon;
- char *mimetype, *glob, *end;
- int weight;
- int case_sensitive;
- if (line[0] == '#' || line[0] == 0)
- continue;
- end = line + strlen(line) - 1;
- if (*end == '\n')
- *end = 0;
- p = line;
- if (version_two)
- {
- colon = strchr (p, ':');
- if (colon == NULL)
- continue;
- *colon = 0;
- weight = atoi (p);
- p = colon + 1;
- }
- else
- weight = 50;
- colon = strchr (p, ':');
- if (colon == NULL)
- continue;
- *colon = 0;
- mimetype = p;
- p = colon + 1;
- glob = p;
- case_sensitive = FALSE;
- colon = strchr (p, ':');
- if (version_two && colon != NULL)
- {
- char *flag;
- /* We got flags */
- *colon = 0;
- p = colon + 1;
- /* Flags end at next colon */
- colon = strchr (p, ':');
- if (colon != NULL)
- *colon = 0;
- flag = strstr (p, "cs");
- if (flag != NULL &&
- /* Start or after comma */
- (flag == p ||
- flag[-1] == ',') &&
- /* ends with comma or end of string */
- (flag[2] == 0 ||
- flag[2] == ','))
- case_sensitive = TRUE;
- }
- _xdg_glob_hash_append_glob (glob_hash, glob, mimetype, weight, case_sensitive);
- }
- fclose (glob_file);
- }
|