..adding new files..

git-svn-id: http://svn.irssi.org/repos/irssi/trunk@171 dbcabf3a-b0e7-0310-adc4-f8d773084564
This commit is contained in:
Timo Sirainen 2000-04-26 08:03:38 +00:00 committed by cras
commit c95034c6de
206 changed files with 31247 additions and 0 deletions

59
src/core/Makefile.am Normal file
View file

@ -0,0 +1,59 @@
noinst_LTLIBRARIES = libcore.la
INCLUDES = \
$(GLIB_CFLAGS) \
-DSYSCONFDIR=\""$(sysconfdir)"\" \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/core
if BUILD_MEMDEBUG
memdebug_src=memdebug.c
else
memdebug_src=
endif
libcore_la_SOURCES = \
args.c \
commands.c \
core.c \
levels.c \
line-split.c \
log.c \
$(memdebug_src) \
misc.c \
modules.c \
net-disconnect.c \
net-nonblock.c \
network.c \
pidwait.c \
rawlog.c \
server.c \
server-redirect.c \
settings.c \
signals.c \
special-vars.c
noinst_HEADERS = \
args.h \
commands.h \
core.h \
levels.h \
line-split.h \
log.h \
memdebug.h \
misc.h \
module.h \
modules.h \
net-disconnect.h \
net-nonblock.h \
network.h \
pidwait.h \
rawlog.h \
server.h \
server-redirect.h \
settings.h \
signals.h \
special-vars.h
EXTRA_DIST = \
memdebug.c

60
src/core/args.c Normal file
View file

@ -0,0 +1,60 @@
/*
args.c : irssi
Copyright (C) 1999-2000 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"
#include "args.h"
GArray *iopt_tables = NULL;
void args_register(struct poptOption *options)
{
if (iopt_tables == NULL)
iopt_tables = g_array_new(TRUE, TRUE, sizeof(struct poptOption));
while (options->longName != NULL || options->shortName != '\0' || options->arg != NULL) {
g_array_append_val(iopt_tables, *options);
options = options+1;
}
}
void args_execute(int argc, char *argv[])
{
poptContext con;
int nextopt;
if (iopt_tables == NULL)
return;
con = poptGetContext(PACKAGE, argc, argv, (struct poptOption *) (iopt_tables->data), 0);
poptReadDefaultConfig(con, TRUE);
while ((nextopt = poptGetNextOpt(con)) > 0) ;
if (nextopt != -1) {
printf(_("Error on option %s: %s.\nRun '%s --help' to see a full list of available command line options.\n"),
poptBadOption(con, 0),
poptStrerror(nextopt),
argv[0]);
exit(1);
}
g_array_free(iopt_tables, TRUE);
iopt_tables = NULL;
}

15
src/core/args.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef __ARGS_H
#define __ARGS_H
#ifdef HAVE_POPT_H
# include <popt.h>
#else
# include "lib-popt/popt.h"
#endif
extern GArray *iopt_tables;
void args_register(struct poptOption *options);
void args_execute(int argc, char *argv[]);
#endif

462
src/core/commands.c Normal file
View file

@ -0,0 +1,462 @@
/*
commands.c : irssi
Copyright (C) 1999-2000 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"
#include "modules.h"
#include "signals.h"
#include "commands.h"
#include "misc.h"
#include "server.h"
#include "server-redirect.h"
#include "special-vars.h"
#include "lib-config/iconfig.h"
#include "settings.h"
#define alias_find(alias) \
iconfig_get_str("aliases", alias, NULL)
GSList *commands;
char *current_command;
static GSList *cmdget_funcs;
static int signal_default_command;
void command_bind(const char *cmd, const char *category, SIGNAL_FUNC func)
{
COMMAND_REC *rec;
char *str;
g_return_if_fail(cmd != NULL);
rec = g_new0(COMMAND_REC, 1);
rec->cmd = g_strdup(cmd);
rec->category = category == NULL ? NULL : g_strdup(category);
commands = g_slist_append(commands, rec);
if (func != NULL) {
str = g_strconcat("command ", cmd, NULL);
signal_add(str, func);
g_free(str);
}
signal_emit("commandlist new", 1, rec);
}
void command_free(COMMAND_REC *rec)
{
commands = g_slist_remove(commands, rec);
signal_emit("commandlist remove", 1, rec);
g_free_not_null(rec->category);
g_free(rec->cmd);
g_free(rec);
}
void command_unbind(const char *cmd, SIGNAL_FUNC func)
{
GSList *tmp;
char *str;
g_return_if_fail(cmd != NULL);
for (tmp = commands; tmp != NULL; tmp = tmp->next) {
COMMAND_REC *rec = tmp->data;
if (g_strcasecmp(rec->cmd, cmd) == 0) {
command_free(rec);
break;
}
}
if (func != NULL) {
str = g_strconcat("command ", cmd, NULL);
signal_remove(str, func);
g_free(str);
}
}
void command_runsub(const char *cmd, const char *data, void *p1, void *p2)
{
char *subcmd, *defcmd, *args;
g_return_if_fail(data != NULL);
/* get command.. */
subcmd = g_strdup_printf("command %s %s", cmd, data);
args = strchr(subcmd+9 + strlen(cmd), ' ');
if (args != NULL) *args++ = '\0'; else args = "";
while (*args == ' ') args++;
g_strdown(subcmd);
if (!signal_emit(subcmd, 3, args, p1, p2)) {
defcmd = g_strdup_printf("default command %s", cmd);
if (!signal_emit(defcmd, 3, data, p1, p2))
signal_emit("unknown command", 3, strchr(subcmd, ' ')+1, p1, p2);
g_free(defcmd);
}
g_free(subcmd);
}
char *cmd_get_param(char **data)
{
char *pos;
g_return_val_if_fail(data != NULL, NULL);
g_return_val_if_fail(*data != NULL, NULL);
while (**data == ' ') (*data)++;
pos = *data;
while (**data != '\0' && **data != ' ') (*data)++;
if (**data == ' ') *(*data)++ = '\0';
return pos;
}
char *cmd_get_quoted_param(char **data)
{
char *pos, quote;
g_return_val_if_fail(data != NULL, NULL);
g_return_val_if_fail(*data != NULL, NULL);
while (**data == ' ') (*data)++;
if (**data != '\'' && **data != '"')
return cmd_get_param(data);
quote = **data; (*data)++;
pos = *data;
while (**data != '\0' && **data != quote) {
if (**data == '\\' && (*data)[1] != '\0')
g_memmove(*data, (*data)+1, strlen(*data));
(*data)++;
}
if (**data != '\0') *(*data)++ = '\0';
return pos;
}
static char *get_opt_args(char **data)
{
/* -cmd1 -cmd2 -cmd3 ... */
char *p, *ret;
g_return_val_if_fail(data != NULL, NULL);
g_return_val_if_fail(*data != NULL, NULL);
ret = NULL;
for (p = *data;;) {
if (*p != '-') {
if (p == *data) return "";
while (isspace(p[-1]) && p > *data) p--;
if (*p != '\0') *p++ = '\0';
ret = *data;
*data = p;
return ret;
}
while (!isspace(*p) && *p != '\0') p++;
while (isspace(*p)) p++;
}
}
static void cmd_params_pack(char ***subargs, char *end, char *start, char *newstart)
{
char ***tmp;
char *data;
int bufsize, datalen, len;
bufsize = (int) (end-newstart)+1;
data = g_malloc(bufsize); datalen = 0;
for (tmp = subargs; *tmp != NULL; tmp++) {
if (**tmp < start || **tmp > end)
continue;
len = strlen(**tmp)+1;
if (datalen+len > bufsize)
g_error("cmd_params_pack() : buffer overflow!");
memcpy(data+datalen, **tmp, len);
**tmp = newstart+datalen;
datalen += len;
}
g_memmove(newstart, data, datalen);
g_free(data);
}
int arg_find(char **array, const char *item)
{
char **tmp;
int index;
g_return_val_if_fail(array != NULL, 0);
g_return_val_if_fail(item != NULL, 0);
index = 0;
for (tmp = array; *tmp != NULL; tmp++, index++) {
if (g_strcasecmp(*tmp + (**tmp == '@'), item) == 0)
return index;
}
return -1;
}
static int get_multi_args(char **data, va_list va)
{
/* -cmd1 arg1 -cmd2 "argument two" -cmd3 */
GString *returnargs;
char **args, **arglist, *arg, *origdata;
char **nextarg, ***subargs;
int eat, pos;
eat = 0;
args = (char **) va_arg(va, char **);
g_return_val_if_fail(args != NULL && *args != NULL && **args != '\0', 0);
arglist = g_strsplit(*args, " ", -1);
eat = strarray_length(arglist);
subargs = g_new(char **, eat+1);
for (pos = 0; pos < eat; pos++) {
subargs[pos] = (char **) va_arg(va, char **);
if (subargs[pos] == NULL) {
g_free(subargs);
g_warning("get_multi_args() : subargument == NULL");
return eat;
}
*subargs[pos] = "";
}
subargs[eat] = NULL;
origdata = *data;
returnargs = g_string_new(NULL);
nextarg = NULL;
for (;;) {
if (**data == '-') {
(*data)++; arg = cmd_get_param(data);
g_string_sprintfa(returnargs, "-%s ", arg);
/* check if this argument can have parameter */
pos = arg_find(arglist, arg);
nextarg = pos == -1 ? NULL : subargs[pos];
while (isspace(**data)) (*data)++;
continue;
}
if (nextarg == NULL)
break;
if (*arglist[pos] == '@' && !isdigit(**data))
break; /* expected a numeric argument */
/* save the sub-argument to `nextarg' */
arg = cmd_get_quoted_param(data);
*nextarg = arg; nextarg = NULL;
while (isspace(**data)) (*data)++;
}
/* ok, this is a bit stupid. this will pack the arguments in `data'
like "-arg1 subarg -arg2 sub2\0" -> "-arg1 -arg2\0subarg\0sub2\0"
this is because it's easier to free only _one_ string instead of
two (`args') when using PARAM_FLAG_MULTIARGS. */
if (origdata == *data)
*args = "";
else {
cmd_params_pack(subargs, **data == '\0' ? *data : (*data)-1,
origdata, origdata+returnargs->len);
g_string_truncate(returnargs, returnargs->len-1);
strcpy(origdata, returnargs->str);
*args = origdata;
}
g_string_free(returnargs, TRUE);
g_strfreev(arglist);
g_free(subargs);
return eat;
}
char *cmd_get_callfuncs(const char *data, int *count, va_list *args)
{
CMD_GET_FUNC func;
GSList *tmp;
char *ret, *old;
ret = g_strdup(data);
for (tmp = cmdget_funcs; tmp != NULL; tmp = tmp->next) {
func = tmp->data;
old = ret;
ret = func(ret, count, args);
g_free(old);
}
return ret;
}
char *cmd_get_params(const char *data, int count, ...)
{
char **str, *arg, *ret, *datad;
va_list args;
int cnt, eat;
g_return_val_if_fail(data != NULL, NULL);
va_start(args, count);
ret = datad = cmd_get_callfuncs(data, &count, &args);
cnt = PARAM_WITHOUT_FLAGS(count);
while (cnt-- > 0) {
if (count & PARAM_FLAG_OPTARGS) {
arg = get_opt_args(&datad);
count &= ~PARAM_FLAG_OPTARGS;
} else if (count & PARAM_FLAG_MULTIARGS) {
eat = get_multi_args(&datad, args)+1;
count &= ~PARAM_FLAG_MULTIARGS;
cnt -= eat-1;
while (eat-- > 0)
str = (char **) va_arg(args, char **);
continue;
} else if (cnt == 0 && count & PARAM_FLAG_GETREST) {
/* get rest */
arg = datad;
} else {
arg = cmd_get_quoted_param(&datad);
}
str = (char **) va_arg(args, char **);
if (str != NULL) *str = arg;
}
va_end(args);
return ret;
}
void cmd_get_add_func(CMD_GET_FUNC func)
{
cmdget_funcs = g_slist_prepend(cmdget_funcs, func);
}
void cmd_get_remove_func(CMD_GET_FUNC func)
{
cmdget_funcs = g_slist_prepend(cmdget_funcs, func);
}
static void parse_outgoing(const char *line, SERVER_REC *server, void *item)
{
const char *cmdchars, *alias;
char *cmd, *str, *args, *oldcmd;
int use_alias = TRUE;
g_return_if_fail(line != NULL);
if (*line == '\0') {
/* empty line, forget it. */
signal_stop();
return;
}
cmdchars = settings_get_str("cmdchars");
if (strchr(cmdchars, *line) == NULL)
return; /* handle only /commands here */
line++;
/* //command ignores aliases */
if (strchr(cmdchars, *line) != NULL) {
line++;
use_alias = FALSE;
}
cmd = str = g_strconcat("command ", line, NULL);
args = strchr(cmd+8, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
/* check if there's an alias for command */
alias = use_alias ? alias_find(cmd+8) : NULL;
if (alias != NULL)
eval_special_string(alias, args, server, item);
else {
if (server != NULL)
server_redirect_default((SERVER_REC *) server, cmd);
g_strdown(cmd);
oldcmd = current_command;
current_command = cmd+8;
if (!signal_emit(cmd, 3, args, server, item))
signal_emit_id(signal_default_command, 3, line, server, item);
current_command = oldcmd;
}
g_free(str);
}
static void cmd_eval(const char *data, SERVER_REC *server, void *item)
{
g_return_if_fail(data != NULL);
eval_special_string(data, "", server, item);
}
static void cmd_cd(const char *data)
{
char *str;
g_return_if_fail(data != NULL);
if (*data == '\0') return;
str = convert_home(data);
chdir(str);
g_free(str);
}
void commands_init(void)
{
cmdget_funcs = NULL;
current_command = NULL;
signal_default_command = module_get_uniq_id_str("signals", "default command");
settings_add_str("misc", "cmdchars", "/");
signal_add("send command", (SIGNAL_FUNC) parse_outgoing);
command_bind("eval", NULL, (SIGNAL_FUNC) cmd_eval);
command_bind("cd", NULL, (SIGNAL_FUNC) cmd_cd);
}
void commands_deinit(void)
{
g_free_not_null(current_command);
g_slist_free(cmdget_funcs);
signal_remove("send command", (SIGNAL_FUNC) parse_outgoing);
command_unbind("eval", (SIGNAL_FUNC) cmd_eval);
command_unbind("cd", (SIGNAL_FUNC) cmd_cd);
}

74
src/core/commands.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef __COMMANDS_H
#define __COMMANDS_H
#include "signals.h"
typedef struct {
char *category;
char *cmd;
}
COMMAND_REC;
enum {
CMDERR_PARAM, /* invalid parameter */
CMDERR_NOT_ENOUGH_PARAMS, /* not enough parameters given */
CMDERR_NOT_CONNECTED, /* not connected to IRC server */
CMDERR_NOT_JOINED, /* not joined to any channels in this window */
CMDERR_GETSOCKNAME, /* getsockname() failed */
CMDERR_LISTEN, /* listen() failed */
CMDERR_MULTIPLE_MATCHES, /* multiple matches found, didn't do anything */
CMDERR_NICK_NOT_FOUND, /* nick not found */
CMDERR_CHAN_NOT_FOUND, /* channel not found */
CMDERR_SERVER_NOT_FOUND, /* server not found */
CMDERR_CHAN_NOT_SYNCED, /* channel not fully synchronized yet */
CMDERR_NOT_GOOD_IDEA /* not good idea to do, -yes overrides this */
};
#define cmd_return_error(a) { signal_emit("error command", 1, GINT_TO_POINTER(a)); return; }
#define cmd_param_error(a) { g_free(params); cmd_return_error(a); }
extern GSList *commands;
extern char *current_command;
void command_bind(const char *cmd, const char *category, SIGNAL_FUNC func);
void command_unbind(const char *cmd, SIGNAL_FUNC func);
void command_runsub(const char *cmd, const char *data, void *p1, void *p2);
/* count can have these flags: */
#define PARAM_WITHOUT_FLAGS(a) ((a) & 0x00ffffff)
/* cmd_get_params() */
#define PARAM_FLAG_GETREST 0x02000000
/* optional arguments (-cmd -cmd2 -cmd3) */
#define PARAM_FLAG_OPTARGS 0x04000000
/* arguments can have arguments too. Example:
-cmd arg -noargcmd -cmd2 "another arg -optnumarg rest of the text
You would call this with:
args = "cmd cmd2 @optnumarg";
cmd_get_params(data, 5 | PARAM_FLAG_MULTIARGS | PARAM_FLAG_GETREST,
&args, &arg_cmd, &arg_cmd2, &arg_optnum, &rest);
The variables are filled as following:
args = "-cmd -noargcmd -cmd2 -optnumarg"
arg_cmd = "arg"
arg_cmd2 = "another arg"
rest = "rest of the text"
arg_optnum = "" - this is because "rest" isn't a numeric value
*/
#define PARAM_FLAG_MULTIARGS 0x08000000
char *cmd_get_param(char **data);
char *cmd_get_params(const char *data, int count, ...);
typedef char* (*CMD_GET_FUNC) (const char *data, int *count, va_list *args);
void cmd_get_add_func(CMD_GET_FUNC func);
void cmd_get_remove_func(CMD_GET_FUNC func);
void commands_init(void);
void commands_deinit(void);
#endif

68
src/core/core.c Normal file
View file

@ -0,0 +1,68 @@
/*
core.c : irssi
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"
#include "modules.h"
#include "pidwait.h"
#include "net-disconnect.h"
#include "signals.h"
#include "settings.h"
#include "server.h"
#include "commands.h"
#include "log.h"
#include "rawlog.h"
#include "special-vars.h"
int irssi_gui;
void core_init(void)
{
modules_init();
pidwait_init();
net_disconnect_init();
signals_init();
settings_init();
servers_init();
commands_init();
log_init();
rawlog_init();
special_vars_init();
}
void core_deinit(void)
{
special_vars_deinit();
rawlog_deinit();
log_deinit();
commands_deinit();
servers_deinit();
settings_deinit();
signals_deinit();
net_disconnect_deinit();
pidwait_deinit();
modules_deinit();
}

17
src/core/core.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef __IRSSI_CORE_H
#define __IRSSI_CORE_H
/* for determining what GUI is currently in use: */
#define IRSSI_GUI_NONE 0
#define IRSSI_GUI_TEXT 1
#define IRSSI_GUI_GTK 2
#define IRSSI_GUI_GNOME 3
#define IRSSI_GUI_QT 4
#define IRSSI_GUI_KDE 5
extern int irssi_gui;
void core_init(void);
void core_deinit(void);
#endif

160
src/core/levels.c Normal file
View file

@ -0,0 +1,160 @@
/*
levels.c : irssi
Copyright (C) 1999-2000 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"
#include "levels.h"
static char *levels[] =
{
"CRAP",
"MSGS",
"PUBLICS",
"NOTICES",
"SNOTES",
"CTCPS",
"ACTIONS",
"JOINS",
"PARTS",
"QUITS",
"KICKS",
"MODES",
"TOPICS",
"WALLS",
"WALLOPS",
"INVITES",
"NICKS",
"DCC",
"CLIENTNOTICES",
"CLIENTCRAP",
"CLIENTERRORS",
"HILIGHT",
"NOHILIGHT",
NULL
};
int level_get(const char *level)
{
int n, len;
if (strcmp(level, "ALL") == 0)
return MSGLEVEL_ALL;
if (strcmp(level, "NEVER") == 0)
return MSGLEVEL_NEVER;
/* I never remember if it was PUBLIC or PUBLICS, MSG or MSGS, etc.
So, make it work with both. */
len = strlen(level);
if (toupper(level[len-1]) == 'S') len--;
for (n = 0; levels[n] != NULL; n++) {
if (strncmp(levels[n], level, len) == 0 &&
(levels[n][len] == '\0' || strcmp(levels[n]+len, "S") == 0))
return 1 << n;
}
return 0;
}
int level2bits(const char *level)
{
char *orig, *str, *ptr;
int ret, slevel, neg;
g_return_val_if_fail(level != NULL, 0);
if (*level == '\0')
return 0;
orig = str = g_strdup(level);
g_strup(str);
ret = 0;
for (ptr = str; ; str++) {
if (*str == ' ')
*str++ = '\0';
else if (*str != '\0')
continue;
neg = *ptr == '-' ? 1 : 0;
if (*ptr == '-' || *ptr == '+') ptr++;
slevel = level_get(ptr);
if (slevel != 0) {
ret = !neg ? (ret | slevel) :
(ret & ~slevel);
}
while (*str == ' ') str++;
if (*str == '\0') break;
ptr = str;
}
g_free(orig);
return ret;
}
char *bits2level(int bits)
{
GString *str;
char *ret;
int n;
if (bits == MSGLEVEL_ALL)
return g_strdup("ALL");
str = g_string_new(NULL);
if (bits & MSGLEVEL_NEVER)
g_string_append(str, "NEVER ");
for (n = 0; levels[n] != NULL; n++) {
if (bits & (1 << n))
g_string_sprintfa(str, "%s ", levels[n]);
}
g_string_truncate(str, str->len-1);
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
int combine_level(int dest, const char *src)
{
char **list, **item;
int itemlevel;
g_return_val_if_fail(src != NULL, dest);
list = g_strsplit(src, " ", -1);
for (item = list; *item != NULL; item++) {
g_strup(*item);
itemlevel = level_get(*item + (**item == '+' || **item == '-' ? 1 : 0));
if (**item == '-')
dest &= ~(itemlevel);
else
dest |= itemlevel;
}
g_strfreev(list);
return dest;
}

44
src/core/levels.h Normal file
View file

@ -0,0 +1,44 @@
#ifndef __LEVELS_H
#define __LEVELS_H
/* This is pretty much IRC specific, but I think it would be easier for
other chats to try to use these same levels instead of implementing too
difficult message leveling system (which might be done if really
needed..). */
/* Message levels */
#define MSGLEVEL_CRAP 0x0000001
#define MSGLEVEL_MSGS 0x0000002
#define MSGLEVEL_PUBLIC 0x0000004
#define MSGLEVEL_NOTICES 0x0000008
#define MSGLEVEL_SNOTES 0x0000010
#define MSGLEVEL_CTCPS 0x0000020
#define MSGLEVEL_ACTIONS 0x0000040
#define MSGLEVEL_JOINS 0x0000080
#define MSGLEVEL_PARTS 0x0000100
#define MSGLEVEL_QUITS 0x0000200
#define MSGLEVEL_KICKS 0x0000400
#define MSGLEVEL_MODES 0x0000800
#define MSGLEVEL_TOPICS 0x0001000
#define MSGLEVEL_WALLS 0x0002000
#define MSGLEVEL_WALLOPS 0x0004000
#define MSGLEVEL_INVITES 0x0008000
#define MSGLEVEL_NICKS 0x0010000
#define MSGLEVEL_DCC 0x0020000
#define MSGLEVEL_CLIENTNOTICE 0x0040000
#define MSGLEVEL_CLIENTCRAP 0x0080000
#define MSGLEVEL_CLIENTERROR 0x0100000
#define MSGLEVEL_HILIGHT 0x0200000
#define MSGLEVEL_ALL 0x03fffff
#define MSGLEVEL_NOHILIGHT 0x1000000 /* Don't try to hilight words in this message */
#define MSGLEVEL_NO_ACT 0x2000000 /* Don't trigger channel activity */
#define MSGLEVEL_NEVER 0x4000000 /* never ignore / never log */
#define MSGLEVEL_LASTLOG 0x8000000 /* never ignore / never log */
int level2bits(const char *level);
char *bits2level(int bits);
int combine_level(int dest, const char *src);
#endif

147
src/core/line-split.c Normal file
View file

@ -0,0 +1,147 @@
/*
line-split.c : irssi
Copyright (C) 1999-2000 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 line length - split to two lines if it's longer than this.
This is mostly to prevent excessive memory usage. Like if someone DCC
chats you, you both have very fast connections and the other side sends
you 100 megs of text without any line feeds -> irssi will (try to)
allocate 128M of memory for the line and will eventually crash when it
can't allocate any more memory. If the line is split at every 64k the
text buffer will free the old lines and the memory usage never gets
too high. */
#define MAX_CHARS_IN_LINE 65536
typedef struct {
int len;
int alloc;
int remove;
char *str;
} LINEBUF_REC;
static inline int nearest_power(int num)
{
int n = 1;
while (n < num) n <<= 1;
return n;
}
static void linebuf_append(LINEBUF_REC *rec, const char *data, int len)
{
if (rec->len+len > rec->alloc) {
rec->alloc = nearest_power(rec->len+len);;
rec->str = rec->str == NULL ? g_malloc(rec->alloc) :
g_realloc(rec->str, rec->alloc);
}
memcpy(rec->str + rec->len, data, len);
rec->len += len;
}
static char *linebuf_find(LINEBUF_REC *rec, char chr)
{
int n;
for (n = 0; n < rec->len; n++)
if (rec->str[n] == chr) return rec->str+n;
return NULL;
}
static int remove_newline(LINEBUF_REC *rec)
{
char *ptr;
ptr = linebuf_find(rec, '\n');
if (ptr == NULL) {
/* LF wasn't found, wait for more data.. */
if (rec->len < MAX_CHARS_IN_LINE)
return 0;
/* line buffer is too big - force a newline. */
linebuf_append(rec, "\n", 1);
ptr = rec->str+rec->len-1;
}
rec->remove = (int) (ptr-rec->str)+1;
if (ptr != rec->str && ptr[-1] == '\r') {
/* remove CR too. */
ptr--;
}
*ptr = '\0';
return 1;
}
/* line-split `data'. Initially `*buffer' should contain NULL. */
int line_split(const char *data, int len, char **output, LINEBUF_REC **buffer)
{
LINEBUF_REC *rec;
g_return_val_if_fail(data != NULL, -1);
g_return_val_if_fail(output != NULL, -1);
g_return_val_if_fail(buffer != NULL, -1);
if (*buffer == NULL)
*buffer = g_new0(LINEBUF_REC, 1);
rec = *buffer;
if (rec->remove > 0) {
rec->len = rec->len - rec->remove;
memcpy(rec->str, rec->str+rec->remove, rec->len);
rec->remove = 0;
}
if (len > 0)
linebuf_append(rec, data, len);
else if (len < 0) {
/* connection closed.. */
if (rec->len == 0)
return -1;
/* no new data got but still something in buffer.. */
len = 0;
if (linebuf_find(rec, '\n') == NULL) {
/* connection closed and last line is missing \n ..
just add it so we can see if it had anything useful.. */
linebuf_append(rec, "\n", 1);
}
}
*output = rec->str;
return remove_newline(rec);
}
void line_split_free(LINEBUF_REC *buffer)
{
if (buffer != NULL) {
if (buffer->str != NULL) g_free(buffer->str);
g_free(buffer);
}
}
/* Return 1 if there is no data in the buffer */
int line_split_is_empty(LINEBUF_REC *buffer)
{
return buffer->len == 0;
}

13
src/core/line-split.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef __LINE_SPLIT_H
#define __LINE_SPLIT_H
typedef struct _LINEBUF_REC LINEBUF_REC;
/* line-split `data'. Initially `*buffer' should contain NULL. */
int line_split(const char *data, int len, char **output, LINEBUF_REC **buffer);
void line_split_free(LINEBUF_REC *buffer);
/* Return 1 if there is no data in the buffer */
int line_split_is_empty(LINEBUF_REC *buffer);
#endif

438
src/core/log.c Normal file
View file

@ -0,0 +1,438 @@
/*
log.c : irssi
Copyright (C) 1999-2000 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"
#include "signals.h"
#include "commands.h"
#include "levels.h"
#include "misc.h"
#include "log.h"
#include "lib-config/iconfig.h"
#include "settings.h"
#define LOG_FILE_CREATE_MODE 644
#ifdef HAVE_FCNTL
static struct flock lock;
#endif
GSList *logs;
const char *log_timestamp;
static int log_file_create_mode;
static int rotate_tag;
static void log_write_timestamp(int handle, const char *format, const char *suffix, time_t t)
{
struct tm *tm;
char str[256];
tm = localtime(&t);
str[sizeof(str)-1] = '\0';
strftime(str, sizeof(str)-1, format, tm);
write(handle, str, strlen(str));
if (suffix != NULL) write(handle, suffix, strlen(suffix));
}
int log_start_logging(LOG_REC *log)
{
char *str, fname[1024];
struct tm *tm;
time_t t;
g_return_val_if_fail(log != NULL, FALSE);
if (log->handle != -1)
return TRUE;
t = time(NULL);
tm = localtime(&t);
/* Append/create log file */
str = convert_home(log->fname);
strftime(fname, sizeof(fname), str, tm);
log->handle = open(fname, O_WRONLY | O_APPEND | O_CREAT, log_file_create_mode);
g_free(str);
if (log->handle == -1) return FALSE;
#ifdef HAVE_FCNTL
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
if (fcntl(log->handle, F_SETLK, &lock) == -1) {
close(log->handle);
log->handle = -1;
signal_emit("log locked", 1, log);
return FALSE;
}
#endif
lseek(log->handle, 0, SEEK_END);
log->opened = log->last = time(NULL);
log_write_timestamp(log->handle, settings_get_str("log_open_string"), "\n", log->last);
signal_emit("log started", 1, log);
return TRUE;
}
void log_stop_logging(LOG_REC *log)
{
g_return_if_fail(log != NULL);
if (log->handle == -1)
return;
signal_emit("log stopped", 1, log);
log_write_timestamp(log->handle, settings_get_str("log_close_string"), "\n", time(NULL));
#ifdef HAVE_FCNTL
memset(&lock, 0, sizeof(lock));
lock.l_type = F_UNLCK;
fcntl(log->handle, F_SETLK, &lock);
#endif
close(log->handle);
log->handle = -1;
}
void log_write_rec(LOG_REC *log, const char *str)
{
struct tm *tm;
time_t t;
int day;
g_return_if_fail(log != NULL);
g_return_if_fail(str != NULL);
if (log->handle == -1)
return;
t = time(NULL);
tm = localtime(&t);
day = tm->tm_mday;
tm = localtime(&log->last);
if (tm->tm_mday != day) {
/* day changed */
log_write_timestamp(log->handle, settings_get_str("log_day_changed"), "\n", t);
}
log->last = t;
log_write_timestamp(log->handle, log_timestamp, str, t);
write(log->handle, "\n", 1);
signal_emit("log written", 2, log, str);
}
void log_file_write(const char *item, int level, const char *str, int no_fallbacks)
{
GSList *tmp, *fallbacks;
char *tmpstr;
int found;
g_return_if_fail(str != NULL);
fallbacks = NULL; found = FALSE;
for (tmp = logs; tmp != NULL; tmp = tmp->next) {
LOG_REC *rec = tmp->data;
if (rec->handle == -1)
continue; /* log not opened yet */
if ((level & rec->level) == 0)
continue;
if (rec->items == NULL)
fallbacks = g_slist_append(fallbacks, rec);
else if (item != NULL && strarray_find(rec->items, item) != -1)
log_write_rec(rec, str);
}
if (!found && !no_fallbacks && fallbacks != NULL) {
/* not found from any items, so write it to all main logs */
tmpstr = NULL;
if (level & MSGLEVEL_PUBLIC)
tmpstr = g_strconcat(item, ": ", str, NULL);
g_slist_foreach(fallbacks, (GFunc) log_write_rec, tmpstr ? tmpstr : (char *)str);
g_free_not_null(tmpstr);
}
g_slist_free(fallbacks);
}
void log_write(const char *item, int level, const char *str)
{
log_file_write(item, level, str, TRUE);
}
LOG_REC *log_find(const char *fname)
{
GSList *tmp;
for (tmp = logs; tmp != NULL; tmp = tmp->next) {
LOG_REC *rec = tmp->data;
if (strcmp(rec->fname, fname) == 0)
return rec;
}
return NULL;
}
const char *log_rotate2str(int rotate)
{
switch (rotate) {
case LOG_ROTATE_HOUR:
return "hour";
case LOG_ROTATE_DAY:
return "day";
case LOG_ROTATE_WEEK:
return "week";
case LOG_ROTATE_MONTH:
return "month";
}
return NULL;
}
int log_str2rotate(const char *str)
{
if (str == NULL)
return -1;
if (g_strncasecmp(str, "hour", 4) == 0)
return LOG_ROTATE_HOUR;
if (g_strncasecmp(str, "day", 3) == 0)
return LOG_ROTATE_DAY;
if (g_strncasecmp(str, "week", 4) == 0)
return LOG_ROTATE_WEEK;
if (g_strncasecmp(str, "month", 5) == 0)
return LOG_ROTATE_MONTH;
if (g_strncasecmp(str, "never", 5) == 0)
return LOG_ROTATE_NEVER;
return -1;
}
static void log_set_config(LOG_REC *log)
{
CONFIG_NODE *node;
char *levelstr;
node = iconfig_node_traverse("logs", TRUE);
node = config_node_section(node, log->fname, NODE_TYPE_BLOCK);
if (log->autoopen)
config_node_set_bool(node, "auto_open", TRUE);
else
config_node_set_str(node, "auto_open", NULL);
config_node_set_str(node, "rotate", log_rotate2str(log->rotate));
levelstr = bits2level(log->level);
config_node_set_str(node, "level", levelstr);
g_free(levelstr);
config_node_set_str(node, "items", NULL);
if (log->items != NULL && *log->items != NULL) {
node = config_node_section(node, "items", NODE_TYPE_LIST);
config_node_add_list(node, log->items);
}
}
static void log_remove_config(LOG_REC *log)
{
iconfig_set_str("logs", log->fname, NULL);
}
LOG_REC *log_create_rec(const char *fname, int level, const char *items)
{
LOG_REC *rec;
g_return_val_if_fail(fname != NULL, NULL);
rec = log_find(fname);
if (rec == NULL) {
rec = g_new0(LOG_REC, 1);
rec->fname = g_strdup(fname);
rec->handle = -1;
} else {
g_strfreev(rec->items);
}
rec->items = items == NULL || *items == '\0' ? NULL :
g_strsplit(items, " ", -1);
rec->level = level;
return rec;
}
void log_update(LOG_REC *log)
{
g_return_if_fail(log != NULL);
if (log_find(log->fname) == NULL) {
logs = g_slist_append(logs, log);
log->handle = -1;
}
log_set_config(log);
signal_emit("log new", 1, log);
}
void log_close(LOG_REC *log)
{
g_return_if_fail(log != NULL);
log_remove_config(log);
if (log->handle != -1)
log_stop_logging(log);
logs = g_slist_remove(logs, log);
signal_emit("log remove", 1, log);
if (log->items != NULL) g_strfreev(log->items);
g_free(log->fname);
g_free(log);
}
static void sig_printtext_stripped(void *server, const char *item, gpointer levelp, const char *str)
{
int level;
g_return_if_fail(str != NULL);
level = GPOINTER_TO_INT(levelp);
if (logs != NULL && level != MSGLEVEL_NEVER)
log_file_write(item, level, str, FALSE);
}
static int sig_rotate_check(void)
{
static int last_hour = -1;
struct tm tm_now, *tm;
GSList *tmp;
time_t now;
/* don't do anything until hour is changed */
now = time(NULL);
memcpy(&tm_now, localtime(&now), sizeof(tm_now));
if (tm_now.tm_hour == last_hour) return 1;
last_hour = tm_now.tm_hour;
for (tmp = logs; tmp != NULL; tmp = tmp->next) {
LOG_REC *rec = tmp->data;
if (rec->handle == -1 || rec->rotate == LOG_ROTATE_NEVER)
continue;
tm = localtime(&rec->opened);
if (rec->rotate == LOG_ROTATE_MONTH) {
if (tm->tm_mon == tm_now.tm_mon)
continue;
} else if (rec->rotate == LOG_ROTATE_WEEK) {
if (tm->tm_wday != 1 || tm->tm_mday == tm_now.tm_mday)
continue;
} else if (rec->rotate == LOG_ROTATE_DAY) {
if (tm->tm_mday == tm_now.tm_mday)
continue;
}
log_stop_logging(rec);
log_start_logging(rec);
}
return 1;
}
static void log_read_config(void)
{
CONFIG_NODE *node;
LOG_REC *log;
GSList *tmp;
while (logs != NULL)
log_close(logs->data);
node = iconfig_node_traverse("logs", FALSE);
if (node == NULL) return;
for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
node = tmp->data;
if (node->type != NODE_TYPE_BLOCK)
continue;
log = g_new0(LOG_REC, 1);
logs = g_slist_append(logs, log);
log->handle = -1;
log->fname = g_strdup(node->key);
log->autoopen = config_node_get_bool(node, "auto_open", FALSE);
log->level = level2bits(config_node_get_str(node, "level", 0));
log->rotate = log_str2rotate(config_node_get_str(node, "rotate", NULL));
if (log->rotate < 0) log->rotate = LOG_ROTATE_NEVER;
node = config_node_section(node, "items", -1);
if (node != NULL) log->items = config_node_get_list(node);
if (log->autoopen) log_start_logging(log);
}
}
static void read_settings(void)
{
log_timestamp = settings_get_str("log_timestamp");
log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
}
void log_init(void)
{
rotate_tag = g_timeout_add(60000, (GSourceFunc) sig_rotate_check, NULL);
logs = NULL;
settings_add_int("log", "log_create_mode", LOG_FILE_CREATE_MODE);
settings_add_str("log", "log_timestamp", "%H:%M ");
settings_add_str("log", "log_open_string", "--- Log opened %a %b %d %H:%M:%S %Y");
settings_add_str("log", "log_close_string", "--- Log closed %a %b %d %H:%M:%S %Y");
settings_add_str("log", "log_day_changed", "--- Day changed %a %b %d %Y");
read_settings();
log_read_config();
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
signal_add("setup reread", (SIGNAL_FUNC) log_read_config);
signal_add("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped);
}
void log_deinit(void)
{
g_source_remove(rotate_tag);
while (logs != NULL)
log_close(logs->data);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
signal_remove("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped);
}

49
src/core/log.h Normal file
View file

@ -0,0 +1,49 @@
#ifndef __LOG_H
#define __LOG_H
enum {
LOG_ROTATE_NEVER,
LOG_ROTATE_HOUR,
LOG_ROTATE_DAY,
LOG_ROTATE_WEEK,
LOG_ROTATE_MONTH
};
typedef struct {
char *fname; /* file name */
int handle; /* file handle */
time_t opened;
int level; /* log only these levels */
char **items; /* log only on these items (channels, queries, window refnums) */
time_t last; /* when last message was written */
int rotate;
int autoopen:1; /* automatically start logging at startup */
int temp:1; /* don't save this to config file */
} LOG_REC;
extern GSList *logs;
/* Create log record - you still need to call log_update() to actually add it
into log list */
LOG_REC *log_create_rec(const char *fname, int level, const char *items);
void log_update(LOG_REC *log);
void log_close(LOG_REC *log);
LOG_REC *log_find(const char *fname);
void log_write(const char *item, int level, const char *str);
void log_write_rec(LOG_REC *log, const char *str);
const char *log_rotate2str(int rotate);
int log_str2rotate(const char *str);
int log_start_logging(LOG_REC *log);
void log_stop_logging(LOG_REC *log);
void log_init(void);
void log_deinit(void);
#endif

356
src/core/memdebug.c Normal file
View file

@ -0,0 +1,356 @@
/*
memdebug.c : irssi
Copyright (C) 1999-2000 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#define ENABLE_BUFFER_CHECKS
#define BUFFER_CHECK_SIZE 5
#define MIN_BUFFER_CHECK_SIZE 2
typedef struct {
void *p;
int size;
char *file;
int line;
char *comment;
} MEM_REC;
static GHashTable *data = NULL, *preallocs = NULL;
static const char *comment = "";
static void add_flow_checks(guchar *p, unsigned long size)
{
#ifdef ENABLE_BUFFER_CHECKS
int n;
for (n = 0; n < BUFFER_CHECK_SIZE; n++)
p[n] = n ^ 0x7f;
for (n = 0; n < BUFFER_CHECK_SIZE; n++)
p[size-BUFFER_CHECK_SIZE+n] = n ^ 0x7f;
#endif
}
void ig_memcheck_rec(void *key, MEM_REC *rec)
{
guchar *p;
int n;
if (rec->size != INT_MIN){
p = rec->p;
for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++)
if (p[n] != (n ^ 0x7f))
g_error("buffer underflow, file %s line %d!\n", rec->file, rec->line);
for (n = 0; n < MIN_BUFFER_CHECK_SIZE; n++)
if (p[rec->size-BUFFER_CHECK_SIZE+n] != (n ^ 0x7f))
g_error("buffer overflow, file %s line %d!\n", rec->file, rec->line);
}
}
static void mem_check(void)
{
#ifdef ENABLE_BUFFER_CHECKS
g_hash_table_foreach(data, (GHFunc) ig_memcheck_rec, NULL);
#endif
}
static void data_add(void *p, int size, const char *file, int line)
{
MEM_REC *rec;
if (size <= 0 && size != INT_MIN)
g_error("size = %d, file %s line %d", size, file, line);
if (data == NULL) {
data = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
preallocs = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
}
if (g_hash_table_lookup(data, p) != NULL)
g_error("data_add() already malloc()'ed %p (in %s:%d)", p, file, line);
rec = g_new(MEM_REC, 1);
g_hash_table_insert(data, p, rec);
rec->p = p;
rec->size = size;
rec->file = (char *) file;
rec->line = line;
rec->comment = g_strdup(comment);
if (size == INT_MIN)
g_hash_table_insert(preallocs, p-BUFFER_CHECK_SIZE, p);
else
add_flow_checks(p, size);
mem_check();
}
static void *data_remove(void *p, const char *file, int line)
{
MEM_REC *rec;
mem_check();
if (g_hash_table_lookup(preallocs, p) != NULL) {
g_hash_table_remove(preallocs, p);
p += BUFFER_CHECK_SIZE;
}
rec = g_hash_table_lookup(data, p);
if (rec == NULL) {
g_warning("data_remove() data %p not found (in %s:%d)", p, file, line);
return p+BUFFER_CHECK_SIZE;
}
g_hash_table_remove(data, p);
g_free(rec->comment);
g_free(rec);
return p;
}
void *ig_malloc(int size, const char *file, int line)
{
#if 1
void *p;
size += BUFFER_CHECK_SIZE*2;
p = g_malloc(size);
data_add(p, size, file, line);
return p+BUFFER_CHECK_SIZE;
#else
return g_malloc(size);
#endif
}
void *ig_malloc0(int size, const char *file, int line)
{
#if 1
void *p;
size += BUFFER_CHECK_SIZE*2;
p = g_malloc0(size);
data_add(p, size, file, line);
return p+BUFFER_CHECK_SIZE;
#else
return g_malloc0(size);
#endif
}
void *ig_realloc(void *mem, unsigned long size, const char *file, int line)
{
#if 1
void *p;
size += BUFFER_CHECK_SIZE*2;
mem -= BUFFER_CHECK_SIZE;
data_remove(mem, file, line);
p = g_realloc(mem, size);
data_add(p, size, file, line);
return p+BUFFER_CHECK_SIZE;
#else
return g_realloc(mem, size);
#endif
}
char *ig_strdup(const char *str, const char *file, int line)
{
void *p;
if (str == NULL) return NULL;
p = ig_malloc(strlen(str)+1, file, line);
strcpy(p, str);
return p;
}
char *ig_strndup(const char *str, int count, const char *file, int line)
{
char *p;
if (str == NULL) return NULL;
p = ig_malloc(count+1, file, line);
strncpy(p, str, count); p[count] = '\0';
return p;
}
char *ig_strconcat(const char *file, int line, const char *str, ...)
{
guint l;
va_list args;
char *s;
char *concat;
g_return_val_if_fail (str != NULL, NULL);
l = 1 + strlen (str);
va_start (args, str);
s = va_arg (args, char*);
while (s)
{
l += strlen (s);
s = va_arg (args, char*);
}
va_end (args);
concat = ig_malloc(l, file, line);
concat[0] = 0;
strcat (concat, str);
va_start (args, str);
s = va_arg (args, char*);
while (s)
{
strcat (concat, s);
s = va_arg (args, char*);
}
va_end (args);
return concat;
}
char *ig_strdup_printf(const char *file, int line, const char *format, ...)
{
char *buffer, *p;
va_list args;
va_start (args, format);
buffer = g_strdup_vprintf (format, args);
va_end (args);
p = ig_malloc(strlen(buffer)+1, file, line);
strcpy(p, buffer);
g_free(buffer);
return p;
}
char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args)
{
char *buffer, *p;
buffer = g_strdup_vprintf (format, args);
p = ig_malloc(strlen(buffer)+1, file, line);
strcpy(p, buffer);
g_free(buffer);
return p;
}
void ig_free(void *p)
{
#if 1
p -= BUFFER_CHECK_SIZE;
p = data_remove(p, "??", 0);
if (p != NULL)
#endif
g_free(p);
}
GString *ig_string_new(const char *file, int line, const char *str)
{
GString *ret;
ret = g_string_new(str);
data_add(ret, INT_MIN, file, line);
return ret;
}
void ig_string_free(const char *file, int line, GString *str, gboolean freeit)
{
data_remove(str, file, line);
if (!freeit)
data_add(str->str, INT_MIN, file, line);
g_string_free(str, freeit);
}
char *ig_strjoinv(const char *file, int line, const char *sepa, char **array)
{
char *ret;
ret = g_strjoinv(sepa, array);
data_add(ret, INT_MIN, file, line);
return ret;
}
void ig_profile_line(void *key, MEM_REC *rec)
{
char *data;
if (*rec->comment == '\0' &&
(strcmp(rec->file, "ig_strdup_printf") == 0 ||
strcmp(rec->file, "ig_strdup_vprintf") == 0 ||
strcmp(rec->file, "ig_strconcat") == 0 ||
strcmp(rec->file, "ig_string_free (free = FALSE)") == 0))
data = rec->p + BUFFER_CHECK_SIZE;
else
data = rec->comment;
fprintf(stderr, "%s:%d %d bytes (%s)\n", rec->file, rec->line, rec->size, data);
}
void ig_mem_profile(void)
{
g_hash_table_foreach(data, (GHFunc) ig_profile_line, NULL);
g_hash_table_destroy(data);
g_hash_table_destroy(preallocs);
}
static MEM_REC *largest[10];
void ig_profile_largest(void *key, MEM_REC *rec)
{
int n;
for (n = 0; n < 10; n++)
{
if (largest[n] == NULL || rec->size > largest[n]->size)
{
g_memmove(largest+n+1, largest+n, sizeof(void *)*(9-n));
largest[n] = rec;
}
}
}
void ig_mem_profile_largest(void)
{
/*int n;*/
memset(&largest, 0, sizeof(MEM_REC*)*10);
/*g_hash_table_foreach(data, (GHFunc) ig_profile_largest, NULL);
for (n = 0; n < 10 && largest[n] != NULL; n++)
{
ig_profile_line(NULL, largest[n]);
}*/
}
void ig_set_data(const char *data)
{
comment = data;
}

31
src/core/memdebug.h Normal file
View file

@ -0,0 +1,31 @@
#ifdef MEM_DEBUG
void ig_mem_profile(void);
void ig_set_data(const char *data);
void *ig_malloc(int size, const char *file, int line);
void *ig_malloc0(int size, const char *file, int line);
void *ig_realloc(void *mem, unsigned long size, const char *file, int line);
char *ig_strdup(const char *str, const char *file, int line);
char *ig_strndup(const char *str, int count, const char *file, int line);
char *ig_strconcat(const char *file, int line, const char *str, ...);
char *ig_strdup_printf(const char *file, int line, const char *format, ...) G_GNUC_PRINTF (3, 4);
char *ig_strdup_vprintf(const char *file, int line, const char *format, va_list args);
void ig_free(void *p);
GString *ig_string_new(const char *file, int line, const char *str);
void ig_string_free(const char *file, int line, GString *str, int freeit);
char *ig_strjoinv(const char *file, int line, const char *sepa, char **array);
#define g_malloc(a) ig_malloc(a, __FILE__, __LINE__)
#define g_malloc0(a) ig_malloc0(a, __FILE__, __LINE__)
#define g_realloc(a,b) ig_realloc(a, b, __FILE__, __LINE__)
#define g_strdup(a) ig_strdup(a, __FILE__, __LINE__)
#define g_strndup(a, b) ig_strndup(a, b, __FILE__, __LINE__)
#define g_strconcat(a...) ig_strconcat(__FILE__, __LINE__, ##a)
#define g_strdup_printf(a, b...) ig_strdup_printf(__FILE__, __LINE__, a, ##b)
#define g_strdup_vprintf(a, b...) ig_strdup_vprintf(__FILE__, __LINE__, a, ##b)
#define g_free ig_free
#define g_string_new(a) ig_string_new(__FILE__, __LINE__, a)
#define g_string_free(a, b) ig_string_free(__FILE__, __LINE__, a, b)
#define g_strjoinv(a,b) ig_strjoinv(__FILE__, __LINE__, a, b)
#endif

467
src/core/misc.c Normal file
View file

@ -0,0 +1,467 @@
/*
misc.c : irssi
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"
#include "misc.h"
#include "pidwait.h"
#include <errno.h>
#include <regex.h>
typedef struct {
GInputCondition condition;
GInputFunction function;
gpointer data;
} IRSSI_INPUT_REC;
static gboolean irssi_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
{
IRSSI_INPUT_REC *rec = data;
GInputCondition icond = 0;
if (condition & (G_IO_IN | G_IO_PRI))
icond |= G_INPUT_READ;
if (condition & G_IO_OUT)
icond |= G_INPUT_WRITE;
if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
icond |= G_INPUT_EXCEPTION;
if (rec->condition & icond)
rec->function(rec->data, g_io_channel_unix_get_fd(source), icond);
return TRUE;
}
int g_input_add(int source, GInputCondition condition,
GInputFunction function, gpointer data)
{
IRSSI_INPUT_REC *rec;
unsigned int result;
GIOChannel *channel;
GIOCondition cond = 0;
rec = g_new(IRSSI_INPUT_REC, 1);
rec->condition = condition;
rec->function = function;
rec->data = data;
if (condition & G_INPUT_READ)
cond |= (G_IO_IN | G_IO_PRI);
if (condition & G_INPUT_WRITE)
cond |= G_IO_OUT;
if (condition & G_INPUT_EXCEPTION)
cond |= G_IO_ERR|G_IO_HUP|G_IO_NVAL;
channel = g_io_channel_unix_new (source);
result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
irssi_io_invoke, rec, g_free);
g_io_channel_unref(channel);
return result;
}
long get_timeval_diff(const GTimeVal *tv1, const GTimeVal *tv2)
{
long secs, usecs;
secs = tv1->tv_sec - tv2->tv_sec;
usecs = tv1->tv_usec - tv2->tv_usec;
if (usecs < 0) {
usecs += 1000000;
secs--;
}
usecs = usecs/1000 + secs * 1000;
return usecs;
}
int find_substr(const char *list, const char *item)
{
const char *ptr;
g_return_val_if_fail(list != NULL, FALSE);
g_return_val_if_fail(item != NULL, FALSE);
if (*item == '\0')
return FALSE;
for (;;) {
while (isspace((gint) *list)) list++;
if (*list == '\0') break;
ptr = strchr(list, ' ');
if (ptr == NULL) ptr = list+strlen(list);
if (g_strncasecmp(list, item, ptr-list) == 0 && item[ptr-list] == '\0')
return TRUE;
list = ptr;
}
return FALSE;
}
int strarray_length(char **array)
{
int len;
g_return_val_if_fail(array != NULL, 0);
len = 0;
while (*array) {
len++;
array++;
}
return len;
}
int strarray_find(char **array, const char *item)
{
char **tmp;
int index;
g_return_val_if_fail(array != NULL, 0);
g_return_val_if_fail(item != NULL, 0);
index = 0;
for (tmp = array; *tmp != NULL; tmp++, index++) {
if (g_strcasecmp(*tmp, item) == 0)
return index;
}
return -1;
}
int copyfile(const char *src, const char *dest)
{
FILE *fs, *fd;
int ret;
g_return_val_if_fail(src != NULL, FALSE);
g_return_val_if_fail(dest != NULL, FALSE);
fs = fopen(src, "rb");
if (fs == NULL) return FALSE;
ret = FALSE;
remove(dest); /* just to be sure there's no link already in dest */
fd = fopen(dest, "w+");
if (fd != NULL) {
int len;
char buf[1024];
while ((len = fread(buf, 1, sizeof(buf), fs)) > 0)
if (fwrite(buf, 1, len, fd) != len) break;
fclose(fd);
ret = TRUE;
}
fclose(fs);
return ret;
}
int execute(const char *cmd)
{
char **args;
char *prev, *dupcmd;
int pid, max, cnt;
g_return_val_if_fail(cmd != NULL, -1);
pid = fork();
if (pid == -1) return FALSE;
if (pid != 0) {
pidwait_add(pid);
return pid;
}
dupcmd = g_strdup(cmd);
max = 5; cnt = 0;
args = g_malloc(sizeof(char *)*max);
for (prev = dupcmd; ; dupcmd++) {
if (*dupcmd == '\0' || *dupcmd == ' ') {
args[cnt++] = prev;
if (cnt == max) {
max += 5;
args = g_realloc(args, sizeof(char *)*max);
}
if (*dupcmd == '\0') break;
*dupcmd++ = '\0';
prev = dupcmd;
}
}
args[cnt] = NULL;
execvp(args[0], args);
g_free(dupcmd);
_exit(99);
return -1;
}
GSList *gslist_find_string(GSList *list, const char *key)
{
for (list = list; list != NULL; list = list->next)
if (strcmp(list->data, key) == 0) return list;
return NULL;
}
GSList *gslist_find_icase_string(GSList *list, const char *key)
{
for (list = list; list != NULL; list = list->next)
if (g_strcasecmp(list->data, key) == 0) return list;
return NULL;
}
void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, void *data)
{
void *ret;
while (list != NULL) {
ret = func(list->data, data);
if (ret != NULL) return ret;
list = list->next;
}
return NULL;
}
char *gslist_to_string(GSList *list, int offset, const char *delimiter)
{
GString *str;
char **data, *ret;
str = g_string_new(NULL);
while (list != NULL) {
data = G_STRUCT_MEMBER_P(list->data, offset);
if (str->len != 0) g_string_append(str, delimiter);
g_string_append(str, *data);
list = list->next;
}
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
GList *glist_find_string(GList *list, const char *key)
{
for (list = list; list != NULL; list = list->next)
if (strcmp(list->data, key) == 0) return list;
return NULL;
}
GList *glist_find_icase_string(GList *list, const char *key)
{
for (list = list; list != NULL; list = list->next)
if (g_strcasecmp(list->data, key) == 0) return list;
return NULL;
}
char *stristr(const char *data, const char *key)
{
const char *pos, *max;
int keylen, datalen;
keylen = strlen(key);
datalen = strlen(data);
if (keylen > datalen)
return NULL;
max = data+datalen-keylen;
for (pos = data; pos <= max; pos++)
if (g_strncasecmp(pos, key, keylen) == 0) return (char *) pos;
return NULL;
}
#define isbound(c) \
((unsigned char) (c) < 128 && \
(isspace((int) (c)) || ispunct((int) (c))))
char *stristr_full(const char *data, const char *key)
{
const char *pos, *max;
int keylen, datalen;
keylen = strlen(key);
datalen = strlen(data);
if (keylen > datalen)
return NULL;
max = data+datalen-keylen;
for (pos = data; pos <= max; pos++) {
if (pos > data && !isbound(pos[-1])) continue;
if (g_strncasecmp(pos, key, keylen) == 0 &&
(pos[keylen] == '\0' || isbound(pos[keylen])))
return (char *) pos;
}
return NULL;
}
int regexp_match(const char *str, const char *regexp)
{
regex_t preg;
int ret;
if (regcomp(&preg, regexp, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0)
return 0;
ret = regexec(&preg, str, 0, NULL, 0);
regfree(&preg);
return ret == 0;
}
char *convert_home(const char *path)
{
return *path == '~' && (*(path+1) == '/' || *(path+1) == '\0') ?
g_strconcat(g_get_home_dir(), path+1, NULL) :
g_strdup(path);
}
int g_istr_equal(gconstpointer v, gconstpointer v2)
{
return g_strcasecmp((const char *) v, (const char *) v2) == 0;
}
/* a char* hash function from ASU */
unsigned int g_istr_hash(gconstpointer v)
{
const char *s = (char *) v;
unsigned int h = 0, g;
while (*s != '\0') {
h = (h << 4) + toupper(*s);
if ((g = h & 0xf0000000)) {
h = h ^ (g >> 24);
h = h ^ g;
}
s++;
}
return h /* % M */;
}
/* Find `mask' from `data', you can use * and ? wildcards. */
int match_wildcards(const char *cmask, const char *data)
{
char *mask, *newmask, *p1, *p2;
int ret;
newmask = mask = strdup(cmask);
for (; *mask != '\0' && *data != '\0'; mask++) {
if (*mask == '?' || toupper(*mask) == toupper(*data)) {
data++;
continue;
}
if (*mask != '*')
break;
while (*mask == '?' || *mask == '*') mask++;
if (*mask == '\0') {
data += strlen(data);
break;
}
p1 = strchr(mask, '*');
p2 = strchr(mask, '?');
if (p1 == NULL || (p2 < p1 && p2 != NULL)) p1 = p2;
if (p1 != NULL) *p1 = '\0';
data = stristr(data, mask);
if (data == NULL) break;
data += strlen(mask);
mask += strlen(mask)-1;
if (p1 != NULL) *p1 = p1 == p2 ? '?' : '*';
}
ret = data != NULL && *data == '\0' && *mask == '\0';
free(newmask);
return ret;
}
/* Return TRUE if all characters in `str' are numbers.
Stop when `end_char' is found from string. */
int is_numeric(const char *str, char end_char)
{
g_return_val_if_fail(str != NULL, FALSE);
while (*str != '\0' && *str != end_char) {
if (!isdigit(*str)) return FALSE;
str++;
}
return TRUE;
}
/* replace all `from' chars in string to `to' chars. returns `str' */
char *replace_chars(char *str, char from, char to)
{
char *p;
for (p = str; *p != '\0'; p++) {
if (*p == from) *p = to;
}
return str;
}
int octal2dec(int octal)
{
int dec, n;
dec = 0; n = 1;
while (octal != 0) {
dec += n*(octal%10);
octal /= 10; n *= 8;
}
return dec;
}
int dec2octal(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;
}

57
src/core/misc.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef __MISC_H
#define __MISC_H
/* `str' should be type char[MAX_INT_STRLEN] */
#define ltoa(str, num) \
g_snprintf(str, sizeof(str), "%d", num)
typedef void* (*FOREACH_FIND_FUNC) (void *item, void *data);
long get_timeval_diff(const GTimeVal *tv1, const GTimeVal *tv2);
/* find `item' from a space separated `list' */
int find_substr(const char *list, const char *item);
/* return how many items `array' has */
int strarray_length(char **array);
/* return index of `item' in `array' or -1 if not found */
int strarray_find(char **array, const char *item);
int copyfile(const char *src, const char *dest);
int execute(const char *cmd); /* returns pid or -1 = error */
GSList *gslist_find_string(GSList *list, const char *key);
GSList *gslist_find_icase_string(GSList *list, const char *key);
GList *glist_find_string(GList *list, const char *key);
GList *glist_find_icase_string(GList *list, const char *key);
void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, void *data);
char *gslist_to_string(GSList *list, int offset, const char *delimiter);
/* strstr() with case-ignoring */
char *stristr(const char *data, const char *key);
/* stristr(), but matches only for full words */
char *stristr_full(const char *data, const char *key);
/* easy way to check if regexp matches */
int regexp_match(const char *str, const char *regexp);
char *convert_home(const char *path);
/* Case-insensitive string hash functions */
int g_istr_equal(gconstpointer v, gconstpointer v2);
unsigned int g_istr_hash(gconstpointer v);
/* Find `mask' from `data', you can use * and ? wildcards. */
int match_wildcards(const char *mask, const char *data);
/* Return TRUE if all characters in `str' are numbers.
Stop when `end_char' is found from string. */
int is_numeric(const char *str, char end_char);
/* replace all `from' chars in string to `to' chars. returns `str' */
char *replace_chars(char *str, char from, char to);
/* octal <-> decimal conversions */
int octal2dec(int octal);
int dec2octal(int decimal);
#endif

3
src/core/module.h Normal file
View file

@ -0,0 +1,3 @@
#include "common.h"
#define MODULE_NAME "core"

185
src/core/modules.c Normal file
View file

@ -0,0 +1,185 @@
/*
modules.c : irssi
Copyright (C) 1999-2000 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"
#include "modules.h"
static GHashTable *uniqids, *uniqstrids;
static GHashTable *idlookup, *stridlookup;
static int next_uniq_id;
/* return unique number across all modules for `id' */
int module_get_uniq_id(const char *module, int id)
{
GHashTable *ids;
gpointer origkey, uniqid;
int ret;
g_return_val_if_fail(module != NULL, -1);
ids = g_hash_table_lookup(idlookup, module);
if (ids == NULL) {
/* new module */
ids = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
g_hash_table_insert(idlookup, g_strdup(module), ids);
}
if (!g_hash_table_lookup_extended(ids, GINT_TO_POINTER(id), &origkey, &uniqid)) {
/* not found */
ret = next_uniq_id++;
g_hash_table_insert(ids, GINT_TO_POINTER(id), GINT_TO_POINTER(ret));
g_hash_table_insert(uniqids, GINT_TO_POINTER(ret), GINT_TO_POINTER(id));
} else {
ret = GPOINTER_TO_INT(uniqid);
}
return ret;
}
/* return unique number across all modules for `id' */
int module_get_uniq_id_str(const char *module, const char *id)
{
GHashTable *ids;
gpointer origkey, uniqid;
int ret;
g_return_val_if_fail(module != NULL, -1);
ids = g_hash_table_lookup(stridlookup, module);
if (ids == NULL) {
/* new module */
ids = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal);
g_hash_table_insert(stridlookup, g_strdup(module), ids);
}
if (!g_hash_table_lookup_extended(ids, id, &origkey, &uniqid)) {
/* not found */
char *saveid;
saveid = g_strdup(id);
ret = next_uniq_id++;
g_hash_table_insert(ids, saveid, GINT_TO_POINTER(ret));
g_hash_table_insert(uniqstrids, GINT_TO_POINTER(ret), saveid);
} else {
ret = GPOINTER_TO_INT(uniqid);
}
return ret;
}
/* returns the original module specific id, -1 = not found */
int module_find_id(const char *module, int uniqid)
{
GHashTable *ids;
gpointer origkey, id;
int ret;
g_return_val_if_fail(module != NULL, -1);
ret = g_hash_table_lookup_extended(uniqids, GINT_TO_POINTER(uniqid), &origkey, &id) ?
GPOINTER_TO_INT(id) : -1;
if (ret != -1) {
/* check that module matches */
ids = g_hash_table_lookup(idlookup, module);
if (ids == NULL || !g_hash_table_lookup_extended(ids, GINT_TO_POINTER(ret), &origkey, &id))
ret = -1;
}
return ret;
}
/* returns the original module specific id, NULL = not found */
const char *module_find_id_str(const char *module, int uniqid)
{
GHashTable *ids;
gpointer origkey, id;
const char *ret;
g_return_val_if_fail(module != NULL, NULL);
ret = g_hash_table_lookup_extended(uniqstrids, GINT_TO_POINTER(uniqid),
&origkey, &id) ? id : NULL;
if (ret != NULL) {
/* check that module matches */
ids = g_hash_table_lookup(stridlookup, module);
if (ids == NULL || !g_hash_table_lookup_extended(ids, GINT_TO_POINTER(ret), &origkey, &id))
ret = NULL;
}
return ret;
}
static void gh_uniq_destroy(gpointer key, gpointer value)
{
g_hash_table_remove(uniqids, value);
}
static void gh_uniq_destroy_str(gpointer key, gpointer value)
{
g_hash_table_remove(uniqstrids, value);
g_free(key);
}
/* Destroy unique IDs from `module'. This function is automatically called
when module is destroyed with module's name as the parameter. */
void module_uniq_destroy(const char *module)
{
GHashTable *ids;
gpointer key;
if (g_hash_table_lookup_extended(idlookup, module, &key, (gpointer *) &ids)) {
g_hash_table_remove(idlookup, key);
g_free(key);
g_hash_table_foreach(ids, (GHFunc) gh_uniq_destroy, NULL);
g_hash_table_destroy(ids);
}
if (g_hash_table_lookup_extended(stridlookup, module, &key, (gpointer *) &ids)) {
g_hash_table_remove(stridlookup, key);
g_free(key);
g_hash_table_foreach(ids, (GHFunc) gh_uniq_destroy_str, NULL);
g_hash_table_destroy(ids);
}
}
void modules_init(void)
{
idlookup = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal);
uniqids = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
stridlookup = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal);
uniqstrids = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
next_uniq_id = 0;
}
void modules_deinit(void)
{
g_hash_table_foreach(idlookup, (GHFunc) module_uniq_destroy, NULL);
g_hash_table_destroy(idlookup);
g_hash_table_destroy(uniqids);
g_hash_table_foreach(stridlookup, (GHFunc) module_uniq_destroy, NULL);
g_hash_table_destroy(stridlookup);
g_hash_table_destroy(uniqstrids);
}

33
src/core/modules.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef __MODULES_H
#define __MODULES_H
#define MODULE_DATA_INIT(rec) \
(rec)->module_data = g_hash_table_new(g_str_hash, g_str_equal)
#define MODULE_DATA_DEINIT(rec) \
g_hash_table_destroy((rec)->module_data)
#define MODULE_DATA_SET(rec, data) \
g_hash_table_insert((rec)->module_data, MODULE_NAME, data)
#define MODULE_DATA(rec) \
g_hash_table_lookup((rec)->module_data, MODULE_NAME)
/* return unique number across all modules for `id' */
int module_get_uniq_id(const char *module, int id);
/* return unique number across all modules for `id'. */
int module_get_uniq_id_str(const char *module, const char *id);
/* returns the original module specific id, -1 = not found */
int module_find_id(const char *module, int uniqid);
/* returns the original module specific id, NULL = not found */
const char *module_find_id_str(const char *module, int uniqid);
/* Destroy unique IDs from `module'. This function is automatically called
when module is destroyed with module's name as the parameter. */
void module_uniq_destroy(const char *module);
void modules_init(void);
void modules_deinit(void);
#endif

155
src/core/net-disconnect.c Normal file
View file

@ -0,0 +1,155 @@
/*
net-disconnect.c :
Copyright (C) 1999-2000 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"
#include "network.h"
/* when quitting, wait for max. 5 seconds before forcing to close the socket */
#define MAX_QUIT_CLOSE_WAIT 5
/* wait for max. 2 minutes for other side to close the socket */
#define MAX_CLOSE_WAIT (60*2)
typedef struct {
time_t created;
int handle;
int tag;
} NET_DISCONNECT_REC;
static GSList *disconnects;
static int timeout_tag;
static void net_disconnect_remove(NET_DISCONNECT_REC *rec)
{
disconnects = g_slist_remove(disconnects, rec);
g_source_remove(rec->tag);
g_free(rec);
}
static void sig_disconnect(NET_DISCONNECT_REC *rec)
{
char buf[128];
int ret;
/* check if there's any data waiting in socket */
for (;;) {
ret = net_receive(rec->handle, buf, sizeof(buf));
if (ret <= 0)
break;
}
if (ret == -1) {
/* socket was closed */
net_disconnect_remove(rec);
}
}
static int sig_timeout_disconnect(void)
{
NET_DISCONNECT_REC *rec;
GSList *tmp, *next;
time_t now;
int ret;
/* check if we've waited enough for sockets to close themselves */
now = time(NULL);
for (tmp = disconnects; tmp != NULL; tmp = next) {
rec = tmp->data;
next = tmp->next;
if (rec->created+MAX_CLOSE_WAIT <= now)
sig_disconnect(rec);
}
if (disconnects == NULL) {
/* no more sockets in disconnect queue, stop calling this
function */
timeout_tag = -1;
}
ret = disconnects != NULL ? 1 : 0;
return ret;
}
/* Try to let the other side close the connection, if it still isn't
disconnected after certain amount of time, close it ourself */
void net_disconnect_later(int handle)
{
NET_DISCONNECT_REC *rec;
rec = g_new(NET_DISCONNECT_REC, 1);
disconnects = g_slist_append(disconnects, rec);
rec->created = time(NULL);
rec->handle = handle;
rec->tag = g_input_add(handle, G_INPUT_READ, (GInputFunction) sig_disconnect, rec);
if (timeout_tag == -1)
timeout_tag = g_timeout_add(10000, (GSourceFunc) sig_timeout_disconnect, NULL);
}
void net_disconnect_init(void)
{
disconnects = NULL;
timeout_tag = -1;
}
void net_disconnect_deinit(void)
{
NET_DISCONNECT_REC *rec;
time_t now, max;
int first;
struct timeval tv;
fd_set set;
if (disconnects == NULL)
return;
/* give the sockets a chance to disconnect themselves.. */
max = time(NULL)+MAX_QUIT_CLOSE_WAIT;
first = 1;
while (disconnects != NULL) {
rec = disconnects->data;
now = time(NULL);
if (rec->created+MAX_QUIT_CLOSE_WAIT <= now || max <= now) {
/* this one has waited enough */
net_disconnect_remove(rec);
continue;
}
FD_ZERO(&set);
FD_SET(rec->handle, &set);
tv.tv_sec = first ? 0 : max-now;
tv.tv_usec = first ? 100000 : 0;
if (select(rec->handle+1, &set, NULL, NULL, &tv) > 0 && FD_ISSET(rec->handle, &set)) {
/* data coming .. check if we can close the handle */
sig_disconnect(rec);
} else if (first) {
/* Display the text when we have already waited for a while */
printf(_("Please wait, waiting for servers to close connections..\n"));
fflush(stdout);
first = 0;
}
}
}

View file

@ -0,0 +1,7 @@
#ifndef __NET_DISCONNECT_H
#define __NET_DISCONNECT_H
void net_disconnect_init(void);
void net_disconnect_deinit(void);
#endif

6
src/core/net-internal.h Normal file
View file

@ -0,0 +1,6 @@
#ifdef HAVE_SOCKS_H
#include <socks.h>
#endif
#include <netdb.h>
#include <arpa/inet.h>

210
src/core/net-nonblock.c Normal file
View file

@ -0,0 +1,210 @@
/*
net-nonblock.c : Nonblocking net_connect()
Copyright (C) 1998-2000 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"
#include <signal.h>
#include "pidwait.h"
#include "net-nonblock.h"
typedef struct
{
NET_CALLBACK func;
void *data;
int pipes[2];
int port;
IPADDR *my_ip;
int tag;
}
SIMPLE_THREAD_REC;
/* nonblocking gethostbyname(), ip (IPADDR) + error (int, 0 = not error) is
written to pipe when found PID of the resolver child is returned */
int net_gethostname_nonblock(const char *addr, int pipe)
{
RESOLVED_IP_REC rec;
const char *errorstr;
int pid;
g_return_val_if_fail(addr != NULL, FALSE);
pid = fork();
if (pid > 0) {
/* parent */
pidwait_add(pid);
return pid;
}
if (pid != 0) {
/* failed! */
g_warning("net_connect_thread(): fork() failed! Using blocking resolving");
}
/* child */
rec.error = net_gethostname(addr, &rec.ip);
if (rec.error == 0) {
errorstr = NULL;
} else {
errorstr = net_gethosterror(rec.error);
rec.errlen = strlen(errorstr)+1;
}
write(pipe, &rec, sizeof(rec));
if (rec.error != 0)
write(pipe, errorstr, rec.errlen);
if (pid == 0)
_exit(99);
/* we used blocking lookup */
return 0;
}
/* get the resolved IP address */
int net_gethostbyname_return(int pipe, RESOLVED_IP_REC *rec)
{
time_t maxwait;
int len, ret;
rec->error = -1;
rec->errorstr = NULL;
/* get ip+error - try for max. 1-2 seconds */
fcntl(pipe, F_SETFL, O_NONBLOCK);
maxwait = time(NULL)+2;
len = 0;
do {
ret = read(pipe, (char *) rec+len, sizeof(*rec)-len);
if (ret == -1) return -1;
len += ret;
} while (len < sizeof(*rec) && time(NULL) < maxwait);
if (len < sizeof(*rec))
return -1; /* timeout */
if (rec->error) {
/* read error string */
rec->errorstr = g_malloc(rec->errlen);
len = 0;
do {
ret = read(pipe, rec->errorstr+len, rec->errlen-len);
if (ret == -1) break;
len += ret;
} while (len < rec->errlen && time(NULL) < maxwait);
if (len < rec->errlen) {
/* just ignore the rest of the error message.. */
rec->errorstr[len] = '\0';
}
}
return 0;
}
/* Kill the resolver child */
void net_disconnect_nonblock(int pid)
{
g_return_if_fail(pid > 0);
kill(pid, SIGKILL);
}
static void simple_init(SIMPLE_THREAD_REC *rec, int handle)
{
g_return_if_fail(rec != NULL);
g_source_remove(rec->tag);
if (net_geterror(handle) != 0) {
/* failed */
close(handle);
handle = -1;
}
rec->func(handle, rec->data);
g_free(rec);
}
static void simple_readpipe(SIMPLE_THREAD_REC *rec, int pipe)
{
RESOLVED_IP_REC iprec;
int handle;
g_return_if_fail(rec != NULL);
g_source_remove(rec->tag);
net_gethostbyname_return(pipe, &iprec);
g_free_not_null(iprec.errorstr);
close(rec->pipes[0]);
close(rec->pipes[1]);
handle = iprec.error == -1 ? -1 :
net_connect_ip(&iprec.ip, rec->port, rec->my_ip);
g_free_not_null(rec->my_ip);
if (handle == -1) {
/* failed */
rec->func(-1, rec->data);
g_free(rec);
return;
}
rec->tag = g_input_add(handle, G_INPUT_WRITE,
(GInputFunction) simple_init, rec);
}
/* Connect to server, call func when finished */
int net_connect_nonblock(const char *server, int port, const IPADDR *my_ip, NET_CALLBACK func, void *data)
{
SIMPLE_THREAD_REC *rec;
int fd[2];
g_return_val_if_fail(server != NULL, FALSE);
g_return_val_if_fail(func != NULL, FALSE);
if (pipe(fd) != 0) {
g_warning("net_connect_nonblock(): pipe() failed.");
return FALSE;
}
/* start nonblocking host name lookup */
net_gethostname_nonblock(server, fd[1]);
rec = g_new0(SIMPLE_THREAD_REC, 1);
rec->port = port;
if (my_ip != NULL) {
rec->my_ip = g_malloc(sizeof(IPADDR));
memcpy(rec->my_ip, my_ip, sizeof(IPADDR));
}
rec->func = func;
rec->data = data;
rec->pipes[0] = fd[0];
rec->pipes[1] = fd[1];
rec->tag = g_input_add(fd[0], G_INPUT_READ, (GInputFunction) simple_readpipe, rec);
return 1;
}

26
src/core/net-nonblock.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef __NET_NONBLOCK_H
#define __NET_NONBLOCK_H
#include "network.h"
typedef struct {
IPADDR ip; /* resolved ip addres */
int error; /* error, 0 = no error, -1 = error: */
int errlen; /* error text length */
char *errorstr; /* error string - dynamically allocated, you'll
need to free() it yourself unless it's NULL */
} RESOLVED_IP_REC;
typedef void (*NET_CALLBACK) (int, void *);
/* nonblocking gethostbyname(), PID of the resolver child is returned. */
int net_gethostname_nonblock(const char *addr, int pipe);
/* get the resolved IP address. returns -1 if some error occured with read() */
int net_gethostbyname_return(int pipe, RESOLVED_IP_REC *rec);
/* Connect to server, call func when finished */
int net_connect_nonblock(const char *server, int port, const IPADDR *my_ip, NET_CALLBACK func, void *data);
/* Kill the resolver child */
void net_disconnect_nonblock(int pid);
#endif

451
src/core/network.c Normal file
View file

@ -0,0 +1,451 @@
/*
network.c : Network stuff
Copyright (C) 1999-2000 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"
#include "network.h"
#include "net-internal.h"
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
#endif
};
/* Cygwin need this, don't know others.. */
/*#define BLOCKING_SOCKETS 1*/
int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
{
if (ip1->family != ip2->family)
return 0;
#ifdef HAVE_IPV6
if (ip1->family == AF_INET6)
return memcmp(&ip1->addr, &ip2->addr, sizeof(ip1->addr)) == 0;
#endif
return memcmp(&ip1->addr, &ip2->addr, 4) == 0;
}
/* copy IP to sockaddr */
inline void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
{
so->sin.sin_family = ip->family;
#ifdef HAVE_IPV6
if (ip->family == AF_INET6)
memcpy(&so->sin6.sin6_addr, &ip->addr, sizeof(ip->addr.ip6));
else
#endif
memcpy(&so->sin.sin_addr, &ip->addr, 4);
}
inline void sin_get_ip(const union sockaddr_union *so, IPADDR *ip)
{
ip->family = so->sin.sin_family;
#ifdef HAVE_IPV6
if (ip->family == AF_INET6)
memcpy(&ip->addr, &so->sin6.sin6_addr, sizeof(ip->addr.ip6));
else
#endif
memcpy(&ip->addr, &so->sin.sin_addr, 4);
}
G_INLINE_FUNC void sin_set_port(union sockaddr_union *so, int port)
{
#ifdef HAVE_IPV6
if (so->sin.sin_family == AF_INET6)
so->sin6.sin6_port = htons(port);
else
#endif
so->sin.sin_port = htons(port);
}
G_INLINE_FUNC int sin_get_port(union sockaddr_union *so)
{
#ifdef HAVE_IPV6
if (so->sin.sin_family == AF_INET6)
return ntohs(so->sin6.sin6_port);
#endif
return ntohs(so->sin.sin_port);
}
/* Connect to socket */
int net_connect(const char *addr, int port, IPADDR *my_ip)
{
IPADDR ip;
g_return_val_if_fail(addr != NULL, -1);
if (net_gethostname(addr, &ip) == -1)
return -1;
return net_connect_ip(&ip, port, my_ip);
}
/* Connect to socket with ip address */
int net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
{
union sockaddr_union so;
int handle, ret, opt = 1;
/* create the socket */
memset(&so, 0, sizeof(so));
so.sin.sin_family = ip->family;
handle = socket(ip->family, SOCK_STREAM, 0);
if (handle == -1)
return -1;
/* set socket options */
fcntl(handle, F_SETFL, O_NONBLOCK);
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt));
setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt));
/* set our own address, ignore if bind() fails */
if (my_ip != NULL) {
sin_set_ip(&so, my_ip);
bind(handle, &so.sa, sizeof(so));
}
/* connect */
sin_set_ip(&so, ip);
sin_set_port(&so, port);
ret = connect(handle, &so.sa, sizeof(so));
if (ret < 0 && errno != EINPROGRESS) {
close(handle);
return -1;
}
return handle;
}
/* Disconnect socket */
void net_disconnect(int handle)
{
g_return_if_fail(handle != -1);
close(handle);
}
/* Listen for connections on a socket */
int net_listen(IPADDR *my_ip, int *port)
{
union sockaddr_union so;
int ret, handle, opt = 1;
socklen_t len = sizeof(so);
g_return_val_if_fail(my_ip != NULL, -1);
g_return_val_if_fail(port != NULL, -1);
/* create the socket */
memset(&so, 0, sizeof(so));
so.sin.sin_family = my_ip->family;
handle = socket(my_ip->family, SOCK_STREAM, 0);
if (handle == -1)
return -1;
/* set socket options */
fcntl(handle, F_SETFL, O_NONBLOCK);
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt));
setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt));
/* specify the address/port we want to listen in */
sin_set_port(&so, *port);
ret = bind(handle, &so.sa, sizeof(so));
if (ret < 0) {
close(handle);
return -1;
}
/* get the actual port we started listen */
ret = getsockname(handle, &so.sa, &len);
if (ret < 0) {
close(handle);
return -1;
}
*port = sin_get_port(&so);
/* start listening */
if (listen(handle, 1) < 0)
{
close(handle);
return -1;
}
return handle;
}
/* Accept a connection on a socket */
int net_accept(int handle, IPADDR *addr, int *port)
{
union sockaddr_union so;
int ret;
socklen_t addrlen;
g_return_val_if_fail(handle != -1, -1);
g_return_val_if_fail(addr != NULL, -1);
g_return_val_if_fail(port != NULL, -1);
addrlen = sizeof(so);
ret = accept(handle, &so.sa, &addrlen);
if (ret < 0)
return -1;
sin_get_ip(&so, addr);
*port = sin_get_port(&so);
fcntl(ret, F_SETFL, O_NONBLOCK);
return ret;
}
/* Read data from socket, return number of bytes read, -1 = error */
int net_receive(int handle, char *buf, int len)
{
#ifdef BLOCKING_SOCKETS
fd_set set;
struct timeval tv;
#endif
int ret;
g_return_val_if_fail(handle != -1, -1);
g_return_val_if_fail(buf != NULL, -1);
#ifdef BLOCKING_SOCKETS
FD_ZERO(&set);
FD_SET(handle, &set);
tv.tv_sec = 0;
tv.tv_usec = 0;
if (select(handle+1, &set, NULL, NULL, &tv) <= 0 ||
!FD_ISSET(handle, &set)) return 0;
#endif
ret = recv(handle, buf, len, 0);
if (ret == 0)
return -1; /* disconnected */
if (ret == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
return 0; /* no bytes received */
return ret;
}
/* Transmit data, return number of bytes sent, -1 = error */
int net_transmit(int handle, const char *data, int len)
{
int n;
g_return_val_if_fail(handle != -1, -1);
g_return_val_if_fail(data != NULL, -1);
n = send(handle, data, len, 0);
if (n == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
return 0;
return n > 0 ? n : -1;
}
/* Get socket address/port */
int net_getsockname(int handle, IPADDR *addr, int *port)
{
union sockaddr_union so;
#ifdef HAVE_IPV6
socklen_t len = sizeof(so.sin6);
#else
socklen_t len = sizeof(so.sin);
#endif
g_return_val_if_fail(handle != -1, -1);
g_return_val_if_fail(addr != NULL, -1);
#ifdef HAVE_IPV6
if (getsockname(handle, &so.sin6, &len) == -1)
#else
if (getsockname(handle, &so.sin, &len) == -1)
#endif
return -1;
sin_get_ip(&so, addr);
if (port) *port = sin_get_port(&so);
return 0;
}
/* Get IP address for host, returns 0 = ok,
others = error code for net_gethosterror() */
int net_gethostname(const char *addr, IPADDR *ip)
{
#ifdef HAVE_IPV6
union sockaddr_union *so;
struct addrinfo req, *ai;
char hbuf[NI_MAXHOST];
int host_error;
#else
struct hostent *hp;
#endif
g_return_val_if_fail(addr != NULL, -1);
/* host name */
#ifdef HAVE_IPV6
memset(ip, 0, sizeof(IPADDR));
memset(&req, 0, sizeof(struct addrinfo));
req.ai_socktype = SOCK_STREAM;
/* save error to host_error for later use */
host_error = getaddrinfo(addr, NULL, &req, &ai);
if (host_error != 0)
return host_error;
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
return 1;
so = (union sockaddr_union *) ai->ai_addr;
sin_get_ip(so, ip);
freeaddrinfo(ai);
#else
hp = gethostbyname(addr);
if (hp == NULL) return -1;
ip->family = AF_INET;
memcpy(&ip->addr, hp->h_addr, 4);
#endif
return 0;
}
int net_ip2host(IPADDR *ip, char *host)
{
#ifdef HAVE_IPV6
if (!inet_ntop(ip->family, &ip->addr, host, MAX_IP_LEN))
return -1;
#else
unsigned long ip4;
ip4 = ntohl(ip->addr.ip.s_addr);
sprintf(host, "%lu.%lu.%lu.%lu",
(ip4 & 0xff000000) >> 24,
(ip4 & 0x00ff0000) >> 16,
(ip4 & 0x0000ff00) >> 8,
(ip4 & 0x000000ff));
#endif
return 0;
}
int net_host2ip(const char *host, IPADDR *ip)
{
unsigned long addr;
#ifdef HAVE_IPV6
if (strchr(host, ':') != NULL) {
/* IPv6 */
ip->family = AF_INET6;
if (inet_pton(AF_INET6, host, &ip->addr) == 0)
return -1;
} else
#endif
{
/* IPv4 */
ip->family = AF_INET;
#ifdef HAVE_INET_ATON
if (inet_aton(host, &ip->addr.ip.s_addr) == 0)
return -1;
#else
addr = inet_addr(host);
if (addr == INADDR_NONE)
return -1;
memcpy(&ip->addr, &addr, 4);
#endif
}
return 0;
}
/* Get socket error */
int net_geterror(int handle)
{
int data;
socklen_t len = sizeof(data);
if (getsockopt(handle, SOL_SOCKET, SO_ERROR, &data, &len) == -1)
return -1;
return data;
}
/* get error of net_gethostname() */
const char *net_gethosterror(int error)
{
#ifdef HAVE_IPV6
g_return_val_if_fail(error != 0, NULL);
if (error == 1) {
/* getnameinfo() failed ..
FIXME: does strerror return the right error message?? */
return g_strerror(errno);
}
return gai_strerror(error);
#else
switch (h_errno) {
case HOST_NOT_FOUND:
return _("Host not found");
case NO_ADDRESS:
return _("No IP address found for name");
case NO_RECOVERY:
return _("A non-recovable name server error occurred");
case TRY_AGAIN:
return _("A temporary error on an authoritative name server");
}
/* unknown error */
return NULL;
#endif
}
int is_ipv4_address(const char *host)
{
while (*host != '\0') {
if (*host != '.' && !isdigit(*host))
return 0;
host++;
}
return 1;
}
int is_ipv6_address(const char *host)
{
while (*host != '\0') {
if (*host != ':' && !isxdigit(*host))
return 0;
host++;
}
return 1;
}

71
src/core/network.h Normal file
View file

@ -0,0 +1,71 @@
#ifndef __NETWORK_H
#define __NETWORK_H
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
struct _ipaddr {
unsigned short family;
union {
#ifdef HAVE_IPV6
struct in6_addr ip6;
#else
struct in_addr ip;
#endif
} addr;
};
typedef struct _ipaddr IPADDR;
/* maxmimum string length of IP address */
#ifdef HAVE_IPV6
# define MAX_IP_LEN INET6_ADDRSTRLEN
#else
# define MAX_IP_LEN 20
#endif
#define is_ipv6_addr(ip) ((ip)->family != AF_INET)
/* returns 1 if IPADDRs are the same */
int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
/* Connect to socket */
int net_connect(const char *addr, int port, IPADDR *my_ip);
/* Connect to socket with ip address */
int net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip);
/* Disconnect socket */
void net_disconnect(int handle);
/* Try to let the other side close the connection, if it still isn't
disconnected after certain amount of time, close it ourself */
void net_disconnect_later(int handle);
/* Listen for connections on a socket */
int net_listen(IPADDR *my_ip, int *port);
/* Accept a connection on a socket */
int net_accept(int handle, IPADDR *addr, int *port);
/* Read data from socket, return number of bytes read, -1 = error */
int net_receive(int handle, char *buf, int len);
/* Transmit data, return number of bytes sent, -1 = error */
int net_transmit(int handle, const char *data, int len);
/* Get IP address for host, returns 0 = ok,
others = error code for net_gethosterror() */
int net_gethostname(const char *addr, IPADDR *ip);
/* get error of net_gethostname() */
const char *net_gethosterror(int error);
/* Get socket address/port */
int net_getsockname(int handle, IPADDR *addr, int *port);
int net_ip2host(IPADDR *ip, char *host);
int net_host2ip(const char *host, IPADDR *ip);
/* Get socket error */
int net_geterror(int handle);
int is_ipv4_address(const char *host);
int is_ipv6_address(const char *host);
#endif

74
src/core/pidwait.c Normal file
View file

@ -0,0 +1,74 @@
/*
pidwait.c :
Copyright (C) 1999-2000 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"
#include "signals.h"
#include "modules.h"
#include <sys/wait.h>
static GSList *pids;
static unsigned int childcheck_tag;
static int signal_pidwait;
/* add a pid to wait list */
void pidwait_add(int pid)
{
pids = g_slist_append(pids, GINT_TO_POINTER(pid));
}
/* remove pid from wait list */
void pidwait_remove(int pid)
{
pids = g_slist_remove(pids, GINT_TO_POINTER(pid));
}
static int child_check(void)
{
GSList *tmp, *next;
int status;
/* wait for each pid.. */
for (tmp = pids; tmp != NULL; tmp = next) {
next = tmp->next;
if (waitpid(GPOINTER_TO_INT(tmp->data), &status, WNOHANG) > 0) {
/* process terminated, remove from list */
pids = g_slist_remove(pids, tmp->data);
signal_emit_id(signal_pidwait, 1, GPOINTER_TO_INT(tmp->data));
}
}
return 1;
}
void pidwait_init(void)
{
pids = NULL;
childcheck_tag = g_timeout_add(1000, (GSourceFunc) child_check, NULL);
signal_pidwait = module_get_uniq_id_str("signals", "pidwait");
}
void pidwait_deinit(void)
{
g_slist_free(pids);
g_source_remove(childcheck_tag);
}

12
src/core/pidwait.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef __PIDWAIT_H
#define __PIDWAIT_H
void pidwait_init(void);
void pidwait_deinit(void);
/* add a pid to wait list */
void pidwait_add(int pid);
/* remove pid from wait list */
void pidwait_remove(int pid);
#endif

167
src/core/rawlog.c Normal file
View file

@ -0,0 +1,167 @@
/*
rawlog.c : irssi
Copyright (C) 1999-2000 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"
#include "rawlog.h"
#include "modules.h"
#include "signals.h"
#include "misc.h"
#include "settings.h"
#include "common-setup.h"
static int rawlog_lines;
static int signal_rawlog;
RAWLOG_REC *rawlog_create(void)
{
RAWLOG_REC *rec;
rec = g_new0(RAWLOG_REC, 1);
return rec;
}
void rawlog_destroy(RAWLOG_REC *rawlog)
{
g_return_if_fail(rawlog != NULL);
g_slist_foreach(rawlog->lines, (GFunc) g_free, NULL);
g_slist_free(rawlog->lines);
if (rawlog->logging) close(rawlog->file);
g_free(rawlog);
}
/* NOTE! str must be dynamically allocated and must not be freed after! */
static void rawlog_add(RAWLOG_REC *rawlog, char *str)
{
if (rawlog->nlines < rawlog_lines || rawlog_lines <= 2)
rawlog->nlines++;
else {
g_free(rawlog->lines->data);
rawlog->lines = g_slist_remove(rawlog->lines, rawlog->lines->data);
}
if (rawlog->logging) {
write(rawlog->file, str, strlen(str));
write(rawlog->file, "\n", 1);
}
rawlog->lines = g_slist_append(rawlog->lines, str);
signal_emit_id(signal_rawlog, 2, rawlog, str);
}
void rawlog_input(RAWLOG_REC *rawlog, const char *str)
{
g_return_if_fail(rawlog != NULL);
g_return_if_fail(str != NULL);
rawlog_add(rawlog, g_strdup_printf(">> %s", str));
}
void rawlog_output(RAWLOG_REC *rawlog, const char *str)
{
g_return_if_fail(rawlog != NULL);
g_return_if_fail(str != NULL);
rawlog_add(rawlog, g_strdup_printf("<< %s", str));
}
void rawlog_redirect(RAWLOG_REC *rawlog, const char *str)
{
g_return_if_fail(rawlog != NULL);
g_return_if_fail(str != NULL);
rawlog_add(rawlog, g_strdup_printf("--> %s", str));
}
static void rawlog_dump(RAWLOG_REC *rawlog, int f)
{
GSList *tmp;
for (tmp = rawlog->lines; tmp != NULL; tmp = tmp->next) {
write(f, tmp->data, strlen((char *) tmp->data));
write(f, "\n", 1);
}
}
void rawlog_open(RAWLOG_REC *rawlog, const char *fname)
{
char *path;
g_return_if_fail(rawlog != NULL);
g_return_if_fail(fname != NULL);
if (rawlog->logging)
return;
path = convert_home(fname);
rawlog->file = open(path, O_WRONLY | O_APPEND | O_CREAT, LOG_FILE_CREATE_MODE);
g_free(path);
rawlog_dump(rawlog, rawlog->file);
rawlog->logging = rawlog->file != -1;
}
void rawlog_close(RAWLOG_REC *rawlog)
{
if (rawlog->logging) {
close(rawlog->file);
rawlog->logging = 0;
}
}
void rawlog_save(RAWLOG_REC *rawlog, const char *fname)
{
char *path;
int f;
path = convert_home(fname);
f = open(path, O_WRONLY | O_APPEND | O_CREAT, LOG_FILE_CREATE_MODE);
g_free(path);
rawlog_dump(rawlog, f);
close(f);
}
void rawlog_set_size(int lines)
{
rawlog_lines = lines;
}
static void read_settings(void)
{
rawlog_set_size(settings_get_int("rawlog_lines"));
}
void rawlog_init(void)
{
signal_rawlog = module_get_uniq_id_str("signals", "rawlog");
settings_add_int("history", "rawlog_lines", 200);
read_settings();
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
}
void rawlog_deinit(void)
{
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
}

28
src/core/rawlog.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef __RAWLOG_H
#define __RAWLOG_H
typedef struct {
int logging;
int file;
int nlines;
GSList *lines;
} RAWLOG_REC;
RAWLOG_REC *rawlog_create(void);
void rawlog_destroy(RAWLOG_REC *rawlog);
void rawlog_input(RAWLOG_REC *rawlog, const char *str);
void rawlog_output(RAWLOG_REC *rawlog, const char *str);
void rawlog_redirect(RAWLOG_REC *rawlog, const char *str);
void rawlog_set_size(int lines);
void rawlog_open(RAWLOG_REC *rawlog, const char *fname);
void rawlog_close(RAWLOG_REC *rawlog);
void rawlog_save(RAWLOG_REC *rawlog, const char *fname);
void rawlog_init(void);
void rawlog_deinit(void);
#endif

323
src/core/server-redirect.c Normal file
View file

@ -0,0 +1,323 @@
/*
server-redirect.c : irssi
Copyright (C) 1999-2000 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"
#include "signals.h"
#include "misc.h"
#include "server.h"
#include "server-redirect.h"
static int redirect_group;
static void server_eventtable_destroy(char *key, GSList *value)
{
GSList *tmp;
g_free(key);
for (tmp = value; tmp != NULL; tmp = tmp->next) {
REDIRECT_REC *rec = tmp->data;
g_free_not_null(rec->arg);
g_free(rec->name);
g_free(rec);
}
g_slist_free(value);
}
static void server_eventgrouptable_destroy(gpointer key, GSList *value)
{
g_slist_foreach(value, (GFunc) g_free, NULL);
g_slist_free(value);
}
static void server_cmdtable_destroy(char *key, REDIRECT_CMD_REC *value)
{
g_free(key);
g_slist_foreach(value->events, (GFunc) g_free, NULL);
g_slist_free(value->events);
g_free(value);
}
static void sig_disconnected(SERVER_REC *server)
{
g_return_if_fail(server != NULL);
if (server->eventtable != NULL) {
g_hash_table_foreach(server->eventtable, (GHFunc) server_eventtable_destroy, NULL);
g_hash_table_destroy(server->eventtable);
}
g_hash_table_foreach(server->eventgrouptable, (GHFunc) server_eventgrouptable_destroy, NULL);
g_hash_table_destroy(server->eventgrouptable);
if (server->cmdtable != NULL) {
g_hash_table_foreach(server->cmdtable, (GHFunc) server_cmdtable_destroy, NULL);
g_hash_table_destroy(server->cmdtable);
}
}
void server_redirect_initv(SERVER_REC *server, const char *command, int last, GSList *list)
{
REDIRECT_CMD_REC *rec;
g_return_if_fail(server != NULL);
g_return_if_fail(command != NULL);
g_return_if_fail(last > 0);
if (g_hash_table_lookup(server->cmdtable, command) != NULL) {
/* already in hash table. list of events SHOULD be the same... */
g_slist_foreach(list, (GFunc) g_free, NULL);
g_slist_free(list);
return;
}
rec = g_new(REDIRECT_CMD_REC, 1);
rec->last = last;
rec->events = list;
g_hash_table_insert(server->cmdtable, g_strdup(command), rec);
}
void server_redirect_init(SERVER_REC *server, const char *command, int last, ...)
{
va_list args;
GSList *list;
char *event;
va_start(args, last);
list = NULL;
while ((event = va_arg(args, gchar *)) != NULL)
list = g_slist_append(list, g_strdup(event));
va_end(args);
server_redirect_initv(server, command, last, list);
}
int server_redirect_single_event(SERVER_REC *server, const char *arg, int last, int group,
const char *event, const char *signal, int argpos)
{
REDIRECT_REC *rec;
GSList *list, *grouplist;
char *origkey;
g_return_val_if_fail(server != NULL, 0);
g_return_val_if_fail(event != NULL, 0);
g_return_val_if_fail(signal != NULL, 0);
g_return_val_if_fail(arg != NULL || argpos == -1, 0);
if (group == 0) group = ++redirect_group;
rec = g_new0(REDIRECT_REC, 1);
rec->arg = arg == NULL ? NULL : g_strdup(arg);
rec->argpos = argpos;
rec->name = g_strdup(signal);
rec->group = group;
rec->last = last;
if (g_hash_table_lookup_extended(server->eventtable, event, (gpointer *) &origkey, (gpointer *) &list))
g_hash_table_remove(server->eventtable, origkey);
else {
list = NULL;
origkey = g_strdup(event);
}
grouplist = g_hash_table_lookup(server->eventgrouptable, GINT_TO_POINTER(group));
if (grouplist != NULL) g_hash_table_remove(server->eventgrouptable, GINT_TO_POINTER(group));
list = g_slist_append(list, rec);
grouplist = g_slist_append(grouplist, g_strdup(event));
g_hash_table_insert(server->eventtable, origkey, list);
g_hash_table_insert(server->eventgrouptable, GINT_TO_POINTER(group), grouplist);
return group;
}
void server_redirect_event(SERVER_REC *server, const char *arg, int last, ...)
{
va_list args;
char *event, *signal;
int argpos, group;
g_return_if_fail(server != NULL);
va_start(args, last);
group = 0;
while ((event = va_arg(args, gchar *)) != NULL) {
signal = va_arg(args, gchar *);
argpos = va_arg(args, gint);
group = server_redirect_single_event(server, arg, last > 0, group, event, signal, argpos);
last--;
}
va_end(args);
}
void server_redirect_default(SERVER_REC *server, const char *command)
{
REDIRECT_CMD_REC *cmdrec;
REDIRECT_REC *rec;
GSList *events, *list, *grouplist;
char *event, *origkey;
int last;
g_return_if_fail(server != NULL);
g_return_if_fail(command != NULL);
if (server->cmdtable == NULL)
return; /* not connected yet */
cmdrec = g_hash_table_lookup(server->cmdtable, command);
if (cmdrec == NULL) return;
/* add all events used by command to eventtable and eventgrouptable */
redirect_group++; grouplist = NULL; last = cmdrec->last;
for (events = cmdrec->events; events != NULL; events = events->next, last--) {
event = events->data;
if (g_hash_table_lookup_extended(server->eventtable, event, (gpointer *) &origkey, (gpointer *) &list))
g_hash_table_remove(server->eventtable, origkey);
else {
list = NULL;
origkey = g_strdup(event);
}
rec = g_new0(REDIRECT_REC, 1);
rec->argpos = -1;
rec->name = g_strdup(event);
rec->group = redirect_group;
rec->last = last > 0;
grouplist = g_slist_append(grouplist, g_strdup(event));
list = g_slist_append(list, rec);
g_hash_table_insert(server->eventtable, origkey, list);
}
g_hash_table_insert(server->eventgrouptable, GINT_TO_POINTER(redirect_group), grouplist);
}
void server_redirect_remove_next(SERVER_REC *server, const char *event, GSList *item)
{
REDIRECT_REC *rec;
GSList *grouplist, *list, *events, *tmp;
char *origkey;
int group;
g_return_if_fail(server != NULL);
g_return_if_fail(event != NULL);
if (!g_hash_table_lookup_extended(server->eventtable, event, (gpointer *) &origkey, (gpointer *) &list))
return;
rec = item == NULL ? list->data : item->data;
if (!rec->last) {
/* this wasn't last expected event */
return;
}
group = rec->group;
/* get list of events from this group */
grouplist = g_hash_table_lookup(server->eventgrouptable, GINT_TO_POINTER(group));
/* remove all of them */
for (list = grouplist; list != NULL; list = list->next) {
char *event = list->data;
if (!g_hash_table_lookup_extended(server->eventtable, event, (gpointer *) &origkey, (gpointer *) &events)) {
g_warning("server_redirect_remove_next() : event in eventgrouptable but not in eventtable");
continue;
}
/* remove the right group */
for (tmp = events; tmp != NULL; tmp = tmp->next) {
rec = tmp->data;
if (rec->group == group)
break;
}
if (rec == NULL) {
g_warning("server_redirect_remove_next() : event in eventgrouptable but not in eventtable (group)");
continue;
}
g_free(event);
events = g_slist_remove(events, rec);
g_free_not_null(rec->arg);
g_free(rec->name);
g_free(rec);
/* update hash table */
g_hash_table_remove(server->eventtable, origkey);
if (events == NULL)
g_free(origkey);
else
g_hash_table_insert(server->eventtable, origkey, events);
}
g_hash_table_remove(server->eventgrouptable, GINT_TO_POINTER(group));
g_slist_free(grouplist);
}
GSList *server_redirect_getqueue(SERVER_REC *server, const char *event, const char *args)
{
REDIRECT_REC *rec;
GSList *list;
char **arglist;
int found;
list = g_hash_table_lookup(server->eventtable, event);
for (; list != NULL; list = list->next) {
rec = list->data;
if (rec->argpos == -1)
break;
if (rec->arg == NULL)
continue;
/* we need to check that the argument is right.. */
arglist = g_strsplit(args, " ", -1);
found = (strarray_length(arglist) > rec->argpos &&
find_substr(rec->arg, arglist[rec->argpos]));
g_strfreev(arglist);
if (found) break;
}
return list;
}
void servers_redirect_init(void)
{
redirect_group = 0;
signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
}
void servers_redirect_deinit(void)
{
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
}

View file

@ -0,0 +1,38 @@
#ifndef __SERVER_REDIRECT_H
#define __SERVER_REDIRECT_H
#include "server.h"
typedef struct {
int last; /* number of "last" events at the start of the events list */
GSList *events; /* char* list of events */
} REDIRECT_CMD_REC;
typedef struct {
char *name; /* event name */
char *arg; /* argument for event we are expecting or NULL */
int argpos; /* argument position */
int group; /* group of events this belongs to */
int last; /* if this event is received, remove all the events in this group */
}
REDIRECT_REC;
void server_redirect_init(SERVER_REC *server, const char *command, int last, ...);
void server_redirect_initv(SERVER_REC *server, const char *command, int last, GSList *list);
/* ... = char *event1, char *event2, ..., NULL */
void server_redirect_event(SERVER_REC *server, const char *arg, int last, ...);
/* ... = char *event, char *callback_signal, int argpos, ..., NULL */
int server_redirect_single_event(SERVER_REC *server, const char *arg, int last, int group,
const char *event, const char *signal, int argpos);
void server_redirect_default(SERVER_REC *server, const char *command);
void server_redirect_remove_next(SERVER_REC *server, const char *event, GSList *item);
GSList *server_redirect_getqueue(SERVER_REC *server, const char *event, const char *args);
void servers_redirect_init(void);
void servers_redirect_deinit(void);
#endif

273
src/core/server.c Normal file
View file

@ -0,0 +1,273 @@
/*
server.c : irssi
Copyright (C) 1999-2000 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"
#include "modules.h"
#include "signals.h"
#include "line-split.h"
#include "net-nonblock.h"
#include "rawlog.h"
#include "misc.h"
#include "server.h"
#include "server-redirect.h"
#include "settings.h"
GSList *servers, *lookup_servers;
/* connection to server failed */
static void server_cant_connect(SERVER_REC *server, const char *msg)
{
g_return_if_fail(server != NULL);
lookup_servers = g_slist_remove(lookup_servers, server);
signal_emit("server connect failed", 2, server, msg);
if (server->connect_tag != -1)
g_source_remove(server->connect_tag);
if (server->connect_pipe[0] != -1) {
close(server->connect_pipe[0]);
close(server->connect_pipe[1]);
}
MODULE_DATA_DEINIT(server);
g_free(server->tag);
g_free(server->nick);
g_free(server);
}
/* generate tag from server's address */
static char *server_create_address_tag(const char *address)
{
const char *start, *end;
/* try to generate a reasonable server tag */
if (g_strncasecmp(address, "irc", 3) == 0 ||
g_strncasecmp(address, "chat", 4) == 0) {
/* irc-2.cs.hut.fi -> hut, chat.bt.net -> bt */
end = strrchr(address, '.');
start = end-1;
while (start > address && *start != '.') start--;
} else {
/* efnet.cs.hut.fi -> efnet */
end = strchr(address, '.');
start = end;
}
if (start == end) start = address; else start++;
if (end == NULL) end = address + strlen(address);
return g_strndup(start, (int) (end-start));
}
/* create unique tag for server. prefer ircnet's name or
generate it from server's address */
static char *server_create_tag(SERVER_CONNECT_REC *conn)
{
GString *str;
char *tag;
int num;
tag = conn->ircnet != NULL ? g_strdup(conn->ircnet) :
server_create_address_tag(conn->address);
/* then just append numbers after tag until unused is found.. */
str = g_string_new(tag);
for (num = 2; server_find_tag(str->str) != NULL; num++)
g_string_sprintf(str, "%s%d", tag, num);
g_free(tag);
tag = str->str;
g_string_free(str, FALSE);
return tag;
}
static void server_connect_callback_init(SERVER_REC *server, int handle)
{
int error;
error = net_geterror(handle);
if (error != 0) {
server->connection_lost = TRUE;
server_cant_connect(server, g_strerror(error));
return;
}
lookup_servers = g_slist_remove(lookup_servers, server);
g_source_remove(server->connect_tag);
server->connect_tag = -1;
server->connect_time = time(NULL);
server->rawlog = rawlog_create();
servers = g_slist_append(servers, server);
signal_emit("server connected", 1, server);
}
static void server_connect_callback_readpipe(SERVER_REC *server, int handle)
{
SERVER_CONNECT_REC *conn;
RESOLVED_IP_REC iprec;
g_source_remove(server->connect_tag);
server->connect_tag = -1;
net_gethostbyname_return(handle, &iprec);
close(server->connect_pipe[0]);
close(server->connect_pipe[1]);
server->connect_pipe[0] = -1;
server->connect_pipe[1] = -1;
conn = server->connrec;
server->handle = iprec.error == -1 ? -1 :
net_connect_ip(&iprec.ip, conn->proxy != NULL ?
conn->proxy_port : conn->port,
conn->own_ip != NULL ? conn->own_ip : NULL);
if (server->handle == -1) {
/* failed */
server->connection_lost = TRUE;
server_cant_connect(server,
iprec.error != -1 ? g_strerror(errno) : /* connect() failed */
(iprec.errorstr != NULL ? iprec.errorstr : "Host lookup failed")); /* gethostbyname() failed */
g_free_not_null(iprec.errorstr);
return;
}
server->connect_tag = g_input_add(server->handle, G_INPUT_WRITE|G_INPUT_READ,
(GInputFunction) server_connect_callback_init, server);
signal_emit("server connecting", 2, server, &iprec.ip);
}
int server_connect(SERVER_REC *server)
{
g_return_val_if_fail(server != NULL, FALSE);
MODULE_DATA_INIT(server);
if (pipe(server->connect_pipe) != 0) {
g_warning("server_connect(): pipe() failed.");
return FALSE;
}
server->tag = server_create_tag(server->connrec);
server->handle = -1;
server->connect_pid =
net_gethostname_nonblock(server->connrec->proxy != NULL ?
server->connrec->proxy : server->connrec->address,
server->connect_pipe[1]);
server->connect_tag =
g_input_add(server->connect_pipe[0], G_INPUT_READ,
(GInputFunction) server_connect_callback_readpipe, server);
lookup_servers = g_slist_append(lookup_servers, server);
signal_emit("server looking", 1, server);
return TRUE;
}
void server_disconnect(SERVER_REC *server)
{
g_return_if_fail(server != NULL);
if (server->connect_tag != -1) {
/* still connecting to server.. */
if (server->connect_pid != -1)
net_disconnect_nonblock(server->connect_pid);
server_cant_connect(server, NULL);
return;
}
servers = g_slist_remove(servers, server);
signal_emit("server disconnected", 1, server);
if (server->handle != -1)
net_disconnect(server->handle);
MODULE_DATA_DEINIT(server);
rawlog_destroy(server->rawlog);
line_split_free(server->buffer);
g_free(server->tag);
g_free(server->nick);
g_free(server);
}
SERVER_REC *server_find_tag(const char *tag)
{
GSList *tmp;
g_return_val_if_fail(tag != NULL, NULL);
if (*tag == '\0') return NULL;
for (tmp = servers; tmp != NULL; tmp = tmp->next) {
SERVER_REC *server = tmp->data;
if (strcmp(server->tag, tag) == 0)
return server;
}
for (tmp = lookup_servers; tmp != NULL; tmp = tmp->next) {
SERVER_REC *server = tmp->data;
if (strcmp(server->tag, tag) == 0)
return server;
}
return NULL;
}
SERVER_REC *server_find_ircnet(const char *ircnet)
{
GSList *tmp;
g_return_val_if_fail(ircnet != NULL, NULL);
if (*ircnet == '\0') return NULL;
for (tmp = servers; tmp != NULL; tmp = tmp->next) {
SERVER_REC *server = tmp->data;
if (server->connrec->ircnet != NULL &&
strcmp(server->connrec->ircnet, ircnet) == 0) return server;
}
return NULL;
}
void servers_init(void)
{
lookup_servers = servers = NULL;
servers_redirect_init();
}
void servers_deinit(void)
{
while (servers != NULL)
server_disconnect(servers->data);
while (lookup_servers != NULL)
server_cant_connect(lookup_servers->data, NULL);
servers_redirect_deinit();
}

66
src/core/server.h Normal file
View file

@ -0,0 +1,66 @@
#ifndef __SERVER_H
#define __SERVER_H
#ifndef __NETWORK_H
typedef struct _ipaddr IPADDR;
#endif
/* all strings should be either NULL or dynamically allocated */
/* address and nick are mandatory, rest are optional */
typedef struct {
/* if we're connecting via proxy, or just NULLs */
char *proxy;
int proxy_port;
char *proxy_string;
char *address;
int port;
char *ircnet;
IPADDR *own_ip;
} SERVER_CONNECT_REC;
typedef struct {
int type; /* server type */
SERVER_CONNECT_REC *connrec;
time_t connect_time; /* connection time */
char *tag; /* tag name for addressing server */
char *nick; /* current nick */
int connected:1; /* connected to server */
int connection_lost:1; /* Connection lost unintentionally */
int handle; /* socket handle */
int readtag; /* input tag */
/* for net_connect_nonblock() */
int connect_pipe[2];
int connect_tag;
int connect_pid;
/* For deciding if event should be handled internally */
GHashTable *eventtable; /* "event xxx" : GSList* of REDIRECT_RECs */
GHashTable *eventgrouptable; /* event group : GSList* of REDIRECT_RECs */
GHashTable *cmdtable; /* "command xxx" : REDIRECT_CMD_REC* */
void *rawlog;
void *buffer; /* receive buffer */
GHashTable *module_data;
} SERVER_REC;
extern GSList *servers, *lookup_servers;
/* Connect to server */
int server_connect(SERVER_REC *server);
/* Disconnect from server */
void server_disconnect(SERVER_REC *server);
SERVER_REC *server_find_tag(const char *tag);
SERVER_REC *server_find_ircnet(const char *ircnet);
void servers_init(void);
void servers_deinit(void);
#endif

336
src/core/settings.c Normal file
View file

@ -0,0 +1,336 @@
/*
settings.c : Irssi settings
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"
#include "signals.h"
#include "commands.h"
#include "lib-config/iconfig.h"
#include "settings.h"
#include "default-config.h"
#include <signal.h>
CONFIG_REC *mainconfig;
static GHashTable *settings;
static char *last_error_msg;
static const char *settings_get_default_str(const char *key)
{
SETTINGS_REC *rec;
g_return_val_if_fail(key != NULL, NULL);
rec = g_hash_table_lookup(settings, key);
if (rec == NULL) {
g_warning("settings_get_default_str(%s) : unknown setting", key);
return NULL;
}
return rec->def;
}
static int settings_get_default_int(const char *key)
{
SETTINGS_REC *rec;
g_return_val_if_fail(key != NULL, -1);
rec = g_hash_table_lookup(settings, key);
if (rec == NULL) {
g_warning("settings_get_default_int(%s) : unknown setting", key);
return -1;
}
return GPOINTER_TO_INT(rec->def);
}
const char *settings_get_str(const char *key)
{
return iconfig_get_str("settings", key, settings_get_default_str(key));
}
const int settings_get_int(const char *key)
{
return iconfig_get_int("settings", key, settings_get_default_int(key));
}
const int settings_get_bool(const char *key)
{
return iconfig_get_bool("settings", key, settings_get_default_int(key));
}
void settings_add_str(const char *section, const char *key, const char *def)
{
SETTINGS_REC *rec;
g_return_if_fail(key != NULL);
g_return_if_fail(section != NULL);
rec = g_hash_table_lookup(settings, key);
g_return_if_fail(rec == NULL);
rec = g_new0(SETTINGS_REC, 1);
rec->key = g_strdup(key);
rec->section = g_strdup(section);
rec->def = def == NULL ? NULL : g_strdup(def);
g_hash_table_insert(settings, rec->key, rec);
}
void settings_add_int(const char *section, const char *key, int def)
{
SETTINGS_REC *rec;
g_return_if_fail(key != NULL);
g_return_if_fail(section != NULL);
rec = g_hash_table_lookup(settings, key);
g_return_if_fail(rec == NULL);
rec = g_new0(SETTINGS_REC, 1);
rec->type = SETTING_TYPE_INT;
rec->key = g_strdup(key);
rec->section = g_strdup(section);
rec->def = GINT_TO_POINTER(def);
g_hash_table_insert(settings, rec->key, rec);
}
void settings_add_bool(const char *section, const char *key, int def)
{
SETTINGS_REC *rec;
g_return_if_fail(key != NULL);
g_return_if_fail(section != NULL);
rec = g_hash_table_lookup(settings, key);
g_return_if_fail(rec == NULL);
rec = g_new0(SETTINGS_REC, 1);
rec->type = SETTING_TYPE_BOOLEAN;
rec->key = g_strdup(key);
rec->section = g_strdup(section);
rec->def = GINT_TO_POINTER(def);
g_hash_table_insert(settings, rec->key, rec);
}
static void settings_destroy(SETTINGS_REC *rec)
{
if (rec->type == SETTING_TYPE_STRING)
g_free_not_null(rec->def);
g_free(rec->section);
g_free(rec->key);
g_free(rec);
}
void settings_remove(const char *key)
{
SETTINGS_REC *rec;
g_return_if_fail(key != NULL);
rec = g_hash_table_lookup(settings, key);
if (rec == NULL) return;
g_hash_table_remove(settings, key);
settings_destroy(rec);
}
int settings_get_type(const char *key)
{
SETTINGS_REC *rec;
g_return_val_if_fail(key != NULL, -1);
rec = g_hash_table_lookup(settings, key);
return rec == NULL ? -1 : rec->type;
}
/* Get the record of the setting */
SETTINGS_REC *settings_get_record(const char *key)
{
g_return_val_if_fail(key != NULL, NULL);
return g_hash_table_lookup(settings, key);
}
static int settings_compare(SETTINGS_REC *v1, SETTINGS_REC *v2)
{
return strcmp(v1->section, v2->section);
}
static void settings_hash_get(const char *key, SETTINGS_REC *rec, GSList **list)
{
*list = g_slist_insert_sorted(*list, rec, (GCompareFunc) settings_compare);
}
GSList *settings_get_sorted(void)
{
GSList *list;
list = NULL;
g_hash_table_foreach(settings, (GHFunc) settings_hash_get, &list);
return list;
}
void sig_term(int n)
{
/* if we get SIGTERM after this, just die instead of coming back here. */
signal(SIGTERM, SIG_DFL);
/* quit from all servers too.. */
signal_emit("command quit", 1, "");
/* and die */
raise(SIGTERM);
}
static CONFIG_REC *parse_configfile(const char *fname)
{
CONFIG_REC *config;
char *str;
str = fname != NULL ? g_strdup(fname) :
g_strdup_printf("%s/.irssi/config", g_get_home_dir());
config = config_open(str, -1);
g_free(str);
if (config == NULL && *fname != '\0')
return NULL; /* specified config file not found */
if (config != NULL)
config_parse(config);
else {
/* user configuration file not found, use the default one
from sysconfdir */
config = config_open(SYSCONFDIR"/irssi/config", -1);
if (config != NULL)
config_parse(config);
else {
/* no configuration file in sysconfdir ..
use the build-in configuration */
config = config_open(NULL, -1);
config_parse_data(config, default_config, "internal");
}
config_change_file_name(config, fname, 0660);
}
return config;
}
static void sig_print_config_error(void)
{
signal_emit("gui dialog", 2, "error", last_error_msg);
signal_remove("irssi init finished", (SIGNAL_FUNC) sig_print_config_error);
g_free_and_null(last_error_msg);
}
static void init_configfile(void)
{
struct stat statbuf;
char *str;
str = g_strdup_printf("%s/.irssi", g_get_home_dir());
if (stat(str, &statbuf) != 0) {
/* ~/.irssi not found, create it. */
if (mkdir(str, 0700) != 0)
g_error("Couldn't create %s/.irssi directory", g_get_home_dir());
}
g_free(str);
mainconfig = parse_configfile(NULL);
/* any errors? */
if (config_last_error(mainconfig) != NULL) {
last_error_msg = g_strdup_printf("Ignored errors in configuration file:\n%s",
config_last_error(mainconfig));
signal_add("irssi init finished", (SIGNAL_FUNC) sig_print_config_error);
}
signal(SIGTERM, sig_term);
}
static void cmd_rehash(const char *data)
{
CONFIG_REC *tempconfig;
char *str, *fname;
fname = *data != '\0' ? g_strdup(data) :
g_strdup_printf("%s/.irssi/config", g_get_home_dir());
tempconfig = parse_configfile(fname);
g_free(fname);
if (tempconfig == NULL) {
signal_emit("gui dialog", 2, "error", g_strerror(errno));
return;
}
if (config_last_error(tempconfig) != NULL) {
/* error */
str = g_strdup_printf("Errors in configuration file:\n%s",
config_last_error(tempconfig));
signal_emit("gui dialog", 2, "error", str);
g_free(str);
config_close(tempconfig);
return;
}
config_close(mainconfig);
mainconfig = tempconfig;
signal_emit("setup changed", 0);
signal_emit("setup reread", 0);
}
static void cmd_save(const char *data)
{
config_write(mainconfig, *data == '\0' ? NULL : data, 0660);
}
void settings_init(void)
{
settings = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal);
init_configfile();
command_bind("rehash", NULL, (SIGNAL_FUNC) cmd_rehash);
command_bind("save", NULL, (SIGNAL_FUNC) cmd_save);
}
static void settings_hash_free(const char *key, SETTINGS_REC *rec)
{
settings_destroy(rec);
}
void settings_deinit(void)
{
command_unbind("rehash", (SIGNAL_FUNC) cmd_rehash);
command_unbind("save", (SIGNAL_FUNC) cmd_save);
g_free_not_null(last_error_msg);
g_hash_table_foreach(settings, (GHFunc) settings_hash_free, NULL);
g_hash_table_destroy(settings);
if (mainconfig != NULL) config_close(mainconfig);
}

56
src/core/settings.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef __SETTINGS_H
#define __SETTINGS_H
#ifndef __ICONFIG_H
typedef struct _config_rec CONFIG_REC;
#endif
enum {
SETTING_TYPE_STRING,
SETTING_TYPE_INT,
SETTING_TYPE_BOOLEAN,
};
typedef struct {
int type;
char *key;
char *section;
void *def;
} SETTINGS_REC;
/* macros for handling the default Irssi configuration */
#define iconfig_get_str(a, b, c) config_get_str(mainconfig, a, b,c)
#define iconfig_get_int(a, b, c) config_get_int(mainconfig, a, b,c)
#define iconfig_get_bool(a, b, c) config_get_bool(mainconfig, a, b,c)
#define iconfig_list_find(a, b, c, d) config_list_find(mainconfig, a, b, c, d)
#define iconfig_set_str(a, b, c) config_set_str(mainconfig, a, b,c)
#define iconfig_set_int(a, b, c) config_set_int(mainconfig, a, b,c)
#define iconfig_set_bool(a, b, c) config_set_bool(mainconfig, a, b,c)
#define iconfig_node_traverse(a, b) config_node_traverse(mainconfig, a, b)
extern CONFIG_REC *mainconfig;
/* Functions for handling the "settings" node of Irssi configuration */
const char *settings_get_str(const char *key);
const int settings_get_int(const char *key);
const int settings_get_bool(const char *key);
/* Functions to add/remove settings */
void settings_add_str(const char *section, const char *key, const char *def);
void settings_add_int(const char *section, const char *key, int def);
void settings_add_bool(const char *section, const char *key, int def);
void settings_remove(const char *key);
/* Get the type (SETTING_TYPE_xxx) of `key' */
int settings_get_type(const char *key);
/* Get all settings sorted by section. Free the result with g_slist_free() */
GSList *settings_get_sorted(void);
/* Get the record of the setting */
SETTINGS_REC *settings_get_record(const char *key);
void settings_init(void);
void settings_deinit(void);
#endif

356
src/core/signals.c Normal file
View file

@ -0,0 +1,356 @@
/*
signals.c : irssi
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 "signals.h"
#include "modules.h"
#define SIGNAL_LISTS 3
typedef struct {
int emitting; /* signal is being emitted */
int altered; /* some signal functions are marked as NULL */
int stop_emit; /* this signal was stopped */
GPtrArray *modulelist[SIGNAL_LISTS]; /* list of what signals belong
to which module */
GPtrArray *siglist[SIGNAL_LISTS]; /* signal lists */
} SIGNAL_REC;
#define signal_is_emitlist_empty(a) \
(!(a)->siglist[0] && !(a)->siglist[1] && !(a)->siglist[2])
static GMemChunk *signals_chunk;
static GHashTable *signals;
static SIGNAL_REC *first_signal_rec, *last_signal_rec; /* "signal" and "last signal" */
static SIGNAL_REC *current_emitted_signal;
/* bind a signal */
void signal_add_to(const char *module, int pos, const char *signal, SIGNAL_FUNC func)
{
SIGNAL_REC *rec;
int signal_id;
g_return_if_fail(signal != NULL);
g_return_if_fail(func != NULL);
g_return_if_fail(pos >= 0 && pos < SIGNAL_LISTS);
signal_id = module_get_uniq_id_str("signals", signal);
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec == NULL) {
rec = g_mem_chunk_alloc0(signals_chunk);
g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), rec);
}
if (strcmp(signal, "signal") == 0)
first_signal_rec = rec;
else if (strcmp(signal, "last signal") == 0)
last_signal_rec = rec;
if (rec->siglist[pos] == NULL) {
rec->siglist[pos] = g_ptr_array_new();
rec->modulelist[pos] = g_ptr_array_new();
}
g_ptr_array_add(rec->siglist[pos], (void *) func);
g_ptr_array_add(rec->modulelist[pos], (void *) module);
}
/* Destroy the whole signal */
static void signal_destroy(int signal_id)
{
SIGNAL_REC *rec;
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec != NULL) {
/* remove whole signal from memory */
g_hash_table_remove(signals, GINT_TO_POINTER(signal_id));
g_free(rec);
}
if (first_signal_rec == rec)
first_signal_rec = NULL;
else if (last_signal_rec == rec)
last_signal_rec = NULL;
}
static int signal_list_find(GPtrArray *array, void *data)
{
int n;
for (n = 0; n < array->len; n++) {
if (g_ptr_array_index(array, n) == data)
return n;
}
return -1;
}
static void signal_remove_from_list(SIGNAL_REC *rec, int signal_id, int list, int index)
{
if (rec->emitting) {
g_ptr_array_index(rec->siglist[list], index) = NULL;
rec->altered = TRUE;
} else {
g_ptr_array_remove_index(rec->siglist[list], index);
g_ptr_array_remove_index(rec->modulelist[list], index);
if (signal_is_emitlist_empty(rec))
signal_destroy(signal_id);
}
}
/* Remove signal from emit lists */
static int signal_remove_from_lists(SIGNAL_REC *rec, int signal_id, SIGNAL_FUNC func)
{
int n, index;
for (n = 0; n < SIGNAL_LISTS; n++) {
if (rec->siglist[n] == NULL)
continue;
index = signal_list_find(rec->siglist[n], (void *) func);
if (index != -1) {
/* remove the function from emit list */
signal_remove_from_list(rec, signal_id, n, index);
return 1;
}
}
return 0;
}
/* unbind signal */
void signal_remove(const char *signal, SIGNAL_FUNC func)
{
SIGNAL_REC *rec;
int signal_id, found;
g_return_if_fail(signal != NULL);
g_return_if_fail(func != NULL);
signal_id = module_get_uniq_id_str("signals", signal);
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
found = rec == NULL ? 0 : signal_remove_from_lists(rec, signal_id, func);
if (!found) g_warning("signal_remove() : signal \"%s\" isn't grabbed for %p", signal, func);
}
/* Remove all NULL functions from signal list */
static void signal_list_clean(SIGNAL_REC *rec)
{
int n, index;
for (n = 0; n < SIGNAL_LISTS; n++) {
if (rec->siglist[n] == NULL)
continue;
for (index = rec->siglist[n]->len-1; index >= 0; index--) {
if (g_ptr_array_index(rec->siglist[n], index) == NULL) {
g_ptr_array_remove_index(rec->siglist[n], index);
g_ptr_array_remove_index(rec->modulelist[n], index);
}
}
}
}
static int signal_emit_real(SIGNAL_REC *rec, gconstpointer *arglist)
{
SIGNAL_REC *prev_emitted_signal;
SIGNAL_FUNC func;
int n, index, stopped;
stopped = FALSE;
rec->emitting++;
for (n = 0; n < SIGNAL_LISTS; n++) {
/* run signals in emit lists */
if (rec->siglist[n] == NULL)
continue;
for (index = rec->siglist[n]->len-1; index >= 0; index--) {
func = (SIGNAL_FUNC) g_ptr_array_index(rec->siglist[n], index);
if (func != NULL) {
prev_emitted_signal = current_emitted_signal;
current_emitted_signal = rec;
func(arglist[0], arglist[1], arglist[2], arglist[3], arglist[4], arglist[5], arglist[6]);
current_emitted_signal = prev_emitted_signal;
}
if (rec->stop_emit) {
stopped = TRUE;
rec->stop_emit--;
n = SIGNAL_LISTS;
break;
}
}
}
rec->emitting--;
if (!rec->emitting && rec->altered) {
signal_list_clean(rec);
rec->altered = FALSE;
}
return stopped;
}
static int signal_emitv_id(int signal_id, int params, va_list va)
{
gconstpointer arglist[8];
SIGNAL_REC *rec;
int n;
g_return_val_if_fail(signal_id >= 0, FALSE);
g_return_val_if_fail(params >= 0 && params <= sizeof(arglist)/sizeof(arglist[0]), FALSE);
arglist[0] = GINT_TO_POINTER(signal_id);
for (n = 1; n < 8; n++)
arglist[n] = n >= params ? NULL : va_arg(va, gconstpointer);
/* send "signal" */
if (first_signal_rec != NULL) {
if (signal_emit_real(first_signal_rec, arglist))
return TRUE;
}
rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
if (rec != NULL && signal_emit_real(rec, arglist+1))
return TRUE;
/* send "last signal" */
if (last_signal_rec != NULL) {
if (signal_emit_real(last_signal_rec, arglist))
return TRUE;
}
return rec != NULL;
}
int signal_emit(const char *signal, int params, ...)
{
va_list va;
int signal_id, ret;
/* get arguments */
signal_id = module_get_uniq_id_str("signals", signal);
va_start(va, params);
ret = signal_emitv_id(signal_id, params, va);
va_end(va);
return ret;
}
int signal_emit_id(int signal_id, int params, ...)
{
va_list va;
int ret;
/* get arguments */
va_start(va, params);
ret = signal_emitv_id(signal_id, params, va);
va_end(va);
return ret;
}
/* stop the current ongoing signal emission */
void signal_stop(void)
{
SIGNAL_REC *rec;
rec = current_emitted_signal;
if (rec == NULL || rec->emitting <= rec->stop_emit)
g_warning("signal_stop() : no signals are being emitted currently");
else
rec->stop_emit++;
}
/* stop ongoing signal emission by signal name */
void signal_stop_by_name(const char *signal)
{
SIGNAL_REC *rec;
rec = g_hash_table_lookup(signals, signal);
if (rec == NULL)
g_warning("signal_stop_by_name() : unknown signal \"%s\"", signal);
else if (rec->emitting <= rec->stop_emit)
g_warning("signal_stop_by_name() : signal \"%s\" not being emitted", signal);
else
rec->stop_emit++;
}
static void signal_remove_module(void *signal, SIGNAL_REC *rec, const char *module)
{
int signal_id, list, index;
signal_id = GPOINTER_TO_INT(signal);
for (list = 0; list < SIGNAL_LISTS; list++) {
for (index = 0; index < rec->modulelist[list]->len; index++) {
if (g_strcasecmp(g_ptr_array_index(rec->modulelist[list], index), module) == 0)
signal_remove_from_list(rec, signal_id, list, index);
}
}
}
/* remove all signals that belong to `module' */
void signals_remove_module(const char *module)
{
g_return_if_fail(module != NULL);
g_hash_table_foreach(signals, (GHFunc) signal_remove_module, (void *) module);
}
void signals_init(void)
{
signals_chunk = g_mem_chunk_new("signals", sizeof(SIGNAL_REC),
sizeof(SIGNAL_REC)*200, G_ALLOC_AND_FREE);
signals = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
first_signal_rec = NULL;
last_signal_rec = NULL;
}
static void signal_free(void *key, SIGNAL_REC *rec)
{
int n;
for (n = 0; n < SIGNAL_LISTS; n++) {
if (rec->siglist[n] != NULL) {
g_ptr_array_free(rec->siglist[n], TRUE);
g_ptr_array_free(rec->modulelist[n], TRUE);
}
}
g_mem_chunk_free(signals_chunk, rec);
current_emitted_signal = NULL;
}
void signals_deinit(void)
{
g_hash_table_foreach(signals, (GHFunc) signal_free, NULL);
g_hash_table_destroy(signals);
module_uniq_destroy("signals");
g_mem_chunk_destroy(signals_chunk);
}

30
src/core/signals.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef __SIGNAL_H
#define __SIGNAL_H
typedef void (*SIGNAL_FUNC) (gconstpointer, gconstpointer, gconstpointer, gconstpointer, gconstpointer, gconstpointer, gconstpointer);
void signals_init(void);
void signals_deinit(void);
/* bind a signal */
void signal_add_to(const char *module, int pos, const char *signal, SIGNAL_FUNC func);
#define signal_add(a, b) signal_add_to(MODULE_NAME, 1, a, b)
#define signal_add_first(a, b) signal_add_to(MODULE_NAME, 0, a, b)
#define signal_add_last(a, b) signal_add_to(MODULE_NAME, 2, a, b)
/* unbind signal */
void signal_remove(const char *signal, SIGNAL_FUNC func);
/* emit signal */
int signal_emit(const char *signal, int params, ...);
int signal_emit_id(int signal_id, int params, ...);
/* stop the current ongoing signal emission */
void signal_stop(void);
/* stop ongoing signal emission by signal name */
void signal_stop_by_name(const char *signal);
/* remove all signals that belong to `module' */
void signals_remove_module(const char *module);
#endif

635
src/core/special-vars.c Normal file
View file

@ -0,0 +1,635 @@
/*
special-vars.c : irssi
Copyright (C) 2000 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"
#include "signals.h"
#include "special-vars.h"
#include "settings.h"
#include "misc.h"
#include "irssi-version.h"
#include <sys/utsname.h>
#define ALIGN_RIGHT 0x01
#define ALIGN_CUT 0x02
static EXPANDO_FUNC char_expandos[256];
static GHashTable *expandos;
static time_t client_start_time;
static SPECIAL_HISTORY_FUNC history_func;
static char *get_argument(char **cmd, char **arglist)
{
GString *str;
char *ret;
int max, arg, argcount;
arg = 0;
max = -1;
argcount = strarray_length(arglist);
if (**cmd == '*') {
/* get all arguments */
} else if (**cmd == '~') {
/* get last argument */
arg = max = argcount-1;
} else {
if (isdigit(**cmd)) {
/* first argument */
arg = max = (**cmd)-'0';
(*cmd)++;
}
if (**cmd == '-') {
/* get more than one argument */
(*cmd)++;
if (!isdigit(**cmd))
max = -1; /* get all the rest */
else {
max = (**cmd)-'0';
(*cmd)++;
}
}
(*cmd)--;
}
str = g_string_new(NULL);
while (arg < argcount && (arg <= max || max == -1)) {
g_string_append(str, arglist[arg]);
g_string_append_c(str, ' ');
arg++;
}
if (str->len > 0) g_string_truncate(str, str->len-1);
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
static char *get_internal_setting(const char *key, int type, int *free_ret)
{
switch (type) {
case SETTING_TYPE_BOOLEAN:
return settings_get_bool(key) ? "yes" : "no";
case SETTING_TYPE_INT:
*free_ret = TRUE;
return g_strdup_printf("%d", settings_get_int(key));
case SETTING_TYPE_STRING:
return (char *) settings_get_str(key);
}
return NULL;
}
static char *get_long_variable_value(const char *key, void *server, void *item, int *free_ret)
{
EXPANDO_FUNC func;
char *ret;
int type;
*free_ret = FALSE;
/* expando? */
func = g_hash_table_lookup(expandos, key);
if (func != NULL)
return func(server, item, free_ret);
/* internal setting? */
type = settings_get_type(key);
if (type != -1)
return get_internal_setting(key, type, free_ret);
/* environment variable? */
ret = g_getenv(key);
if (ret != NULL) {
*free_ret = TRUE;
return ret;
}
return NULL;
}
static char *get_long_variable(char **cmd, void *server, void *item, int *free_ret)
{
char *start, *var, *ret;
/* get variable name */
start = *cmd;
while (isalnum((*cmd)[1])) (*cmd)++;
var = g_strndup(start, (int) (*cmd-start)+1);
ret = get_long_variable_value(var, server, item, free_ret);
g_free(var);
return ret;
}
/* return the value of the variable found from `cmd' */
static char *get_variable(char **cmd, void *server, void *item, char **arglist, int *free_ret, int *arg_used)
{
if (isdigit(**cmd) || **cmd == '*' || **cmd == '-' || **cmd == '~') {
/* argument */
*free_ret = TRUE;
if (arg_used != NULL) *arg_used = TRUE;
return get_argument(cmd, arglist);
}
if (isalpha(**cmd) && isalnum((*cmd)[1])) {
/* long variable name.. */
return get_long_variable(cmd, server, item, free_ret);
}
/* single character variable. */
*free_ret = FALSE;
return char_expandos[(int) **cmd] == NULL ? NULL :
char_expandos[(int) **cmd](server, item, free_ret);
}
static char *get_history(char **cmd, void *item, int *free_ret)
{
char *start, *text, *ret;
/* get variable name */
start = ++(*cmd);
while (**cmd != '\0' && **cmd != '!') (*cmd)++;
if (history_func == NULL)
ret = NULL;
else {
text = g_strndup(start, (int) (*cmd-start)+1);
ret = history_func(text, item, free_ret);
g_free(text);
}
if (**cmd == '\0') (*cmd)--;
return ret;
}
static char *get_special_value(char **cmd, void *server, void *item, char **arglist, int *free_ret, int *arg_used)
{
char command, *value, *p;
int len;
if (**cmd == '!') {
/* find text from command history */
return get_history(cmd, item, free_ret);
}
command = 0;
if (**cmd == '#' || **cmd == '@') {
command = **cmd;
if ((*cmd)[1] != '\0')
(*cmd)++;
else {
/* default to $* */
char *temp_cmd = "*";
*free_ret = TRUE;
return get_argument(&temp_cmd, arglist);
}
}
value = get_variable(cmd, server, item, arglist, free_ret, arg_used);
if (command == '#') {
/* number of words */
if (value == NULL || *value == '\0') {
if (value != NULL && *free_ret) {
g_free(value);
*free_ret = FALSE;
}
return "0";
}
len = 1;
for (p = value; *p != '\0'; p++) {
if (*p == ' ' && (p[1] != ' ' && p[1] != '\0'))
len++;
}
if (*free_ret) g_free(value);
*free_ret = TRUE;
return g_strdup_printf("%d", len);
}
if (command == '@') {
/* number of characters */
if (value == NULL) return "0";
len = strlen(value);
if (*free_ret) g_free(value);
*free_ret = TRUE;
return g_strdup_printf("%d", len);
}
return value;
}
/* get alignment arguments (inside the []) */
static int get_alignment_args(char **data, int *align, int *flags, char *pad)
{
char *str;
*align = 0;
*flags = ALIGN_CUT;
*pad = ' ';
/* '!' = don't cut, '-' = right padding */
str = *data;
while (*str != '\0' && *str != ']' && !isdigit(*str)) {
if (*str == '!')
*flags &= ~ALIGN_CUT;
else if (*str == '-')
*flags |= ALIGN_RIGHT;
str++;
}
if (!isdigit(*str))
return FALSE; /* expecting number */
/* get the alignment size */
while (isdigit(*str)) {
*align = (*align) * 10 + (*str-'0');
str++;
}
/* get the pad character */
while (*str != '\0' && *str != ']') {
*pad = *str;
str++;
}
if (*str++ != ']') return FALSE;
*data = str;
return TRUE;
}
/* return the aligned text */
static char *get_alignment(const char *text, int align, int flags, char pad)
{
GString *str;
char *ret;
g_return_val_if_fail(text != NULL, NULL);
str = g_string_new(text);
/* cut */
if ((flags & ALIGN_CUT) && align > 0 && str->len > align)
g_string_truncate(str, align);
/* add pad characters */
while (str->len < align) {
if (flags & ALIGN_RIGHT)
g_string_prepend_c(str, pad);
else
g_string_append_c(str, pad);
}
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
/* Parse and expand text after '$' character. return value has to be
g_free()'d if `free_ret' is TRUE. */
char *parse_special(char **cmd, void *server, void *item, char **arglist, int *free_ret, int *arg_used)
{
static char **nested_orig_cmd = NULL; /* FIXME: KLUDGE! */
char command, *value;
char align_pad;
int align, align_flags;
char *nest_value;
int brackets, nest_free;
*free_ret = FALSE;
command = **cmd; (*cmd)++;
switch (command) {
case '[':
/* alignment */
if (!get_alignment_args(cmd, &align, &align_flags, &align_pad) ||
**cmd == '\0') {
(*cmd)--;
return NULL;
}
break;
default:
command = 0;
(*cmd)--;
}
nest_free = FALSE; nest_value = NULL;
if (**cmd == '(') {
/* subvariable */
int toplevel = nested_orig_cmd == NULL;
if (toplevel) nested_orig_cmd = cmd;
(*cmd)++;
if (**cmd != '$') {
/* ... */
nest_value = *cmd;
} else {
(*cmd)++;
nest_value = parse_special(cmd, server, item, arglist, &nest_free, arg_used);
}
while ((*nested_orig_cmd)[1] != '\0') {
(*nested_orig_cmd)++;
if (**nested_orig_cmd == ')') break;
}
cmd = &nest_value;
if (toplevel) nested_orig_cmd = NULL;
}
if (**cmd != '{')
brackets = FALSE;
else {
/* special value is inside {...} (foo${test}bar -> fooXXXbar) */
(*cmd)++;
brackets = TRUE;
}
value = get_special_value(cmd, server, item, arglist, free_ret, arg_used);
if (**cmd == '\0')
g_error("parse_special() : buffer overflow!");
if (brackets) {
while (**cmd != '}' && (*cmd)[1] != '\0')
(*cmd)++;
}
if (nest_free) g_free(nest_value);
if (command == '[') {
/* alignment */
char *p;
if (value == NULL) return "";
p = get_alignment(value, align, align_flags, align_pad);
if (*free_ret) g_free(value);
*free_ret = TRUE;
return p;
}
return value;
}
/* parse the whole string. $ and \ chars are replaced */
char *parse_special_string(const char *cmd, void *server, void *item, const char *data, int *arg_used)
{
char code, **arglist, *ret;
GString *str;
int need_free;
g_return_val_if_fail(cmd != NULL, NULL);
g_return_val_if_fail(data != NULL, NULL);
/* create the argument list */
arglist = g_strsplit(data, " ", -1);
if (arg_used != NULL) *arg_used = FALSE;
code = 0;
str = g_string_new(NULL);
while (*cmd != '\0') {
if (code == '\\'){
g_string_append_c(str, *cmd);
code = 0;
} else if (code == '$') {
char *ret;
ret = parse_special((char **) &cmd, server, item, arglist, &need_free, arg_used);
if (ret != NULL) {
g_string_append(str, ret);
if (need_free) g_free(ret);
}
code = 0;
} else {
if (*cmd == '\\' || *cmd == '$')
code = *cmd;
else
g_string_append_c(str, *cmd);
}
cmd++;
}
g_strfreev(arglist);
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
/* execute the commands in string - commands can be split with ';' */
void eval_special_string(const char *cmd, const char *data, void *server, void *item)
{
const char *cmdchars;
char *orig, *str, *start, *ret;
int arg_used;
cmdchars = settings_get_str("cmdchars");
orig = start = str = g_strdup(cmd);
do {
if (*str == ';' && (start == str || (str[-1] != '\\' && str[-1] != '$')))
*str++ = '\0';
else if (*str != '\0') {
str++;
continue;
}
ret = parse_special_string(start, server, item, data, &arg_used);
if (strchr(cmdchars, *ret) == NULL) {
/* no command char - let's put it there.. */
char *old = ret;
ret = g_strdup_printf("%c%s", *cmdchars, old);
g_free(old);
}
if (!arg_used && *data != '\0') {
/* append the string with all the arguments */
char *old = ret;
ret = g_strconcat(old, " ", data, NULL);
g_free(old);
}
signal_emit("send command", 3, ret, server, item);
g_free(ret);
start = str;
} while (*start != '\0');
g_free(orig);
}
/* Create expando - overrides any existing ones. */
void expando_create(const char *key, EXPANDO_FUNC func)
{
gpointer origkey, origvalue;
g_return_if_fail(key != NULL || *key == '\0');
g_return_if_fail(func != NULL);
if (key[1] == '\0') {
/* single character expando */
char_expandos[(int) *key] = func;
return;
}
if (g_hash_table_lookup_extended(expandos, key, &origkey, &origvalue)) {
g_free(origkey);
g_hash_table_remove(expandos, key);
}
g_hash_table_insert(expandos, g_strdup(key), func);
}
/* Destroy expando */
void expando_destroy(const char *key, EXPANDO_FUNC func)
{
gpointer origkey, origvalue;
g_return_if_fail(key != NULL || *key == '\0');
g_return_if_fail(func != NULL);
if (key[1] == '\0') {
/* single character expando */
if (char_expandos[(int) *key] == func)
char_expandos[(int) *key] = NULL;
return;
}
if (g_hash_table_lookup_extended(expandos, key, &origkey, &origvalue)) {
g_free(origkey);
g_hash_table_remove(expandos, key);
}
}
void special_history_func_set(SPECIAL_HISTORY_FUNC func)
{
history_func = func;
}
/* time client was started, $time() format */
static char *expando_clientstarted(void *server, void *item, int *free_ret)
{
*free_ret = TRUE;
return g_strdup_printf("%ld", (long) client_start_time);
}
/* client version text string */
static char *expando_version(void *server, void *item, int *free_ret)
{
return IRSSI_VERSION;
}
/* current value of CMDCHARS */
static char *expando_cmdchars(void *server, void *item, int *free_ret)
{
return (char *) settings_get_str("cmdchars");
}
/* client release date (numeric version string) */
static char *expando_releasedate(void *server, void *item, int *free_ret)
{
return IRSSI_VERSION_DATE;
}
/* current working directory */
static char *expando_workdir(void *server, void *item, int *free_ret)
{
*free_ret = TRUE;
return g_get_current_dir();
}
/* time of day (hh:mm) */
static char *expando_time(void *server, void *item, int *free_ret)
{
time_t now = time(NULL);
struct tm *tm;
tm = localtime(&now);
*free_ret = TRUE;
return g_strdup_printf("%02d:%02d", tm->tm_hour, tm->tm_min);
}
/* a literal '$' */
static char *expando_dollar(void *server, void *item, int *free_ret)
{
return "$";
}
/* system name */
static char *expando_sysname(void *server, void *item, int *free_ret)
{
struct utsname un;
if (uname(&un) != 0)
return NULL;
*free_ret = TRUE;
return g_strdup(un.sysname);
}
/* system release */
static char *expando_sysrelease(void *server, void *item, int *free_ret)
{
struct utsname un;
if (uname(&un) != 0)
return NULL;
*free_ret = TRUE;
return g_strdup(un.release);
}
void special_vars_init(void)
{
client_start_time = time(NULL);
memset(char_expandos, 0, sizeof(char_expandos));
expandos = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal);
history_func = NULL;
char_expandos['F'] = expando_clientstarted;
char_expandos['J'] = expando_version;
char_expandos['K'] = expando_cmdchars;
char_expandos['V'] = expando_releasedate;
char_expandos['W'] = expando_workdir;
char_expandos['Z'] = expando_time;
char_expandos['$'] = expando_dollar;
expando_create("sysname", expando_sysname);
expando_create("sysrelease", expando_sysrelease);
}
void special_vars_deinit(void)
{
expando_destroy("sysname", expando_sysname);
expando_destroy("sysrelease", expando_sysrelease);
g_hash_table_destroy(expandos);
}

27
src/core/special-vars.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef __SPECIAL_VARS_H
#define __SPECIAL_VARS_H
typedef char* (*EXPANDO_FUNC) (void *server, void *item, int *free_ret);
typedef char* (*SPECIAL_HISTORY_FUNC) (const char *text, void *item, int *free_ret);
/* Parse and expand text after '$' character. return value has to be
g_free()'d if `free_ret' is TRUE. */
char *parse_special(char **cmd, void *server, void *item, char **arglist, int *free_ret, int *arg_used);
/* parse the whole string. $ and \ chars are replaced */
char *parse_special_string(const char *cmd, void *server, void *item, const char *data, int *arg_used);
/* execute the commands in string - commands can be split with ';' */
void eval_special_string(const char *cmd, const char *data, void *server, void *item);
/* Create expando - overrides any existing ones. */
void expando_create(const char *key, EXPANDO_FUNC func);
/* Destroy expando */
void expando_destroy(const char *key, EXPANDO_FUNC func);
void special_history_func_set(SPECIAL_HISTORY_FUNC func);
void special_vars_init(void);
void special_vars_deinit(void);
#endif