| /* -*- 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 it's 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); | 
 | } |