Remove old autoplug code
[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 <utime.h>
34
35 #include <gst/gst_private.h>
36 #include <gst/gstelement.h>
37 #include <gst/gsttypefind.h>
38 #include <gst/gstscheduler.h>
39 #include <gst/gsturi.h>
40 #include <gst/gstinfo.h>
41
42 #include "gstxmlregistry.h"
43
44 #define BLOCK_SIZE 1024*10
45
46 #define CLASS(registry)  GST_XML_REGISTRY_CLASS (G_OBJECT_GET_CLASS (registry))
47
48
49 enum 
50 {
51   PROP_0,
52   PROP_LOCATION
53 };
54
55
56 static void             gst_xml_registry_class_init             (GstXMLRegistryClass *klass);
57 static void             gst_xml_registry_init                   (GstXMLRegistry *registry);
58
59 static void             gst_xml_registry_set_property           (GObject * object, guint prop_id,
60                                                                  const GValue * value, GParamSpec * pspec);
61 static void             gst_xml_registry_get_property           (GObject * object, guint prop_id,
62                                                                  GValue * value, GParamSpec * pspec);
63
64 static gboolean         gst_xml_registry_load                   (GstRegistry *registry);
65 static gboolean         gst_xml_registry_save                   (GstRegistry *registry);
66 static gboolean         gst_xml_registry_rebuild                (GstRegistry *registry);
67
68 static void             gst_xml_registry_get_perms_func         (GstXMLRegistry *registry);
69 static void             gst_xml_registry_add_path_list_func     (GstXMLRegistry *registry);
70 static gboolean         gst_xml_registry_open_func              (GstXMLRegistry *registry, GstXMLRegistryMode mode);
71 static gboolean         gst_xml_registry_load_func              (GstXMLRegistry *registry, gchar *data, gssize *size);
72 static gboolean         gst_xml_registry_save_func              (GstXMLRegistry *registry, gchar *format, ...);
73 static gboolean         gst_xml_registry_close_func             (GstXMLRegistry *registry);
74
75 static GstRegistryReturn gst_xml_registry_load_plugin           (GstRegistry *registry, GstPlugin *plugin);
76
77 static void             gst_xml_registry_start_element          (GMarkupParseContext *context,
78                                                                  const gchar         *element_name,
79                                                                  const gchar        **attribute_names,
80                                                                  const gchar        **attribute_values,
81                                                                  gpointer             user_data,
82                                                                  GError             **error);
83 static void             gst_xml_registry_end_element            (GMarkupParseContext *context,
84                                                                  const gchar         *element_name,
85                                                                  gpointer             user_data,
86                                                                  GError             **error);
87 static void             gst_xml_registry_text                   (GMarkupParseContext *context,
88                                                                  const gchar         *text,
89                                                                  gsize                text_len,  
90                                                                  gpointer             user_data,
91                                                                  GError             **error);
92 static void             gst_xml_registry_passthrough            (GMarkupParseContext *context,
93                                                                  const gchar         *passthrough_text,
94                                                                  gsize                text_len,  
95                                                                  gpointer             user_data,
96                                                                  GError             **error);
97 static void             gst_xml_registry_error                  (GMarkupParseContext *context,
98                                                                  GError              *error,
99                                                                  gpointer             user_data);
100
101
102 static void             gst_xml_registry_paths_start_element    (GMarkupParseContext *context,
103                                                                  const gchar         *element_name,
104                                                                  const gchar        **attribute_names,
105                                                                  const gchar        **attribute_values,
106                                                                  gpointer             user_data,
107                                                                  GError             **error);
108 static void             gst_xml_registry_paths_end_element      (GMarkupParseContext *context,
109                                                                  const gchar         *element_name,
110                                                                  gpointer             user_data,
111                                                                  GError             **error);
112 static void             gst_xml_registry_paths_text             (GMarkupParseContext *context,
113                                                                  const gchar         *text,
114                                                                  gsize                text_len,  
115                                                                  gpointer             user_data,
116                                                                  GError             **error);
117
118 static GstRegistryClass *parent_class = NULL;
119 /* static guint gst_xml_registry_signals[LAST_SIGNAL] = { 0 }; */
120
121 static const GMarkupParser 
122 gst_xml_registry_parser = 
123 {
124   gst_xml_registry_start_element,
125   gst_xml_registry_end_element,
126   gst_xml_registry_text,
127   gst_xml_registry_passthrough,
128   gst_xml_registry_error,
129 };
130
131 static const GMarkupParser 
132 gst_xml_registry_paths_parser = 
133 {
134   gst_xml_registry_paths_start_element,
135   gst_xml_registry_paths_end_element,
136   gst_xml_registry_paths_text,
137   NULL,
138   NULL
139 };
140
141
142 GType 
143 gst_xml_registry_get_type (void) 
144 {
145   static GType xml_registry_type = 0;
146
147   if (!xml_registry_type) {
148     static const GTypeInfo xml_registry_info = {
149       sizeof (GstXMLRegistryClass),
150       NULL,
151       NULL,
152       (GClassInitFunc) gst_xml_registry_class_init,
153       NULL,
154       NULL,
155       sizeof(GstXMLRegistry),
156       0,
157       (GInstanceInitFunc) gst_xml_registry_init,
158       NULL
159     };
160     xml_registry_type = g_type_register_static (GST_TYPE_REGISTRY, 
161                                                 "GstXMLRegistry", &xml_registry_info, 0);
162   }
163   return xml_registry_type;
164 }
165
166 static void
167 gst_xml_registry_class_init (GstXMLRegistryClass *klass)
168 {
169   GObjectClass *gobject_class;
170   GstRegistryClass *gstregistry_class;
171   GstXMLRegistryClass *gstxmlregistry_class;
172
173   gobject_class = (GObjectClass*)klass;
174   gstregistry_class = (GstRegistryClass*)klass;
175   gstxmlregistry_class = (GstXMLRegistryClass*)klass;
176
177   parent_class = g_type_class_ref (GST_TYPE_REGISTRY);
178
179   gobject_class->get_property           = GST_DEBUG_FUNCPTR (gst_xml_registry_get_property);
180   gobject_class->set_property           = GST_DEBUG_FUNCPTR (gst_xml_registry_set_property);
181
182   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LOCATION,
183     g_param_spec_string ("location", "Location", "Location of the registry file",
184                          NULL, G_PARAM_READWRITE));
185
186   gstregistry_class->load               = GST_DEBUG_FUNCPTR (gst_xml_registry_load);
187   gstregistry_class->save               = GST_DEBUG_FUNCPTR (gst_xml_registry_save);
188   gstregistry_class->rebuild            = GST_DEBUG_FUNCPTR (gst_xml_registry_rebuild);
189   
190   gstregistry_class->load_plugin        = GST_DEBUG_FUNCPTR (gst_xml_registry_load_plugin);
191
192   gstxmlregistry_class->get_perms_func  = GST_DEBUG_FUNCPTR (gst_xml_registry_get_perms_func);
193   gstxmlregistry_class->add_path_list_func = GST_DEBUG_FUNCPTR (gst_xml_registry_add_path_list_func);
194   gstxmlregistry_class->open_func       = GST_DEBUG_FUNCPTR (gst_xml_registry_open_func);
195   gstxmlregistry_class->load_func       = GST_DEBUG_FUNCPTR (gst_xml_registry_load_func);
196   gstxmlregistry_class->save_func       = GST_DEBUG_FUNCPTR (gst_xml_registry_save_func);
197   gstxmlregistry_class->close_func      = GST_DEBUG_FUNCPTR (gst_xml_registry_close_func);
198 }
199
200 static void
201 gst_xml_registry_init (GstXMLRegistry *registry)
202 {
203   registry->location = NULL;
204   registry->context = NULL;
205   registry->state = GST_XML_REGISTRY_NONE;
206   registry->current_plugin = NULL;
207   registry->current_feature = NULL;
208   registry->open_tags = NULL;
209 }
210
211 /**
212  * gst_xml_registry_new:
213  * @name: the name of the registry
214  * @location: the location of the registry file
215  *
216  * Create a new xml registry with the given name and location.
217  *
218  * Returns: a new GstXMLRegistry with the given name an location.
219  */
220 GstRegistry*
221 gst_xml_registry_new (const gchar *name, const gchar *location) 
222 {
223   GstXMLRegistry *xmlregistry;
224   
225   xmlregistry = GST_XML_REGISTRY (g_object_new (GST_TYPE_XML_REGISTRY, NULL));
226   
227   g_object_set (G_OBJECT (xmlregistry), "location", location, NULL);
228
229   GST_REGISTRY (xmlregistry)->name = g_strdup (name);
230
231   return GST_REGISTRY (xmlregistry);
232 }
233
234 static void
235 gst_xml_registry_set_property (GObject* object, guint prop_id, 
236                                const GValue* value, GParamSpec* pspec)
237 {
238   GstXMLRegistry *registry;
239             
240   registry = GST_XML_REGISTRY (object);
241
242   switch (prop_id) {
243     case PROP_LOCATION:
244       if (registry->open) {
245         CLASS (object)->close_func (registry);
246         g_return_if_fail (registry->open == FALSE);
247       }
248       
249       if (registry->location)
250         g_free (registry->location);
251       
252       registry->location = g_strdup (g_value_get_string (value));
253       GST_REGISTRY (registry)->flags = 0x0;
254
255       if (CLASS (object)->get_perms_func)
256         CLASS (object)->get_perms_func (registry);
257
258       if (CLASS (object)->add_path_list_func)
259         CLASS (object)->add_path_list_func (registry);
260       break;
261     default:
262       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
263       break;
264   }
265 }
266
267 static void
268 gst_xml_registry_get_property (GObject* object, guint prop_id, 
269                                GValue* value, GParamSpec* pspec)
270 {
271   GstXMLRegistry *registry;
272             
273   registry = GST_XML_REGISTRY (object);
274
275   switch (prop_id) {
276     case PROP_LOCATION:
277       g_value_set_string (value, g_strdup (registry->location));
278       break;
279     default:
280       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
281       break;
282   }
283 }
284
285 /* this function returns the biggest of the path's mtime and ctime
286  * mtime is updated through an actual write (data)
287  * ctime is updated through changing inode information
288  * so this function returns the last time *anything* changed to this path
289  */
290 static time_t
291 get_time(const char * path)
292 {
293   struct stat statbuf;
294   if (stat(path, &statbuf)) return 0;
295   if (statbuf.st_mtime > statbuf.st_ctime) return statbuf.st_mtime;
296   return statbuf.st_ctime;
297 }
298
299 /* same as 0755 */
300 #define dirmode \
301   (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
302
303 static gboolean
304 make_dir (gchar *filename) 
305 {
306   struct stat dirstat;
307   gchar *dirname;
308
309   if (strrchr (filename, '/') == NULL)
310     return FALSE;
311   
312   dirname = g_strndup(filename, strrchr(filename, '/') - filename);
313
314   if (stat (dirname, &dirstat) == -1 && errno == ENOENT) {
315     if (mkdir (dirname, dirmode) != 0) {
316       if (make_dir (dirname) != TRUE) {
317         g_free(dirname);
318         return FALSE;
319       } else {
320         if (mkdir (dirname, dirmode) != 0)
321           return FALSE;
322       }
323     }
324   }
325
326   g_free(dirname);
327   return TRUE;
328 }
329
330 static void
331 gst_xml_registry_get_perms_func (GstXMLRegistry *registry)
332 {
333   time_t mod_time = 0;
334   FILE *temp;
335
336   /* if the dir does not exist, make it. if that can't be done, flags = 0x0.
337      if the file can be appended to, it's writable. if it can then be read,
338      it's readable. 
339      After that check if it exists. */
340   
341   if (make_dir (registry->location) != TRUE) {
342     /* we can't do anything with it, leave flags as 0x0 */
343     return;
344   }
345   
346   mod_time = get_time (registry->location);
347   
348   if ((temp = fopen (registry->location, "a"))) {
349     GST_REGISTRY (registry)->flags |= GST_REGISTRY_WRITABLE;
350     fclose (temp);
351   }
352   
353   if ((temp = fopen (registry->location, "r"))) {
354     GST_REGISTRY (registry)->flags |= GST_REGISTRY_READABLE;
355     fclose (temp);
356   }
357
358   if (g_file_test (registry->location, G_FILE_TEST_EXISTS)) {
359     GST_REGISTRY (registry)->flags |= GST_REGISTRY_EXISTS;
360   }
361
362   if (mod_time) {
363     struct utimbuf utime_buf;
364     
365     /* set the modification time back to its previous value */
366     utime_buf.actime = mod_time;
367     utime_buf.modtime = mod_time;
368     utime (registry->location, &utime_buf);
369   } else if (GST_REGISTRY (registry)->flags & GST_REGISTRY_WRITABLE) {
370     /* it did not exist before, so delete it */
371     unlink (registry->location);
372   }
373 }
374       
375 static void
376 gst_xml_registry_add_path_list_func (GstXMLRegistry *registry)
377 {
378   FILE *reg;
379   GMarkupParseContext *context;
380   gchar *text;
381   gssize size;
382   GError *error = NULL;
383
384   context = g_markup_parse_context_new (&gst_xml_registry_paths_parser, 0, 
385                                         registry, NULL);
386
387   if (! (reg = fopen (registry->location, "r"))) {
388     return;
389   }
390
391   /* slightly allocate more as gmarkup reads too much */
392   text = g_malloc0 (BLOCK_SIZE + 32);
393
394   size = fread (text, 1, BLOCK_SIZE, reg);
395
396   while (size) {
397     g_markup_parse_context_parse (context, text, size, &error);
398
399     if (error) {
400       GST_ERROR ("parsing registry %s: %s\n", 
401                  registry->location, error->message);
402       g_free (text);
403       fclose (reg);
404       return;
405     }
406
407     if (registry->state == GST_XML_REGISTRY_PATHS_DONE)
408       break;
409
410     size = fread (text, 1, BLOCK_SIZE, reg);
411   }
412
413   fclose (reg);
414
415   g_free (text);
416 }
417
418 static gboolean
419 plugin_times_older_than_recurse(gchar *path, time_t regtime)
420 {
421   DIR *dir;
422   struct dirent *dirent;
423   gchar *pluginname;
424
425   time_t pathtime = get_time(path);
426
427   if (pathtime > regtime) {
428     GST_CAT_INFO (GST_CAT_PLUGIN_LOADING,
429                      "time for %s was %ld; more recent than registry time of %ld\n",
430                      path, (long)pathtime, (long)regtime);
431     return FALSE;
432   }
433
434   dir = opendir(path);
435   if (dir) {
436     while ((dirent = readdir(dir))) {
437       /* don't want to recurse in place or backwards */
438       if (strcmp(dirent->d_name,".") && strcmp(dirent->d_name,"..")) {
439         pluginname = g_strjoin("/",path,dirent->d_name,NULL);
440         if (!plugin_times_older_than_recurse(pluginname , regtime)) {
441           g_free (pluginname);
442           closedir(dir);
443           return FALSE;
444         }
445         g_free (pluginname);
446       }
447     }
448     closedir(dir);
449   }
450   return TRUE;
451 }
452
453 static gboolean
454 plugin_times_older_than(GList *paths, time_t regtime)
455 {
456   /* return true iff regtime is more recent than the times of all the files
457    * in the plugin dirs.
458    */
459
460   while (paths) {
461     GST_CAT_DEBUG (GST_CAT_PLUGIN_LOADING,
462                       "comparing plugin times from %s with %ld\n",
463                       (gchar *)paths->data, (long) regtime);
464     if(!plugin_times_older_than_recurse(paths->data, regtime))
465       return FALSE;
466     paths = g_list_next(paths);
467   }
468   return TRUE;
469 }
470
471 static gboolean
472 gst_xml_registry_open_func (GstXMLRegistry *registry, GstXMLRegistryMode mode)
473 {
474   GstRegistry *gst_registry;
475   GList *paths;
476
477   gst_registry = GST_REGISTRY (registry);
478   paths = gst_registry->paths;
479
480   g_return_val_if_fail (registry->open == FALSE, FALSE);
481
482   /* if it doesn't exist, first try to build it, and check if it worked
483    * if it's not readable, return false
484    * if it's out of date, rebuild it */
485   if (mode == GST_XML_REGISTRY_READ) {
486     if (!(gst_registry->flags & GST_REGISTRY_EXISTS))
487     {
488       /* if it's not writable, then don't bother */
489       if (!(gst_registry->flags & GST_REGISTRY_WRITABLE))
490       {
491         GST_CAT_INFO (GST_CAT_GST_INIT, "Registry isn't writable");
492         return FALSE;
493       }
494       GST_CAT_INFO (GST_CAT_GST_INIT, "Registry doesn't exist, trying to build...");
495       gst_registry_rebuild (gst_registry);
496       gst_registry_save (gst_registry);
497       /* FIXME: verify that the flags actually get updated ! */
498       if (!(gst_registry->flags & GST_REGISTRY_EXISTS))
499       {
500         return FALSE;
501       }
502     }
503     /* at this point we know it exists */
504     g_return_val_if_fail (gst_registry->flags & GST_REGISTRY_READABLE, FALSE);
505
506     if (!plugin_times_older_than (paths, get_time (registry->location))) {
507       if (gst_registry->flags & GST_REGISTRY_WRITABLE) {
508         GST_CAT_INFO (GST_CAT_GST_INIT, "Registry out of date, rebuilding...");
509       
510         gst_registry_rebuild (gst_registry);
511
512         gst_registry_save (gst_registry);
513
514         if (!plugin_times_older_than (paths, get_time (registry->location))) {
515           GST_CAT_INFO (GST_CAT_GST_INIT, "Registry still out of date, something is wrong...");
516           return FALSE;
517         }
518       } else {
519         GST_CAT_INFO (GST_CAT_PLUGIN_LOADING, "Can't write to this registry and it's out of date, ignoring it");
520         return FALSE;
521       }
522     }
523     
524     registry->regfile = fopen (registry->location, "r");
525   }
526   else if (mode == GST_XML_REGISTRY_WRITE)
527   {
528     g_return_val_if_fail (gst_registry->flags & GST_REGISTRY_WRITABLE, FALSE);
529
530     registry->regfile = fopen (registry->location, "w");
531   }
532
533   if (!registry->regfile)
534     return FALSE;
535
536   registry->open = TRUE;
537
538   return TRUE;
539 }
540
541 static gboolean
542 gst_xml_registry_load_func (GstXMLRegistry *registry, gchar *data, gssize *size)
543 {
544   *size = fread (data, 1, *size, registry->regfile);
545   
546   return TRUE;
547 }
548
549 static gboolean
550 gst_xml_registry_save_func (GstXMLRegistry *registry, gchar *format, ...)
551 {
552   va_list var_args;
553
554   va_start (var_args, format);
555
556   vfprintf (registry->regfile, format, var_args);
557
558   va_end (var_args);
559
560   return TRUE;
561 }
562
563 static gboolean
564 gst_xml_registry_close_func (GstXMLRegistry *registry)
565 {
566   fclose (registry->regfile);
567
568   registry->open = FALSE;
569
570   return TRUE;
571 }
572
573 static gboolean
574 gst_xml_registry_load (GstRegistry *registry)
575 {
576   GstXMLRegistry *xmlregistry;
577   gchar *text;
578   gssize size;
579   GError *error = NULL;
580   GTimer *timer;
581   gdouble seconds;
582
583   xmlregistry = GST_XML_REGISTRY (registry);
584
585   timer = g_timer_new();
586   
587   xmlregistry->context = g_markup_parse_context_new (&gst_xml_registry_parser, 0, registry, NULL);
588
589   if (!CLASS (xmlregistry)->open_func (xmlregistry, GST_XML_REGISTRY_READ)) {
590     return FALSE;
591   }
592
593   text = g_malloc0 (BLOCK_SIZE + 32);
594
595   size = BLOCK_SIZE;
596   CLASS (xmlregistry)->load_func (xmlregistry, text, &size);
597
598   while (size) {
599     g_markup_parse_context_parse (xmlregistry->context, text, size, &error);
600
601     if (error) {
602       GST_ERROR ("parsing registry: %s\n", error->message);
603       g_free (text);
604       CLASS (xmlregistry)->close_func (xmlregistry);
605       return FALSE;
606     }
607
608     size = BLOCK_SIZE;
609     CLASS (xmlregistry)->load_func (xmlregistry, text, &size);
610   }
611
612   g_free (text);
613
614   g_timer_stop (timer);
615
616   seconds = g_timer_elapsed (timer, NULL);
617   g_timer_destroy (timer);
618
619   GST_INFO ( "registry: loaded %s in %f seconds\n          (%s)", 
620            registry->name, seconds, xmlregistry->location);
621
622   CLASS (xmlregistry)->close_func (xmlregistry);
623
624   
625   return TRUE;
626 }
627
628 static GstRegistryReturn 
629 gst_xml_registry_load_plugin (GstRegistry *registry, GstPlugin *plugin)
630 {
631   GError *error = NULL;
632   GstPlugin *loaded_plugin;
633
634   /* FIXME: add gerror support */
635   loaded_plugin = gst_plugin_load_file (plugin->filename, &error);
636   if (!plugin) {
637     if (error) {
638       g_warning ("could not load plugin %s: %s", plugin->desc.name, error->message);
639     }
640     return GST_REGISTRY_PLUGIN_LOAD_ERROR;
641   } else if (loaded_plugin != plugin) {
642     g_critical ("how to remove plugins?");
643   }
644
645   return GST_REGISTRY_OK;
646 }
647
648 static gboolean
649 gst_xml_registry_parse_plugin (GMarkupParseContext *context, const gchar *tag, const gchar *text,
650                                gsize text_len, GstXMLRegistry *registry, GError **error)
651 {
652   GstPlugin *plugin = registry->current_plugin;
653   
654   if (!strcmp (tag, "name")) {
655     plugin->desc.name = g_strndup (text, text_len);
656   }
657   else if (!strcmp (tag, "description")) {
658     plugin->desc.description = g_strndup (text, text_len);
659   }
660   else if (!strcmp (tag, "filename")) {
661     plugin->filename = g_strndup (text, text_len);
662   }
663   else if (!strcmp (tag, "version")) {
664     plugin->desc.version = g_strndup (text, text_len);
665   }
666   else if (!strcmp (tag, "copyright")) {
667     plugin->desc.copyright = g_strndup (text, text_len);
668   }
669   else if (!strcmp (tag, "license")) {
670     plugin->desc.license = g_strndup (text, text_len);
671   }
672   else if (!strcmp (tag, "package")) {
673     plugin->desc.package = g_strndup (text, text_len);
674   }
675   else if (!strcmp (tag, "origin")) {
676     plugin->desc.origin = g_strndup (text, text_len);
677   }
678   
679   return TRUE;
680 }
681
682 static void
683 add_to_char_array (gchar ***array, gchar *value)
684 {
685   gchar **new;
686   gchar **old = *array;
687   gint i = 0;
688     
689   /* expensive, but cycles are cheap... */
690   if (old)
691     while (old[i]) i++;
692   new = g_new0 (gchar *, i + 2);
693   new[i] = value;
694   while (i > 0) {
695     i--;
696     new[i] = old[i];
697   }
698   g_free (old);
699   *array = new;
700 }
701
702 static gboolean
703 gst_xml_registry_parse_element_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
704                                         gsize text_len, GstXMLRegistry *registry, GError **error)
705 {
706   GstElementFactory *factory = GST_ELEMENT_FACTORY (registry->current_feature);
707
708   if (!strcmp (tag, "name")) {
709     gchar *name = g_strndup (text, text_len);
710     gst_plugin_feature_set_name (registry->current_feature, name);
711     g_free (name);
712   }
713   else if (!strcmp (tag, "longname")) {
714     g_free (factory->details.longname);
715     factory->details.longname = g_strndup (text, text_len);
716   }
717   else if (!strcmp(tag, "class")) {
718     g_free (factory->details.klass);
719     factory->details.klass = g_strndup (text, text_len);
720   }
721   else if (!strcmp(tag, "description")) {
722     g_free (factory->details.description);
723     factory->details.description = g_strndup (text, text_len);
724   }
725   else if (!strcmp(tag, "author")) {
726     g_free (factory->details.author);
727     factory->details.author = g_strndup (text, text_len);
728   }
729   else if (!strcmp(tag, "rank")) {
730     gint rank;
731     gchar *ret;
732     
733     rank = strtol (text, &ret, 0);
734     if (ret == text + text_len) {
735      gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
736     }
737   } else if (!strcmp (tag, "uri_type")) {
738     if (strncasecmp (text, "sink", 4) == 0) {
739       factory->uri_type = GST_URI_SINK;
740     } else if (strncasecmp (text, "source", 5) == 0) {
741       factory->uri_type = GST_URI_SRC;
742     }
743   } else if (!strcmp (tag, "uri_protocol")) {
744     add_to_char_array (&factory->uri_protocols, g_strndup (text, text_len));
745   }
746   else if (!strcmp(tag, "interface")) {
747     gchar *tmp = g_strndup (text, text_len);
748     __gst_element_factory_add_interface (factory, tmp);
749     g_free (tmp);
750   }
751
752   return TRUE;
753 }
754
755 static gboolean
756 gst_xml_registry_parse_type_find_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
757                                           gsize text_len, GstXMLRegistry *registry, GError **error)
758 {
759   GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (registry->current_feature);
760
761   if (!strcmp (tag, "name")) {
762     registry->current_feature->name = g_strndup (text, text_len);
763   }
764   else if (!strcmp(tag, "rank")) {
765     glong rank;
766     gchar *ret;
767      rank = strtol (text, &ret, 0);
768     if (ret == text + text_len) {
769      gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
770     }
771   }
772   /* FIXME!!
773   else if (!strcmp (tag, "caps")) {
774     factory->caps = g_strndup (text, text_len);
775   }*/
776   else if (!strcmp(tag, "extension")) {
777     add_to_char_array (&factory->extensions, g_strndup (text, text_len));
778   }
779
780   return TRUE;
781 }
782
783 static gboolean
784 gst_xml_registry_parse_scheduler_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
785                                           gsize text_len, GstXMLRegistry *registry, GError **error)
786 {
787   GstSchedulerFactory *factory = GST_SCHEDULER_FACTORY (registry->current_feature);
788
789   if (!strcmp (tag, "name")) {
790     registry->current_feature->name = g_strndup (text, text_len);
791   }
792   else if (!strcmp (tag, "longdesc")) {
793     factory->longdesc = g_strndup (text, text_len);
794   }
795   return TRUE;
796 }
797
798 static gboolean
799 gst_xml_registry_parse_index_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
800                                       gsize text_len, GstXMLRegistry *registry, GError **error)
801 {
802   GstIndexFactory *factory = GST_INDEX_FACTORY (registry->current_feature);
803
804   if (!strcmp (tag, "name")) {
805     registry->current_feature->name = g_strndup (text, text_len);
806   }
807   else if (!strcmp (tag, "longdesc")) {
808     factory->longdesc = g_strndup (text, text_len);
809   }
810   return TRUE;
811 }
812
813 static gboolean
814 gst_xml_registry_parse_padtemplate (GMarkupParseContext *context, const gchar *tag, const gchar *text,
815                                     gsize text_len, GstXMLRegistry *registry, GError **error)
816 {
817   if (!strcmp (tag, "nametemplate")) {
818     registry->name_template = g_strndup (text, text_len);
819   }
820   else if (!strcmp (tag, "direction")) {
821     if (!strncmp(text, "sink", text_len)) {
822       registry->direction = GST_PAD_SINK;
823     }
824     else if (!strncmp(text, "src", text_len)) {
825       registry->direction = GST_PAD_SRC;
826     }
827   }
828   else if (!strcmp (tag, "presence")) {
829     if (!strncmp(text, "always", text_len)) {
830       registry->presence = GST_PAD_ALWAYS;
831     }
832     else if (!strncmp(text, "sometimes", text_len)) {
833       registry->presence = GST_PAD_SOMETIMES;
834     }
835     else if (!strncmp(text, "request", text_len)) {
836       registry->presence = GST_PAD_REQUEST;
837     }
838   }
839   return TRUE;
840 }
841
842 static gboolean
843 gst_xml_registry_parse_capscomp (GMarkupParseContext *context, const gchar *tag, const gchar *text,
844                                  gsize text_len, GstXMLRegistry *registry, GError **error)
845 {
846   if (!strcmp (tag, "name")) {
847     registry->caps_name = g_strndup (text, text_len);
848   }
849   else if (!strcmp (tag, "type")) {
850     registry->caps_mime = g_strndup (text, text_len);
851   }
852   return TRUE;
853 }
854
855 static gint
856 find_index_for (const gchar *name, const gchar **attribute_names)
857 {
858   gint i=0;
859   
860   while (attribute_names[i]) {
861     if (!strcmp (attribute_names[i], name))
862       return i;
863     i++;
864   }
865   return -1;
866 }
867
868 static void
869 gst_xml_registry_start_element (GMarkupParseContext *context, 
870                                 const gchar *element_name,
871                                 const gchar **attribute_names, 
872                                 const gchar **attribute_values,
873                                 gpointer user_data, GError **error)
874 {
875   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
876
877   xmlregistry->open_tags = g_list_prepend (xmlregistry->open_tags, 
878                                            g_strdup (element_name));
879
880   switch (xmlregistry->state) {
881     case GST_XML_REGISTRY_NONE:
882       if (!strcmp (element_name, "GST-PluginRegistry")) {
883         xmlregistry->state = GST_XML_REGISTRY_TOP;
884       }
885       break;
886     case GST_XML_REGISTRY_TOP:
887       if (!strncmp (element_name, "plugin", 6)) {
888         xmlregistry->state = GST_XML_REGISTRY_PLUGIN;
889         xmlregistry->parser = gst_xml_registry_parse_plugin;
890         xmlregistry->current_plugin = (GstPlugin *) g_new0 (GstPlugin, 1);
891       }
892       break;
893     case GST_XML_REGISTRY_PLUGIN:
894       if (!strncmp (element_name, "feature", 7)) {
895         gint i = 0;
896         GstPluginFeature *feature = NULL;
897         
898         xmlregistry->state = GST_XML_REGISTRY_FEATURE;
899
900         while (attribute_names[i]) {
901           if (!strncmp (attribute_names[i], "typename", 8)) {
902             feature = GST_PLUGIN_FEATURE (g_object_new (g_type_from_name (attribute_values[i]), NULL));
903             break;
904           }
905           i++;
906         }
907         if (feature) {
908           xmlregistry->current_feature = feature;
909
910           if (GST_IS_ELEMENT_FACTORY (feature)) {
911             GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
912
913             factory->padtemplates = NULL;
914             xmlregistry->parser = gst_xml_registry_parse_element_factory;
915             break;
916           }
917           else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
918             xmlregistry->parser = gst_xml_registry_parse_type_find_factory;
919           }
920           else if (GST_IS_SCHEDULER_FACTORY (feature)) {
921             xmlregistry->parser = gst_xml_registry_parse_scheduler_factory;
922             GST_SCHEDULER_FACTORY (feature)->type = 0;
923           }
924           else if (GST_IS_INDEX_FACTORY (feature)) {
925             xmlregistry->parser = gst_xml_registry_parse_index_factory;
926           }
927           else {
928             g_warning ("unkown 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     case GST_XML_REGISTRY_PADTEMPLATE:
944       if (!strncmp (element_name, "caps", 4)) {
945         xmlregistry->state = GST_XML_REGISTRY_CAPS;
946         xmlregistry->parser = NULL;
947       }
948       break;
949     case GST_XML_REGISTRY_CAPS:
950       if (!strncmp (element_name, "capscomp", 8)) {
951         xmlregistry->state = GST_XML_REGISTRY_CAPSCOMP;
952         xmlregistry->parser = gst_xml_registry_parse_capscomp;
953       }
954       break;
955     case GST_XML_REGISTRY_CAPSCOMP:
956       if (!strncmp (element_name, "properties", 10)) {
957         xmlregistry->state = GST_XML_REGISTRY_PROPERTIES;
958         xmlregistry->parser = NULL;
959         xmlregistry->props = gst_props_empty_new ();
960       }
961       break;
962     case GST_XML_REGISTRY_PROPERTIES:
963     {
964       gint name_index;
965       GstPropsEntry *entry = NULL;
966
967       name_index = find_index_for ("name", attribute_names);
968       if (name_index < 0)
969         break;
970
971       if (!strncmp (element_name, "list", 4)) {
972         xmlregistry->in_list = TRUE;
973         xmlregistry->list_name = g_strdup (attribute_values[name_index]);
974       }
975
976       if (!strncmp (element_name, "int", 3)) {
977         gint value;
978         gint index;
979
980         if ((index = find_index_for ("value", attribute_names)) < 0) 
981           break;
982         sscanf (attribute_values[index], "%d", &value);
983
984         entry = gst_props_entry_new (attribute_values[name_index], 
985                                      GST_PROPS_INT (value));
986       }
987       else if (!strncmp (element_name, "range", 5)) {
988         gint min, max;
989         gint min_idx, max_idx;
990
991         if ((min_idx = find_index_for ("min", attribute_names)) < 0) 
992           break;
993         if ((max_idx = find_index_for ("max", attribute_names)) < 0) 
994           break;
995         sscanf (attribute_values[min_idx], "%d", &min);
996         sscanf (attribute_values[max_idx], "%d", &max);
997
998         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_INT_RANGE (min, max));
999       }
1000       else if (!strncmp (element_name, "float", 5)) {
1001         gfloat value;
1002         gint index;
1003
1004         if ((index = find_index_for ("value", attribute_names)) < 0) 
1005           break;
1006         sscanf (attribute_values[index], "%f", &value);
1007         
1008         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_FLOAT (value));
1009       }
1010       else if (!strncmp (element_name, "floatrange", 10)) {
1011         gfloat min, max;
1012         gint min_idx, max_idx;
1013
1014         if ((min_idx = find_index_for ("min", attribute_names)) < 0) 
1015           break;
1016         if ((max_idx = find_index_for ("max", attribute_names)) < 0) 
1017           break;
1018         sscanf (attribute_values[min_idx], "%f", &min);
1019         sscanf (attribute_values[max_idx], "%f", &max);
1020
1021         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_FLOAT_RANGE (min, max));
1022       }
1023       else if (!strncmp (element_name, "boolean", 7)) {
1024         gboolean value = TRUE;
1025         gint index;
1026
1027         if ((index = find_index_for ("value", attribute_names)) < 0) 
1028           break;
1029         if (!strcmp (attribute_values[index], "false")) 
1030           value = FALSE;
1031
1032         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_BOOLEAN (value));
1033       }
1034       else if (!strncmp (element_name, "fourcc", 6)) {
1035         guint32 value;
1036         gint index;
1037
1038         if ((index = find_index_for ("hexvalue", attribute_names)) < 0) 
1039           break;
1040         sscanf (attribute_values[index], "%08x", &value);
1041
1042         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_FOURCC (value));
1043       }
1044       else if (!strncmp (element_name, "string", 6)) {
1045         gint index;
1046
1047         if ((index = find_index_for ("value", attribute_names)) < 0) 
1048           break;
1049
1050         entry = gst_props_entry_new (attribute_values[name_index], 
1051                                      GST_PROPS_STRING (attribute_values[index]));
1052       }
1053       /* add property to list or parent */
1054       if (entry) {
1055         if (xmlregistry->in_list)
1056           xmlregistry->entry_list = g_list_prepend (xmlregistry->entry_list, entry);
1057         else
1058           gst_props_add_entry (xmlregistry->props, entry);
1059       }
1060       break;
1061     }
1062     default:
1063       break;
1064   }
1065 }
1066
1067 static void
1068 gst_xml_registry_end_element (GMarkupParseContext *context, 
1069                               const gchar *element_name,
1070                               gpointer user_data, GError **error)
1071 {
1072   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1073   gchar *open_tag = (gchar *)xmlregistry->open_tags->data;
1074
1075   xmlregistry->open_tags = g_list_remove (xmlregistry->open_tags, open_tag);
1076   g_free (open_tag);
1077
1078   switch (xmlregistry->state) {
1079     case GST_XML_REGISTRY_TOP:
1080       if (!strcmp (element_name, "GST-PluginRegistry")) {
1081         xmlregistry->state = GST_XML_REGISTRY_NONE;
1082       }
1083       break;
1084     case GST_XML_REGISTRY_PLUGIN:
1085       if (!strcmp (element_name, "plugin")) {
1086         xmlregistry->state = GST_XML_REGISTRY_TOP;
1087         xmlregistry->parser = NULL;
1088         gst_registry_add_plugin (GST_REGISTRY (xmlregistry), xmlregistry->current_plugin);
1089       }
1090       break;
1091     case GST_XML_REGISTRY_FEATURE:
1092       if (!strcmp (element_name, "feature")) {
1093         xmlregistry->state = GST_XML_REGISTRY_PLUGIN;
1094         xmlregistry->parser = gst_xml_registry_parse_plugin;
1095         gst_plugin_add_feature (xmlregistry->current_plugin, xmlregistry->current_feature);
1096         xmlregistry->current_feature = NULL;
1097       }
1098       break;
1099     case GST_XML_REGISTRY_PADTEMPLATE:
1100       if (!strcmp (element_name, "padtemplate")) {
1101         GstPadTemplate *template;
1102
1103         template = gst_pad_template_new (xmlregistry->name_template,
1104                                          xmlregistry->direction,
1105                                          xmlregistry->presence,
1106                                          xmlregistry->caps, NULL);
1107
1108         g_free (xmlregistry->name_template);
1109         xmlregistry->name_template = NULL;
1110         xmlregistry->caps = NULL;
1111
1112         __gst_element_factory_add_pad_template (GST_ELEMENT_FACTORY (xmlregistry->current_feature),
1113                                               template);
1114         xmlregistry->state = GST_XML_REGISTRY_FEATURE;
1115         xmlregistry->parser = gst_xml_registry_parse_element_factory;
1116       }
1117       break;
1118     case GST_XML_REGISTRY_CAPS:
1119       if (!strcmp (element_name, "caps")) {
1120         xmlregistry->state = GST_XML_REGISTRY_PADTEMPLATE;
1121         xmlregistry->parser = gst_xml_registry_parse_padtemplate;
1122       }
1123       break;
1124     case GST_XML_REGISTRY_CAPSCOMP:
1125       if (!strcmp (element_name, "capscomp")) {
1126         GstCaps *caps;
1127         
1128         xmlregistry->state = GST_XML_REGISTRY_CAPS;
1129         xmlregistry->parser = gst_xml_registry_parse_padtemplate;
1130
1131         caps = gst_caps_new (xmlregistry->caps_name, xmlregistry->caps_mime, xmlregistry->props);
1132         g_free (xmlregistry->caps_mime);
1133         g_free (xmlregistry->caps_name);
1134
1135         xmlregistry->caps = gst_caps_append (xmlregistry->caps, caps);
1136         xmlregistry->props = NULL;
1137       }
1138       break;
1139     case GST_XML_REGISTRY_PROPERTIES:
1140       if (!strncmp (element_name, "list", 4)) {
1141         GstPropsEntry *entry;
1142         
1143         xmlregistry->entry_list = g_list_reverse (xmlregistry->entry_list);
1144         
1145         entry = gst_props_entry_new (xmlregistry->list_name, 
1146                                      GST_PROPS_GLIST (xmlregistry->entry_list));
1147
1148         gst_props_add_entry (xmlregistry->props, entry);
1149         g_list_free (xmlregistry->entry_list);
1150         g_free (xmlregistry->list_name);
1151
1152         xmlregistry->entry_list = NULL;
1153         xmlregistry->list_name = NULL;
1154         xmlregistry->in_list = FALSE;
1155       }
1156       else if (!strcmp (element_name, "properties")) {
1157         xmlregistry->state = GST_XML_REGISTRY_CAPSCOMP;
1158         xmlregistry->parser = NULL;
1159       }
1160       break;
1161     default:
1162       break;
1163   }
1164 }
1165
1166 static void
1167 gst_xml_registry_text (GMarkupParseContext *context, const gchar *text,
1168                        gsize text_len, gpointer user_data, GError **error)
1169 {
1170   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1171   gchar *open_tag;
1172   
1173   if (xmlregistry->open_tags) {
1174     open_tag = (gchar *)xmlregistry->open_tags->data;
1175
1176     if (!strcmp (open_tag, "plugin-path")) {
1177       //gst_plugin_add_path (g_strndup (text, text_len));
1178     }
1179     else if (xmlregistry->parser) {
1180       xmlregistry->parser (context, open_tag, text, text_len, xmlregistry, error);
1181     }
1182   }
1183 }
1184
1185 static void
1186 gst_xml_registry_passthrough (GMarkupParseContext *context, const gchar *passthrough_text,
1187                               gsize text_len, gpointer user_data, GError **error)
1188 {
1189 }
1190
1191 static void
1192 gst_xml_registry_error (GMarkupParseContext *context, GError *error,
1193                         gpointer user_data)
1194 {
1195   GST_ERROR ("%s\n", error->message);
1196 }
1197
1198 static void
1199 gst_xml_registry_paths_start_element (GMarkupParseContext *context, 
1200                                       const gchar *element_name,
1201                                       const gchar **attribute_names, 
1202                                       const gchar **attribute_values,
1203                                       gpointer user_data, GError **error)
1204 {
1205   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1206
1207   switch (xmlregistry->state) {
1208     case GST_XML_REGISTRY_NONE:
1209       if (!strcmp (element_name, "GST-PluginRegistry")) {
1210         xmlregistry->state = GST_XML_REGISTRY_TOP;
1211       }
1212       break;
1213     case GST_XML_REGISTRY_TOP:
1214       if (!strcmp (element_name, "gst-registry-paths")) {
1215         xmlregistry->state = GST_XML_REGISTRY_PATHS;
1216       }
1217       break;
1218     case GST_XML_REGISTRY_PATHS:
1219       if (!strcmp (element_name, "path")) {
1220         xmlregistry->state = GST_XML_REGISTRY_PATH;
1221       }
1222       break;
1223     default:
1224       break;
1225   }
1226 }
1227
1228 static void
1229 gst_xml_registry_paths_end_element (GMarkupParseContext *context, 
1230                                     const gchar *element_name,
1231                                     gpointer user_data, GError **error)
1232 {
1233   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1234
1235   switch (xmlregistry->state) {
1236     case GST_XML_REGISTRY_PATH:
1237       if (!strcmp (element_name, "path")) {
1238         xmlregistry->state = GST_XML_REGISTRY_PATHS;
1239       }
1240       break;
1241     case GST_XML_REGISTRY_PATHS:
1242       if (!strcmp (element_name, "gst-plugin-paths")) {
1243         xmlregistry->state = GST_XML_REGISTRY_PATHS_DONE;
1244       }
1245       break;
1246     default:
1247       break;
1248   }
1249 }
1250
1251 static void
1252 gst_xml_registry_paths_text (GMarkupParseContext *context, const gchar *text,
1253                              gsize text_len, gpointer user_data, GError **error)
1254 {
1255   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1256   
1257   if (xmlregistry->state == GST_XML_REGISTRY_PATH)
1258     gst_registry_add_path (GST_REGISTRY (xmlregistry), g_strndup (text, text_len));
1259 }
1260
1261 /*
1262  * Save
1263  */
1264 #define PUT_ESCAPED(tag,value)                                  \
1265 G_STMT_START{                                                   \
1266   const gchar *toconv = value;                                  \
1267   if (toconv) {                                                 \
1268     gchar *v = g_markup_escape_text (toconv, strlen (toconv));  \
1269     CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, v, tag);                 \
1270     g_free (v);                                                 \
1271   }                                                             \
1272 }G_STMT_END
1273 #define PUT_ESCAPED_INT(tag,value)                              \
1274 G_STMT_START{                                                   \
1275   gchar *save = g_strdup_printf ("%ld", (glong) value);         \
1276   CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, save, tag);                \
1277   g_free (save);                                                \
1278 }G_STMT_END
1279
1280
1281 static gboolean
1282 gst_xml_registry_save_props_func (GstPropsEntry *entry, 
1283                                   GstXMLRegistry *xmlregistry)
1284 {
1285   const gchar *name;
1286
1287   name = gst_props_entry_get_name (entry);
1288
1289   switch (gst_props_entry_get_props_type (entry)) {
1290     case GST_PROPS_INT_TYPE:
1291     {
1292       gint value;
1293       gst_props_entry_get_int (entry, &value);
1294       CLASS (xmlregistry)->save_func (xmlregistry, "<int name=\"%s\" value=\"%d\"/>\n", name, value);
1295       break;
1296     }
1297     case GST_PROPS_INT_RANGE_TYPE:
1298     {
1299       gint min, max;
1300       gst_props_entry_get_int_range (entry, &min, &max);
1301       CLASS (xmlregistry)->save_func (xmlregistry, "<range name=\"%s\" min=\"%d\" max=\"%d\"/>\n", name, min, max);
1302       break;
1303     }
1304     case GST_PROPS_FLOAT_TYPE:
1305     {
1306       gfloat value;
1307       gst_props_entry_get_float (entry, &value);
1308       CLASS (xmlregistry)->save_func (xmlregistry, "<float name=\"%s\" value=\"%f\"/>\n", name, value);
1309       break;
1310     }
1311     case GST_PROPS_FLOAT_RANGE_TYPE:
1312     {
1313       gfloat min, max;
1314       gst_props_entry_get_float_range (entry, &min, &max);
1315       CLASS (xmlregistry)->save_func (xmlregistry, "<floatrange name=\"%s\" min=\"%f\" max=\"%f\"/>\n", name, min, max);
1316       break;
1317     }
1318     case GST_PROPS_FOURCC_TYPE:
1319     {
1320       guint32 fourcc;
1321       gst_props_entry_get_fourcc_int (entry, &fourcc);
1322       CLASS (xmlregistry)->save_func (xmlregistry, "<!-- "GST_FOURCC_FORMAT" -->\n", 
1323                                       GST_FOURCC_ARGS (fourcc));
1324       CLASS (xmlregistry)->save_func (xmlregistry, "<fourcc name=\"%s\" hexvalue=\"%08x\"/>\n", name, fourcc);
1325       break;
1326     }
1327     case GST_PROPS_BOOLEAN_TYPE:
1328     {
1329       gboolean value;
1330       gst_props_entry_get_boolean (entry, &value);
1331       CLASS (xmlregistry)->save_func (xmlregistry, "<boolean name=\"%s\" value=\"%s\"/>\n", name, (value ? "true" : "false"));
1332       break;
1333     }
1334     case GST_PROPS_STRING_TYPE:
1335     {
1336       const gchar *value;
1337       gst_props_entry_get_string (entry, &value);
1338       CLASS (xmlregistry)->save_func (xmlregistry, "<string name=\"%s\" value=\"%s\"/>\n", name, value);
1339       break;
1340     }
1341     default:
1342       g_warning ("trying to save unknown property type %d", gst_props_entry_get_props_type (entry));
1343       return FALSE;
1344   }
1345   return TRUE;
1346 }
1347
1348 static gboolean
1349 gst_xml_registry_save_props (GstXMLRegistry *xmlregistry, GstProps *props)
1350 {
1351   GList *proplist;
1352
1353   proplist = props->properties;
1354
1355   while (proplist) {
1356     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
1357
1358     switch (gst_props_entry_get_props_type (entry)) {
1359       case GST_PROPS_LIST_TYPE: 
1360       {
1361         const GList *list;
1362
1363         gst_props_entry_get_list (entry, &list);
1364         
1365         CLASS (xmlregistry)->save_func (xmlregistry, "<list name=\"%s\">\n", gst_props_entry_get_name (entry));
1366         g_list_foreach ((GList *)list, (GFunc) gst_xml_registry_save_props_func, xmlregistry);
1367         CLASS (xmlregistry)->save_func (xmlregistry, "</list>\n");
1368         break;
1369       }
1370       default:
1371         gst_xml_registry_save_props_func (entry, xmlregistry);
1372         break;
1373     }
1374     proplist = g_list_next (proplist);
1375   }
1376   return TRUE;
1377 }
1378
1379 static gboolean
1380 gst_xml_registry_save_caps (GstXMLRegistry *xmlregistry, GstCaps *caps)
1381 {
1382   while (caps) {
1383     CLASS (xmlregistry)->save_func (xmlregistry, "<capscomp>\n");
1384     PUT_ESCAPED ("name", caps->name);
1385     PUT_ESCAPED ("type", gst_caps_get_mime (caps));
1386
1387     if (caps->properties) {
1388       CLASS (xmlregistry)->save_func (xmlregistry, "<properties>\n");
1389       gst_xml_registry_save_props (xmlregistry, caps->properties);
1390       CLASS (xmlregistry)->save_func (xmlregistry, "</properties>\n");
1391     }
1392     CLASS (xmlregistry)->save_func (xmlregistry, "</capscomp>\n");
1393     caps = caps->next;
1394   }
1395   return TRUE;
1396 }
1397
1398 static gboolean
1399 gst_xml_registry_save_pad_template (GstXMLRegistry *xmlregistry, GstPadTemplate *template)
1400 {
1401   gchar *presence;
1402   
1403   PUT_ESCAPED ("nametemplate", template->name_template);
1404   CLASS (xmlregistry)->save_func (xmlregistry, "<direction>%s</direction>\n", (template->direction == GST_PAD_SINK? "sink":"src"));
1405
1406   switch (template->presence) {
1407     case GST_PAD_ALWAYS:
1408       presence = "always";
1409       break;
1410     case GST_PAD_SOMETIMES:
1411       presence = "sometimes";
1412       break;
1413     case GST_PAD_REQUEST:
1414       presence = "request";
1415       break;
1416     default:
1417       presence = "unknown";
1418       break;
1419   }
1420   CLASS (xmlregistry)->save_func (xmlregistry, "<presence>%s</presence>\n", presence);
1421
1422   if (GST_PAD_TEMPLATE_CAPS (template)) {
1423     CLASS (xmlregistry)->save_func (xmlregistry, "<caps>\n");
1424     gst_xml_registry_save_caps (xmlregistry, GST_PAD_TEMPLATE_CAPS (template));
1425     CLASS (xmlregistry)->save_func (xmlregistry, "</caps>\n");
1426   }
1427   return TRUE;
1428 }
1429
1430 static gboolean
1431 gst_xml_registry_save_feature (GstXMLRegistry *xmlregistry, GstPluginFeature *feature)
1432 {
1433   PUT_ESCAPED ("name", feature->name);
1434
1435   if (feature->rank > 0) {
1436     gint rank = feature->rank;
1437     CLASS (xmlregistry)->save_func (xmlregistry, "<rank>%d</rank>\n", rank);
1438   }     
1439     
1440   if (GST_IS_ELEMENT_FACTORY (feature)) {
1441     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
1442     GList *walk;
1443
1444     PUT_ESCAPED ("longname", factory->details.longname);
1445     PUT_ESCAPED ("class", factory->details.klass);
1446     PUT_ESCAPED ("description", factory->details.description);
1447     PUT_ESCAPED ("author", factory->details.author);
1448     
1449     walk = factory->padtemplates;
1450
1451     while (walk) {
1452       GstPadTemplate *template = GST_PAD_TEMPLATE (walk->data);
1453
1454       CLASS (xmlregistry)->save_func (xmlregistry, "<padtemplate>\n");
1455       gst_xml_registry_save_pad_template (xmlregistry, template);
1456       CLASS (xmlregistry)->save_func (xmlregistry, "</padtemplate>\n");
1457       
1458       walk = g_list_next (walk);
1459     }
1460
1461     walk = factory->interfaces;
1462     while (walk) {
1463       PUT_ESCAPED ("interface", (gchar *) walk->data);
1464       walk = g_list_next (walk);
1465     }
1466
1467     if (GST_URI_TYPE_IS_VALID (factory->uri_type)) {
1468       gchar **protocol;
1469       
1470       PUT_ESCAPED ("uri_type", factory->uri_type == GST_URI_SINK ? "sink" : "source");
1471       g_assert (factory->uri_protocols);
1472       protocol = factory->uri_protocols;
1473       while (*protocol) {
1474         PUT_ESCAPED ("uri_protocol", *protocol);
1475         protocol++;
1476       }
1477     }
1478   }
1479   else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
1480     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
1481     gint i = 0;
1482     /* FIXME 
1483     if (factory->caps) {
1484       CLASS (xmlregistry)->save_func (xmlregistry, "<caps>\n");
1485       gst_xml_registry_save_caps (xmlregistry, factory->caps);
1486       CLASS (xmlregistry)->save_func (xmlregistry, "</caps>\n");
1487     } */
1488     if (factory->extensions) {
1489       while (factory->extensions[i]) {
1490         PUT_ESCAPED ("extension", factory->extensions[i]);
1491         i++;
1492       }
1493     }
1494   }
1495   else if (GST_IS_SCHEDULER_FACTORY (feature)) {
1496     PUT_ESCAPED ("longdesc", GST_SCHEDULER_FACTORY (feature)->longdesc);
1497   }
1498   else if (GST_IS_INDEX_FACTORY (feature)) {
1499     PUT_ESCAPED ("longdesc", GST_INDEX_FACTORY (feature)->longdesc);
1500   }
1501   return TRUE;
1502 }
1503
1504 static gboolean
1505 gst_xml_registry_save_plugin (GstXMLRegistry *xmlregistry, GstPlugin *plugin)
1506 {
1507   GList *walk;
1508   
1509   PUT_ESCAPED ("name", plugin->desc.name);
1510   PUT_ESCAPED ("description", plugin->desc.description);
1511   PUT_ESCAPED ("filename", plugin->filename);
1512   PUT_ESCAPED ("version", plugin->desc.version);
1513   PUT_ESCAPED ("license", plugin->desc.license);
1514   PUT_ESCAPED ("copyright", plugin->desc.copyright);
1515   PUT_ESCAPED ("package", plugin->desc.package);
1516   PUT_ESCAPED ("origin", plugin->desc.origin);
1517
1518   walk = plugin->features;
1519
1520   while (walk) {
1521     GstPluginFeature *feature = GST_PLUGIN_FEATURE (walk->data);
1522
1523     CLASS (xmlregistry)->save_func (xmlregistry, "<feature typename=\"%s\">\n", g_type_name (G_OBJECT_TYPE (feature)));
1524     gst_xml_registry_save_feature (xmlregistry, feature);
1525     CLASS (xmlregistry)->save_func (xmlregistry, "</feature>\n");
1526     
1527     walk = g_list_next (walk);
1528   }
1529   return TRUE;
1530 }
1531
1532
1533 static gboolean
1534 gst_xml_registry_save (GstRegistry *registry)
1535 {
1536   GList *walk;
1537   GstXMLRegistry *xmlregistry;
1538   
1539   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
1540   g_return_val_if_fail (registry->flags & GST_REGISTRY_WRITABLE, FALSE);
1541   
1542   xmlregistry = GST_XML_REGISTRY (registry);
1543
1544   if (!CLASS (xmlregistry)->open_func (xmlregistry, GST_XML_REGISTRY_WRITE)) {
1545     return FALSE;
1546   }
1547
1548   CLASS (xmlregistry)->save_func (xmlregistry, "<?xml version=\"1.0\"?>\n");
1549   CLASS (xmlregistry)->save_func (xmlregistry, "<GST-PluginRegistry>\n");
1550   
1551   walk = g_list_last (gst_registry_get_path_list (GST_REGISTRY (registry)));
1552   
1553   CLASS (xmlregistry)->save_func (xmlregistry, "<gst-plugin-paths>\n");
1554   while (walk) {
1555     CLASS (xmlregistry)->save_func (xmlregistry, "<path>");
1556     CLASS (xmlregistry)->save_func (xmlregistry, (gchar*)walk->data);
1557     CLASS (xmlregistry)->save_func (xmlregistry, "</path>\n");
1558     walk = g_list_previous (walk);
1559   }
1560   CLASS (xmlregistry)->save_func (xmlregistry, "</gst-plugin-paths>\n");
1561
1562   walk = g_list_last (registry->plugins);
1563
1564   while (walk) {
1565     GstPlugin *plugin = GST_PLUGIN (walk->data);
1566
1567     CLASS (xmlregistry)->save_func (xmlregistry, "<plugin>\n");
1568     gst_xml_registry_save_plugin (xmlregistry, plugin);
1569     CLASS (xmlregistry)->save_func (xmlregistry, "</plugin>\n");
1570     
1571     walk = g_list_previous (walk);
1572   }
1573   CLASS (xmlregistry)->save_func (xmlregistry, "</GST-PluginRegistry>\n");
1574
1575   CLASS (xmlregistry)->close_func (xmlregistry);
1576
1577   return TRUE;
1578 }
1579
1580 static GList*
1581 gst_xml_registry_rebuild_recurse (GstXMLRegistry *registry, 
1582                                   const gchar *directory)
1583 {
1584   GDir *dir;
1585   gchar *temp;
1586   GList *ret = NULL;
1587
1588   dir = g_dir_open (directory, 0, NULL);
1589
1590   if (dir) {
1591     const gchar *dirent;
1592
1593     while ((dirent = g_dir_read_name (dir))) {
1594       gchar *dirname;
1595         
1596       if (*dirent == '=') {
1597         /* =build, =inst, etc. -- automake distcheck directories */
1598         continue;
1599       }
1600       
1601       dirname = g_strjoin ("/", directory, dirent, NULL);
1602       ret = g_list_concat (ret, gst_xml_registry_rebuild_recurse (registry, dirname));
1603       g_free(dirname);
1604     }
1605     g_dir_close (dir);
1606   } else {
1607     if ((temp = strstr (directory, G_MODULE_SUFFIX)) &&
1608         (!strcmp (temp, G_MODULE_SUFFIX))) {
1609       ret = g_list_prepend (ret, g_strdup (directory));
1610     }
1611   }
1612
1613   return ret;
1614 }
1615
1616 static gboolean
1617 gst_xml_registry_rebuild (GstRegistry *registry)
1618 {
1619   GList *walk = NULL, *plugins = NULL, *prune = NULL;
1620   GError *error = NULL;
1621   guint length;
1622   GstPlugin *plugin;
1623   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (registry);
1624  
1625   walk = registry->paths;
1626
1627   while (walk) {
1628     gchar *path = (gchar *) walk->data;
1629     
1630     GST_CAT_INFO (GST_CAT_PLUGIN_LOADING, 
1631               "Rebuilding registry %p in directory %s...", registry, path);
1632
1633     plugins = g_list_concat (plugins, 
1634                              gst_xml_registry_rebuild_recurse (xmlregistry, 
1635                                                                path));
1636
1637     walk = g_list_next (walk);
1638   }
1639   
1640   plugins = g_list_reverse (plugins);
1641
1642   do {
1643     length = g_list_length (plugins);
1644
1645     walk = plugins;
1646     while (walk) {
1647       g_assert (walk->data);
1648       plugin = gst_plugin_load_file ((gchar *) walk->data, NULL);
1649       if (plugin) {
1650         prune = g_list_prepend (prune, walk->data);
1651         gst_registry_add_plugin (registry, plugin);
1652       }
1653
1654       walk = g_list_next (walk);
1655     }
1656
1657     walk = prune;
1658     while (walk) {
1659       plugins = g_list_remove (plugins, walk->data);
1660       g_free (walk->data);
1661       walk = g_list_next (walk);
1662     }
1663     g_list_free (prune);
1664     prune = NULL;
1665   } while (g_list_length (plugins) != length);
1666   
1667   walk = plugins;
1668   while (walk) {
1669     if ((plugin = gst_plugin_load_file ((gchar *) walk->data, &error))) {
1670       g_warning ("Bizarre behavior: plugin %s actually loaded", 
1671                  (gchar *) walk->data);
1672       gst_registry_add_plugin (registry, plugin);
1673     } else {
1674       GST_CAT_INFO (GST_CAT_PLUGIN_LOADING, "Plugin %s failed to load: %s", 
1675                 (gchar *) walk->data, error->message);
1676
1677       g_free (walk->data);
1678       g_error_free (error);
1679       error = NULL;
1680     }
1681         
1682     walk = g_list_next (walk);
1683   }
1684   return TRUE;
1685 }