moap ignore
[platform/upstream/gstreamer.git] / gst / gstregistryxml.c
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   size_t 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       if (found)
127         return FALSE;
128       *write_to = g_strdup ((gchar *) xmlTextReaderConstValue (reader));
129       found = TRUE;
130     }
131   }
132   return FALSE;
133 }
134
135 static gboolean
136 read_uint (xmlTextReaderPtr reader, guint * write_to)
137 {
138   int depth = xmlTextReaderDepth (reader);
139   gboolean found = FALSE;
140
141   while (xmlTextReaderRead (reader) == 1) {
142     if (xmlTextReaderDepth (reader) == depth)
143       return found;
144     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_TEXT) {
145       gchar *ret;
146       const gchar *s;
147
148       if (found) {
149         GST_DEBUG ("failed to read uint, multiple text nodes");
150         return FALSE;
151       }
152       s = (const gchar *) xmlTextReaderConstValue (reader);
153       *write_to = strtol (s, &ret, 0);
154       if (s == ret) {
155         GST_DEBUG ("failed to read uint, text didn't convert to int");
156         return FALSE;
157       }
158       found = TRUE;
159     }
160   }
161   GST_DEBUG ("failed to read uint, no text node");
162   return FALSE;
163 }
164
165 static gboolean
166 read_enum (xmlTextReaderPtr reader, GType enum_type, guint * write_to)
167 {
168   int depth = xmlTextReaderDepth (reader);
169   gboolean found = FALSE;
170
171   if (*write_to)
172     return FALSE;
173   while (xmlTextReaderRead (reader) == 1) {
174     if (xmlTextReaderDepth (reader) == depth)
175       return found;
176     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_TEXT) {
177       GEnumClass *enum_class;
178       GEnumValue *value;
179
180       if (found)
181         return FALSE;
182       enum_class = g_type_class_ref (enum_type);
183       if (!enum_class)
184         return FALSE;
185       value =
186           g_enum_get_value_by_nick (enum_class,
187           (gchar *) xmlTextReaderConstValue (reader));
188       if (value) {
189         *write_to = value->value;
190         found = TRUE;
191       }
192       g_type_class_unref (enum_class);
193     }
194   }
195   return FALSE;
196 }
197
198 static GstStaticPadTemplate *
199 load_pad_template (xmlTextReaderPtr reader)
200 {
201   int ret;
202   int depth = xmlTextReaderDepth (reader);
203   gchar *name = NULL, *caps_str = NULL;
204   guint direction = 0, presence = 0;
205
206   while ((ret = xmlTextReaderRead (reader)) == 1) {
207     if (xmlTextReaderDepth (reader) == depth) {
208       GstStaticPadTemplate *template;
209
210       template = g_new0 (GstStaticPadTemplate, 1);
211       template->name_template = name;
212       template->presence = presence;
213       template->direction = direction;
214       template->static_caps.string = caps_str;
215
216       return template;
217     }
218     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
219         xmlTextReaderDepth (reader) == depth + 1) {
220       const gchar *tag = (gchar *) xmlTextReaderConstName (reader);
221
222       if (g_str_equal (tag, "nametemplate")) {
223         read_string (reader, &name, FALSE);
224       } else if (g_str_equal (tag, "direction")) {
225         read_enum (reader, GST_TYPE_PAD_DIRECTION, &direction);
226       } else if (g_str_equal (tag, "presence")) {
227         read_enum (reader, GST_TYPE_PAD_PRESENCE, &presence);
228       } else if (!strncmp (tag, "caps", 4)) {
229         read_string (reader, &caps_str, FALSE);
230       }
231     }
232   }
233   g_free (name);
234   g_free (caps_str);
235
236   return NULL;
237 }
238
239 static GstPluginFeature *
240 load_feature (xmlTextReaderPtr reader)
241 {
242   int ret;
243   int depth;
244   gchar *feature_name;
245   GstPluginFeature *feature;
246   GType type;
247
248   depth = xmlTextReaderDepth (reader);
249   feature_name =
250       (gchar *) xmlTextReaderGetAttribute (reader, BAD_CAST "typename");
251
252   GST_DEBUG ("loading feature");
253
254   if (!feature_name)
255     return NULL;
256
257   type = g_type_from_name (feature_name);
258   g_free (feature_name);
259   feature_name = NULL;
260
261   if (!type) {
262     return NULL;
263   }
264   feature = g_object_new (type, NULL);
265   if (!feature) {
266     return NULL;
267   }
268   if (!GST_IS_PLUGIN_FEATURE (feature)) {
269     /* don't really know what it is */
270     if (GST_IS_OBJECT (feature))
271       gst_object_unref (feature);
272     else
273       g_object_unref (feature);
274     return NULL;
275   }
276   while ((ret = xmlTextReaderRead (reader)) == 1) {
277     if (xmlTextReaderDepth (reader) == depth) {
278       GST_DEBUG ("loaded feature %p with name %s", feature, feature->name);
279       return feature;
280     }
281     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
282         xmlTextReaderDepth (reader) == depth + 1) {
283       const gchar *tag = (gchar *) xmlTextReaderConstName (reader);
284
285       if (g_str_equal (tag, "name"))
286         read_string (reader, &feature->name, FALSE);
287       else if (g_str_equal (tag, "rank"))
288         read_uint (reader, &feature->rank);
289
290       if (GST_IS_ELEMENT_FACTORY (feature)) {
291         GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (feature);
292
293         if (g_str_equal (tag, "longname")) {
294           int ret;
295
296           ret = read_string (reader, &factory->details.longname, TRUE);
297           GST_DEBUG ("longname ret=%d, name=%s",
298               ret, factory->details.longname);
299         } else if (g_str_equal (tag, "class")) {
300           read_string (reader, &factory->details.klass, TRUE);
301         } else if (g_str_equal (tag, "description")) {
302           read_string (reader, &factory->details.description, TRUE);
303         } else if (g_str_equal (tag, "author")) {
304           read_string (reader, &factory->details.author, TRUE);
305         } else if (g_str_equal (tag, "uri_type")) {
306           gchar *s = NULL;
307
308           if (read_string (reader, &s, FALSE)) {
309             if (g_ascii_strncasecmp (s, "sink", 4) == 0) {
310               factory->uri_type = GST_URI_SINK;
311             } else if (g_ascii_strncasecmp (s, "source", 5) == 0) {
312               factory->uri_type = GST_URI_SRC;
313             }
314             g_free (s);
315           }
316         } else if (g_str_equal (tag, "uri_protocol")) {
317           gchar *s = NULL;
318
319           if (read_string (reader, &s, FALSE))
320             add_to_char_array (&factory->uri_protocols, s);
321         } else if (g_str_equal (tag, "interface")) {
322           gchar *s = NULL;
323
324           if (read_string (reader, &s, FALSE)) {
325             __gst_element_factory_add_interface (factory, s);
326             /* add_interface strdup's s */
327             g_free (s);
328           }
329         } else if (g_str_equal (tag, "padtemplate")) {
330           GstStaticPadTemplate *template = load_pad_template (reader);
331
332           if (template) {
333             GST_LOG ("adding template %s to factory %s",
334                 GST_STR_NULL (GST_PAD_TEMPLATE_NAME_TEMPLATE (template)),
335                 GST_PLUGIN_FEATURE_NAME (feature));
336             __gst_element_factory_add_static_pad_template (factory, template);
337           }
338         }
339       } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
340         GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
341
342         if (g_str_equal (tag, "extension")) {
343           gchar *s = NULL;
344
345           if (read_string (reader, &s, TRUE))
346             add_to_char_array (&factory->extensions, s);
347         } else if (g_str_equal (tag, "caps")) {
348           gchar *s = NULL;
349
350           if (read_string (reader, &s, FALSE)) {
351             factory->caps = gst_caps_from_string (s);
352             g_free (s);
353           }
354         }
355       } else if (GST_IS_INDEX_FACTORY (feature)) {
356         GstIndexFactory *factory = GST_INDEX_FACTORY (feature);
357
358         if (g_str_equal (tag, "longdesc"))
359           read_string (reader, &factory->longdesc, TRUE);
360       }
361     }
362   }
363
364   GST_WARNING ("Error reading feature from registry: registry corrupt?");
365   return NULL;
366 }
367
368 static GstPlugin *
369 load_plugin (xmlTextReaderPtr reader, GList ** feature_list)
370 {
371   int ret;
372   GstPlugin *plugin;
373
374   *feature_list = NULL;
375
376   GST_DEBUG ("creating new plugin and parsing");
377
378   plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
379
380   plugin->flags |= GST_PLUGIN_FLAG_CACHED;
381   while ((ret = xmlTextReaderRead (reader)) == 1) {
382     if (xmlTextReaderDepth (reader) == 1) {
383       return plugin;
384     }
385     if (xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
386         xmlTextReaderDepth (reader) == 2) {
387       const gchar *tag = (gchar *) xmlTextReaderConstName (reader);
388
389       if (g_str_equal (tag, "name")) {
390         int ret;
391
392         ret = read_string (reader, &plugin->desc.name, FALSE);
393         GST_DEBUG ("name ret=%d, name=%s", ret, plugin->desc.name);
394         if (!ret)
395           break;
396       } else if (g_str_equal (tag, "description")) {
397         if (!read_string (reader, &plugin->desc.description, TRUE)) {
398           GST_DEBUG ("description field was invalid in registry");
399           break;
400         }
401         GST_DEBUG ("description %s", plugin->desc.description);
402       } else if (g_str_equal (tag, "filename")) {
403         if (!read_string (reader, &plugin->filename, FALSE)) {
404           GST_DEBUG ("filename field was invalid in registry");
405           break;
406         }
407         GST_DEBUG ("filename %s", plugin->filename);
408         plugin->basename = g_path_get_basename (plugin->filename);
409       } else if (g_str_equal (tag, "version")) {
410         if (!read_string (reader, &plugin->desc.version, TRUE)) {
411           GST_DEBUG ("version field was invalid in registry");
412           break;
413         }
414         GST_DEBUG ("version %s", plugin->desc.version);
415       } else if (g_str_equal (tag, "license")) {
416         if (!read_string (reader, &plugin->desc.license, TRUE)) {
417           GST_DEBUG ("license field was invalid in registry");
418           break;
419         }
420         GST_DEBUG ("license %s", plugin->desc.license);
421       } else if (g_str_equal (tag, "source")) {
422         if (!read_string (reader, &plugin->desc.source, TRUE)) {
423           GST_DEBUG ("source field was invalid in registry");
424           break;
425         }
426         GST_DEBUG ("source %s", plugin->desc.source);
427       } else if (g_str_equal (tag, "package")) {
428         if (!read_string (reader, &plugin->desc.package, TRUE)) {
429           GST_DEBUG ("package field was invalid in registry");
430           break;
431         }
432         GST_DEBUG ("package %s", plugin->desc.package);
433       } else if (g_str_equal (tag, "origin")) {
434         if (!read_string (reader, &plugin->desc.origin, TRUE)) {
435           GST_DEBUG ("failed to read origin");
436           break;
437         }
438       } else if (g_str_equal (tag, "m32p")) {
439         char *s;
440
441         if (!read_string (reader, &s, FALSE)) {
442           GST_DEBUG ("failed to read mtime");
443           break;
444         }
445         plugin->file_mtime = strtol (s, NULL, 0);
446         GST_DEBUG ("mtime %d", (int) plugin->file_mtime);
447         g_free (s);
448       } else if (g_str_equal (tag, "size")) {
449         unsigned int x;
450
451         if (read_uint (reader, &x)) {
452           plugin->file_size = x;
453           GST_DEBUG ("file_size %d", plugin->file_size);
454         } else {
455           GST_DEBUG ("failed to read size");
456         }
457       } else if (g_str_equal (tag, "feature")) {
458         GstPluginFeature *feature = load_feature (reader);
459
460         if (feature) {
461           feature->plugin_name = g_strdup (plugin->desc.name);
462           *feature_list = g_list_prepend (*feature_list, feature);
463         }
464       } else {
465         GST_DEBUG ("unknown tag %s", tag);
466       }
467     }
468   }
469   gst_object_unref (plugin);
470
471   GST_DEBUG ("problem reading plugin");
472
473   return NULL;
474 }
475
476 /**
477  * gst_registry_xml_read_cache:
478  * @registry: a #GstRegistry
479  * @location: a filename
480  *
481  * Read the contents of the XML cache file at location 
482  * @location into @registry.
483  *
484  * Returns: TRUE on success.
485  */
486 gboolean
487 gst_registry_xml_read_cache (GstRegistry * registry, const char *location)
488 {
489   GMappedFile *mapped = NULL;
490   GTimer *timer;
491   gdouble seconds;
492   xmlTextReaderPtr reader = NULL;
493   int ret;
494   gboolean in_registry = FALSE;
495   FILE *file = NULL;
496
497   /* make sure these types exist */
498   GST_TYPE_ELEMENT_FACTORY;
499   GST_TYPE_TYPE_FIND_FACTORY;
500   GST_TYPE_INDEX_FACTORY;
501
502   timer = g_timer_new ();
503
504   mapped = g_mapped_file_new (location, FALSE, NULL);
505   if (mapped) {
506     reader = xmlReaderForMemory (g_mapped_file_get_contents (mapped),
507         g_mapped_file_get_length (mapped), NULL, NULL, 0);
508     if (reader == NULL) {
509       g_mapped_file_free (mapped);
510       mapped = NULL;
511     }
512   }
513
514   if (reader == NULL) {
515     file = fopen (location, "r");
516     if (file == NULL) {
517       g_timer_destroy (timer);
518       return FALSE;
519     }
520
521     reader = xmlReaderForFd (fileno (file), NULL, NULL, 0);
522     if (!reader) {
523       fclose (file);
524       g_timer_destroy (timer);
525       return FALSE;
526     }
527   }
528
529   while ((ret = xmlTextReaderRead (reader)) == 1) {
530     if (xmlTextReaderDepth (reader) == 0) {
531       in_registry = xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT &&
532           g_str_equal ("GST-PluginRegistry", xmlTextReaderConstName (reader));
533     } else if (in_registry) {
534       if (xmlTextReaderDepth (reader) == 1 &&
535           xmlTextReaderNodeType (reader) == XML_READER_TYPE_ELEMENT) {
536         const gchar *tag = (const gchar *) xmlTextReaderConstName (reader);
537
538         if (g_str_equal (tag, "plugin")) {
539           GList *feature_list;
540           GstPlugin *plugin = load_plugin (reader, &feature_list);
541
542           if (plugin) {
543             GList *g;
544
545             GST_DEBUG ("adding plugin %s", plugin->desc.name);
546             gst_registry_add_plugin (registry, plugin);
547             for (g = feature_list; g; g = g_list_next (g)) {
548               gst_registry_add_feature (registry,
549                   GST_PLUGIN_FEATURE_CAST (g->data));
550             }
551             g_list_free (feature_list);
552           }
553         }
554       }
555     }
556   }
557   xmlFreeTextReader (reader);
558   if (ret != 0) {
559     GST_ERROR ("parsing registry cache: %s", location);
560     if (mapped)
561       g_mapped_file_free (mapped);
562     if (file)
563       fclose (file);
564     g_timer_destroy (timer);
565     return FALSE;
566   }
567
568   g_timer_stop (timer);
569   seconds = g_timer_elapsed (timer, NULL);
570   g_timer_destroy (timer);
571
572   GST_INFO ("loaded %s in %f seconds", location, seconds);
573
574   if (mapped)
575     g_mapped_file_free (mapped);
576
577   if (file)
578     fclose (file);
579
580   return TRUE;
581 }
582
583 /*
584  * Save
585  */
586 static gboolean
587 gst_registry_save_escaped (GstRegistry * registry, char *prefix, char *tag,
588     char *value)
589 {
590   gboolean ret = TRUE;
591
592   if (value) {
593     gchar *v = g_markup_escape_text (value, strlen (value));
594
595     ret = gst_registry_save (registry, "%s<%s>%s</%s>\n", prefix, tag, v, tag);
596     g_free (v);
597   }
598
599   return ret;
600 }
601
602
603 static gboolean
604 gst_registry_xml_save_caps (GstRegistry * registry, const GstCaps * caps)
605 {
606   /* we copy the caps here so we can simplify them before saving. This is a lot
607    * faster when loading them later on */
608   char *s;
609   GstCaps *copy = gst_caps_copy (caps);
610   gboolean ret;
611
612   gst_caps_do_simplify (copy);
613   s = gst_caps_to_string (copy);
614   gst_caps_unref (copy);
615
616   ret = gst_registry_save_escaped (registry, "  ", "caps", s);
617   g_free (s);
618   return ret;
619 }
620
621 static gboolean
622 gst_registry_xml_save_pad_template (GstRegistry * registry,
623     GstStaticPadTemplate * template)
624 {
625   gchar *presence;
626
627   if (!gst_registry_save_escaped (registry, "   ", "nametemplate",
628           template->name_template))
629     return FALSE;
630
631   if (!gst_registry_save (registry,
632           "   <direction>%s</direction>\n",
633           (template->direction == GST_PAD_SINK ? "sink" : "src")))
634     return FALSE;
635
636   switch (template->presence) {
637     case GST_PAD_ALWAYS:
638       presence = "always";
639       break;
640     case GST_PAD_SOMETIMES:
641       presence = "sometimes";
642       break;
643     case GST_PAD_REQUEST:
644       presence = "request";
645       break;
646     default:
647       presence = "unknown";
648       break;
649   }
650   if (!gst_registry_save (registry, "   <presence>%s</presence>\n", presence))
651     return FALSE;
652
653   if (template->static_caps.string) {
654     if (!gst_registry_save (registry, "   <caps>%s</caps>\n",
655             template->static_caps.string))
656       return FALSE;
657   }
658   return TRUE;
659 }
660
661 static gboolean
662 gst_registry_xml_save_feature (GstRegistry * registry,
663     GstPluginFeature * feature)
664 {
665   if (!gst_registry_save_escaped (registry, "  ", "name", feature->name))
666     return FALSE;
667
668   if (feature->rank > 0) {
669     gint rank = feature->rank;
670
671     if (!gst_registry_save (registry, "  <rank>%d</rank>\n", rank))
672       return FALSE;
673   }
674
675   if (GST_IS_ELEMENT_FACTORY (feature)) {
676     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
677     GList *walk;
678
679     if (!gst_registry_save_escaped (registry, "  ", "longname",
680             factory->details.longname))
681       return FALSE;
682     if (!gst_registry_save_escaped (registry, "  ", "class",
683             factory->details.klass))
684       return FALSE;
685     if (!gst_registry_save_escaped (registry, "  ", "description",
686             factory->details.description))
687       return FALSE;
688     if (!gst_registry_save_escaped (registry, "  ", "author",
689             factory->details.author))
690       return FALSE;
691
692     walk = factory->staticpadtemplates;
693
694     while (walk) {
695       GstStaticPadTemplate *template = walk->data;
696
697       if (!gst_registry_save (registry, "  <padtemplate>\n"))
698         return FALSE;
699       if (!gst_registry_xml_save_pad_template (registry, template))
700         return FALSE;
701       if (!gst_registry_save (registry, "  </padtemplate>\n"))
702         return FALSE;
703
704       walk = g_list_next (walk);
705     }
706
707     walk = factory->interfaces;
708     while (walk) {
709       if (!gst_registry_save_escaped (registry, "  ", "interface",
710               (gchar *) walk->data))
711         return FALSE;
712       walk = g_list_next (walk);
713     }
714
715     if (GST_URI_TYPE_IS_VALID (factory->uri_type)) {
716       gchar **protocol;
717
718       if (!gst_registry_save_escaped (registry, "  ", "uri_type",
719               factory->uri_type == GST_URI_SINK ? "sink" : "source"))
720         return FALSE;
721       g_assert (factory->uri_protocols);
722       protocol = factory->uri_protocols;
723       while (*protocol) {
724         if (!gst_registry_save_escaped (registry, "  ", "uri_protocol",
725                 *protocol))
726           return FALSE;
727         protocol++;
728       }
729     }
730   } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
731     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
732     gint i = 0;
733
734     if (factory->caps) {
735       if (!gst_registry_xml_save_caps (registry, factory->caps))
736         return FALSE;
737     }
738     if (factory->extensions) {
739       while (factory->extensions[i]) {
740         if (!gst_registry_save_escaped (registry, "  ", "extension",
741                 factory->extensions[i]))
742           return FALSE;
743         i++;
744       }
745     }
746   } else if (GST_IS_INDEX_FACTORY (feature)) {
747     if (!gst_registry_save_escaped (registry, "  ", "longdesc",
748             GST_INDEX_FACTORY (feature)->longdesc))
749       return FALSE;
750   }
751   return TRUE;
752 }
753
754 static gboolean
755 gst_registry_xml_save_plugin (GstRegistry * registry, GstPlugin * plugin)
756 {
757   GList *list;
758   GList *walk;
759   char s[100];
760
761   if (!gst_registry_save_escaped (registry, " ", "name", plugin->desc.name))
762     return FALSE;
763   if (!gst_registry_save_escaped (registry, " ", "description",
764           plugin->desc.description))
765     return FALSE;
766   if (!gst_registry_save_escaped (registry, " ", "filename", plugin->filename))
767     return FALSE;
768
769   sprintf (s, "%d", (int) plugin->file_size);
770   if (!gst_registry_save_escaped (registry, " ", "size", s))
771     return FALSE;
772
773   sprintf (s, "%d", (int) plugin->file_mtime);
774   if (!gst_registry_save_escaped (registry, " ", "m32p", s))
775     return FALSE;
776
777   if (!gst_registry_save_escaped (registry, " ", "version",
778           plugin->desc.version))
779     return FALSE;
780   if (!gst_registry_save_escaped (registry, " ", "license",
781           plugin->desc.license))
782     return FALSE;
783   if (!gst_registry_save_escaped (registry, " ", "source", plugin->desc.source))
784     return FALSE;
785   if (!gst_registry_save_escaped (registry, " ", "package",
786           plugin->desc.package))
787     return FALSE;
788   if (!gst_registry_save_escaped (registry, " ", "origin", plugin->desc.origin))
789     return FALSE;
790
791   list = gst_registry_get_feature_list_by_plugin (registry, plugin->desc.name);
792
793   for (walk = list; walk; walk = g_list_next (walk)) {
794     GstPluginFeature *feature = GST_PLUGIN_FEATURE (walk->data);
795
796     if (!gst_registry_save (registry,
797             " <feature typename=\"%s\">\n",
798             g_type_name (G_OBJECT_TYPE (feature))))
799       goto fail;
800     if (!gst_registry_xml_save_feature (registry, feature))
801       goto fail;
802     if (!gst_registry_save (registry, " </feature>\n"))
803       goto fail;
804   }
805
806   gst_plugin_feature_list_free (list);
807   return TRUE;
808
809 fail:
810   gst_plugin_feature_list_free (list);
811   return FALSE;
812
813 }
814
815 /**
816  * gst_registry_xml_write_cache:
817  * @registry: a #GstRegistry
818  * @location: a filename
819  *
820  * Write @registry in an XML format at the location given by
821  * @location. Directories are automatically created.
822  *
823  * Returns: TRUE on success.
824  */
825 gboolean
826 gst_registry_xml_write_cache (GstRegistry * registry, const char *location)
827 {
828   GList *walk;
829   char *tmp_location;
830
831   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
832
833   tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
834   registry->cache_file = g_mkstemp (tmp_location);
835   if (registry->cache_file == -1) {
836     char *dir;
837
838     /* oops, I bet the directory doesn't exist */
839     dir = g_path_get_dirname (location);
840     g_mkdir_with_parents (dir, 0777);
841     g_free (dir);
842
843     /* the previous g_mkstemp call overwrote the XXXXXX placeholder ... */
844     g_free (tmp_location);
845     tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
846     registry->cache_file = g_mkstemp (tmp_location);
847
848     if (registry->cache_file == -1) {
849       GST_DEBUG ("g_mkstemp() failed: %s", g_strerror (errno));
850       g_free (tmp_location);
851       return FALSE;
852     }
853   }
854
855   if (!gst_registry_save (registry, "<?xml version=\"1.0\"?>\n"))
856     goto fail;
857   if (!gst_registry_save (registry, "<GST-PluginRegistry>\n"))
858     goto fail;
859
860
861   for (walk = g_list_last (registry->plugins); walk;
862       walk = g_list_previous (walk)) {
863     GstPlugin *plugin = GST_PLUGIN_CAST (walk->data);
864
865     if (!plugin->filename)
866       continue;
867
868     if (plugin->flags & GST_PLUGIN_FLAG_CACHED) {
869       int ret;
870       struct stat statbuf;
871
872       ret = g_stat (plugin->filename, &statbuf);
873       if (ret < 0)
874         continue;
875       if (plugin->file_mtime != statbuf.st_mtime ||
876           plugin->file_size != statbuf.st_size) {
877         continue;
878       }
879     }
880
881     if (!gst_registry_save (registry, "<plugin>\n"))
882       goto fail;
883     if (!gst_registry_xml_save_plugin (registry, plugin))
884       goto fail;
885     if (!gst_registry_save (registry, "</plugin>\n"))
886       goto fail;
887   }
888   if (!gst_registry_save (registry, "</GST-PluginRegistry>\n"))
889     goto fail;
890
891   close (registry->cache_file);
892
893   if (g_file_test (tmp_location, G_FILE_TEST_EXISTS)) {
894 #ifdef WIN32
895     g_remove (location);
896 #endif
897     g_rename (tmp_location, location);
898   }
899
900   g_free (tmp_location);
901
902   return TRUE;
903
904 fail:
905   close (registry->cache_file);
906   g_free (tmp_location);
907
908   return FALSE;
909 }