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