Also register for path / on D-Bus
[browser-switch] / config.c
1 /*
2  * config.c -- configuration functions for Browser Switchboard
3  *
4  * Copyright (C) 2009-2010 Steven Luo
5  * Derived from a Python implementation by Jason Simpson and Steven Luo
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program 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
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20  * USA.
21  */
22
23 #include <stdlib.h>
24 #include <stddef.h>
25 #include <string.h>
26
27 #include "configfile.h"
28 #include "config.h"
29
30 /* The Browser Switchboard config file options */
31 struct swb_config_option swb_config_options[] = {
32         { "continuous_mode", SWB_CONFIG_OPT_INT, SWB_CONFIG_CONTINUOUS_MODE_SET },
33         { "default_browser", SWB_CONFIG_OPT_STRING, SWB_CONFIG_DEFAULT_BROWSER_SET },
34         { "other_browser_cmd", SWB_CONFIG_OPT_STRING, SWB_CONFIG_OTHER_BROWSER_CMD_SET },
35         { "logging", SWB_CONFIG_OPT_STRING, SWB_CONFIG_LOGGING_SET },
36         { "autostart_microb", SWB_CONFIG_OPT_INT, SWB_CONFIG_AUTOSTART_MICROB_SET },
37         { NULL, 0, 0 },
38 };
39
40 /* Browser Switchboard configuration defaults */
41 static struct swb_config swb_config_defaults = {
42         .flags = SWB_CONFIG_INITIALIZED,
43         .continuous_mode = 1,
44         .default_browser = "microb",
45         .other_browser_cmd = NULL,
46         .logging = "stdout",
47         .autostart_microb = -1,
48 };
49
50
51 /* Copy the contents of an swb_config struct
52    The entries[] array means that the standard copy will not work */
53 void swb_config_copy(struct swb_config *dst, struct swb_config *src) {
54         if (!dst || !src)
55                 return;
56
57         dst->entries[0] = &(dst->continuous_mode);
58         dst->entries[1] = &(dst->default_browser);
59         dst->entries[2] = &(dst->other_browser_cmd);
60         dst->entries[3] = &(dst->logging);
61         dst->entries[4] = &(dst->autostart_microb);
62
63         dst->flags = src->flags;
64
65         dst->continuous_mode = src->continuous_mode;
66         dst->default_browser = src->default_browser;
67         dst->other_browser_cmd = src->other_browser_cmd;
68         dst->logging = src->logging;
69         dst->autostart_microb = src->autostart_microb;
70 }
71
72 /* Initialize a swb_config struct with configuration defaults */
73 void swb_config_init(struct swb_config *cfg) {
74         swb_config_copy(cfg, &swb_config_defaults);
75 }
76
77 /* Free all heap memory used in an swb_config struct
78    This MUST NOT be done if any of the strings are being used elsewhere! */
79 void swb_config_free(struct swb_config *cfg) {
80         int i;
81
82         if (!cfg)
83                 return;
84         if (!(cfg->flags & SWB_CONFIG_INITIALIZED))
85                 return;
86
87         for (i = 0; swb_config_options[i].name; ++i) {
88                 if (cfg->flags & swb_config_options[i].set_mask) {
89                         switch (swb_config_options[i].type) {
90                           case SWB_CONFIG_OPT_STRING:
91                                 free(*(char **)cfg->entries[i]);
92                                 *(char **)cfg->entries[i] = NULL;
93                                 break;
94                           default:
95                                 break;
96                         }
97                 }
98         }
99
100         cfg->flags = 0;
101 }
102
103 /* Load a value into the part of a struct swb_config indicated by name */
104 static int swb_config_load_option(struct swb_config *cfg,
105                                   char *name, char *value) {
106         struct swb_config_option *opt;
107         ptrdiff_t i;
108
109         /* Search through list of recognized config options for a match */
110         for (opt = swb_config_options; opt->name; ++opt) {
111                 if (strcmp(name, opt->name))
112                         continue;
113
114                 if (!(cfg->flags & opt->set_mask)) {
115                         i = opt - swb_config_options;
116                         switch (opt->type) {
117                           case SWB_CONFIG_OPT_STRING:
118                                 *(char **)cfg->entries[i] = value;
119                                 break;
120                           case SWB_CONFIG_OPT_INT:
121                                 *(int *)cfg->entries[i] = atoi(value);
122                                 free(value);
123                                 break;
124                         }
125                         cfg->flags |= opt->set_mask;
126                 } else {
127                         /* Option was repeated in the config file
128                            We want the first value, so ignore this one */
129                         free(value);
130                 }
131                 return 1;
132         }
133
134         /* Unrecognized config option */
135         free(value);
136         return 0;
137 }
138
139 /* Read the config file and load settings into the provided swb_config struct
140    Caller is responsible for freeing allocated strings with free()
141    Returns true on success, false otherwise */
142 int swb_config_load(struct swb_config *cfg) {
143         FILE *fp;
144         struct swb_config_line line;
145
146         if (!cfg || !(cfg->flags & SWB_CONFIG_INITIALIZED))
147                 return 0;
148
149         if (!(fp = open_config_file()))
150                 goto out_noopen;
151
152         /* Parse the config file
153            TODO: should we handle errors differently than EOF? */
154         if (!parse_config_file_begin())
155                 goto out;
156         while (!parse_config_file_line(fp, &line)) {
157                 if (line.parsed)
158                         swb_config_load_option(cfg, line.key, line.value);
159                 free(line.key);
160         }
161         parse_config_file_end();
162
163 out:
164         fclose(fp);
165 out_noopen:
166         return 1;
167 }