mirror of
https://github.com/irssi/irssi.git
synced 2026-08-18 16:12:30 +02:00
Sorry for a big update - I still don't have internet connection at home
and this is what I've been doing a few weeks now.. :) You really shouldn't upgrade to this version without keeping a backup of the working one, since this will break everything and at least notify list is broken - probably something else too. * On the way to 0.8.0 .. Major rewriting/rearranging code. There's some changes in behaviour because I'm trying to make Irssi a bit more compatible with EPIC. * libPropList isn't needed anymore - I'm using my own configuration library. This is mostly because different proplists worked a bit differently everywhere and several people had problems with it. It's also yet another extra library that you needed to compile Irssi. New configuration library has several advantages: You can add comments to configuration file and they also stay there when it's saved. It's not nearly as vulnerable as proplist. If some error occurs, instead of just not reading anything it will try to continue if possible. Also the error messages are written to irssi's text window instead of stdout. It can be managed more easily than proplist - setting/getting the configuration is a lot more easier. * Coding style changes - I'm not using gint, gchar etc. anymore, they're just extra pain when moving code to non-glib projects and syntax hilighting doesn't work by default with most editors ;) Indentation style was also changed to K&R because of some political reasons ;) And I'm already starting to like it.. :) It forces me to split code to different functions more often and the result is that the code gets more readable. And finally I'm also using nst' all over the place. + /EVAL <commands> - Expand all the special variables from string and run it. Commands can be split with ; character. See docs/SPECIAL_VARS for more info. + Aliases are parsed just like /EVAL - arguments are in $0..$9. + Text formats are also parsed like /EVAL, arguments used to be in $1..$9, now they're in $0..$8 so it messes up existing themes.. + /SET [key [value]] - no more the '=' character. Boolean values also need to be changed with ON/OFF/TOGGLE values (not yes/no). Settings aren't saved to disk until you use /SAVE. + /TOGGLE <key> [ON/OFF] - same as /SET <key> TOGGLE git-svn-id: http://svn.irssi.org/repos/irssi/trunk@163 dbcabf3a-b0e7-0310-adc4-f8d773084564
This commit is contained in:
parent
5fb98c2210
commit
53b248f6de
23 changed files with 1532 additions and 400 deletions
336
src/lib-config/write.c
Normal file
336
src/lib-config/write.c
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
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
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#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 */
|
||||
if (rec->tmp_last_lf && rec->tmp_indent_level > 0) {
|
||||
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') {
|
||||
if ((unsigned char) *text <= 32 || *text == '"' || *text == '\\')
|
||||
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) {
|
||||
if (config_write_str(rec, node->key) == -1 ||
|
||||
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) {
|
||||
if (config_write_str(rec, node->key) == -1 ||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
if (config_write_block(rec, rec->mainnode, FALSE, TRUE) == -1) {
|
||||
/* write error */
|
||||
config_error(rec, errno == 0 ? "bug" : g_strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
write(rec->handle, "\n", 1);
|
||||
|
||||
close(rec->handle);
|
||||
rec->handle = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue