gstreamer_core/gst/gstregistryxml.c
branchRCL_3
changeset 30 7e817e7e631c
parent 0 0e761a78d257
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
       
     1 /* GStreamer
       
     2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
       
     3  *                    2000 Wim Taymans <wtay@chello.be>
       
     4  *                    2005 David A. Schleef <ds@schleef.org>
       
     5  *
       
     6  * gstregistryxml.c: GstRegistry object, support routines
       
     7  *
       
     8  * This library is free software; you can redistribute it and/or
       
     9  * modify it ulnder the terms of the GNU Library General Public
       
    10  * License as published by the Free Software Foundation; either
       
    11  * version 2 of the License, or (at your option) any later version.
       
    12  *
       
    13  * This library is distributed in the hope that it will be useful,
       
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    16  * Library General Public License for more details.
       
    17  *
       
    18  * You should have received a copy of the GNU Library General Public
       
    19  * License along with this library; if not, write to the
       
    20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    21  * Boston, MA 02111-1307, USA.
       
    22  */
       
    23 
       
    24 #ifdef HAVE_CONFIG_H
       
    25 #  include "config.h"
       
    26 #endif
       
    27 
       
    28 #include <stdio.h>
       
    29 #include <errno.h>
       
    30 #include <sys/types.h>
       
    31 #include <sys/stat.h>
       
    32 #include <dirent.h>
       
    33 #include <fcntl.h>
       
    34 #ifdef HAVE_UNISTD_H
       
    35 #include <unistd.h>
       
    36 #endif
       
    37 
       
    38 #include <gst/gst_private.h>
       
    39 #include <gst/gstelement.h>
       
    40 #include <gst/gsttypefind.h>
       
    41 #include <gst/gsttypefindfactory.h>
       
    42 #include <gst/gsturi.h>
       
    43 #include <gst/gstinfo.h>
       
    44 #include <gst/gstenumtypes.h>
       
    45 #include <gst/gstregistry.h>
       
    46 
       
    47 #include <libxml/xmlreader.h>
       
    48 
       
    49 #include "glib-compat-private.h"
       
    50 #include <glib/gstdio.h>
       
    51 
       
    52 #define BLOCK_SIZE 1024*10
       
    53 
       
    54 #define GST_CAT_DEFAULT GST_CAT_REGISTRY
       
    55 
       
    56 #define CLASS(registry)  GST_XML_REGISTRY_CLASS (G_OBJECT_GET_CLASS (registry))
       
    57 
       
    58 static gboolean
       
    59 gst_registry_save (GstRegistry * registry, gchar * format, ...)
       
    60 {
       
    61   va_list var_args;
       
    62   gsize written, len;
       
    63   gboolean ret;
       
    64   char *str;
       
    65 
       
    66   va_start (var_args, format);
       
    67   str = g_strdup_vprintf (format, var_args);
       
    68   va_end (var_args);
       
    69 
       
    70   len = strlen (str);
       
    71 
       
    72   written = write (registry->cache_file, str, len);
       
    73 
       
    74   if (len == written)
       
    75     ret = TRUE;
       
    76   else {
       
    77     ret = FALSE;
       
    78     GST_ERROR ("Failed to write registry to temporary file: %s",
       
    79         g_strerror (errno));
       
    80   }
       
    81 
       
    82   g_free (str);
       
    83 
       
    84   return ret;
       
    85 }
       
    86 
       
    87 static void
       
    88 add_to_char_array (gchar *** array, gchar * value)
       
    89 {
       
    90   gchar **new;
       
    91   gchar **old = *array;
       
    92   gint i = 0;
       
    93 
       
    94   /* expensive, but cycles are cheap... */
       
    95   if (old)
       
    96     while (old[i])
       
    97       i++;
       
    98   new = g_new0 (gchar *, i + 2);
       
    99   new[i] = value;
       
   100   while (i > 0) {
       
   101     i--;
       
   102     new[i] = old[i];
       
   103   }
       
   104   g_free (old);
       
   105   *array = new;
       
   106 }
       
   107 
       
   108 /* read a string and copy it into the given location */
       
   109 static gboolean
       
   110 read_string (xmlTextReaderPtr reader, gchar ** write_to, gboolean allow_blank)
       
   111 {
       
   112   int depth = xmlTextReaderDepth (reader);
       
   113   gboolean found = FALSE;
       
   114 
       
   115   while (xmlTextReaderRead (reader) == 1) {
       
   116     if (xmlTextReaderDepth (reader) == depth) {
       
   117       if (allow_blank && !found &&
       
   118           xmlTextReaderNodeType (reader) == XML_READER_TYPE_END_ELEMENT) {
       
   119         /* Allow blank strings */
       
   120         *write_to = g_strdup ("");
       
   121         found = TRUE;
       
   122       }
       
   123       return found;
       
   124     }
       
   125     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_TEXT) {
       
   126       xmlChar *value;
       
   127 
       
   128       if (found)
       
   129         return FALSE;
       
   130 
       
   131       value = xmlTextReaderValue (reader);
       
   132       *write_to = g_strdup ((gchar *) value);
       
   133       xmlFree (value);
       
   134 
       
   135       found = TRUE;
       
   136     }
       
   137   }
       
   138   return FALSE;
       
   139 }
       
   140 
       
   141 static gboolean
       
   142 read_const_interned_string (xmlTextReaderPtr reader, const gchar ** write_to,
       
   143     gboolean allow_blank)
       
   144 {
       
   145   gchar *s = NULL;
       
   146 
       
   147   if (!read_string (reader, &s, allow_blank))
       
   148     return FALSE;
       
   149 
       
   150   *write_to = g_intern_string (s);
       
   151   g_free (s);
       
   152   return TRUE;
       
   153 }
       
   154 
       
   155 static gboolean
       
   156 read_uint (xmlTextReaderPtr reader, guint * write_to)
       
   157 {
       
   158   int depth = xmlTextReaderDepth (reader);
       
   159   gboolean found = FALSE;
       
   160 
       
   161   while (xmlTextReaderRead (reader) == 1) {
       
   162     if (xmlTextReaderDepth (reader) == depth)
       
   163       return found;
       
   164     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_TEXT) {
       
   165       gchar *ret;
       
   166       const gchar *s;
       
   167 
       
   168       if (found) {
       
   169         GST_DEBUG ("failed to read uint, multiple text nodes");
       
   170         return FALSE;
       
   171       }
       
   172       s = (const gchar *) xmlTextReaderConstValue (reader);
       
   173       *write_to = strtol (s, &ret, 0);
       
   174       if (s == ret) {
       
   175         GST_DEBUG ("failed to read uint, text didn't convert to int");
       
   176         return FALSE;
       
   177       }
       
   178       found = TRUE;
       
   179     }
       
   180   }
       
   181   GST_DEBUG ("failed to read uint, no text node");
       
   182   return FALSE;
       
   183 }
       
   184 
       
   185 static gboolean
       
   186 read_enum (xmlTextReaderPtr reader, GType enum_type, guint * write_to)
       
   187 {
       
   188   int depth = xmlTextReaderDepth (reader);
       
   189   gboolean found = FALSE;
       
   190 
       
   191   if (*write_to)
       
   192     return FALSE;
       
   193   while (xmlTextReaderRead (reader) == 1) {
       
   194     if (xmlTextReaderDepth (reader) == depth)
       
   195       return found;
       
   196     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_TEXT) {
       
   197       GEnumClass *enum_class;
       
   198       GEnumValue *value;
       
   199 
       
   200       if (found)
       
   201         return FALSE;
       
   202       enum_class = g_type_class_ref (enum_type);
       
   203       if (!enum_class)
       
   204         return FALSE;
       
   205       value =
       
   206           g_enum_get_value_by_nick (enum_class,
       
   207           (gchar *) xmlTextReaderConstValue (reader));
       
   208       if (value) {
       
   209         *write_to = value->value;
       
   210         found = TRUE;
       
   211       }
       
   212       g_type_class_unref (enum_class);
       
   213     }
       
   214   }
       
   215   return FALSE;
       
   216 }
       
   217 
       
   218 static GstStaticPadTemplate *
       
   219 load_pad_template (xmlTextReaderPtr reader)
       
   220 {
       
   221   int ret;
       
   222   int depth = xmlTextReaderDepth (reader);
       
   223   const gchar *name = NULL;
       
   224   gchar *caps_str = NULL;
       
   225   guint direction = 0, presence = 0;
       
   226 
       
   227   while ((ret = xmlTextReaderRead (reader)) == 1) {
       
   228     if (xmlTextReaderDepth (reader) == depth) {
       
   229       GstStaticPadTemplate *template;
       
   230 
       
   231       template = g_new0 (GstStaticPadTemplate, 1);
       
   232       template->name_template = name;   /* must be an interned string! */
       
   233       template->presence = presence;
       
   234       template->direction = direction;
       
   235       template->static_caps.string = caps_str;
       
   236 
       
   237       return template;
       
   238     }
       
   239     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
       
   240         xmlTextReaderDepth (reader) == depth + 1) {
       
   241       const gchar *tag = (gchar *) xmlTextReaderConstName (reader);
       
   242 
       
   243       if (g_str_equal (tag, "nametemplate")) {
       
   244         read_const_interned_string (reader, &name, FALSE);
       
   245       } else if (g_str_equal (tag, "direction")) {
       
   246         read_enum (reader, GST_TYPE_PAD_DIRECTION, &direction);
       
   247       } else if (g_str_equal (tag, "presence")) {
       
   248         read_enum (reader, GST_TYPE_PAD_PRESENCE, &presence);
       
   249       } else if (!strncmp (tag, "caps", 4)) {
       
   250         read_string (reader, &caps_str, FALSE);
       
   251       }
       
   252     }
       
   253   }
       
   254   g_free (caps_str);
       
   255 
       
   256   return NULL;
       
   257 }
       
   258 
       
   259 static GstPluginFeature *
       
   260 load_feature (xmlTextReaderPtr reader)
       
   261 {
       
   262   int ret;
       
   263   int depth;
       
   264   xmlChar *feature_name;
       
   265   GstPluginFeature *feature;
       
   266   GType type;
       
   267 
       
   268   depth = xmlTextReaderDepth (reader);
       
   269   feature_name = xmlTextReaderGetAttribute (reader, BAD_CAST "typename");
       
   270 
       
   271   GST_LOG ("loading feature '%s'", GST_STR_NULL ((const char *) feature_name));
       
   272 
       
   273   if (!feature_name)
       
   274     return NULL;
       
   275 
       
   276   type = g_type_from_name ((const char *) feature_name);
       
   277   xmlFree (feature_name);
       
   278   feature_name = NULL;
       
   279 
       
   280   if (!type) {
       
   281     return NULL;
       
   282   }
       
   283   feature = g_object_new (type, NULL);
       
   284   if (!feature) {
       
   285     return NULL;
       
   286   }
       
   287   if (!GST_IS_PLUGIN_FEATURE (feature)) {
       
   288     /* don't really know what it is */
       
   289     if (GST_IS_OBJECT (feature))
       
   290       gst_object_unref (feature);
       
   291     else
       
   292       g_object_unref (feature);
       
   293     return NULL;
       
   294   }
       
   295   while ((ret = xmlTextReaderRead (reader)) == 1) {
       
   296     if (xmlTextReaderDepth (reader) == depth) {
       
   297       GST_LOG ("loaded feature %p with name %s", feature, feature->name);
       
   298       return feature;
       
   299     }
       
   300     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
       
   301         xmlTextReaderDepth (reader) == depth + 1) {
       
   302       const gchar *tag = (gchar *) xmlTextReaderConstName (reader);
       
   303 
       
   304       if (g_str_equal (tag, "name"))
       
   305         read_string (reader, &feature->name, FALSE);
       
   306       else if (g_str_equal (tag, "rank"))
       
   307         read_uint (reader, &feature->rank);
       
   308 
       
   309       if (GST_IS_ELEMENT_FACTORY (feature)) {
       
   310         GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (feature);
       
   311 
       
   312         if (g_str_equal (tag, "longname")) {
       
   313           int ret;
       
   314 
       
   315           ret = read_string (reader, &factory->details.longname, TRUE);
       
   316           GST_LOG ("longname ret=%d, name=%s", ret, factory->details.longname);
       
   317         } else if (g_str_equal (tag, "class")) {
       
   318           read_string (reader, &factory->details.klass, TRUE);
       
   319         } else if (g_str_equal (tag, "description")) {
       
   320           read_string (reader, &factory->details.description, TRUE);
       
   321         } else if (g_str_equal (tag, "author")) {
       
   322           read_string (reader, &factory->details.author, TRUE);
       
   323         } else if (g_str_equal (tag, "uri_type")) {
       
   324           gchar *s = NULL;
       
   325 
       
   326           if (read_string (reader, &s, FALSE)) {
       
   327             if (g_ascii_strncasecmp (s, "sink", 4) == 0) {
       
   328               factory->uri_type = GST_URI_SINK;
       
   329             } else if (g_ascii_strncasecmp (s, "source", 5) == 0) {
       
   330               factory->uri_type = GST_URI_SRC;
       
   331             }
       
   332             g_free (s);
       
   333           }
       
   334         } else if (g_str_equal (tag, "uri_protocol")) {
       
   335           gchar *s = NULL;
       
   336 
       
   337           if (read_string (reader, &s, FALSE))
       
   338             add_to_char_array (&factory->uri_protocols, s);
       
   339         } else if (g_str_equal (tag, "interface")) {
       
   340           gchar *s = NULL;
       
   341 
       
   342           if (read_string (reader, &s, FALSE)) {
       
   343             __gst_element_factory_add_interface (factory, s);
       
   344             /* add_interface strdup's s */
       
   345             g_free (s);
       
   346           }
       
   347         } else if (g_str_equal (tag, "padtemplate")) {
       
   348           GstStaticPadTemplate *template = load_pad_template (reader);
       
   349 
       
   350           if (template) {
       
   351             GST_LOG ("adding template %s to factory %s",
       
   352                 GST_STR_NULL (GST_PAD_TEMPLATE_NAME_TEMPLATE (template)),
       
   353                 GST_PLUGIN_FEATURE_NAME (feature));
       
   354             __gst_element_factory_add_static_pad_template (factory, template);
       
   355           }
       
   356         }
       
   357       } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
       
   358         GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
       
   359 
       
   360         if (g_str_equal (tag, "extension")) {
       
   361           gchar *s = NULL;
       
   362 
       
   363           if (read_string (reader, &s, TRUE))
       
   364             add_to_char_array (&factory->extensions, s);
       
   365         } else if (g_str_equal (tag, "caps")) {
       
   366           gchar *s = NULL;
       
   367 
       
   368           if (read_string (reader, &s, FALSE)) {
       
   369             factory->caps = gst_caps_from_string (s);
       
   370             g_free (s);
       
   371           }
       
   372         }
       
   373 #ifndef GST_DISABLE_INDEX
       
   374       } else if (GST_IS_INDEX_FACTORY (feature)) {
       
   375         GstIndexFactory *factory = GST_INDEX_FACTORY (feature);
       
   376 
       
   377         if (g_str_equal (tag, "longdesc"))
       
   378           read_string (reader, &factory->longdesc, TRUE);
       
   379 #endif
       
   380       }
       
   381     }
       
   382   }
       
   383 
       
   384   GST_WARNING ("Error reading feature from registry: registry corrupt?");
       
   385   return NULL;
       
   386 }
       
   387 
       
   388 static GstPlugin *
       
   389 load_plugin (xmlTextReaderPtr reader, GList ** feature_list)
       
   390 {
       
   391   int ret;
       
   392   GstPlugin *plugin;
       
   393 
       
   394   *feature_list = NULL;
       
   395 
       
   396   GST_LOG ("creating new plugin and parsing");
       
   397 
       
   398   plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
       
   399 
       
   400   plugin->flags |= GST_PLUGIN_FLAG_CACHED;
       
   401   while ((ret = xmlTextReaderRead (reader)) == 1) {
       
   402     if (xmlTextReaderDepth (reader) == 1) {
       
   403       return plugin;
       
   404     }
       
   405     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
       
   406         xmlTextReaderDepth (reader) == 2) {
       
   407       const gchar *tag = (gchar *) xmlTextReaderConstName (reader);
       
   408 
       
   409       if (g_str_equal (tag, "name")) {
       
   410         int ret;
       
   411 
       
   412         ret = read_const_interned_string (reader, &plugin->desc.name, FALSE);
       
   413         GST_LOG ("name ret=%d, name=%s", ret, plugin->desc.name);
       
   414         if (!ret)
       
   415           break;
       
   416       } else if (g_str_equal (tag, "description")) {
       
   417         if (!read_string (reader, &plugin->desc.description, TRUE)) {
       
   418           GST_WARNING ("description field was invalid in registry");
       
   419           break;
       
   420         }
       
   421         GST_LOG ("description %s", plugin->desc.description);
       
   422       } else if (g_str_equal (tag, "filename")) {
       
   423         if (!read_string (reader, &plugin->filename, FALSE)) {
       
   424           GST_WARNING ("filename field was invalid in registry");
       
   425           break;
       
   426         }
       
   427         GST_LOG ("filename %s", plugin->filename);
       
   428         plugin->basename = g_path_get_basename (plugin->filename);
       
   429       } else if (g_str_equal (tag, "version")) {
       
   430         if (!read_const_interned_string (reader, &plugin->desc.version, TRUE)) {
       
   431           GST_WARNING ("version field was invalid in registry");
       
   432           break;
       
   433         }
       
   434         GST_LOG ("version %s", plugin->desc.version);
       
   435       } else if (g_str_equal (tag, "license")) {
       
   436         if (!read_const_interned_string (reader, &plugin->desc.license, TRUE)) {
       
   437           GST_WARNING ("license field was invalid in registry");
       
   438           break;
       
   439         }
       
   440         GST_LOG ("license %s", plugin->desc.license);
       
   441       } else if (g_str_equal (tag, "source")) {
       
   442         if (!read_const_interned_string (reader, &plugin->desc.source, TRUE)) {
       
   443           GST_WARNING ("source field was invalid in registry");
       
   444           break;
       
   445         }
       
   446         GST_LOG ("source %s", plugin->desc.source);
       
   447       } else if (g_str_equal (tag, "package")) {
       
   448         if (!read_const_interned_string (reader, &plugin->desc.package, TRUE)) {
       
   449           GST_WARNING ("package field was invalid in registry");
       
   450           break;
       
   451         }
       
   452         GST_LOG ("package %s", plugin->desc.package);
       
   453       } else if (g_str_equal (tag, "origin")) {
       
   454         if (!read_const_interned_string (reader, &plugin->desc.origin, TRUE)) {
       
   455           GST_WARNING ("failed to read origin");
       
   456           break;
       
   457         }
       
   458       } else if (g_str_equal (tag, "m32p")) {
       
   459         char *s;
       
   460 
       
   461         if (!read_string (reader, &s, FALSE)) {
       
   462           GST_WARNING ("failed to read mtime");
       
   463           break;
       
   464         }
       
   465         plugin->file_mtime = strtol (s, NULL, 0);
       
   466         GST_LOG ("mtime %d", (int) plugin->file_mtime);
       
   467         g_free (s);
       
   468       } else if (g_str_equal (tag, "size")) {
       
   469         unsigned int x;
       
   470 
       
   471         if (read_uint (reader, &x)) {
       
   472           plugin->file_size = x;
       
   473           GST_LOG ("file_size %" G_GINT64_FORMAT, (gint64) plugin->file_size);
       
   474         } else {
       
   475           GST_WARNING ("failed to read size");
       
   476         }
       
   477       } else if (g_str_equal (tag, "feature")) {
       
   478         GstPluginFeature *feature = load_feature (reader);
       
   479 
       
   480         if (feature) {
       
   481           feature->plugin_name = plugin->desc.name;     /* interned string */
       
   482           *feature_list = g_list_prepend (*feature_list, feature);
       
   483         }
       
   484       } else {
       
   485         GST_WARNING ("unknown tag %s", tag);
       
   486       }
       
   487     }
       
   488   }
       
   489   gst_object_unref (plugin);
       
   490 
       
   491   GST_WARNING ("problem reading plugin");
       
   492 
       
   493   return NULL;
       
   494 }
       
   495 
       
   496 /**
       
   497  * gst_registry_xml_read_cache:
       
   498  * @registry: a #GstRegistry
       
   499  * @location: a filename
       
   500  *
       
   501  * Read the contents of the XML cache file at @location into @registry.
       
   502  *
       
   503  * Returns: %TRUE on success.
       
   504  */
       
   505 gboolean
       
   506 gst_registry_xml_read_cache (GstRegistry * registry, const char *location)
       
   507 {
       
   508   GMappedFile *mapped = NULL;
       
   509   GTimer *timer;
       
   510   gdouble seconds;
       
   511   xmlTextReaderPtr reader = NULL;
       
   512   int ret;
       
   513   gboolean in_registry = FALSE;
       
   514   FILE *file = NULL;
       
   515 
       
   516   /* make sure these types exist */
       
   517   GST_TYPE_ELEMENT_FACTORY;
       
   518   GST_TYPE_TYPE_FIND_FACTORY;
       
   519 #ifndef GST_DISABLE_INDEX
       
   520   GST_TYPE_INDEX_FACTORY;
       
   521 #endif
       
   522 
       
   523   timer = g_timer_new ();
       
   524 
       
   525   mapped = g_mapped_file_new (location, FALSE, NULL);
       
   526   if (mapped) {
       
   527     reader = xmlReaderForMemory (g_mapped_file_get_contents (mapped),
       
   528         g_mapped_file_get_length (mapped), NULL, NULL, 0);
       
   529     if (reader == NULL) {
       
   530       g_mapped_file_free (mapped);
       
   531       mapped = NULL;
       
   532     }
       
   533   }
       
   534 
       
   535   if (reader == NULL) {
       
   536     file = fopen (location, "r");
       
   537     if (file == NULL) {
       
   538       g_timer_destroy (timer);
       
   539       return FALSE;
       
   540     }
       
   541 
       
   542     reader = xmlReaderForFd (fileno (file), NULL, NULL, 0);
       
   543     if (!reader) {
       
   544       fclose (file);
       
   545       g_timer_destroy (timer);
       
   546       return FALSE;
       
   547     }
       
   548   }
       
   549 
       
   550   while ((ret = xmlTextReaderRead (reader)) == 1) {
       
   551     if (xmlTextReaderDepth (reader) == 0) {
       
   552       in_registry = xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
       
   553           g_str_equal ("GST-PluginRegistry", xmlTextReaderConstName (reader));
       
   554     } else if (in_registry) {
       
   555       if (xmlTextReaderDepth (reader) == 1 &&
       
   556           xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT) {
       
   557         const gchar *tag = (const gchar *) xmlTextReaderConstName (reader);
       
   558 
       
   559         if (g_str_equal (tag, "plugin")) {
       
   560           GList *feature_list;
       
   561           GstPlugin *plugin = load_plugin (reader, &feature_list);
       
   562 
       
   563           if (plugin) {
       
   564             GList *g;
       
   565 
       
   566             gst_registry_add_plugin (registry, plugin);
       
   567             for (g = feature_list; g; g = g_list_next (g)) {
       
   568               gst_registry_add_feature (registry,
       
   569                   GST_PLUGIN_FEATURE_CAST (g->data));
       
   570             }
       
   571             g_list_free (feature_list);
       
   572           }
       
   573         }
       
   574       }
       
   575     }
       
   576   }
       
   577   xmlFreeTextReader (reader);
       
   578   if (ret != 0) {
       
   579     GST_ERROR ("parsing registry cache: %s", location);
       
   580     if (mapped)
       
   581       g_mapped_file_free (mapped);
       
   582     if (file)
       
   583       fclose (file);
       
   584     g_timer_destroy (timer);
       
   585     return FALSE;
       
   586   }
       
   587 
       
   588   g_timer_stop (timer);
       
   589   seconds = g_timer_elapsed (timer, NULL);
       
   590   g_timer_destroy (timer);
       
   591 
       
   592   GST_INFO ("loaded %s in %lf seconds", location, seconds);
       
   593 
       
   594   if (mapped)
       
   595     g_mapped_file_free (mapped);
       
   596 
       
   597   if (file)
       
   598     fclose (file);
       
   599 
       
   600   return TRUE;
       
   601 }
       
   602 
       
   603 /*
       
   604  * Save
       
   605  */
       
   606 static gboolean
       
   607 gst_registry_save_escaped (GstRegistry * registry, const char *prefix,
       
   608     const char *tag, const char *value)
       
   609 {
       
   610   gboolean ret = TRUE;
       
   611 
       
   612   if (value) {
       
   613     gchar *v;
       
   614 
       
   615     if (g_utf8_validate (value, -1, NULL)) {
       
   616       v = g_markup_escape_text (value, -1);
       
   617     } else {
       
   618       g_warning ("Invalid UTF-8 while saving registry tag '%s'", tag);
       
   619       v = g_strdup ("[ERROR: invalid UTF-8]");
       
   620     }
       
   621 
       
   622     ret = gst_registry_save (registry, "%s<%s>%s</%s>\n", prefix, tag, v, tag);
       
   623     g_free (v);
       
   624   }
       
   625 
       
   626   return ret;
       
   627 }
       
   628 
       
   629 
       
   630 static gboolean
       
   631 gst_registry_xml_save_caps (GstRegistry * registry, const GstCaps * caps)
       
   632 {
       
   633   /* we copy the caps here so we can simplify them before saving. This is a lot
       
   634    * faster when loading them later on */
       
   635   char *s;
       
   636   GstCaps *copy = gst_caps_copy (caps);
       
   637   gboolean ret;
       
   638 
       
   639   gst_caps_do_simplify (copy);
       
   640   s = gst_caps_to_string (copy);
       
   641   gst_caps_unref (copy);
       
   642 
       
   643   ret = gst_registry_save_escaped (registry, "  ", "caps", s);
       
   644   g_free (s);
       
   645   return ret;
       
   646 }
       
   647 
       
   648 static gboolean
       
   649 gst_registry_xml_save_pad_template (GstRegistry * registry,
       
   650     GstStaticPadTemplate * template)
       
   651 {
       
   652   gchar *presence;
       
   653 
       
   654   if (!gst_registry_save_escaped (registry, "   ", "nametemplate",
       
   655           template->name_template))
       
   656     return FALSE;
       
   657 
       
   658   if (!gst_registry_save (registry,
       
   659           "   <direction>%s</direction>\n",
       
   660           (template->direction == GST_PAD_SINK ? "sink" : "src")))
       
   661     return FALSE;
       
   662 
       
   663   switch (template->presence) {
       
   664     case GST_PAD_ALWAYS:
       
   665       presence = "always";
       
   666       break;
       
   667     case GST_PAD_SOMETIMES:
       
   668       presence = "sometimes";
       
   669       break;
       
   670     case GST_PAD_REQUEST:
       
   671       presence = "request";
       
   672       break;
       
   673     default:
       
   674       presence = "unknown";
       
   675       break;
       
   676   }
       
   677   if (!gst_registry_save (registry, "   <presence>%s</presence>\n", presence))
       
   678     return FALSE;
       
   679 
       
   680   if (template->static_caps.string) {
       
   681     if (!gst_registry_save (registry, "   <caps>%s</caps>\n",
       
   682             template->static_caps.string))
       
   683       return FALSE;
       
   684   }
       
   685   return TRUE;
       
   686 }
       
   687 
       
   688 static gboolean
       
   689 gst_registry_xml_save_feature (GstRegistry * registry,
       
   690     GstPluginFeature * feature)
       
   691 {
       
   692   if (!gst_registry_save_escaped (registry, "  ", "name", feature->name))
       
   693     return FALSE;
       
   694 
       
   695   if (feature->rank > 0) {
       
   696     gint rank = feature->rank;
       
   697 
       
   698     if (!gst_registry_save (registry, "  <rank>%d</rank>\n", rank))
       
   699       return FALSE;
       
   700   }
       
   701 
       
   702   if (GST_IS_ELEMENT_FACTORY (feature)) {
       
   703     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
       
   704     GList *walk;
       
   705 
       
   706     if (!gst_registry_save_escaped (registry, "  ", "longname",
       
   707             factory->details.longname))
       
   708       return FALSE;
       
   709     if (!gst_registry_save_escaped (registry, "  ", "class",
       
   710             factory->details.klass))
       
   711       return FALSE;
       
   712     if (!gst_registry_save_escaped (registry, "  ", "description",
       
   713             factory->details.description))
       
   714       return FALSE;
       
   715     if (!gst_registry_save_escaped (registry, "  ", "author",
       
   716             factory->details.author))
       
   717       return FALSE;
       
   718 
       
   719     walk = factory->staticpadtemplates;
       
   720 
       
   721     while (walk) {
       
   722       GstStaticPadTemplate *template = walk->data;
       
   723 
       
   724       if (!gst_registry_save (registry, "  <padtemplate>\n"))
       
   725         return FALSE;
       
   726       if (!gst_registry_xml_save_pad_template (registry, template))
       
   727         return FALSE;
       
   728       if (!gst_registry_save (registry, "  </padtemplate>\n"))
       
   729         return FALSE;
       
   730 
       
   731       walk = g_list_next (walk);
       
   732     }
       
   733 
       
   734     walk = factory->interfaces;
       
   735     while (walk) {
       
   736       if (!gst_registry_save_escaped (registry, "  ", "interface",
       
   737               (gchar *) walk->data))
       
   738         return FALSE;
       
   739       walk = g_list_next (walk);
       
   740     }
       
   741 
       
   742     if (GST_URI_TYPE_IS_VALID (factory->uri_type)) {
       
   743       if (!gst_registry_save_escaped (registry, "  ", "uri_type",
       
   744               factory->uri_type == GST_URI_SINK ? "sink" : "source"))
       
   745         return FALSE;
       
   746       if (factory->uri_protocols) {
       
   747         gchar **protocol;
       
   748 
       
   749         protocol = factory->uri_protocols;
       
   750         while (*protocol) {
       
   751           if (!gst_registry_save_escaped (registry, "  ", "uri_protocol",
       
   752                   *protocol))
       
   753             return FALSE;
       
   754           protocol++;
       
   755         }
       
   756       } else {
       
   757         g_warning ("GStreamer feature '%s' is URI handler but does not provide"
       
   758             " any protocols it can handle", feature->name);
       
   759       }
       
   760     }
       
   761   } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
       
   762     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
       
   763     gint i = 0;
       
   764 
       
   765     if (factory->caps) {
       
   766       if (!gst_registry_xml_save_caps (registry, factory->caps))
       
   767         return FALSE;
       
   768     }
       
   769     if (factory->extensions) {
       
   770       while (factory->extensions[i]) {
       
   771         if (!gst_registry_save_escaped (registry, "  ", "extension",
       
   772                 factory->extensions[i]))
       
   773           return FALSE;
       
   774         i++;
       
   775       }
       
   776     }
       
   777 #ifndef GST_DISABLE_INDEX
       
   778   } else if (GST_IS_INDEX_FACTORY (feature)) {
       
   779     if (!gst_registry_save_escaped (registry, "  ", "longdesc",
       
   780             GST_INDEX_FACTORY (feature)->longdesc))
       
   781       return FALSE;
       
   782 #endif
       
   783   }
       
   784   return TRUE;
       
   785 }
       
   786 
       
   787 static gboolean
       
   788 gst_registry_xml_save_plugin (GstRegistry * registry, GstPlugin * plugin)
       
   789 {
       
   790   GList *list;
       
   791   GList *walk;
       
   792   char s[100];
       
   793 
       
   794   if (!gst_registry_save_escaped (registry, " ", "name", plugin->desc.name))
       
   795     return FALSE;
       
   796   if (!gst_registry_save_escaped (registry, " ", "description",
       
   797           plugin->desc.description))
       
   798     return FALSE;
       
   799   if (!gst_registry_save_escaped (registry, " ", "filename", plugin->filename))
       
   800     return FALSE;
       
   801 
       
   802   sprintf (s, "%d", (int) plugin->file_size);
       
   803   if (!gst_registry_save_escaped (registry, " ", "size", s))
       
   804     return FALSE;
       
   805 
       
   806   sprintf (s, "%d", (int) plugin->file_mtime);
       
   807   if (!gst_registry_save_escaped (registry, " ", "m32p", s))
       
   808     return FALSE;
       
   809 
       
   810   if (!gst_registry_save_escaped (registry, " ", "version",
       
   811           plugin->desc.version))
       
   812     return FALSE;
       
   813   if (!gst_registry_save_escaped (registry, " ", "license",
       
   814           plugin->desc.license))
       
   815     return FALSE;
       
   816   if (!gst_registry_save_escaped (registry, " ", "source", plugin->desc.source))
       
   817     return FALSE;
       
   818   if (!gst_registry_save_escaped (registry, " ", "package",
       
   819           plugin->desc.package))
       
   820     return FALSE;
       
   821   if (!gst_registry_save_escaped (registry, " ", "origin", plugin->desc.origin))
       
   822     return FALSE;
       
   823 
       
   824   list = gst_registry_get_feature_list_by_plugin (registry, plugin->desc.name);
       
   825 
       
   826   for (walk = list; walk; walk = g_list_next (walk)) {
       
   827     GstPluginFeature *feature = GST_PLUGIN_FEATURE (walk->data);
       
   828 
       
   829     if (!gst_registry_save (registry,
       
   830             " <feature typename=\"%s\">\n",
       
   831             g_type_name (G_OBJECT_TYPE (feature))))
       
   832       goto fail;
       
   833     if (!gst_registry_xml_save_feature (registry, feature))
       
   834       goto fail;
       
   835     if (!gst_registry_save (registry, " </feature>\n"))
       
   836       goto fail;
       
   837   }
       
   838 
       
   839   gst_plugin_feature_list_free (list);
       
   840   return TRUE;
       
   841 
       
   842 fail:
       
   843   gst_plugin_feature_list_free (list);
       
   844   return FALSE;
       
   845 
       
   846 }
       
   847 
       
   848 /**
       
   849  * gst_registry_xml_write_cache:
       
   850  * @registry: a #GstRegistry
       
   851  * @location: a filename
       
   852  *
       
   853  * Write @registry in an XML format at the location given by
       
   854  * @location. Directories are automatically created.
       
   855  *
       
   856  * Returns: TRUE on success.
       
   857  */
       
   858 gboolean
       
   859 gst_registry_xml_write_cache (GstRegistry * registry, const char *location)
       
   860 {
       
   861   GList *walk;
       
   862   char *tmp_location;
       
   863 
       
   864   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
       
   865 
       
   866   tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
       
   867   registry->cache_file = g_mkstemp (tmp_location);
       
   868   if (registry->cache_file == -1) {
       
   869     char *dir;
       
   870 
       
   871     /* oops, I bet the directory doesn't exist */
       
   872     dir = g_path_get_dirname (location);
       
   873     g_mkdir_with_parents (dir, 0777);
       
   874     g_free (dir);
       
   875 
       
   876     /* the previous g_mkstemp call overwrote the XXXXXX placeholder ... */
       
   877     g_free (tmp_location);
       
   878     tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
       
   879     registry->cache_file = g_mkstemp (tmp_location);
       
   880 
       
   881     if (registry->cache_file == -1) {
       
   882       GST_DEBUG ("g_mkstemp() failed: %s", g_strerror (errno));
       
   883       g_free (tmp_location);
       
   884       return FALSE;
       
   885     }
       
   886   }
       
   887 
       
   888   if (!gst_registry_save (registry, "<?xml version=\"1.0\"?>\n"))
       
   889     goto fail;
       
   890   if (!gst_registry_save (registry, "<GST-PluginRegistry>\n"))
       
   891     goto fail;
       
   892 
       
   893 
       
   894   for (walk = g_list_last (registry->plugins); walk;
       
   895       walk = g_list_previous (walk)) {
       
   896     GstPlugin *plugin = GST_PLUGIN_CAST (walk->data);
       
   897 
       
   898     if (!plugin->filename)
       
   899       continue;
       
   900 
       
   901     if (plugin->flags & GST_PLUGIN_FLAG_CACHED) {
       
   902       int ret;
       
   903       struct stat statbuf;
       
   904 
       
   905       ret = g_stat (plugin->filename, &statbuf);
       
   906       if (ret < 0)
       
   907         continue;
       
   908       if (plugin->file_mtime != statbuf.st_mtime ||
       
   909           plugin->file_size != statbuf.st_size) {
       
   910         continue;
       
   911       }
       
   912     }
       
   913 
       
   914     if (!gst_registry_save (registry, "<plugin>\n"))
       
   915       goto fail;
       
   916     if (!gst_registry_xml_save_plugin (registry, plugin))
       
   917       goto fail;
       
   918     if (!gst_registry_save (registry, "</plugin>\n"))
       
   919       goto fail;
       
   920   }
       
   921   if (!gst_registry_save (registry, "</GST-PluginRegistry>\n"))
       
   922     goto fail;
       
   923 
       
   924   /* check return value of close(), write errors may only get reported here */
       
   925   if (close (registry->cache_file) < 0)
       
   926     goto close_failed;
       
   927 
       
   928   if (g_file_test (tmp_location, G_FILE_TEST_EXISTS)) {
       
   929 #ifdef WIN32
       
   930     g_remove (location);
       
   931 #endif
       
   932     if (g_rename (tmp_location, location) < 0)
       
   933       goto rename_failed;
       
   934   } else {
       
   935     /* FIXME: shouldn't we return FALSE here? */
       
   936   }
       
   937 
       
   938   g_free (tmp_location);
       
   939   GST_INFO ("Wrote XML registry cache");
       
   940   return TRUE;
       
   941 
       
   942 /* ERRORS */
       
   943 fail:
       
   944   {
       
   945     (void) close (registry->cache_file);
       
   946     /* fall through */
       
   947   }
       
   948 fail_after_close:
       
   949   {
       
   950     g_remove (tmp_location);
       
   951     g_free (tmp_location);
       
   952     return FALSE;
       
   953   }
       
   954 close_failed:
       
   955   {
       
   956     GST_ERROR ("close() failed: %s", g_strerror (errno));
       
   957     goto fail_after_close;
       
   958   }
       
   959 rename_failed:
       
   960   {
       
   961     GST_ERROR ("g_rename() failed: %s", g_strerror (errno));
       
   962     goto fail_after_close;
       
   963   }
       
   964 }