mirror of
https://github.com/irssi/irssi.git
synced 2026-08-11 04:43:34 +02:00
..adding new files..
git-svn-id: http://svn.irssi.org/repos/irssi/trunk@171 dbcabf3a-b0e7-0310-adc4-f8d773084564
This commit is contained in:
parent
d01b094151
commit
c95034c6de
206 changed files with 31247 additions and 0 deletions
59
src/core/Makefile.am
Normal file
59
src/core/Makefile.am
Normal 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
60
src/core/args.c
Normal 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
15
src/core/args.h
Normal 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
462
src/core/commands.c
Normal 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
74
src/core/commands.h
Normal 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
68
src/core/core.c
Normal 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
17
src/core/core.h
Normal 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
160
src/core/levels.c
Normal 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
44
src/core/levels.h
Normal 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
147
src/core/line-split.c
Normal 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
13
src/core/line-split.h
Normal 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
438
src/core/log.c
Normal 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
49
src/core/log.h
Normal 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
356
src/core/memdebug.c
Normal 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
31
src/core/memdebug.h
Normal 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
467
src/core/misc.c
Normal 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
57
src/core/misc.h
Normal 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
3
src/core/module.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#include "common.h"
|
||||
|
||||
#define MODULE_NAME "core"
|
||||
185
src/core/modules.c
Normal file
185
src/core/modules.c
Normal 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
33
src/core/modules.h
Normal 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
155
src/core/net-disconnect.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/core/net-disconnect.h
Normal file
7
src/core/net-disconnect.h
Normal 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
6
src/core/net-internal.h
Normal 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
210
src/core/net-nonblock.c
Normal 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
26
src/core/net-nonblock.h
Normal 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
451
src/core/network.c
Normal 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
71
src/core/network.h
Normal 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
74
src/core/pidwait.c
Normal 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
12
src/core/pidwait.h
Normal 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
167
src/core/rawlog.c
Normal 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
28
src/core/rawlog.h
Normal 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
323
src/core/server-redirect.c
Normal 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);
|
||||
}
|
||||
38
src/core/server-redirect.h
Normal file
38
src/core/server-redirect.h
Normal 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
273
src/core/server.c
Normal 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
66
src/core/server.h
Normal 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
336
src/core/settings.c
Normal 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
56
src/core/settings.h
Normal 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
356
src/core/signals.c
Normal 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
30
src/core/signals.h
Normal 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
635
src/core/special-vars.c
Normal 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
27
src/core/special-vars.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue