2 write.c : irssi configuration - write 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 along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 /* maximum length of lines in config file before splitting them to multiple lines */
24 #define MAX_CHARS_IN_LINE 70
26 #define CONFIG_INDENT_SIZE 2
27 static const char *indent_block = " "; /* needs to be the same size as CONFIG_INDENT_SIZE! */
29 /* write needed amount of indentation to the start of the line */
30 static int config_write_indent(CONFIG_REC *rec)
34 for (n = 0; n < rec->tmp_indent_level/CONFIG_INDENT_SIZE; n++) {
35 if (g_io_channel_write_chars(rec->handle, indent_block, CONFIG_INDENT_SIZE,
36 NULL, NULL) == G_IO_STATUS_ERROR)
43 static int config_write_str(CONFIG_REC *rec, const char *str)
45 const char *strpos, *p;
47 g_return_val_if_fail(rec != NULL, -1);
48 g_return_val_if_fail(str != NULL, -1);
51 while (*strpos != '\0') {
52 /* fill the indentation */
53 if (rec->tmp_last_lf && rec->tmp_indent_level > 0 &&
55 if (config_write_indent(rec) == -1)
59 p = strchr(strpos, '\n');
61 if (g_io_channel_write_chars(rec->handle, strpos, strlen(strpos),
62 NULL, NULL) == G_IO_STATUS_ERROR)
65 rec->tmp_last_lf = FALSE;
67 if (g_io_channel_write_chars(rec->handle, strpos, (int) (p-strpos)+1,
68 NULL, NULL) == G_IO_STATUS_ERROR)
71 rec->tmp_last_lf = TRUE;
78 static int config_has_specials(const char *text)
80 g_return_val_if_fail(text != NULL, FALSE);
82 while (*text != '\0') {
83 if (!i_isalnum(*text) && *text != '_')
91 static char *config_escape_string(const char *text)
96 g_return_val_if_fail(text != NULL, NULL);
98 str = g_string_new("\"");
99 while (*text != '\0') {
100 if (*text == '\\' || *text == '"')
101 g_string_append_printf(str, "\\%c", *text);
102 else if ((unsigned char) *text < 32)
103 g_string_append_printf(str, "\\%03o", *text);
105 g_string_append_c(str, *text);
109 g_string_append_c(str, '"');
112 g_string_free(str, FALSE);
116 static int config_write_word(CONFIG_REC *rec, const char *word, int string)
121 g_return_val_if_fail(rec != NULL, -1);
122 g_return_val_if_fail(word != NULL, -1);
124 if (!string && !config_has_specials(word))
125 return config_write_str(rec, word);
127 str = config_escape_string(word);
128 ret = config_write_str(rec, str);
134 static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds);
136 static int config_write_node(CONFIG_REC *rec, CONFIG_NODE *node, int line_feeds)
138 g_return_val_if_fail(rec != NULL, -1);
139 g_return_val_if_fail(node != NULL, -1);
141 switch (node->type) {
143 if (config_write_word(rec, node->key, FALSE) == -1 ||
144 config_write_str(rec, " = ") == -1 ||
145 config_write_word(rec, node->value, TRUE) == -1)
148 case NODE_TYPE_VALUE:
149 if (config_write_word(rec, node->value, TRUE) == -1)
152 case NODE_TYPE_BLOCK:
154 if (node->key != NULL) {
155 if (config_write_word(rec, node->key, FALSE) == -1 ||
156 config_write_str(rec, " = ") == -1)
159 if (config_write_str(rec, line_feeds ? "{\n" : "{ ") == -1)
163 rec->tmp_indent_level += CONFIG_INDENT_SIZE;
164 if (config_write_block(rec, node, FALSE, line_feeds) == -1)
166 rec->tmp_indent_level -= CONFIG_INDENT_SIZE;
169 if (config_write_str(rec, "}") == -1)
174 if (node->key != NULL) {
175 if (config_write_word(rec, node->key, FALSE) == -1 ||
176 config_write_str(rec, " = ") == -1)
179 if (config_write_str(rec, line_feeds ? "(\n" : "( ") == -1)
183 rec->tmp_indent_level += CONFIG_INDENT_SIZE;
184 if (config_write_block(rec, node, TRUE, line_feeds) == -1)
186 rec->tmp_indent_level -= CONFIG_INDENT_SIZE;
189 if (config_write_str(rec, ")") == -1)
192 case NODE_TYPE_COMMENT:
193 if (node->value == NULL)
196 if (config_write_str(rec, "#") == -1 ||
197 config_write_str(rec, node->value) == -1)
205 static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node);
207 static int config_node_get_length(CONFIG_REC *rec, CONFIG_NODE *node)
211 switch (node->type) {
213 /* "key = value; " */
214 len = 5 + strlen(node->key) + strlen(node->value);
216 case NODE_TYPE_VALUE:
218 len = 2 + strlen(node->value);
220 case NODE_TYPE_BLOCK:
224 if (node->key != NULL) len += strlen(node->key);
225 len += config_block_get_length(rec, node);
228 /* comments always split the line */
236 /* return the number of characters `node' and it's subnodes take
237 if written to file */
238 static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node)
244 for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
245 CONFIG_NODE *subnode = tmp->data;
247 len += config_node_get_length(rec, subnode);
248 if (len > MAX_CHARS_IN_LINE) return len;
254 /* check if `node' and it's subnodes fit in one line in the config file */
255 static int config_block_fit_one_line(CONFIG_REC *rec, CONFIG_NODE *node)
257 g_return_val_if_fail(rec != NULL, 0);
258 g_return_val_if_fail(node != NULL, 0);
260 return rec->tmp_indent_level +
261 config_node_get_length(rec, node) <= MAX_CHARS_IN_LINE;
264 static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds)
267 int list_line_feeds, node_line_feeds;
269 g_return_val_if_fail(rec != NULL, -1);
270 g_return_val_if_fail(node != NULL, -1);
271 g_return_val_if_fail(is_node_list(node), -1);
273 list_line_feeds = !config_block_fit_one_line(rec, node);
275 if (!line_feeds && list_line_feeds)
276 config_write_str(rec, "\n");
278 for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
279 CONFIG_NODE *subnode = tmp->data;
281 node_line_feeds = !line_feeds ? FALSE : !config_block_fit_one_line(rec, subnode);
282 if (config_write_node(rec, subnode, node_line_feeds) == -1)
285 if (subnode->type == NODE_TYPE_COMMENT)
286 config_write_str(rec, "\n");
288 if (tmp->next != NULL)
289 config_write_str(rec, list_line_feeds ? ",\n" : ", ");
291 config_write_str(rec, list_line_feeds ? "\n" : " ");
293 config_write_str(rec, list_line_feeds ? ";\n" : "; ");
300 int config_write(CONFIG_REC *rec, const char *fname, int create_mode)
305 g_return_val_if_fail(rec != NULL, -1);
306 g_return_val_if_fail(fname != NULL || rec->fname != NULL, -1);
307 g_return_val_if_fail(create_mode != -1 || rec->create_mode != -1, -1);
309 fd = open(fname != NULL ? fname : rec->fname,
310 O_WRONLY | O_TRUNC | O_CREAT,
311 create_mode != -1 ? create_mode : rec->create_mode);
313 return config_error(rec, g_strerror(errno));
315 rec->handle = g_io_channel_unix_new(fd);
316 g_io_channel_set_encoding(rec->handle, NULL, NULL);
317 g_io_channel_set_close_on_unref(rec->handle, TRUE);
318 rec->tmp_indent_level = 0;
319 rec->tmp_last_lf = TRUE;
320 ret = config_write_block(rec, rec->mainnode, FALSE, TRUE);
323 config_error(rec, errno == 0 ? "bug" : g_strerror(errno));
326 g_io_channel_unref(rec->handle);