summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTimo Sirainen <cras@irssi.org>2000-04-14 11:27:14 +0000
committercras <cras@dbcabf3a-b0e7-0310-adc4-f8d773084564>2000-04-14 11:27:14 +0000
commit53b248f6deafb553aae133792659a8412bc00925 (patch)
tree0c7287dfd3921308073463f4feca46a2c8e1acf6 /src
parent5fb98c2210033df3b6bdb4cdd6af77e7c32f6714 (diff)
downloadirssi-53b248f6deafb553aae133792659a8412bc00925.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/common-setup.h60
-rw-r--r--src/common.h10
-rw-r--r--src/lib-config/Makefile.am12
-rw-r--r--src/lib-config/get.c256
-rw-r--r--src/lib-config/iconfig.h136
-rw-r--r--src/lib-config/irssi-config.c189
-rw-r--r--src/lib-config/irssi-config.h25
-rw-r--r--src/lib-config/module.h6
-rw-r--r--src/lib-config/parse.c335
-rw-r--r--src/lib-config/set.c122
-rw-r--r--src/lib-config/write.c336
12 files changed, 1209 insertions, 280 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 90006689..7f8b82f1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,4 +17,4 @@ noinst_HEADERS = \
irssi-plugin.h \
irssi-plugin-gui.h
-SUBDIRS = irc-base irc-extra ui-common lib-config lib-popt settings $(GNOMEUI) $(TEXTUI) $(BOTUI)
+SUBDIRS = lib-popt lib-config irc-base irc-extra ui-common $(GNOMEUI) $(TEXTUI) $(BOTUI)
diff --git a/src/common-setup.h b/src/common-setup.h
index 42a60fa5..e04ca4f0 100644
--- a/src/common-setup.h
+++ b/src/common-setup.h
@@ -1,11 +1,7 @@
#ifndef __COMMON_SETUP_H
#define __COMMON_SETUP_H
-#include "irc-base/network.h"
-#include "settings/settings-public.h"
-
#define LOG_FILE_CREATE_MODE 0644
-#define CMD_CHAR '/'
/* wait for half an hour before trying to reconnect to host where last
connection failed */
@@ -26,59 +22,7 @@
/* Maximum time to wait for more JOINs before sending massjoin signal */
#define MAX_MASSJOIN_WAIT 5000
-/* lists */
-extern GSList *aliases, *ignores, *completions, *notifies, *hilights, *replaces, *popups;
-
-/* servers */
-typedef struct {
- char *server;
- int port;
-
- char *ircnet;
- char *password;
- int autoconnect;
- int cmd_queue_speed; /* override the default if > 0 */
-
- char *own_address; /* address to use when connecting this server */
- IPADDR own_ip; /* resolved own_address or full of zeros */
-
- time_t last_connect; /* to avoid reconnecting too fast.. */
- int last_failed; /* if last connection attempt failed */
-} SETUP_SERVER_REC;
-
-typedef struct {
- char *name;
-
- char *nick;
- char *username;
- char *realname;
-
- /* max. number of kicks/msgs/mode changes per command */
- int max_kicks, max_msgs, max_modes;
-} IRCNET_REC;
-
-extern GSList *setupservers; /* list of local servers */
-extern GSList *ircnets; /* list of available ircnets */
-
-/* channels */
-typedef struct {
- int autojoin;
-
- char *name;
- char *ircnet;
- char *password;
-
- char *botmasks;
- char *autosendcmd;
-
- char *background;
- char *font;
-} SETUP_CHANNEL_REC;
-
-extern GSList *setupchannels;
-
-extern gboolean readonly;
-extern IPADDR source_host_ip; /* Resolved address */
-extern gboolean source_host_ok; /* Use source_host_ip .. */
+/* How long to keep netsplits in memory (seconds) */
+#define NETSPLIT_MAX_REMEMBER (60*30)
#endif
diff --git a/src/common.h b/src/common.h
index 3fa42b2d..e3dde2c6 100644
--- a/src/common.h
+++ b/src/common.h
@@ -23,8 +23,6 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/utsname.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
@@ -38,10 +36,14 @@
#include <gmodule.h>
#include "irc-base/memdebug.h"
-#include "lib-config/irssi-config.h"
-#include "common-setup.h"
#include "nls.h"
+#define g_free_not_null(a) \
+ if (a) g_free(a);
+
+#define g_free_and_null(a) \
+ if (a) { g_free(a); (a) = NULL; }
+
typedef enum
{
G_INPUT_READ = 1 << 0,
diff --git a/src/lib-config/Makefile.am b/src/lib-config/Makefile.am
index 7de63122..00969218 100644
--- a/src/lib-config/Makefile.am
+++ b/src/lib-config/Makefile.am
@@ -1,9 +1,15 @@
noinst_LTLIBRARIES = libirssi_config.la
-INCLUDES = $(GLIB_CFLAGS)
+INCLUDES = \
+ $(GLIB_CFLAGS) \
+ -I$(top_srcdir)/src
libirssi_config_la_SOURCES = \
- irssi-config.c
+ get.c \
+ set.c \
+ parse.c \
+ write.c
noinst_HEADERS = \
- irssi-config.h
+ iconfig.h \
+ module.h
diff --git a/src/lib-config/get.c b/src/lib-config/get.c
new file mode 100644
index 00000000..e29df699
--- /dev/null
+++ b/src/lib-config/get.c
@@ -0,0 +1,256 @@
+/*
+ get.c : irssi configuration - get settings from memory
+
+ 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"
+
+CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key)
+{
+ GSList *tmp;
+
+ g_return_val_if_fail(node != NULL, NULL);
+ g_return_val_if_fail(key != NULL, NULL);
+ g_return_val_if_fail(is_node_list(node), NULL);
+
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ CONFIG_NODE *node = tmp->data;
+
+ if (node->key != NULL && g_strcasecmp(node->key, key) == 0)
+ return node;
+ }
+
+ return NULL;
+}
+
+/* find the section from node - if not found create it unless new_type is -1.
+ you can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */
+CONFIG_NODE *config_node_section(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int new_type)
+{
+ CONFIG_NODE *node;
+
+ g_return_val_if_fail(rec != NULL, NULL);
+ g_return_val_if_fail(parent != NULL, NULL);
+ g_return_val_if_fail(is_node_list(parent), NULL);
+
+ node = key == NULL ? NULL : config_node_find(parent, key);
+ if (node != NULL) {
+ g_return_val_if_fail(new_type == -1 || new_type == node->type, NULL);
+ return node;
+ }
+
+ if (new_type == -1)
+ return NULL;
+
+ node = g_new0(CONFIG_NODE, 1);
+ parent->value = g_slist_append(parent->value, node);
+
+ node->type = new_type;
+ node->key = key == NULL ? NULL : g_strdup(key);
+
+ return node;
+}
+
+/* find the section with the whole path.
+ create the path if necessary `create' is TRUE. */
+CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int create)
+{
+ CONFIG_NODE *node;
+ char **list, **tmp;
+ int is_list, new_type;
+
+ g_return_val_if_fail(rec != NULL, NULL);
+
+ if (section == NULL || *section == '\0')
+ return rec->mainnode;
+
+ /* check if it already exists in cache */
+ node = g_hash_table_lookup(rec->cache, section);
+ if (node != NULL) return node;
+
+ new_type = -1;
+
+ node = rec->mainnode;
+ list = g_strsplit(section, "/", -1);
+ for (tmp = list; *tmp != NULL; tmp++) {
+ is_list = **tmp == '(';
+ if (create) new_type = is_list ? NODE_TYPE_LIST : NODE_TYPE_BLOCK;
+
+ node = config_node_section(rec, node, *tmp + is_list, new_type);
+ if (node == NULL) return NULL;
+ }
+ g_strfreev(list);
+
+ /* save to cache */
+ g_hash_table_insert(rec->cache, g_strdup(section), node);
+ return node;
+}
+
+char *config_get_str(CONFIG_REC *rec, const char *section, const char *key, const char *def)
+{
+ CONFIG_NODE *parent, *node;
+ char *path;
+
+ g_return_val_if_fail(rec != NULL, (char *) def);
+ g_return_val_if_fail(section != NULL, (char *) def);
+ g_return_val_if_fail(key != NULL, (char *) def);
+
+ /* check if it already exists in cache */
+ path = g_strconcat(section, "/", key, NULL);
+ node = g_hash_table_lookup(rec->cache, path);
+
+ if (node != NULL)
+ g_free(path);
+ else {
+ parent = config_node_traverse(rec, section, FALSE);
+ node = parent == NULL ? NULL :
+ config_node_find(parent, key);
+
+ /* save to cache */
+ if (node != NULL)
+ g_hash_table_insert(rec->cache, path, node);
+ else
+ g_free(path);
+ }
+
+ return (node == NULL || !has_node_value(node)) ? (char *) def : node->value;
+}
+
+int config_get_int(CONFIG_REC *rec, const char *section, const char *key, int def)
+{
+ char *str;
+
+ str = config_get_str(rec, section, key, NULL);
+ if (str == NULL) return def;
+
+ return atoi(str);
+}
+
+int config_get_bool(CONFIG_REC *rec, const char *section, const char *key, int def)
+{
+ char *str;
+
+ str = config_get_str(rec, section, key, NULL);
+ if (str == NULL) return def;
+
+ return toupper(*str) == 'T' || toupper(*str) == 'Y';
+}
+
+/* Return value of key `value_key' from list item where `key' is `value' */
+const char *config_list_find(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key)
+{
+ CONFIG_NODE *node;
+
+ node = config_list_find_node(rec, section, key, value, value_key);
+ return node != NULL && node->type == NODE_TYPE_KEY ?
+ node->value : NULL;
+}
+
+/* Like config_list_find(), but return node instead of it's value */
+CONFIG_NODE *config_list_find_node(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key)
+{
+ CONFIG_NODE *node, *keynode;
+ GSList *tmp;
+
+ g_return_val_if_fail(rec != NULL, NULL);
+ g_return_val_if_fail(section != NULL, NULL);
+ g_return_val_if_fail(key != NULL, NULL);
+ g_return_val_if_fail(value_key != NULL, NULL);
+
+ node = config_node_traverse(rec, section, FALSE);
+ if (node == NULL || !is_node_list(node)) return NULL;
+
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ node = tmp->data;
+
+ if (node->type != NODE_TYPE_BLOCK)
+ continue;
+
+ /* key matches value? */
+ keynode = config_node_find(node, key);
+ if (keynode == NULL || keynode->type != NODE_TYPE_KEY ||
+ g_strcasecmp(keynode->value, value) != 0) continue;
+
+ return config_node_find(node, value_key);
+ }
+
+ return NULL;
+}
+
+char *config_node_get_str(CONFIG_NODE *parent, const char *key, const char *def)
+{
+ CONFIG_NODE *node;
+
+ node = config_node_find(parent, key);
+ return (node == NULL || !has_node_value(node)) ? def : node->value;
+}
+
+int config_node_get_int(CONFIG_NODE *parent, const char *key, int def)
+{
+ char *str;
+
+ str = config_node_get_str(parent, key, NULL);
+ if (str == NULL) return def;
+
+ return atoi(str);
+}
+
+int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def)
+{
+ char *str;
+
+ str = config_node_get_str(parent, key, NULL);
+ if (str == NULL) return def;
+
+ return toupper(*str) == 'T' || toupper(*str) == 'Y' ||
+ (toupper(*str) == 'O' && toupper(str[1]) == 'N');
+}
+
+/* Get the value of keys `key' and `key_value' and put them to
+ `ret_key' and `ret_value'. Returns -1 if not found. */
+int config_node_get_keyvalue(CONFIG_NODE *node, const char *key, const char *value_key, char **ret_key, char **ret_value)
+{
+ CONFIG_NODE *keynode, *valuenode;
+ GSList *tmp;
+
+ g_return_val_if_fail(node != NULL, -1);
+ g_return_val_if_fail(key != NULL, -1);
+ g_return_val_if_fail(value_key != NULL, -1);
+ g_return_val_if_fail(ret_key != NULL, -1);
+ g_return_val_if_fail(ret_value != NULL, -1);
+
+ for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+ node = tmp->data;
+
+ if (node->type != NODE_TYPE_BLOCK)
+ continue;
+
+ keynode = config_node_find(node, key);
+ if (keynode == NULL || keynode->type != NODE_TYPE_KEY)
+ continue;
+
+ valuenode = config_node_find(node, value_key);
+
+ *ret_key = keynode->key;
+ *ret_value = valuenode != NULL && valuenode->type == NODE_TYPE_KEY ?
+ valuenode->value : NULL;
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/src/lib-config/iconfig.h b/src/lib-config/iconfig.h
new file mode 100644
index 00000000..bbee0f6e
--- /dev/null
+++ b/src/lib-config/iconfig.h
@@ -0,0 +1,136 @@
+#ifndef __ICONFIG_H
+#define __ICONFIG_H
+
+enum {
+ NODE_TYPE_KEY,
+ NODE_TYPE_VALUE,
+ NODE_TYPE_BLOCK,
+ NODE_TYPE_LIST,
+ NODE_TYPE_COMMENT,
+};
+
+#define has_node_value(a) \
+ ((a)->type == NODE_TYPE_KEY || (a)->type == NODE_TYPE_VALUE)
+#define is_node_list(a) \
+ ((a)->type == NODE_TYPE_BLOCK || (a)->type == NODE_TYPE_LIST)
+
+typedef struct {
+ int type;
+ char *key;
+ void *value;
+} CONFIG_NODE;
+
+/* a = { x=y; y=z; }
+
+ node1: type = NODE_TYPE_BLOCK, key = "a", value = (GSList *) nodes
+ nodes: (node2, node3)
+ node2: type = NODE_TYPE_KEY, key = "x", value = (char *) "y"
+ node3: type = NODE_TYPE_KEY, key = "y", value = (char *) "z"
+
+ b = ( a, { b=c; d=e; } )
+
+ node1: type = NODE_TYPE_LIST, key = "b", value = (GSList *) nodes
+ nodes: (node2, node3)
+ node2: type = NODE_TYPE_VALUE, key = NULL, value = (char *) "a"
+ node4: type = NODE_TYPE_BLOCK, key = NULL, value = (GSList *) nodes2
+ nodes2: (node4, node5)
+ node4: type = NODE_TYPE_KEY, key = "b", value = (char *) "c"
+ node5: type = NODE_TYPE_KEY, key = "d", value = (char *) "e"
+
+ Comments node has key=NULL and value is the comment line. Empty lines are
+ also in comments so they won't be forgotten when the config file is
+ written.
+
+*/
+
+struct _config_rec {
+ char *fname;
+ int handle;
+ int create_mode;
+
+ char *last_error;
+ CONFIG_NODE *mainnode;
+ GHashTable *cache;
+
+ GScanner *scanner;
+
+ /* while writing to configuration file.. */
+ int tmp_indent_level; /* indentation position */
+ int tmp_last_lf; /* last character was a line feed */
+};
+
+typedef struct _config_rec CONFIG_REC;
+
+/* Open configuration. The file is created if it doesn't exist, unless
+ `create_mode' is -1. `fname' can be NULL if you just want to use
+ config_parse_data() */
+CONFIG_REC *config_open(const char *fname, int create_mode);
+/* Release all memory used by configuration */
+void config_close(CONFIG_REC *rec);
+/* Change file name of config file */
+void config_change_file_name(CONFIG_REC *rec, const char *fname, int create_mode);
+
+/* Parse configuration file */
+int config_parse(CONFIG_REC *rec);
+/* Parse configuration found from `data'. `input_name' is specifies the
+ "configuration name" which is displayed in error messages. */
+int config_parse_data(CONFIG_REC *rec, const char *data, const char *input_name);
+
+/* Write configuration file. Write to `fname' if it's not NULL.
+ If `create_mode' is -1, use the one that was given to config_open(). */
+int config_write(CONFIG_REC *rec, const char *fname, int create_mode);
+
+#define config_last_error(rec) \
+ (rec)->last_error
+
+/* Getting values
+
+ `section' is something like "maingroup/key/subkey", or with lists
+ "maingroup/(list/subkey"
+
+ `def' is returned if the value is not found. */
+char *config_get_str(CONFIG_REC *rec, const char *section, const char *key, const char *def);
+int config_get_int(CONFIG_REC *rec, const char *section, const char *key, int def);
+int config_get_bool(CONFIG_REC *rec, const char *section, const char *key, int def);
+
+/* Return value of key `value_key' from list item where `key' is `value' */
+const char *config_list_find(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key);
+/* Like config_list_find(), but return node instead of it's value */
+CONFIG_NODE *config_list_find_node(CONFIG_REC *rec, const char *section, const char *key, const char *value, const char *value_key);
+
+/* Setting values */
+int config_set_str(CONFIG_REC *rec, const char *section, const char *key, const char *value);
+int config_set_int(CONFIG_REC *rec, const char *section, const char *key, int value);
+int config_set_bool(CONFIG_REC *rec, const char *section, const char *key, int value);
+
+/* Handling the configuration directly with nodes -
+ useful when you need to read all values in a block/list. */
+CONFIG_NODE *config_node_find(CONFIG_NODE *node, const char *key);
+/* Find the section from node - if not found create it unless new_type is -1.
+ You can also specify in new_type if it's NODE_TYPE_LIST or NODE_TYPE_BLOCK */
+CONFIG_NODE *config_node_section(CONFIG_REC *rec, CONFIG_NODE *parent, const char *key, int new_type);
+/* Find the section with the whole path.
+ Create the path if necessary `create' is TRUE. */
+CONFIG_NODE *config_node_traverse(CONFIG_REC *rec, const char *section, int create);
+/* Get the value of keys `key' and `key_value' and put them to
+ `ret_key' and `ret_value'. Returns -1 if not found. */
+int config_node_get_keyvalue(CONFIG_NODE *node, const char *key, const char *value_key, char **ret_key, char **ret_value);
+
+char *config_node_get_str(CONFIG_NODE *parent, const char *key, const char *def);
+int config_node_get_int(CONFIG_NODE *parent, const char *key, int def);
+int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def);
+
+void config_node_set_str(CONFIG_NODE *parent, const char *key, const char *value);
+void config_node_set_int(CONFIG_NODE *parent, const char *key, int value);
+void config_node_set_bool(CONFIG_NODE *parent, const char *key, int value);
+
+/* add/change the value of the `key' */
+void config_node_set_str(CONFIG_NODE *parent, const char *key, const char *value);
+/* remove one node from block/list.
+ ..set_str() with value = NULL does the same. */
+void config_node_remove(CONFIG_NODE *parent, CONFIG_NODE *node);
+
+/* clear the entire configuration */
+void config_nodes_remove_all(CONFIG_REC *rec);
+
+#endif
diff --git a/src/lib-config/irssi-config.c b/src/lib-config/irssi-config.c
deleted file mode 100644
index 2750397c..00000000
--- a/src/lib-config/irssi-config.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- config.c : Functions for reading onfiguration 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 "../common.h"
-#include "../irc-base/irc-base.h"
-
-proplist_t cprop = NULL;
-
-gboolean config_get_bool(proplist_t prop, gchar *key, gboolean def)
-{
- proplist_t pkey, pvalue;
- gchar *value;
-
- if (prop == NULL)
- return def;
-
- pkey = PLMakeString(key);
- pvalue = PLGetDictionaryEntry(prop, pkey);
- PLRelease(pkey);
- if (pvalue == NULL) return def;
-
- value = PLGetString(pvalue);
- return toupper(*value) == 'T' || toupper(*value) == 'Y';
-}
-
-gint config_get_int(proplist_t prop, gchar *key, gint def)
-{
- proplist_t pkey, pvalue;
- gint num;
-
- if (prop == NULL)
- return def;
-
- pkey = PLMakeString(key);
- pvalue = PLGetDictionaryEntry(prop, pkey);
- PLRelease(pkey);
- if (pvalue == NULL) return def;
-
- return sscanf(PLGetString(pvalue), "%d", &num) != 1 ? def : num;
-}
-
-gchar *config_get_str(proplist_t prop, gchar *key, gchar *def)
-{
- proplist_t pkey, pvalue;
-
- if (prop == NULL)
- return def;
-
- pkey = PLMakeString(key);
- pvalue = PLGetDictionaryEntry(prop, pkey);
- PLRelease(pkey);
-
- return pvalue == NULL ? def : PLGetString(pvalue);
-}
-
-proplist_t config_get_prop(proplist_t prop, gchar *key)
-{
- proplist_t ret, pkey;
-
- pkey = PLMakeString(key);
- ret = PLGetDictionaryEntry(prop, pkey);
- PLRelease(pkey);
-
- return ret;
-}
-
-proplist_t config_make_dict(proplist_t prop, gchar *section)
-{
- proplist_t psect, pkey;
-
- pkey = PLMakeString(section);
- psect = PLMakeDictionaryFromEntries(NULL, NULL);
- prop = PLInsertDictionaryEntry(prop, pkey, psect);
- return prop;
-}
-
-proplist_t config_set_str(proplist_t prop, gchar *key, gchar *value)
-{
- proplist_t pkey, pvalue;
-
- pkey = PLMakeString(key); pvalue = PLMakeString(value);
- prop = PLInsertDictionaryEntry(prop, pkey, pvalue);
- PLRelease(pkey); PLRelease(pvalue);
- return prop;
-}
-
-proplist_t config_set_int(proplist_t prop, gchar *key, gint value)
-{
- proplist_t pkey, pvalue;
- gchar *strval;
-
- strval = g_strdup_printf("%d", value);
- pkey = PLMakeString(key); pvalue = PLMakeString(strval);
- prop = PLInsertDictionaryEntry(prop, pkey, pvalue);
- PLRelease(pkey); PLRelease(pvalue);
- g_free(strval);
- return prop;
-}
-
-proplist_t config_set_bool(proplist_t prop, gchar *key, gboolean value)
-{
- proplist_t pkey, pvalue;
-
- pkey = PLMakeString(key); pvalue = PLMakeString(value ? "Yes" : "No");
- prop = PLInsertDictionaryEntry(prop, pkey, pvalue);
- PLRelease(pkey); PLRelease(pvalue);
- return prop;
-}
-
-proplist_t config_clean_key(proplist_t prop, gchar *key)
-{
- proplist_t pkey;
-
- pkey = PLMakeString(key);
- PLRemoveDictionaryEntry(prop, pkey);
- PLRelease(pkey);
- return prop;
-}
-
-proplist_t config_section(proplist_t *prop, gchar *section)
-{
- proplist_t ret, pkey;
-
- pkey = PLMakeString(section);
- ret = PLGetDictionaryEntry(*prop, pkey);
- if (ret == NULL)
- {
- ret = PLMakeDictionaryFromEntries(NULL, NULL);
- *prop = PLInsertDictionaryEntry(*prop, pkey, ret);
- }
- PLRelease(pkey);
-
- return ret;
-}
-
-proplist_t config_list_section(proplist_t *prop, gchar *section)
-{
- proplist_t ret, pkey;
-
- pkey = PLMakeString(section);
- ret = PLGetDictionaryEntry(*prop, pkey);
- if (ret == NULL)
- {
- ret = PLMakeArrayFromElements(NULL);
- *prop = PLInsertDictionaryEntry(*prop, pkey, ret);
- }
- PLRelease(pkey);
-
- return ret;
-}
-
-gint config_list_find(proplist_t prop, gchar *key, gchar *value)
-{
- proplist_t item;
- gint num, max;
- gchar *ret;
-
- if (prop == NULL)
- return -1;
-
- max = PLGetNumberOfElements(prop);
- for (num = 0; num < max; num++)
- {
- item = PLGetArrayElement(prop, num);
- ret = config_get_str(item, key, NULL);
- if (ret != NULL && g_strcasecmp(ret, value) == 0)
- return num;
- }
-
- return -1;
-}
-
diff --git a/src/lib-config/irssi-config.h b/src/lib-config/irssi-config.h
deleted file mode 100644
index 2dc06c5e..00000000
--- a/src/lib-config/irssi-config.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef __IRSSI_CONFIG_H
-#define __IRSSI_CONFIG_H
-
-#include <proplist.h>
-
-extern proplist_t cprop;
-
-/* make proplist handling easier */
-gchar *config_get_str(proplist_t prop, gchar *key, gchar *def);
-gint config_get_int(proplist_t prop, gchar *key, gint def);
-gboolean config_get_bool(proplist_t prop, gchar *key, gboolean def);
-proplist_t config_get_prop(proplist_t prop, gchar *key);
-
-proplist_t config_set_str(proplist_t prop, gchar *key, gchar *value);
-proplist_t config_set_int(proplist_t prop, gchar *key, gint value);
-proplist_t config_set_bool(proplist_t prop, gchar *key, gboolean value);
-
-proplist_t config_section(proplist_t *prop, gchar *section);
-proplist_t config_list_section(proplist_t *prop, gchar *section);
-proplist_t config_make_dict(proplist_t prop, gchar *section);
-proplist_t config_clean_key(proplist_t prop, gchar *key);
-
-gint config_list_find(proplist_t prop, gchar *key, gchar *value);
-
-#endif
diff --git a/src/lib-config/module.h b/src/lib-config/module.h
new file mode 100644
index 00000000..22e0e7c7
--- /dev/null
+++ b/src/lib-config/module.h
@@ -0,0 +1,6 @@
+#include "common.h"
+#include "iconfig.h"
+
+/* private */
+int config_error(CONFIG_REC *rec, const char *msg);
+
diff --git a/src/lib-config/parse.c b/src/lib-config/parse.c
new file mode 100644
index 00000000..5171161c
--- /dev/null
+++ b/src/lib-config/parse.c
@@ -0,0 +1,335 @@
+/*
+ parse.c : irssi configuration - parse 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"
+
+int config_error(CONFIG_REC *rec, const char *msg)
+{
+ g_free_and_null(rec->last_error);
+ rec->last_error = g_strdup(msg);
+ return -1;
+}
+
+static int node_add_comment(CONFIG_NODE *parent, const char *str)
+{
+ CONFIG_NODE *node;
+
+ g_return_val_if_fail(parent != NULL, -1);
+
+ if (!is_node_list(parent))
+ return -1;
+
+ node = g_new0(CONFIG_NODE, 1);
+ node->type = NODE_TYPE_COMMENT;
+ node->value = str == NULL ? NULL : g_strdup(str);
+
+ parent->value = g_slist_append(parent->value, node);
+ return 0;
+}
+
+/* same as g_scanner_get_next_token() except skips and reads the comments */
+static void config_parse_get_token(GScanner *scanner, CONFIG_NODE *node)
+{
+ int prev_empty = FALSE;
+
+ for (;;) {
+ g_scanner_get_next_token(scanner);
+
+ if (scanner->token == G_TOKEN_COMMENT_SINGLE)
+ node_add_comment(node, scanner->value.v_string);
+ else if (scanner->token == '\n') {
+ if (prev_empty) node_add_comment(node, NULL);
+ } else {
+ if (scanner->token == G_TOKEN_INT) {
+ scanner->token = G_TOKEN_STRING;
+#undef g_strdup_printf /* This is free'd by GLib itself */
+ scanner->value.v_string = g_strdup_printf("%lu", scanner->value.v_int);
+#ifdef MEM_DEBUG
+#define g_strdup_printf ig_strdup_printf
+#endif
+ }
+ break;
+ }
+
+ prev_empty = TRUE;
+ }
+}
+
+/* same as g_scanner_peek_next_token() except skips and reads the comments */
+static void config_parse_peek_token(GScanner *scanner, CONFIG_NODE *node)
+{
+ int prev_empty = FALSE;
+
+ for (;;) {
+ g_scanner_peek_next_token(scanner);
+
+ if (scanner->next_token == G_TOKEN_COMMENT_SINGLE)
+ node_add_comment(node, scanner->next_value.v_string);
+ else if (scanner->next_token == '\n') {
+ if (prev_empty) node_add_comment(node, NULL);
+ } else
+ break;
+
+ prev_empty = TRUE;
+ g_scanner_get_next_token(scanner);
+ }
+}
+
+/* get optional token, optionally warn if it's missing */
+static void config_parse_warn_missing(CONFIG_REC *rec, CONFIG_NODE *node, int expected_token, int print_warning)
+{
+ config_parse_peek_token(rec->scanner, node);
+ if (rec->scanner->next_token == expected_token) {
+ g_scanner_get_next_token(rec->scanner);
+ return;
+ }
+
+ if (print_warning)
+ g_scanner_warn(rec->scanner, "Warning: missing '%c'", expected_token);
+}
+
+static void config_parse_loop(CONFIG_REC *rec, CONFIG_NODE *node, int expect);
+
+static int config_parse_symbol(CONFIG_REC *rec, CONFIG_NODE *node)
+{
+ CONFIG_NODE *newnode;
+ int print_warning;
+ char *key, last_char;
+
+ g_return_val_if_fail(rec != NULL, G_TOKEN_ERROR);
+ g_return_val_if_fail(node != NULL, G_TOKEN_ERROR);
+
+ config_parse_get_token(rec->scanner, node);
+
+ last_char = node->type == NODE_TYPE_LIST ? ',' : ';';
+
+ /* key */
+ key = NULL;
+ if (node->type != NODE_TYPE_LIST &&
+ (rec->scanner->token == G_TOKEN_STRING)) {
+ key = g_strdup(rec->scanner->value.v_string);
+
+ config_parse_get_token(rec->scanner, node);
+ if (rec->scanner->token != '=')
+ return '=';
+
+ config_parse_get_token(rec->scanner, node);
+ }
+
+ switch (rec->scanner->token) {
+ case G_TOKEN_STRING:
+ /* value */
+ config_node_set_str(node, key, rec->scanner->value.v_string);
+ g_free_not_null(key);
+
+ print_warning = TRUE;
+ if (node->type == NODE_TYPE_LIST) {
+ /* if it's last item it doesn't need comma */
+ config_parse_peek_token(rec->scanner, node);
+ if (rec->scanner->next_token == ')')
+ print_warning = FALSE;
+ }
+
+ config_parse_warn_missing(rec, node, last_char, print_warning);
+ break;
+
+ case '{':
+ /* block */
+ if (key == NULL && node->type != NODE_TYPE_LIST)
+ return G_TOKEN_ERROR;
+
+ newnode = config_node_section(rec, node, key, NODE_TYPE_BLOCK);
+ config_parse_loop(rec, newnode, '}');
+ g_free_not_null(key);
+
+ config_parse_get_token(rec->scanner, node);
+ if (rec->scanner->token != '}')
+ return '}';
+
+ config_parse_warn_missing(rec, node, last_char, FALSE);
+ break;
+
+ case '(':
+ /* list */
+ if (key == NULL)
+ return G_TOKEN_ERROR;
+ newnode = config_node_section(rec, node, key, NODE_TYPE_LIST);
+ config_parse_loop(rec, newnode, ')');
+ g_free_not_null(key);
+
+ config_parse_get_token(rec->scanner, node);
+ if (rec->scanner->token != ')')
+ return ')';
+
+ config_parse_warn_missing(rec, node, last_char, FALSE);
+ break;
+
+ default:
+ /* error */
+ g_free_not_null(key);
+ return G_TOKEN_STRING;
+ }
+
+ return G_TOKEN_NONE;
+}
+
+static void config_parse_loop(CONFIG_REC *rec, CONFIG_NODE *node, int expect)
+{
+ int expected_token;
+
+ g_return_if_fail(rec != NULL);
+ g_return_if_fail(node != NULL);
+
+ do {
+ expected_token = config_parse_symbol(rec, node);
+ if (expected_token != G_TOKEN_NONE) {
+ if (expected_token == G_TOKEN_ERROR)
+ expected_token = G_TOKEN_NONE;
+ g_scanner_unexp_token(rec->scanner, expected_token, NULL, "symbol", NULL, NULL, TRUE);
+ }
+
+ config_parse_peek_token(rec->scanner, node);
+ } while (rec->scanner->next_token != expect &&
+ rec->scanner->next_token != G_TOKEN_EOF);
+}
+
+static void config_parse_error_func(GScanner *scanner, char *message, int is_error)
+{
+ CONFIG_REC *rec = scanner->user_data;
+ char *old;
+
+ old = rec->last_error;
+ rec->last_error = g_strdup_printf("%s%s:%d: %s%s\n",
+ old == NULL ? "" : old,
+ scanner->input_name, scanner->line,
+ is_error ? "error: " : "",
+ message);
+ g_free_not_null(old);
+}
+
+void config_parse_init(CONFIG_REC *rec, const char *name)
+{
+ GScanner *scanner;
+
+ g_free_and_null(rec->last_error);
+ config_nodes_remove_all(rec);
+
+ rec->scanner = scanner = g_scanner_new(NULL);
+ scanner->config->skip_comment_single = FALSE;
+ scanner->config->cset_skip_characters = " \t";
+ scanner->config->scan_binary = FALSE;
+ scanner->config->scan_octal = FALSE;
+ scanner->config->scan_float = FALSE;
+ scanner->config->scan_string_sq = TRUE;
+ scanner->config->scan_string_dq = TRUE;
+ scanner->config->scan_identifier_1char = TRUE;
+ scanner->config->identifier_2_string = TRUE;
+
+ scanner->user_data = rec;
+ scanner->input_name = name;
+ scanner->msg_handler = (GScannerMsgFunc) config_parse_error_func;
+}
+
+/* Parse configuration file */
+int config_parse(CONFIG_REC *rec)
+{
+ g_return_val_if_fail(rec != NULL, -1);
+ g_return_val_if_fail(rec->fname != NULL, -1);
+
+ rec->handle = open(rec->fname, O_RDONLY);
+ if (rec->handle == -1)
+ return config_error(rec, g_strerror(errno));
+
+ config_parse_init(rec, rec->fname);
+ g_scanner_input_file(rec->scanner, rec->handle);
+ config_parse_loop(rec, rec->mainnode, G_TOKEN_EOF);
+ g_scanner_destroy(rec->scanner);
+
+ close(rec->handle);
+ rec->handle = -1;
+
+ return rec->last_error == NULL ? 0 : -1;
+}
+
+/* Parse configuration found from `data'. `input_name' is specifies the
+ "configuration name" which is displayed in error messages. */
+int config_parse_data(CONFIG_REC *rec, const char *data, const char *input_name)
+{
+ config_parse_init(rec, input_name);
+ g_scanner_input_text(rec->scanner, data, strlen(data));
+ config_parse_loop(rec, rec->mainnode, G_TOKEN_EOF);
+ g_scanner_destroy(rec->scanner);
+
+ return rec->last_error == NULL ? 0 : -1;
+}
+
+/* Open configuration. The file is created if it doesn't exist, unless
+ `create_mode' is -1. `fname' can be NULL if you just want to use
+ config_parse_data() */
+CONFIG_REC *config_open(const char *fname, int create_mode)
+{
+ CONFIG_REC *rec;
+ int f;
+
+ if (fname != NULL) {
+ f = open(fname, O_RDONLY | (create_mode != -1 ? O_CREAT : 0), create_mode);
+ if (f == -1) return NULL;
+ close(f);
+ }
+
+ rec = g_new0(CONFIG_REC, 1);
+ rec->fname = fname == NULL ? NULL : g_strdup(fname);
+ rec->handle = -1;
+ rec->create_mode = create_mode;
+ rec->mainnode = g_new0(CONFIG_NODE, 1);
+ rec->mainnode->type = NODE_TYPE_BLOCK;
+ rec->cache = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal);
+
+ return rec;
+}
+
+/* Release all memory used by configuration */
+void config_close(CONFIG_REC *rec)
+{
+ g_return_if_fail(rec != NULL);
+
+ config_nodes_remove_all(rec);
+ g_free(rec->mainnode);
+
+ if (rec->handle != -1) close(rec->handle);
+ g_hash_table_foreach(rec->cache, (GHFunc) g_free, NULL);
+ g_hash_table_destroy(rec->cache);
+ g_free_not_null(rec->last_error);
+ g_free(rec->fname);
+ g_free(rec);
+}
+
+/* Change file name of config file */
+void config_change_file_name(CONFIG_REC *rec, const char *fname, int create_mode)
+{
+ g_return_if_fail(rec != NULL);
+ g_return_if_fail(fname != NULL);
+
+ g_free_not_null(rec->fname);
+ rec->fname = g_strdup(fname);
+
+ if (create_mode != -1)
+ rec->create_mode = create_mode;
+}
diff --git a/src/lib-config/set.c b/src/lib-config/set.c
new file mode 100644
index 00000000..49457576
--- /dev/null
+++ b/src/lib-config/set.c
@@ -0,0 +1,122 @@
+/*
+ set.c : irssi configuration - change settings in memory
+
+ 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"
+
+void config_node_remove(CONFIG_NODE *parent, CONFIG_NODE *node)
+{
+ g_return_if_fail(parent != NULL);
+ g_return_if_fail(node != NULL);
+
+ parent->value = g_slist_remove(parent->value, node);
+
+ switch (node->type) {
+ case NODE_TYPE_KEY:
+ case NODE_TYPE_VALUE:
+ case NODE_TYPE_COMMENT:
+ g_free_not_null(node->value);
+ break;
+ case NODE_TYPE_BLOCK:
+ case NODE_TYPE_LIST:
+ while (node->value != NULL)
+ config_node_remove(node, ((GSList *) node->value)->data);
+ break;
+ }
+ g_free_not_null(node->key);
+ g_free(node);
+}
+
+void config_nodes_remove_all(CONFIG_REC *rec)
+{
+ g_return_if_fail(rec != NULL);
+
+ while (rec->mainnode->value != NULL)
+ config_node_remove(rec->mainnode, ((GSList *) rec->mainnode->value)->data);
+}
+
+
+void config_node_set_str(CONFIG_NODE *parent, const char *key, const char *value)
+{
+ CONFIG_NODE *node;
+ int no_key;
+
+ g_return_if_fail(parent != NULL);
+
+ no_key = key == NULL;
+ node = no_key ? NULL : config_node_find(parent, key);
+
+ if (value == NULL) {
+ /* remove the key */
+ if (node != NULL) config_node_remove(parent, node);
+ return;
+ }
+
+ if (node != NULL)
+ g_free(node->value);
+ else {
+ node = g_new0(CONFIG_NODE, 1);
+ parent->value = g_slist_append(parent->value, node);
+
+ node->type = no_key ? NODE_TYPE_VALUE : NODE_TYPE_KEY;
+ node->key = no_key ? NULL : g_strdup(key);
+ }
+
+ node->value = g_strdup(value);
+}
+
+void config_node_set_int(CONFIG_NODE *parent, const char *key, int value)
+{
+ char str[MAX_INT_STRLEN];
+
+ g_snprintf(str, sizeof(str), "%d", value);
+ return config_node_set_str(parent, key, str);
+}
+
+void config_node_set_bool(CONFIG_NODE *parent, const char *key, int value)
+{
+ return config_node_set_str(parent, key, value ? "yes" : "no");
+}
+
+int config_set_str(CONFIG_REC *rec, const char *section, const char *key, const char *value)
+{
+ CONFIG_NODE *parent;
+
+ g_return_val_if_fail(rec != NULL, -1);
+ g_return_val_if_fail(section != NULL, -1);
+
+ parent = config_node_traverse(rec, section, TRUE);
+ if (parent == NULL) return -1;
+
+ config_node_set_str(parent, key, value);
+ return 0;
+}
+
+int config_set_int(CONFIG_REC *rec, const char *section, const char *key, int value)
+{
+ char str[MAX_INT_STRLEN];
+
+ g_snprintf(str, sizeof(str), "%d", value);
+ return config_set_str(rec, section, key, str);
+}
+
+int config_set_bool(CONFIG_REC *rec, const char *section, const char *key, int value)
+{
+ return config_set_str(rec, section, key, value ? "yes" : "no");
+}
diff --git a/src/lib-config/write.c b/src/lib-config/write.c
new file mode 100644
index 00000000..30a41fd4
--- /dev/null
+++ b/src/lib-config/write.c
@@ -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;
+}