d7dd9ec69dbb7deb1ed6a3e81714196e8c854e03
[platform/upstream/gstreamer.git] / gst / registries / gstxmlregistry.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstxml_registry.c: GstXMLRegistry object, support routines
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <stdio.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <dirent.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <utime.h>
35
36 #include <gst/gst_private.h>
37 #include <gst/gstelement.h>
38 #include <gst/gsttypefind.h>
39 #include <gst/gstscheduler.h>
40 #include <gst/gsturi.h>
41 #include <gst/gstinfo.h>
42
43 #include "gstxmlregistry.h"
44
45 #define BLOCK_SIZE 1024*10
46
47 #define CLASS(registry)  GST_XML_REGISTRY_CLASS (G_OBJECT_GET_CLASS (registry))
48
49
50 enum
51 {
52   PROP_0,
53   PROP_LOCATION
54 };
55
56
57 static void gst_xml_registry_class_init (GstXMLRegistryClass * klass);
58 static void gst_xml_registry_init (GstXMLRegistry * registry);
59
60 static void gst_xml_registry_set_property (GObject * object, guint prop_id,
61     const GValue * value, GParamSpec * pspec);
62 static void gst_xml_registry_get_property (GObject * object, guint prop_id,
63     GValue * value, GParamSpec * pspec);
64
65 static gboolean gst_xml_registry_load (GstRegistry * registry);
66 static gboolean gst_xml_registry_save (GstRegistry * registry);
67 static gboolean gst_xml_registry_rebuild (GstRegistry * registry);
68
69 static void gst_xml_registry_get_perms_func (GstXMLRegistry * registry);
70 static void gst_xml_registry_add_path_list_func (GstXMLRegistry * registry);
71 static gboolean gst_xml_registry_open_func (GstXMLRegistry * registry,
72     GstXMLRegistryMode mode);
73 static gboolean gst_xml_registry_load_func (GstXMLRegistry * registry,
74     gchar * data, gssize * size);
75 static gboolean gst_xml_registry_save_func (GstXMLRegistry * registry,
76     gchar * format, ...);
77 static gboolean gst_xml_registry_close_func (GstXMLRegistry * registry);
78
79 static GstRegistryReturn gst_xml_registry_load_plugin (GstRegistry * registry,
80     GstPlugin * plugin);
81
82 static void gst_xml_registry_start_element (GMarkupParseContext * context,
83     const gchar * element_name,
84     const gchar ** attribute_names,
85     const gchar ** attribute_values, gpointer user_data, GError ** error);
86 static void gst_xml_registry_end_element (GMarkupParseContext * context,
87     const gchar * element_name, gpointer user_data, GError ** error);
88 static void gst_xml_registry_text (GMarkupParseContext * context,
89     const gchar * text, gsize text_len, gpointer user_data, GError ** error);
90 static void gst_xml_registry_passthrough (GMarkupParseContext * context,
91     const gchar * passthrough_text,
92     gsize text_len, gpointer user_data, GError ** error);
93 static void gst_xml_registry_error (GMarkupParseContext * context,
94     GError * error, gpointer user_data);
95
96
97 static void gst_xml_registry_paths_start_element (GMarkupParseContext * context,
98     const gchar * element_name,
99     const gchar ** attribute_names,
100     const gchar ** attribute_values, gpointer user_data, GError ** error);
101 static void gst_xml_registry_paths_end_element (GMarkupParseContext * context,
102     const gchar * element_name, gpointer user_data, GError ** error);
103 static void gst_xml_registry_paths_text (GMarkupParseContext * context,
104     const gchar * text, gsize text_len, gpointer user_data, GError ** error);
105
106 static GstRegistryClass *parent_class = NULL;
107
108 /* static guint gst_xml_registry_signals[LAST_SIGNAL] = { 0 }; */
109
110 static const GMarkupParser gst_xml_registry_parser = {
111   gst_xml_registry_start_element,
112   gst_xml_registry_end_element,
113   gst_xml_registry_text,
114   gst_xml_registry_passthrough,
115   gst_xml_registry_error,
116 };
117
118 static const GMarkupParser gst_xml_registry_paths_parser = {
119   gst_xml_registry_paths_start_element,
120   gst_xml_registry_paths_end_element,
121   gst_xml_registry_paths_text,
122   NULL,
123   NULL
124 };
125
126
127 GType
128 gst_xml_registry_get_type (void)
129 {
130   static GType xml_registry_type = 0;
131
132   if (!xml_registry_type) {
133     static const GTypeInfo xml_registry_info = {
134       sizeof (GstXMLRegistryClass),
135       NULL,
136       NULL,
137       (GClassInitFunc) gst_xml_registry_class_init,
138       NULL,
139       NULL,
140       sizeof (GstXMLRegistry),
141       0,
142       (GInstanceInitFunc) gst_xml_registry_init,
143       NULL
144     };
145
146     xml_registry_type = g_type_register_static (GST_TYPE_REGISTRY,
147         "GstXMLRegistry", &xml_registry_info, 0);
148   }
149   return xml_registry_type;
150 }
151
152 static void
153 gst_xml_registry_class_init (GstXMLRegistryClass * klass)
154 {
155   GObjectClass *gobject_class;
156   GstRegistryClass *gstregistry_class;
157   GstXMLRegistryClass *gstxmlregistry_class;
158
159   gobject_class = (GObjectClass *) klass;
160   gstregistry_class = (GstRegistryClass *) klass;
161   gstxmlregistry_class = (GstXMLRegistryClass *) klass;
162
163   parent_class = g_type_class_ref (GST_TYPE_REGISTRY);
164
165   gobject_class->get_property =
166       GST_DEBUG_FUNCPTR (gst_xml_registry_get_property);
167   gobject_class->set_property =
168       GST_DEBUG_FUNCPTR (gst_xml_registry_set_property);
169
170   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LOCATION,
171       g_param_spec_string ("location", "Location",
172           "Location of the registry file", NULL, G_PARAM_READWRITE));
173
174   gstregistry_class->load = GST_DEBUG_FUNCPTR (gst_xml_registry_load);
175   gstregistry_class->save = GST_DEBUG_FUNCPTR (gst_xml_registry_save);
176   gstregistry_class->rebuild = GST_DEBUG_FUNCPTR (gst_xml_registry_rebuild);
177
178   gstregistry_class->load_plugin =
179       GST_DEBUG_FUNCPTR (gst_xml_registry_load_plugin);
180
181   gstxmlregistry_class->get_perms_func =
182       GST_DEBUG_FUNCPTR (gst_xml_registry_get_perms_func);
183   gstxmlregistry_class->add_path_list_func =
184       GST_DEBUG_FUNCPTR (gst_xml_registry_add_path_list_func);
185   gstxmlregistry_class->open_func =
186       GST_DEBUG_FUNCPTR (gst_xml_registry_open_func);
187   gstxmlregistry_class->load_func =
188       GST_DEBUG_FUNCPTR (gst_xml_registry_load_func);
189   gstxmlregistry_class->save_func =
190       GST_DEBUG_FUNCPTR (gst_xml_registry_save_func);
191   gstxmlregistry_class->close_func =
192       GST_DEBUG_FUNCPTR (gst_xml_registry_close_func);
193 }
194
195 static void
196 gst_xml_registry_init (GstXMLRegistry * registry)
197 {
198   registry->location = NULL;
199   registry->context = NULL;
200   registry->state = GST_XML_REGISTRY_NONE;
201   registry->current_plugin = NULL;
202   registry->current_feature = NULL;
203   registry->open_tags = NULL;
204 }
205
206 /**
207  * gst_xml_registry_new:
208  * @name: the name of the registry
209  * @location: the location of the registry file
210  *
211  * Create a new xml registry with the given name and location.
212  *
213  * Returns: a new GstXMLRegistry with the given name an location.
214  */
215 GstRegistry *
216 gst_xml_registry_new (const gchar * name, const gchar * location)
217 {
218   GstXMLRegistry *xmlregistry;
219
220   xmlregistry = GST_XML_REGISTRY (g_object_new (GST_TYPE_XML_REGISTRY, NULL));
221
222   g_object_set (G_OBJECT (xmlregistry), "location", location, NULL);
223
224   GST_REGISTRY (xmlregistry)->name = g_strdup (name);
225
226   return GST_REGISTRY (xmlregistry);
227 }
228
229 static void
230 gst_xml_registry_set_property (GObject * object, guint prop_id,
231     const GValue * value, GParamSpec * pspec)
232 {
233   GstXMLRegistry *registry;
234
235   registry = GST_XML_REGISTRY (object);
236
237   switch (prop_id) {
238     case PROP_LOCATION:
239       if (registry->open) {
240         CLASS (object)->close_func (registry);
241         g_return_if_fail (registry->open == FALSE);
242       }
243
244       if (registry->location)
245         g_free (registry->location);
246
247       registry->location = g_strdup (g_value_get_string (value));
248       GST_REGISTRY (registry)->flags = 0x0;
249
250       if (CLASS (object)->get_perms_func)
251         CLASS (object)->get_perms_func (registry);
252
253       if (CLASS (object)->add_path_list_func)
254         CLASS (object)->add_path_list_func (registry);
255       break;
256     default:
257       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
258       break;
259   }
260 }
261
262 static void
263 gst_xml_registry_get_property (GObject * object, guint prop_id,
264     GValue * value, GParamSpec * pspec)
265 {
266   GstXMLRegistry *registry;
267
268   registry = GST_XML_REGISTRY (object);
269
270   switch (prop_id) {
271     case PROP_LOCATION:
272       g_value_set_string (value, g_strdup (registry->location));
273       break;
274     default:
275       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
276       break;
277   }
278 }
279
280 /* this function returns the biggest of the path's mtime and ctime
281  * mtime is updated through an actual write (data)
282  * ctime is updated through changing inode information
283  * so this function returns the last time *anything* changed to this path
284  */
285 static time_t
286 get_time (const char *path, gboolean * is_dir)
287 {
288   struct stat statbuf;
289
290   if (stat (path, &statbuf)) {
291     *is_dir = FALSE;
292     return 0;
293   }
294
295   if (is_dir)
296     *is_dir = S_ISDIR (statbuf.st_mode);
297
298   if (statbuf.st_mtime > statbuf.st_ctime)
299     return statbuf.st_mtime;
300   return statbuf.st_ctime;
301 }
302
303 /* same as 0755 */
304 #define dirmode \
305   (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
306
307 static gboolean
308 make_dir (gchar * filename)
309 {
310   struct stat dirstat;
311   gchar *dirname;
312
313   if (strrchr (filename, '/') == NULL)
314     return FALSE;
315
316   dirname = g_strndup (filename, strrchr (filename, '/') - filename);
317
318   if (stat (dirname, &dirstat) == -1 && errno == ENOENT) {
319     if (mkdir (dirname, dirmode) != 0) {
320       if (make_dir (dirname) != TRUE) {
321         g_free (dirname);
322         return FALSE;
323       } else {
324         if (mkdir (dirname, dirmode) != 0)
325           return FALSE;
326       }
327     }
328   }
329
330   g_free (dirname);
331   return TRUE;
332 }
333
334 static void
335 gst_xml_registry_get_perms_func (GstXMLRegistry * registry)
336 {
337   gchar *dirname;
338
339   /* if the dir does not exist, make it. if that can't be done, flags = 0x0.
340      if the file can be appended to, it's writable. if it can then be read,
341      it's readable. 
342      After that check if it exists. */
343
344   if (make_dir (registry->location) != TRUE) {
345     /* we can't do anything with it, leave flags as 0x0 */
346     return;
347   }
348
349   dirname = g_path_get_dirname (registry->location);
350
351   if (g_file_test (registry->location, G_FILE_TEST_EXISTS)) {
352     GST_REGISTRY (registry)->flags |= GST_REGISTRY_EXISTS;
353   }
354
355   if (!access (dirname, W_OK)) {
356     GST_REGISTRY (registry)->flags |= GST_REGISTRY_WRITABLE;
357   }
358
359   if (!access (dirname, R_OK)) {
360     GST_REGISTRY (registry)->flags |= GST_REGISTRY_READABLE;
361   }
362
363   g_free (dirname);
364 }
365
366 static void
367 gst_xml_registry_add_path_list_func (GstXMLRegistry * registry)
368 {
369   FILE *reg = NULL;
370   GMarkupParseContext *context;
371   gchar *text = NULL;
372   gssize size;
373   GError *error = NULL;
374
375   context = g_markup_parse_context_new (&gst_xml_registry_paths_parser, 0,
376       registry, NULL);
377
378   if (!(reg = fopen (registry->location, "r"))) {
379     goto finished;
380   }
381
382   /* slightly allocate more as gmarkup reads too much */
383   text = g_malloc0 (BLOCK_SIZE + 32);
384
385   size = fread (text, 1, BLOCK_SIZE, reg);
386
387   while (size) {
388     g_markup_parse_context_parse (context, text, size, &error);
389
390     if (error) {
391       GST_ERROR ("parsing registry %s: %s\n",
392           registry->location, error->message);
393       goto finished;
394     }
395
396     if (registry->state == GST_XML_REGISTRY_PATHS_DONE)
397       break;
398
399     size = fread (text, 1, BLOCK_SIZE, reg);
400   }
401
402 finished:
403
404   g_markup_parse_context_free (context);
405
406   if (reg)
407     fclose (reg);
408
409   g_free (text);
410 }
411
412 static gboolean
413 plugin_times_older_than_recurse (gchar * path, time_t regtime)
414 {
415   DIR *dir;
416   struct dirent *dirent;
417   gboolean is_dir;
418   gchar *pluginname;
419
420   time_t pathtime = get_time (path, &is_dir);
421
422   if (pathtime > regtime) {
423     GST_CAT_INFO (GST_CAT_PLUGIN_LOADING,
424         "time for %s was %ld; more recent than registry time of %ld\n",
425         path, (long) pathtime, (long) regtime);
426     return FALSE;
427   }
428
429   if (!is_dir)
430     return TRUE;
431
432   dir = opendir (path);
433   if (dir) {
434     while ((dirent = readdir (dir))) {
435       /* don't want to recurse in place or backwards */
436       if (strcmp (dirent->d_name, ".") && strcmp (dirent->d_name, "..")) {
437         pluginname = g_strjoin ("/", path, dirent->d_name, NULL);
438         if (!plugin_times_older_than_recurse (pluginname, regtime)) {
439           g_free (pluginname);
440           closedir (dir);
441           return FALSE;
442         }
443         g_free (pluginname);
444       }
445     }
446     closedir (dir);
447   }
448   return TRUE;
449 }
450
451 static gboolean
452 plugin_times_older_than (GList * paths, time_t regtime)
453 {
454   /* return true iff regtime is more recent than the times of all the files
455    * in the plugin dirs.
456    */
457
458   while (paths) {
459     GST_CAT_LOG (GST_CAT_PLUGIN_LOADING,
460         "comparing plugin times from %s with %ld",
461         (gchar *) paths->data, (long) regtime);
462     if (!plugin_times_older_than_recurse (paths->data, regtime))
463       return FALSE;
464     paths = g_list_next (paths);
465   }
466   return TRUE;
467 }
468
469 static gboolean
470 gst_xml_registry_open_func (GstXMLRegistry * registry, GstXMLRegistryMode mode)
471 {
472   GstRegistry *gst_registry;
473   GList *paths;
474
475   gst_registry = GST_REGISTRY (registry);
476   paths = gst_registry->paths;
477   GST_CAT_DEBUG (GST_CAT_GST_INIT, "opening registry %s", registry->location);
478
479   g_return_val_if_fail (registry->open == FALSE, FALSE);
480
481   /* if it doesn't exist, first try to build it, and check if it worked
482    * if it's not readable, return false
483    * if it's out of date, rebuild it */
484   if (mode == GST_XML_REGISTRY_READ) {
485     if (!(gst_registry->flags & GST_REGISTRY_EXISTS)) {
486       /* if it's not writable, then don't bother */
487       if (!(gst_registry->flags & GST_REGISTRY_WRITABLE)) {
488         GST_CAT_INFO (GST_CAT_GST_INIT, "Registry isn't writable");
489         return FALSE;
490       }
491       GST_CAT_INFO (GST_CAT_GST_INIT,
492           "Registry doesn't exist, trying to build...");
493       gst_registry_rebuild (gst_registry);
494       gst_registry_save (gst_registry);
495       /* FIXME: verify that the flags actually get updated ! */
496       if (!(gst_registry->flags & GST_REGISTRY_EXISTS)) {
497         return FALSE;
498       }
499     }
500     /* at this point we know it exists */
501     g_return_val_if_fail (gst_registry->flags & GST_REGISTRY_READABLE, FALSE);
502
503     if (!plugin_times_older_than (paths, get_time (registry->location, NULL))) {
504       if (gst_registry->flags & GST_REGISTRY_WRITABLE) {
505         GST_CAT_INFO (GST_CAT_GST_INIT, "Registry out of date, rebuilding...");
506
507         gst_registry_rebuild (gst_registry);
508
509         gst_registry_save (gst_registry);
510
511         if (!plugin_times_older_than (paths, get_time (registry->location,
512                     NULL))) {
513           GST_CAT_INFO (GST_CAT_GST_INIT,
514               "Registry still out of date, something is wrong...");
515           return FALSE;
516         }
517       } else {
518         GST_CAT_INFO (GST_CAT_GST_INIT,
519             "Can't write to this registry and it's out of date, ignoring it");
520         return FALSE;
521       }
522     }
523
524     GST_CAT_DEBUG (GST_CAT_GST_INIT, "opening registry %s for reading",
525         registry->location);
526     registry->regfile = fopen (registry->location, "r");
527   } else if (mode == GST_XML_REGISTRY_WRITE) {
528     char *tmploc;
529     int fd;
530
531     g_return_val_if_fail (gst_registry->flags & GST_REGISTRY_WRITABLE, FALSE);
532
533     tmploc = g_strconcat (registry->location, ".tmp", NULL);
534
535     GST_CAT_DEBUG (GST_CAT_GST_INIT, "opening registry %s for writing", tmploc);
536
537     if ((fd = open (tmploc, O_WRONLY | O_CREAT | O_EXCL, 0644)) < 0) {
538       g_free (tmploc);
539       return FALSE;
540     }
541     g_free (tmploc);
542
543     registry->regfile = fdopen (fd, "w");
544   }
545
546   if (!registry->regfile)
547     return FALSE;
548
549   registry->open = TRUE;
550
551   return TRUE;
552 }
553
554 static gboolean
555 gst_xml_registry_load_func (GstXMLRegistry * registry, gchar * data,
556     gssize * size)
557 {
558   *size = fread (data, 1, *size, registry->regfile);
559
560   return TRUE;
561 }
562
563 static gboolean
564 gst_xml_registry_save_func (GstXMLRegistry * registry, gchar * format, ...)
565 {
566   va_list var_args;
567
568   va_start (var_args, format);
569
570   vfprintf (registry->regfile, format, var_args);
571
572   va_end (var_args);
573
574   return TRUE;
575 }
576
577 static gboolean
578 gst_xml_registry_close_func (GstXMLRegistry * registry)
579 {
580   char *tmploc;
581
582   GST_CAT_DEBUG (GST_CAT_GST_INIT, "closing registry %s", registry->location);
583   fclose (registry->regfile);
584
585   /* If we opened for writing, rename our temporary file. */
586   tmploc = g_strconcat (registry->location, ".tmp", NULL);
587   if (g_file_test (tmploc, G_FILE_TEST_EXISTS))
588     rename (tmploc, registry->location);
589   g_free (tmploc);
590
591   registry->open = FALSE;
592
593   return TRUE;
594 }
595
596 static gboolean
597 gst_xml_registry_load (GstRegistry * registry)
598 {
599   GstXMLRegistry *xmlregistry;
600   gchar *text;
601   gssize size;
602   GError *error = NULL;
603   GTimer *timer;
604   gdouble seconds;
605
606   xmlregistry = GST_XML_REGISTRY (registry);
607
608   timer = g_timer_new ();
609
610   xmlregistry->context =
611       g_markup_parse_context_new (&gst_xml_registry_parser, 0, registry, NULL);
612
613   if (!CLASS (xmlregistry)->open_func (xmlregistry, GST_XML_REGISTRY_READ)) {
614     g_timer_destroy (timer);
615     return FALSE;
616   }
617
618   text = g_malloc0 (BLOCK_SIZE + 32);
619
620   size = BLOCK_SIZE;
621   CLASS (xmlregistry)->load_func (xmlregistry, text, &size);
622
623   while (size) {
624     g_markup_parse_context_parse (xmlregistry->context, text, size, &error);
625
626     if (error) {
627       GST_ERROR ("parsing registry: %s\n", error->message);
628       g_free (text);
629       CLASS (xmlregistry)->close_func (xmlregistry);
630       g_timer_destroy (timer);
631       return FALSE;
632     }
633
634     size = BLOCK_SIZE;
635     CLASS (xmlregistry)->load_func (xmlregistry, text, &size);
636   }
637
638   g_free (text);
639
640   g_timer_stop (timer);
641
642   seconds = g_timer_elapsed (timer, NULL);
643   g_timer_destroy (timer);
644
645   GST_INFO ("loaded %s in %f seconds (%s)",
646       registry->name, seconds, xmlregistry->location);
647
648   CLASS (xmlregistry)->close_func (xmlregistry);
649
650
651   return TRUE;
652 }
653
654 static GstRegistryReturn
655 gst_xml_registry_load_plugin (GstRegistry * registry, GstPlugin * plugin)
656 {
657   GError *error = NULL;
658   GstPlugin *loaded_plugin;
659
660   /* FIXME: add gerror support */
661   loaded_plugin = gst_plugin_load_file (plugin->filename, &error);
662   if (!plugin) {
663     if (error) {
664       g_warning ("could not load plugin %s: %s", plugin->desc.name,
665           error->message);
666       g_error_free (error);
667     }
668     return GST_REGISTRY_PLUGIN_LOAD_ERROR;
669   } else if (loaded_plugin != plugin) {
670     g_critical ("how to remove plugins?");
671   }
672
673   return GST_REGISTRY_OK;
674 }
675
676 static gboolean
677 gst_xml_registry_parse_plugin (GMarkupParseContext * context, const gchar * tag,
678     const gchar * text, gsize text_len, GstXMLRegistry * registry,
679     GError ** error)
680 {
681   GstPlugin *plugin = registry->current_plugin;
682
683   if (!strcmp (tag, "name")) {
684     plugin->desc.name = g_strndup (text, text_len);
685   } else if (!strcmp (tag, "description")) {
686     plugin->desc.description = g_strndup (text, text_len);
687   } else if (!strcmp (tag, "filename")) {
688     plugin->filename = g_strndup (text, text_len);
689   } else if (!strcmp (tag, "version")) {
690     plugin->desc.version = g_strndup (text, text_len);
691   } else if (!strcmp (tag, "license")) {
692     plugin->desc.license = g_strndup (text, text_len);
693   } else if (!strcmp (tag, "package")) {
694     plugin->desc.package = g_strndup (text, text_len);
695   } else if (!strcmp (tag, "origin")) {
696     plugin->desc.origin = g_strndup (text, text_len);
697   }
698
699   return TRUE;
700 }
701
702 static void
703 add_to_char_array (gchar *** array, gchar * value)
704 {
705   gchar **new;
706   gchar **old = *array;
707   gint i = 0;
708
709   /* expensive, but cycles are cheap... */
710   if (old)
711     while (old[i])
712       i++;
713   new = g_new0 (gchar *, i + 2);
714   new[i] = value;
715   while (i > 0) {
716     i--;
717     new[i] = old[i];
718   }
719   g_free (old);
720   *array = new;
721 }
722
723 static gboolean
724 gst_xml_registry_parse_element_factory (GMarkupParseContext * context,
725     const gchar * tag, const gchar * text, gsize text_len,
726     GstXMLRegistry * registry, GError ** error)
727 {
728   GstElementFactory *factory = GST_ELEMENT_FACTORY (registry->current_feature);
729
730   if (!strcmp (tag, "name")) {
731     gchar *name = g_strndup (text, text_len);
732
733     gst_plugin_feature_set_name (registry->current_feature, name);
734     g_free (name);
735   } else if (!strcmp (tag, "longname")) {
736     g_free (factory->details.longname);
737     factory->details.longname = g_strndup (text, text_len);
738   } else if (!strcmp (tag, "class")) {
739     g_free (factory->details.klass);
740     factory->details.klass = g_strndup (text, text_len);
741   } else if (!strcmp (tag, "description")) {
742     g_free (factory->details.description);
743     factory->details.description = g_strndup (text, text_len);
744   } else if (!strcmp (tag, "author")) {
745     g_free (factory->details.author);
746     factory->details.author = g_strndup (text, text_len);
747   } else if (!strcmp (tag, "rank")) {
748     gint rank;
749     gchar *ret;
750
751     rank = strtol (text, &ret, 0);
752     if (ret == text + text_len) {
753       gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
754     }
755   } else if (!strcmp (tag, "uri_type")) {
756     if (strncasecmp (text, "sink", 4) == 0) {
757       factory->uri_type = GST_URI_SINK;
758     } else if (strncasecmp (text, "source", 5) == 0) {
759       factory->uri_type = GST_URI_SRC;
760     }
761   } else if (!strcmp (tag, "uri_protocol")) {
762     add_to_char_array (&factory->uri_protocols, g_strndup (text, text_len));
763   } else if (!strcmp (tag, "interface")) {
764     gchar *tmp = g_strndup (text, text_len);
765
766     __gst_element_factory_add_interface (factory, tmp);
767     g_free (tmp);
768   }
769
770   return TRUE;
771 }
772
773 static gboolean
774 gst_xml_registry_parse_type_find_factory (GMarkupParseContext * context,
775     const gchar * tag, const gchar * text, gsize text_len,
776     GstXMLRegistry * registry, GError ** error)
777 {
778   GstTypeFindFactory *factory =
779       GST_TYPE_FIND_FACTORY (registry->current_feature);
780
781   if (!strcmp (tag, "name")) {
782     registry->current_feature->name = g_strndup (text, text_len);
783   } else if (!strcmp (tag, "rank")) {
784     glong rank;
785     gchar *ret;
786
787     rank = strtol (text, &ret, 0);
788     if (ret == text + text_len) {
789       gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
790     }
791   }
792   /* FIXME!!
793      else if (!strcmp (tag, "caps")) {
794      factory->caps = g_strndup (text, text_len);
795      } */
796   else if (!strcmp (tag, "extension")) {
797     add_to_char_array (&factory->extensions, g_strndup (text, text_len));
798   }
799
800   return TRUE;
801 }
802
803 static gboolean
804 gst_xml_registry_parse_scheduler_factory (GMarkupParseContext * context,
805     const gchar * tag, const gchar * text, gsize text_len,
806     GstXMLRegistry * registry, GError ** error)
807 {
808   GstSchedulerFactory *factory =
809       GST_SCHEDULER_FACTORY (registry->current_feature);
810
811   if (!strcmp (tag, "name")) {
812     registry->current_feature->name = g_strndup (text, text_len);
813   } else if (!strcmp (tag, "longdesc")) {
814     factory->longdesc = g_strndup (text, text_len);
815   }
816   return TRUE;
817 }
818
819 static gboolean
820 gst_xml_registry_parse_index_factory (GMarkupParseContext * context,
821     const gchar * tag, const gchar * text, gsize text_len,
822     GstXMLRegistry * registry, GError ** error)
823 {
824   GstIndexFactory *factory = GST_INDEX_FACTORY (registry->current_feature);
825
826   if (!strcmp (tag, "name")) {
827     registry->current_feature->name = g_strndup (text, text_len);
828   } else if (!strcmp (tag, "longdesc")) {
829     factory->longdesc = g_strndup (text, text_len);
830   }
831   return TRUE;
832 }
833
834 static gboolean
835 gst_xml_registry_parse_padtemplate (GMarkupParseContext * context,
836     const gchar * tag, const gchar * text, gsize text_len,
837     GstXMLRegistry * registry, GError ** error)
838 {
839   if (!strcmp (tag, "nametemplate")) {
840     registry->name_template = g_strndup (text, text_len);
841   } else if (!strcmp (tag, "direction")) {
842     if (!strncmp (text, "sink", text_len)) {
843       registry->direction = GST_PAD_SINK;
844     } else if (!strncmp (text, "src", text_len)) {
845       registry->direction = GST_PAD_SRC;
846     }
847   } else if (!strcmp (tag, "presence")) {
848     if (!strncmp (text, "always", text_len)) {
849       registry->presence = GST_PAD_ALWAYS;
850     } else if (!strncmp (text, "sometimes", text_len)) {
851       registry->presence = GST_PAD_SOMETIMES;
852     } else if (!strncmp (text, "request", text_len)) {
853       registry->presence = GST_PAD_REQUEST;
854     }
855   } else if (!strncmp (tag, "caps", 4)) {
856     char *s;
857
858     s = g_strndup (text, text_len);
859     g_assert (registry->caps == NULL);
860     registry->caps = gst_caps_from_string (s);
861     if (registry->caps == NULL) {
862       g_critical ("Could not parse caps: length %d, content: %*s\n", text_len,
863           text_len, text);
864     }
865     g_free (s);
866     return TRUE;
867   }
868   return TRUE;
869 }
870
871 static void
872 gst_xml_registry_start_element (GMarkupParseContext * context,
873     const gchar * element_name,
874     const gchar ** attribute_names,
875     const gchar ** attribute_values, gpointer user_data, GError ** error)
876 {
877   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
878
879   xmlregistry->open_tags = g_list_prepend (xmlregistry->open_tags,
880       g_strdup (element_name));
881
882   switch (xmlregistry->state) {
883     case GST_XML_REGISTRY_NONE:
884       if (!strcmp (element_name, "GST-PluginRegistry")) {
885         xmlregistry->state = GST_XML_REGISTRY_TOP;
886       }
887       break;
888     case GST_XML_REGISTRY_TOP:
889       if (!strncmp (element_name, "plugin", 6)) {
890         xmlregistry->state = GST_XML_REGISTRY_PLUGIN;
891         xmlregistry->parser = gst_xml_registry_parse_plugin;
892         xmlregistry->current_plugin = (GstPlugin *) g_new0 (GstPlugin, 1);
893       }
894       break;
895     case GST_XML_REGISTRY_PLUGIN:
896       if (!strncmp (element_name, "feature", 7)) {
897         gint i = 0;
898         GstPluginFeature *feature = NULL;
899
900         xmlregistry->state = GST_XML_REGISTRY_FEATURE;
901
902         while (attribute_names[i]) {
903           if (!strncmp (attribute_names[i], "typename", 8)) {
904             feature =
905                 GST_PLUGIN_FEATURE (g_object_new (g_type_from_name
906                     (attribute_values[i]), NULL));
907             break;
908           }
909           i++;
910         }
911         if (feature) {
912           xmlregistry->current_feature = feature;
913
914           if (GST_IS_ELEMENT_FACTORY (feature)) {
915             GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
916
917             factory->padtemplates = NULL;
918             xmlregistry->parser = gst_xml_registry_parse_element_factory;
919             break;
920           } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
921             xmlregistry->parser = gst_xml_registry_parse_type_find_factory;
922           } else if (GST_IS_SCHEDULER_FACTORY (feature)) {
923             xmlregistry->parser = gst_xml_registry_parse_scheduler_factory;
924             GST_SCHEDULER_FACTORY (feature)->type = 0;
925           } else if (GST_IS_INDEX_FACTORY (feature)) {
926             xmlregistry->parser = gst_xml_registry_parse_index_factory;
927           } else {
928             g_warning ("unknown feature type");
929           }
930         }
931       }
932       break;
933     case GST_XML_REGISTRY_FEATURE:
934       if (!strncmp (element_name, "padtemplate", 11)) {
935         xmlregistry->state = GST_XML_REGISTRY_PADTEMPLATE;
936         xmlregistry->parser = gst_xml_registry_parse_padtemplate;
937         xmlregistry->name_template = NULL;
938         xmlregistry->direction = 0;
939         xmlregistry->presence = 0;
940         xmlregistry->caps = NULL;
941       }
942       break;
943     default:
944       break;
945   }
946 }
947
948 static void
949 gst_xml_registry_end_element (GMarkupParseContext * context,
950     const gchar * element_name, gpointer user_data, GError ** error)
951 {
952   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
953   gchar *open_tag = (gchar *) xmlregistry->open_tags->data;
954
955   xmlregistry->open_tags = g_list_remove (xmlregistry->open_tags, open_tag);
956   g_free (open_tag);
957
958   switch (xmlregistry->state) {
959     case GST_XML_REGISTRY_TOP:
960       if (!strcmp (element_name, "GST-PluginRegistry")) {
961         xmlregistry->state = GST_XML_REGISTRY_NONE;
962       }
963       break;
964     case GST_XML_REGISTRY_PLUGIN:
965       if (!strcmp (element_name, "plugin")) {
966         xmlregistry->state = GST_XML_REGISTRY_TOP;
967         xmlregistry->parser = NULL;
968         gst_registry_add_plugin (GST_REGISTRY (xmlregistry),
969             xmlregistry->current_plugin);
970       }
971       break;
972     case GST_XML_REGISTRY_FEATURE:
973       if (!strcmp (element_name, "feature")) {
974         xmlregistry->state = GST_XML_REGISTRY_PLUGIN;
975         xmlregistry->parser = gst_xml_registry_parse_plugin;
976         gst_plugin_add_feature (xmlregistry->current_plugin,
977             xmlregistry->current_feature);
978         xmlregistry->current_feature = NULL;
979       }
980       break;
981     case GST_XML_REGISTRY_PADTEMPLATE:
982       if (!strcmp (element_name, "padtemplate")) {
983         GstPadTemplate *template;
984
985         template = gst_pad_template_new (xmlregistry->name_template,
986             xmlregistry->direction, xmlregistry->presence, xmlregistry->caps);
987
988         g_free (xmlregistry->name_template);
989         xmlregistry->name_template = NULL;
990         xmlregistry->caps = NULL;
991
992         __gst_element_factory_add_pad_template (GST_ELEMENT_FACTORY
993             (xmlregistry->current_feature), template);
994         xmlregistry->state = GST_XML_REGISTRY_FEATURE;
995         xmlregistry->parser = gst_xml_registry_parse_element_factory;
996       }
997       break;
998     default:
999       break;
1000   }
1001 }
1002
1003 static void
1004 gst_xml_registry_text (GMarkupParseContext * context, const gchar * text,
1005     gsize text_len, gpointer user_data, GError ** error)
1006 {
1007   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1008   gchar *open_tag;
1009
1010   if (xmlregistry->open_tags) {
1011     open_tag = (gchar *) xmlregistry->open_tags->data;
1012
1013     if (!strcmp (open_tag, "plugin-path")) {
1014       //gst_plugin_add_path (g_strndup (text, text_len));
1015     } else if (xmlregistry->parser) {
1016       xmlregistry->parser (context, open_tag, text, text_len, xmlregistry,
1017           error);
1018     }
1019   }
1020 }
1021
1022 static void
1023 gst_xml_registry_passthrough (GMarkupParseContext * context,
1024     const gchar * passthrough_text, gsize text_len, gpointer user_data,
1025     GError ** error)
1026 {
1027 }
1028
1029 static void
1030 gst_xml_registry_error (GMarkupParseContext * context, GError * error,
1031     gpointer user_data)
1032 {
1033   GST_ERROR ("%s\n", error->message);
1034 }
1035
1036 static void
1037 gst_xml_registry_paths_start_element (GMarkupParseContext * context,
1038     const gchar * element_name,
1039     const gchar ** attribute_names,
1040     const gchar ** attribute_values, gpointer user_data, GError ** error)
1041 {
1042   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1043
1044   switch (xmlregistry->state) {
1045     case GST_XML_REGISTRY_NONE:
1046       if (!strcmp (element_name, "GST-PluginRegistry")) {
1047         xmlregistry->state = GST_XML_REGISTRY_TOP;
1048       }
1049       break;
1050     case GST_XML_REGISTRY_TOP:
1051       if (!strcmp (element_name, "gst-registry-paths")) {
1052         xmlregistry->state = GST_XML_REGISTRY_PATHS;
1053       }
1054       break;
1055     case GST_XML_REGISTRY_PATHS:
1056       if (!strcmp (element_name, "path")) {
1057         xmlregistry->state = GST_XML_REGISTRY_PATH;
1058       }
1059       break;
1060     default:
1061       break;
1062   }
1063 }
1064
1065 static void
1066 gst_xml_registry_paths_end_element (GMarkupParseContext * context,
1067     const gchar * element_name, gpointer user_data, GError ** error)
1068 {
1069   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1070
1071   switch (xmlregistry->state) {
1072     case GST_XML_REGISTRY_PATH:
1073       if (!strcmp (element_name, "path")) {
1074         xmlregistry->state = GST_XML_REGISTRY_PATHS;
1075       }
1076       break;
1077     case GST_XML_REGISTRY_PATHS:
1078       if (!strcmp (element_name, "gst-plugin-paths")) {
1079         xmlregistry->state = GST_XML_REGISTRY_PATHS_DONE;
1080       }
1081       break;
1082     default:
1083       break;
1084   }
1085 }
1086
1087 static void
1088 gst_xml_registry_paths_text (GMarkupParseContext * context, const gchar * text,
1089     gsize text_len, gpointer user_data, GError ** error)
1090 {
1091   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1092
1093   if (xmlregistry->state == GST_XML_REGISTRY_PATH)
1094     gst_registry_add_path (GST_REGISTRY (xmlregistry), g_strndup (text,
1095             text_len));
1096 }
1097
1098 /*
1099  * Save
1100  */
1101 #define PUT_ESCAPED(tag,value)                                  \
1102 G_STMT_START{                                                   \
1103   const gchar *toconv = value;                                  \
1104   if (toconv) {                                                 \
1105     gchar *v = g_markup_escape_text (toconv, strlen (toconv));  \
1106     CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, v, tag);                 \
1107     g_free (v);                                                 \
1108   }                                                             \
1109 }G_STMT_END
1110 #define PUT_ESCAPED_INT(tag,value)                              \
1111 G_STMT_START{                                                   \
1112   gchar *save = g_strdup_printf ("%ld", (glong) value);         \
1113   CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, save, tag);                \
1114   g_free (save);                                                \
1115 }G_STMT_END
1116
1117
1118 static gboolean
1119 gst_xml_registry_save_caps (GstXMLRegistry * xmlregistry, const GstCaps * caps)
1120 {
1121   char *s = gst_caps_to_string (caps);
1122
1123   PUT_ESCAPED ("caps", s);
1124   g_free (s);
1125   return TRUE;
1126 }
1127
1128 static gboolean
1129 gst_xml_registry_save_pad_template (GstXMLRegistry * xmlregistry,
1130     GstPadTemplate * template)
1131 {
1132   gchar *presence;
1133
1134   PUT_ESCAPED ("nametemplate", template->name_template);
1135   CLASS (xmlregistry)->save_func (xmlregistry, "<direction>%s</direction>\n",
1136       (template->direction == GST_PAD_SINK ? "sink" : "src"));
1137
1138   switch (template->presence) {
1139     case GST_PAD_ALWAYS:
1140       presence = "always";
1141       break;
1142     case GST_PAD_SOMETIMES:
1143       presence = "sometimes";
1144       break;
1145     case GST_PAD_REQUEST:
1146       presence = "request";
1147       break;
1148     default:
1149       presence = "unknown";
1150       break;
1151   }
1152   CLASS (xmlregistry)->save_func (xmlregistry, "<presence>%s</presence>\n",
1153       presence);
1154
1155   if (GST_PAD_TEMPLATE_CAPS (template)) {
1156     gst_xml_registry_save_caps (xmlregistry, GST_PAD_TEMPLATE_CAPS (template));
1157   }
1158   return TRUE;
1159 }
1160
1161 static gboolean
1162 gst_xml_registry_save_feature (GstXMLRegistry * xmlregistry,
1163     GstPluginFeature * feature)
1164 {
1165   PUT_ESCAPED ("name", feature->name);
1166
1167   if (feature->rank > 0) {
1168     gint rank = feature->rank;
1169
1170     CLASS (xmlregistry)->save_func (xmlregistry, "<rank>%d</rank>\n", rank);
1171   }
1172
1173   if (GST_IS_ELEMENT_FACTORY (feature)) {
1174     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
1175     GList *walk;
1176
1177     PUT_ESCAPED ("longname", factory->details.longname);
1178     PUT_ESCAPED ("class", factory->details.klass);
1179     PUT_ESCAPED ("description", factory->details.description);
1180     PUT_ESCAPED ("author", factory->details.author);
1181
1182     walk = factory->padtemplates;
1183
1184     while (walk) {
1185       GstPadTemplate *template = GST_PAD_TEMPLATE (walk->data);
1186
1187       CLASS (xmlregistry)->save_func (xmlregistry, "<padtemplate>\n");
1188       gst_xml_registry_save_pad_template (xmlregistry, template);
1189       CLASS (xmlregistry)->save_func (xmlregistry, "</padtemplate>\n");
1190
1191       walk = g_list_next (walk);
1192     }
1193
1194     walk = factory->interfaces;
1195     while (walk) {
1196       PUT_ESCAPED ("interface", (gchar *) walk->data);
1197       walk = g_list_next (walk);
1198     }
1199
1200     if (GST_URI_TYPE_IS_VALID (factory->uri_type)) {
1201       gchar **protocol;
1202
1203       PUT_ESCAPED ("uri_type",
1204           factory->uri_type == GST_URI_SINK ? "sink" : "source");
1205       g_assert (factory->uri_protocols);
1206       protocol = factory->uri_protocols;
1207       while (*protocol) {
1208         PUT_ESCAPED ("uri_protocol", *protocol);
1209         protocol++;
1210       }
1211     }
1212   } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
1213     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
1214     gint i = 0;
1215
1216     if (factory->caps) {
1217       gst_xml_registry_save_caps (xmlregistry, factory->caps);
1218     }
1219     if (factory->extensions) {
1220       while (factory->extensions[i]) {
1221         PUT_ESCAPED ("extension", factory->extensions[i]);
1222         i++;
1223       }
1224     }
1225   } else if (GST_IS_SCHEDULER_FACTORY (feature)) {
1226     PUT_ESCAPED ("longdesc", GST_SCHEDULER_FACTORY (feature)->longdesc);
1227   } else if (GST_IS_INDEX_FACTORY (feature)) {
1228     PUT_ESCAPED ("longdesc", GST_INDEX_FACTORY (feature)->longdesc);
1229   }
1230   return TRUE;
1231 }
1232
1233 static gboolean
1234 gst_xml_registry_save_plugin (GstXMLRegistry * xmlregistry, GstPlugin * plugin)
1235 {
1236   GList *walk;
1237
1238   PUT_ESCAPED ("name", plugin->desc.name);
1239   PUT_ESCAPED ("description", plugin->desc.description);
1240   PUT_ESCAPED ("filename", plugin->filename);
1241   PUT_ESCAPED ("version", plugin->desc.version);
1242   PUT_ESCAPED ("license", plugin->desc.license);
1243   PUT_ESCAPED ("package", plugin->desc.package);
1244   PUT_ESCAPED ("origin", plugin->desc.origin);
1245
1246   walk = plugin->features;
1247
1248   while (walk) {
1249     GstPluginFeature *feature = GST_PLUGIN_FEATURE (walk->data);
1250
1251     CLASS (xmlregistry)->save_func (xmlregistry, "<feature typename=\"%s\">\n",
1252         g_type_name (G_OBJECT_TYPE (feature)));
1253     gst_xml_registry_save_feature (xmlregistry, feature);
1254     CLASS (xmlregistry)->save_func (xmlregistry, "</feature>\n");
1255
1256     walk = g_list_next (walk);
1257   }
1258   return TRUE;
1259 }
1260
1261
1262 static gboolean
1263 gst_xml_registry_save (GstRegistry * registry)
1264 {
1265   GList *walk;
1266   GstXMLRegistry *xmlregistry;
1267
1268   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
1269   g_return_val_if_fail (registry->flags & GST_REGISTRY_WRITABLE, FALSE);
1270
1271   xmlregistry = GST_XML_REGISTRY (registry);
1272
1273   if (!CLASS (xmlregistry)->open_func (xmlregistry, GST_XML_REGISTRY_WRITE)) {
1274     return FALSE;
1275   }
1276
1277   CLASS (xmlregistry)->save_func (xmlregistry, "<?xml version=\"1.0\"?>\n");
1278   CLASS (xmlregistry)->save_func (xmlregistry, "<GST-PluginRegistry>\n");
1279
1280   walk = g_list_last (gst_registry_get_path_list (GST_REGISTRY (registry)));
1281
1282   CLASS (xmlregistry)->save_func (xmlregistry, "<gst-plugin-paths>\n");
1283   while (walk) {
1284     CLASS (xmlregistry)->save_func (xmlregistry, "<path>");
1285     CLASS (xmlregistry)->save_func (xmlregistry, (gchar *) walk->data);
1286     CLASS (xmlregistry)->save_func (xmlregistry, "</path>\n");
1287     walk = g_list_previous (walk);
1288   }
1289   CLASS (xmlregistry)->save_func (xmlregistry, "</gst-plugin-paths>\n");
1290
1291   walk = g_list_last (registry->plugins);
1292
1293   while (walk) {
1294     GstPlugin *plugin = GST_PLUGIN (walk->data);
1295
1296     CLASS (xmlregistry)->save_func (xmlregistry, "<plugin>\n");
1297     gst_xml_registry_save_plugin (xmlregistry, plugin);
1298     CLASS (xmlregistry)->save_func (xmlregistry, "</plugin>\n");
1299
1300     walk = g_list_previous (walk);
1301   }
1302   CLASS (xmlregistry)->save_func (xmlregistry, "</GST-PluginRegistry>\n");
1303
1304   CLASS (xmlregistry)->close_func (xmlregistry);
1305
1306   return TRUE;
1307 }
1308
1309 static GList *
1310 gst_xml_registry_rebuild_recurse (GstXMLRegistry * registry,
1311     const gchar * directory)
1312 {
1313   GDir *dir;
1314   GList *ret = NULL;
1315   gint dr_len, sf_len;
1316
1317   dir = g_dir_open (directory, 0, NULL);
1318
1319   if (dir) {
1320     const gchar *dirent;
1321
1322     while ((dirent = g_dir_read_name (dir))) {
1323       gchar *dirname;
1324
1325       if (*dirent == '=') {
1326         /* =build, =inst, etc. -- automake distcheck directories */
1327         continue;
1328       }
1329
1330       dirname = g_strjoin ("/", directory, dirent, NULL);
1331       ret =
1332           g_list_concat (ret, gst_xml_registry_rebuild_recurse (registry,
1333               dirname));
1334       g_free (dirname);
1335     }
1336     g_dir_close (dir);
1337   } else {
1338     dr_len = strlen (directory);
1339     sf_len = strlen (G_MODULE_SUFFIX);
1340     if (dr_len >= sf_len &&
1341         strcmp (directory + dr_len - sf_len, G_MODULE_SUFFIX) == 0) {
1342       ret = g_list_prepend (ret, g_strdup (directory));
1343     }
1344   }
1345
1346   return ret;
1347 }
1348
1349 static gboolean
1350 gst_xml_registry_rebuild (GstRegistry * registry)
1351 {
1352   GList *walk = NULL, *plugins = NULL, *prune = NULL;
1353   GError *error = NULL;
1354   guint length;
1355   GstPlugin *plugin;
1356   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (registry);
1357
1358   walk = registry->paths;
1359
1360   while (walk) {
1361     gchar *path = (gchar *) walk->data;
1362
1363     GST_CAT_INFO (GST_CAT_PLUGIN_LOADING,
1364         "Rebuilding registry %p in directory %s...", registry, path);
1365
1366     plugins = g_list_concat (plugins,
1367         gst_xml_registry_rebuild_recurse (xmlregistry, path));
1368
1369     walk = g_list_next (walk);
1370   }
1371
1372   plugins = g_list_reverse (plugins);
1373
1374   do {
1375     length = g_list_length (plugins);
1376
1377     walk = plugins;
1378     while (walk) {
1379       g_assert (walk->data);
1380       plugin = gst_plugin_load_file ((gchar *) walk->data, NULL);
1381       if (plugin) {
1382         prune = g_list_prepend (prune, walk->data);
1383         gst_registry_add_plugin (registry, plugin);
1384       }
1385
1386       walk = g_list_next (walk);
1387     }
1388
1389     walk = prune;
1390     while (walk) {
1391       plugins = g_list_remove (plugins, walk->data);
1392       g_free (walk->data);
1393       walk = g_list_next (walk);
1394     }
1395     g_list_free (prune);
1396     prune = NULL;
1397   } while (g_list_length (plugins) != length);
1398
1399   walk = plugins;
1400   while (walk) {
1401     if ((plugin = gst_plugin_load_file ((gchar *) walk->data, &error))) {
1402       g_warning ("Bizarre behavior: plugin %s actually loaded",
1403           (gchar *) walk->data);
1404       gst_registry_add_plugin (registry, plugin);
1405     } else {
1406       GST_CAT_INFO (GST_CAT_PLUGIN_LOADING, "Plugin %s failed to load: %s",
1407           (gchar *) walk->data, error->message);
1408
1409       g_free (walk->data);
1410       g_error_free (error);
1411       error = NULL;
1412     }
1413
1414     walk = g_list_next (walk);
1415   }
1416   return TRUE;
1417 }