dcf10b3d2ad4a8e4dce28376a276855d3345b58b
[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/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       GST_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       GST_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   GstPlugin *loaded_plugin;
634
635   /* FIXME: add gerror support */
636   loaded_plugin = gst_plugin_load_file (plugin->filename, &error);
637   if (!plugin) {
638     if (error) {
639       g_warning ("could not load plugin %s: %s", plugin->desc.name, error->message);
640     }
641     return GST_REGISTRY_PLUGIN_LOAD_ERROR;
642   } else if (loaded_plugin != plugin) {
643     g_critical ("how to remove plugins?");
644   }
645
646   return GST_REGISTRY_OK;
647 }
648
649 static gboolean
650 gst_xml_registry_parse_plugin (GMarkupParseContext *context, const gchar *tag, const gchar *text,
651                                gsize text_len, GstXMLRegistry *registry, GError **error)
652 {
653   GstPlugin *plugin = registry->current_plugin;
654   
655   if (!strcmp (tag, "name")) {
656     plugin->desc.name = g_strndup (text, text_len);
657   }
658   else if (!strcmp (tag, "description")) {
659     plugin->desc.description = g_strndup (text, text_len);
660   }
661   else if (!strcmp (tag, "filename")) {
662     plugin->filename = g_strndup (text, text_len);
663   }
664   else if (!strcmp (tag, "version")) {
665     plugin->desc.version = g_strndup (text, text_len);
666   }
667   else if (!strcmp (tag, "copyright")) {
668     plugin->desc.copyright = g_strndup (text, text_len);
669   }
670   else if (!strcmp (tag, "license")) {
671     plugin->desc.license = g_strndup (text, text_len);
672   }
673   else if (!strcmp (tag, "package")) {
674     plugin->desc.package = g_strndup (text, text_len);
675   }
676   else if (!strcmp (tag, "origin")) {
677     plugin->desc.origin = g_strndup (text, text_len);
678   }
679   
680   return TRUE;
681 }
682
683 static void
684 add_to_char_array (gchar ***array, gchar *value)
685 {
686   gchar **new;
687   gchar **old = *array;
688   gint i = 0;
689     
690   /* expensive, but cycles are cheap... */
691   if (old)
692     while (old[i]) i++;
693   new = g_new0 (gchar *, i + 2);
694   new[i] = value;
695   while (i > 0) {
696     i--;
697     new[i] = old[i];
698   }
699   g_free (old);
700   *array = new;
701 }
702
703 static gboolean
704 gst_xml_registry_parse_element_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
705                                         gsize text_len, GstXMLRegistry *registry, GError **error)
706 {
707   GstElementFactory *factory = GST_ELEMENT_FACTORY (registry->current_feature);
708
709   if (!strcmp (tag, "name")) {
710     gchar *name = g_strndup (text, text_len);
711     gst_plugin_feature_set_name (registry->current_feature, name);
712     g_free (name);
713   }
714   else if (!strcmp (tag, "longname")) {
715     g_free (factory->details.longname);
716     factory->details.longname = g_strndup (text, text_len);
717   }
718   else if (!strcmp(tag, "class")) {
719     g_free (factory->details.klass);
720     factory->details.klass = g_strndup (text, text_len);
721   }
722   else if (!strcmp(tag, "description")) {
723     g_free (factory->details.description);
724     factory->details.description = g_strndup (text, text_len);
725   }
726   else if (!strcmp(tag, "author")) {
727     g_free (factory->details.author);
728     factory->details.author = g_strndup (text, text_len);
729   }
730   else if (!strcmp(tag, "rank")) {
731     gint rank;
732     gchar *ret;
733     
734     rank = strtol (text, &ret, 0);
735     if (ret == text + text_len) {
736      gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
737     }
738   } else if (!strcmp (tag, "uri_type")) {
739     if (strncasecmp (text, "sink", 4) == 0) {
740       factory->uri_type = GST_URI_SINK;
741     } else if (strncasecmp (text, "source", 5) == 0) {
742       factory->uri_type = GST_URI_SRC;
743     }
744   } else if (!strcmp (tag, "uri_protocol")) {
745     add_to_char_array (&factory->uri_protocols, g_strndup (text, text_len));
746   }
747   else if (!strcmp(tag, "interface")) {
748     gchar *tmp = g_strndup (text, text_len);
749     __gst_element_factory_add_interface (factory, tmp);
750     g_free (tmp);
751   }
752
753   return TRUE;
754 }
755
756 static gboolean
757 gst_xml_registry_parse_type_find_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
758                                           gsize text_len, GstXMLRegistry *registry, GError **error)
759 {
760   GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (registry->current_feature);
761
762   if (!strcmp (tag, "name")) {
763     registry->current_feature->name = g_strndup (text, text_len);
764   }
765   else if (!strcmp(tag, "rank")) {
766     glong rank;
767     gchar *ret;
768      rank = strtol (text, &ret, 0);
769     if (ret == text + text_len) {
770      gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), rank);
771     }
772   }
773   /* FIXME!!
774   else if (!strcmp (tag, "caps")) {
775     factory->caps = g_strndup (text, text_len);
776   }*/
777   else if (!strcmp(tag, "extension")) {
778     add_to_char_array (&factory->extensions, g_strndup (text, text_len));
779   }
780
781   return TRUE;
782 }
783
784 static gboolean
785 gst_xml_registry_parse_scheduler_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
786                                           gsize text_len, GstXMLRegistry *registry, GError **error)
787 {
788   GstSchedulerFactory *factory = GST_SCHEDULER_FACTORY (registry->current_feature);
789
790   if (!strcmp (tag, "name")) {
791     registry->current_feature->name = g_strndup (text, text_len);
792   }
793   else if (!strcmp (tag, "longdesc")) {
794     factory->longdesc = g_strndup (text, text_len);
795   }
796   return TRUE;
797 }
798
799 static gboolean
800 gst_xml_registry_parse_autoplug_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
801                                          gsize text_len, GstXMLRegistry *registry, GError **error)
802 {
803   GstAutoplugFactory *factory = GST_AUTOPLUG_FACTORY (registry->current_feature);
804
805   if (!strcmp (tag, "name")) {
806     registry->current_feature->name = g_strndup (text, text_len);
807   }
808   else if (!strcmp (tag, "longdesc")) {
809     factory->longdesc = g_strndup (text, text_len);
810   }
811   return TRUE;
812 }
813
814 static gboolean
815 gst_xml_registry_parse_index_factory (GMarkupParseContext *context, const gchar *tag, const gchar *text,
816                                       gsize text_len, GstXMLRegistry *registry, GError **error)
817 {
818   GstIndexFactory *factory = GST_INDEX_FACTORY (registry->current_feature);
819
820   if (!strcmp (tag, "name")) {
821     registry->current_feature->name = g_strndup (text, text_len);
822   }
823   else if (!strcmp (tag, "longdesc")) {
824     factory->longdesc = g_strndup (text, text_len);
825   }
826   return TRUE;
827 }
828
829 static gboolean
830 gst_xml_registry_parse_padtemplate (GMarkupParseContext *context, const gchar *tag, const gchar *text,
831                                     gsize text_len, GstXMLRegistry *registry, GError **error)
832 {
833   if (!strcmp (tag, "nametemplate")) {
834     registry->name_template = g_strndup (text, text_len);
835   }
836   else if (!strcmp (tag, "direction")) {
837     if (!strncmp(text, "sink", text_len)) {
838       registry->direction = GST_PAD_SINK;
839     }
840     else if (!strncmp(text, "src", text_len)) {
841       registry->direction = GST_PAD_SRC;
842     }
843   }
844   else if (!strcmp (tag, "presence")) {
845     if (!strncmp(text, "always", text_len)) {
846       registry->presence = GST_PAD_ALWAYS;
847     }
848     else if (!strncmp(text, "sometimes", text_len)) {
849       registry->presence = GST_PAD_SOMETIMES;
850     }
851     else if (!strncmp(text, "request", text_len)) {
852       registry->presence = GST_PAD_REQUEST;
853     }
854   }
855   return TRUE;
856 }
857
858 static gboolean
859 gst_xml_registry_parse_capscomp (GMarkupParseContext *context, const gchar *tag, const gchar *text,
860                                  gsize text_len, GstXMLRegistry *registry, GError **error)
861 {
862   if (!strcmp (tag, "name")) {
863     registry->caps_name = g_strndup (text, text_len);
864   }
865   else if (!strcmp (tag, "type")) {
866     registry->caps_mime = g_strndup (text, text_len);
867   }
868   return TRUE;
869 }
870
871 static gint
872 find_index_for (const gchar *name, const gchar **attribute_names)
873 {
874   gint i=0;
875   
876   while (attribute_names[i]) {
877     if (!strcmp (attribute_names[i], name))
878       return i;
879     i++;
880   }
881   return -1;
882 }
883
884 static void
885 gst_xml_registry_start_element (GMarkupParseContext *context, 
886                                 const gchar *element_name,
887                                 const gchar **attribute_names, 
888                                 const gchar **attribute_values,
889                                 gpointer user_data, GError **error)
890 {
891   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
892
893   xmlregistry->open_tags = g_list_prepend (xmlregistry->open_tags, 
894                                            g_strdup (element_name));
895
896   switch (xmlregistry->state) {
897     case GST_XML_REGISTRY_NONE:
898       if (!strcmp (element_name, "GST-PluginRegistry")) {
899         xmlregistry->state = GST_XML_REGISTRY_TOP;
900       }
901       break;
902     case GST_XML_REGISTRY_TOP:
903       if (!strncmp (element_name, "plugin", 6)) {
904         xmlregistry->state = GST_XML_REGISTRY_PLUGIN;
905         xmlregistry->parser = gst_xml_registry_parse_plugin;
906         xmlregistry->current_plugin = (GstPlugin *) g_new0 (GstPlugin, 1);
907       }
908       break;
909     case GST_XML_REGISTRY_PLUGIN:
910       if (!strncmp (element_name, "feature", 7)) {
911         gint i = 0;
912         GstPluginFeature *feature = NULL;
913         
914         xmlregistry->state = GST_XML_REGISTRY_FEATURE;
915
916         while (attribute_names[i]) {
917           if (!strncmp (attribute_names[i], "typename", 8)) {
918             feature = GST_PLUGIN_FEATURE (g_object_new (g_type_from_name (attribute_values[i]), NULL));
919             break;
920           }
921           i++;
922         }
923         if (feature) {
924           xmlregistry->current_feature = feature;
925
926           if (GST_IS_ELEMENT_FACTORY (feature)) {
927             GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
928
929             factory->padtemplates = NULL;
930             xmlregistry->parser = gst_xml_registry_parse_element_factory;
931             break;
932           }
933           else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
934             xmlregistry->parser = gst_xml_registry_parse_type_find_factory;
935           }
936           else if (GST_IS_SCHEDULER_FACTORY (feature)) {
937             xmlregistry->parser = gst_xml_registry_parse_scheduler_factory;
938             GST_SCHEDULER_FACTORY (feature)->type = 0;
939           }
940           else if (GST_IS_AUTOPLUG_FACTORY (feature)) {
941             xmlregistry->parser = gst_xml_registry_parse_autoplug_factory;
942           }
943           else if (GST_IS_INDEX_FACTORY (feature)) {
944             xmlregistry->parser = gst_xml_registry_parse_index_factory;
945           }
946           else {
947             g_warning ("unkown feature type");
948           }
949         }
950       }
951       break;
952     case GST_XML_REGISTRY_FEATURE:
953       if (!strncmp (element_name, "padtemplate", 11)) {
954         xmlregistry->state = GST_XML_REGISTRY_PADTEMPLATE;
955         xmlregistry->parser = gst_xml_registry_parse_padtemplate;
956         xmlregistry->name_template = NULL;
957         xmlregistry->direction = 0;
958         xmlregistry->presence = 0;
959         xmlregistry->caps = NULL;
960       }
961       break;
962     case GST_XML_REGISTRY_PADTEMPLATE:
963       if (!strncmp (element_name, "caps", 4)) {
964         xmlregistry->state = GST_XML_REGISTRY_CAPS;
965         xmlregistry->parser = NULL;
966       }
967       break;
968     case GST_XML_REGISTRY_CAPS:
969       if (!strncmp (element_name, "capscomp", 8)) {
970         xmlregistry->state = GST_XML_REGISTRY_CAPSCOMP;
971         xmlregistry->parser = gst_xml_registry_parse_capscomp;
972       }
973       break;
974     case GST_XML_REGISTRY_CAPSCOMP:
975       if (!strncmp (element_name, "properties", 10)) {
976         xmlregistry->state = GST_XML_REGISTRY_PROPERTIES;
977         xmlregistry->parser = NULL;
978         xmlregistry->props = gst_props_empty_new ();
979       }
980       break;
981     case GST_XML_REGISTRY_PROPERTIES:
982     {
983       gint name_index;
984       GstPropsEntry *entry = NULL;
985
986       name_index = find_index_for ("name", attribute_names);
987       if (name_index < 0)
988         break;
989
990       if (!strncmp (element_name, "list", 4)) {
991         xmlregistry->in_list = TRUE;
992         xmlregistry->list_name = g_strdup (attribute_values[name_index]);
993       }
994
995       if (!strncmp (element_name, "int", 3)) {
996         gint value;
997         gint index;
998
999         if ((index = find_index_for ("value", attribute_names)) < 0) 
1000           break;
1001         sscanf (attribute_values[index], "%d", &value);
1002
1003         entry = gst_props_entry_new (attribute_values[name_index], 
1004                                      GST_PROPS_INT (value));
1005       }
1006       else if (!strncmp (element_name, "range", 5)) {
1007         gint min, max;
1008         gint min_idx, max_idx;
1009
1010         if ((min_idx = find_index_for ("min", attribute_names)) < 0) 
1011           break;
1012         if ((max_idx = find_index_for ("max", attribute_names)) < 0) 
1013           break;
1014         sscanf (attribute_values[min_idx], "%d", &min);
1015         sscanf (attribute_values[max_idx], "%d", &max);
1016
1017         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_INT_RANGE (min, max));
1018       }
1019       else if (!strncmp (element_name, "float", 5)) {
1020         gfloat value;
1021         gint index;
1022
1023         if ((index = find_index_for ("value", attribute_names)) < 0) 
1024           break;
1025         sscanf (attribute_values[index], "%f", &value);
1026         
1027         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_FLOAT (value));
1028       }
1029       else if (!strncmp (element_name, "floatrange", 10)) {
1030         gfloat min, max;
1031         gint min_idx, max_idx;
1032
1033         if ((min_idx = find_index_for ("min", attribute_names)) < 0) 
1034           break;
1035         if ((max_idx = find_index_for ("max", attribute_names)) < 0) 
1036           break;
1037         sscanf (attribute_values[min_idx], "%f", &min);
1038         sscanf (attribute_values[max_idx], "%f", &max);
1039
1040         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_FLOAT_RANGE (min, max));
1041       }
1042       else if (!strncmp (element_name, "boolean", 7)) {
1043         gboolean value = TRUE;
1044         gint index;
1045
1046         if ((index = find_index_for ("value", attribute_names)) < 0) 
1047           break;
1048         if (!strcmp (attribute_values[index], "false")) 
1049           value = FALSE;
1050
1051         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_BOOLEAN (value));
1052       }
1053       else if (!strncmp (element_name, "fourcc", 6)) {
1054         guint32 value;
1055         gint index;
1056
1057         if ((index = find_index_for ("hexvalue", attribute_names)) < 0) 
1058           break;
1059         sscanf (attribute_values[index], "%08x", &value);
1060
1061         entry = gst_props_entry_new (attribute_values[name_index], GST_PROPS_FOURCC (value));
1062       }
1063       else if (!strncmp (element_name, "string", 6)) {
1064         gint index;
1065
1066         if ((index = find_index_for ("value", attribute_names)) < 0) 
1067           break;
1068
1069         entry = gst_props_entry_new (attribute_values[name_index], 
1070                                      GST_PROPS_STRING (attribute_values[index]));
1071       }
1072       /* add property to list or parent */
1073       if (entry) {
1074         if (xmlregistry->in_list)
1075           xmlregistry->entry_list = g_list_prepend (xmlregistry->entry_list, entry);
1076         else
1077           gst_props_add_entry (xmlregistry->props, entry);
1078       }
1079       break;
1080     }
1081     default:
1082       break;
1083   }
1084 }
1085
1086 static void
1087 gst_xml_registry_end_element (GMarkupParseContext *context, 
1088                               const gchar *element_name,
1089                               gpointer user_data, GError **error)
1090 {
1091   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1092   gchar *open_tag = (gchar *)xmlregistry->open_tags->data;
1093
1094   xmlregistry->open_tags = g_list_remove (xmlregistry->open_tags, open_tag);
1095   g_free (open_tag);
1096
1097   switch (xmlregistry->state) {
1098     case GST_XML_REGISTRY_TOP:
1099       if (!strcmp (element_name, "GST-PluginRegistry")) {
1100         xmlregistry->state = GST_XML_REGISTRY_NONE;
1101       }
1102       break;
1103     case GST_XML_REGISTRY_PLUGIN:
1104       if (!strcmp (element_name, "plugin")) {
1105         xmlregistry->state = GST_XML_REGISTRY_TOP;
1106         xmlregistry->parser = NULL;
1107         gst_registry_add_plugin (GST_REGISTRY (xmlregistry), xmlregistry->current_plugin);
1108       }
1109       break;
1110     case GST_XML_REGISTRY_FEATURE:
1111       if (!strcmp (element_name, "feature")) {
1112         xmlregistry->state = GST_XML_REGISTRY_PLUGIN;
1113         xmlregistry->parser = gst_xml_registry_parse_plugin;
1114         gst_plugin_add_feature (xmlregistry->current_plugin, xmlregistry->current_feature);
1115         xmlregistry->current_feature = NULL;
1116       }
1117       break;
1118     case GST_XML_REGISTRY_PADTEMPLATE:
1119       if (!strcmp (element_name, "padtemplate")) {
1120         GstPadTemplate *template;
1121
1122         template = gst_pad_template_new (xmlregistry->name_template,
1123                                          xmlregistry->direction,
1124                                          xmlregistry->presence,
1125                                          xmlregistry->caps, NULL);
1126
1127         g_free (xmlregistry->name_template);
1128         xmlregistry->name_template = NULL;
1129         xmlregistry->caps = NULL;
1130
1131         __gst_element_factory_add_pad_template (GST_ELEMENT_FACTORY (xmlregistry->current_feature),
1132                                               template);
1133         xmlregistry->state = GST_XML_REGISTRY_FEATURE;
1134         xmlregistry->parser = gst_xml_registry_parse_element_factory;
1135       }
1136       break;
1137     case GST_XML_REGISTRY_CAPS:
1138       if (!strcmp (element_name, "caps")) {
1139         xmlregistry->state = GST_XML_REGISTRY_PADTEMPLATE;
1140         xmlregistry->parser = gst_xml_registry_parse_padtemplate;
1141       }
1142       break;
1143     case GST_XML_REGISTRY_CAPSCOMP:
1144       if (!strcmp (element_name, "capscomp")) {
1145         GstCaps *caps;
1146         
1147         xmlregistry->state = GST_XML_REGISTRY_CAPS;
1148         xmlregistry->parser = gst_xml_registry_parse_padtemplate;
1149
1150         caps = gst_caps_new (xmlregistry->caps_name, xmlregistry->caps_mime, xmlregistry->props);
1151         g_free (xmlregistry->caps_mime);
1152         g_free (xmlregistry->caps_name);
1153
1154         xmlregistry->caps = gst_caps_append (xmlregistry->caps, caps);
1155         xmlregistry->props = NULL;
1156       }
1157       break;
1158     case GST_XML_REGISTRY_PROPERTIES:
1159       if (!strncmp (element_name, "list", 4)) {
1160         GstPropsEntry *entry;
1161         
1162         xmlregistry->entry_list = g_list_reverse (xmlregistry->entry_list);
1163         
1164         entry = gst_props_entry_new (xmlregistry->list_name, 
1165                                      GST_PROPS_GLIST (xmlregistry->entry_list));
1166
1167         gst_props_add_entry (xmlregistry->props, entry);
1168         g_list_free (xmlregistry->entry_list);
1169         g_free (xmlregistry->list_name);
1170
1171         xmlregistry->entry_list = NULL;
1172         xmlregistry->list_name = NULL;
1173         xmlregistry->in_list = FALSE;
1174       }
1175       else if (!strcmp (element_name, "properties")) {
1176         xmlregistry->state = GST_XML_REGISTRY_CAPSCOMP;
1177         xmlregistry->parser = NULL;
1178       }
1179       break;
1180     default:
1181       break;
1182   }
1183 }
1184
1185 static void
1186 gst_xml_registry_text (GMarkupParseContext *context, const gchar *text,
1187                        gsize text_len, gpointer user_data, GError **error)
1188 {
1189   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1190   gchar *open_tag;
1191   
1192   if (xmlregistry->open_tags) {
1193     open_tag = (gchar *)xmlregistry->open_tags->data;
1194
1195     if (!strcmp (open_tag, "plugin-path")) {
1196       //gst_plugin_add_path (g_strndup (text, text_len));
1197     }
1198     else if (xmlregistry->parser) {
1199       xmlregistry->parser (context, open_tag, text, text_len, xmlregistry, error);
1200     }
1201   }
1202 }
1203
1204 static void
1205 gst_xml_registry_passthrough (GMarkupParseContext *context, const gchar *passthrough_text,
1206                               gsize text_len, gpointer user_data, GError **error)
1207 {
1208 }
1209
1210 static void
1211 gst_xml_registry_error (GMarkupParseContext *context, GError *error,
1212                         gpointer user_data)
1213 {
1214   GST_ERROR ("%s\n", error->message);
1215 }
1216
1217 static void
1218 gst_xml_registry_paths_start_element (GMarkupParseContext *context, 
1219                                       const gchar *element_name,
1220                                       const gchar **attribute_names, 
1221                                       const gchar **attribute_values,
1222                                       gpointer user_data, GError **error)
1223 {
1224   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1225
1226   switch (xmlregistry->state) {
1227     case GST_XML_REGISTRY_NONE:
1228       if (!strcmp (element_name, "GST-PluginRegistry")) {
1229         xmlregistry->state = GST_XML_REGISTRY_TOP;
1230       }
1231       break;
1232     case GST_XML_REGISTRY_TOP:
1233       if (!strcmp (element_name, "gst-registry-paths")) {
1234         xmlregistry->state = GST_XML_REGISTRY_PATHS;
1235       }
1236       break;
1237     case GST_XML_REGISTRY_PATHS:
1238       if (!strcmp (element_name, "path")) {
1239         xmlregistry->state = GST_XML_REGISTRY_PATH;
1240       }
1241       break;
1242     default:
1243       break;
1244   }
1245 }
1246
1247 static void
1248 gst_xml_registry_paths_end_element (GMarkupParseContext *context, 
1249                                     const gchar *element_name,
1250                                     gpointer user_data, GError **error)
1251 {
1252   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1253
1254   switch (xmlregistry->state) {
1255     case GST_XML_REGISTRY_PATH:
1256       if (!strcmp (element_name, "path")) {
1257         xmlregistry->state = GST_XML_REGISTRY_PATHS;
1258       }
1259       break;
1260     case GST_XML_REGISTRY_PATHS:
1261       if (!strcmp (element_name, "gst-plugin-paths")) {
1262         xmlregistry->state = GST_XML_REGISTRY_PATHS_DONE;
1263       }
1264       break;
1265     default:
1266       break;
1267   }
1268 }
1269
1270 static void
1271 gst_xml_registry_paths_text (GMarkupParseContext *context, const gchar *text,
1272                              gsize text_len, gpointer user_data, GError **error)
1273 {
1274   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (user_data);
1275   
1276   if (xmlregistry->state == GST_XML_REGISTRY_PATH)
1277     gst_registry_add_path (GST_REGISTRY (xmlregistry), g_strndup (text, text_len));
1278 }
1279
1280 /*
1281  * Save
1282  */
1283 #define PUT_ESCAPED(tag,value)                                  \
1284 G_STMT_START{                                                   \
1285   const gchar *toconv = value;                                  \
1286   if (toconv) {                                                 \
1287     gchar *v = g_markup_escape_text (toconv, strlen (toconv));  \
1288     CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, v, tag);                 \
1289     g_free (v);                                                 \
1290   }                                                             \
1291 }G_STMT_END
1292 #define PUT_ESCAPED_INT(tag,value)                              \
1293 G_STMT_START{                                                   \
1294   gchar *save = g_strdup_printf ("%ld", (glong) value);         \
1295   CLASS (xmlregistry)->save_func (xmlregistry, "<%s>%s</%s>\n", tag, save, tag);                \
1296   g_free (save);                                                \
1297 }G_STMT_END
1298
1299
1300 static gboolean
1301 gst_xml_registry_save_props_func (GstPropsEntry *entry, 
1302                                   GstXMLRegistry *xmlregistry)
1303 {
1304   const gchar *name;
1305
1306   name = gst_props_entry_get_name (entry);
1307
1308   switch (gst_props_entry_get_props_type (entry)) {
1309     case GST_PROPS_INT_TYPE:
1310     {
1311       gint value;
1312       gst_props_entry_get_int (entry, &value);
1313       CLASS (xmlregistry)->save_func (xmlregistry, "<int name=\"%s\" value=\"%d\"/>\n", name, value);
1314       break;
1315     }
1316     case GST_PROPS_INT_RANGE_TYPE:
1317     {
1318       gint min, max;
1319       gst_props_entry_get_int_range (entry, &min, &max);
1320       CLASS (xmlregistry)->save_func (xmlregistry, "<range name=\"%s\" min=\"%d\" max=\"%d\"/>\n", name, min, max);
1321       break;
1322     }
1323     case GST_PROPS_FLOAT_TYPE:
1324     {
1325       gfloat value;
1326       gst_props_entry_get_float (entry, &value);
1327       CLASS (xmlregistry)->save_func (xmlregistry, "<float name=\"%s\" value=\"%f\"/>\n", name, value);
1328       break;
1329     }
1330     case GST_PROPS_FLOAT_RANGE_TYPE:
1331     {
1332       gfloat min, max;
1333       gst_props_entry_get_float_range (entry, &min, &max);
1334       CLASS (xmlregistry)->save_func (xmlregistry, "<floatrange name=\"%s\" min=\"%f\" max=\"%f\"/>\n", name, min, max);
1335       break;
1336     }
1337     case GST_PROPS_FOURCC_TYPE:
1338     {
1339       guint32 fourcc;
1340       gst_props_entry_get_fourcc_int (entry, &fourcc);
1341       CLASS (xmlregistry)->save_func (xmlregistry, "<!-- "GST_FOURCC_FORMAT" -->\n", 
1342                                       GST_FOURCC_ARGS (fourcc));
1343       CLASS (xmlregistry)->save_func (xmlregistry, "<fourcc name=\"%s\" hexvalue=\"%08x\"/>\n", name, fourcc);
1344       break;
1345     }
1346     case GST_PROPS_BOOLEAN_TYPE:
1347     {
1348       gboolean value;
1349       gst_props_entry_get_boolean (entry, &value);
1350       CLASS (xmlregistry)->save_func (xmlregistry, "<boolean name=\"%s\" value=\"%s\"/>\n", name, (value ? "true" : "false"));
1351       break;
1352     }
1353     case GST_PROPS_STRING_TYPE:
1354     {
1355       const gchar *value;
1356       gst_props_entry_get_string (entry, &value);
1357       CLASS (xmlregistry)->save_func (xmlregistry, "<string name=\"%s\" value=\"%s\"/>\n", name, value);
1358       break;
1359     }
1360     default:
1361       g_warning ("trying to save unknown property type %d", gst_props_entry_get_props_type (entry));
1362       return FALSE;
1363   }
1364   return TRUE;
1365 }
1366
1367 static gboolean
1368 gst_xml_registry_save_props (GstXMLRegistry *xmlregistry, GstProps *props)
1369 {
1370   GList *proplist;
1371
1372   proplist = props->properties;
1373
1374   while (proplist) {
1375     GstPropsEntry *entry = (GstPropsEntry *) proplist->data;
1376
1377     switch (gst_props_entry_get_props_type (entry)) {
1378       case GST_PROPS_LIST_TYPE: 
1379       {
1380         const GList *list;
1381
1382         gst_props_entry_get_list (entry, &list);
1383         
1384         CLASS (xmlregistry)->save_func (xmlregistry, "<list name=\"%s\">\n", gst_props_entry_get_name (entry));
1385         g_list_foreach ((GList *)list, (GFunc) gst_xml_registry_save_props_func, xmlregistry);
1386         CLASS (xmlregistry)->save_func (xmlregistry, "</list>\n");
1387         break;
1388       }
1389       default:
1390         gst_xml_registry_save_props_func (entry, xmlregistry);
1391         break;
1392     }
1393     proplist = g_list_next (proplist);
1394   }
1395   return TRUE;
1396 }
1397
1398 static gboolean
1399 gst_xml_registry_save_caps (GstXMLRegistry *xmlregistry, GstCaps *caps)
1400 {
1401   while (caps) {
1402     CLASS (xmlregistry)->save_func (xmlregistry, "<capscomp>\n");
1403     PUT_ESCAPED ("name", caps->name);
1404     PUT_ESCAPED ("type", gst_caps_get_mime (caps));
1405
1406     if (caps->properties) {
1407       CLASS (xmlregistry)->save_func (xmlregistry, "<properties>\n");
1408       gst_xml_registry_save_props (xmlregistry, caps->properties);
1409       CLASS (xmlregistry)->save_func (xmlregistry, "</properties>\n");
1410     }
1411     CLASS (xmlregistry)->save_func (xmlregistry, "</capscomp>\n");
1412     caps = caps->next;
1413   }
1414   return TRUE;
1415 }
1416
1417 static gboolean
1418 gst_xml_registry_save_pad_template (GstXMLRegistry *xmlregistry, GstPadTemplate *template)
1419 {
1420   gchar *presence;
1421   
1422   PUT_ESCAPED ("nametemplate", template->name_template);
1423   CLASS (xmlregistry)->save_func (xmlregistry, "<direction>%s</direction>\n", (template->direction == GST_PAD_SINK? "sink":"src"));
1424
1425   switch (template->presence) {
1426     case GST_PAD_ALWAYS:
1427       presence = "always";
1428       break;
1429     case GST_PAD_SOMETIMES:
1430       presence = "sometimes";
1431       break;
1432     case GST_PAD_REQUEST:
1433       presence = "request";
1434       break;
1435     default:
1436       presence = "unknown";
1437       break;
1438   }
1439   CLASS (xmlregistry)->save_func (xmlregistry, "<presence>%s</presence>\n", presence);
1440
1441   if (GST_PAD_TEMPLATE_CAPS (template)) {
1442     CLASS (xmlregistry)->save_func (xmlregistry, "<caps>\n");
1443     gst_xml_registry_save_caps (xmlregistry, GST_PAD_TEMPLATE_CAPS (template));
1444     CLASS (xmlregistry)->save_func (xmlregistry, "</caps>\n");
1445   }
1446   return TRUE;
1447 }
1448
1449 static gboolean
1450 gst_xml_registry_save_feature (GstXMLRegistry *xmlregistry, GstPluginFeature *feature)
1451 {
1452   PUT_ESCAPED ("name", feature->name);
1453
1454   if (feature->rank > 0) {
1455     gint rank = feature->rank;
1456     CLASS (xmlregistry)->save_func (xmlregistry, "<rank>%d</rank>\n", rank);
1457   }     
1458     
1459   if (GST_IS_ELEMENT_FACTORY (feature)) {
1460     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
1461     GList *walk;
1462
1463     PUT_ESCAPED ("longname", factory->details.longname);
1464     PUT_ESCAPED ("class", factory->details.klass);
1465     PUT_ESCAPED ("description", factory->details.description);
1466     PUT_ESCAPED ("author", factory->details.author);
1467     
1468     walk = factory->padtemplates;
1469
1470     while (walk) {
1471       GstPadTemplate *template = GST_PAD_TEMPLATE (walk->data);
1472
1473       CLASS (xmlregistry)->save_func (xmlregistry, "<padtemplate>\n");
1474       gst_xml_registry_save_pad_template (xmlregistry, template);
1475       CLASS (xmlregistry)->save_func (xmlregistry, "</padtemplate>\n");
1476       
1477       walk = g_list_next (walk);
1478     }
1479
1480     walk = factory->interfaces;
1481     while (walk) {
1482       PUT_ESCAPED ("interface", (gchar *) walk->data);
1483       walk = g_list_next (walk);
1484     }
1485
1486     if (GST_URI_TYPE_IS_VALID (factory->uri_type)) {
1487       gchar **protocol;
1488       
1489       PUT_ESCAPED ("uri_type", factory->uri_type == GST_URI_SINK ? "sink" : "source");
1490       g_assert (factory->uri_protocols);
1491       protocol = factory->uri_protocols;
1492       while (*protocol) {
1493         PUT_ESCAPED ("uri_protocol", *protocol);
1494         protocol++;
1495       }
1496     }
1497   }
1498   else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
1499     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
1500     gint i = 0;
1501     /* FIXME 
1502     if (factory->caps) {
1503       CLASS (xmlregistry)->save_func (xmlregistry, "<caps>\n");
1504       gst_xml_registry_save_caps (xmlregistry, factory->caps);
1505       CLASS (xmlregistry)->save_func (xmlregistry, "</caps>\n");
1506     } */
1507     if (factory->extensions) {
1508       while (factory->extensions[i]) {
1509         PUT_ESCAPED ("extension", factory->extensions[i]);
1510         i++;
1511       }
1512     }
1513   }
1514   else if (GST_IS_SCHEDULER_FACTORY (feature)) {
1515     PUT_ESCAPED ("longdesc", GST_SCHEDULER_FACTORY (feature)->longdesc);
1516   }
1517   else if (GST_IS_AUTOPLUG_FACTORY (feature)) {
1518     PUT_ESCAPED ("longdesc", GST_AUTOPLUG_FACTORY (feature)->longdesc);
1519   }
1520   else if (GST_IS_INDEX_FACTORY (feature)) {
1521     PUT_ESCAPED ("longdesc", GST_INDEX_FACTORY (feature)->longdesc);
1522   }
1523   return TRUE;
1524 }
1525
1526 static gboolean
1527 gst_xml_registry_save_plugin (GstXMLRegistry *xmlregistry, GstPlugin *plugin)
1528 {
1529   GList *walk;
1530   
1531   PUT_ESCAPED ("name", plugin->desc.name);
1532   PUT_ESCAPED ("description", plugin->desc.description);
1533   PUT_ESCAPED ("filename", plugin->filename);
1534   PUT_ESCAPED ("version", plugin->desc.version);
1535   PUT_ESCAPED ("license", plugin->desc.license);
1536   PUT_ESCAPED ("copyright", plugin->desc.copyright);
1537   PUT_ESCAPED ("package", plugin->desc.package);
1538   PUT_ESCAPED ("origin", plugin->desc.origin);
1539
1540   walk = plugin->features;
1541
1542   while (walk) {
1543     GstPluginFeature *feature = GST_PLUGIN_FEATURE (walk->data);
1544
1545     CLASS (xmlregistry)->save_func (xmlregistry, "<feature typename=\"%s\">\n", g_type_name (G_OBJECT_TYPE (feature)));
1546     gst_xml_registry_save_feature (xmlregistry, feature);
1547     CLASS (xmlregistry)->save_func (xmlregistry, "</feature>\n");
1548     
1549     walk = g_list_next (walk);
1550   }
1551   return TRUE;
1552 }
1553
1554
1555 static gboolean
1556 gst_xml_registry_save (GstRegistry *registry)
1557 {
1558   GList *walk;
1559   GstXMLRegistry *xmlregistry;
1560   
1561   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
1562   g_return_val_if_fail (registry->flags & GST_REGISTRY_WRITABLE, FALSE);
1563   
1564   xmlregistry = GST_XML_REGISTRY (registry);
1565
1566   if (!CLASS (xmlregistry)->open_func (xmlregistry, GST_XML_REGISTRY_WRITE)) {
1567     return FALSE;
1568   }
1569
1570   CLASS (xmlregistry)->save_func (xmlregistry, "<?xml version=\"1.0\"?>\n");
1571   CLASS (xmlregistry)->save_func (xmlregistry, "<GST-PluginRegistry>\n");
1572   
1573   walk = g_list_last (gst_registry_get_path_list (GST_REGISTRY (registry)));
1574   
1575   CLASS (xmlregistry)->save_func (xmlregistry, "<gst-plugin-paths>\n");
1576   while (walk) {
1577     CLASS (xmlregistry)->save_func (xmlregistry, "<path>");
1578     CLASS (xmlregistry)->save_func (xmlregistry, (gchar*)walk->data);
1579     CLASS (xmlregistry)->save_func (xmlregistry, "</path>\n");
1580     walk = g_list_previous (walk);
1581   }
1582   CLASS (xmlregistry)->save_func (xmlregistry, "</gst-plugin-paths>\n");
1583
1584   walk = g_list_last (registry->plugins);
1585
1586   while (walk) {
1587     GstPlugin *plugin = GST_PLUGIN (walk->data);
1588
1589     CLASS (xmlregistry)->save_func (xmlregistry, "<plugin>\n");
1590     gst_xml_registry_save_plugin (xmlregistry, plugin);
1591     CLASS (xmlregistry)->save_func (xmlregistry, "</plugin>\n");
1592     
1593     walk = g_list_previous (walk);
1594   }
1595   CLASS (xmlregistry)->save_func (xmlregistry, "</GST-PluginRegistry>\n");
1596
1597   CLASS (xmlregistry)->close_func (xmlregistry);
1598
1599   return TRUE;
1600 }
1601
1602 static GList*
1603 gst_xml_registry_rebuild_recurse (GstXMLRegistry *registry, 
1604                                   const gchar *directory)
1605 {
1606   GDir *dir;
1607   gchar *temp;
1608   GList *ret = NULL;
1609
1610   dir = g_dir_open (directory, 0, NULL);
1611
1612   if (dir) {
1613     const gchar *dirent;
1614
1615     while ((dirent = g_dir_read_name (dir))) {
1616       gchar *dirname;
1617         
1618       if (*dirent == '=') {
1619         /* =build, =inst, etc. -- automake distcheck directories */
1620         continue;
1621       }
1622       
1623       dirname = g_strjoin ("/", directory, dirent, NULL);
1624       ret = g_list_concat (ret, gst_xml_registry_rebuild_recurse (registry, dirname));
1625       g_free(dirname);
1626     }
1627     g_dir_close (dir);
1628   } else {
1629     if ((temp = strstr (directory, G_MODULE_SUFFIX)) &&
1630         (!strcmp (temp, G_MODULE_SUFFIX))) {
1631       ret = g_list_prepend (ret, g_strdup (directory));
1632     }
1633   }
1634
1635   return ret;
1636 }
1637
1638 static gboolean
1639 gst_xml_registry_rebuild (GstRegistry *registry)
1640 {
1641   GList *walk = NULL, *plugins = NULL, *prune = NULL;
1642   GError *error = NULL;
1643   guint length;
1644   GstPlugin *plugin;
1645   GstXMLRegistry *xmlregistry = GST_XML_REGISTRY (registry);
1646  
1647   walk = registry->paths;
1648
1649   while (walk) {
1650     gchar *path = (gchar *) walk->data;
1651     
1652     GST_CAT_INFO (GST_CAT_PLUGIN_LOADING, 
1653               "Rebuilding registry %p in directory %s...", registry, path);
1654
1655     plugins = g_list_concat (plugins, 
1656                              gst_xml_registry_rebuild_recurse (xmlregistry, 
1657                                                                path));
1658
1659     walk = g_list_next (walk);
1660   }
1661   
1662   plugins = g_list_reverse (plugins);
1663
1664   do {
1665     length = g_list_length (plugins);
1666
1667     walk = plugins;
1668     while (walk) {
1669       g_assert (walk->data);
1670       plugin = gst_plugin_load_file ((gchar *) walk->data, NULL);
1671       if (plugin) {
1672         prune = g_list_prepend (prune, walk->data);
1673         gst_registry_add_plugin (registry, plugin);
1674       }
1675
1676       walk = g_list_next (walk);
1677     }
1678
1679     walk = prune;
1680     while (walk) {
1681       plugins = g_list_remove (plugins, walk->data);
1682       g_free (walk->data);
1683       walk = g_list_next (walk);
1684     }
1685     g_list_free (prune);
1686     prune = NULL;
1687   } while (g_list_length (plugins) != length);
1688   
1689   walk = plugins;
1690   while (walk) {
1691     if ((plugin = gst_plugin_load_file ((gchar *) walk->data, &error))) {
1692       g_warning ("Bizarre behavior: plugin %s actually loaded", 
1693                  (gchar *) walk->data);
1694       gst_registry_add_plugin (registry, plugin);
1695     } else {
1696       GST_CAT_INFO (GST_CAT_PLUGIN_LOADING, "Plugin %s failed to load: %s", 
1697                 (gchar *) walk->data, error->message);
1698
1699       g_free (walk->data);
1700       g_error_free (error);
1701       error = NULL;
1702     }
1703         
1704     walk = g_list_next (walk);
1705   }
1706   return TRUE;
1707 }