configure.ac: Disable various code when compiling for MinGW.
[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 #ifndef HAVE_WIN32
320     if (mkdir (dirname, DIRMODE) != 0) {
321       if (make_dir (dirname) != TRUE) {
322         g_free (dirname);
323         return FALSE;
324       } else {
325         if (mkdir (dirname, DIRMODE) != 0)
326           return FALSE;
327       }
328     }
329 #else
330     return FALSE;
331 #endif
332   }
333
334   g_free (dirname);
335   return TRUE;
336 }
337
338 static void
339 gst_xml_registry_get_perms_func (GstXMLRegistry * registry)
340 {
341   gchar *dirname;
342
343   /* if the dir does not exist, make it. if that can't be done, flags = 0x0.
344      if the file can be appended to, it's writable. if it can then be read,
345      it's readable. 
346      After that check if it exists. */
347
348   if (make_dir (registry->location) != TRUE) {
349     /* we can't do anything with it, leave flags as 0x0 */
350     return;
351   }
352
353   dirname = g_path_get_dirname (registry->location);
354
355   if (g_file_test (registry->location, G_FILE_TEST_EXISTS)) {
356     GST_REGISTRY (registry)->flags |= GST_REGISTRY_EXISTS;
357   }
358
359   if (!access (dirname, W_OK)) {
360     GST_REGISTRY (registry)->flags |= GST_REGISTRY_WRITABLE;
361   }
362
363   if (!access (dirname, R_OK)) {
364     GST_REGISTRY (registry)->flags |= GST_REGISTRY_READABLE;
365   }
366
367   g_free (dirname);
368 }
369
370 static void
371 gst_xml_registry_add_path_list_func (GstXMLRegistry * registry)
372 {
373   FILE *reg = NULL;
374   GMarkupParseContext *context;
375   gchar *text = NULL;
376   gssize size;
377   GError *error = NULL;
378
379   context = g_markup_parse_context_new (&gst_xml_registry_paths_parser, 0,
380       registry, NULL);
381
382   if (!(reg = fopen (registry->location, "r"))) {
383     goto finished;
384   }
385
386   /* slightly allocate more as gmarkup reads too much */
387   text = g_malloc0 (BLOCK_SIZE + 32);
388
389   size = fread (text, 1, BLOCK_SIZE, reg);
390
391   while (size) {
392     g_markup_parse_context_parse (context, text, size, &error);
393
394     if (error) {
395       GST_ERROR ("parsing registry %s: %s\n",
396           registry->location, error->message);
397       goto finished;
398     }
399
400     if (registry->state == GST_XML_REGISTRY_PATHS_DONE)
401       break;
402
403     size = fread (text, 1, BLOCK_SIZE, reg);
404   }
405
406 finished:
407
408   g_markup_parse_context_free (context);
409
410   if (reg)
411     fclose (reg);
412
413   g_free (text);
414 }
415
416 static gboolean
417 plugin_times_older_than_recurse (gchar * path, time_t regtime)
418 {
419   DIR *dir;
420   struct dirent *dirent;
421   gboolean is_dir;
422   gchar *pluginname;
423
424   time_t pathtime = get_time (path, &is_dir);
425
426   if (pathtime > regtime) {
427     GST_CAT_INFO (GST_CAT_PLUGIN_LOADING,
428         "time for %s was %ld; more recent than registry time of %ld\n",
429         path, (long) pathtime, (long) regtime);
430     return FALSE;
431   }
432
433   if (!is_dir)
434     return TRUE;
435
436   dir = opendir (path);
437   if (dir) {
438     while ((dirent = readdir (dir))) {
439       /* don't want to recurse in place or backwards */
440       if (strcmp (dirent->d_name, ".") && strcmp (dirent->d_name, "..")) {
441         pluginname = g_strjoin ("/", path, dirent->d_name, NULL);
442         if (!plugin_times_older_than_recurse (pluginname, regtime)) {
443           g_free (pluginname);
444           closedir (dir);
445           return FALSE;
446         }
447         g_free (pluginname);
448       }
449     }
450     closedir (dir);
451   }
452   return TRUE;
453 }
454
455 static gboolean
456 plugin_times_older_than (GList * paths, time_t regtime)
457 {
458   /* return true iff regtime is more recent than the times of all the files
459    * in the plugin dirs.
460    */
461
462   while (paths) {
463     GST_CAT_LOG (GST_CAT_PLUGIN_LOADING,
464         "comparing plugin times from %s with %ld",
465         (gchar *) paths->data, (long) regtime);
466     if (!plugin_times_older_than_recurse (paths->data, regtime))
467       return FALSE;
468     paths = g_list_next (paths);
469   }
470   return TRUE;
471 }
472
473 static gboolean
474 gst_xml_registry_open_func (GstXMLRegistry * registry, GstXMLRegistryMode mode)
475 {
476   GstRegistry *gst_registry;
477   GList *paths;
478
479   gst_registry = GST_REGISTRY (registry);
480   paths = gst_registry->paths;
481   GST_CAT_DEBUG (GST_CAT_GST_INIT, "opening registry %s", registry->location);
482
483   g_return_val_if_fail (registry->open == FALSE, FALSE);
484
485   /* if it doesn't exist, first try to build it, and check if it worked
486    * if it's not readable, return false
487    * if it's out of date, rebuild it */
488   if (mode == GST_XML_REGISTRY_READ) {
489     if (!(gst_registry->flags & GST_REGISTRY_EXISTS)) {
490       /* if it's not writable, then don't bother */
491       if (!(gst_registry->flags & GST_REGISTRY_WRITABLE)) {
492         GST_CAT_INFO (GST_CAT_GST_INIT, "Registry isn't writable");
493         return FALSE;
494       }
495       GST_CAT_INFO (GST_CAT_GST_INIT,
496           "Registry doesn't exist, trying to build...");
497       gst_registry_rebuild (gst_registry);
498       gst_registry_save (gst_registry);
499       /* FIXME: verify that the flags actually get updated ! */
500       if (!(gst_registry->flags & GST_REGISTRY_EXISTS)) {
501         return FALSE;
502       }
503     }
504     /* at this point we know it exists */
505     g_return_val_if_fail (gst_registry->flags & GST_REGISTRY_READABLE, FALSE);
506
507     if (!plugin_times_older_than (paths, get_time (registry->location, NULL))) {
508       if (gst_registry->flags & GST_REGISTRY_WRITABLE) {
509         GST_CAT_INFO (GST_CAT_GST_INIT, "Registry out of date, rebuilding...");
510
511         gst_registry_rebuild (gst_registry);
512
513         gst_registry_save (gst_registry);
514
515         if (!plugin_times_older_than (paths, get_time (registry->location,
516                     NULL))) {
517           GST_CAT_INFO (GST_CAT_GST_INIT,
518               "Registry still out of date, something is wrong...");
519           return FALSE;
520         }
521       } else {
522         GST_CAT_INFO (GST_CAT_GST_INIT,
523             "Can't write to this registry and it's out of date, ignoring it");
524         return FALSE;
525       }
526     }
527
528     GST_CAT_DEBUG (GST_CAT_GST_INIT, "opening registry %s for reading",
529         registry->location);
530     registry->regfile = fopen (registry->location, "r");
531   } else if (mode == GST_XML_REGISTRY_WRITE) {
532     char *tmploc;
533     int fd;
534
535     g_return_val_if_fail (gst_registry->flags & GST_REGISTRY_WRITABLE, FALSE);
536
537     tmploc = g_strconcat (registry->location, ".tmp", NULL);
538
539     GST_CAT_DEBUG (GST_CAT_GST_INIT, "opening registry %s for writing", tmploc);
540
541     if ((fd = open (tmploc, O_WRONLY | O_CREAT | O_EXCL, 0644)) < 0) {
542       g_free (tmploc);
543       return FALSE;
544     }
545     g_free (tmploc);
546
547     registry->regfile = fdopen (fd, "w");
548   }
549
550   if (!registry->regfile)
551     return FALSE;
552
553   registry->open = TRUE;
554
555   return TRUE;
556 }
557
558 static gboolean
559 gst_xml_registry_load_func (GstXMLRegistry * registry, gchar * data,
560     gssize * size)
561 {
562   *size = fread (data, 1, *size, registry->regfile);
563
564   return TRUE;
565 }
566
567 static gboolean
568 gst_xml_registry_save_func (GstXMLRegistry * registry, gchar * format, ...)
569 {
570   va_list var_args;
571
572   va_start (var_args, format);
573
574   vfprintf (registry->regfile, format, var_args);
575
576   va_end (var_args);
577
578   return TRUE;
579 }
580
581 static gboolean
582 gst_xml_registry_close_func (GstXMLRegistry * registry)
583 {
584   char *tmploc;
585
586   GST_CAT_DEBUG (GST_CAT_GST_INIT, "closing registry %s", registry->location);
587   fclose (registry->regfile);
588
589   /* If we opened for writing, rename our temporary file. */
590   tmploc = g_strconcat (registry->location, ".tmp", NULL);
591   if (g_file_test (tmploc, G_FILE_TEST_EXISTS))
592     rename (tmploc, registry->location);
593   g_free (tmploc);
594
595   registry->open = FALSE;
596
597   return TRUE;
598 }
599
600 static gboolean
601 gst_xml_registry_load (GstRegistry * registry)
602 {
603   GstXMLRegistry *xmlregistry;
604   gchar *text;
605   gssize size;
606   GError *error = NULL;
607   GTimer *timer;
608   gdouble seconds;
609
610   xmlregistry = GST_XML_REGISTRY (registry);
611
612   timer = g_timer_new ();
613
614   xmlregistry->context =
615       g_markup_parse_context_new (&gst_xml_registry_parser, 0, registry, NULL);
616
617   if (!CLASS (xmlregistry)->open_func (xmlregistry, GST_XML_REGISTRY_READ)) {
618     g_timer_destroy (timer);
619     return FALSE;
620   }
621
622   text = g_malloc0 (BLOCK_SIZE + 32);
623
624   size = BLOCK_SIZE;
625   CLASS (xmlregistry)->load_func (xmlregistry, text, &size);
626
627   while (size) {
628     g_markup_parse_context_parse (xmlregistry->context, text, size, &error);
629
630     if (error) {
631       GST_ERROR ("parsing registry: %s\n", error->message);
632       g_free (text);
633       CLASS (xmlregistry)->close_func (xmlregistry);
634       g_timer_destroy (timer);
635       return FALSE;
636     }
637
638     size = BLOCK_SIZE;
639     CLASS (xmlregistry)->load_func (xmlregistry, text, &size);
640   }
641
642   g_free (text);
643
644   g_timer_stop (timer);
645
646   seconds = g_timer_elapsed (timer, NULL);
647   g_timer_destroy (timer);
648
649   GST_INFO ("loaded %s in %f seconds (%s)",
650       registry->name, seconds, xmlregistry->location);
651
652   CLASS (xmlregistry)->close_func (xmlregistry);
653
654
655   return TRUE;
656 }
657
658 static GstRegistryReturn
659 gst_xml_registry_load_plugin (GstRegistry * registry, GstPlugin * plugin)
660 {
661   GError *error = NULL;
662   GstPlugin *loaded_plugin;
663
664   /* FIXME: add gerror support */
665   loaded_plugin = gst_plugin_load_file (plugin->filename, &error);
666   if (!plugin) {
667     if (error) {
668       g_warning ("could not load plugin %s: %s", plugin->desc.name,
669           error->message);
670       g_error_free (error);
671     }
672     return GST_REGISTRY_PLUGIN_LOAD_ERROR;
673   } else if (loaded_plugin != plugin) {
674     g_critical ("how to remove plugins?");
675   }
676
677   return GST_REGISTRY_OK;
678 }
679
680 static gboolean
681 gst_xml_registry_parse_plugin (GMarkupParseContext * context, const gchar * tag,
682     const gchar * text, gsize text_len, GstXMLRegistry * registry,
683     GError ** error)
684 {
685   GstPlugin *plugin = registry->current_plugin;
686
687   if (!strcmp (tag, "name")) {
688     plugin->desc.name = g_strndup (text, text_len);
689   } else if (!strcmp (tag, "description")) {
690     plugin->desc.description = g_strndup (text, text_len);
691   } else if (!strcmp (tag, "filename")) {
692     plugin->filename = g_strndup (text, text_len);
693   } else if (!strcmp (tag, "version")) {
694     plugin->desc.version = g_strndup (text, text_len);
695   } else if (!strcmp (tag, "license")) {
696     plugin->desc.license = g_strndup (text, text_len);
697   } else if (!strcmp (tag, "package")) {
698     plugin->desc.package = g_strndup (text, text_len);
699   } else if (!strcmp (tag, "origin")) {
700     plugin->desc.origin = g_strndup (text, text_len);
701   }
702
703   return TRUE;
704 }
705
706 static void
707 add_to_char_array (gchar *** array, gchar * value)
708 {
709   gchar **new;
710   gchar **old = *array;
711   gint i = 0;
712
713   /* expensive, but cycles are cheap... */
714   if (old)
715     while (old[i])
716       i++;
717   new = g_new0 (gchar *, i + 2);
718   new[i] = value;
719   while (i > 0) {
720     i--;
721     new[i] = old[i];
722   }
723   g_free (old);
724   *array = new;
725 }
726
727 static gboolean
728 gst_xml_registry_parse_element_factory (GMarkupParseContext * context,
729     const gchar * tag, const gchar * text, gsize text_len,
730     GstXMLRegistry * registry, GError ** error)
731 {
732   GstElementFactory *factory = GST_ELEMENT_FACTORY (registry->current_feature);
733
734   if (!strcmp (tag, "name")) {
735     gchar *name = g_strndup (text, text_len);
736
737     gst_plugin_feature_set_name (registry->current_feature, name);
738     g_free (name);
739   } else if (!strcmp (tag, "longname")) {
740     g_free (factory->details.longname);
741     factory->details.longname = g_strndup (text, text_len);
742   } else if (!strcmp (tag, "class")) {
743     g_free (factory->details.klass);
744     factory->details.klass = g_strndup (text, text_len);
745   } else if (!strcmp (tag, "description")) {
746     g_free (factory->details.description);
747     factory->details.description = g_strndup (text, text_len);
748   } else if (!strcmp (tag, "author")) {
749     g_free (factory->details.author);
750     factory->details.author = g_strndup (text, text_len);
751   } else if (!strcmp (tag, "rank")) {
752     gint rank;
753     gchar *ret;
754
755     rank = strtol (text, &ret, 0);
756     if (ret == text + text_len) {
757       gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
758     }
759   } else if (!strcmp (tag, "uri_type")) {
760     if (strncasecmp (text, "sink", 4) == 0) {
761       factory->uri_type = GST_URI_SINK;
762     } else if (strncasecmp (text, "source", 5) == 0) {
763       factory->uri_type = GST_URI_SRC;
764     }
765   } else if (!strcmp (tag, "uri_protocol")) {
766     add_to_char_array (&factory->uri_protocols, g_strndup (text, text_len));
767   } else if (!strcmp (tag, "interface")) {
768     gchar *tmp = g_strndup (text, text_len);
769
770     __gst_element_factory_add_interface (factory, tmp);
771     g_free (tmp);
772   }
773
774   return TRUE;
775 }
776
777 static gboolean
778 gst_xml_registry_parse_type_find_factory (GMarkupParseContext * context,
779     const gchar * tag, const gchar * text, gsize text_len,
780     GstXMLRegistry * registry, GError ** error)
781 {
782   GstTypeFindFactory *factory =
783       GST_TYPE_FIND_FACTORY (registry->current_feature);
784
785   if (!strcmp (tag, "name")) {
786     registry->current_feature->name = g_strndup (text, text_len);
787   } else if (!strcmp (tag, "rank")) {
788     glong rank;
789     gchar *ret;
790
791     rank = strtol (text, &ret, 0);
792     if (ret == text + text_len) {
793       gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
794     }
795   }
796   /* FIXME!!
797      else if (!strcmp (tag, "caps")) {
798      factory->caps = g_strndup (text, text_len);
799      } */
800   else if (!strcmp (tag, "extension")) {
801     add_to_char_array (&factory->extensions, g_strndup (text, text_len));
802   }
803
804   return TRUE;
805 }
806
807 static gboolean
808 gst_xml_registry_parse_scheduler_factory (GMarkupParseContext * context,
809     const gchar * tag, const gchar * text, gsize text_len,
810     GstXMLRegistry * registry, GError ** error)
811 {
812   GstSchedulerFactory *factory =
813       GST_SCHEDULER_FACTORY (registry->current_feature);
814
815   if (!strcmp (tag, "name")) {
816     registry->current_feature->name = g_strndup (text, text_len);
817   } else if (!strcmp (tag, "longdesc")) {
818     factory->longdesc = g_strndup (text, text_len);
819   }
820   return TRUE;
821 }
822
823 static gboolean
824 gst_xml_registry_parse_index_factory (GMarkupParseContext * context,
825     const gchar * tag, const gchar * text, gsize text_len,
826     GstXMLRegistry * registry, GError ** error)
827 {
828   GstIndexFactory *factory = GST_INDEX_FACTORY (registry->current_feature);
829
830   if (!strcmp (tag, "name")) {
831     registry->current_feature->name = g_strndup (text, text_len);
832   } else if (!strcmp (tag, "longdesc")) {
833     factory->longdesc = g_strndup (text, text_len);
834   }
835   return TRUE;
836 }
837
838 static gboolean
839 gst_xml_registry_parse_padtemplate (GMarkupParseContext * context,
840     const gchar * tag, const gchar * text, gsize text_len,
841     GstXMLRegistry * registry, GError ** error)
842 {
843   if (!strcmp (tag, "nametemplate")) {
844     registry->name_template = g_strndup (text, text_len);
845   } else if (!strcmp (tag, "direction")) {
846     if (!strncmp (text, "sink", text_len)) {
847       registry->direction = GST_PAD_SINK;
848     } else if (!strncmp (text, "src", text_len)) {
849       registry->direction = GST_PAD_SRC;
850     }
851   } else if (!strcmp (tag, "presence")) {
852     if (!strncmp (text, "always", text_len)) {
853       registry->presence = GST_PAD_ALWAYS;
854     } else if (!strncmp (text, "sometimes", text_len)) {
855       registry->presence = GST_PAD_SOMETIMES;
856     } else if (!strncmp (text, "request", text_len)) {
857       registry->presence = GST_PAD_REQUEST;
858     }
859   } else if (!strncmp (tag, "caps", 4)) {
860     char *s;
861
862     s = g_strndup (text, text_len);
863     g_assert (registry->caps == NULL);
864     registry->caps = gst_caps_from_string (s);
865     if (registry->caps == NULL) {
866       g_critical ("Could not parse caps: length %d, content: %*s\n", text_len,
867           text_len, text);
868     }
869     g_free (s);
870     return TRUE;
871   }
872   return TRUE;
873 }
874
875 static void
876 gst_xml_registry_start_element (GMarkupParseContext * context,
877     const gchar * element_name,
878     const gchar ** attribute_names,
879     const gchar ** attribute_values, gpointer user_data, GError ** error)
880 {
881   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
882
883   xmlregistry->open_tags = g_list_prepend (xmlregistry->open_tags,
884       g_strdup (element_name));
885
886   switch (xmlregistry->state) {
887     case GST_XML_REGISTRY_NONE:
888       if (!strcmp (element_name, "GST-PluginRegistry")) {
889         xmlregistry->state = GST_XML_REGISTRY_TOP;
890       }
891       break;
892     case GST_XML_REGISTRY_TOP:
893       if (!strncmp (element_name, "plugin", 6)) {
894         xmlregistry->state = GST_XML_REGISTRY_PLUGIN;
895         xmlregistry->parser = gst_xml_registry_parse_plugin;
896         xmlregistry->current_plugin = (GstPlugin *) g_new0 (GstPlugin, 1);
897       }
898       break;
899     case GST_XML_REGISTRY_PLUGIN:
900       if (!strncmp (element_name, "feature", 7)) {
901         gint i = 0;
902         GstPluginFeature *feature = NULL;
903
904         xmlregistry->state = GST_XML_REGISTRY_FEATURE;
905
906         while (attribute_names[i]) {
907           if (!strncmp (attribute_names[i], "typename", 8)) {
908             feature =
909                 GST_PLUGIN_FEATURE (g_object_new (g_type_from_name
910                     (attribute_values[i]), NULL));
911             break;
912           }
913           i++;
914         }
915         if (feature) {
916           xmlregistry->current_feature = feature;
917
918           if (GST_IS_ELEMENT_FACTORY (feature)) {
919             GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
920
921             factory->padtemplates = NULL;
922             xmlregistry->parser = gst_xml_registry_parse_element_factory;
923             break;
924           } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
925             xmlregistry->parser = gst_xml_registry_parse_type_find_factory;
926           } else if (GST_IS_SCHEDULER_FACTORY (feature)) {
927             xmlregistry->parser = gst_xml_registry_parse_scheduler_factory;
928             GST_SCHEDULER_FACTORY (feature)->type = 0;
929           } else if (GST_IS_INDEX_FACTORY (feature)) {
930             xmlregistry->parser = gst_xml_registry_parse_index_factory;
931           } else {
932             g_warning ("unknown feature type");
933           }
934         }
935       }
936       break;
937     case GST_XML_REGISTRY_FEATURE:
938       if (!strncmp (element_name, "padtemplate", 11)) {
939         xmlregistry->state = GST_XML_REGISTRY_PADTEMPLATE;
940         xmlregistry->parser = gst_xml_registry_parse_padtemplate;
941         xmlregistry->name_template = NULL;
942         xmlregistry->direction = 0;
943         xmlregistry->presence = 0;
944         xmlregistry->caps = NULL;
945       }
946       break;
947     default:
948       break;
949   }
950 }
951
952 static void
953 gst_xml_registry_end_element (GMarkupParseContext * context,
954     const gchar * element_name, gpointer user_data, GError ** error)
955 {
956   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
957   gchar *open_tag = (gchar *) xmlregistry->open_tags->data;
958
959   xmlregistry->open_tags = g_list_remove (xmlregistry->open_tags, open_tag);
960   g_free (open_tag);
961
962   switch (xmlregistry->state) {
963     case GST_XML_REGISTRY_TOP:
964       if (!strcmp (element_name, "GST-PluginRegistry")) {
965         xmlregistry->state = GST_XML_REGISTRY_NONE;
966       }
967       break;
968     case GST_XML_REGISTRY_PLUGIN:
969       if (!strcmp (element_name, "plugin")) {
970         xmlregistry->state = GST_XML_REGISTRY_TOP;
971         xmlregistry->parser = NULL;
972         gst_registry_add_plugin (GST_REGISTRY (xmlregistry),
973             xmlregistry->current_plugin);
974       }
975       break;
976     case GST_XML_REGISTRY_FEATURE:
977       if (!strcmp (element_name, "feature")) {
978         xmlregistry->state = GST_XML_REGISTRY_PLUGIN;
979         xmlregistry->parser = gst_xml_registry_parse_plugin;
980         gst_plugin_add_feature (xmlregistry->current_plugin,
981             xmlregistry->current_feature);
982         xmlregistry->current_feature = NULL;
983       }
984       break;
985     case GST_XML_REGISTRY_PADTEMPLATE:
986       if (!strcmp (element_name, "padtemplate")) {
987         GstPadTemplate *template;
988
989         template = gst_pad_template_new (xmlregistry->name_template,
990             xmlregistry->direction, xmlregistry->presence, xmlregistry->caps);
991
992         g_free (xmlregistry->name_template);
993         xmlregistry->name_template = NULL;
994         xmlregistry->caps = NULL;
995
996         __gst_element_factory_add_pad_template (GST_ELEMENT_FACTORY
997             (xmlregistry->current_feature), template);
998         xmlregistry->state = GST_XML_REGISTRY_FEATURE;
999         xmlregistry->parser = gst_xml_registry_parse_element_factory;
1000       }
1001       break;
1002     default:
1003       break;
1004   }
1005 }
1006
1007 static void
1008 gst_xml_registry_text (GMarkupParseContext * context, const gchar * text,
1009     gsize text_len, gpointer user_data, GError ** error)
1010 {
1011   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1012   gchar *open_tag;
1013
1014   if (xmlregistry->open_tags) {
1015     open_tag = (gchar *) xmlregistry->open_tags->data;
1016
1017     if (!strcmp (open_tag, "plugin-path")) {
1018       //gst_plugin_add_path (g_strndup (text, text_len));
1019     } else if (xmlregistry->parser) {
1020       xmlregistry->parser (context, open_tag, text, text_len, xmlregistry,
1021           error);
1022     }
1023   }
1024 }
1025
1026 static void
1027 gst_xml_registry_passthrough (GMarkupParseContext * context,
1028     const gchar * passthrough_text, gsize text_len, gpointer user_data,
1029     GError ** error)
1030 {
1031 }
1032
1033 static void
1034 gst_xml_registry_error (GMarkupParseContext * context, GError * error,
1035     gpointer user_data)
1036 {
1037   GST_ERROR ("%s\n", error->message);
1038 }
1039
1040 static void
1041 gst_xml_registry_paths_start_element (GMarkupParseContext * context,
1042     const gchar * element_name,
1043     const gchar ** attribute_names,
1044     const gchar ** attribute_values, gpointer user_data, GError ** error)
1045 {
1046   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1047
1048   switch (xmlregistry->state) {
1049     case GST_XML_REGISTRY_NONE:
1050       if (!strcmp (element_name, "GST-PluginRegistry")) {
1051         xmlregistry->state = GST_XML_REGISTRY_TOP;
1052       }
1053       break;
1054     case GST_XML_REGISTRY_TOP:
1055       if (!strcmp (element_name, "gst-registry-paths")) {
1056         xmlregistry->state = GST_XML_REGISTRY_PATHS;
1057       }
1058       break;
1059     case GST_XML_REGISTRY_PATHS:
1060       if (!strcmp (element_name, "path")) {
1061         xmlregistry->state = GST_XML_REGISTRY_PATH;
1062       }
1063       break;
1064     default:
1065       break;
1066   }
1067 }
1068
1069 static void
1070 gst_xml_registry_paths_end_element (GMarkupParseContext * context,
1071     const gchar * element_name, gpointer user_data, GError ** error)
1072 {
1073   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1074
1075   switch (xmlregistry->state) {
1076     case GST_XML_REGISTRY_PATH:
1077       if (!strcmp (element_name, "path")) {
1078         xmlregistry->state = GST_XML_REGISTRY_PATHS;
1079       }
1080       break;
1081     case GST_XML_REGISTRY_PATHS:
1082       if (!strcmp (element_name, "gst-plugin-paths")) {
1083         xmlregistry->state = GST_XML_REGISTRY_PATHS_DONE;
1084       }
1085       break;
1086     default:
1087       break;
1088   }
1089 }
1090
1091 static void
1092 gst_xml_registry_paths_text (GMarkupParseContext * context, const gchar * text,
1093     gsize text_len, gpointer user_data, GError ** error)
1094 {
1095   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1096
1097   if (xmlregistry->state == GST_XML_REGISTRY_PATH)
1098     gst_registry_add_path (GST_REGISTRY (xmlregistry), g_strndup (text,
1099             text_len));
1100 }
1101
1102 /*
1103  * Save
1104  */
1105 #define PUT_ESCAPED(tag,value)                                  \
1106 G_STMT_START{                                                   \
1107   const gchar *toconv = value;                                  \
1108   if (toconv) {                                                 \
1109     gchar *v = g_markup_escape_text (toconv, strlen (toconv));  \
1110     CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, v, tag);                 \
1111     g_free (v);                                                 \
1112   }                                                             \
1113 }G_STMT_END
1114 #define PUT_ESCAPED_INT(tag,value)                              \
1115 G_STMT_START{                                                   \
1116   gchar *save = g_strdup_printf ("%ld", (glong) value);         \
1117   CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, save, tag);                \
1118   g_free (save);                                                \
1119 }G_STMT_END
1120
1121
1122 static gboolean
1123 gst_xml_registry_save_caps (GstXMLRegistry * xmlregistry, const GstCaps * caps)
1124 {
1125   char *s = gst_caps_to_string (caps);
1126
1127   PUT_ESCAPED ("caps", s);
1128   g_free (s);
1129   return TRUE;
1130 }
1131
1132 static gboolean
1133 gst_xml_registry_save_pad_template (GstXMLRegistry * xmlregistry,
1134     GstPadTemplate * template)
1135 {
1136   gchar *presence;
1137
1138   PUT_ESCAPED ("nametemplate", template->name_template);
1139   CLASS (xmlregistry)->save_func (xmlregistry, "<direction>%s</direction>\n",
1140       (template->direction == GST_PAD_SINK ? "sink" : "src"));
1141
1142   switch (template->presence) {
1143     case GST_PAD_ALWAYS:
1144       presence = "always";
1145       break;
1146     case GST_PAD_SOMETIMES:
1147       presence = "sometimes";
1148       break;
1149     case GST_PAD_REQUEST:
1150       presence = "request";
1151       break;
1152     default:
1153       presence = "unknown";
1154       break;
1155   }
1156   CLASS (xmlregistry)->save_func (xmlregistry, "<presence>%s</presence>\n",
1157       presence);
1158
1159   if (GST_PAD_TEMPLATE_CAPS (template)) {
1160     gst_xml_registry_save_caps (xmlregistry, GST_PAD_TEMPLATE_CAPS (template));
1161   }
1162   return TRUE;
1163 }
1164
1165 static gboolean
1166 gst_xml_registry_save_feature (GstXMLRegistry * xmlregistry,
1167     GstPluginFeature * feature)
1168 {
1169   PUT_ESCAPED ("name", feature->name);
1170
1171   if (feature->rank > 0) {
1172     gint rank = feature->rank;
1173
1174     CLASS (xmlregistry)->save_func (xmlregistry, "<rank>%d</rank>\n", rank);
1175   }
1176
1177   if (GST_IS_ELEMENT_FACTORY (feature)) {
1178     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
1179     GList *walk;
1180
1181     PUT_ESCAPED ("longname", factory->details.longname);
1182     PUT_ESCAPED ("class", factory->details.klass);
1183     PUT_ESCAPED ("description", factory->details.description);
1184     PUT_ESCAPED ("author", factory->details.author);
1185
1186     walk = factory->padtemplates;
1187
1188     while (walk) {
1189       GstPadTemplate *template = GST_PAD_TEMPLATE (walk->data);
1190
1191       CLASS (xmlregistry)->save_func (xmlregistry, "<padtemplate>\n");
1192       gst_xml_registry_save_pad_template (xmlregistry, template);
1193       CLASS (xmlregistry)->save_func (xmlregistry, "</padtemplate>\n");
1194
1195       walk = g_list_next (walk);
1196     }
1197
1198     walk = factory->interfaces;
1199     while (walk) {
1200       PUT_ESCAPED ("interface", (gchar *) walk->data);
1201       walk = g_list_next (walk);
1202     }
1203
1204     if (GST_URI_TYPE_IS_VALID (factory->uri_type)) {
1205       gchar **protocol;
1206
1207       PUT_ESCAPED ("uri_type",
1208           factory->uri_type == GST_URI_SINK ? "sink" : "source");
1209       g_assert (factory->uri_protocols);
1210       protocol = factory->uri_protocols;
1211       while (*protocol) {
1212         PUT_ESCAPED ("uri_protocol", *protocol);
1213         protocol++;
1214       }
1215     }
1216   } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
1217     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
1218     gint i = 0;
1219
1220     if (factory->caps) {
1221       gst_xml_registry_save_caps (xmlregistry, factory->caps);
1222     }
1223     if (factory->extensions) {
1224       while (factory->extensions[i]) {
1225         PUT_ESCAPED ("extension", factory->extensions[i]);
1226         i++;
1227       }
1228     }
1229   } else if (GST_IS_SCHEDULER_FACTORY (feature)) {
1230     PUT_ESCAPED ("longdesc", GST_SCHEDULER_FACTORY (feature)->longdesc);
1231   } else if (GST_IS_INDEX_FACTORY (feature)) {
1232     PUT_ESCAPED ("longdesc", GST_INDEX_FACTORY (feature)->longdesc);
1233   }
1234   return TRUE;
1235 }
1236
1237 static gboolean
1238 gst_xml_registry_save_plugin (GstXMLRegistry * xmlregistry, GstPlugin * plugin)
1239 {
1240   GList *walk;
1241
1242   PUT_ESCAPED ("name", plugin->desc.name);
1243   PUT_ESCAPED ("description", plugin->desc.description);
1244   PUT_ESCAPED ("filename", plugin->filename);
1245   PUT_ESCAPED ("version", plugin->desc.version);
1246   PUT_ESCAPED ("license", plugin->desc.license);
1247   PUT_ESCAPED ("package", plugin->desc.package);
1248   PUT_ESCAPED ("origin", plugin->desc.origin);
1249
1250   walk = plugin->features;
1251
1252   while (walk) {
1253     GstPluginFeature *feature = GST_PLUGIN_FEATURE (walk->data);
1254
1255     CLASS (xmlregistry)->save_func (xmlregistry, "<feature typename=\"%s\">\n",
1256         g_type_name (G_OBJECT_TYPE (feature)));
1257     gst_xml_registry_save_feature (xmlregistry, feature);
1258     CLASS (xmlregistry)->save_func (xmlregistry, "</feature>\n");
1259
1260     walk = g_list_next (walk);
1261   }
1262   return TRUE;
1263 }
1264
1265
1266 static gboolean
1267 gst_xml_registry_save (GstRegistry * registry)
1268 {
1269   GList *walk;
1270   GstXMLRegistry *xmlregistry;
1271
1272   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
1273   g_return_val_if_fail (registry->flags & GST_REGISTRY_WRITABLE, FALSE);
1274
1275   xmlregistry = GST_XML_REGISTRY (registry);
1276
1277   if (!CLASS (xmlregistry)->open_func (xmlregistry, GST_XML_REGISTRY_WRITE)) {
1278     return FALSE;
1279   }
1280
1281   CLASS (xmlregistry)->save_func (xmlregistry, "<?xml version=\"1.0\"?>\n");
1282   CLASS (xmlregistry)->save_func (xmlregistry, "<GST-PluginRegistry>\n");
1283
1284   walk = g_list_last (gst_registry_get_path_list (GST_REGISTRY (registry)));
1285
1286   CLASS (xmlregistry)->save_func (xmlregistry, "<gst-plugin-paths>\n");
1287   while (walk) {
1288     CLASS (xmlregistry)->save_func (xmlregistry, "<path>");
1289     CLASS (xmlregistry)->save_func (xmlregistry, (gchar *) walk->data);
1290     CLASS (xmlregistry)->save_func (xmlregistry, "</path>\n");
1291     walk = g_list_previous (walk);
1292   }
1293   CLASS (xmlregistry)->save_func (xmlregistry, "</gst-plugin-paths>\n");
1294
1295   walk = g_list_last (registry->plugins);
1296
1297   while (walk) {
1298     GstPlugin *plugin = GST_PLUGIN (walk->data);
1299
1300     CLASS (xmlregistry)->save_func (xmlregistry, "<plugin>\n");
1301     gst_xml_registry_save_plugin (xmlregistry, plugin);
1302     CLASS (xmlregistry)->save_func (xmlregistry, "</plugin>\n");
1303
1304     walk = g_list_previous (walk);
1305   }
1306   CLASS (xmlregistry)->save_func (xmlregistry, "</GST-PluginRegistry>\n");
1307
1308   CLASS (xmlregistry)->close_func (xmlregistry);
1309
1310   return TRUE;
1311 }
1312
1313 static GList *
1314 gst_xml_registry_rebuild_recurse (GstXMLRegistry * registry,
1315     const gchar * directory)
1316 {
1317   GDir *dir;
1318   GList *ret = NULL;
1319   gint dr_len, sf_len;
1320
1321   dir = g_dir_open (directory, 0, NULL);
1322
1323   if (dir) {
1324     const gchar *dirent;
1325
1326     while ((dirent = g_dir_read_name (dir))) {
1327       gchar *dirname;
1328
1329       if (*dirent == '=') {
1330         /* =build, =inst, etc. -- automake distcheck directories */
1331         continue;
1332       }
1333
1334       dirname = g_strjoin ("/", directory, dirent, NULL);
1335       ret =
1336           g_list_concat (ret, gst_xml_registry_rebuild_recurse (registry,
1337               dirname));
1338       g_free (dirname);
1339     }
1340     g_dir_close (dir);
1341   } else {
1342     dr_len = strlen (directory);
1343     sf_len = strlen (G_MODULE_SUFFIX);
1344     if (dr_len >= sf_len &&
1345         strcmp (directory + dr_len - sf_len, G_MODULE_SUFFIX) == 0) {
1346       ret = g_list_prepend (ret, g_strdup (directory));
1347     }
1348   }
1349
1350   return ret;
1351 }
1352
1353 static gboolean
1354 gst_xml_registry_rebuild (GstRegistry * registry)
1355 {
1356   GList *walk = NULL, *plugins = NULL, *prune = NULL;
1357   GError *error = NULL;
1358   guint length;
1359   GstPlugin *plugin;
1360   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (registry);
1361
1362   walk = registry->paths;
1363
1364   while (walk) {
1365     gchar *path = (gchar *) walk->data;
1366
1367     GST_CAT_INFO (GST_CAT_PLUGIN_LOADING,
1368         "Rebuilding registry %p in directory %s...", registry, path);
1369
1370     plugins = g_list_concat (plugins,
1371         gst_xml_registry_rebuild_recurse (xmlregistry, path));
1372
1373     walk = g_list_next (walk);
1374   }
1375
1376   plugins = g_list_reverse (plugins);
1377
1378   do {
1379     length = g_list_length (plugins);
1380
1381     walk = plugins;
1382     while (walk) {
1383       g_assert (walk->data);
1384       plugin = gst_plugin_load_file ((gchar *) walk->data, NULL);
1385       if (plugin) {
1386         prune = g_list_prepend (prune, walk->data);
1387         gst_registry_add_plugin (registry, plugin);
1388       }
1389
1390       walk = g_list_next (walk);
1391     }
1392
1393     walk = prune;
1394     while (walk) {
1395       plugins = g_list_remove (plugins, walk->data);
1396       g_free (walk->data);
1397       walk = g_list_next (walk);
1398     }
1399     g_list_free (prune);
1400     prune = NULL;
1401   } while (g_list_length (plugins) != length);
1402
1403   walk = plugins;
1404   while (walk) {
1405     if ((plugin = gst_plugin_load_file ((gchar *) walk->data, &error))) {
1406       g_warning ("Bizarre behavior: plugin %s actually loaded",
1407           (gchar *) walk->data);
1408       gst_registry_add_plugin (registry, plugin);
1409     } else {
1410       GST_CAT_INFO (GST_CAT_PLUGIN_LOADING, "Plugin %s failed to load: %s",
1411           (gchar *) walk->data, error->message);
1412
1413       g_free (walk->data);
1414       g_error_free (error);
1415       error = NULL;
1416     }
1417
1418     walk = g_list_next (walk);
1419   }
1420   return TRUE;
1421 }