1
0
mirror of https://github.com/irssi/irssi.git synced 2024-12-04 14:46:39 -05: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:
Timo Sirainen 2000-04-14 11:27:14 +00:00 committed by cras
parent 5fb98c2210
commit 53b248f6de
23 changed files with 1532 additions and 400 deletions

45
NEWS
View File

@ -1,3 +1,48 @@
v0.7.90 2000-04-xx Timo Sirainen <tss@iki.fi>
* 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 `const' 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
v0.7.28 2000-03-11 Timo Sirainen <tss@iki.fi>
+ irssi-text: New improved "text widget". It takes less memory and

1
README
View File

@ -68,7 +68,6 @@ make install
Configure can use these parameters (all of these defaults to yes):
--with-proplist=dir Specify libPropList directory
--with-servertest Build test irc server which you can use to try crash
irc clients
--with-socks Build with socks library

38
TODO
View File

@ -1,3 +1,41 @@
- notifylist ei toimi, /ALIAS, /IGNORE
- g_strndup() !!!!! auttaa varmaan vaikka missä
- server-specific source_host
- curses sijainti jotain rikkoo
Day changed to 30-26 2000
[00:10] .. [00:20]
- "away mode changed"
- dcc on särki
- /server +blah tekis uuden ikkunan.
[17:37] -!- Magi [^magi@magi.yok.utu.fi] has quit IRC [Killed (Uni-Stuttgart.DE ((^magi@magi.yok.utu.fi)GMD.DE <- (.@vipek-IV.vip.net.pl)*.pl[ircd@hub.irc.pl]))]
:Magi!^magi@magi.yok.utu.fi QUIT :
Kalled (gart.DE ((^magi@magi.yok.utu.fi)GMD.DE <- (.@vipek-IV.vip.net.pl)*.pl[ircd@hub.irc.pl]))
~cras@0:0:0:0:0:ffff:
-teemoihin tee jotain pientä selitystä edes!
- alt+left/right vaihtaa kanavaa..
- checkkaa miten ne autojoin_channelsit nyt meni.. että vapautetaan ym.
- raiseta ikkuna jossa on tekstiä mut ei over aktiivisen päälle
- line-split.c: varmista että se 64k limitti toimii eikä esim. kaada!
- vaihda /set nimet järkevimmiksi
- optio että vaihtaa automaagisesti sinne autocreatettuun ikkunaan. tyhjennä
entry (laita historyyn) ettei uusi rivi mene query ikkunaan suoraan vahingossa.
- "älä näytä n. sekunttia pienempää lagia"
- cmd line switchi source hostille
- autojoinikaan ei tunnu oikein pelaavan..? vain kun vaihtuu serveri
- quit näytettäisiin vaan yhdessä ikkunassa.
- /exec
- optionaalisesti voisi niitä logeja ajella siinä toisessa irssisessiossa
- msg:issä kun tulee sitä away viestiä näyttäisi vaan kerran..
- logrotate
- ignoroida tietyt ctcpt. regexpit. ignorettaa tekstiä ..
egopallo/#tv.fi CANAL|MAFIA|...
- autorun.ircnet
- flood protectionia paremmaksi
- bottipluginiin tms. .. channel not available tms. rejoini
*** Bugs

138
config
View File

@ -1,72 +1,70 @@
{
setupservers = (
{server = irc.funet.fi;ircnet = IRCNet;port = 6667;autoconnect = No;},
{server = irc.efnet.net;ircnet = EFNet;port = 6667;autoconnect = No;},
{server = irc.undernet.net;ircnet = Undernet;port = 6667;autoconnect = No;},
{server = irc.dal.net;ircnet = DALNet;port = 6667;autoconnect = No;},
{server = irc.openprojects.net;ircnet = OPN;port = 6667;autoconnect = No;},
{server = irc.ptlink.net;ircnet = PTlink;port = 6667;autoconnect = No;},
{server = irc.multichat.org;ircnet = Multichat;port = 6667;autoconnect = No;}
);
setupservers = (
{server = "irc.funet.fi"; ircnet = IRCNet; port = 6667; autoconnect = No;},
{server = "irc.efnet.net"; ircnet = EFNet; port = 6667; autoconnect = No;},
{server = "irc.undernet.net"; ircnet = Undernet; port = 6667; autoconnect = No;},
{server = "irc.dal.net"; ircnet = DALNet; port = 6667; autoconnect = No;},
{server = "irc.openprojects.net"; ircnet = OPN; port = 6667; autoconnect = No;},
{server = "irc.ptlink.net"; ircnet = PTlink; port = 6667; autoconnect = No;},
{server = "irc.multichat.org"; ircnet = Multichat; port = 6667; autoconnect = No;}
);
ircnets = (
{name = IRCNet;max_kicks = 4;max_modes = 3;max_msgs = 3;},
{name = EFNet;max_kicks = 4;max_modes = 4;max_msgs = 3;},
{name = Undernet;max_kicks = 4;max_modes = 3;max_msgs = 3;},
{name = DALNet;max_kicks = 4;max_modes = 6;max_msgs = 3;},
{name = OPN;max_kicks = 1;max_modes = 6;max_msgs = 100;},
{name = PTlink;max_kicks = 1;max_modes = 6;max_msgs = 100;},
{name = Multichat;max_kicks = 1;max_modes = 6;max_msgs = 100;}
);
ircnets = (
{name = IRCNet; max_kicks = 4; max_modes = 3; max_msgs = 5;},
{name = EFNet; max_kicks = 4; max_modes = 4; max_msgs = 3;},
{name = Undernet; max_kicks = 4; max_modes = 3; max_msgs = 3;},
{name = DALNet; max_kicks = 4; max_modes = 6; max_msgs = 3;},
{name = OPN; max_kicks = 1; max_modes = 6; max_msgs = 100;},
{name = PTlink; max_kicks = 1; max_modes = 6; max_msgs = 100;},
{name = Multichat; max_kicks = 1; max_modes = 6; max_msgs = 100;}
);
channels = (
{
name = "#irssi";
ircnet = ircnet;
autojoin = No;
}
);
aliases = (
{alias = J;command = "/join &1";},
{alias = LEAVE;command = "/part &1";},
{alias = BYE;command = "/quit &1";},
{alias = WI;command = "/whois &1";},
{alias = WII;command = "/whois %1 %1";},
{alias = WW;command = "/whowas &1";},
{alias = W;command = "/who *";},
{alias = N;command = "/names *";},
{alias = M;command = "/msg &1";},
{alias = T;command = "/topic &1";},
{alias = C;command = "/clear";},
{alias = CL;command = "/clear";},
{alias = K;command = "/kick &1";},
{alias = KB;command = "/kickban &1";},
{alias = KN;command = "/knockout &1";},
{alias = B;command = "/ban &1";},
{alias = UB;command = "/unban &1";},
{alias = IG;command = "/ignore &1";},
{alias = UNIG;command = "/unignore &1";},
{alias = SB;command = "/scrollback &1";},
{alias = UMODE;command = "/mode %n &1";}
);
popups = (
{label = "<MULTICOMMA>Whois";command = "/whois %s";},
{label = "DCC Send File";command = "/dcc send %s";},
{label = "Open DCC Chat";command = "/dcc chat %s";},
{label = Query;command = "/query %s";},
{label = "<MENU><OP>";command = "Op";},
{label = "<MULTI>Op";command = "/op %s";},
{label = "<MULTI>Deop";command = "/deop %s";},
{label = "<MULTI>Voice";command = "/voice %s";},
{label = "<MULTI>Devoice";command = "/devoice %s";},
{label = "<KICK>Kick";command = "/kick %s %s";},
{label = "<MULTI>Ban";command = "/ban %s";},
{label = "<KICK>Kick+ban";command = "/kickban %s %s";},
{label = "<KICK>Knockout";command = "/knockout %s %s";},
{label = "</MENU>";command = "";},
{label = "<MENU>";command = "CTCP";},
{label = Ping;command = "/ping %s";},
{label = Version;command = "/ver %s";},
{label = "</MENU>";command = "";}
);
}
channels = (
{
name = "#irssi";
ircnet = ircnet;
autojoin = No;
}
);
aliases = (
{alias = J; command = "join";},
{alias = LEAVE; command = "part";},
{alias = BYE; command = "quit";},
{alias = WI; command = "whois";},
{alias = WII; command = "whois $0 $0";},
{alias = WW; command = "whowas";},
{alias = W; command = "who $C";},
{alias = N; command = "names $C";},
{alias = M; command = "msg";},
{alias = T; command = "topic";},
{alias = C; command = "clear";},
{alias = CL; command = "clear";},
{alias = K; command = "kick";},
{alias = KB; command = "kickban";},
{alias = KN; command = "knockout";},
{alias = B; command = "ban";},
{alias = UB; command = "unban";},
{alias = IG; command = "ignore";},
{alias = UNIG; command = "unignore";},
{alias = SB; command = "scrollback";},
{alias = UMODE; command = "mode $N";}
);
popups = (
{label = "<MULTICOMMA>Whois"; command = "/whois %s";},
{label = "DCC Send File"; command = "/dcc send %s";},
{label = "Open DCC Chat"; command = "/dcc chat %s";},
{label = Query; command = "/query %s";},
{label = "<MENU><OP>"; command = "Op";},
{label = "<MULTI>Op"; command = "/op %s";},
{label = "<MULTI>Deop"; command = "/deop %s";},
{label = "<MULTI>Voice"; command = "/voice %s";},
{label = "<MULTI>Devoice"; command = "/devoice %s";},
{label = "<KICK>Kick"; command = "/kick %s %s";},
{label = "<MULTI>Ban"; command = "/ban %s";},
{label = "<KICK>Kick+ban"; command = "/kickban %s %s";},
{label = "<KICK>Knockout"; command = "/knockout %s %s";},
{label = "</MENU>"; command = "";},
{label = "<MENU>"; command = "CTCP";},
{label = Ping; command = "/ping %s";},
{label = Version; command = "/ver %s";},
{label = "</MENU>"; command = "";}
);

View File

@ -19,10 +19,6 @@ AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h libintl.h)
GNOME_INIT
GNOME_SUPPORT_CHECKS
AC_ARG_WITH(proplist,
[ --with-proplist Specify libPropList location],
proplist_dir=$withval)
AC_ARG_WITH(socks,
[ --with-socks Build with socks support],
if test x$withval = xyes; then
@ -228,29 +224,6 @@ AC_DEFINE(socklen_t, int, Define to 'int' if <sys/socket.h> doesn't define.)
fi
AC_MSG_RESULT($irssi_cv_type_socklen_t)
dnl **
dnl ** check for libPropList
dnl **
if test "x$proplist_dir" = "x"; then
proplib=
else
proplib=-L$proplist_dir/lib
fi
AC_CHECK_LIB(PropList, PLSave, [
PROG_LIBS="$PROG_LIBS $proplib -lPropList"
if test "x$proplist_dir" != "x"; then
CFLAGS="$CFLAGS -I$proplist_dir/include"
fi
], [
echo "ERROR: Irssi needs libPropList for configuration file handling."
echo "Go get it from http://xlife.dhs.org/irssi/download.php"
AC_ERROR(["libPropList not found"])
], $PROG_LIBS $proplib -lPropList)
dnl **
dnl ** check for socks
dnl **

View File

@ -318,20 +318,24 @@ LAST [-pub -msgs...] <text>
** Configuration
SET [key [=value / [key [key..]]
SET [key [value]]
Get/set configuration
Get/set configuration. 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, or if ON or OFF parameter is given
it will work just like /SET.
SAVE
Save configuration to disk.
ALIAS, UNALIAS <alias> [command]
Set/remove alias, /unalias is the same as /alias without command
These codes are extracted in commands:
%0 : name of alias
%1, %2, %3 .. : %th word
&1, &2, &3 .. : &th word + the rest of the text after it
%c : channel name
Typing extra / before /command (//command) ignores any aliases
IGNORE, UNIGNORE <mask> [level [level..]]

View File

@ -14,13 +14,6 @@
%8 Reverse on/off
%9 %_ Bold on/off
%: Insert newline
%| Marks the indentation position
%% A single %
parameter handling:
$[30]1 prints parameter 1 cut/padded to 30 chars
$[!30]1 prints parameter 1 padded to min. 30 chars
$[-30]1 prints parameter 1 right aligned
$[30?]1 prints parameter 1 padded with '?' characters
$[30.0]1 prints parameter 1 padded with '0' characters
%| marks the indentation position.

View File

@ -243,7 +243,7 @@ Server server_find_tag(tag)
Server server_find_ircnet(ircnet)
Find first server that is in `ircnet'
Channel channel_find_any(channel)
Channel channel_find(channel)
Find `channel' from any server
Channel Server::channel_find_level(level)
@ -436,6 +436,7 @@ Nick::values()
"nick" - Plain nick
"host" - Host (blah@there.org)
"name" - Real name
"hops" - Hop count to the server nick is using
"op", "voice", "gone", "ircop" - 1 or 0
"last_check" - timestamp when last checked gone/ircop status.
"send_massjoin" - Waiting to be sent in a "massjoin" signal - 1 or 0

View File

@ -42,7 +42,7 @@ channels.c:
"channel destroyed", CHANNEL_REC
"channel name changed", CHANNEL_REC
"channel topic changed", CHANNEL_REC
"channel server changed", CHANNEL_REC
"channel server changed", CHANNEL_REC, SERVER_REC *oldserver
"channel query", CHANNEL_REC
"channel wholist", CHANNEL_REC

104
docs/SPECIAL_VARS Normal file
View File

@ -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 File

@ -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 File

@ -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 File

@ -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 File

@ -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 File

@ -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

256
src/lib-config/get.c Normal file
View File

@ -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;
}

136
src/lib-config/iconfig.h Normal file
View File

@ -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 File

@ -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;
}

View File

@ -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

6
src/lib-config/module.h Normal file
View File

@ -0,0 +1,6 @@
#include "common.h"
#include "iconfig.h"
/* private */
int config_error(CONFIG_REC *rec, const char *msg);

335
src/lib-config/parse.c Normal file
View File

@ -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;
}

122
src/lib-config/set.c Normal file
View File

@ -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");
}

336
src/lib-config/write.c Normal file
View 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;
}