ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-configuration.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gnome-vfs-configuration.c - Handling of the GNOME Virtual File System
3    configuration.
4
5    Copyright (C) 1999 Free Software Foundation
6
7    The Gnome Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11
12    The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.
21
22    Author: Ettore Perazzoli <ettore@gnu.org> */
23
24 #include <config.h>
25 #include "gnome-vfs-configuration.h"
26
27 #include <dirent.h>
28 #include <errno.h>
29 #include <glib/ghash.h>
30 #include <glib/glist.h>
31 #include <glib/gmem.h>
32 #include <glib/gmessages.h>
33 #include <glib/gstrfuncs.h>
34 #include <glib/gthread.h>
35 #include <glib/gutils.h>
36 #include "gnome-vfs-i18n.h"
37 #include "gnome-vfs-private.h"
38 #include <sys/stat.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43
44 typedef struct _Configuration Configuration;
45 struct _Configuration {
46         GHashTable *method_to_module_path;
47         time_t last_checked;
48         GList *directories;
49 };
50
51 typedef struct _ModulePathElement ModulePathElement;
52 struct _ModulePathElement {
53         char *method_name;
54         char *path;
55         char *args;
56 };
57
58 typedef struct _VfsDirSource VfsDirSource;
59 struct _VfsDirSource {
60         char *dirname;
61         struct stat s;
62         unsigned int valid : 1;
63 };
64
65 /* Global variable */
66 static Configuration *configuration = NULL;
67
68 G_LOCK_DEFINE_STATIC (configuration);
69 #define MAX_CFG_FILES 128
70
71 \f
72
73 static ModulePathElement *
74 module_path_element_new (const char *method_name,
75                          const char *path,
76                          const char *args)
77 {
78         ModulePathElement *new;
79
80         new = g_new (ModulePathElement, 1);
81         new->method_name = g_strdup (method_name);
82         new->path = g_strdup (path);
83         new->args = g_strdup (args);
84
85         return new;
86 }
87
88 static void
89 module_path_element_free (ModulePathElement *module_path)
90 {
91         g_free (module_path->method_name);
92         g_free (module_path->path);
93         g_free (module_path->args);
94         g_free (module_path);
95 }
96
97 static VfsDirSource *
98 vfs_dir_source_new (const char *dirname)
99 {
100         VfsDirSource *new;
101
102         new = g_new (VfsDirSource, 1);
103         new->dirname = g_strdup (dirname);
104
105         return new;
106 }
107
108 static void
109 vfs_dir_source_free (VfsDirSource *vfs_source)
110 {
111         g_free (vfs_source->dirname);
112         g_free (vfs_source);
113 }
114
115 \f
116
117 static void
118 hash_free_module_path (gpointer value)
119 {
120         ModulePathElement *module_path;
121
122         module_path = (ModulePathElement *) value;
123         module_path_element_free (module_path);
124 }
125
126 /* Destroy configuration information.  */
127 static void
128 configuration_destroy (Configuration *configuration)
129 {
130         g_return_if_fail (configuration != NULL);
131
132         g_hash_table_destroy (configuration->method_to_module_path);
133         g_list_foreach (configuration->directories, (GFunc) vfs_dir_source_free, NULL);
134         g_list_free (configuration->directories);
135         g_free (configuration);
136 }
137
138 \f
139
140 /* This reads a line and handles backslashes at the end of the line to join
141    lines.  */
142 static gint
143 read_line (FILE *stream,
144            gchar **line_return,
145            guint *n,
146            guint *lines_read)
147 {
148 #define START_BUFFER_SIZE 1024
149         gboolean backslash;
150         gint pos;
151
152         if (feof (stream))
153                 return -1;
154
155         pos = 0;
156         backslash = FALSE;
157         *lines_read = 0;
158         while (1) {
159                 int c;
160
161                 if (pos == *n) {
162                         if (*n == 0)
163                                 *n = START_BUFFER_SIZE;
164                         else
165                                 *n *= 2;
166                         *line_return = g_realloc (*line_return, *n);
167                 }
168
169                 c = fgetc (stream);
170                 if (c == '\n')
171                         (*lines_read)++;
172                 if (c == EOF || (c == '\n' && ! backslash)) {
173                         (*line_return)[pos] = 0;
174                         return pos;
175                 }
176
177                 if (c == '\\' && ! backslash) {
178                         backslash = TRUE;
179                 } else if (c != '\n') {
180                         if (backslash)
181                                 (*line_return)[pos++] = '\\';
182                         (*line_return)[pos] = c;
183                         pos++;
184                         backslash = FALSE;
185                 }
186         }
187 #undef START_BUFFER_SIZE
188 }
189
190 static void
191 remove_comment (gchar *buf)
192 {
193         gchar *p;
194
195         p = strchr (buf, '#');
196         if (p != NULL)
197                 *p = '\0';
198 }
199
200 static gboolean
201 parse_line (Configuration *configuration,
202             gchar *line_buffer,
203             guint line_len,
204             const gchar *file_name,
205
206             guint line_number)
207 {
208         guint string_len;
209         gboolean retval;
210         gchar *p;
211         gchar *method_start;
212         char *module_name;
213         char *args = NULL;
214         GList *method_list;
215         GList *lp;
216
217         string_len = strlen (line_buffer);
218         if (string_len != line_len) {
219                 g_warning (_("%s:%d contains NUL characters."),
220                            file_name, line_number);
221                 return FALSE;
222         }
223
224         remove_comment (line_buffer);
225         line_buffer = g_strstrip (line_buffer);
226
227         method_list = NULL;
228         p = line_buffer;
229         method_start = line_buffer;
230         retval = TRUE;
231         while (*p != '\0') {
232                 if (*p == ' ' || *p == '\t' || *p == ':') {
233                         gchar *method_name;
234
235                         if (p == method_start) {
236                                 g_warning (_("%s:%d contains no method name."),
237                                            file_name, line_number);
238                                 retval = FALSE;
239                                 goto cleanup;
240                         }
241
242                         method_name = g_strndup (method_start,
243                                                  p  - method_start);
244                         method_list = g_list_prepend (method_list, method_name);
245
246                         while (*p == ' ' || *p == '\t')
247                                 p++;
248
249                         if (*p == ':') {
250                                 p++;
251                                 break;
252                         }
253
254                         method_start = p;
255                 }
256
257                 p++;
258         }
259
260         while (*p && g_ascii_isspace (*p))
261                 p++;
262
263         if (*p == '\0') {
264                 if (method_list != NULL) {
265                         g_warning (_("%s:%d contains no module name."),
266                                    file_name, line_number);
267                         retval = FALSE;
268                 } else {
269                         /* Empty line.  */
270                         retval = TRUE;
271                 }
272                 goto cleanup;
273         }
274
275         module_name = p;
276         while(*p && !g_ascii_isspace (*p)) p++;
277
278         if(*p) {
279                 *p = '\0';
280                 p++;
281                 while(*p && g_ascii_isspace (*p)) p++;
282                 if(*p)
283                         args = p;
284         }
285
286         for (lp = method_list; lp != NULL; lp = lp->next) {
287                 ModulePathElement *element;
288                 gchar *method_name;
289
290                 method_name = lp->data;
291                 element = module_path_element_new (method_name, module_name, args);
292                 g_hash_table_insert (configuration->method_to_module_path,
293                                      method_name, element);
294         }
295
296         retval = TRUE;
297
298  cleanup:
299         if (method_list != NULL)
300                 g_list_free (method_list);
301         return retval;
302 }
303
304 /* FIXME bugzilla.eazel.com 1139:
305    maybe we should return FALSE if any errors during parsing happen so
306    that we abort immediately, but this sounds a bit too overkill.  */
307 static gboolean
308 parse_file (Configuration *configuration,
309             const gchar *file_name)
310 {
311         FILE *f;
312         gchar *line_buffer;
313         guint line_buffer_size;
314         guint line_number;
315
316         f = fopen (file_name, "r");
317         if (f == NULL) {
318                 g_warning (_("Configuration file `%s' was not found: %s"),
319                            file_name, strerror (errno));
320                 return FALSE;
321         }
322
323         line_buffer = NULL;
324         line_buffer_size = 0;
325         line_number = 0;
326         while (1) {
327                 guint lines_read;
328                 gint line_len;
329
330                 line_len = read_line (f, &line_buffer, &line_buffer_size,
331                                       &lines_read);
332                 if (line_len == -1)
333                         break;  /* EOF */
334                 parse_line (configuration, line_buffer, line_len, file_name,
335                             line_number);
336                 line_number += lines_read;
337         }
338
339         g_free (line_buffer);
340
341         fclose (f);
342
343         return TRUE;
344 }
345
346 static void
347 configuration_load (void)
348 {
349         gchar *file_names[MAX_CFG_FILES + 1];
350         GList *list;
351         int i = 0;
352         DIR *dirh;
353
354         configuration->method_to_module_path = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, hash_free_module_path);
355
356         /* Go through the list of configuration directories and build up a list of config files */
357         for (list = configuration->directories; list && i < MAX_CFG_FILES; list = list->next) {
358                 VfsDirSource *dir_source = (VfsDirSource *)list->data;
359                 struct dirent *dent;
360
361                 if (stat (dir_source->dirname, &dir_source->s) == -1)
362                         continue;
363
364                 dirh = opendir (dir_source->dirname);
365                 if(!dirh)
366                         continue;
367
368                 while ((dent = readdir(dirh)) && i < MAX_CFG_FILES) {
369                         char *ctmp;
370                         ctmp = strstr(dent->d_name, ".conf");
371                         if(!ctmp || strcmp(ctmp, ".conf"))
372                                 continue;
373                         file_names[i] = g_strdup_printf ("%s/%s", dir_source->dirname, dent->d_name);
374                         i++;
375                 }
376                 closedir(dirh);
377         }
378         file_names[i] = NULL;
379
380         /* Now read these cfg files */
381         for(i = 0; file_names[i]; i++) {
382                 /* FIXME: should we try to catch errors? */
383                 parse_file (configuration, file_names[i]);
384                 g_free (file_names[i]);
385         }
386 }
387
388
389 static void
390 add_directory_internal (const char *dir)
391 {
392         VfsDirSource *dir_source = vfs_dir_source_new (dir);
393
394         configuration->directories = g_list_prepend (configuration->directories, dir_source);
395 }
396
397 void
398 _gnome_vfs_configuration_add_directory (const char *dir)
399 {
400         G_LOCK (configuration);
401         if (configuration == NULL) {
402                 g_warning ("_gnome_vfs_configuration_init must be called prior to adding a directory.");
403                 G_UNLOCK (configuration);
404                 return;
405         }
406
407         add_directory_internal (dir);
408
409         G_UNLOCK (configuration);
410 }
411
412
413 static void
414 install_path_list (const gchar *environment_path)
415 {
416         const char *p, *oldp;
417
418         oldp = environment_path;
419         while (1) {
420                 char *elem;
421
422                 p = strchr (oldp, ':');
423
424                 if (p == NULL) {
425                         if (*oldp != '\0') {
426                                 add_directory_internal (oldp);
427                         }
428                         break;
429                 } else {
430                         elem = g_strndup (oldp, p - oldp);
431                         add_directory_internal (elem);
432                         g_free (elem);
433                 } 
434
435                 oldp = p + 1;
436         }
437 }
438
439
440 gboolean
441 _gnome_vfs_configuration_init (void)
442 {
443         char *home_config;
444         char *environment_path;
445         const char *home_dir;
446
447         G_LOCK (configuration);
448         if (configuration != NULL) {
449                 G_UNLOCK (configuration);
450                 return FALSE;
451         }
452
453         configuration = g_new0 (Configuration, 1);
454
455         add_directory_internal (GNOME_VFS_MODULE_CFGDIR);
456         environment_path = getenv ("GNOME_VFS_MODULE_CONFIG_PATH");
457         if (environment_path != NULL) {
458                 install_path_list (environment_path);
459         }
460
461         home_dir = g_get_home_dir ();
462         if (home_dir != NULL) {
463                 home_config = g_strdup_printf ("%s%c%s",
464                                                home_dir,
465                                                G_DIR_SEPARATOR,
466                                                ".gnome2/vfs/modules");
467                 add_directory_internal (home_config);
468                 g_free (home_config);
469         }
470
471         configuration_load ();
472
473         G_UNLOCK (configuration);
474
475         if (configuration == NULL) {
476                 return FALSE;
477         } else {
478                 return TRUE;
479         }
480 }
481
482 void
483 _gnome_vfs_configuration_uninit (void)
484 {
485         G_LOCK (configuration);
486         if (configuration == NULL) {
487                 G_UNLOCK (configuration);
488                 return;
489         }
490
491         configuration_destroy (configuration);
492         configuration = NULL;
493         G_UNLOCK (configuration);
494 }
495
496 static void
497 maybe_reload (void)
498 {
499         time_t now = time (NULL);
500         GList *list;
501         gboolean need_reload = FALSE;
502         struct stat s;
503
504         /* only check every 5 seconds minimum */
505         if (configuration->last_checked + 5 >= now)
506                 return;
507
508         for (list = configuration->directories; list; list = list->next) {
509                 VfsDirSource *dir_source = (VfsDirSource *) list->data;
510                 if (stat (dir_source->dirname, &s) == -1)
511                         continue;
512                 if (s.st_mtime != dir_source->s.st_mtime) {
513                         need_reload = TRUE;
514                         break;
515                 }
516         }
517
518         configuration->last_checked = now;
519
520         if (!need_reload)
521                 return;
522
523         configuration->last_checked = time (NULL);
524
525         g_hash_table_destroy (configuration->method_to_module_path);
526         configuration_load ();
527 }
528
529 const gchar *
530 _gnome_vfs_configuration_get_module_path (const gchar *method_name, const char ** args)
531 {
532         ModulePathElement *element;
533
534         g_return_val_if_fail (method_name != NULL, NULL);
535
536         G_LOCK (configuration);
537
538         if (configuration != NULL) {
539                 maybe_reload ();
540                 element = g_hash_table_lookup
541                         (configuration->method_to_module_path, method_name);
542         } else {
543                 /* This should never happen.  */
544                 g_warning ("Internal error: the configuration system was not initialized. Did you call _gnome_vfs_configuration_init?");
545                 element = NULL;
546         }
547
548         G_UNLOCK (configuration);
549
550         if (element == NULL)
551                 return NULL;
552
553         if (args)
554                 *args = element->args;
555         return element->path;
556 }
557
558 static void
559 add_method_to_list(const gchar *key, gpointer value, GList **methods_list)
560 {
561         *methods_list = g_list_append(*methods_list, g_strdup(key));
562 }
563
564 GList *
565 _gnome_vfs_configuration_get_methods_list (void)
566 {
567         GList *methods_list = NULL;
568
569         G_LOCK (configuration);
570         if (configuration != NULL) {
571                 maybe_reload ();
572                 g_hash_table_foreach(configuration->method_to_module_path, 
573                                      (GHFunc)add_method_to_list, &methods_list);
574         } else {
575                 /* This should never happen.  */
576                 methods_list = NULL;
577         }
578
579         G_UNLOCK (configuration);
580         return methods_list;
581 }