Back to index

libcitadel  8.12
xdgmimeglob.c
Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* xdgmimeglob.c: Private file.  Datastructure for storing the globs.
00003  *
00004  * More info can be found at http://www.freedesktop.org/standards/
00005  *
00006  * Copyright (C) 2003  Red Hat, Inc.
00007  * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
00008  *
00009  * Licensed under the Academic Free License version 2.0
00010  * Or under the following terms:
00011  *
00012  * This library is free software; you can redistribute it and/or
00013  * modify it under the terms of the GNU Lesser General Public
00014  * License as published by the Free Software Foundation; either
00015  * version 2 of the License, or (at your option) any later version.
00016  *
00017  * This library is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020  * Lesser General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU Lesser General Public
00023  * License along with this library; if not, write to the
00024  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00025  * Boston, MA 02111-1307, USA.
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include "xdgmimeglob.h"
00033 #include "xdgmimeint.h"
00034 #include <stdlib.h>
00035 #include <stdio.h>
00036 #include <assert.h>
00037 #include <string.h>
00038 #include <fnmatch.h>
00039 
00040 #ifndef       FALSE
00041 #define       FALSE  (0)
00042 #endif
00043 
00044 #ifndef       TRUE
00045 #define       TRUE   (!FALSE)
00046 #endif
00047 
00048 typedef struct XdgGlobHashNode XdgGlobHashNode;
00049 typedef struct XdgGlobList XdgGlobList;
00050 
00051 struct XdgGlobHashNode
00052 {
00053   xdg_unichar_t character;
00054   const char *mime_type;
00055   XdgGlobHashNode *next;
00056   XdgGlobHashNode *child;
00057 };
00058 struct XdgGlobList
00059 {
00060   const char *data;
00061   const char *mime_type;
00062   XdgGlobList *next;
00063 };
00064 
00065 struct XdgGlobHash
00066 {
00067   XdgGlobList *literal_list;
00068   XdgGlobHashNode *simple_node;
00069   XdgGlobList *full_list;
00070 };
00071 
00072 
00073 /* XdgGlobList
00074  */
00075 static XdgGlobList *
00076 _xdg_glob_list_new (void)
00077 {
00078   XdgGlobList *new_element;
00079 
00080   new_element = calloc (1, sizeof (XdgGlobList));
00081 
00082   return new_element;
00083 }
00084 
00085 /* Frees glob_list and all of it's children */
00086 static void
00087 _xdg_glob_list_free (XdgGlobList *glob_list)
00088 {
00089   XdgGlobList *ptr, *next;
00090 
00091   ptr = glob_list;
00092 
00093   while (ptr != NULL)
00094     {
00095       next = ptr->next;
00096 
00097       if (ptr->data)
00098        free ((void *) ptr->data);
00099       if (ptr->mime_type)
00100        free ((void *) ptr->mime_type);
00101       free (ptr);
00102 
00103       ptr = next;
00104     }
00105 }
00106 
00107 static XdgGlobList *
00108 _xdg_glob_list_append (XdgGlobList *glob_list,
00109                      void        *data,
00110                      const char  *mime_type)
00111 {
00112   XdgGlobList *new_element;
00113   XdgGlobList *tmp_element;
00114 
00115   new_element = _xdg_glob_list_new ();
00116   new_element->data = data;
00117   new_element->mime_type = mime_type;
00118   if (glob_list == NULL)
00119     return new_element;
00120 
00121   tmp_element = glob_list;
00122   while (tmp_element->next != NULL)
00123     tmp_element = tmp_element->next;
00124 
00125   tmp_element->next = new_element;
00126 
00127   return glob_list;
00128 }
00129 
00130 #if 0
00131 static XdgGlobList *
00132 _xdg_glob_list_prepend (XdgGlobList *glob_list,
00133                      void        *data,
00134                      const char  *mime_type)
00135 {
00136   XdgGlobList *new_element;
00137 
00138   new_element = _xdg_glob_list_new ();
00139   new_element->data = data;
00140   new_element->next = glob_list;
00141   new_element->mime_type = mime_type;
00142 
00143   return new_element;
00144 }
00145 #endif
00146 
00147 /* XdgGlobHashNode
00148  */
00149 
00150 static XdgGlobHashNode *
00151 _xdg_glob_hash_node_new (void)
00152 {
00153   XdgGlobHashNode *glob_hash_node;
00154 
00155   glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
00156 
00157   return glob_hash_node;
00158 }
00159 
00160 static void
00161 _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
00162                        int depth)
00163 {
00164   int i;
00165   for (i = 0; i < depth; i++)
00166     printf (" ");
00167 
00168   printf ("%c", (char)glob_hash_node->character);
00169   if (glob_hash_node->mime_type)
00170     printf (" - %s\n", glob_hash_node->mime_type);
00171   else
00172     printf ("\n");
00173   if (glob_hash_node->child)
00174     _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
00175   if (glob_hash_node->next)
00176     _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
00177 }
00178 
00179 static XdgGlobHashNode *
00180 _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
00181                          const char      *text,
00182                          const char      *mime_type)
00183 {
00184   XdgGlobHashNode *node;
00185   xdg_unichar_t character;
00186 
00187   character = _xdg_utf8_to_ucs4 (text);
00188 
00189   if ((glob_hash_node == NULL) ||
00190       (character < glob_hash_node->character))
00191     {
00192       node = _xdg_glob_hash_node_new ();
00193       node->character = character;
00194       node->next = glob_hash_node;
00195       glob_hash_node = node;
00196     }
00197   else if (character == glob_hash_node->character)
00198     {
00199       node = glob_hash_node;
00200     }
00201   else
00202     {
00203       XdgGlobHashNode *prev_node;
00204       int found_node = FALSE;
00205 
00206       /* Look for the first character of text in glob_hash_node, and insert it if we
00207        * have to.*/
00208       prev_node = glob_hash_node;
00209       node = prev_node->next;
00210 
00211       while (node != NULL)
00212        {
00213          if (character < node->character)
00214            {
00215              node = _xdg_glob_hash_node_new ();
00216              node->character = character;
00217              node->next = prev_node->next;
00218              prev_node->next = node;
00219 
00220              found_node = TRUE;
00221              break;
00222            }
00223          else if (character == node->character)
00224            {
00225              found_node = TRUE;
00226              break;
00227            }
00228          prev_node = node;
00229          node = node->next;
00230        }
00231 
00232       if (! found_node)
00233        {
00234          node = _xdg_glob_hash_node_new ();
00235          node->character = character;
00236          node->next = prev_node->next;
00237          prev_node->next = node;
00238        }
00239     }
00240 
00241   text = _xdg_utf8_next_char (text);
00242   if (*text == '\000')
00243     {
00244       if (node->mime_type)
00245        {
00246          if (strcmp (node->mime_type, mime_type))
00247            {
00248              XdgGlobHashNode *child;
00249              int found_node = FALSE;
00250              
00251              child = node->child;
00252              while (child && child->character == '\0')
00253               {
00254                 if (strcmp (child->mime_type, mime_type) == 0)
00255                   {
00256                     found_node = TRUE;
00257                     break;
00258                   }
00259                 child = child->next;
00260               }
00261 
00262              if (!found_node)
00263               {
00264                 child = _xdg_glob_hash_node_new ();
00265                 child->character = '\000';
00266                 child->mime_type = strdup (mime_type);
00267                 child->child = NULL;
00268                 child->next = node->child;
00269                 node->child = child;
00270               }
00271            }
00272        }
00273       else
00274        {
00275          node->mime_type = strdup (mime_type);
00276        }
00277     }
00278   else
00279     {
00280       node->child = _xdg_glob_hash_insert_text (node->child, text, mime_type);
00281     }
00282   return glob_hash_node;
00283 }
00284 
00285 static int
00286 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
00287                                   const char      *file_name,
00288                                   int              ignore_case,
00289                                   const char      *mime_types[],
00290                                   int              n_mime_types)
00291 {
00292   int n;
00293   XdgGlobHashNode *node;
00294   xdg_unichar_t character;
00295 
00296   if (glob_hash_node == NULL)
00297     return 0;
00298 
00299   character = _xdg_utf8_to_ucs4 (file_name);
00300   if (ignore_case)
00301     character = _xdg_ucs4_to_lower(character);
00302 
00303   for (node = glob_hash_node; node && character >= node->character; node = node->next)
00304     {
00305       if (character == node->character)
00306        {
00307          file_name = _xdg_utf8_next_char (file_name);
00308          if (*file_name == '\000')
00309            {
00310              n = 0;
00311 
00312              if (node->mime_type != NULL)
00313                mime_types[n++] = node->mime_type;
00314 
00315              node = node->child;
00316              while (n < n_mime_types && node && node->character == 0)
00317               {
00318                 if (node->mime_type != NULL)
00319                   mime_types[n++] = node->mime_type;
00320 
00321                 node = node->next;
00322               }
00323            }
00324          else
00325            {
00326              n = _xdg_glob_hash_node_lookup_file_name (node->child,
00327                                                  file_name,
00328                                                  ignore_case,
00329                                                  mime_types,
00330                                                  n_mime_types);
00331            }
00332          return n;
00333        }
00334     }
00335 
00336   return 0;
00337 }
00338 
00339 int
00340 _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
00341                              const char  *file_name,
00342                              const char  *mime_types[],
00343                              int          n_mime_types)
00344 {
00345   XdgGlobList *list;
00346   const char *ptr;
00347   char stopchars[128];
00348   int i, n;
00349   XdgGlobHashNode *node;
00350 
00351   /* First, check the literals */
00352 
00353   assert (file_name != NULL && n_mime_types > 0);
00354 
00355   for (list = glob_hash->literal_list; list; list = list->next)
00356     {
00357       if (strcmp ((const char *)list->data, file_name) == 0)
00358        {
00359          mime_types[0] = list->mime_type;
00360          return 1;
00361        }
00362     }
00363 
00364   i = 0;
00365   for (node = glob_hash->simple_node; node; node = node->next)
00366     {
00367       if (node->character < 128)
00368        stopchars[i++] = (char)node->character;
00369     }
00370   stopchars[i] = '\0';
00371  
00372   ptr = strpbrk (file_name, stopchars);
00373   while (ptr)
00374     {
00375       n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, FALSE,
00376                                           mime_types, n_mime_types);
00377       if (n > 0)
00378        return n;
00379       
00380       n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, TRUE,
00381                                           mime_types, n_mime_types);
00382       if (n > 0)
00383        return n;
00384       
00385       ptr = strpbrk (ptr + 1, stopchars);
00386     }
00387 
00388   /* FIXME: Not UTF-8 safe */
00389   n = 0;
00390   for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
00391     {
00392       if (fnmatch ((const char *)list->data, file_name, 0) == 0)
00393        mime_types[n++] = list->mime_type;
00394     }
00395 
00396   return n;
00397 }
00398 
00399 
00400 
00401 /* XdgGlobHash
00402  */
00403 
00404 XdgGlobHash *
00405 _xdg_glob_hash_new (void)
00406 {
00407   XdgGlobHash *glob_hash;
00408 
00409   glob_hash = calloc (1, sizeof (XdgGlobHash));
00410 
00411   return glob_hash;
00412 }
00413 
00414 
00415 static void
00416 _xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
00417 {
00418   if (node)
00419     {
00420       if (node->child)
00421        _xdg_glob_hash_free_nodes (node->child);
00422       if (node->next)
00423        _xdg_glob_hash_free_nodes (node->next);
00424       if (node->mime_type)
00425        free ((void *) node->mime_type);
00426       free (node);
00427     }
00428 }
00429 
00430 void
00431 _xdg_glob_hash_free (XdgGlobHash *glob_hash)
00432 {
00433   _xdg_glob_list_free (glob_hash->literal_list);
00434   _xdg_glob_list_free (glob_hash->full_list);
00435   _xdg_glob_hash_free_nodes (glob_hash->simple_node);
00436   free (glob_hash);
00437 }
00438 
00439 XdgGlobType
00440 _xdg_glob_determine_type (const char *glob)
00441 {
00442   const char *ptr;
00443   int maybe_in_simple_glob = FALSE;
00444   int first_char = TRUE;
00445 
00446   ptr = glob;
00447 
00448   while (*ptr != '\000')
00449     {
00450       if (*ptr == '*' && first_char)
00451        maybe_in_simple_glob = TRUE;
00452       else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
00453          return XDG_GLOB_FULL;
00454 
00455       first_char = FALSE;
00456       ptr = _xdg_utf8_next_char (ptr);
00457     }
00458   if (maybe_in_simple_glob)
00459     return XDG_GLOB_SIMPLE;
00460   else
00461     return XDG_GLOB_LITERAL;
00462 }
00463 
00464 /* glob must be valid UTF-8 */
00465 void
00466 _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
00467                          const char  *glob,
00468                          const char  *mime_type)
00469 {
00470   XdgGlobType type;
00471 
00472   assert (glob_hash != NULL);
00473   assert (glob != NULL);
00474 
00475   type = _xdg_glob_determine_type (glob);
00476 
00477   switch (type)
00478     {
00479     case XDG_GLOB_LITERAL:
00480       glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type));
00481       break;
00482     case XDG_GLOB_SIMPLE:
00483       glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type);
00484       break;
00485     case XDG_GLOB_FULL:
00486       glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type));
00487       break;
00488     }
00489 }
00490 
00491 void
00492 _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
00493 {
00494   XdgGlobList *list;
00495   printf ("LITERAL STRINGS\n");
00496   if (glob_hash->literal_list == NULL)
00497     {
00498       printf ("    None\n");
00499     }
00500   else
00501     {
00502       for (list = glob_hash->literal_list; list; list = list->next)
00503        printf ("    %s - %s\n", (char *)list->data, list->mime_type);
00504     }
00505   printf ("\nSIMPLE GLOBS\n");
00506   _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
00507 
00508   printf ("\nFULL GLOBS\n");
00509   if (glob_hash->full_list == NULL)
00510     {
00511       printf ("    None\n");
00512     }
00513   else
00514     {
00515       for (list = glob_hash->full_list; list; list = list->next)
00516        printf ("    %s - %s\n", (char *)list->data, list->mime_type);
00517     }
00518 }
00519 
00520 
00521 void
00522 _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
00523                             const char  *file_name)
00524 {
00525   FILE *glob_file;
00526   char line[255];
00527 
00528   glob_file = fopen (file_name, "r");
00529 
00530   if (glob_file == NULL)
00531     return;
00532 
00533   /* FIXME: Not UTF-8 safe.  Doesn't work if lines are greater than 255 chars.
00534    * Blah */
00535   while (fgets (line, 255, glob_file) != NULL)
00536     {
00537       char *colon;
00538       if (line[0] == '#')
00539        continue;
00540 
00541       colon = strchr (line, ':');
00542       if (colon == NULL)
00543        continue;
00544       *(colon++) = '\000';
00545       colon[strlen (colon) -1] = '\000';
00546       _xdg_glob_hash_append_glob (glob_hash, colon, line);
00547     }
00548 
00549   fclose (glob_file);
00550 }