View
@@ -0,0 +1,104 @@
NOTE: This is just a slightly modified file taken from EPIC's help.
'!' at start of the line means that the feature doesn't work yet..
Special Variables and Expandos
Irssi supports a number of reserved, dynamic variables, sometimes
referred to as expandos. They are special in that the client is
constantly updating their values automatically. There are also
numerous variable modifiers available.
Modifier Description
$variable A normal variable, expanding to the first match of:
| 1) an internal SET variable
| 2) an environment variable
$[num]variable Expands to the variables value, with 'num' width. If
| the number is negative, the value is right-aligned.
| The value is padded to meet the width with the
| character given after number (default is space).
| The value is truncated to specified width unless
| '!' character precedes the number.
$#variable Expands to the number of words in $variable. If $variable
| is omitted, it assumes $*
$@variable Expands to the number of characters in $variable. if
| $variable is omitted, it assumes $*
$($subvariable) This is somewhat similar to a pointer, in that the
| value of $subvar is taken as the name of the
| variable to expand to. Nesting is allowed.
${expression} Permits the value to be embedded in another string
| unambiguously.
! $!history! Expands to a matching entry in the client's command
| history, wildcards allowed.
! $"some text" Uses 'text' as an input prompt, and returns whatever
| is typed next. This usage is deprecated, use the
| INPUT command instead.
! $'some text' Same as $"text" except that it only returns the first
| next typed character.
Whenever an alias is called, these expandos are set to the arguments passed
to it. If none of these expandos are used in the alias, or the $() form
shown above, any arguments passed will automatically be appended to the last
command in the alias.
Expando Description
$* expands to all arguments passed to an alias
$n expands to argument 'n' passed to an alias (counting from zero)
$n-m expands to arguments 'n' through 'm' passed to an alias
$n- expands to all arguments from 'n' on passed to an alias
$-m expands to all arguments up to 'm' passed to an alias
$~ expands to the last argument passed to an alias
These variables are set and updated dynamically by the client. The case of
$A .. $Z is important.
Variable Description
! $, last person who sent you a MSG
! $. last person to whom you sent a MSG
! $: last person to join a channel you are on
! $; last person to send a public message to a channel you are on
$A text of your AWAY message, if any
! $B body of last MSG you sent
$C current channel
! $D last person that NOTIFY detected a signon for
! $E idle time
! $F time client was started, $time() format
! $H current server numeric being processed
! $I channel you were last INVITEd to
$J client version text string
$K current value of CMDCHARS
! $L current contents of the input line
$M modes of current channel, if any
$N current nickname
! $O value of STATUS_OPER if you are an irc operator
$P if you are a channel operator in $C, expands to a '@'
$Q nickname of whomever you are QUERYing
! $R version of current server
$S current server name
$T target of current input (channel or QUERY nickname)
! $U value of cutbuffer
! $V client release date (numeric version string)
$W current working directory
! $X your /userhost $N address (user@host)
$Y value of REALNAME
$Z time of day (hh:mm)
$$ a literal '$'
For example, assume you have the following alias:
alias blah msg $D Hi there!
If /blah is passed any arguments, they will automatically be appended to the
MSG text. For example:
/blah oops /* command as entered */
"Hi there! oops" /* text sent to $D */
Another useful form is ${}. In general, variables can be embedded inside
strings without problems, assuming the surrounding text could not be
misinterpreted as part of the variable name. This form guarantees that
surrounding text will not affect the expression's return value.
/eval echo foo$Nfoo /* breaks, looks for $nfoo */
/eval echo foo${N}foo /* ${N} returns current nickname */
fooYourNickfoo /* returned by above command */
View
@@ -1,4 +1,4 @@
# $Revision: 1.7 $, $Date: 2000/02/25 17:03:15 $
# $Revision: 1.8 $, $Date: 2000/04/14 11:27:02 $
Name: irssi
Version: @VERSION@
Release: 1
@@ -10,7 +10,6 @@ Group: Applications/Communications
Group(pl): Aplikacje/Komunikacja
URL: http://xlife.dhs.org/irssi/
Source0: http://xlife.dhs.org/irssi/files/%{name}-%{version}.tar.gz
BuildRequires: libPropList
BuildRequires: glib-devel
BuildRequires: ncurses-devel
BuildRequires: imlib-devel
@@ -63,7 +62,6 @@ LDFLAGS="-s -L/usr/X11R6/lib"; export LDFLAGS
--with-imlib \
--enable-ipv6 \
--with-textui=ncurses \
--with-proplist \
--without-socks \
--with-plugins
make
@@ -110,6 +108,56 @@ rm -rf $RPM_BUILD_ROOT
All below listed persons can be reached on <cvs_login>@pld.org.pl
$Log: irssi.spec.in,v $
Revision 1.8 2000/04/14 11:27:02 cras
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
Revision 1.7 2000/02/25 17:03:15 cras
Irssi 0.7.27 released.
View
@@ -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)
View
@@ -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
View
@@ -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,
View
@@ -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
View
@@ -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;
}
View
@@ -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
View

This file was deleted.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
View
@@ -0,0 +1,6 @@
#include "common.h"
#include "iconfig.h"
/* private */
int config_error(CONFIG_REC *rec, const char *msg);
View
@@ -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;
}
View
@@ -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");
}
View
@@ -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;
}