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
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 /* 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 (write(rec->handle, indent_block, CONFIG_INDENT_SIZE) == -1)
42 static int config_write_str(CONFIG_REC *rec, const char *str)
44 const char *strpos, *p;
46 g_return_val_if_fail(rec != NULL, -1);
47 g_return_val_if_fail(str != NULL, -1);
50 while (*strpos != '\0') {
51 /* fill the indentation */
52 if (rec->tmp_last_lf && rec->tmp_indent_level > 0 &&
54 if (config_write_indent(rec) == -1)
58 p = strchr(strpos, '\n');
60 if (write(rec->handle, strpos, strlen(strpos)) == -1)
63 rec->tmp_last_lf = FALSE;
65 if (write(rec->handle, strpos, (int) (p-strpos)+1) == -1)
68 rec->tmp_last_lf = TRUE;
75 static int config_has_specials(const char *text)
77 g_return_val_if_fail(text != NULL, FALSE);
79 while (*text != '\0') {
80 if (!i_isalnum(*text) && *text != '_')
88 static int get_octal(int decimal)
94 octal += (decimal & 7)*(pos == 0 ? 1 : pos);
102 static char *config_escape_string(const char *text)
107 g_return_val_if_fail(text != NULL, NULL);
109 str = g_string_new("\"");
110 while (*text != '\0') {
111 if (*text == '\\' || *text == '"')
112 g_string_sprintfa(str, "\\%c", *text);
113 else if ((unsigned char) *text < 32)
114 g_string_sprintfa(str, "\\%03d", get_octal(*text));
116 g_string_append_c(str, *text);
120 g_string_append_c(str, '"');
123 g_string_free(str, FALSE);
127 static int config_write_word(CONFIG_REC *rec, const char *word, int string)
132 g_return_val_if_fail(rec != NULL, -1);
133 g_return_val_if_fail(word != NULL, -1);
135 if (!string && !config_has_specials(word))
136 return config_write_str(rec, word);
138 str = config_escape_string(word);
139 ret = config_write_str(rec, str);
145 static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds);
147 static int config_write_node(CONFIG_REC *rec, CONFIG_NODE *node, int line_feeds)
149 g_return_val_if_fail(rec != NULL, -1);
150 g_return_val_if_fail(node != NULL, -1);
152 switch (node->type) {
154 if (config_write_word(rec, node->key, FALSE) == -1 ||
155 config_write_str(rec, " = ") == -1 ||
156 config_write_word(rec, node->value, TRUE) == -1)
159 case NODE_TYPE_VALUE:
160 if (config_write_word(rec, node->value, TRUE) == -1)
163 case NODE_TYPE_BLOCK:
165 if (node->key != NULL) {
166 if (config_write_word(rec, node->key, FALSE) == -1 ||
167 config_write_str(rec, " = ") == -1)
170 if (config_write_str(rec, line_feeds ? "{\n" : "{ ") == -1)
174 rec->tmp_indent_level += CONFIG_INDENT_SIZE;
175 if (config_write_block(rec, node, FALSE, line_feeds) == -1)
177 rec->tmp_indent_level -= CONFIG_INDENT_SIZE;
180 if (config_write_str(rec, "}") == -1)
185 if (node->key != NULL) {
186 if (config_write_word(rec, node->key, FALSE) == -1 ||
187 config_write_str(rec, " = ") == -1)
190 if (config_write_str(rec, line_feeds ? "(\n" : "( ") == -1)
194 rec->tmp_indent_level += CONFIG_INDENT_SIZE;
195 if (config_write_block(rec, node, TRUE, line_feeds) == -1)
197 rec->tmp_indent_level -= CONFIG_INDENT_SIZE;
200 if (config_write_str(rec, ")") == -1)
203 case NODE_TYPE_COMMENT:
204 if (node->value == NULL)
207 if (config_write_str(rec, "#") == -1 ||
208 config_write_str(rec, node->value) == -1)
216 static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node);
218 static int config_node_get_length(CONFIG_REC *rec, CONFIG_NODE *node)
222 switch (node->type) {
224 /* "key = value; " */
225 len = 5 + strlen(node->key) + strlen(node->value);
227 case NODE_TYPE_VALUE:
229 len = 2 + strlen(node->value);
231 case NODE_TYPE_BLOCK:
235 if (node->key != NULL) len += strlen(node->key);
236 len += config_block_get_length(rec, node);
239 /* comments always split the line */
247 /* return the number of characters `node' and it's subnodes take
248 if written to file */
249 static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node)
255 for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
256 CONFIG_NODE *subnode = tmp->data;
258 len += config_node_get_length(rec, subnode);
259 if (len > MAX_CHARS_IN_LINE) return len;
265 /* check if `node' and it's subnodes fit in one line in the config file */
266 static int config_block_fit_one_line(CONFIG_REC *rec, CONFIG_NODE *node)
268 g_return_val_if_fail(rec != NULL, 0);
269 g_return_val_if_fail(node != NULL, 0);
271 return rec->tmp_indent_level +
272 config_node_get_length(rec, node) <= MAX_CHARS_IN_LINE;
275 static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds)
278 int list_line_feeds, node_line_feeds;
280 g_return_val_if_fail(rec != NULL, -1);
281 g_return_val_if_fail(node != NULL, -1);
282 g_return_val_if_fail(is_node_list(node), -1);
284 list_line_feeds = !config_block_fit_one_line(rec, node);
286 if (!line_feeds && list_line_feeds)
287 config_write_str(rec, "\n");
289 for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
290 CONFIG_NODE *subnode = tmp->data;
292 node_line_feeds = !line_feeds ? FALSE : !config_block_fit_one_line(rec, subnode);
293 if (config_write_node(rec, subnode, node_line_feeds) == -1)
296 if (subnode->type == NODE_TYPE_COMMENT)
297 config_write_str(rec, "\n");
299 if (tmp->next != NULL)
300 config_write_str(rec, list_line_feeds ? ",\n" : ", ");
302 config_write_str(rec, list_line_feeds ? "\n" : " ");
304 config_write_str(rec, list_line_feeds ? ";\n" : "; ");
311 /* Write configuration file. Write to `fname' if it's not NULL. */
312 int config_write(CONFIG_REC *rec, const char *fname, int create_mode)
316 g_return_val_if_fail(rec != NULL, -1);
317 g_return_val_if_fail(fname != NULL || rec->fname != NULL, -1);
318 g_return_val_if_fail(create_mode != -1 || rec->create_mode != -1, -1);
320 if (rec->handle != -1)
323 rec->handle = open(fname != NULL ? fname : rec->fname,
324 O_WRONLY | O_TRUNC | O_CREAT,
325 create_mode != -1 ? create_mode : rec->create_mode);
326 if (rec->handle == -1)
327 return config_error(rec, g_strerror(errno));
329 rec->tmp_indent_level = 0;
330 rec->tmp_last_lf = TRUE;
331 ret = config_write_block(rec, rec->mainnode, FALSE, TRUE);
334 config_error(rec, errno == 0 ? "bug" : g_strerror(errno));