source: trunk/third/gnome-vfs/libgnomevfs/gnome-vfs-configuration.c @ 17128

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