2000-04-14 07:27:14 -04:00
|
|
|
/*
|
|
|
|
write.c : irssi configuration - write configuration file
|
|
|
|
|
|
|
|
Copyright (C) 1999 Timo Sirainen
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
2007-05-08 14:16:58 -04:00
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
2000-04-14 07:27:14 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "module.h"
|
|
|
|
|
|
|
|
/* maximum length of lines in config file before splitting them to multiple lines */
|
|
|
|
#define MAX_CHARS_IN_LINE 70
|
|
|
|
|
|
|
|
#define CONFIG_INDENT_SIZE 2
|
|
|
|
static const char *indent_block = " "; /* needs to be the same size as CONFIG_INDENT_SIZE! */
|
|
|
|
|
|
|
|
/* write needed amount of indentation to the start of the line */
|
|
|
|
static int config_write_indent(CONFIG_REC *rec)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
for (n = 0; n < rec->tmp_indent_level/CONFIG_INDENT_SIZE; n++) {
|
|
|
|
if (write(rec->handle, indent_block, CONFIG_INDENT_SIZE) == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_write_str(CONFIG_REC *rec, const char *str)
|
|
|
|
{
|
|
|
|
const char *strpos, *p;
|
|
|
|
|
|
|
|
g_return_val_if_fail(rec != NULL, -1);
|
|
|
|
g_return_val_if_fail(str != NULL, -1);
|
|
|
|
|
|
|
|
strpos = str;
|
|
|
|
while (*strpos != '\0') {
|
|
|
|
/* fill the indentation */
|
2001-01-07 03:05:14 -05:00
|
|
|
if (rec->tmp_last_lf && rec->tmp_indent_level > 0 &&
|
|
|
|
*str != '\n') {
|
2000-04-14 07:27:14 -04:00
|
|
|
if (config_write_indent(rec) == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = strchr(strpos, '\n');
|
|
|
|
if (p == NULL) {
|
|
|
|
if (write(rec->handle, strpos, strlen(strpos)) == -1)
|
|
|
|
return -1;
|
|
|
|
strpos = "";
|
|
|
|
rec->tmp_last_lf = FALSE;
|
|
|
|
} else {
|
|
|
|
if (write(rec->handle, strpos, (int) (p-strpos)+1) == -1)
|
|
|
|
return -1;
|
|
|
|
strpos = p+1;
|
|
|
|
rec->tmp_last_lf = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_has_specials(const char *text)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail(text != NULL, FALSE);
|
|
|
|
|
|
|
|
while (*text != '\0') {
|
2002-01-27 15:45:59 -05:00
|
|
|
if (!i_isalnum(*text) && *text != '_')
|
2000-04-14 07:27:14 -04:00
|
|
|
return TRUE;
|
|
|
|
text++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_octal(int decimal)
|
|
|
|
{
|
|
|
|
int octal, pos;
|
|
|
|
|
|
|
|
octal = 0; pos = 0;
|
|
|
|
while (decimal > 0) {
|
|
|
|
octal += (decimal & 7)*(pos == 0 ? 1 : pos);
|
|
|
|
decimal /= 8;
|
|
|
|
pos += 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
return octal;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *config_escape_string(const char *text)
|
|
|
|
{
|
|
|
|
GString *str;
|
|
|
|
char *ret;
|
|
|
|
|
|
|
|
g_return_val_if_fail(text != NULL, NULL);
|
|
|
|
|
|
|
|
str = g_string_new("\"");
|
|
|
|
while (*text != '\0') {
|
|
|
|
if (*text == '\\' || *text == '"')
|
|
|
|
g_string_sprintfa(str, "\\%c", *text);
|
|
|
|
else if ((unsigned char) *text < 32)
|
|
|
|
g_string_sprintfa(str, "\\%03d", get_octal(*text));
|
|
|
|
else
|
|
|
|
g_string_append_c(str, *text);
|
|
|
|
text++;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_string_append_c(str, '"');
|
|
|
|
|
|
|
|
ret = str->str;
|
|
|
|
g_string_free(str, FALSE);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_write_word(CONFIG_REC *rec, const char *word, int string)
|
|
|
|
{
|
|
|
|
char *str;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
g_return_val_if_fail(rec != NULL, -1);
|
|
|
|
g_return_val_if_fail(word != NULL, -1);
|
|
|
|
|
|
|
|
if (!string && !config_has_specials(word))
|
|
|
|
return config_write_str(rec, word);
|
|
|
|
|
|
|
|
str = config_escape_string(word);
|
|
|
|
ret = config_write_str(rec, str);
|
|
|
|
g_free(str);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds);
|
|
|
|
|
|
|
|
static int config_write_node(CONFIG_REC *rec, CONFIG_NODE *node, int line_feeds)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail(rec != NULL, -1);
|
|
|
|
g_return_val_if_fail(node != NULL, -1);
|
|
|
|
|
|
|
|
switch (node->type) {
|
|
|
|
case NODE_TYPE_KEY:
|
|
|
|
if (config_write_word(rec, node->key, FALSE) == -1 ||
|
|
|
|
config_write_str(rec, " = ") == -1 ||
|
|
|
|
config_write_word(rec, node->value, TRUE) == -1)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case NODE_TYPE_VALUE:
|
|
|
|
if (config_write_word(rec, node->value, TRUE) == -1)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case NODE_TYPE_BLOCK:
|
|
|
|
/* key = { */
|
|
|
|
if (node->key != NULL) {
|
2000-04-26 04:10:09 -04:00
|
|
|
if (config_write_word(rec, node->key, FALSE) == -1 ||
|
2000-04-14 07:27:14 -04:00
|
|
|
config_write_str(rec, " = ") == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (config_write_str(rec, line_feeds ? "{\n" : "{ ") == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* ..block.. */
|
|
|
|
rec->tmp_indent_level += CONFIG_INDENT_SIZE;
|
|
|
|
if (config_write_block(rec, node, FALSE, line_feeds) == -1)
|
|
|
|
return -1;
|
|
|
|
rec->tmp_indent_level -= CONFIG_INDENT_SIZE;
|
|
|
|
|
|
|
|
/* }; */
|
|
|
|
if (config_write_str(rec, "}") == -1)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case NODE_TYPE_LIST:
|
|
|
|
/* key = ( */
|
|
|
|
if (node->key != NULL) {
|
2000-04-26 04:10:09 -04:00
|
|
|
if (config_write_word(rec, node->key, FALSE) == -1 ||
|
2000-04-14 07:27:14 -04:00
|
|
|
config_write_str(rec, " = ") == -1)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (config_write_str(rec, line_feeds ? "(\n" : "( ") == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* ..list.. */
|
|
|
|
rec->tmp_indent_level += CONFIG_INDENT_SIZE;
|
|
|
|
if (config_write_block(rec, node, TRUE, line_feeds) == -1)
|
|
|
|
return -1;
|
|
|
|
rec->tmp_indent_level -= CONFIG_INDENT_SIZE;
|
|
|
|
|
|
|
|
/* ); */
|
|
|
|
if (config_write_str(rec, ")") == -1)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case NODE_TYPE_COMMENT:
|
|
|
|
if (node->value == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (config_write_str(rec, "#") == -1 ||
|
|
|
|
config_write_str(rec, node->value) == -1)
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node);
|
|
|
|
|
|
|
|
static int config_node_get_length(CONFIG_REC *rec, CONFIG_NODE *node)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
switch (node->type) {
|
|
|
|
case NODE_TYPE_KEY:
|
|
|
|
/* "key = value; " */
|
|
|
|
len = 5 + strlen(node->key) + strlen(node->value);
|
|
|
|
break;
|
|
|
|
case NODE_TYPE_VALUE:
|
|
|
|
/* "value, " */
|
|
|
|
len = 2 + strlen(node->value);
|
|
|
|
break;
|
|
|
|
case NODE_TYPE_BLOCK:
|
|
|
|
case NODE_TYPE_LIST:
|
|
|
|
/* "{ list }; " */
|
|
|
|
len = 6;
|
|
|
|
if (node->key != NULL) len += strlen(node->key);
|
|
|
|
len += config_block_get_length(rec, node);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* comments always split the line */
|
|
|
|
len = 1000;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return the number of characters `node' and it's subnodes take
|
|
|
|
if written to file */
|
|
|
|
static int config_block_get_length(CONFIG_REC *rec, CONFIG_NODE *node)
|
|
|
|
{
|
|
|
|
GSList *tmp;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = 0;
|
|
|
|
for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
|
|
|
|
CONFIG_NODE *subnode = tmp->data;
|
|
|
|
|
|
|
|
len += config_node_get_length(rec, subnode);
|
|
|
|
if (len > MAX_CHARS_IN_LINE) return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if `node' and it's subnodes fit in one line in the config file */
|
|
|
|
static int config_block_fit_one_line(CONFIG_REC *rec, CONFIG_NODE *node)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail(rec != NULL, 0);
|
|
|
|
g_return_val_if_fail(node != NULL, 0);
|
|
|
|
|
|
|
|
return rec->tmp_indent_level +
|
|
|
|
config_node_get_length(rec, node) <= MAX_CHARS_IN_LINE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int config_write_block(CONFIG_REC *rec, CONFIG_NODE *node, int list, int line_feeds)
|
|
|
|
{
|
|
|
|
GSList *tmp;
|
|
|
|
int list_line_feeds, node_line_feeds;
|
|
|
|
|
|
|
|
g_return_val_if_fail(rec != NULL, -1);
|
|
|
|
g_return_val_if_fail(node != NULL, -1);
|
|
|
|
g_return_val_if_fail(is_node_list(node), -1);
|
|
|
|
|
|
|
|
list_line_feeds = !config_block_fit_one_line(rec, node);
|
|
|
|
|
|
|
|
if (!line_feeds && list_line_feeds)
|
|
|
|
config_write_str(rec, "\n");
|
|
|
|
|
|
|
|
for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
|
|
|
|
CONFIG_NODE *subnode = tmp->data;
|
|
|
|
|
|
|
|
node_line_feeds = !line_feeds ? FALSE : !config_block_fit_one_line(rec, subnode);
|
|
|
|
if (config_write_node(rec, subnode, node_line_feeds) == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (subnode->type == NODE_TYPE_COMMENT)
|
|
|
|
config_write_str(rec, "\n");
|
|
|
|
else if (list) {
|
|
|
|
if (tmp->next != NULL)
|
|
|
|
config_write_str(rec, list_line_feeds ? ",\n" : ", ");
|
|
|
|
else
|
|
|
|
config_write_str(rec, list_line_feeds ? "\n" : " ");
|
|
|
|
} else {
|
|
|
|
config_write_str(rec, list_line_feeds ? ";\n" : "; ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write configuration file. Write to `fname' if it's not NULL. */
|
|
|
|
int config_write(CONFIG_REC *rec, const char *fname, int create_mode)
|
|
|
|
{
|
2000-12-16 18:38:12 -05:00
|
|
|
int ret;
|
|
|
|
|
2000-04-14 07:27:14 -04:00
|
|
|
g_return_val_if_fail(rec != NULL, -1);
|
|
|
|
g_return_val_if_fail(fname != NULL || rec->fname != NULL, -1);
|
|
|
|
g_return_val_if_fail(create_mode != -1 || rec->create_mode != -1, -1);
|
|
|
|
|
2000-12-16 18:38:12 -05:00
|
|
|
if (rec->handle != -1)
|
|
|
|
close(rec->handle);
|
|
|
|
|
2000-04-14 07:27:14 -04:00
|
|
|
rec->handle = open(fname != NULL ? fname : rec->fname,
|
|
|
|
O_WRONLY | O_TRUNC | O_CREAT,
|
|
|
|
create_mode != -1 ? create_mode : rec->create_mode);
|
|
|
|
if (rec->handle == -1)
|
|
|
|
return config_error(rec, g_strerror(errno));
|
|
|
|
|
|
|
|
rec->tmp_indent_level = 0;
|
|
|
|
rec->tmp_last_lf = TRUE;
|
2000-12-16 18:38:12 -05:00
|
|
|
ret = config_write_block(rec, rec->mainnode, FALSE, TRUE);
|
|
|
|
if (ret == -1) {
|
2000-04-14 07:27:14 -04:00
|
|
|
/* write error */
|
|
|
|
config_error(rec, errno == 0 ? "bug" : g_strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
close(rec->handle);
|
|
|
|
rec->handle = -1;
|
|
|
|
|
2000-12-16 18:38:12 -05:00
|
|
|
return ret;
|
2000-04-14 07:27:14 -04:00
|
|
|
}
|