2 parse.c : irssi configuration - parse configuration file
4 Copyright (C) 1999 Timo Sirainen
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 static int g_istr_equal(gconstpointer v, gconstpointer v2)
25 return g_strcasecmp((const char *) v, (const char *) v2) == 0;
28 /* a char* hash function from ASU */
29 static unsigned int g_istr_hash(gconstpointer v)
31 const char *s = (const char *) v;
32 unsigned int h = 0, g;
35 h = (h << 4) + toupper(*s);
36 if ((g = h & 0xf0000000UL)) {
46 int config_error(CONFIG_REC *rec, const char *msg)
48 g_free_and_null(rec->last_error);
49 rec->last_error = g_strdup(msg);
53 static int node_add_comment(CONFIG_NODE *parent, const char *str)
57 g_return_val_if_fail(parent != NULL, -1);
59 if (!is_node_list(parent))
62 node = g_new0(CONFIG_NODE, 1);
63 node->type = NODE_TYPE_COMMENT;
64 node->value = str == NULL ? NULL : g_strdup(str);
66 parent->value = g_slist_append(parent->value, node);
70 /* same as g_scanner_get_next_token() except skips and reads the comments */
71 static void config_parse_get_token(GScanner *scanner, CONFIG_NODE *node)
73 int prev_empty = FALSE;
76 g_scanner_get_next_token(scanner);
78 if (scanner->token == G_TOKEN_COMMENT_SINGLE)
79 node_add_comment(node, scanner->value.v_string);
80 else if (scanner->token == '\n') {
81 if (prev_empty) node_add_comment(node, NULL);
83 if (scanner->token == G_TOKEN_INT) {
84 scanner->token = G_TOKEN_STRING;
85 #undef g_strdup_printf /* This is free'd by GLib itself */
86 scanner->value.v_string = g_strdup_printf("%lu", scanner->value.v_int);
88 #define g_strdup_printf(a, b...) ig_strdup_printf(__FILE__, __LINE__, a, ##b)
98 /* same as g_scanner_peek_next_token() except skips and reads the comments */
99 static void config_parse_peek_token(GScanner *scanner, CONFIG_NODE *node)
101 int prev_empty = FALSE;
104 g_scanner_peek_next_token(scanner);
106 if (scanner->next_token == G_TOKEN_COMMENT_SINGLE)
107 node_add_comment(node, scanner->next_value.v_string);
108 else if (scanner->next_token == '\n') {
109 if (prev_empty) node_add_comment(node, NULL);
114 g_scanner_get_next_token(scanner);
118 /* get optional token, optionally warn if it's missing */
119 static void config_parse_warn_missing(CONFIG_REC *rec, CONFIG_NODE *node,
120 GTokenType expected_token, int print_warning)
122 config_parse_peek_token(rec->scanner, node);
123 if (rec->scanner->next_token == expected_token) {
124 g_scanner_get_next_token(rec->scanner);
129 g_scanner_warn(rec->scanner, "Warning: missing '%c'", expected_token);
132 static void config_parse_loop(CONFIG_REC *rec, CONFIG_NODE *node, GTokenType expect);
134 static GTokenType config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node)
136 CONFIG_NODE *newnode;
137 GTokenType last_char;
141 g_return_val_if_fail(rec != NULL, G_TOKEN_ERROR);
142 g_return_val_if_fail(node != NULL, G_TOKEN_ERROR);
144 config_parse_get_token(rec->scanner, node);
146 last_char = (GTokenType) (node->type == NODE_TYPE_LIST ? ',' : ';');
150 if (node->type != NODE_TYPE_LIST &&
151 (rec->scanner->token == G_TOKEN_STRING)) {
152 key = g_strdup(rec->scanner->value.v_string);
154 config_parse_warn_missing(rec, node, '=', TRUE);
155 config_parse_get_token(rec->scanner, node);
158 switch (rec->scanner->token) {
161 config_node_set_str(rec, node, key, rec->scanner->value.v_string);
162 g_free_not_null(key);
164 print_warning = TRUE;
165 if (node->type == NODE_TYPE_LIST) {
166 /* if it's last item it doesn't need comma */
167 config_parse_peek_token(rec->scanner, node);
168 if (rec->scanner->next_token == ')')
169 print_warning = FALSE;
172 config_parse_warn_missing(rec, node, last_char, print_warning);
177 if (key == NULL && node->type != NODE_TYPE_LIST)
178 return G_TOKEN_ERROR;
180 newnode = config_node_section(node, key, NODE_TYPE_BLOCK);
181 config_parse_loop(rec, newnode, (GTokenType) '}');
182 g_free_not_null(key);
184 config_parse_get_token(rec->scanner, node);
185 if (rec->scanner->token != '}')
186 return (GTokenType) '}';
188 config_parse_warn_missing(rec, node, last_char, FALSE);
194 return G_TOKEN_ERROR;
195 newnode = config_node_section(node, key, NODE_TYPE_LIST);
196 config_parse_loop(rec, newnode, (GTokenType) ')');
197 g_free_not_null(key);
199 config_parse_get_token(rec->scanner, node);
200 if (rec->scanner->token != ')')
201 return (GTokenType) ')';
203 config_parse_warn_missing(rec, node, last_char, FALSE);
208 g_free_not_null(key);
209 return G_TOKEN_STRING;
215 static void config_parse_loop(CONFIG_REC *rec, CONFIG_NODE *node, GTokenType expect)
217 GTokenType expected_token;
219 g_return_if_fail(rec != NULL);
220 g_return_if_fail(node != NULL);
223 config_parse_peek_token(rec->scanner, node);
224 if (rec->scanner->next_token == expect ||
225 rec->scanner->next_token == G_TOKEN_EOF) break;
227 expected_token = config_parse_symbol(rec, node);
228 if (expected_token != G_TOKEN_NONE) {
229 if (expected_token == G_TOKEN_ERROR)
230 expected_token = G_TOKEN_NONE;
231 g_scanner_unexp_token(rec->scanner, expected_token, NULL, "symbol", NULL, NULL, TRUE);
236 static void config_parse_error_func(GScanner *scanner, char *message, int is_error)
238 CONFIG_REC *rec = scanner->user_data;
241 old = rec->last_error;
242 rec->last_error = g_strdup_printf("%s%s:%d: %s%s\n",
243 old == NULL ? "" : old,
244 scanner->input_name, scanner->line,
245 is_error ? "error: " : "",
247 g_free_not_null(old);
250 void config_parse_init(CONFIG_REC *rec, const char *name)
254 g_free_and_null(rec->last_error);
255 config_nodes_remove_all(rec);
257 rec->scanner = scanner = g_scanner_new(NULL);
258 scanner->config->skip_comment_single = FALSE;
259 scanner->config->cset_skip_characters = " \t";
260 scanner->config->scan_binary = FALSE;
261 scanner->config->scan_octal = FALSE;
262 scanner->config->scan_float = FALSE;
263 scanner->config->scan_string_sq = TRUE;
264 scanner->config->scan_string_dq = TRUE;
265 scanner->config->scan_identifier_1char = TRUE;
266 scanner->config->identifier_2_string = TRUE;
268 scanner->user_data = rec;
269 scanner->input_name = name;
270 scanner->msg_handler = (GScannerMsgFunc) config_parse_error_func;
273 /* Parse configuration file */
274 int config_parse(CONFIG_REC *rec)
276 g_return_val_if_fail(rec != NULL, -1);
277 g_return_val_if_fail(rec->fname != NULL, -1);
279 rec->handle = open(rec->fname, O_RDONLY);
280 if (rec->handle == -1)
281 return config_error(rec, g_strerror(errno));
283 config_parse_init(rec, rec->fname);
284 g_scanner_input_file(rec->scanner, rec->handle);
285 config_parse_loop(rec, rec->mainnode, G_TOKEN_EOF);
286 g_scanner_destroy(rec->scanner);
291 return rec->last_error == NULL ? 0 : -1;
294 /* Parse configuration found from `data'. `input_name' is specifies the
295 "configuration name" which is displayed in error messages. */
296 int config_parse_data(CONFIG_REC *rec, const char *data, const char *input_name)
298 config_parse_init(rec, input_name);
299 g_scanner_input_text(rec->scanner, data, strlen(data));
300 config_parse_loop(rec, rec->mainnode, G_TOKEN_EOF);
301 g_scanner_destroy(rec->scanner);
303 return rec->last_error == NULL ? 0 : -1;
306 /* Open configuration. The file is created if it doesn't exist, unless
307 `create_mode' is -1. `fname' can be NULL if you just want to use
308 config_parse_data() */
309 CONFIG_REC *config_open(const char *fname, int create_mode)
315 f = open(fname, O_RDONLY | (create_mode != -1 ? O_CREAT : 0), create_mode);
316 if (f == -1) return NULL;
320 rec = g_new0(CONFIG_REC, 1);
321 rec->fname = fname == NULL ? NULL : g_strdup(fname);
323 rec->create_mode = create_mode;
324 rec->mainnode = g_new0(CONFIG_NODE, 1);
325 rec->mainnode->type = NODE_TYPE_BLOCK;
326 rec->cache = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
327 rec->cache_nodes = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
332 /* Release all memory used by configuration */
333 void config_close(CONFIG_REC *rec)
335 g_return_if_fail(rec != NULL);
337 config_nodes_remove_all(rec);
338 g_free(rec->mainnode);
340 if (rec->handle != -1) close(rec->handle);
341 g_hash_table_foreach(rec->cache, (GHFunc) g_free, NULL);
342 g_hash_table_destroy(rec->cache);
343 g_hash_table_destroy(rec->cache_nodes);
344 g_free_not_null(rec->last_error);
345 g_free_not_null(rec->fname);
349 /* Change file name of config file */
350 void config_change_file_name(CONFIG_REC *rec, const char *fname, int create_mode)
352 g_return_if_fail(rec != NULL);
353 g_return_if_fail(fname != NULL);
355 g_free_not_null(rec->fname);
356 rec->fname = g_strdup(fname);
358 if (create_mode != -1)
359 rec->create_mode = create_mode;