aboutsummaryrefslogtreecommitdiffstats
path: root/main/eggdrop
diff options
context:
space:
mode:
authorNatanael Copa <ncopa@alpinelinux.org>2009-07-24 08:01:31 +0000
committerNatanael Copa <ncopa@alpinelinux.org>2009-07-24 08:01:31 +0000
commitb70981b68efcce5256eb11c6cd26ae123b10b6ea (patch)
treea38be6efae5e2ba15c2e839504632f9b7bfd5f91 /main/eggdrop
parent2b4df81538b8398442d5296650905c70341dd8d3 (diff)
downloadaports-b70981b68efcce5256eb11c6cd26ae123b10b6ea.tar.bz2
aports-b70981b68efcce5256eb11c6cd26ae123b10b6ea.tar.xz
moved extra/* to main/
and fixed misc build issues
Diffstat (limited to 'main/eggdrop')
-rw-r--r--main/eggdrop/APKBUILD69
-rw-r--r--main/eggdrop/eggdrop-installer84
-rw-r--r--main/eggdrop/eggdrop.post-install6
-rw-r--r--main/eggdrop/gseen.mod.patch5123
-rw-r--r--main/eggdrop/logs2html.mod.patch2404
5 files changed, 7686 insertions, 0 deletions
diff --git a/main/eggdrop/APKBUILD b/main/eggdrop/APKBUILD
new file mode 100644
index 0000000000..59c21f2e07
--- /dev/null
+++ b/main/eggdrop/APKBUILD
@@ -0,0 +1,69 @@
+# Contributor: Carlo Landmeter <clandmeter at gmail>
+pkgname=eggdrop
+pkgver=1.6.19
+pkgrel=1
+pkgdesc="World's most popular Open Source IRC bot"
+url="http://www.eggheads.org/"
+license='GPL-2'
+depends="tcl"
+makedepends="tcl-dev !bind-libs"
+install="$pkgname.post-install"
+subpackages="$pkgname-logs2html $pkgname-gseen"
+source="ftp://ftp.eggheads.org/pub/eggdrop/GNU/1.6/${pkgname}${pkgver}.tar.bz2
+eggdrop-installer
+$install
+gseen.mod.patch
+logs2html.mod.patch"
+
+build() {
+ cd "$srcdir/${pkgname}${pkgver}"
+
+ for i in "$srcdir"/*.patch; do
+ msg "Applying ${i}"
+ patch -p0 -i $i || return 1
+ done
+
+ ./configure --prefix=/usr
+ make config || return 1
+ make || return 1
+ mkdir -p $pkgdir/opt/eggdrop
+ make -j1 DEST="$pkgdir/opt/eggdrop" install
+ install -Dm755 "$srcdir"/eggdrop-installer "$pkgdir"/usr/bin/eggdrop-installer || return 1
+}
+
+logs2html() {
+ install=""
+ cd "$srcdir/${pkgname}${pkgver}"
+
+ for dirs in language help; do
+ mkdir -p "$subpkgdir"/opt/eggdrop/${dirs}
+ mv "$pkgdir"/opt/eggdrop/${dirs}/logs2html.* "$subpkgdir"/opt/eggdrop/${dirs}/ || return 1
+ done
+
+ mkdir -p "$subpkgdir"/opt/eggdrop/modules/
+ mv "$pkgdir"/opt/eggdrop/modules/logs2html.so "$subpkgdir"/opt/eggdrop/modules/ || return 1
+ mkdir -p "$subpkgdir"/opt/eggdrop/log2html
+
+ for files in logs2html.conf top100.tpl user.css readme.txt chan.list; do
+ cp src/mod/logs2html.mod/${files} "$subpkgdir"/opt/eggdrop/log2html/ || return 1
+ done
+}
+
+
+gseen() {
+ install=""
+ cd "$srcdir/${pkgname}${pkgver}"
+ mkdir -p "$subpkgdir"/opt/eggdrop/language
+ mv "$pkgdir"/opt/eggdrop/language/gseen.* "$subpkgdir"/opt/eggdrop/language/ || return 1
+ mkdir -p "$subpkgdir"/opt/eggdrop/modules/
+ mv "$pkgdir"/opt/eggdrop/modules/gseen.so "$subpkgdir"/opt/eggdrop/modules/ || return 1
+ mkdir -p "$subpkgdir"/opt/eggdrop/gseen
+ cp src/mod/gseen.mod/gseen.conf "$subpkgdir"/opt/eggdrop/gseen/ || return 1
+ cp src/mod/gseen.mod/README "$subpkgdir"/opt/eggdrop/gseen/ || return 1
+}
+
+md5sums="b706bbe4fdd05964e0ea0cd920f28539 eggdrop1.6.19.tar.bz2
+d5214dc16c07c55edff22f495c9c367b eggdrop-installer
+48ac9f366630ca18281ab2c98a5091ba eggdrop.post-install
+37b82f60413913758cd4161dbc2e7849 gseen.mod.patch
+490ffd522d5058413c96b8325d19b838 logs2html.mod.patch"
diff --git a/main/eggdrop/eggdrop-installer b/main/eggdrop/eggdrop-installer
new file mode 100644
index 0000000000..69faa00dca
--- /dev/null
+++ b/main/eggdrop/eggdrop-installer
@@ -0,0 +1,84 @@
+#!/bin/sh
+# Copyright 1999-2005 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/www/viewcvs.gentoo.org/raw_cvs/gentoo-x86/net-irc/eggdrop/files/eggdrop-installer,v 1.10 2007/03/14 17:52:24 drac Exp $
+
+source /sbin/functions.sh || {
+ echo "${0}: Could not source /etc/init.d/functions.sh!"
+ exit 1
+}
+
+# Checks to see if user is trying to install eggdrop as root.
+root_check() {
+ if [ "${HOME}" = "/root" ] || [ "$(whoami)" = "root" ]
+ then
+ ewarn "You should not be installing eggdrop as root."
+ ewarn
+ ewarn "Installing eggdrop as root leaves your computer vulnerable"
+ ewarn "to attack from other irc clients. Please use the eggdrop-installer"
+ ewarn "script as the user who you wish to run eggdrop with"
+ exit 1
+ fi
+}
+
+# Usage information
+usage() {
+ cat <<USAGE_END
+Usage: eggdrop-installer <bot-name>
+Install eggdrop for a specific user, creating the directories and files
+needed for eggdrop to run securely and safely.
+USAGE_END
+ exit 1
+}
+
+# Install eggdrop
+install_eggdrop() {
+ einfo "Installing Eggdrop"
+ einfo
+
+ root_check
+
+ if [ -d "${bot_dir}" ]
+ then
+ einfo "Already found a bot home directory for ${bot_name}"
+ exit 1
+ fi
+
+ einfo "Creating directories for your eggdrop..."
+ for dir in logs filesys/incoming text tmp scripts var
+ do
+ mkdir -p "${bot_dir}/${dir}"
+ done
+
+ einfo "Creating symlinks to required files for your bot to run..."
+ for file in help language modules eggdrop
+ do
+ ln -s "/opt/eggdrop/${file}" "${bot_dir}/${file}"
+ done
+
+ einfo "Copying motd, banner, scripts and config file..."
+ cp /opt/eggdrop/text/* "${bot_dir}/text"
+ cp /opt/eggdrop/scripts/* "${bot_dir}/scripts"
+ cp /opt/eggdrop/eggdrop.conf "${bot_dir}/eggdrop.conf"
+
+ einfo "Finished..."
+ einfo
+ einfo "Please edit your ${bot_dir}/eggdrop.conf!"
+ einfo
+ einfo "The bot needs to be run from the ${bot_dir} directory."
+ einfo "Run 'cd ${bot_dir} && ./eggdrop -m eggdrop.conf'"
+ einfo "to create the user file and then remove the -m command line option"
+ einfo "to start your eggdrop bot."
+ einfo
+ einfo "If you need any help please refer to the man page, or"
+ einfo "eggdrop website at http://www.egghelp.org/"
+}
+
+if [ -z "${1}" ]
+then
+ usage
+else
+ bot_name="${1}"
+ bot_dir="${HOME}/.eggdrop/${bot_name}"
+ install_eggdrop
+fi
diff --git a/main/eggdrop/eggdrop.post-install b/main/eggdrop/eggdrop.post-install
new file mode 100644
index 0000000000..44b43cf34e
--- /dev/null
+++ b/main/eggdrop/eggdrop.post-install
@@ -0,0 +1,6 @@
+#!/bin/sh
+echo "*"
+echo "* Please run /usr/bin/eggdrop-installer to install your eggdrop bot."
+echo "*"
+exit 0
+
diff --git a/main/eggdrop/gseen.mod.patch b/main/eggdrop/gseen.mod.patch
new file mode 100644
index 0000000000..e70aef8873
--- /dev/null
+++ b/main/eggdrop/gseen.mod.patch
@@ -0,0 +1,5123 @@
+diff -Nur src/mod/gseen.mod/Makefile src/mod/gseen.mod/Makefile
+--- src/mod/gseen.mod/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/Makefile 2002-10-26 13:17:44.000000000 +0200
+@@ -0,0 +1,28 @@
++# Makefile for src/mod/gseen.mod/
++
++doofus:
++ @echo ""
++ @echo "Let's try this from the right directory..."
++ @echo ""
++ @cd ../../../; make
++
++clean:
++ @rm -f *.o *.$(MOD_EXT) *~
++
++static: ../gseen.o
++
++modules: ../../../gseen.$(MOD_EXT)
++
++../gseen.o: ../module.h ../modvals.h ../../eggdrop.h datahandling.c \
++ gseen.c sensors.c gseencmds.c gseencmds.c do_seen.c ai.c tclcmds.c \
++ misc.c seentree.c generic_binary_tree.c slang_gseen_commands.c \
++ slang.c slang_text.c slang_ids.c slang_chanlang.c seenlang.h \
++ slang_multitext.c gseen.h
++ $(CC) $(CFLAGS) $(CPPFLAGS) -DMAKING_MODS -c gseen.c
++ rm -f ../gseen.o
++ mv gseen.o ../
++
++../../../gseen.$(MOD_EXT): ../gseen.o
++ $(LD) -o ../../../gseen.$(MOD_EXT) ../gseen.o $(XLIBS)
++
++#safety hash
+diff -Nur src/mod/gseen.mod/README src/mod/gseen.mod/README
+--- src/mod/gseen.mod/README 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/README 2002-10-26 13:17:45.000000000 +0200
+@@ -0,0 +1,140 @@
++Description:
++------------
++
++gseen.mod is a seen module for eggdrop that tracks not only the users in the
++bot's userfile, but everyone who enters one of the bots channels.
++It does pretty much the same as the popular script bseen and has a few
++additional features like AI-seen and seen-notification.
++It's also way faster than any corresponding script because scripts are always
++much slower than modules. Especially scripts that deal with large amount of
++data often become incredible slow.
++
++Installation:
++-------------
++
++gseen.mod is written for eggdrop1.6, but it should also work with eggdrop 1.4.
++
++You need the eggdrop source to compile the module.
++
++The following instructions assume, ~/eggdrop1.6.2/ is the directory
++where you installed your eggdrop from. (of course, other source dirs
++will work as well)
++
++Put gseen.mod.1.1.0.tar.gz in ~/eggdrop1.6.2/src/mod/,
++and unpack it (tar xfz gseen.mod.1.1.0.tar.gz). Change directory
++back to ~/eggdrop1.6.2/.
++
++Now just do what you've done when you compiled your bot:
++"./configure"
++"make config" (you can skip this command on eggdrop 1.4)
++"make"
++"make install"
++
++Don't forget to copy the langfiles from eggdrop1.6.2/src/mod/gseen.mod/ to
++eggdrop/language.
++
++All settings can be found in ~/eggdrop1.6.2/src/mod/gseen.mod/gseen.conf
++Copy it to your eggdrop directory, edit it to fit your needs and put
++"source gseen.conf" at the end of your eggdrop config file. The last thing
++to do is to .rehash your bot.
++
++
++Public commands:
++----------------
++
++!seen <nick>
++ I think this command doesn't need an explanation. ^_^
++!seen <mask>
++ Searches the database for entries that match <mask>
++ for example "!seen *!user@dialin-*.isp.com"
++!seennick <nick>
++ !seen also checks if a user was online later with a
++ different nick. !seennick only seens for <nick>
++!seenstats
++ just a little report on how many nicks are tracked
++
++All commands are also accessible via /msg.
++("/msg <bot> seen <nick>", for example)
++
++
++AI seen:
++--------
++
++This module has a simple built in AI routine.
++A short example:
++
++<G`Quann> Argo: have you seen Fabian recently?
++<|Argo|> G`Quann, fabian (~fabian@dns.gifs.de) was last seen quitting
++from #eggdev 1 week 4 days 9 hours 40 minutes 56 seconds ago
++(20.02. 01:39) stating ".....zzzzZZZzzZZZzZZZZZZZZZZzzz..".
++
++Well, it's not a very intelligent AI, it's rather brute-force. So don't
++forget to use the ai-seen-ignore setting.
++I know that's not coded very elegant, but if you configure it correctly,
++the failure-rate is way lower than with other AI scripts...
++
++DCC commands:
++-------------
++
++.seen
++.seennick
++.seenstats
++ just the same as the public versions
++.purgeseens
++ deletes expired data (this also happens automatically once a day)
++ (m)
++
++Channel Settings:
++-----------------
++
++ +noseendata
++ don't log any seen data in this channel
++ +quietseens
++ send answers directly via notice to the person who asked and
++ don't bother the rest of the channel with the reply
++ +quietaiseens
++ same as +quietseens, but for AI seens
++ +nopubseens
++ ignore every seen-command in this channel
++
++TCL commands:
++-------------
++
++There are no special tcl commands, only the usual bind procs.
++
++The only one that should be mentioned is:
++
++*pubm:seen <nick> <uhost> <hand> <chan> <text>
++ triggers the AI seen
++ returns: 1 if a reply was sent, 0 otherwise
++
++So if you're using another AI script on your bot, you can modify it to
++use this proc and avoid doubled replies this way.
++
++Other:
++------
++
++There is absolutely NO WARRANTY on this module. I do my best to make it
++work properly, but if anything gets screwed up, I'm not responsible. Use
++this module at your own risk.
++
++Feedback:
++---------
++
++Feel free to send feedback and bugreports (I hope there won't be any<g>) to
++gseen.mod@visions-of-fantasy.de
++
++The newest gseen version can always be found at:
++http://www.visions-of-fantasy.de/gseen.mod/
++
++Thanks to:
++----------
++
++- Fabian for teaching me plenty of things
++- everyone who tested the many buggy development versions :)
++- the eggdev team for developing eggdrop
++
++Most of all, I would like to thank Bass for writing bseen.tcl because alot
++of the ideas for this module came from using that tcl script. It's still the
++most powerful seen script, so if you want something that's easier to use than
++a module, get a copy of bseen.tcl.
+diff -Nur src/mod/gseen.mod/UPDATES src/mod/gseen.mod/UPDATES
+--- src/mod/gseen.mod/UPDATES 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/UPDATES 2002-10-26 13:17:46.000000000 +0200
+@@ -0,0 +1,55 @@
++Changes in gseen.mod: (since v1.0.0)
++--------------------
++
++1.1.1
++- fixed "no newline" compilation warnings that appeared on some systems.
++- fixed uninitialized "li" variable in do_seen()
++- fixed lacking compatibility to eggdrop1.4 (confirmation anyone?)
++- new option: hide-secret-chans
++
++1.1.0 (15.6.2001)
++- added multilang support
++- removed static buffers
++- organized data in a binary search tree (much faster)
++- optimized a few other things
++- added settings:
++ - fuzzy-search
++ - max-matches
++ - wildcard-search
++
++1.0.8
++- quiet-seens wasn't working for !seennick
++- added quiet-ai-seens
++- renamed nopub to nopubseens and nolog to noseendata and
++ quietseen to quietseens
++
++1.0.7
++- added compatibility to !channels
++- fixed a bug relating strict-host 0 had some strange effects on
++ !seen requests for users with ~ in their ident
++
++1.0.6
++- fixed a very evil bug that allowed anyone to crash the bot, sorry
++
++1.0.5
++- quietseens wasn't working correctly
++- added support for egg1.5's udef chansets
++
++1.0.4
++- added GPL stuff
++- changed error msg that appears if no gseen file exists
++
++1.0.3
++- readme updates
++- fixed a grammatical error in do_seen
++
++1.0.2
++- bot wanted to free a NULL pointer sometimes
++
++1.0.1
++- !seen without parameter returned stupid results :)
++- fixed little typo in .purgeseens
++- "I found 1 matches..." -> "I found 1 match..."
++
++1.0.0
++- release :)
+diff -Nur src/mod/gseen.mod/addons/gseen.selectlang.1.0.0.tcl src/mod/gseen.mod/addons/gseen.selectlang.1.0.0.tcl
+--- src/mod/gseen.mod/addons/gseen.selectlang.1.0.0.tcl 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/addons/gseen.selectlang.1.0.0.tcl 2002-10-26 13:18:14.000000000 +0200
+@@ -0,0 +1,80 @@
++#####################################################################
++#
++# gseen.selectlang v1.0.0
++#
++# This is a simple script which selects a language based on the
++# user's host.
++#
++# It only works for /msg commands.
++#
++# If the user is in a channel which has a language defined, gseen's
++# internal functions will override this selection and use the language
++# of the channel instead.
++#
++#####################################################################
++
++
++# Here you can define which language to use for which host.
++# The first part is the mask for the host, and the second part
++# is the language which should be used for this host.
++
++set tld-langs {
++ {"*.de" "de"}
++ {"*.at" "de"}
++ {"*.ch" "de"}
++ {"*.t-dialin.net" "de"}
++ {"*.t-ipconnect.net" "de"}
++ {"*.pl" "pl"}
++ {"*.jp" "ja"}
++}
++
++#################################################
++
++
++proc selectlang:getlang {uhost} {
++ global tld-langs
++
++ foreach tld ${tld-langs} {
++ if {[string match [lindex $tld 0] $uhost]} {
++ return [lindex $tld 1]
++ }
++ }
++ return ""
++}
++
++proc sl:rebind {oldtarget newtarget} {
++ foreach binding [binds msg] {
++ if {[lindex $binding 4] == $oldtarget} {
++ unbind [lindex $binding 0] [lindex $binding 1] [lindex $binding 2] [lindex $binding 4]
++ bind [lindex $binding 0] [lindex $binding 1] [lindex $binding 2] $newtarget
++ }
++ }
++}
++
++proc sl:msg:trigger {nick uhost hand rest target} {
++ global default-slang
++
++ set lang [selectlang:getlang $uhost]
++ set old-slang ${default-slang}
++ if {$lang != ""} {
++ set default-slang $lang
++ putlog "using '$lang'..."
++ }
++ $target $nick $uhost $hand $rest
++ set default-slang ${old-slang}
++}
++
++sl:rebind *msg:seen sl:msg:seen
++proc sl:msg:seen {nick uhost hand rest} {
++ sl:msg:trigger $nick $uhost $hand $rest *msg:seen
++}
++
++sl:rebind *msg:seenstats sl:msg:seenstats
++proc sl:msg:seenstats {nick uhost hand rest} {
++ sl:msg:trigger $nick $uhost $hand $rest *msg:seenstats
++}
++
++sl:rebind *msg:seennick sl:msg:seennick
++proc sl:msg:seennick {nick uhost hand rest} {
++ sl:msg:trigger $nick $uhost $hand $rest *msg:seennick
++}
+\ No newline at end of file
+diff -Nur src/mod/gseen.mod/ai.c src/mod/gseen.mod/ai.c
+--- src/mod/gseen.mod/ai.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/ai.c 2002-10-26 13:17:47.000000000 +0200
+@@ -0,0 +1,151 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++static int quietaiseens(char *chan)
++{
++ char buf[121], *b;
++
++ Context;
++ strncpy(buf, quiet_ai_seen, 120);
++ buf[120] = 0;
++ b = buf;
++ while (b[0])
++ if (!strcasecmp(chan, newsplit(&b)))
++ return 1;
++#if EGG_IS_MIN_VER(10503)
++ if (ngetudef("quietaiseens", chan))
++ return 1;
++#endif
++ return 0;
++}
++
++static int tcl_pubmseen STDVAR
++{
++ char *nick, *uhost, *hand, *chan, *text;
++ char buf[1024];
++ char *words, *word;
++ seendat *l;
++ int i;
++
++ Context;
++ BADARGS(6, 6, " nick uhost hand chan text");
++ nick = argv[1];
++ uhost = argv[2];
++ hand = argv[3];
++ chan = argv[4];
++ text = argv[5];
++ reset_global_vars();
++ glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, chan));
++ glob_nick = nick;
++ for (i = 0; i < strlen(text); i++)
++ if (strchr("!?.,\"", text[i]))
++ text[i] = ' ';
++ strncpy(buf, ignore_words, 1023);
++ buf[1023] = 0;
++ words = buf;
++ while (words[0])
++ add_ignoredword(newsplit(&words));
++ strncpy(buf, text, 1023);
++ buf[1023] = 0;
++ words = buf;
++ while (words[0]) {
++ word = newsplit(&words);
++ if (word_is_ignored(word))
++ continue;
++ l = findseen(word);
++ if (l) {
++ if (quietaiseens(chan)) {
++ set_prefix(SLNOTPREFIX);
++ dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix,
++ do_seen(word, nick, uhost, chan, 0));
++ } else {
++ set_prefix(SLPUBPREFIX);
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", chan, reply_prefix,
++ do_seen(word, nick, uhost, chan, 0));
++ }
++ add_seenreq(word, nick, uhost, chan, now);
++ free_ignoredwords();
++ Tcl_AppendResult(irp, "1", NULL);
++ return TCL_OK;
++ }
++ }
++ free_ignoredwords();
++ Tcl_AppendResult(irp, "0", NULL);
++ return TCL_OK;
++}
++
++static tcl_cmds mytcls[] =
++{
++ {"*pubm:seen", tcl_pubmseen},
++ {"*chjn:gseen", gseen_chjn},
++ {"*chpt:gseen", gseen_chpt},
++ {0, 0}
++};
++
++static void add_ignoredword(char *word)
++{
++ ignoredword *l, *nl;
++
++ l = ignoredwords;
++ while (l && l->next)
++ l = l->next;
++ nl = nmalloc(sizeof(ignoredword));
++ nl->word = nmalloc(strlen(word) + 1);
++ strcpy(nl->word, word);
++ nl->next = NULL;
++ if (ignoredwords)
++ l->next = nl;
++ else
++ ignoredwords = nl;
++}
++
++static void free_ignoredwords()
++{
++ ignoredword *l, *ll;
++
++ l = ignoredwords;
++ while (l) {
++ ll = l->next;
++ nfree(l->word);
++ nfree(l);
++ l = ll;
++ }
++ ignoredwords = NULL;
++}
++
++static int expmem_ignoredwords()
++{
++ ignoredword *l;
++ int size = 0;
++
++ for (l = ignoredwords; l; l = l->next) {
++ size += sizeof(ignoredword);
++ size += strlen(l->word) + 1;
++ }
++ return size;
++}
++
++static int word_is_ignored(char *word)
++{
++ ignoredword *l;
++
++ for (l = ignoredwords; l; l = l->next)
++ if (!strcasecmp(l->word, word))
++ return 1;
++ return 0;
++}
+diff -Nur src/mod/gseen.mod/datahandling.c src/mod/gseen.mod/datahandling.c
+--- src/mod/gseen.mod/datahandling.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/datahandling.c 2002-10-26 13:17:48.000000000 +0200
+@@ -0,0 +1,151 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++
++static void write_seens()
++{
++ seenreq *r;
++ seenreq_by *b;
++ FILE *f;
++ char s[125];
++
++ Context;
++ /* putlog(LOG_MISC, "*", "Saving seen data..."); */
++ if (!gseenfile[0])
++ return;
++ sprintf(s, "%s~new", gseenfile);
++ f = fopen(s, "w");
++ chmod(s, 0600);
++ if (f == NULL) {
++ putlog(LOG_MISC, "*", "ERROR writing gseen file.");
++ return;
++ }
++ fprintf(f, "# gseen data file v1.\n");
++ write_seen_tree_target = f;
++ btree_getall(&seentree, write_seen_tree);
++ for (r = requests; r; r = r->next)
++ for (b = r->by; b; b = b->next)
++ /* @ nick by host chan when */
++ fprintf(f, "@ %s %s %s %s %lu\n", r->nick, b->who, b->host, b->chan,
++ b->when);
++ fclose(f);
++ unlink(gseenfile);
++ movefile(s, gseenfile);
++ /* putlog(LOG_MISC, "*", "Done."); */
++ return;
++}
++
++static void read_seens()
++{
++ FILE *f;
++ char buf[512], *s, *type, *nick, *host, *chan, *msg, *by;
++ time_t when;
++ int spent, iType, i;
++
++ Context;
++ f = fopen(gseenfile, "r");
++ if (f == NULL) {
++ putlog(LOG_MISC, "*", "Can't open gseen file, creating new database...");
++ return;
++ }
++ while (!feof(f)) {
++ buf[0] = 0;
++ s = buf;
++ fgets(s, 511, f);
++ i = strlen(buf);
++ if (buf[i - 1] == '\n')
++ buf[i - 1] = 0;
++ if ((buf[0] == 0) || (buf[0] == '#'))
++ continue;
++ type = newsplit(&s);
++ if (!strcmp(type, "!")) {
++ nick = newsplit(&s);
++ host = newsplit(&s);
++ chan = newsplit(&s);
++ iType = atoi(newsplit(&s));
++ when = (time_t) atoi(newsplit(&s));
++ spent = atoi(newsplit(&s));
++ msg = s;
++ add_seen(iType, nick, host, chan, msg, when, spent);
++ } else if (!strcmp(type, "@")) {
++ nick = newsplit(&s);
++ by = newsplit(&s);
++ host = newsplit(&s);
++ chan = newsplit(&s);
++ when = (time_t) atoi(newsplit(&s));
++ add_seenreq(nick, by, host, chan, when);
++ }
++ }
++ fclose(f);
++ Context;
++ return;
++}
++
++static void purge_seens()
++{
++ seenreq *r, *rr;
++ seenreq_by *b, *bb;
++
++ Context;
++ if (!expire_seens)
++ return;
++ btree_getall_expanded(&seentree, purge_seen_tree);
++ debug0("purge done");
++ r = requests;
++ rr = NULL;
++ while (r) {
++ b = r->by;
++ bb = NULL;
++ while (b) {
++ if ((now - b->when) > (expire_seens * 86400)) {
++ debug2("request for %s from %s has expired.", r->nick, b->who);
++ nfree(b->who);
++ nfree(b->host);
++ nfree(b->chan);
++ if (bb) {
++ bb->next = b->next;
++ nfree(b);
++ b = bb->next;
++ } else {
++ r->by = b->next;
++ nfree(b);
++ b = r->by;
++ }
++ } else {
++ bb = b;
++ b = b->next;
++ }
++ }
++ if (!r->by) {
++ debug1("no further seen requests for %s, deleting", r->nick);
++ nfree(r->nick);
++ if (rr) {
++ rr->next = r->next;
++ nfree(r);
++ r = rr->next;
++ } else {
++ requests = r->next;
++ nfree(r);
++ r = requests;
++ }
++ } else {
++ rr = r;
++ r = r->next;
++ }
++ }
++}
+diff -Nur src/mod/gseen.mod/do_seen.c src/mod/gseen.mod/do_seen.c
+--- src/mod/gseen.mod/do_seen.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/do_seen.c 2002-10-26 13:17:50.000000000 +0200
+@@ -0,0 +1,840 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++/* do_seen(): Checks if someone matches the mask, and returns the reply
++ * mask : first paramater (e.g. "G`Quann", "G`Quann", "*!*@*.isp.de", ...)
++ * nick : nick of the one, who triggered the command
++ * uhost: user@host of nick
++ * chan : chan, where the command was triggered
++ * bns :
++ * 1 : do a botnet-seen if no matches are found
++ * 0 : don't do a botnet-seen
++ * -1 : return NULL instead of text, if no matches were found
++ * (necessary for botnet seen)
++ */
++static char *do_seen(char *mask, char *nick, char *uhost, char *chan, int bns)
++{
++ char hostbuf[UHOSTLEN + 1], *host, *newhost, *tmp, *dur;
++ seendat *l;
++ gseenres *r;
++ int wild, nr;
++ char bnquery[256];
++ struct userrec *u;
++ struct laston_info *li;
++ struct chanset_t *ch;
++
++ Context;
++ start_seentime_calc();
++ if (seen_reply) {
++ nfree(seen_reply);
++ seen_reply = NULL;
++ }
++ l = NULL;
++ li = NULL;
++ host = hostbuf;
++ newhost = NULL;
++ mask = newsplit(&mask);
++ glob_query = mask;
++ while (mask[0] == ' ')
++ mask++;
++ if (!mask[0]) {
++ return SLNOPARAM;
++ }
++ if (strchr(mask, '?') || strchr(mask, '*')) {
++ // if wildcard-searches ares not allowed, then either return
++ // NULL (for botnet-seen), or a appropriate warning
++ if (!wildcard_search) {
++ if (bns == -1)
++ return NULL;
++ else
++ return SLNOWILDCARDS;
++ } else
++ wild = 1;
++ } else {
++ if (strlen(mask) > seen_nick_len) // don't process if requested nick is too long
++ return SLTOOLONGNICK; // (e.g. stop stupid jokes)
++ if (!strcasecmp(mask, nick)) {
++ return SLMIRROR;
++ }
++ // check if the nick is on the current channel
++ if (onchan(mask, chan))
++ return SLONCHAN;
++ if ((glob_othernick = handonchan(mask, chan)))
++ return SLHANDONCHAN;
++ // check if it is on any other channel
++ if ((ch = onanychan(mask))) {
++#if EGG_IS_MIN_VER(10500)
++ if (!secretchan(ch->dname)) {
++ glob_otherchan = ch->dname;
++ return SLONOTHERCHAN;
++ }
++#else
++ if (!secretchan(ch->name)) {
++ glob_otherchan = ch->name;
++ return SLONOTHERCHAN;
++ }
++#endif
++ }
++ // check if the user who uses this handle is on the channel under
++ // a different nick
++ if ((ch = handonanychan(mask))) {
++#if EGG_IS_MIN_VER(10500)
++ if (!secretchan(ch->dname)) {
++ glob_otherchan = ch->dname;
++ return SLONOTHERCHAN;
++ }
++#else
++ if (!secretchan(ch->name)) {
++ glob_otherchan = ch->name;
++ return SLONOTHERCHAN;
++ }
++#endif
++ }
++ add_seenreq(mask, nick, uhost, chan, now);
++ wild = 0;
++ l = findseen(mask);
++ // if there's a result, and if we don't want to search for the same user
++ // under a different nick, just make a do_seennick on the result
++ if (l && !fuzzy_search) {
++ tmp = do_seennick(l);
++ end_seentime_calc();
++ return tmp;
++ }
++ if (!l) {
++ u = get_user_by_handle(userlist, mask);
++ if (u) {
++ li = get_user(&USERENTRY_LASTON, u);
++ }
++ if (!u || !li) {
++ if (bns == -1) { // if bns is 0, then do_seen() was triggered by
++ end_seentime_calc(); // a botnet seen function, which needs a clear
++ return NULL; // NULL to detect if there was a result or not
++ }
++ tmp = SLNOTSEEN;
++ if (bns && ((strlen(mask) + strlen(nick) + strlen(uhost)
++ + strlen(chan) + 20) < 255)) {
++ debug0("trying botnet seen");
++ if (bnsnick)
++ nfree(bnsnick);
++ if (bnschan)
++ nfree(bnschan);
++ bnsnick = nmalloc(strlen(nick) + 1);
++ strcpy(bnsnick, nick);
++ bnschan = nmalloc(strlen(chan) + 1);
++ strcpy(bnschan, chan);
++ sprintf(bnquery, "gseen_req %s %s %s %s", mask, nick, uhost, chan);
++ botnet_send_zapf_broad(-1, botnetnick, NULL, bnquery);
++ }
++ } else {
++ // we have a matching handle, no seen-entry, but a laston entry
++ // in the userbase, so let's just return that one.
++ dur = gseen_duration(now - li->laston);
++ glob_laston = dur;
++ tmp = SLPOORSEEN;
++ seen_reply = nmalloc(strlen(tmp) + 1);
++ strcpy(seen_reply, tmp);
++ end_seentime_calc();
++ return seen_reply;
++ }
++ end_seentime_calc();
++ return tmp;
++ }
++ // now prepare the host for fuzzy-search
++ if (strlen(l->host) < UHOSTLEN) {
++ maskstricthost(l->host, host);
++ host = strchr(host, '!') + 1; // strip nick from host for faster search
++ } else {
++ end_seentime_calc();
++ return "error, too long host";
++ }
++ }
++ if (l && (l->type == SEEN_CHPT)) {
++ tmp = do_seennick(l);
++ end_seentime_calc();
++ return tmp;
++ }
++ numresults = 0;
++ // wildmatch_seens uses a global var to store hosts in it
++ // (to prevent massive nmalloc/nfree-usage), so don't forget
++ // to initialize and free it
++ temp_wildmatch_host = my_malloc(1);
++ wildmatch_seens(host, mask, wild);
++ my_free(temp_wildmatch_host);
++ temp_wildmatch_host = NULL;
++ if (!results) {
++ end_seentime_calc();
++ if (bns == -1)
++ return NULL; // let the botnet seen function know, that seen failed
++ return SLNOMATCH;
++ }
++ if (numresults >= max_matches) {
++ end_seentime_calc();
++ free_seenresults();
++ return SLTOOMANYMATCHES;
++ }
++ sortresults();
++ if (strcasecmp(results->seen->nick, mask)) {
++ // if the user's latest nick is not the nick for which we were searching,
++ // say that there were multiple matches and display the latest one
++ if (numresults == 1)
++ tmp = SLONEMATCH;
++ else if (numresults <= 5)
++ tmp = SLLITTLEMATCHES;
++ else
++ tmp = SLMANYMATCHES;
++ seen_reply = nmalloc(strlen(tmp) + 1);
++ strcpy(seen_reply, tmp);
++ nr = 0;
++ for (r = results; (r && (nr < 5)); r = r->next) {
++ nr++;
++ if (nr > 1) {
++ seen_reply = nrealloc(seen_reply, 1 + strlen(seen_reply) + 1 + strlen(r->seen->nick) + 1);
++ strcat(seen_reply, ", ");
++ } else {
++ seen_reply = nrealloc(seen_reply, 1 + strlen(seen_reply) + strlen(r->seen->nick) + 1);
++ strcat(seen_reply, " ");
++ }
++ strcat(seen_reply, r->seen->nick);
++ }
++ tmp = do_seennick(results->seen);
++ seen_reply = nrealloc(seen_reply, 2 + strlen(seen_reply) + strlen(tmp) + 1);
++ sprintf(seen_reply, "%s. %s", seen_reply, tmp);
++ } else { // first result is the nick which we were searching for
++ // just return the info for this nick and don't care about other results
++ tmp = do_seennick(results->seen);
++ seen_reply = nmalloc(strlen(tmp) + 1);
++ strcpy(seen_reply, tmp);
++ }
++ free_seenresults();
++ end_seentime_calc();
++ return seen_reply;
++}
++
++/* do_seennick():
++ * takes a seen-dataset and produces the corresponding reply basically
++ * by referencing to the lang entry with the same number as the seen-type.
++ */
++static char *do_seennick(seendat *l)
++{
++// char buf[256], *msg;
++ int stype;
++
++ Context;
++ if (!l) {
++ debug0("ERROR! Tryed to do a seennick on a NULL pointer!");
++ return "ERROR! seendat == NULL!!!";
++ }
++ glob_seendat = l;
++ // l->type is the basic language-entry-number
++ stype = l->type + 100;
++ // in some cases, we might need a special reply, so modify the
++ // number if neccessary
++ switch (l->type) {
++ case SEEN_JOIN:
++ if (!onchan(l->nick, l->chan))
++ stype += 20;
++ break;
++ case SEEN_PART:
++ /* nothing to do here */
++ break;
++ case SEEN_SIGN:
++ /* nothing again */
++ break;
++ case SEEN_NICK:
++ if (!onchan(l->msg, l->chan))
++ stype += 20;
++ break;
++ case SEEN_NCKF:
++ if (!onchan(l->nick, l->chan))
++ stype += 20;
++ break;
++ case SEEN_KICK:
++/* msg = buf;
++ strncpy(buf, l->msg, 255);
++ msg[255] = 0;
++ sglobpunisher = newsplit(&msg);
++ sglobreason = msg; */
++ break;
++ case SEEN_SPLT:
++ /* nothing to do here */
++ break;
++ case SEEN_REJN:
++ if (!onchan(l->nick, l->chan))
++ stype += 20;
++ break;
++ case SEEN_CHJN:
++ case SEEN_CHPT:
++ if (!strcmp(l->chan, "0"))
++ stype += 20;
++ break;
++ default:
++ stype = 140;
++ }
++ return getslang(stype);
++}
++
++/* findseens():
++ * interface for webseen.mod
++ * find all results for a query and return a pointer to this list
++ * (basically the core of do_seen())
++ */
++static gseenres *findseens(char *mask, int *ret, int fuzzy)
++{
++ char hostbuf[UHOSTLEN + 1], *host, *newhost;
++ seendat *l;
++ int wild;
++
++ Context;
++ start_seentime_calc();
++ *ret = WS_OK;
++ l = NULL;
++ host = hostbuf;
++ newhost = NULL;
++ mask = newsplit(&mask);
++ while (mask[0] == ' ')
++ mask++;
++ if (!mask[0]) {
++ *ret = WS_NOPARAM;
++ return NULL;
++ }
++ if (strchr(mask, '?') || strchr(mask, '*')) {
++ // if wildcard-searches ares not allowed, then either return
++ // NULL (for botnet-seen), or a appropriate warning
++ if (!wildcard_search) {
++ *ret = WS_NOWILDCARDS;
++ return NULL;
++ }
++ wild = 1;
++ } else {
++ if (strlen(mask) > seen_nick_len) { // don't process if requested nick is too long
++ *ret = WS_TOOLONGNICK; // (e.g. stop stupid jokes)
++ return NULL;
++ }
++ add_seenreq(mask, "www-user", "unknown_host", "webinterface", now);
++ wild = 0;
++ l = findseen(mask);
++ // if there's a result, and if we don't want to search for the same user
++ // under a different nick, just return this result
++ if (l && (!fuzzy_search || !fuzzy)) {
++ numresults = 1;
++ add_seenresult(l);
++ end_seentime_calc();
++ return results;
++ }
++ if (!l) {
++ // no matching user was found :(
++ *ret = WS_NORESULT;
++ end_seentime_calc();
++ return NULL;
++ }
++ // now prepare the host for fuzzy-search
++ if (strlen(l->host) < UHOSTLEN) {
++ maskstricthost(l->host, host);
++ host = strchr(host, '!') + 1; // strip nick from host for faster search
++ } else {
++ *ret = WS_TOOLONGHOST;
++ end_seentime_calc();
++ return NULL;
++ }
++ }
++ if (l && (l->type == SEEN_CHPT)) {
++ numresults = 1;
++ add_seenresult(l);
++ end_seentime_calc();
++ return results;
++ }
++ numresults = 0;
++ // wildmatch_seens uses a global var to store hosts in it
++ // (to prevent massive nmalloc/nfree-usage), so don't forget
++ // to initialize and free it
++ temp_wildmatch_host = my_malloc(1);
++ wildmatch_seens(host, mask, wild);
++ my_free(temp_wildmatch_host);
++ temp_wildmatch_host = NULL;
++ if (!results) {
++ // no match :(
++ *ret = WS_NORESULT;
++ end_seentime_calc();
++ return NULL;
++ }
++ if (numresults >= max_matches) {
++ free_seenresults();
++ *ret = WS_TOOMANYMATCHES;
++ end_seentime_calc();
++ return NULL;
++ }
++ sortresults();
++ *ret = 0;
++ end_seentime_calc();
++ return results;
++}
++
++
++char seenstats_reply[512];
++static char *do_seenstats()
++{
++ glob_totalnicks = count_seens();
++ glob_totalbytes = gseen_expmem();
++ sprintf(seenstats_reply, "%s", SLSEENSTATS);
++ return seenstats_reply;
++}
++
++// add an seen result (to the top of the list)
++static void add_seenresult(seendat *seen)
++{
++ gseenres *nl;
++
++ numresults++;
++ if (numresults > max_matches)
++ return;
++ nl = nmalloc(sizeof(gseenres));
++ nl->seen = seen;
++ nl->next = results;
++ results = nl;
++}
++
++static int expmem_seenresults()
++{
++ int bytes = 0;
++ gseenres *l;
++
++ for (l = results; l; l = l->next)
++ bytes += sizeof(gseenres);
++ return bytes;
++}
++
++static void free_seenresults()
++{
++ gseenres *l, *ll;
++
++ l = results;
++ while (l) {
++ ll = l->next;
++ nfree(l);
++ l = ll;
++ }
++ results = NULL;
++}
++
++static void sortresults()
++{
++ int again = 1;
++ gseenres *last, *p, *c, *n;
++ int a, b;
++
++ Context;
++ again = 1;
++ last = NULL;
++ while ((results != last) && (again)) {
++ p = NULL;
++ c = results;
++ n = c->next;
++ again = 0;
++ while (n != last) {
++ if (!c || !n)
++ a = b = 0;
++ else
++ a = c->seen->when;
++ b = n->seen->when;
++ if (a < b) {
++ again = 1;
++ c->next = n->next;
++ n->next = c;
++ if (p == NULL)
++ results = n;
++ else
++ p->next = n;
++ }
++ p = c;
++ c = n;
++ n = n->next;
++ }
++ last = c;
++ }
++ Context;
++ return;
++}
++
++static void sortrequests(seenreq *l)
++{
++ int again = 1;
++ seenreq_by *last, *p, *c, *n;
++ int a, b;
++
++ Context;
++ again = 1;
++ last = NULL;
++ while ((l->by != last) && (again)) {
++ p = NULL;
++ c = l->by;
++ n = c->next;
++ again = 0;
++ while (n != last) {
++ if (!c || !n)
++ a = b = 0;
++ else
++ a = c->when;
++ b = n->when;
++ if (a < b) {
++ again = 1;
++ c->next = n->next;
++ n->next = c;
++ if (p == NULL)
++ l->by = n;
++ else
++ p->next = n;
++ }
++ p = c;
++ c = n;
++ n = n->next;
++ }
++ last = c;
++ }
++ Context;
++ return;
++}
++
++/* stolen from tcl_duration in tclmisc.c */
++char gs_duration_temp[256];
++static char *gseen_duration(int seconds)
++{
++ char s[256];
++ time_t sec;
++
++ sec = seconds;
++ s[0] = 0;
++ if (sec < 1) {
++ snprintf(gs_duration_temp, sizeof(gs_duration_temp), "%s", SLSOMETIME);
++ return gs_duration_temp;
++ }
++ if (sec < 60) {
++ sprintf(gs_duration_temp, "%d %s", (int) (sec / 1),
++ ((int) (sec / 1) > 1) ? SLSECONDS : SLSECOND);
++ return gs_duration_temp;
++ }
++ if (sec >= 31536000) {
++ sprintf(s, "%d %s ", (int) (sec / 31536000),
++ ((int) (sec / 31536000) > 1) ? SLYEARS : SLYEAR);
++ sec -= (((int) (sec / 31536000)) * 31536000);
++ }
++ if (sec >= 604800) {
++ sprintf(&s[strlen(s)], "%d %s ", (int) (sec / 604800),
++ ((int) (sec / 604800) > 1) ? SLWEEKS : SLWEEK);
++ sec -= (((int) (sec / 604800)) * 604800);
++ }
++ if (sec >= 86400) {
++ sprintf(&s[strlen(s)], "%d %s ", (int) (sec / 86400),
++ ((int) (sec / 86400) > 1) ? SLDAYS : SLDAY);
++ sec -= (((int) (sec / 86400)) * 86400);
++ }
++ if (sec >= 3600) {
++ sprintf(&s[strlen(s)], "%d %s ", (int) (sec / 3600),
++ ((int) (sec / 3600) > 1) ? SLHOURS : SLHOUR);
++ sec -= (((int) (sec / 3600)) * 3600);
++ }
++ if (sec >= 60) {
++ sprintf(&s[strlen(s)], "%d %s ", (int) (sec / 60),
++ ((int) (sec / 60) > 1) ? SLMINUTES : SLMINUTE);
++ sec -= (((int) (sec / 60)) * 60);
++ }
++ strcpy(gs_duration_temp, s);
++ if (gs_duration_temp[strlen(gs_duration_temp) - 1] == ' ')
++ gs_duration_temp[strlen(gs_duration_temp) - 1] = 0;
++ return gs_duration_temp;
++}
++
++static int onchan(char *nick, char *chan)
++{
++ struct chanset_t *ch;
++ memberlist *m;
++
++ ch = findchan_by_dname(chan);
++ if (!ch)
++ return 0;
++ m = ismember(ch, nick);
++ if (!m)
++ return 0;
++ else if (chan_issplit(m))
++ return 0;
++ else
++ return 1;
++}
++
++/* handonchan():
++ * checks if the given user is on the channel and returns its nick
++ */
++static char *handonchan(char *hand, char *chan)
++{
++ struct chanset_t *ch;
++ memberlist *m;
++
++ ch = findchan_by_dname(chan);
++ if (!ch)
++ return 0;
++ if (ch->channel.members > 0) {
++ for (m = ch->channel.member; m; m = m->next) {
++ if (m->user) {
++ if (m->user->handle && !rfc_casecmp(m->user->handle, hand))
++ return m->nick;
++ }
++ }
++ }
++ return NULL;
++}
++
++/* onanychan():
++ * checks if the given nickname is on any of the bot's chans.
++ */
++static struct chanset_t *onanychan(char *nick)
++{
++ struct chanset_t *ch;
++ memberlist *m;
++
++ for (ch = chanset; ch; ch = ch->next) {
++ m = ismember(ch, nick);
++ if (m && !chan_issplit(m))
++ return ch;
++ }
++ return NULL;
++}
++
++/* handonanychan():
++ * checks if the given user is on any channel (no matter under which nick)
++ */
++static struct chanset_t *handonanychan(char *hand)
++{
++ struct chanset_t *ch;
++ memberlist *m;
++
++ for (ch = chanset; ch; ch = ch->next) {
++ if (ch->channel.members > 0) {
++ for (m = ch->channel.member; m; m = m->next) {
++ if (m->user) {
++ if (m->user->handle && !rfc_casecmp(m->user->handle, hand))
++ return ch;
++ }
++ }
++ }
++ }
++ return NULL;
++}
++
++static void add_seenreq(char *nick, char *from, char *host, char *chan,
++ time_t when)
++{
++ seenreq *l, *nl;
++ seenreq_by *b, *nb;
++ char buf[10] = "[secret]";
++
++ Context;
++ if (!tell_seens)
++ return;
++ if (strcmp(chan, "[partyline]") && secretchan(chan))
++ chan = buf;
++ for (l = requests; l; l = l->next) {
++ if (!strcasecmp(nick, l->nick)) {
++ for (b = l->by; b; b = b->next) {
++ if (!strcasecmp(from, b->who)) {
++ nfree(b->chan);
++ b->chan = nmalloc(strlen(chan) + 1);
++ strcpy(b->chan, chan);
++ b->when = when;
++ return;
++ }
++ }
++ b = l->by;
++ while (b && b->next)
++ b = b->next;
++ nb = nmalloc(sizeof(seenreq_by));
++ nb->who = nmalloc(strlen(from) + 1);
++ strcpy(nb->who, from);
++ nb->host = nmalloc(strlen(host) + 1);
++ strcpy(nb->host, host);
++ nb->chan = nmalloc(strlen(chan) + 1);
++ strcpy(nb->chan, chan);
++ nb->when = when;
++ nb->next = NULL;
++ if (l->by)
++ b->next = nb;
++ else
++ l->by = nb;
++ return;
++ }
++ }
++ nb = nmalloc(sizeof(seenreq_by));
++ nb->who = nmalloc(strlen(from) + 1);
++ strcpy(nb->who, from);
++ nb->host = nmalloc(strlen(host) + 1);
++ strcpy(nb->host, host);
++ nb->chan = nmalloc(strlen(chan) + 1);
++ strcpy(nb->chan, chan);
++ nb->when = when;
++ nb->next = NULL;
++ l = requests;
++ while (l && l->next)
++ l = l->next;
++ nl = nmalloc(sizeof(seenreq));
++ nl->nick = nmalloc(strlen(nick) + 1);
++ strcpy(nl->nick, nick);
++ nl->by = nb;
++ nl->next = NULL;
++ if (requests)
++ l->next = nl;
++ else
++ requests = nl;
++}
++
++static int expmem_seenreq()
++{
++ seenreq *l;
++ seenreq_by *b;
++ int size;
++
++ size = 0;
++ for (l = requests; l; l = l->next) {
++ size += sizeof(seenreq);
++ size += strlen(l->nick) + 1;
++ for (b = l->by; b; b = b->next) {
++ size += sizeof(seenreq_by);
++ size += strlen(b->who) + 1;
++ size += strlen(b->host) + 1;
++ size += strlen(b->chan) + 1;
++ }
++ }
++ return size;
++}
++
++static int count_seenreq(seenreq_by *b)
++{
++ seenreq_by *l;
++ int nr;
++
++ nr = 0;
++ for (l = b; l; l = l->next)
++ nr++;
++ return nr;
++}
++
++static void free_seenreq()
++{
++ seenreq *l, *ll;
++ seenreq_by *b, *bb;
++
++ Context;
++ l = requests;
++ while (l) {
++ b = l->by;
++ while (b) {
++ bb = b->next;
++ nfree(b->who);
++ nfree(b->host);
++ nfree(b->chan);
++ nfree(b);
++ b = bb;
++ }
++ ll = l->next;
++ nfree(l->nick);
++ nfree(l);
++ l = ll;
++ }
++ requests = NULL;
++}
++
++static void report_seenreq(char *channel, char *nick)
++{
++ seenreq *l, *ll;
++ seenreq_by *b, *bb;
++ char *reply, *tmp;
++ int nr;
++
++ if (!tell_seens)
++ return;
++ ll = NULL;
++ l = requests;
++ reply = NULL;
++ while (l) {
++ if (!strcasecmp(l->nick, nick)) {
++ reset_global_vars();
++ glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, channel));
++ glob_nick = nick;
++ nr = count_seenreq(l->by);
++ if (nr == 1) {
++ glob_seenrequest = l;
++ dprintf(DP_HELP, "NOTICE %s :%s\n", l->nick, SLONELOOK);
++ } else {
++ sortrequests(l);
++ glob_seenrequest = l;
++ glob_seenrequests = nr;
++ tmp = SLMORELOOKS;
++ reply = nmalloc(strlen(tmp) + 1);
++ strcpy(reply, tmp);
++ nr = 0;
++ for (b = l->by; b; b = b->next) {
++ nr++;
++ reply = nrealloc(reply, strlen(reply) + ((nr == 1) ? 1 : 2) + strlen(b->who) + 1);
++ sprintf(reply, "%s%s%s", reply, (nr == 1) ? " " : ", ", b->who);
++ }
++ tmp = SLLASTLOOK;
++ reply = nrealloc(reply, strlen(reply) + 2 + strlen(tmp) + 1);
++ sprintf(reply, "%s. %s", reply, tmp);
++ dprintf(DP_HELP, "NOTICE %s :%s\n", l->nick, reply);
++ nfree(reply);
++ }
++ b = l->by;
++ while (b) {
++ bb = b->next;
++ nfree(b->who);
++ nfree(b->host);
++ nfree(b->chan);
++ nfree(b);
++ b = bb;
++ }
++ nfree(l->nick);
++ if (ll)
++ ll->next = l->next;
++ else
++ requests = l->next;
++ nfree(l);
++ if (ll)
++ l = ll->next;
++ else
++ l = requests;
++ } else {
++ ll = l;
++ l = l->next;
++ }
++ }
++}
++
++static void start_seentime_calc()
++{
++ struct timeval t;
++
++ gettimeofday(&t, NULL);
++ glob_presearch = (float) t.tv_sec + (((float) t.tv_usec) / 1000000);
++}
++
++static void end_seentime_calc()
++{
++ struct timeval t;
++
++ gettimeofday(&t, NULL);
++ glob_aftersearch = (float) t.tv_sec + (((float) t.tv_usec) / 1000000);
++ glob_total_searchtime += glob_aftersearch - glob_presearch;
++ glob_total_queries++;
++}
+diff -Nur src/mod/gseen.mod/generic_binary_tree.c src/mod/gseen.mod/generic_binary_tree.c
+--- src/mod/gseen.mod/generic_binary_tree.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/generic_binary_tree.c 2002-10-26 13:17:51.000000000 +0200
+@@ -0,0 +1,311 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++#define GENERIC_BINARY_TREE 1
++
++struct generic_binary_tree {
++ void *root;
++ int (*comparedata) (void *data1, void *data2);
++ int (*expmemdata) (void *data);
++ void (*freedata) (void *data);
++};
++
++struct generic_binary_tree_node {
++ void *data;
++ void *left;
++ void *right;
++};
++
++static void btree_add(struct generic_binary_tree *, void *);
++static int btree_expmem(struct generic_binary_tree *);
++static int btree_recursive_expmem(struct generic_binary_tree *, struct generic_binary_tree_node *);
++static void *btree_get(struct generic_binary_tree *, void *t);
++static void btree_freetree(struct generic_binary_tree *);
++static void btree_recursive_free(struct generic_binary_tree *,
++ struct generic_binary_tree_node *);
++static void btree_getall(struct generic_binary_tree *, void (*) (void *));
++static void btree_recursive_getall(struct generic_binary_tree_node *,
++ void (*) (void *));
++static void btree_getall_expanded(struct generic_binary_tree *tree, void (*) (void *));
++static void btree_recursive_getall_expanded(struct generic_binary_tree_node *,
++ void (*) (void *));
++static void btree_remove(struct generic_binary_tree *, void *);
++
++static void btree_add(struct generic_binary_tree *tree, void *data)
++{
++ struct generic_binary_tree_node *node, *lastnode;
++ int cmp, lastcmp;
++
++ Assert(tree);
++ Assert(data);
++ cmp = lastcmp = 0;
++ node = tree->root;
++ lastnode = NULL;
++ while (node) {
++ cmp = tree->comparedata(node->data, data);
++ if (!cmp) {
++ // item is identical -> free old data and insert new
++ tree->freedata(node->data);
++ node->data = data;
++ return;
++ }
++ lastnode = node;
++ lastcmp = cmp;
++ if (cmp < 0)
++ node = node->left;
++ else
++ node = node->right;
++ }
++ node = nmalloc(sizeof(struct generic_binary_tree_node));
++ node->left = NULL;
++ node->right = NULL;
++ node->data = data;
++ if (!lastnode)
++ tree->root = node;
++ else {
++ Assert(lastcmp);
++ if (lastcmp < 0) {
++ Assert(!lastnode->left);
++ lastnode->left = node;
++ } else {
++ Assert(!lastnode->right);
++ lastnode->right = node;
++ }
++ }
++}
++
++static int btree_expmem(struct generic_binary_tree *tree)
++{
++ int size = 0;
++
++ Assert(tree);
++ size += btree_recursive_expmem(tree, tree->root);
++ return size;
++}
++
++static int btree_recursive_expmem(struct generic_binary_tree *tree, struct generic_binary_tree_node *node)
++{
++ int size = 0;
++
++ if (!node)
++ return 0;
++ size += sizeof(struct generic_binary_tree_node);
++ size += tree->expmemdata(node->data);
++ size += btree_recursive_expmem(tree, node->left);
++ size += btree_recursive_expmem(tree, node->right);
++ return size;
++}
++
++static void *btree_get(struct generic_binary_tree *tree, void *what)
++{
++ struct generic_binary_tree_node *node;
++ int cmp;
++
++ node = tree->root;
++ while (node) {
++ cmp = tree->comparedata(node->data, what);
++ if (!cmp)
++ return node->data;
++ if (cmp < 0)
++ node = node->left;
++ else
++ node = node->right;
++ }
++ return NULL;
++}
++
++static void btree_freetree(struct generic_binary_tree *tree)
++{
++ btree_recursive_free(tree, tree->root);
++}
++
++static void btree_recursive_free(struct generic_binary_tree *tree,
++ struct generic_binary_tree_node *node)
++{
++ if (!node)
++ return;
++ btree_recursive_free(tree, node->left);
++ btree_recursive_free(tree, node->right);
++ tree->freedata(node->data);
++ nfree(node);
++}
++
++/* btree_getall():
++ * calls the specified function for each item in the tree.
++ * NOTE: getall() calls the proc _before_ it proceeds into recursion. This way,
++ * one can savely store the tree into a file without mixing up its form.
++ * But if you delete an item from the called prcedure, this function
++ * WILL crash. Use btree_getall() expanded instead.
++ */
++static void btree_getall(struct generic_binary_tree *tree, void (*func) (void *))
++{
++ Assert(tree);
++ btree_recursive_getall(tree->root, func);
++}
++
++static void btree_recursive_getall(struct generic_binary_tree_node *node,
++ void (*func) (void *))
++{
++ if (!node)
++ return;
++ // first call the function, then proceed into recursion
++ // this way, the tree keeps in form if its saved to a file, for example
++ Assert(func);
++ func(node->data);
++
++ btree_recursive_getall(node->left, func);
++ btree_recursive_getall(node->right, func);
++}
++
++/* btree_getall_expanded():
++ * the same as btree_getall(), but calls the function after the greatest level of recursion
++ * has been reached. The node-pointers won't be accessed anymore when the first function
++ * gets called. You can savely use this to free items.
++ */
++static void btree_getall_expanded(struct generic_binary_tree *tree, void (*func) (void *))
++{
++ Assert(tree);
++ btree_recursive_getall_expanded(tree->root, func);
++}
++
++static void btree_recursive_getall_expanded(struct generic_binary_tree_node *node,
++ void (*func) (void *))
++{
++ if (!node)
++ return;
++ btree_recursive_getall_expanded(node->left, func);
++ btree_recursive_getall_expanded(node->right, func);
++
++ Assert(func);
++ func(node->data);
++}
++
++static void btree_remove(struct generic_binary_tree *tree, void *data)
++{
++ struct generic_binary_tree_node *node, *last, *largenode, *lastlarge;
++ int ret, lastret;
++
++ Assert(tree);
++ Assert(data);
++ last = NULL;
++ lastret = 0;
++ node = tree->root;
++ while (node) {
++ ret = tree->comparedata(node->data, data);
++ if (ret == 0)
++ break;
++ last = node;
++ lastret = ret;
++ if (ret < 0)
++ node = node->left;
++ else
++ node = node->right;
++ }
++ if (!node) // oops, item not found
++ return;
++ if (!node->left && !node->right) {
++ // *freu* no sub-branches! We can easily delete this item.
++ if (last) {
++ if (lastret < 0)
++ last->left = NULL;
++ else
++ last->right = NULL;
++ } else
++ tree->root = NULL;
++ } else if (!node->left) {
++ // also pretty easy. Just connect the child to the parent.
++ if (last) {
++ if (lastret < 0)
++ last->left = node->right;
++ else
++ last->right = node->right;
++ } else
++ tree->root = node->right;
++ } else if (!node->right) {
++ // same as above, but mirrored
++ if (last) {
++ if (lastret < 0)
++ last->left = node->left;
++ else
++ last->right = node->left;
++ } else
++ tree->root = node->left;
++ } else {
++ // aaargh... two sub-trees! The world is not fair... *sigh*
++ debug0("argl... worst case, two subtrees. :( Let's pray...");
++ // now we take the largest item from the left subtree and replace the
++ // doomed node with it.
++ // since it is the largest val, the tree remains valid and doesn't
++ // get deformed too much.
++
++ // at first, we have to find this node and cut it from the tree
++ largenode = node->left;
++ lastlarge = NULL;
++ while (largenode && largenode->right) {
++ lastlarge = largenode;
++ largenode = largenode->right;
++ }
++
++ // only set largenode->left to node->left if largenode exists.
++ // otherwise node->left points to largenode, which would result
++ // in a nice short-circuit
++ // If it does not exist, just leave largenode->left as it is because we just
++ // move largenode one level up, so it can keep its left subtree.
++ if (lastlarge) {
++ lastlarge->right = largenode->left;
++ largenode->left = node->left;
++ }
++
++ // now connect node's subtrees to it
++ largenode->right = node->right;
++
++ // and finally replace node with largenode
++ if (last) {
++ if (lastret < 0)
++ last->left = largenode;
++ else
++ last->right = largenode;
++ } else
++ tree->root = largenode;
++ }
++ // finally kill the node... we shouldn't need it anymore
++ tree->freedata(node->data);
++ nfree(node);
++ node = NULL;
++}
++
++#ifdef BTREE_WITHOPTIMIZE
++static void btree_optimize(struct generic_binary_tree *tree,
++ struct generic_binary_tree_node *node,
++ struct generic_binary_tree_node *last,
++ int limit)
++{
++/* int leftdepth, rightdepth;
++
++ if (!node)
++ return;
++ btree_optimize(tree, node->left, node, last, limit);
++ btree_optimize(tree, node->right, node, last, limit);
++ leftdepth = btree_depth(node->left);
++ rightdepth = btree_depth(node->right);
++ if ((leftdepth - rightdepth) > limit) {
++
++ }
++*/
++}
++#endif
+diff -Nur src/mod/gseen.mod/global_vars.c src/mod/gseen.mod/global_vars.c
+--- src/mod/gseen.mod/global_vars.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/global_vars.c 2002-10-26 13:18:09.000000000 +0200
+@@ -0,0 +1,34 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++static char *glob_query, *glob_laston, *glob_otherchan, *glob_othernick;
++static char *glob_remotebot, *glob_nick;
++static struct slang_header *glob_slang;
++static seendat *glob_seendat;
++static seenreq *glob_seenrequest;
++static int glob_seenrequests, glob_totalnicks, glob_totalbytes;
++
++static void reset_global_vars()
++{
++ glob_query = glob_laston = glob_otherchan = glob_othernick = NULL;
++ glob_remotebot = glob_nick = NULL;
++ glob_seendat = NULL;
++ glob_slang = NULL;
++ glob_seenrequest = NULL;
++ glob_seenrequests = glob_totalnicks = glob_totalbytes = 0;
++}
+diff -Nur src/mod/gseen.mod/gseen.c src/mod/gseen.mod/gseen.c
+--- src/mod/gseen.mod/gseen.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/gseen.c 2002-10-26 14:24:48.000000000 +0200
+@@ -0,0 +1,328 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++#define MAKING_GSEEN
++#define MODULE_NAME "gseen"
++#define MODULE_VERSION "1.1.1 dev3"
++#define MODULE_NUMVERSION 10100
++#include "../module.h"
++#include "../irc.mod/irc.h"
++#include "../server.mod/server.h"
++#include "../channels.mod/channels.h"
++#include <stdlib.h>
++#include <sys/stat.h>
++#include <time.h> /* for time_t */
++
++#undef global
++static Function *global = NULL, *irc_funcs = NULL, *server_funcs = NULL, *channels_funcs = NULL;
++
++#ifndef EGG_IS_MIN_VER
++#define EGG_IS_MIN_VER(ver) ((ver) <= 10400)
++#endif
++
++#ifndef EGG_IS_MAX_VER
++#define EGG_IS_MAX_VER(ver) ((ver) >= 10400)
++#endif
++
++#ifndef Context
++#define Context context
++#endif
++
++#ifndef findchan_by_dname
++#define findchan_by_dname findchan
++#endif
++
++#include "gseen.h"
++#include "seenlang.h"
++
++static struct slang_header *coreslangs = NULL;
++static gseenres *results = NULL;
++static seenreq *requests = NULL;
++static ignoredword *ignoredwords = NULL;
++static char *bnsnick = NULL;
++static char *bnschan = NULL;
++static char *seen_reply = NULL;
++static char *temp_wildmatch_host;
++static int numresults = 0;
++static double glob_presearch, glob_aftersearch;
++int numseens, glob_total_queries;
++double glob_total_searchtime;
++
++static char gseenfile[121] = "gseen.dat";
++static char no_pub[121];
++static char quiet_seen[121];
++static char quiet_ai_seen[121];
++static char no_log[121];
++static char ignore_words[1024];
++static char default_slang[21] = "eng";
++static int gseen_numversion = MODULE_NUMVERSION;
++static int save_seens = 60;
++static int save_seens_temp = 1;
++static int expire_seens = 60;
++static int maxseen_thr = 0;
++static int maxseen_time = 0;
++static int seenflood_thr = 0;
++static time_t seenflood_time = 0;
++static int use_handles = 0;
++static int tell_seens = 1;
++static int botnet_seen = 1;
++int fuzzy_search = 1; // search for the same user under a differnt nick
++static int wildcard_search = 1;// allow wildcard seaching? ("*!*@*.isp.de")
++static int max_matches = 500; // break if there are more than X matches
++static int hide_secret_chans = 1; // #chan (+secret) => [secret]
++static int seen_nick_len = 32;
++
++#include "global_vars.c"
++#define SLANG_NOTYPES 1
++#define SLANG_NOFACTS 1
++#define SLANG_NOGETALL 1
++#define SLANG_NOVALIDATE 1
++#include "slang.c"
++#include "slang_gseen_commands.c"
++#include "generic_binary_tree.c"
++#include "seentree.c"
++#include "datahandling.c"
++#include "sensors.c"
++#include "do_seen.c"
++#include "gseencmds.c"
++#include "ai.c"
++#include "misc.c"
++#include "tclcmds.c"
++
++static int gseen_expmem()
++{
++ int size = 0;
++
++ size += seentree_expmem();
++ size += expmem_seenresults();
++ size += expmem_seenreq();
++ size += expmem_ignoredwords();
++ size += slang_expmem(coreslangs);
++ size += slang_glob_expmem();
++ size += slang_chanlang_expmem(chanlangs);
++ if (bnsnick)
++ size += strlen(bnsnick) + 1;
++ if (bnschan)
++ size += strlen(bnschan) + 1;
++ if (seen_reply) {
++ size += strlen(seen_reply) + 1;
++ }
++ return size;
++}
++
++static void free_gseen()
++{
++ seentree_free();
++ slang_free(coreslangs);
++ slang_chanlang_free(chanlangs);
++ if (seen_reply)
++ nfree(seen_reply);
++ return;
++}
++
++/* a report on the module status */
++static void gseen_report(int idx, int details)
++{
++ int size = 0;
++
++ Context;
++ if (details) {
++ size = gseen_expmem();
++ dprintf(idx, " using %d bytes\n", size);
++ }
++}
++
++static void gseen_minutely ()
++{
++ if (save_seens_temp >= save_seens) {
++ write_seens();
++ save_seens_temp = 1;
++ } else
++ save_seens_temp++;
++}
++
++static void gseen_daily ()
++{
++ Context;
++ purge_seens();
++}
++
++static tcl_strings my_tcl_strings[] =
++{
++ {"gseenfile", gseenfile, 121, 0},
++ {"ai-seen-ignore", ignore_words, 1024, 0},
++ {"no-pub-seens", no_pub, 121, 0},
++ {"quiet-seens", quiet_seen, 121, 0},
++ {"quiet-ai-seens", quiet_ai_seen, 121, 0},
++ {"no-log", no_log, 121, 0},
++ {"no-seendata", no_log, 121, 0},
++ {"default-slang", default_slang, 20, 0},
++ {0, 0, 0, 0}
++};
++
++static tcl_ints my_tcl_ints[] =
++{
++ {"save-seens", &save_seens, 0},
++ {"expire-seens", &expire_seens, 0},
++ {"use-handles", &use_handles, 0},
++ {"tell-seens", &tell_seens, 0},
++ {"botnet-seens", &botnet_seen, 0},
++ {"max-matches", &max_matches, 0},
++ {"fuzzy-search", &fuzzy_search, 0},
++ {"wildcard-search", &wildcard_search, 0},
++ {"hide-secret-chans", &hide_secret_chans, 0},
++ {"seen-nick-len", &seen_nick_len, 0},
++ {0, 0, 0}
++};
++
++static tcl_coups my_tcl_coups[] =
++{
++ {"max-seens", &maxseen_thr, &maxseen_time},
++ {0, 0, 0},
++};
++
++static char *gseen_close()
++{
++ Context;
++ write_seens();
++ slang_glob_free();
++ free_gseen();
++ free_seenreq();
++ free_seenresults();
++ free_ignoredwords();
++ if (bnsnick)
++ nfree(bnsnick);
++ if (bnschan)
++ nfree(bnschan);
++ rem_tcl_strings(my_tcl_strings);
++ rem_tcl_ints(my_tcl_ints);
++ rem_tcl_coups(my_tcl_coups);
++ rem_tcl_commands(mytcls);
++ rem_tcl_commands(gseentcls);
++ rem_tcl_commands(seendebugtcls);
++ rem_tcl_commands(gseentcls);
++ rem_builtins(H_dcc, mydcc);
++ rem_builtins(H_join, seen_join);
++ rem_builtins(H_kick, seen_kick);
++ rem_builtins(H_nick, seen_nick);
++ rem_builtins(H_part, seen_part);
++ rem_builtins(H_sign, seen_sign);
++ rem_builtins(H_splt, seen_splt);
++ rem_builtins(H_rejn, seen_rejn);
++ rem_builtins(H_pub, seen_pub);
++ rem_builtins(H_msg, seen_msg);
++ rem_builtins(H_bot, seen_bot);
++ del_hook(HOOK_MINUTELY, (Function) gseen_minutely);
++ del_hook(HOOK_DAILY, (Function) gseen_daily);
++ module_undepend(MODULE_NAME);
++ return NULL;
++}
++
++char *gseen_start();
++
++static Function gseen_table[] =
++{
++ (Function) gseen_start,
++ (Function) gseen_close,
++ (Function) gseen_expmem,
++ (Function) gseen_report,
++ /* 4 - 7 */
++ (Function) findseens,
++ (Function) free_seenresults,
++ (Function) gseen_duration,
++ (Function) & glob_seendat,
++ (Function) & numresults,
++ (Function) & fuzzy_search,
++ (Function) & numseens,
++ (Function) & glob_total_queries,
++ (Function) & glob_total_searchtime,
++ (Function) & gseen_numversion,
++};
++
++char *gseen_start(Function * global_funcs)
++{
++ global = global_funcs;
++ Context;
++ module_register(MODULE_NAME, gseen_table, 1, 1);
++ if (!(irc_funcs = module_depend(MODULE_NAME, "irc", 1, 0)))
++ return "You need the irc module to use the gseen module.";
++ if (!(server_funcs = module_depend(MODULE_NAME, "server", 1, 0)))
++ return "You need the server module to use the gseen module.";
++ if (!(channels_funcs = module_depend(MODULE_NAME, "channels", 1, 0)))
++ return "You need the channels module to use the gseen module.";
++ if (!module_depend(MODULE_NAME, "eggdrop", 107, 0)) {
++ if (!module_depend(MODULE_NAME, "eggdrop", 106, 0)) {
++ if (!module_depend(MODULE_NAME, "eggdrop", 105, 0)) {
++ if (!module_depend(MODULE_NAME, "eggdrop", 104, 0)) {
++ module_undepend(MODULE_NAME);
++ return "This module requires eggdrop1.4.0 or later";
++ }
++ }
++ }
++ }
++ chanlangs = NULL;
++ coreslangs = NULL;
++ slang_glob_init();
++
++ results = NULL;
++ requests = NULL;
++ ignoredwords = NULL;
++ bnsnick = NULL;
++ bnschan = NULL;
++ seen_reply = NULL;
++
++ numresults = 0;
++ numseens = 0;
++ glob_total_queries = 0;
++ glob_total_searchtime = 0.0;
++ ignore_words[0] = 0;
++ no_pub[0] = 0;
++ quiet_seen[0] = 0;
++ no_log[0] = 0;
++ seentree_init();
++ add_tcl_strings(my_tcl_strings);
++ add_tcl_ints(my_tcl_ints);
++ add_tcl_coups(my_tcl_coups);
++ add_tcl_commands(mytcls);
++ add_tcl_commands(seendebugtcls);
++ add_tcl_commands(gseentcls);
++ add_builtins(H_dcc, mydcc);
++ add_builtins(H_join, seen_join);
++ add_builtins(H_kick, seen_kick);
++ add_builtins(H_nick, seen_nick);
++ add_builtins(H_part, seen_part);
++ add_builtins(H_sign, seen_sign);
++ add_builtins(H_sign, seen_sign);
++ add_builtins(H_splt, seen_splt);
++ add_builtins(H_rejn, seen_rejn);
++ add_builtins(H_pub, seen_pub);
++ add_builtins(H_msg, seen_msg);
++ add_builtins(H_bot, seen_bot);
++ read_seens();
++ add_hook(HOOK_MINUTELY, (Function) gseen_minutely);
++ add_hook(HOOK_DAILY, (Function) gseen_daily);
++#if EGG_IS_MIN_VER(10503)
++ initudef(1, "noseendata", 1);
++ initudef(1, "quietseens", 1);
++ initudef(1, "quietaiseens", 1);
++ initudef(1, "nopubseens", 1);
++#endif
++ glob_slang_cmd_list = slang_commands_list_add(glob_slang_cmd_list, slang_text_gseen_command_table);
++ putlog(LOG_MISC, "*", "gseen.mod v%s loaded.", MODULE_VERSION);
++ return NULL;
++}
+diff -Nur src/mod/gseen.mod/gseen.conf src/mod/gseen.mod/gseen.conf
+--- src/mod/gseen.mod/gseen.conf 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/gseen.conf 2002-10-26 13:17:54.000000000 +0200
+@@ -0,0 +1,147 @@
++
++######
++#####
++### General Settings
++#####
++######
++
++# the file where the seen data will be backuped.
++# WARNING: set this _before_ the module is loaded.
++set gseenfile "gseen.dat"
++
++# now load the module
++loadmodule gseen
++
++# load the English language file
++loadseenslang "en" "English" language/gseen.en.lang
++
++# load the German language file
++loadseenslang "de" "Deutsch" language/gseen.de.lang
++
++# set the default language to english...
++set default-slang "en"
++
++# ... but let #xwp use the german langfile
++setchanseenlang #xwp "de"
++
++# the char that marks public commands (!seen, etc...)
++# "" is a valid option
++set cmdchar "!"
++
++# delete data sets that are older than x days
++set expire-seens 60
++
++# only answer x seen requests in y seconds to prevent flooding
++set max-seens 7:60
++
++# tell users if someone was !seen'ing for them
++set tell-seens 1
++
++# check if the user was online under a different nick
++set fuzzy-search 1
++
++# allow user to include wildcards in the search?
++set wildcard-search 1
++
++# break search if there are more than x matches
++set max-matches 250
++
++# forward a request to other bots, if a !seen returned no result?
++set botnet-seens 1
++
++# store channels, which are +secret on the bot as [secret]?
++set hide-secret-chans 1
++
++# backup the seen data every x minutes
++set save-seens 60
++
++######
++#####
++### AI Settings
++#####
++######
++
++# this setting configures on which sentences your bot should
++# attempt to do an ai-seen. Each of them is a simple wildcard
++# mask. Set this to "" if you want to deactivate ai-seens or
++# create more precise masks if the bots reacts too often.
++set ai-seen-binds {
++ "${nick}*seen*"
++ "${botnet-nick}*seen*"
++ "${nick}*gesehen*"
++ "${botnet-nick}*gesehen*"
++}
++
++# this is just the same as above, but if triggered it will
++# not do an ai-seen, but display its seen-stats.
++set ai-seenstats-binds {
++ "${nick}*seenstats*"
++ "${botnet-nick}*seenstats*"
++}
++
++# when doing an AI seen, ignore the following words (otherwise
++# the bot might give weird answers like "<bot> nick, bot was last seen..." :)
++set ai-seen-ignore "$nick ${botnet-nick} seen"
++
++######
++#####
++### special stuff (can be ignored in most cases)
++#####
++######
++
++# if the user is known by the bot, log their handle instead of the nick
++# (not recommended, might cause confusion by the users)
++set use-handles 0
++
++######
++#####
++### outdated settings (only important for eggdropv1.4 users)
++#####
++######
++
++# channels where you do not want your bot to reply to public queries
++set no-pub-seens ""
++
++# channels where you want your bot to send replies via notice to the user and
++# not to the channel
++set quiet-seens ""
++
++# same as quiet-seens but for AI seen
++set quiet-ai-seens ""
++
++# channels where you do not want your bot to log seen data
++set no-seendata ""
++
++
++###############################################################################
++# end of configuration
++# just ignore everything below ^_^
++###############################################################################
++
++bind chjn - * *chjn:gseen
++bind chpt - * *chpt:gseen
++
++catch "unbind pub - !seen *pub:!seen"
++catch "unbind pub - !seennick *pub:!seennick"
++catch "unbind pub - !seenstats *pub:!seenstats"
++bind pub - ${cmdchar}seen *pub:!seen
++bind pub - ${cmdchar}seennick *pub:!seennick
++bind pub - ${cmdchar}seenstats *pub:!seenstats
++
++foreach bnd [binds pubm] {
++ if {([lindex $bnd 2] == "*pubm:seen") || ([lindex $bnd 2] == "*pub:!seenstats")} {
++ unbind [lindex $bnd 0] [lindex $bnd 1] [lindex $bnd 2] [lindex $bnd 4]
++ }
++}
++
++if {${ai-seen-binds} != ""} {
++ foreach mask ${ai-seen-binds} {
++ bind pubm -|- "% [subst $mask]" *pubm:seen
++ }
++}
++
++if {${ai-seenstats-binds} != ""} {
++ foreach mask ${ai-seenstats-binds} {
++ bind pubm -|- "% [subst $mask]" *pub:!seenstats
++ }
++}
+diff -Nur src/mod/gseen.mod/language/gseen.de.lang src/mod/gseen.mod/language/gseen.de.lang
+--- src/mod/gseen.mod/language/gseen.de.lang 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/language/gseen.de.lang 2002-10-26 13:18:12.000000000 +0200
+@@ -0,0 +1,131 @@
++#####################################################################
++#
++# Deutsche Sprachdatei fьr GSeen.Mod v1.1.0
++#
++# Der Text in dieser Datei kann nach belieben verдndert werden. Du
++# kannst Tags hinzufьgen oder entfernen, wie es Dir gefдllt. Die Tags
++# mьssen nicht in einer bestimmten Reihenfolge oder Anzahl vorkommen.
++#
++# Wenn Du mehr als eine Zeile pro ID angibst, dann wird bei der
++# Antwort per Zufall eine daraus ausgewдhlt. (das funktioniert nicht
++# bei den Zeiteinheiten)
++#
++# Falls Du denkst, daЯ hier noch ein paar wichtige Tags fehlen, dann
++# schick mir einfach eine email. Vielleicht fьge ich sie dann in der
++# nдchsten Version hinzu.
++#
++# Eine komplette Liste der Verfьgbaren Tags befindet sich am Ende von
++# slang_gseen_commands.c (leider ohne Erklдhrungen)
++#
++#####################################################################
++
++#
++## Zeiteinheiten
++#
++# jeweils in Singular und Plural
++#
++D 0 Jahr
++D 1 Jahre
++D 2 Woche
++D 3 Wochen
++D 4 Tag
++D 5 Tage
++D 6 Stunde
++D 7 Stunden
++D 8 Minute
++D 9 Minuten
++D 10 Sekunde
++D 11 Sekunden
++# falls ein ьngьltiger Zeitwert angegeben war, dann wird dieser Text ausgegeben:
++D 12 einiger Zeit
++
++
++#
++## Prдfixe
++#
++# Dieses Fragment wird jeweils vor eine Antwort gesetzt. Dadurch
++# ist beispielsweise bei цffentlichen Anfragen ersichtlich, fьr
++# wen die Antwort ist.
++# Achtung: Die Nummer muss auf jeden Fall definiert werden. Sie muss
++# zwar keinen Text beinhalten, aber wenn sie nicht vorhanden
++# ist, dann gibt es eine Fehlermeldung
++
++# fьr Antworten, die in den Channel geschrieben werden:
++10 <?nick/?>,
++# fьr Antworten, die per NOTICE an den User geschickt werden:
++11
++# fьr Antworten auf Anfragen, die per "/msg <bot> seen" erfolgt sind:
++12
++# und fьr Antworten auf der Partyline:
++13
++
++#
++## Fehlermeldungen
++#
++54 weiЯt Du was ein Parameter ist? ^_^
++54 ich wьrde Dir ja gerne helfen, aber solange Du nicht sagst, nach wem Du suchst, kann ich nicht viel tun.
++54 meinst Du nicht, es wдre geschickter zu sagen, nach wem Du ьberhaupt suchst?
++54 42.
++55 sehe ich etwa wie ein Spiegel aus? ^_^
++55 Spieglein, Spieglein an der Wand...
++55 leidest Du etwa unter multiplen Persцnlichkeiten? *eg*
++56 also wenn Du <?query/?> jetzt hier nicht sehen kannst, dann brauchst Du sicherlich eine neue Brille ^_^
++56 ich muss mir unbedingt mal die Tarnkappe von <?query/?> ausleihen. Scheint ja prima zu funktioneren.
++56 schau Dir bitte nochmal ganz genau an, wer grade alles im Channel ist.
++57 Tut mir leid, aber Wildcards ('?', oder '*') sind bei der Suche nicht erlaubt.
++58 Цhm... naja... etwas arg lang, dieser Nick... :)
++
++#
++## Kein Ergebnis
++#
++65 Ich kann mich nicht daran erinnern, <?query/?> gesehen zu haben...
++65 <?query/?>? Hmm... ich bin mir nicht sicher... vielleicht... eventuell... nein, kenne ich nicht.
++65 der Name sagt mir nichts. Hast Du Dich vielleicht vertippt?
++66 Ich hab' <?query/?> seit <?laston/?> nicht mehr gesehen.
++67 Sorry, aber zu deiner Anfrage passt nichts in meiner Datenbank :(
++68 Autschi, das gab viel zu viele Ergebnisse. Bitte formuliere deine Suche etwas genauer.
++
++73 <?query/?> ist grade unter dem Nick "<?othernick/?>" in diesem Channel zu finden.
++74 <?query/?> ist gerade in <?otherchan/?>.
++75 Deine Anfrage fьhrte zu genau einem Ergebnis:
++76 Immerhin <?numresults/?> Treffer ergab deine Anfrage:
++77 Wow, auf deine Anfrage passen sogar <?numresults/?> Eintrдge in meiner Datenbank! Dies sind die 5 aktuellsten:
++
++#
++## falls ein anderer Bot etwas gefunden hat:
++#
++85 <?remotebot/?> sagt:
++
++#
++## die eigentliche Information
++#
++101 Ich habe <?snick/?> (<?shost/?>) zuletzt <?schan/?> vor <?swhen/?> betreten sehen (<?stime/?>). <?snick/?> ist noch immer da.
++121 Ich habe <?snick/?> (<?shost/?>) zuletzt <?schan/?> vor <?swhen/?> betreten sehen (<?stime/?>), aber <?snick/?> verschwand mysteriцserweise.
++102 Ich habe <?snick/?> (<?shost/?>) zuletzt <?schan/?> vor <?swhen/?> nach <?spent/?> verchatteter Zeit verlassen sehen (<?stime/?>)
++103 Ich habe <?snick/?> (<?shost/?>) zuletzt in <?schan/?> gesehen, als er/sie vor <?swhen/?> (<?stime/?>) nach <?spent/?> das IRC verlieЯ ("<?smsg/?>").
++104 Zuletzt habe ich <?snick/?> (<?shost/?>) vor <?swhen/?> in <?schan/?> gesehen, den Nick zu <?snick2/?> wechselnd. <?snick2/?> ist noch immer dort.
++124 <?snick/?> (<?shost/?>) was last seen changing his/her nick to <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick2/?> mysteriously dematerialized.
++105 Zuletzt habe ich <?snick/?> (<?shost/?>) vor <?swhen/?> in <?schan/?> gesehen, den Nick von <?snick2/?> wechselnd. <?snick/?> ist noch immer dort.
++125 <?snick/?> (<?shost/?>) was last seen changing his/her nick from <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick/?> mysteriously dematerialized.
++106 Zuletzt habe ich <?snick/?> (<?shost/?>) gesehen, als er vor <?swhen/?> (<?stime/?>) von <?punisher/?> aus <?schan/?> gejagt wurde. (<?kickreason/?>)
++107 <?snick/?> (<?shost/?>) habe ich zuletzt vor <?swhen/?> gesehen, als er/sie von <?schan/?> aus in einem Netsplit verschwand.
++108 <?snick/?> (<?shost/?>) habe ich zuletzt vor <?swhen/?> gesehen, als er/sie nach einem Netsplit in <?schan/?> zurьck kam. <?snick/?> ist noch immer dort.
++128 <?snick/?> (<?shost/?>) habe ich zuletzt vor <?swhen/?> gesehen, als er/sie nach einem Netsplit in <?schan/?> zurьck kam. Allerdings konnte <?snick/?> dem Gott der Netsplits nicht endgьltig entkommen und ist wieder verschollen...
++109 <?snick/?> was last seen joining the botnet channel <?schan/?> on <?bnbot/?> <?swhen/?> ago (<?stime/?>).
++129 <?snick/?> was last seen joining the partyline on <?bnbot/?> <?swhen/?> ago (<?stime/?>).
++110 <?snick/?> was last seen leaving the botnet channel <?schan/?> from <?bnbot/?> <?swhen/?> ago (<?stime/?>).
++130 <?snick/?> was last seen leaving the partyline from <?bnbot/?> <?swhen/?> ago (<?stime/?>).
++140 <?snick/?> (<?shost/?>) was last seen on <?schan/?> <?swhen/?> ago (<?stime/?>).
++
++#
++## Seen-Mitteilungen
++#
++170 <?rnick/?> (<?rhost/?>) scheint vor <?rwhen/?> (<?rtime/?>) in <?rchan/?> auf der Suche nach Dir gewesen zu sein.
++171 <?requests/?> Leute haben sich nach Dir erkundigt:
++172 Der/die letzte war <?rnick/?> (<?rhost/?>) in <?rchan/?> vor <?rwhen/?> (<?rtime/?>).
++
++#
++## Statistiken
++#
++180 Momentan sind <?totalnicks/?> Nicks in meiner Datenbank. Gesamter Speicherverbrauch: <?totalbytes/?> Bytes
++180 In meiner Datenbank befinden sich <?totalnicks/?> Nicks und verbrauchen <?totalbytes/?> Bytes Speicher.
+diff -Nur src/mod/gseen.mod/language/gseen.en.lang src/mod/gseen.mod/language/gseen.en.lang
+--- src/mod/gseen.mod/language/gseen.en.lang 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/language/gseen.en.lang 2002-10-26 13:18:13.000000000 +0200
+@@ -0,0 +1,131 @@
++#####################################################################
++#
++# Default English langfile for GSeen.Mod v1.1.0
++#
++# Just edit the text below to fit your needs. You can add or remove
++# any tag just like you want, they do not need to appear in a special
++# order (or number).
++#
++# If you enter more than one line per ID, then a random one will be
++# chosen for each reply. (this does not work for the time strings)
++#
++# If you think you need more tags, just email me and maybe I'll add
++# them in the next release.
++#
++# A complete list of available Tags can be found at the end of the
++# file slang_gseen_commands.c (unfortunately, it does not contain any
++# descriptions for the tags)
++#
++#####################################################################
++
++#
++## time string
++#
++# each time string in singular and plural
++#
++D 0 year
++D 1 years
++D 2 week
++D 3 weeks
++D 4 day
++D 5 days
++D 6 hour
++D 7 hours
++D 8 minute
++D 9 minutes
++D 10 second
++D 11 seconds
++# if an invalid time value was supplied, output the following string
++D 12 some time
++
++#
++## Prefixes
++#
++# These are the prefixes of the replies. By default, there's only
++# a prefix for public requests (so you know for whom the answer is),
++# but you can also define prefixes for other requests.
++
++# for replies in the channel:
++10 <?nick/?>,
++# for replies via notice:
++11
++# for replies via PRIVMSG
++12
++# for replies on the partyline
++13
++
++#
++## error messages
++#
++54 do you know what a parameter is?
++54 don't you think it would be more reasonable to say for whom you are searching?
++54 42.
++55 do I look like a mirror? ^_^
++55 mirror mirror on the wall...
++55 do you have a split personality? *eg*
++56 if you can't see <?query/?> here right now, you probably need new glasses. ^_^
++56 please look a bit closer at the memberlist of this channel.
++57 I'm sorry, but wildcards ('?' or '*') are not allowed in a search.
++58 Hum... don't you think this nick is a bit long? ^_^
++58 you know that the length of nicks is limited, don't you?
++
++#
++## no result
++#
++65 I don't remember seeing <?query/?>.
++65 <?query/?>? hmm... I'm trying to remember... maybe... I'm not sure... no. I don't remember <?query/?>.
++66 I haven't seen <?query/?> for <?laston/?>.
++67 I found no matches to your query.
++67 I'm sorry, but your search didn't return any results.
++68 Ouch, your search returned way too many matches. Please refine it.
++
++#
++## victim is online
++#
++73 <?query/?> is <?othernick/?>, who is on this channel right now.
++74 <?query/?> is on <?otherchan/?> right now.
++
++#
++## results found
++#
++75 I found one match to your query:
++76 I found <?numresults/?> matches to your query:
++77 I found <?numresults/?> matches to your query. These are the 5 most recent ones:
++
++#
++## results found by another bot in the botnet
++#
++85 <?remotebot/?> says:
++
++#
++## the core info
++#
++101 <?snick/?> (<?shost/?>) was last seen joining <?schan/?> <?swhen/?> ago (<?stime/?>). <?snick/?> is still there.
++121 <?snick/?> (<?shost/?>) was last seen joining <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick/?> mysteriously dematerialized.
++102 <?snick/?> (<?shost/?>) was last seen parting <?schan/?> <?swhen/?> ago (<?stime/?>), after spending <?spent/?> there.
++103 <?snick/?> (<?shost/?>) was last seen quitting <?schan/?> <?swhen/?> ago (<?stime/?>) stating "<?smsg/?>" after spending <?spent/?> there.
++104 <?snick/?> (<?shost/?>) was last seen changing his/her nick to <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>). <?snick2/?> is still there.
++124 <?snick/?> (<?shost/?>) was last seen changing his/her nick to <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick2/?> mysteriously dematerialized.
++105 <?snick/?> (<?shost/?>) was last seen changing his/her nick from <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>). <?snick/?> is still there.
++125 <?snick/?> (<?shost/?>) was last seen changing his/her nick from <?snick2/?> on <?schan/?> <?swhen/?> ago (<?stime/?>), but <?snick/?> mysteriously dematerialized.
++106 <?snick/?> (<?shost/?>) was last seen being kicked from <?schan/?> by <?punisher/?> (<?kickreason/?>) <?swhen/?> ago (<?stime/?>), after spending <?spent/?> there.
++107 <?snick/?> (<?shost/?>) was last seen splitting from <?schan/?> <?swhen/?> ago (<?stime/?>), after spending <?spent/?> there.
++108 <?snick/?> (<?shost/?>) was last seen rejoining <?schan/?> from a netsplit <?swhen/?> ago (<?stime/?>) <?snick/?> is still there.
++128 <?snick/?> (<?shost/?>) was last seen rejoining <?schan/?> from a netsplit <?swhen/?> ago (<?stime/?>), but the god of netsplits didn't let him escape, so he's not here now.
++109 <?snick/?> was last seen joining the botnet channel <?schan/?> on <?bnbot/?> <?swhen/?> ago (<?stime/?>).
++129 <?snick/?> was last seen joining the partyline on <?bnbot/?> <?swhen/?> ago (<?stime/?>).
++110 <?snick/?> was last seen leaving the botnet channel <?schan/?> from <?bnbot/?> <?swhen/?> ago (<?stime/?>).
++130 <?snick/?> was last seen leaving the partyline from <?bnbot/?> <?swhen/?> ago (<?stime/?>).
++140 <?snick/?> (<?shost/?>) was last seen on <?schan/?> <?swhen/?> ago (<?stime/?>).
++
++#
++## seen notification
++#
++170 <?rnick/?> (<?rhost/?>) was looking for you on <?rchan/?> <?rwhen/?> ago (<?rtime/?>).
++171 There have been <?requests/?> users looking for you:
++172 The last one was <?rnick/?> (<?rhost/?>) on <?rchan/?> <?rwhen/?> ago (<?rtime/?>).
++
++#
++## seen stats
++#
++180 I'm currently tracking <?totalnicks/?> nicks using <?totalbytes/?> bytes.
+diff -Nur src/mod/gseen.mod/gseen.h src/mod/gseen.mod/gseen.h
+--- src/mod/gseen.mod/gseen.h 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/gseen.h 2002-10-26 13:17:55.000000000 +0200
+@@ -0,0 +1,157 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++/* #define USE_MEMDEBUG 1 */
++
++#define SEEN_JOIN 1
++#define SEEN_PART 2
++#define SEEN_SIGN 3
++#define SEEN_NICK 4
++#define SEEN_NCKF 5
++#define SEEN_KICK 6
++#define SEEN_SPLT 7
++#define SEEN_REJN 8
++#define SEEN_CHPT 9
++#define SEEN_CHJN 10
++
++typedef struct gseen_data {
++ int type;
++ char *nick;
++ char *host;
++ char *chan;
++ char *msg;
++ time_t when;
++ int spent;
++} seendat;
++
++typedef struct gseen_result {
++ struct gseen_result *next;
++ seendat *seen;
++} gseenres;
++
++typedef struct gseen_requests {
++ struct gseen_requests *next;
++ char *who;
++ char *host;
++ char *chan;
++ time_t when;
++} seenreq_by;
++
++typedef struct gseen_request {
++ struct gseen_request *next;
++ char *nick;
++ struct gseen_requests *by;
++} seenreq;
++
++typedef struct gseen_ignorewords {
++ struct gseen_ignorewords *next;
++ char *word;
++} ignoredword;
++
++#ifdef MAKING_GSEEN
++static int gseen_expmem();
++static void free_gseen();
++static int get_spent(char *, char *);
++static void write_seens();
++static void read_seens();
++static char *do_seen(char *, char *, char *, char *, int);
++static void add_seenresult(seendat *);
++static int expmem_seenresults();
++static void free_seenresults();
++static void sortresults();
++static char *do_seennick(seendat *);
++static int onchan(char *, char *);
++static char *handonchan(char *, char *);
++static struct chanset_t *onanychan(char *);
++static struct chanset_t *handonanychan(char *);
++static char *do_seenstats();
++static void add_seenreq(char *, char *, char *, char *, time_t);
++static int expmem_seenreq();
++static void free_seenreq();
++static void sortrequests(seenreq *);
++static void report_seenreq(char *, char *);
++static int count_seenreq(seenreq_by *b);
++static int expmem_ignoredwords();
++static void free_ignoredwords();
++static void add_ignoredword(char *word);
++static int word_is_ignored(char *word);
++static void purge_seens();
++static int seenflood();
++static int secretchan(char *);
++static int nopub(char *);
++static int quietseen(char *);
++static int quietaiseens(char *);
++static int nolog(char *);
++static void start_seentime_calc();
++static void end_seentime_calc();
++#endif
++
++
++#ifdef MAKING_GSEEN
++
++// tree stuff
++static void maskstricthost(const char *, char *);
++#endif
++
++// interface for webseen
++#define WS_OK 0
++#define WS_NORESULT 1
++#define WS_NOPARAM 2
++#define WS_NOWILDCARDS 3
++#define WS_TOOLONGNICK 4
++#define WS_TOOMANYMATCHES 5
++#define WS_TOOLONGHOST 6
++
++#ifndef MAKING_GSEEN
++#define findseens ((gseenres *(*)(char *, int *, int))gseen_funcs[4])
++#define free_seenresults ((void (*)())gseen_funcs[5])
++#define gseen_duration ((char *(*)(int))gseen_funcs[6])
++#define numresults (*(int *)(gseen_funcs[12]))
++#define fuzzy_search (*(int *)(gseen_funcs[13]))
++#define numseens (*(int *)(gseen_funcs[15]))
++#define glob_total_queries (*(int *)(gseen_funcs[16]))
++#define glob_total_searchtime (*(double *)(gseen_funcs[17]))
++#define gseen_numversion (*(int *)(gseen_funcs[19]))
++#else
++static gseenres *findseens(char *, int *, int);
++static char *gseen_duration(int);
++#endif
++
++#ifdef MAKING_GSEEN
++
++#ifdef malloc
++#undef malloc
++#endif
++#ifdef free
++#undef free
++#endif
++#ifdef realloc
++#undef realloc
++#endif
++
++#ifdef USE_MEMDEBUG
++#define my_malloc nmalloc
++#define my_free nfree
++#define my_realloc nrealloc
++#else
++#define my_malloc malloc
++#define my_free free
++#define my_realloc realloc
++#endif
++
++#endif
+diff -Nur src/mod/gseen.mod/gseencmds.c src/mod/gseen.mod/gseencmds.c
+--- src/mod/gseen.mod/gseencmds.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/gseencmds.c 2002-10-26 13:17:56.000000000 +0200
+@@ -0,0 +1,420 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++#define PREFIX_LENGTH 20
++
++static char reply_prefix[PREFIX_LENGTH + 1];
++#define set_prefix(x) strncpy(reply_prefix, x, PREFIX_LENGTH); \
++ reply_prefix[PREFIX_LENGTH] = 0;
++
++static int seenflood()
++{
++ if (!maxseen_thr || !maxseen_time)
++ return 0;
++ if ((now - seenflood_time) > maxseen_time) {
++ seenflood_time = now;
++ seenflood_thr = 0;
++ }
++ seenflood_thr++;
++ if (seenflood_thr > maxseen_thr)
++ return 1;
++ else
++ return 0;
++}
++
++static int nopub(char *chan)
++{
++ char buf[121], *b;
++
++ Context;
++ strncpy(buf, no_pub, 120);
++ buf[120] = 0;
++ b = buf;
++ while (b[0])
++ if (!strcasecmp(chan, newsplit(&b)))
++ return 1;
++#if EGG_IS_MIN_VER(10503)
++ if (ngetudef("nopubseens", chan))
++ return 1;
++#endif
++ return 0;
++}
++
++static int quietseen(char *chan)
++{
++ char buf[121], *b;
++
++ Context;
++ strncpy(buf, quiet_seen, 120);
++ buf[120] = 0;
++ b = buf;
++ while (b[0])
++ if (!strcasecmp(chan, newsplit(&b)))
++ return 1;
++#if EGG_IS_MIN_VER(10503)
++ if (ngetudef("quietseens", chan))
++ return 1;
++#endif
++ return 0;
++}
++
++static int cmd_seen(struct userrec *u, int idx, char *par)
++{
++ char *query;
++
++ Context;
++ if (seenflood())
++ return 0;
++ reset_global_vars();
++ glob_slang = slang_find(coreslangs, default_slang);
++ glob_nick = dcc[idx].nick;
++ query = newsplit(&par);
++ glob_query = query;
++ set_prefix(SLDCCPREFIX);
++ putlog(LOG_CMDS, "*", "#%s# seen %s", dcc[idx].nick, par);
++ dprintf(idx, "%s%s\n", reply_prefix, do_seen(query, dcc[idx].nick,
++ dcc[idx].host, "[partyline]", botnet_seen));
++ return 0;
++}
++
++static int cmd_seenstats(struct userrec *u, int idx, char *par)
++{
++ Context;
++ if (seenflood())
++ return 0;
++ reset_global_vars();
++ glob_slang = slang_find(coreslangs, default_slang);
++ glob_nick = dcc[idx].nick;
++ set_prefix(SLDCCPREFIX);
++ putlog(LOG_CMDS, "*", "#%s# seenstats", dcc[idx].nick);
++ dprintf(idx, "%s%s\n", reply_prefix, do_seenstats());
++ return 0;
++}
++
++static int cmd_purgeseens(struct userrec *u, int idx, char *par)
++{
++ Context;
++ purge_seens();
++ putlog(LOG_CMDS, "*", "#%s# purgeseens", dcc[idx].nick);
++ return 0;
++}
++
++static int pub_seen(char *nick, char *host, char *hand,
++ char *channel, char *text)
++{
++ char *dest;
++#if EGG_IS_MIN_VER(10500)
++ struct chanset_t *chan;
++#endif
++
++ Context;
++ if (seenflood() || nopub(channel))
++ return 0;
++ reset_global_vars();
++ glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, channel));
++ glob_nick = nick;
++ putlog(LOG_CMDS, "*", "<<%s>> !%s! seen %s", nick, hand, text);
++ if (quietseen(channel)) {
++ set_prefix(SLNOTPREFIX);
++ dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix,
++ do_seen(newsplit(&text), nick, host, channel, botnet_seen));
++ return 0;
++ }
++#if EGG_IS_MIN_VER(10500)
++ chan = findchan_by_dname(channel);
++ if (chan)
++ dest = chan->name;
++ else
++ dest = channel;
++#else
++ dest = channel;
++#endif
++ set_prefix(SLPUBPREFIX);
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", dest, reply_prefix,
++ do_seen(newsplit(&text), nick, host, channel, botnet_seen));
++ return 0;
++}
++
++static int pub_seenstats(char *nick, char *host, char *hand,
++ char *channel, char *text)
++{
++ char *dest;
++#if EGG_IS_MIN_VER(10500)
++ struct chanset_t *chan;
++#endif
++
++ Context;
++ if (seenflood())
++ return 0;
++ if (nopub(channel))
++ return 0;
++ reset_global_vars();
++ glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, channel));
++ glob_nick = nick;
++ putlog(LOG_CMDS, "*", "<<%s>> !%s! seenstats", nick, hand);
++ if (quietseen(channel)) {
++ set_prefix(SLNOTPREFIX);
++ dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix, do_seenstats());
++ return 0;
++ }
++#if EGG_IS_MIN_VER(10500)
++ chan = findchan_by_dname(channel);
++ if (chan)
++ dest = chan->name;
++ else
++ dest = channel;
++#else
++ dest = channel;
++#endif
++ set_prefix(SLPUBPREFIX);
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", dest, reply_prefix, do_seenstats());
++ return 1;
++}
++
++static int msg_seen(char *nick, char *uhost, struct userrec *u, char *text)
++{
++ Context;
++ if (seenflood())
++ return 0;
++ reset_global_vars();
++ glob_slang = slang_getbynick(coreslangs, nick);
++ glob_nick = nick;
++ putlog(LOG_CMDS, "*", "(%s!%s) !%s! seen %s", nick, uhost, u ? u->handle : "*", text);
++ set_prefix(SLMSGPREFIX);
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", nick, reply_prefix,
++ do_seen(newsplit(&text), nick, uhost, "[/msg]", botnet_seen));
++ return 1;
++}
++
++static int pub_seennick(char *nick, char *host, char *hand,
++ char *channel, char *text)
++{
++ seendat *l;
++ char *dest;
++#if EGG_IS_MIN_VER(10500)
++ struct chanset_t *chan;
++#endif
++
++ Context;
++ if (seenflood())
++ return 0;
++ if (nopub(channel))
++ return 0;
++ putlog(LOG_CMDS, "*", "<<%s>> !%s! seennick %s", nick, hand, text);
++ reset_global_vars();
++ glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, channel));
++ glob_nick = nick;
++#if EGG_IS_MIN_VER(10500)
++ chan = findchan_by_dname(channel);
++ if (chan)
++ dest = chan->name;
++ else
++ dest = channel;
++#else
++ dest = channel;
++#endif
++ text = newsplit(&text);
++ l = findseen(text);
++ if (!l) {
++ glob_query = text;
++ if (quietseen(channel)) {
++ set_prefix(SLNOTPREFIX);
++ dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix, SLNOTSEEN);
++ } else {
++ set_prefix(SLPUBPREFIX);
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", dest, reply_prefix, SLNOTSEEN);
++ }
++ return 0;
++ }
++ if (quietseen(channel)) {
++ set_prefix(SLNOTPREFIX);
++ dprintf(DP_HELP, "NOTICE %s :%s%s\n", nick, reply_prefix, do_seennick(l));
++ } else {
++ set_prefix(SLPUBPREFIX);
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", dest, reply_prefix, do_seennick(l));
++ }
++ return 0;
++}
++
++static int msg_seennick(char *nick, char *uhost, struct userrec *u, char *text)
++{
++ seendat *l;
++
++ Context;
++ if (seenflood())
++ return 0;
++ putlog(LOG_CMDS, "*", "(%s!%s) !%s! seennick %s", nick, uhost, u ? u->handle : "*", text);
++ reset_global_vars();
++ glob_slang = slang_getbynick(coreslangs, nick);
++ glob_nick = nick;
++ set_prefix(SLMSGPREFIX);
++ text = newsplit(&text);
++ l = findseen(text);
++ if (!l) {
++ glob_query = text;
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", nick, reply_prefix, SLNOTSEEN);
++ return 0;
++ }
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s\n", nick, reply_prefix, do_seennick(l));
++ return 0;
++}
++
++static int cmd_seennick(struct userrec *u, int idx, char *text)
++{
++ seendat *l;
++
++ Context;
++ if (seenflood())
++ return 0;
++ putlog(LOG_CMDS, "*", "#%s# seennick %s", dcc[idx].nick, text);
++ reset_global_vars();
++ glob_slang = slang_find(coreslangs, default_slang);
++ glob_nick = dcc[idx].nick;
++ set_prefix(SLMSGPREFIX);
++ text = newsplit(&text);
++ l = findseen(text);
++ if (!l) {
++ glob_query = text;
++ dprintf(idx, "%s%s\n", reply_prefix, SLNOTSEEN);
++ return 0;
++ }
++ dprintf(idx, "%s%s\n", reply_prefix, do_seennick(l));
++ return 0;
++}
++
++static int bot_gseen_req(char *bot, char *code, char *par)
++{
++ char *mask, *nick, *uhost, *chan, *reply;
++ char tosend[256];
++ int i;
++
++ Context;
++ if (seenflood())
++ return 0;
++ i = nextbot(bot);
++ if (i < 0) {
++ debug1("Couldn't answer botnet-seen-request from %s: no such bot", bot);
++ return 0;
++ }
++ mask = newsplit(&par);
++ nick = newsplit(&par);
++ uhost = newsplit(&par);
++ chan = newsplit(&par);
++ reset_global_vars();
++ glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, chan));
++ glob_nick = nick;
++ reply = do_seen(mask, nick, uhost, chan, -1);
++ if (!reply)
++ return 0;
++ if ((strlen(nick) + strlen(chan) + strlen(reply)) < 255) {
++ sprintf(tosend, "gseen_rep %s %s %s", nick, chan, reply);
++ botnet_send_zapf(i, botnetnick, bot, tosend);
++ }
++ return 0;
++}
++
++static int bot_gseen_rep(char *bot, char *code, char *par)
++{
++ char *nick, *chan, *reply;
++ int i;
++
++ Context;
++ if (seenflood())
++ return 0;
++ if (!bnsnick || !bnschan) {
++ if (bnsnick)
++ nfree(bnsnick);
++ if (bnschan)
++ nfree(bnschan);
++ bnsnick = bnschan = NULL;
++ return 0;
++ }
++ nick = newsplit(&par);
++ chan = newsplit(&par);
++ reset_global_vars();
++ glob_remotebot = bot;
++ glob_slang = slang_find(coreslangs, slang_chanlang_get(chanlangs, chan));
++ glob_nick = nick;
++ reply = par;
++ if (strcmp(nick, bnsnick) || strcmp(chan, bnschan))
++ return 0; /* unwanted reply */
++ if (findchan(chan)) {
++ if (nopub(chan)) {
++ nfree(bnsnick);
++ nfree(bnschan);
++ bnsnick = bnschan = NULL;
++ debug1("%s is nopub, bns-reply dropped", chan);
++ return 0;
++ }
++ if (quietseen(chan)) {
++ set_prefix(SLNOTPREFIX);
++ dprintf(DP_HELP, "NOTICE %s :%s%s%s\n", nick, reply_prefix, SLRBOTSAYS, reply);
++ } else {
++ set_prefix(SLPUBPREFIX);
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s%s\n", chan, reply_prefix, SLRBOTSAYS, reply);
++ }
++ } else if (!strcmp(chan, "[/msg]")) {
++ set_prefix(SLMSGPREFIX);
++ dprintf(DP_HELP, "PRIVMSG %s :%s%s%s\n", nick, reply_prefix, SLRBOTSAYS, reply);
++ } else if (!strcmp(chan, "[partyline]")) {
++ for (i = 0; i < dcc_total; i++) {
++ if ((!strcasecmp(nick, dcc[i].nick)) &&
++ (dcc[i].type->flags & DCT_SIMUL)) {
++ set_prefix(SLDCCPREFIX);
++ dprintf(i, "%s%s%s\n", reply_prefix, SLRBOTSAYS, reply);
++ break;
++ }
++ }
++ } else
++ debug1("Couldn't send received bns answer, no such chan %s", chan);
++ nfree(bnsnick);
++ nfree(bnschan);
++ bnsnick = bnschan = NULL;
++ return 0;
++}
++
++static cmd_t mydcc[] =
++{
++ {"seen", "-|-", cmd_seen, NULL},
++ {"seenstats", "-|-", cmd_seenstats, NULL},
++ {"purgeseens", "m", cmd_purgeseens, NULL},
++ {"seennick", "-|-", cmd_seennick, NULL},
++ {0, 0, 0, 0}
++};
++
++static cmd_t seen_pub[] =
++{
++ {"!seen", "", pub_seen, 0},
++ {"!seenstats", "", pub_seenstats, 0},
++ {"!seennick", "", pub_seennick, 0},
++ {0, 0, 0, 0}
++};
++
++static cmd_t seen_msg[] =
++{
++ {"seen", "", msg_seen, 0},
++ {"seennick", "", msg_seennick, 0},
++ {0, 0, 0, 0}
++};
++
++static cmd_t seen_bot[] =
++{
++ {"gseen_req", "", bot_gseen_req, 0},
++ {"gseen_rep", "", bot_gseen_rep, 0},
++ {0, 0, 0, 0}
++};
+diff -Nur src/mod/gseen.mod/misc.c src/mod/gseen.mod/misc.c
+--- src/mod/gseen.mod/misc.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/misc.c 2002-10-26 13:17:57.000000000 +0200
+@@ -0,0 +1,116 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++/* maskstricthost():
++ * basically the same as maskhost() from src/misc.c, but _never_ stripts
++ * "~+-^=" off the host
++ * maskhost() version: * $Id: misc.c,v 1.30 2000/10/27 19:27:32 fabian Exp $
++ */
++static void maskstricthost(const char *s, char *nw)
++{
++ register const char *p, *q, *e, *f;
++ int i;
++
++ *nw++ = '*';
++ *nw++ = '!';
++ p = (q = strchr(s, '!')) ? q + 1 : s;
++ /* Strip of any nick, if a username is found, use last 8 chars */
++ if ((q = strchr(p, '@'))) {
++ int fl = 0;
++
++ if ((q - p) > 9) {
++ nw[0] = '*';
++ p = q - 7;
++ i = 1;
++ } else
++ i = 0;
++ while (*p != '@') {
++ if (!fl && strchr("~+-^=", *p)) {
++// if (strict_host)
++ nw[i] = '?';
++// else
++// i--;
++ } else
++ nw[i] = *p;
++ fl++;
++ p++;
++ i++;
++ }
++ nw[i++] = '@';
++ q++;
++ } else {
++ nw[0] = '*';
++ nw[1] = '@';
++ i = 2;
++ q = s;
++ }
++ nw += i;
++ e = NULL;
++ /* Now q points to the hostname, i point to where to put the mask */
++ if ((!(p = strchr(q, '.')) || !(e = strchr(p + 1, '.'))) && !strchr(q, ':'))
++ /* TLD or 2 part host */
++ strcpy(nw, q);
++ else {
++ if (e == NULL) { /* IPv6 address? */
++ const char *mask_str;
++
++ f = strrchr(q, ':');
++ if (strchr(f, '.')) { /* IPv4 wrapped in an IPv6? */
++ f = strrchr(f, '.');
++ mask_str = ".*";
++ } else /* ... no, true IPv6. */
++ mask_str = ":*";
++ strncpy(nw, q, f - q);
++ /* No need to nw[f-q] = 0 here, as the strcpy below will
++ * terminate the string for us.
++ */
++ nw += (f - q);
++ strcpy(nw, mask_str);
++ } else {
++ for (f = e; *f; f++);
++ f--;
++ if (*f >= '0' && *f <= '9') { /* Numeric IP address */
++ while (*f != '.')
++ f--;
++ strncpy(nw, q, f - q);
++ /* No need to nw[f-q] = 0 here, as the strcpy below will
++ * terminate the string for us.
++ */
++ nw += (f - q);
++ strcpy(nw, ".*");
++ } else { /* Normal host >= 3 parts */
++ /* a.b.c -> *.b.c
++ * a.b.c.d -> *.b.c.d if tld is a country (2 chars)
++ * OR *.c.d if tld is com/edu/etc (3 chars)
++ * a.b.c.d.e -> *.c.d.e etc
++ */
++ const char *x = strchr(e + 1, '.');
++
++ if (!x)
++ x = p;
++ else if (strchr(x + 1, '.'))
++ x = e;
++ else if (strlen(x) == 3)
++ x = p;
++ else
++ x = e;
++ sprintf(nw, "*%s", x);
++ }
++ }
++ }
++}
+diff -Nur src/mod/gseen.mod/seenlang.h src/mod/gseen.mod/seenlang.h
+--- src/mod/gseen.mod/seenlang.h 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/seenlang.h 2002-10-26 13:17:58.000000000 +0200
+@@ -0,0 +1,61 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++#define SLPUBPREFIX getslang(10)
++#define SLNOTPREFIX getslang(11)
++#define SLMSGPREFIX getslang(12)
++#define SLDCCPREFIX getslang(13)
++
++#define SLNOPARAM getslang(54)
++#define SLMIRROR getslang(55)
++#define SLONCHAN getslang(56)
++#define SLNOWILDCARDS getslang(57)
++#define SLTOOLONGNICK getslang(58)
++
++#define SLNOTSEEN getslang(65)
++#define SLPOORSEEN getslang(66)
++#define SLNOMATCH getslang(67)
++#define SLTOOMANYMATCHES getslang(68)
++
++#define SLHANDONCHAN getslang(73)
++#define SLONOTHERCHAN getslang(74)
++#define SLONEMATCH getslang(75)
++#define SLLITTLEMATCHES getslang(76)
++#define SLMANYMATCHES getslang(77)
++
++#define SLRBOTSAYS getslang(85)
++
++#define SLYEAR getdur(0)
++#define SLYEARS getdur(1)
++#define SLWEEK getdur(2)
++#define SLWEEKS getdur(3)
++#define SLDAY getdur(4)
++#define SLDAYS getdur(5)
++#define SLHOUR getdur(6)
++#define SLHOURS getdur(7)
++#define SLMINUTE getdur(8)
++#define SLMINUTES getdur(9)
++#define SLSECOND getdur(10)
++#define SLSECONDS getdur(11)
++#define SLSOMETIME getdur(12)
++
++#define SLONELOOK getslang(170)
++#define SLMORELOOKS getslang(171)
++#define SLLASTLOOK getslang(172)
++
++#define SLSEENSTATS getslang(180)
+diff -Nur src/mod/gseen.mod/seentree.c src/mod/gseen.mod/seentree.c
+--- src/mod/gseen.mod/seentree.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/seentree.c 2002-10-26 13:18:10.000000000 +0200
+@@ -0,0 +1,213 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++static struct generic_binary_tree seentree;
++
++static void seentree_init();
++static int seentree_expmem();
++static void seentree_free();
++static int compareseens(void *, void *);
++static int expmemseen(void *);
++static void add_seen(int, char *, char *, char *, char *,
++ time_t, int);
++static void freeseen(void *);
++static seendat *findseen(char *);
++static void wildmatch_seens(char *, char *, int);
++static void process_wildmatch_seens(void *);
++static void write_seen_tree(void *);
++static void purge_seen_tree(void *);
++static int count_seens();
++static void _count_seens(void *);
++
++
++static void seentree_init()
++{
++ seentree.root = NULL;
++ seentree.comparedata = compareseens;
++ seentree.expmemdata = expmemseen;
++ seentree.freedata = freeseen;
++}
++
++static int seentree_expmem()
++{
++ return btree_expmem(&seentree);
++}
++
++static void seentree_free()
++{
++ btree_freetree(&seentree);
++ seentree.root = NULL;
++}
++
++static int compareseens(void *first, void *second)
++{
++ return rfc_casecmp(((seendat *) first)->nick, ((seendat *) second)->nick);
++}
++
++// add another entry to the tree
++static void add_seen(int type, char *nick, char *host, char *chan, char *msg,
++ time_t when, int spent)
++{
++ seendat *newseen;
++
++ newseen = nmalloc(sizeof(seendat));
++ newseen->type = type;
++ newseen->nick = nmalloc(strlen(nick) + 1);
++ strcpy(newseen->nick, nick);
++ newseen->host = nmalloc(strlen(host) + 1);
++ strcpy(newseen->host, host);
++ newseen->chan = nmalloc(strlen(chan) + 1);
++ strcpy(newseen->chan, chan);
++ newseen->msg = nmalloc(strlen(msg) + 1);
++ strcpy(newseen->msg, msg);
++ newseen->when = when;
++ newseen->spent = spent;
++ btree_add(&seentree, newseen);
++}
++
++static void freeseen(void *what)
++{
++ seendat *s = (seendat *) what;
++
++ Assert(s);
++ Assert(s->nick);
++ Assert(s->host);
++ Assert(s->chan);
++ Assert(s->msg);
++
++ nfree(s->nick);
++ nfree(s->host);
++ nfree(s->chan);
++ nfree(s->msg);
++ nfree(s);
++}
++
++static int expmemseen(void *what)
++{
++ int size = 0;
++ seendat *d = (seendat *) what;
++
++ size += sizeof(seendat);
++ size += strlen(d->nick) + 1;
++ size += strlen(d->host) + 1;
++ size += strlen(d->chan) + 1;
++ size += strlen(d->msg) + 1;
++ return size;
++}
++
++// finds a seen entry in the tree
++seendat findseen_temp;
++static seendat *findseen(char *nick)
++{
++ findseen_temp.nick = nick;
++ return btree_get(&seentree, &findseen_temp);
++}
++
++// function to find all nicks that match a host
++// (calls btree_getall() which calls a target function for each item)
++// host: user's hostmask (used if search query doesn't contain any wildcards)
++// mask: search mask
++// wild: defines if we want to use the mask, or host for the search
++static char *wildmatch_host, *wildmatch_mask;
++int wildmatch_wild;
++static void wildmatch_seens(char *host, char *mask, int wild)
++{
++ wildmatch_host = host;
++ wildmatch_mask = mask;
++ wildmatch_wild = wild;
++ btree_getall(&seentree, process_wildmatch_seens);
++}
++
++/* process_wildmatch_seens():
++ * gets called from the binary tree for each existing item.
++ */
++static void process_wildmatch_seens(void *data)
++{
++ seendat *s = (seendat *) data;
++
++ if ((numresults > max_matches) && (max_matches > 0)) // Don't return too many
++ return; // matches...
++ if (!wildmatch_wild) {
++ if (wild_match(wildmatch_host, s->host))
++ add_seenresult(s);
++ } else {
++ temp_wildmatch_host = my_realloc(temp_wildmatch_host, strlen(s->nick) + 1 + strlen(s->host) + 1);
++ strcpy(temp_wildmatch_host, s->nick);
++ strcat(temp_wildmatch_host, "!");
++ strcat(temp_wildmatch_host, s->host);
++ if (wild_match(wildmatch_mask, s->nick) || wild_match(wildmatch_mask, temp_wildmatch_host))
++ add_seenresult(s);
++ }
++}
++
++// write seendata in the datafile
++FILE *write_seen_tree_target;
++static void write_seen_tree(void *data)
++{
++ seendat *node = (seendat *) data;
++
++ /* format: "! nick host chan type when spent msg" */
++ fprintf(write_seen_tree_target, "! %s %s %s %d %lu %d %s\n", node->nick,
++ node->host, node->chan, node->type, node->when, node->spent,
++ node->msg);
++}
++
++// recursive function to remove old data
++// QUESTION: What happens if one of the nodes get moved by killseen()?
++// Possible bug/crash?
++// I think it should not be a problem. When killseen() is called the
++// first time, recursion already reached its end and no pointers
++// are accessed anymore. But I'm not sure... maybe I'm wrong.
++static void purge_seen_tree(void *data)
++{
++ seendat *node = (seendat *) data;
++
++ if ((now - node->when) > (expire_seens * 86400)) {
++ debug1("seen data for %s has expired.", node->nick);
++ btree_remove(&seentree, node);
++ }
++}
++
++// counts the number of nicks in the database
++static int count_seens_temp;
++static int count_seens()
++{
++ count_seens_temp = 0;
++ btree_getall(&seentree, _count_seens);
++ return count_seens_temp;
++}
++
++static void _count_seens(void *node)
++{
++ count_seens_temp++;
++}
++
++static int tcl_killseen STDVAR
++{
++ Context;
++ BADARGS(2, 2, " nick");
++ findseen_temp.nick = argv[1];
++ btree_remove(&seentree, &findseen_temp);
++ return TCL_OK;
++}
++
++static tcl_cmds seendebugtcls[] =
++{
++ {"killseen", tcl_killseen},
++ {0, 0}
++};
+diff -Nur src/mod/gseen.mod/sensors.c src/mod/gseen.mod/sensors.c
+--- src/mod/gseen.mod/sensors.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/sensors.c 2002-10-26 13:18:00.000000000 +0200
+@@ -0,0 +1,273 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++static int get_spent(char *nick, char *chan)
++{
++ struct chanset_t *ch = NULL;
++ memberlist *m = NULL;
++
++ int spent;
++ ch = findchan_by_dname(chan);
++ if (ch)
++ m = ismember(ch, nick);
++ if (m && m->joined)
++ spent = now - m->joined;
++ else
++ spent = -1;
++ return spent;
++}
++
++static int secretchan(char *chan)
++{
++ struct chanset_t *ch;
++
++ ch = findchan_by_dname(chan);
++ if (!ch)
++ return 0;
++ if (ch->status & CHAN_SECRET)
++ return 1;
++ return 0;
++}
++
++static int nolog(char *chan)
++{
++ char buf[121], *b;
++
++ Context;
++ strncpy(buf, no_log, 120);
++ buf[120] = 0;
++ b = buf;
++ while (b[0])
++ if (!strcasecmp(chan, newsplit(&b)))
++ return 1;
++#if EGG_IS_MIN_VER(10503)
++ if (ngetudef("noseendata", chan))
++ return 1;
++#endif
++ return 0;
++}
++
++static int gseen_join(char *nick, char *uhost, char *hand, char *chan)
++{
++ char buf[10] = "[secret]";
++
++ Context;
++ if (nolog(chan))
++ return 0;
++ if (use_handles && (hand[0] != '*'))
++ nick = hand;
++ if (secretchan(chan))
++ chan = buf;
++ add_seen(SEEN_JOIN, nick, uhost, chan, "", now, get_spent(nick, chan));
++ report_seenreq(chan, nick);
++ if ((hand[0] == '*') && strcasecmp(nick, hand))
++ report_seenreq(chan, hand);
++ return 0;
++}
++
++static int gseen_kick(char *nick, char *uhost, char *hand, char *chan,
++ char *victim, char *reason)
++{
++ struct chanset_t *ch = NULL;
++ memberlist *m = NULL;
++ char msg[1024], *s;
++ char buf[10] = "[secret]";
++
++ Context;
++ if (nolog(chan))
++ return 0;
++ if (use_handles && (hand[0] != '*'))
++ nick = hand;
++ ch = findchan_by_dname(chan);
++ if (!ch) {
++ debug2("Unable to seen %s getting kicked from %s", victim, chan);
++ return 0;
++ }
++ if (secretchan(chan))
++ chan = buf;
++ s = msg;
++ s[0] = 0;
++ m = ismember(ch, victim);
++ if (!m) {
++ debug2("Unable to seen %s getting kicked from %s", victim, chan);
++ return 0;
++ }
++ if ((strlen(nick) + strlen(reason) + 2) < 1024)
++ sprintf(s, "%s %s", nick, reason);
++ add_seen(SEEN_KICK, victim, m->userhost, chan, s, now,
++ get_spent(victim, chan));
++ return 0;
++}
++
++static int gseen_nick(char *nick, char *uhost, char *hand, char *chan,
++ char *newnick)
++{
++ char buf[10] = "[secret]";
++
++ Context;
++ if (nolog(chan))
++ return 0;
++ if (use_handles && (hand[0] != '*'))
++ nick = hand;
++ if (secretchan(chan))
++ chan = buf;
++ add_seen(SEEN_NICK, nick, uhost, chan, newnick, now, get_spent(nick, chan));
++ if (!(use_handles && (hand[0] != '*')))
++ add_seen(SEEN_NCKF, newnick, uhost, chan, nick, now, get_spent(nick, chan));
++ report_seenreq(chan, newnick);
++ if ((hand[0] != '*') && strcasecmp(newnick, hand))
++ report_seenreq(chan, hand);
++ return 0;
++}
++
++#if EGG_IS_MIN_VER(10502)
++static int gseen_part(char *nick, char *uhost, char *hand, char *chan,
++ char *reason)
++{
++ char buf[10] = "[secret]";
++
++ Context;
++ if (nolog(chan))
++ return 0;
++ if (use_handles && (hand[0] != '*'))
++ nick = hand;
++ if (secretchan(chan))
++ chan = buf;
++ add_seen(SEEN_PART, nick, uhost, chan, reason, now, get_spent(nick, chan));
++ return 0;
++}
++#else
++static int gseen_part(char *nick, char *uhost, char *hand, char *chan)
++{
++ char buf[10] = "[secret]";
++
++ Context;
++ if (nolog(chan))
++ return 0;
++ if (use_handles && (hand[0] != '*'))
++ nick = hand;
++ if (secretchan(chan))
++ chan = buf;
++ add_seen(SEEN_PART, nick, uhost, chan, "", now, get_spent(nick, chan));
++ return 0;
++}
++#endif
++
++static int gseen_sign(char *nick, char *uhost, char *hand, char *chan,
++ char *reason)
++{
++ char buf[10] = "[secret]";
++
++ Context;
++ if (nolog(chan))
++ return 0;
++ if (use_handles && (hand[0] != '*'))
++ nick = hand;
++ if (secretchan(chan))
++ chan = buf;
++ add_seen(SEEN_SIGN, nick, uhost, chan, reason, now, get_spent(nick, chan));
++ return 0;
++}
++
++static int gseen_splt(char *nick, char *uhost, char *hand, char *chan)
++{
++ char buf[10] = "[secret]";
++
++ Context;
++ if (nolog(chan))
++ return 0;
++ if (use_handles && (hand[0] != '*'))
++ nick = hand;
++ if (secretchan(chan))
++ chan = buf;
++ add_seen(SEEN_SPLT, nick, uhost, chan, "", now, get_spent(nick, chan));
++ return 0;
++}
++
++static int gseen_rejn(char *nick, char *uhost, char *hand, char *chan)
++{
++ char buf[10] = "[secret]";
++
++ Context;
++ if (nolog(chan))
++ return 0;
++ if (use_handles && (hand[0] != '*'))
++ nick = hand;
++ if (secretchan(chan))
++ chan = buf;
++ add_seen(SEEN_REJN, nick, uhost, chan, "", now, get_spent(nick, chan));
++ return 0;
++}
++
++static int gseen_chjn STDVAR
++{
++ Context;
++ BADARGS(7, 7, " bot hand chan flag idx host");
++ add_seen(SEEN_CHJN, argv[2], argv[6], argv[3], argv[1], now, -1);
++ return 0;
++}
++
++static int gseen_chpt STDVAR
++{
++ Context;
++ BADARGS(5, 5, " bot hand idx chan");
++ add_seen(SEEN_CHPT, argv[2], "unknown", argv[4], argv[1], now, -1);
++ return 0;
++}
++
++static cmd_t seen_kick[] =
++{
++ {"*", "", (Function) gseen_kick, "gseen"},
++ {0, 0, 0, 0}
++};
++
++static cmd_t seen_nick[] =
++{
++ {"*", "", (Function) gseen_nick, "gseen"},
++ {0, 0, 0, 0}
++};
++
++static cmd_t seen_join[] =
++{
++ {"*", "", (Function) gseen_join, "gseen"},
++ {0, 0, 0, 0}
++};
++
++static cmd_t seen_part[] =
++{
++ {"*", "", (Function) gseen_part, "gseen"},
++ {0, 0, 0, 0}
++};
++
++static cmd_t seen_sign[] =
++{
++ {"*", "", (Function) gseen_sign, "gseen"},
++ {0, 0, 0, 0}
++};
++
++static cmd_t seen_splt[] =
++{
++ {"*", "", (Function) gseen_splt, "gseen"},
++ {0, 0, 0, 0}
++};
++
++static cmd_t seen_rejn[] =
++{
++ {"*", "", (Function) gseen_rejn, "gseen"},
++ {0, 0, 0, 0}
++};
+diff -Nur src/mod/gseen.mod/slang.c src/mod/gseen.mod/slang.c
+--- src/mod/gseen.mod/slang.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/slang.c 2002-10-26 13:18:03.000000000 +0200
+@@ -0,0 +1,309 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++static struct slang_header *slang_find(struct slang_header *, char *);
++
++#include "slang_text.c"
++#include "slang_multitext.c"
++#include "slang_ids.c"
++#ifndef SLANG_NOTYPES
++#include "slang_types.c"
++#endif
++#include "slang_duration.c"
++#ifndef SLANG_NOFACTS
++#include "slang_facts_places.c"
++#include "slang_facts.c"
++#endif
++#include "slang_chanlang.c"
++
++
++struct slang_header {
++ struct slang_header *next;
++ char *lang;
++ char *desc;
++ struct slang_id *ids;
++#ifndef SLANG_NOTYPES
++ struct slang_type *types;
++#endif
++ struct slang_duration *durations;
++};
++
++static void slang_glob_init()
++{
++ glob_slang_cmd_list = NULL;
++}
++
++static int slang_glob_expmem()
++{
++ return slang_commands_list_expmem(glob_slang_cmd_list);
++}
++
++static void slang_glob_free()
++{
++ slang_commands_list_free(glob_slang_cmd_list);
++ glob_slang_cmd_list = NULL;
++}
++
++static struct slang_header *slang_create(struct slang_header *list, char *lang, char *desc)
++{
++ struct slang_header *nslang, *l;
++
++ Assert(lang);
++ debug2("Creating language '%s' starting by %d", lang, (int) list);
++ for (nslang = list; nslang; nslang = nslang->next)
++ if (!strcasecmp(nslang->lang, lang))
++ return list;
++ nslang = nmalloc(sizeof(struct slang_header));
++ nslang->next = NULL;
++ nslang->desc = NULL;
++ nslang->lang = nmalloc(strlen(lang) + 1);
++ strcpy(nslang->lang, lang);
++ nslang->desc = nmalloc(strlen(desc) + 1);
++ strcpy(nslang->desc, desc);
++ nslang->ids = NULL;
++#ifndef SLANG_NOTYPES
++ nslang->types = NULL;
++#endif
++ nslang->durations = NULL;
++ for (l = list; l && l->next; l = l->next);
++ if (l)
++ l->next = nslang;
++ else {
++ Assert(!list);
++ list = nslang;
++ }
++ return list;
++}
++
++static int slang_expmem(struct slang_header *what)
++{
++ int size = 0;
++
++ while (what) {
++ size += sizeof(struct slang_header);
++ size += strlen(what->lang) + 1;
++ size += strlen(what->desc) + 1;
++ size += slang_id_expmem(what->ids);
++#ifndef SLANG_NOTYPES
++ size += slang_type_expmem(what->types);
++#endif
++ size += slang_duration_expmem(what->durations);
++ what = what->next;
++ }
++ return size;
++}
++
++static void slang_free(struct slang_header *what)
++{
++ struct slang_header *next;
++
++ while (what) {
++ next = what->next;
++ slang_id_free(what->ids);
++#ifndef SLANG_NOTYPES
++ slang_type_free(what->types);
++#endif
++ slang_duration_free(what->durations);
++ nfree(what->lang);
++ nfree(what->desc);
++ nfree(what);
++ what = next;
++ }
++}
++
++static int slang_load(struct slang_header *slang, char *filename)
++{
++ FILE *f;
++ char *buffer, *s;
++ char *cmd, *sid, *strtol_ret;
++#ifndef SLANG_NOTYPES
++ char *type;
++#endif
++ int line, id;
++
++ Assert(slang);
++ putlog(LOG_MISC, "*", "Loading language \"%s\" from %s...", slang->lang, filename);
++ f = fopen(filename, "r");
++ if (!f) {
++ putlog(LOG_MISC, "*", "Couldn't open slangfile \"%s\"!", filename);
++ return 0;
++ }
++ buffer = nmalloc(2000);
++ line = 0;
++ while (!feof(f)) {
++ s = buffer;
++ if (fgets(s, 2000, f)) {
++ line++;
++ // at first, kill those stupid line feeds and carriage returns...
++ if (s[strlen(s) - 1] == '\n')
++ s[strlen(s) - 1] = 0;
++ if (s[strlen(s) - 1] == '\r')
++ s[strlen(s) - 1] = 0;
++ if (!s[0])
++ continue;
++ cmd = newsplit(&s);
++
++ if (!strcasecmp(cmd, "T")) {
++#ifndef SLANG_NOTYPES
++ type = newsplit(&s);
++ slang->types = slang_type_add(slang->types, type, s);
++#endif
++ } else if (!strcasecmp(cmd, "D")) {
++ sid = newsplit(&s);
++ id = strtol(sid, &strtol_ret, 10);
++ if (strtol_ret == sid) {
++ putlog(LOG_MISC, "*", "ERROR in slangfile \"%s\", line %d: %s is not a valid "
++ "duration index!", filename, line, sid);
++ continue;
++ }
++ slang->durations = slang_duration_add(slang->durations, id, s);
++ } else {
++ id = strtol(cmd, &strtol_ret, 10);
++ if (strtol_ret == cmd)
++ continue;
++ slang->ids = slang_id_add(slang->ids, id, s);
++ }
++ }
++ }
++ fclose(f);
++ nfree(buffer);
++ return 1;
++}
++
++static struct slang_header *slang_find(struct slang_header *where, char *language)
++{
++ struct slang_header *slang = NULL;
++
++ // at first, search for the specified language
++ for (slang = where; slang; slang = slang->next)
++ if (!strcasecmp(slang->lang, language))
++ return slang;
++ // oops... language seems to be invalid. Let's find the default.
++ Assert(default_slang);
++ for (slang = where; slang; slang = slang->next)
++ if (!strcasecmp(slang->lang, default_slang))
++ return slang;
++ // default_slang wasn't found either? *sigh*
++ // Let's return the first known language then.
++ return where;
++}
++
++#ifndef SLANG_NOVALIDATE
++/* slang_valid():
++ * check if the given language is a valid one
++ */
++static int slang_valid(struct slang_header *where, char *language)
++{
++ struct slang_header *slang = NULL;
++
++ for (slang = where; slang; slang = slang->next)
++ if (!strcasecmp(slang->lang, language))
++ return 1;
++ return 0;
++}
++#endif
++
++static char getslang_error[12];
++static char *getslang(int id)
++{
++ char *text;
++
++ if (!glob_slang) {
++ putlog(LOG_MISC, "*", "WARNING! No language selected! (getslang())");
++ return "NOLANG";
++ }
++ text = slang_id_get(glob_slang->ids, id);
++ if (!text) {
++ snprintf(getslang_error, sizeof(getslang_error), "SLANG%d", id);
++ return getslang_error;
++ }
++ return text;
++}
++
++static char *getdur(int idx)
++{
++ char *text;
++
++ Assert((idx >= 0) && (idx < DURATIONS));
++ if (!glob_slang) {
++ putlog(LOG_MISC, "*", "WARNING! No language selected! (getdur())");
++ return "NOLANG";
++ }
++ text = slang_duration_get(glob_slang->durations, idx);
++ if (!text) {
++ snprintf(getslang_error, sizeof(getslang_error), "DUR%d", idx);
++ return getslang_error;
++ }
++ return text;
++}
++
++#ifndef SLANG_NOTYPES
++static char *getslangtype(char *type)
++{
++ char *stype;
++
++ if (!glob_slang) {
++ putlog(LOG_MISC, "*", "WARNING! No language selected! (getslangtype())");
++ return "NOLANG";
++ }
++ stype = slang_type_get(glob_slang->types, type);
++ if (stype)
++ return stype;
++ else
++ return type;
++}
++
++static int slangtypetoi(char *slangtype)
++{
++ char *type;
++
++ if (!glob_slang) {
++ putlog(LOG_MISC, "*", "WARNING! No language selected! (slangtypetoi())");
++ return T_ERROR;
++ }
++ type = slang_type_slang2type(glob_slang->types, slangtype);
++ if (type) {
++ debug1("type: %s", type);
++ return typetoi(type);
++ } else
++ return typetoi(slangtype);
++}
++#endif
++
++#ifndef SLANG_NOGETALL
++static char *getslang_first(int id)
++{
++ char *text;
++
++ if (!glob_slang) {
++ putlog(LOG_MISC, "*", "WARNING! No language selected! (getslang())");
++ return "NOLANG";
++ }
++ text = slang_id_get_first(glob_slang->ids, id);
++ if (!text) {
++ snprintf(getslang_error, sizeof(getslang_error), "SLANG%d", id);
++ return getslang_error;
++ }
++ return text;
++}
++
++static char *getslang_next()
++{
++ return slang_id_get_next();
++}
++#endif
+diff -Nur src/mod/gseen.mod/slang_chanlang.c src/mod/gseen.mod/slang_chanlang.c
+--- src/mod/gseen.mod/slang_chanlang.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/slang_chanlang.c 2002-10-26 13:18:02.000000000 +0200
+@@ -0,0 +1,113 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++struct slang_chanlang {
++ struct slang_chanlang *next;
++ char *chan;
++ char *lang;
++};
++
++static struct slang_chanlang *chanlangs = NULL;
++
++static struct slang_chanlang *slang_chanlang_add(struct slang_chanlang *, char *, char *);
++static int slang_chanlang_expmem(struct slang_chanlang *);
++static void slang_chanlang_free(struct slang_chanlang *);
++static char *slang_chanlang_get(struct slang_chanlang *, char *);
++
++static struct slang_chanlang *slang_chanlang_add(struct slang_chanlang *where, char *chan, char *lang)
++{
++ struct slang_chanlang *item;
++
++ for (item = where; item; item = item->next)
++ if (!rfc_casecmp(item->chan, chan))
++ break;
++ if (!item) {
++ item = nmalloc(sizeof(struct slang_chanlang));
++ item->chan = nmalloc(strlen(chan) + 1);
++ strcpy(item->chan, chan);
++ item->lang = nmalloc(strlen(lang) + 1);
++ strcpy(item->lang, lang);
++ item->next = where;
++ where = item;
++ } else {
++ Assert(item->lang);
++ item->lang = nrealloc(item->lang, strlen(lang) + 1);
++ strcpy(item->lang, lang);
++ }
++ return where;
++}
++
++static int slang_chanlang_expmem(struct slang_chanlang *what)
++{
++ int size = 0;
++
++ while (what) {
++ Assert(what);
++ Assert(what->chan);
++ Assert(what->lang);
++ size += sizeof(struct slang_chanlang);
++ size += strlen(what->chan) + 1;
++ size += strlen(what->lang) + 1;
++ what = what->next;
++ }
++ return size;
++}
++
++static void slang_chanlang_free(struct slang_chanlang *what)
++{
++ struct slang_chanlang *next;
++
++ while (what) {
++ Assert(what);
++ Assert(what->chan);
++ Assert(what->lang);
++ next = what->next;
++ nfree(what->chan);
++ nfree(what->lang);
++ nfree(what);
++ what = next;
++ }
++}
++
++static char *slang_chanlang_get(struct slang_chanlang *where, char *chan)
++{
++ while (where) {
++ if (!rfc_casecmp(where->chan, chan))
++ return where->lang;
++ where = where->next;
++ }
++ return default_slang;
++}
++
++/* slang_getbynick():
++ * tries to find an appropriate language for nick by searching
++ * him on a channel and using the language of this channel.
++ */
++static struct slang_header *slang_getbynick(struct slang_header *where, char *nick)
++{
++ struct chanset_t *chan;
++
++ for (chan = chanset; chan; chan = chan->next)
++ if (ismember(chan, nick))
++#if EGG_IS_MIN_VER(10500)
++ return slang_find(where, slang_chanlang_get(chanlangs, chan->dname));
++#else
++ return slang_find(where, slang_chanlang_get(chanlangs, chan->name));
++#endif
++ return slang_find(where, default_slang);
++}
+diff -Nur src/mod/gseen.mod/slang_duration.c src/mod/gseen.mod/slang_duration.c
+--- src/mod/gseen.mod/slang_duration.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/slang_duration.c 2002-10-26 13:18:01.000000000 +0200
+@@ -0,0 +1,82 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++#define DURATIONS 13
++
++struct slang_duration {
++ char *durs[DURATIONS];
++};
++
++static struct slang_duration *slang_duration_add(struct slang_duration *where, int idx, char *text)
++{
++ int i;
++
++ if ((idx < 0) || (idx >= DURATIONS)) {
++ putlog(LOG_MISC, "*", "Warning: Invalid duration index \"%d\".", idx);
++ return where;
++ }
++ debug2("Adding duration[%d]: %s", idx, text);
++ if (!where) {
++ where = nmalloc(sizeof(struct slang_duration));
++ for (i = 0; i < DURATIONS; i++)
++ where->durs[i] = NULL;
++ }
++ if (where->durs[idx])
++ nfree(where->durs[idx]);
++ where->durs[idx] = nmalloc(strlen(text) + 1);
++ strcpy(where->durs[idx], text);
++ return where;
++}
++
++static int slang_duration_expmem(struct slang_duration *what)
++{
++ int i, size = 0;
++
++ if (!what)
++ return 0;
++ size += sizeof(struct slang_duration);
++ for (i = 0; i < DURATIONS; i++)
++ if (what->durs[i])
++ size += strlen(what->durs[i]) + 1;
++ return size;
++}
++
++static void slang_duration_free(struct slang_duration *what)
++{
++ int i;
++
++ if (what) {
++ for (i = 0; i < DURATIONS; i++)
++ if (what->durs[i])
++ nfree(what->durs[i]);
++ nfree(what);
++ }
++}
++
++static char *slang_duration_get(struct slang_duration *where, int idx)
++{
++ if (!where) {
++ debug0("no where");
++ return NULL;
++ }
++ if ((idx < 0) || (idx >= DURATIONS)) {
++ debug1("invalid duration index: %d", idx);
++ return NULL;
++ }
++ return where->durs[idx];
++}
+diff -Nur src/mod/gseen.mod/slang_gseen_commands.c src/mod/gseen.mod/slang_gseen_commands.c
+--- src/mod/gseen.mod/slang_gseen_commands.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/slang_gseen_commands.c 2002-10-26 13:18:06.000000000 +0200
+@@ -0,0 +1,235 @@
++static void slang_send_botnick()
++{
++ strncat(slang_text_buf, botname, sizeof(slang_text_buf));
++}
++
++static void slang_send_query()
++{
++ if (glob_query)
++ strncat(slang_text_buf, glob_query, sizeof(slang_text_buf));
++}
++
++static void slang_send_laston()
++{
++ if (glob_laston)
++ strncat(slang_text_buf, glob_laston, sizeof(slang_text_buf));
++}
++
++static void slang_send_otherchan()
++{
++ if (glob_otherchan)
++ strncat(slang_text_buf, glob_otherchan, sizeof(slang_text_buf));
++}
++
++static void slang_send_othernick()
++{
++ if (glob_othernick)
++ strncat(slang_text_buf, glob_othernick, sizeof(slang_text_buf));
++}
++
++static void slang_send_remotebot()
++{
++ if (glob_remotebot)
++ strncat(slang_text_buf, glob_remotebot, sizeof(slang_text_buf));
++}
++
++static void slang_send_snick()
++{
++ if (glob_seendat)
++ strncat(slang_text_buf, glob_seendat->nick, sizeof(slang_text_buf));
++}
++
++static void slang_send_shost()
++{
++ if (glob_seendat)
++ strncat(slang_text_buf, glob_seendat->host, sizeof(slang_text_buf));
++}
++
++static void slang_send_schan()
++{
++ if (glob_seendat)
++ strncat(slang_text_buf, glob_seendat->chan, sizeof(slang_text_buf));
++}
++
++static void slang_send_swhen()
++{
++ char *dur;
++
++ if (glob_seendat) {
++ dur = gseen_duration(now - glob_seendat->when);
++ strncat(slang_text_buf, dur, sizeof(slang_text_buf));
++ }
++}
++
++static void slang_send_stime()
++{
++ time_t tt;
++ char t[20];
++
++ if (glob_seendat) {
++ tt = glob_seendat->when;
++ strftime(t, 19, "%d.%m. %H:%M", localtime(&tt));
++ strncat(slang_text_buf, t, sizeof(slang_text_buf));
++ }
++}
++
++static void slang_send_spent()
++{
++ char *dur;
++
++ if (glob_seendat) {
++ dur = gseen_duration(glob_seendat->spent);
++ strncat(slang_text_buf, dur, sizeof(slang_text_buf));
++ }
++}
++
++static void slang_send_smsg()
++{
++ if (glob_seendat)
++ strncat(slang_text_buf, glob_seendat->msg, sizeof(slang_text_buf));
++}
++
++static void slang_send_numresults()
++{
++ char buf[7];
++
++ snprintf(buf, sizeof(buf), "%d", numresults);
++ strncat(slang_text_buf, buf, sizeof(slang_text_buf));
++}
++
++static void slang_send_punisher()
++{
++ char *reason;
++ int len;
++
++ if (glob_seendat) {
++ reason = strchr(glob_seendat->msg, ' ');
++ if (!reason)
++ strncat(slang_text_buf, glob_seendat->msg, sizeof(slang_text_buf));
++ else {
++ len = (int) reason - (int) glob_seendat->msg;
++ strncat(slang_text_buf, glob_seendat->msg, (sizeof(slang_text_buf) < len) ? sizeof(slang_text_buf) : len);
++ }
++ }
++}
++
++static void slang_send_kickreason()
++{
++ char *reason;
++
++ if (glob_seendat) {
++ reason = strchr(glob_seendat->msg, ' ');
++ if (reason)
++ strncat(slang_text_buf, reason, sizeof(slang_text_buf));
++ }
++}
++
++static void slang_send_rnick()
++{
++ if (glob_seenrequest) {
++ Assert(glob_seenrequest->by);
++ Assert(glob_seenrequest->by->who);
++ strncat(slang_text_buf, glob_seenrequest->by->who, sizeof(slang_text_buf));
++ }
++}
++
++static void slang_send_rchan()
++{
++ if (glob_seenrequest) {
++ Assert(glob_seenrequest->by);
++ Assert(glob_seenrequest->by->chan);
++ strncat(slang_text_buf, glob_seenrequest->by->chan, sizeof(slang_text_buf));
++ }
++}
++
++static void slang_send_rhost()
++{
++ if (glob_seenrequest) {
++ Assert(glob_seenrequest->by);
++ Assert(glob_seenrequest->by->host);
++ strncat(slang_text_buf, glob_seenrequest->by->host, sizeof(slang_text_buf));
++ }
++}
++
++static void slang_send_rtime()
++{
++ time_t tt;
++ char t[20];
++
++ if (glob_seenrequest) {
++ Assert(glob_seenrequest->by);
++ tt = glob_seenrequest->by->when;
++ strftime(t, sizeof(t), "%d.%m. %H:%M", localtime(&tt));
++ strncat(slang_text_buf, t, sizeof(slang_text_buf));
++ }
++}
++
++static void slang_send_rwhen()
++{
++ if (glob_seenrequest) {
++ Assert(glob_seenrequest->by);
++ strncat(slang_text_buf, gseen_duration(now - glob_seenrequest->by->when), sizeof(slang_text_buf));
++ }
++}
++
++static void slang_send_requests()
++{
++ char buf[7];
++
++ snprintf(buf, sizeof(buf), "%d", glob_seenrequests);
++ strncat(slang_text_buf, buf, sizeof(slang_text_buf));
++}
++
++static void slang_send_totalnicks()
++{
++ char buf[7];
++
++ snprintf(buf, sizeof(buf), "%d", glob_totalnicks);
++ strncat(slang_text_buf, buf, sizeof(slang_text_buf));
++}
++
++static void slang_send_totalbytes()
++{
++ char buf[20];
++
++ snprintf(buf, sizeof(buf), "%d", glob_totalbytes);
++ strncat(slang_text_buf, buf, sizeof(slang_text_buf));
++}
++
++static void slang_send_nick()
++{
++ if (glob_nick)
++ strncat(slang_text_buf, glob_nick, sizeof(slang_text_buf));
++}
++
++struct slang_text_commands slang_text_gseen_command_table[] =
++{
++ {"botnick", slang_send_botnick},
++ {"query", slang_send_query},
++ {"laston", slang_send_laston},
++ {"otherchan", slang_send_otherchan},
++ {"othernick", slang_send_othernick},
++ {"remotebot", slang_send_remotebot},
++ {"snick", slang_send_snick},
++ {"swhen", slang_send_swhen},
++ {"stime", slang_send_stime},
++ {"shost", slang_send_shost},
++ {"schan", slang_send_schan},
++ {"spent", slang_send_spent},
++ {"smsg", slang_send_smsg},
++ {"numresults", slang_send_numresults},
++ {"snick2", slang_send_smsg},
++ {"bnbot", slang_send_smsg},
++ {"punisher", slang_send_punisher},
++ {"kickreason", slang_send_kickreason},
++ {"rnick", slang_send_rnick},
++ {"rchan", slang_send_rchan},
++ {"rhost", slang_send_rhost},
++ {"rtime", slang_send_rtime},
++ {"rwhen", slang_send_rwhen},
++ {"requests", slang_send_requests},
++ {"totalnicks", slang_send_totalnicks},
++ {"totalbytes", slang_send_totalbytes},
++ {"nick", slang_send_nick},
++ {0, 0}
++};
+diff -Nur src/mod/gseen.mod/slang_ids.c src/mod/gseen.mod/slang_ids.c
+--- src/mod/gseen.mod/slang_ids.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/slang_ids.c 2002-10-26 13:18:04.000000000 +0200
+@@ -0,0 +1,104 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++struct slang_id {
++ struct slang_id *next;
++ int id;
++ struct slang_multitext *mtext;
++};
++
++static struct slang_id* slang_id_add(struct slang_id *, int, char *);
++static int slang_id_expmem(struct slang_id *);
++static void slang_id_free(struct slang_id *);
++static char *slang_id_get(struct slang_id *, int);
++
++static struct slang_id* slang_id_add(struct slang_id *where, int id, char *text)
++{
++ struct slang_id *newitem;
++
++ newitem = NULL;
++ if (where) {
++ for (newitem = where; newitem; newitem = newitem->next)
++ if (newitem->id == id)
++ break;
++ }
++ if (!newitem) {
++ newitem = nmalloc(sizeof(struct slang_id));
++ newitem->next = NULL;
++ newitem->id = id;
++ newitem->mtext = NULL;
++ if (where)
++ newitem->next = where;
++ else
++ newitem->next = NULL;
++ where = newitem;
++ }
++ newitem->mtext = slang_mtext_add(newitem->mtext, text);
++ return where;
++}
++
++static int slang_id_expmem(struct slang_id *what)
++{
++ int size = 0;
++
++ for (; what; what = what->next) {
++ size += sizeof(struct slang_id);
++ size += slang_multitext_expmem(what->mtext);
++ }
++ return size;
++}
++
++static void slang_id_free(struct slang_id *what)
++{
++ struct slang_id *next;
++
++ while (what) {
++ next = what->next;
++ slang_multitext_free(what->mtext);
++ nfree(what);
++ what = next;
++ }
++}
++
++static char *slang_id_get(struct slang_id *where, int i)
++{
++ while (where) {
++ if (where->id == i)
++ return slang_multitext_getrandomtext(where->mtext);
++ where = where->next;
++ }
++ return NULL;
++}
++
++#ifndef SLANG_NOGETALL
++static char *slang_id_get_first(struct slang_id *where, int id)
++{
++ while (where) {
++ if (where->id == id) {
++ return slang_multitext_get_first(where->mtext);
++ }
++ where = where->next;
++ }
++ return NULL;
++}
++
++static char *slang_id_get_next()
++{
++ return slang_multitext_get_next();
++}
++#endif
+diff -Nur src/mod/gseen.mod/slang_multitext.c src/mod/gseen.mod/slang_multitext.c
+--- src/mod/gseen.mod/slang_multitext.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/slang_multitext.c 2002-10-26 13:18:05.000000000 +0200
+@@ -0,0 +1,151 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++struct slang_mt_content {
++ struct slang_mt_content *next;
++ struct slang_text *text;
++};
++
++struct slang_multitext {
++ int nr;
++ struct slang_mt_content *contents;
++};
++
++static struct slang_multitext *slang_mtext_add(struct slang_multitext *, char *);
++static int slang_multitext_expmem(struct slang_multitext *);
++static void slang_multitext_free(struct slang_multitext *);
++static char *slang_multitext_getrandomtext(struct slang_multitext *);
++#ifndef SLANG_NOTYPES
++static struct slang_text *slang_multitext_find(struct slang_multitext *, char *);
++#endif
++#ifndef SLANG_NOGETALL
++static char *slang_multitext_get_first(struct slang_multitext *);
++static char *slang_multitext_get_next();
++#endif
++
++static struct slang_multitext *slang_mtext_add(struct slang_multitext *where, char *text)
++{
++ struct slang_mt_content *oc, *nc;
++
++ if (!where) {
++ where = nmalloc(sizeof(struct slang_multitext));
++ where->nr = 0;
++ where->contents = NULL;
++ }
++ nc = nmalloc(sizeof(struct slang_mt_content));
++ nc->next = NULL;
++ nc->text = slang_text_parse(text);
++ for (oc = where->contents; oc && oc->next; oc = oc->next);
++ if (oc) {
++ Assert(!oc->next);
++ oc->next = nc;
++ } else
++ where->contents = nc;
++ where->nr++;
++ return where;
++}
++
++static int slang_multitext_expmem(struct slang_multitext *what)
++{
++ struct slang_mt_content *content;
++ int size = 0;
++
++ if (!what) {
++ debug0("WARNING! slang_multitext_expmem() called with NULL pointer!");
++ return 0;
++ }
++ size += sizeof(struct slang_multitext);
++ for (content = what->contents; content; content = content->next) {
++ size += sizeof(struct slang_mt_content);
++ size += slang_text_expmem(content->text);
++ }
++ return size;
++}
++
++static void slang_multitext_free(struct slang_multitext *what)
++{
++ struct slang_mt_content *content, *next;
++
++ if (!what) {
++ debug0("WARNING! slang_multitext_free() called with NULL pointer!");
++ return;
++ }
++ content = what->contents;
++ while (content) {
++ next = content->next;
++ slang_text_free(content->text);
++ nfree(content);
++ content = next;
++ }
++ nfree(what);
++}
++
++static char *slang_multitext_getrandomtext(struct slang_multitext *where)
++{
++ struct slang_mt_content *content;
++ unsigned long x;
++
++ if (!where)
++ return NULL;
++ x = random() % where->nr;
++ for (content = where->contents; content; content = content->next)
++ if (!x)
++ return slang_text_get(content->text);
++ else
++ x--;
++ // we should never reach this part
++ debug0("warning: getrandomtext didn't find anything!");
++ return NULL;
++}
++
++#ifndef SLANG_NOTYPES
++static struct slang_text *slang_multitext_find(struct slang_multitext *where, char *what)
++{
++ struct slang_mt_content *content;
++
++ Assert(where);
++ for (content = where->contents; content; content = content->next) {
++ Assert(content->text);
++ if (!slang_text_strcasecmp(content->text, what))
++ return content->text;
++ }
++ return NULL;
++}
++#endif
++
++#ifndef SLANG_NOGETALL
++static struct slang_mt_content *glob_mtext_content;
++static char *slang_multitext_get_first(struct slang_multitext *where)
++{
++ Assert(where);
++ glob_mtext_content = where->contents;
++ if (glob_mtext_content)
++ return slang_text_get(glob_mtext_content->text);
++ else
++ return NULL;
++}
++
++static char *slang_multitext_get_next()
++{
++ glob_mtext_content = glob_mtext_content->next;
++ if (glob_mtext_content)
++ return slang_text_get(glob_mtext_content->text);
++ else
++ return NULL;
++}
++#endif
+diff -Nur src/mod/gseen.mod/slang_text.c src/mod/gseen.mod/slang_text.c
+--- src/mod/gseen.mod/slang_text.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/slang_text.c 2002-10-26 13:18:07.000000000 +0200
+@@ -0,0 +1,200 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++struct slang_text {
++ struct slang_text *next;
++ char *string;
++ void (*command) ();
++};
++
++struct slang_text_commands {
++ char *command;
++ void (*targetfunc) ();
++};
++
++struct slang_command_list {
++ struct slang_command_list *next;
++ struct slang_text_commands *commands;
++};
++
++static struct slang_text *slang_text_parse(char *);
++static struct slang_text *slang_text_create(struct slang_text *);
++static void slang_text_add_string(struct slang_text *, char *);
++static void slang_text_add_command(struct slang_text *, char *);
++static void slang_text_free(struct slang_text *);
++static int slang_text_expmem(struct slang_text *);
++static char *slang_text_get(struct slang_text *);
++#ifndef SLANG_NOTYPES
++static int slang_text_strcasecmp(struct slang_text *, char *);
++#endif
++
++static struct slang_text *slang_text_parse(char *text)
++{
++ char *cmdstart, *cmdend;
++ struct slang_text *firstitem, *item;
++
++ firstitem = slang_text_create(NULL);
++ item = firstitem;
++ while ((cmdstart = strstr(text, "<?"))) {
++ cmdstart[0] = 0;
++ slang_text_add_string(item, text);
++ item = slang_text_create(item);
++ text += 2;
++ cmdstart += 2;
++ cmdend = strstr(cmdstart, "/?>");
++ if (!cmdend) {
++ putlog(LOG_MISC, "*", "ERROR parsing slang text: unterminated command \"%s\"!", cmdstart);
++ break;
++ }
++ cmdend[0] = 0;
++ slang_text_add_command(item, cmdstart);
++ item = slang_text_create(item);
++ text = cmdend + 3;
++ }
++ slang_text_add_string(item, text);
++ return firstitem;
++}
++
++static struct slang_text *slang_text_create(struct slang_text *where)
++{
++ struct slang_text *newpart;
++
++ newpart = nmalloc(sizeof(struct slang_text));
++ newpart->next = NULL;
++ newpart->string = NULL;
++ newpart->command = NULL;
++ while (where && where->next)
++ where = where->next;
++ if (where)
++ where->next = newpart;
++ return newpart;
++}
++
++static void slang_text_add_string(struct slang_text *item, char *s)
++{
++ Assert(item);
++ Assert(!item->string);
++ item->string = nmalloc(strlen(s) + 1);
++ strcpy(item->string, s);
++}
++
++static void slang_text_free(struct slang_text *item)
++{
++ if (!item)
++ return;
++ slang_text_free(item->next);
++ if (item->string)
++ nfree(item->string);
++ nfree(item);
++}
++
++static int slang_text_expmem(struct slang_text *item)
++{
++ int size = 0;
++
++ while (item) {
++ size += sizeof(struct slang_text);
++ if (item->string)
++ size += strlen(item->string) + 1;
++ item = item->next;
++ }
++ return size;
++}
++
++#ifndef SLANG_NOTYPES
++static int slang_text_strcasecmp(struct slang_text *item, char *text)
++{
++ Assert(item);
++ debug2("s_t_sc: '%s', '%s'", text, item->string);
++ if (item->command || item->next)
++ return 1;
++ return strcasecmp(item->string, text);
++}
++#endif
++
++static char slang_text_buf[500];
++static char *slang_text_get(struct slang_text *item)
++{
++ slang_text_buf[0] = 0;
++ while (item) {
++ if (item->string)
++ strncat(slang_text_buf, item->string, sizeof(slang_text_buf));
++ else if (item->command)
++ item->command();
++ item = item->next;
++ }
++ return slang_text_buf;
++}
++
++/*****************************************************/
++
++
++static struct slang_command_list *glob_slang_cmd_list;
++
++static struct slang_command_list *slang_commands_list_add(struct slang_command_list *where, struct slang_text_commands *what)
++{
++ struct slang_command_list *newcommandlist;
++
++ newcommandlist = nmalloc(sizeof(struct slang_command_list));
++ newcommandlist->commands = what;
++ newcommandlist->next = where;
++ return newcommandlist;
++}
++
++static int slang_commands_list_expmem(struct slang_command_list *what)
++{
++ int size = 0;
++
++ while (what) {
++ size += sizeof(struct slang_command_list);
++ what = what->next;
++ }
++ return size;
++}
++
++static void slang_commands_list_free(struct slang_command_list *what)
++{
++ struct slang_command_list *next;
++
++ while (what) {
++ next = what->next;
++ nfree(what);
++ what = next;
++ }
++}
++
++static void slang_text_add_command(struct slang_text *item, char *s)
++{
++ struct slang_command_list *cmdlist;
++ char *cmd;
++ int i;
++
++ cmd = newsplit(&s);
++ i = 0;
++ for (cmdlist = glob_slang_cmd_list; cmdlist; cmdlist = cmdlist->next) {
++ for (i = 0; 1; i++) {
++ if (!cmdlist->commands[i].command)
++ break;
++ if (!strcasecmp(cmdlist->commands[i].command, cmd)) {
++ item->command = cmdlist->commands[i].targetfunc;
++ return;
++ }
++ }
++ }
++ putlog(LOG_MISC, "*", "ERROR! Unknown slang-command: '%s'", cmd);
++}
+diff -Nur src/mod/gseen.mod/tclcmds.c src/mod/gseen.mod/tclcmds.c
+--- src/mod/gseen.mod/tclcmds.c 1970-01-01 01:00:00.000000000 +0100
++++ src/mod/gseen.mod/tclcmds.c 2002-10-26 13:18:08.000000000 +0200
+@@ -0,0 +1,53 @@
++/*
++ * Copyright (C) 2000,2001 Florian Sander
++ *
++ * 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.
++ */
++
++static int tcl_setchanseenlang STDVAR
++{
++ Context;
++ BADARGS(3, 3, " channel language");
++ chanlangs = slang_chanlang_add(chanlangs, argv[1], argv[2]);
++ return TCL_OK;
++}
++
++static int tcl_loadseenslang STDVAR
++{
++// int ret = 0;
++ char *shortname, *longname, *filename;
++ struct slang_header *slang;
++
++ Context;
++ BADARGS(4, 4, " language description langfile");
++ shortname = argv[1];
++ longname = argv[2];
++ filename = argv[3];
++ coreslangs = slang_create(coreslangs, shortname, longname);
++ slang = slang_find(coreslangs, shortname);
++ Assert(slang);
++ if (!slang_load(slang, filename)) {
++ Tcl_AppendResult(irp, "Couldn't open seenslang file!!!", NULL);
++ return TCL_ERROR;
++ }
++ return TCL_OK;
++}
++
++static tcl_cmds gseentcls[] =
++{
++ {"loadseenslang", tcl_loadseenslang},
++ {"setchanseenlang", tcl_setchanseenlang},
++ {0, 0}
++};
diff --git a/main/eggdrop/logs2html.mod.patch b/main/eggdrop/logs2html.mod.patch
new file mode 100644
index 0000000000..8ea2e94e62
--- /dev/null
+++ b/main/eggdrop/logs2html.mod.patch
@@ -0,0 +1,2404 @@
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/Makefile eggdrop1.6.19/src/mod/logs2html.mod/Makefile
+--- src/mod/logs2html.mod/Makefile 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/Makefile 2009-03-28 01:32:21.000000000 +0000
+@@ -0,0 +1,42 @@
++# Makefile for src/mod/logs2html.mod/
++
++srcdir = .
++
++
++doofus:
++ @echo ""
++ @echo "Let's try this from the right directory..."
++ @echo ""
++ @cd ../../../ && make
++
++static: ../logs2html.o
++
++modules: ../../../logs2html.$(MOD_EXT)
++
++../logs2html.o:
++ $(CC) $(CFLAGS) $(CPPFLAGS) -DMAKING_MODS -c $(srcdir)/logs2html.c
++ @rm -f ../logs2html.o
++ mv logs2html.o ../
++
++../../../logs2html.$(MOD_EXT): ../logs2html.o
++ $(LD) -o ../../../logs2html.$(MOD_EXT) ../logs2html.o
++ $(STRIP) ../../../logs2html.$(MOD_EXT)
++
++depend:
++ $(CC) $(CFLAGS) $(CPPFLAGS) -MM $(srcdir)/logs2html.c > .depend
++
++clean:
++ @rm -f .depend *.o *.$(MOD_EXT) *~
++distclean: clean
++
++#safety hash
++../logs2html.o: .././logs2html.mod/logs2html.c \
++ ../../../src/mod/module.h ../../../src/main.h ../../../src/lang.h \
++ ../../../src/eggdrop.h ../../../src/flags.h ../../../src/proto.h \
++ ../../../lush.h ../../../src/misc_file.h ../../../src/cmdt.h \
++ ../../../src/tclegg.h ../../../src/tclhash.h ../../../src/chan.h \
++ ../../../src/users.h ../../../src/compat/compat.h \
++ ../../../src/compat/inet_aton.h \
++ ../../../src/compat/snprintf.h ../../../src/compat/memset.h \
++ ../../../src/compat/memcpy.h ../../../src/compat/strcasecmp.h \
++ ../../../src/compat/strftime.h ../../../src/mod/modvals.h
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/chan.list eggdrop1.6.19/src/mod/logs2html.mod/chan.list
+--- src/mod/logs2html.mod/chan.list 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/chan.list 2009-03-28 01:43:40.000000000 +0000
+@@ -0,0 +1,22 @@
++# Add your channels here. The format is:
++# addlogs2htmlchan channel output-path main-page-name main-page-title logs-page-prefix logs-page-title
++# channel - your channel which logs you want to convert
++# output-path - your ftp path, where converted files will be put
++# (don't forget to check settings to be sure bot have
++# permision write to the path you specify)
++# main-page-name - name of your mainpage (i.e. page with calendar) for
++# this channel
++# main-page-title - your mainpage title for this channel (will be shown
++# as the caption of the web page in your browser and as
++# the title of calendar)
++# logs-page-prefix - your logs page name (prefix) for this channel.
++# Resulting name'll be 'logs-page-prefixYYmmdd'
++# logs-page-title - your logs page title for this channel (will be shown
++# as the caption of the web page in your browser)
++# Example:
++# addlogs2htmlchan "#MyChan" "logs" "index" "Logs of MyChan" "mychan" "Log of MyChan"
++#
++# Note: expression "logs2html" MUST be one line (i.e. without linefeeds
++# and carrige returns) and every parameter in the expression MUST be enclosed
++# with ".
++
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/fileoperations.c eggdrop1.6.19/src/mod/logs2html.mod/fileoperations.c
+--- src/mod/logs2html.mod/fileoperations.c 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/fileoperations.c 2009-03-28 01:45:19.000000000 +0000
+@@ -0,0 +1,142 @@
++/*
++ * fileoperations.c -- part of logs2html.mod
++ *
++ * Written by Fedotov Alexander aka Gray_Angel aka Shmupsik <shurikvz@mail.ru>
++ *
++ * 2004-2005 year
++ */
++/*
++ * 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 <stdarg.h>
++
++/****************************************************************************/
++/*
++ * function FILE *openfile(char *newfilename, const char *mode, bool silent)
++ *
++ * Input:
++ * newfilename - имя файла, который необходимо создать
++ * mode - режим открытия файла
++ *
++ * Output:
++ * указатель на файл
++ *
++ * Discription:
++ * функция осуществляет создание и открытие файла в указанном режиме
++ * и возвращает указатель на созданный файл
++ */
++static FILE *openfile(char *newfilename, const char *mode, bool silent) {
++ FILE *file;
++ static char *filename = NULL;
++
++ Context;
++
++ filename = (char *)nmalloc(sizeof(char) * (strlen(newfilename) + 1));
++
++ if (filename == NULL) {
++ putlog(LOG_MISC, "*", "logs2html: ERROR! Can't allocate enough space for filename.");
++ return NULL;
++ }
++
++ sprintf(filename, "%s", newfilename);
++ file = fopen(filename, mode);
++ if ((file == NULL) && (!silent)) {
++ putlog(LOG_MISC, "*", "logs2html: Warning! Can't open file \"%s\".", filename);
++ }
++
++ nfree(filename); filename = NULL;
++
++ return file;
++} /* openfile() */
++/****************************************************************************/
++
++
++/****************************************************************************/
++/*
++ * function void writefromexfile(FILE *dst_file, char *exfilename)
++ *
++ * Input:
++ *
++ *
++ *
++ *
++ * Output:
++ *
++ *
++ * Discription:
++ *
++ *
++ */
++static void writefromexfile(FILE *dst_file, char *exfilename) {
++ FILE *addfile;
++ char buffer[512];
++ size_t n;
++
++ if (strlen(exfilename) > 0) {
++ if ((addfile = openfile(exfilename, "r", false)) != NULL) {
++ while(!feof(addfile)) {
++ n = fread(buffer, sizeof(char), sizeof buffer, addfile);
++ fwrite(buffer, sizeof(char), n, dst_file);
++ }
++ fclose(addfile);
++ }
++ }
++
++ return;
++} /* writefromexfile() */
++/****************************************************************************/
++
++
++/****************************************************************************/
++/*
++ * function void str_write(FILE *file, char *fmt, ... )
++ *
++ * Input:
++ * file - файл в который пишем данные
++ * fmt - строка для записи со спецификаторами формата
++ * ... - данные для записи в строку
++ *
++ * Output:
++ * ничего
++ *
++ * Discription:
++ * функция осуществляет запись переданной строки в указанный файл,
++ * производя соответствующее ее форматирование
++ */
++static void str_write(FILE *file, char *fstr, ... ) {
++ va_list ap;
++ int nchars;
++ int size = 256;
++ static char *buffer = NULL;
++
++ Context;
++ buffer = (char *)nmalloc(size);
++
++ va_start(ap, fstr);
++ while (true) {
++ nchars = egg_vsnprintf(buffer, size, fstr, ap);
++ if (nchars < size) break;
++ size *= 2;
++ buffer = (char *)nrealloc(buffer, size);
++ }
++ va_end(ap);
++ fwrite(buffer, sizeof(char), strlen(buffer), file);
++ nfree(buffer); buffer = NULL;
++
++ return;
++} /* str_write() */
++/****************************************************************************/
++
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/help/logs2html.help eggdrop1.6.19/src/mod/logs2html.mod/help/logs2html.help
+--- src/mod/logs2html.mod/help/logs2html.help 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/help/logs2html.help 2009-03-28 01:32:21.000000000 +0000
+@@ -0,0 +1,22 @@
++%{help=convertalllogs}%{+n}
++### %bconvertalllogs%b
++ Reconverts all your logs.
++
++See also: makemainpage
++%{help=makemainpage}%{+n}
++### %bmakemainpage%b
++ Redraws your mainpages.
++
++See also: convertalllogs
++%{help=logs2html module}%{+n}
++### %blogs2html module%b
++ This module convert all existing log files of your eggdrop for giving
++ channels to their html representation.
++
++ The following commands are provided by the logs2html module:
++%{+n}
++ %bconvertalllogs makemainpage%b
++%{help=all}%{+n}
++### %blogs2html module%b commands
++%{+n}
++ %bconvertalllogs makemainpage%b
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/language/logs2html.english.lang eggdrop1.6.19/src/mod/logs2html.mod/language/logs2html.english.lang
+--- src/mod/logs2html.mod/language/logs2html.english.lang 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/language/logs2html.english.lang 2009-03-28 01:32:53.000000000 +0000
+@@ -0,0 +1,38 @@
++# logs2html.english.lang
++# language messages for the logs2html module
++
++# Year
++0xe000,year
++
++# Back to mainpage link
++0xe001,Main page
++
++# Up link
++0xe002,up
++
++# Backward, forward link
++0xe003,back
++0xe004,next
++
++# Month names
++0xe005,January
++0xe006,February
++0xe007,March
++0xe008,April
++0xe009,May
++0xe010,June
++0xe011,July
++0xe012,August
++0xe013,September
++0xe014,October
++0xe015,November
++0xe016,December
++
++# Days of week names
++0xe017,Su
++0xe018,Mo
++0xe019,Tu
++0xe020,Wn
++0xe021,Th
++0xe022,Fr
++0xe023,St
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/language/logs2html.french.lang eggdrop1.6.19/src/mod/logs2html.mod/language/logs2html.french.lang
+--- src/mod/logs2html.mod/language/logs2html.french.lang 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/language/logs2html.french.lang 2009-03-28 01:32:53.000000000 +0000
+@@ -0,0 +1,38 @@
++# logs2html.french.lang
++# language messages for the logs2html module
++
++# Year
++0xe000,Annйe
++
++# Back to mainpage link
++0xe001,Page principale
++
++# Up link
++0xe002,haut
++
++# Backward, forward link
++0xe003,Prйcйdente
++0xe004,Suivante
++
++# Month names
++0xe005,Janvier
++0xe006,Fйvrier
++0xe007,Mars
++0xe008,Avril
++0xe009,Mai
++0xe010,Juin
++0xe011,Juillet
++0xe012,Aoыt
++0xe013,Septembre
++0xe014,Octobre
++0xe015,Novembre
++0xe016,Dйcembre
++
++# Days of week names
++0xe017,Di
++0xe018,Lu
++0xe019,Ma
++0xe020,Me
++0xe021,Je
++0xe022,Ve
++0xe023,Sa
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/language/logs2html.russian.lang eggdrop1.6.19/src/mod/logs2html.mod/language/logs2html.russian.lang
+--- src/mod/logs2html.mod/language/logs2html.russian.lang 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/language/logs2html.russian.lang 2009-03-28 01:32:21.000000000 +0000
+@@ -0,0 +1,38 @@
++# logs2html.ru.lang
++# language messages for the logs2html module
++
++# Year
++0xe000,год
++
++# Back to mainpage link
++0xe001,На главную страницу
++
++# Up link
++0xe002,наверх
++
++# Backward, forward link
++0xe003,предыдущая
++0xe004,следующая
++
++# Month names
++0xe005,Январь
++0xe006,Февраль
++0xe007,Март
++0xe008,Апрель
++0xe009,Май
++0xe010,Июнь
++0xe011,Июль
++0xe012,Август
++0xe013,Сентябрь
++0xe014,Октябрь
++0xe015,Ноябрь
++0xe016,Декабрь
++
++# Days of week names
++0xe017,Вс
++0xe018,Пн
++0xe019,Вт
++0xe020,Ср
++0xe021,Чт
++0xe022,Пт
++0xe023,Сб
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/language.h eggdrop1.6.19/src/mod/logs2html.mod/language.h
+--- src/mod/logs2html.mod/language.h 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/language.h 2009-03-28 01:32:21.000000000 +0000
+@@ -0,0 +1,51 @@
++/*
++ * language.h -- part of logs2html.mod
++ *
++ * Written by Fedotov Alexander aka Gray_Angel aka Shmupsik <shurikvz@mail.ru>
++ *
++ * 2004-2005 year
++ */
++/*
++ * 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.
++ */
++
++
++#define LOGS2HTML_YEAR get_language(0xe000)
++#define LOGS2HTML_MAINPAGE get_language(0xe001)
++#define LOGS2HTML_UP get_language(0xe002)
++#define LOGS2HTML_BACK get_language(0xe003)
++#define LOGS2HTML_NEXT get_language(0xe004)
++
++#define LOGS2HTML_JANUARY get_language(0xe005)
++#define LOGS2HTML_FEBRIARY get_language(0xe006)
++#define LOGS2HTML_MARCH get_language(0xe007)
++#define LOGS2HTML_APRIL get_language(0xe008)
++#define LOGS2HTML_MAY get_language(0xe009)
++#define LOGS2HTML_JUNE get_language(0xe010)
++#define LOGS2HTML_JULY get_language(0xe011)
++#define LOGS2HTML_AUGUST get_language(0xe012)
++#define LOGS2HTML_SEPTEMBER get_language(0xe013)
++#define LOGS2HTML_OCTOBER get_language(0xe014)
++#define LOGS2HTML_NOVEMBER get_language(0xe015)
++#define LOGS2HTML_DECEMBER get_language(0xe016)
++
++#define LOGS2HTML_SUNDAY get_language(0xe017)
++#define LOGS2HTML_MONDAY get_language(0xe018)
++#define LOGS2HTML_TUESDAY get_language(0xe019)
++#define LOGS2HTML_WEDNESDAY get_language(0xe020)
++#define LOGS2HTML_THURSDAY get_language(0xe021)
++#define LOGS2HTML_FRIDAY get_language(0xe022)
++#define LOGS2HTML_SATURDAY get_language(0xe023)
++
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/logs2html.c eggdrop1.6.19/src/mod/logs2html.mod/logs2html.c
+--- src/mod/logs2html.mod/logs2html.c 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/logs2html.c 2009-03-28 01:32:53.000000000 +0000
+@@ -0,0 +1,1572 @@
++/*
++ * logs2html.c -- part of logs2html.mod
++ *
++ * Written by Fedotov Alexander aka Gray_Angel aka Shmupsik <shurikvz@mail.ru>
++ *
++ * 2004-2005 year
++ */
++/*
++ * 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.
++ */
++
++
++#define MODULE_NAME "logs2html"
++#define MAKING_LOGS2HTML
++#include "src/mod/module.h"
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <time.h>
++#include <ctype.h>
++#include <sys/types.h>
++#include <math.h>
++#include "logs2html.h"
++#include "language.h"
++
++#undef global
++
++#define MODULE_MAJOR 2
++#define MODULE_MINOR 3
++#define MODULE_SUBMINOR 4
++
++static Function *global = NULL;
++
++#include "fileoperations.c"
++
++
++static int shtime;
++static int keep_all_logs;
++static char logfile_suffix[21];
++static char configfile[121];
++static int lines_per_page;
++
++static int days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
++
++static int month_cols_count = 3;
++static int month_rows_count = 4;
++
++static char mainpage_top_filename[257] = "\0";
++static char mainpage_bottom_filename[257] = "\0";
++static char logspage_top_filename[257] = "\0";
++static char logspage_bottom_filename[257] = "\0";
++static char chanlist_filename[257] = "\0";
++static char userstyle_filename[257] = "\0";
++static char encoding_string[31] = "\0";
++
++static logs2htmlchan *logs2htmlchanlist = NULL;
++
++/* for language file */
++static char month_names[12][21];
++static char days_names[7][21];
++
++static void logs2html_hook_5minutely(void);
++static void logs2html_hook_daily(void);
++static void logs2html_hook_pre_rehash(void);
++static void logs2html_hook_rehash(void);
++
++
++/* Calculate the memory we keep allocated.
++ */
++static int logs2html_expmem()
++{
++ logs2htmlchan *p;
++ int size = 0;
++
++ Context;
++ p = logs2htmlchanlist;
++ while (p != NULL) {
++ size++;
++ p = p->next;
++ }
++
++ size *= sizeof(struct logs2html_data);
++
++ return size;
++}
++
++
++/****************************************************************************/
++/*
++ * function int convertalllogs(struct userrec *u, int idx, char *par)
++ *
++ * Input:
++ *
++ *
++ *
++ * Output:
++ *
++ *
++ * Discription:
++ *
++ */
++static int cmd_convertalllogs(struct userrec *u, int idx, char *par) {
++ int add_day;
++ logs2htmlchan *p;
++ int i, j, k;
++ struct tm tblock;
++ time_t t = time(NULL);
++ struct tm *st = localtime(&t);
++
++ tblock.tm_year = st->tm_year;
++ tblock.tm_isdst = st->tm_isdst;
++ tblock.tm_hour = 0;
++ tblock.tm_min = 0;
++ tblock.tm_sec = 1;
++ /* Other fields not necessary here
++ tblock.tm_mday = st->tm_mday;
++ tblock.tm_mon = st->tm_mon;
++ tblock.tm_wday = st->tm_wday;
++ tblock.tm_yday = st->tm_yday;
++ */
++
++ putlog(LOG_CMDS, "*", "#%s# start converting all log files.", dcc[idx].nick);
++ for (i = 0; i < 12; i++) {
++ add_day = ((i == 1) && isleap(tblock.tm_year)) ? 1 : 0;
++ for (j = 0; j < ((days_in_month[i]) + add_day); j++) {
++ tblock.tm_mon = i; tblock.tm_mday = j + 1;
++
++ tblock.tm_wday = (getdayofweek(tblock.tm_year + 1900, tblock.tm_mon + 1, tblock.tm_mday) + 1) % 7;
++ tblock.tm_yday = 0;
++ for (k = 0; k < tblock.tm_mon; k++) tblock.tm_yday += days_in_month[k];
++ tblock.tm_yday += tblock.tm_mday - 1;
++
++ p = logs2htmlchanlist;
++ while (p != NULL) {
++ convertfile(&tblock, p);
++ p = p->next;
++ }
++ }
++ }
++ putlog(LOG_CMDS, "*", "#%s# converting of all log files done.", dcc[idx].nick);
++
++ return 0;
++} /* convertalllogs() */
++/****************************************************************************/
++
++
++/****************************************************************************/
++/*
++ * function int cmd_makemainpage(struct userrec *u, int idx, char *par)
++ *
++ * Input:
++ *
++ *
++ *
++ * Output:
++ *
++ *
++ * Discription:
++ *
++ */
++static int cmd_makemainpage(struct userrec *u, int idx, char *par) {
++ logs2htmlchan *p;
++
++ putlog(LOG_CMDS, "*", "#%s# starting making mainpages.", dcc[idx].nick);
++ p = logs2htmlchanlist;
++ while (p != NULL) {
++ dprintf(idx, "Making mainpage for channel '%s'...\n", p->channame);
++ makemainpage(p);
++ p = p->next;
++ }
++ putlog(LOG_CMDS, "*", "#%s# making of mainpages done.", dcc[idx].nick);
++
++ return 0;
++} /* convertalllogs() */
++/****************************************************************************/
++
++
++/****************************************************************************/
++/*
++ * function bool isvalidlink(char *link)
++ *
++ * Input:
++ * link - строка указывающая на ссылку
++ *
++ *
++ * Output:
++ *
++ *
++ * Discription:
++ * Функция проверяет являются ли переданные в строке символы - символами
++ * допустимыми в ссылках
++ */
++static patternkind whatisit(char *pattern) {
++ char alpha_common[] = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789._-";
++ char alpha_http[] = "=?&/:%";
++ char *p;
++
++ if (strstr(pattern, "..") != NULL) {
++ return ITS_NOTHING;
++ }
++
++ p = strchr(pattern, '@');
++ if (p) {
++ /* it's can't be www link */
++
++ /* well, i can't imagine e-mail shorter than i@m.ru */
++ /* let's check it */
++ *p = '\0';
++ if (strlen(pattern) < 1) {
++ *p = '@';
++ return ITS_NOTHING;
++ }
++ while (*pattern) {
++ if (strchr(alpha_common, *pattern) == NULL) {
++ *p = '@';
++ return ITS_NOTHING;
++ }
++ pattern++;
++ }
++ *p = '@';
++ p++;
++ if (strlen(p) < 4) {
++ return ITS_NOTHING;
++ }
++ while (*p) {
++ if (strchr(alpha_common, *p) == NULL) return ITS_NOTHING;
++ p++;
++ }
++
++ return ITS_EMAIL;
++ } else {
++ if ((strncmp(pattern, "http://", 7) != 0) && (strncmp(pattern, "ftp://", 6) != 0)) {
++ if (strncmp(pattern, "www.", 4) != 0) {
++ return ITS_NOTHING;
++ } else {
++ while (*pattern) {
++ if ((strchr(alpha_common, *pattern) == NULL) && (strchr(alpha_http, *pattern) == NULL)) return ITS_NOTHING;
++ pattern++;
++ }
++
++ return ITS_TRUNKLINK;
++ }
++ }
++
++ while (*pattern) {
++ if ((strchr(alpha_common, *pattern) == NULL) && (strchr(alpha_http, *pattern) == NULL)) return ITS_NOTHING;
++ pattern++;
++ }
++
++ return ITS_LINK;
++ }
++
++ return ITS_NOTHING;
++} /* whatisit() */
++/****************************************************************************/
++
++
++/* A report on the module status.
++ *
++ * details is either 0 or 1:
++ * 0 - `.status'
++ * 1 - `.status all' or `.module woobie'
++ */
++static void logs2html_report(int idx, int details)
++{
++ if (details) {
++ int size = logs2html_expmem();
++
++ dprintf(idx, " Using %d byte%s of memory\n", size,
++ (size != 1) ? "s" : "");
++ }
++}
++
++static cmd_t mydcc[] = {
++ {"convertalllogs", "n", cmd_convertalllogs, NULL},
++ {"makemainpage", "n", cmd_makemainpage, NULL},
++ {NULL, NULL, NULL, NULL} /* Mark end. */
++};
++
++static tcl_strings my_tcl_strings[] = {
++ {"logfile-suffix", logfile_suffix, 20, STR_PROTECT},
++ {"config", configfile, 121, STR_PROTECT},
++ {"mainpage-top", mainpage_top_filename, 256, 0},
++ {"mainpage-bottom", mainpage_bottom_filename, 256, 0},
++ {"logspage-top", logspage_top_filename, 256, 0},
++ {"logspage-bottom", logspage_bottom_filename, 256, 0},
++ {"channels-list", chanlist_filename, 256, 0},
++ {"user-style", userstyle_filename, 256, 0},
++ {"insert-encoding-str", encoding_string, 30, 0},
++ {NULL, NULL, 0, 0} /* Mark end. */
++};
++
++static tcl_ints my_tcl_ints[] = {
++ {"col-count", &month_cols_count, 0},
++ {"lines-per-page", &lines_per_page, 0},
++ {"log-time", &shtime, 1},
++ {"keep-all-logs", &keep_all_logs, 1},
++ {NULL, NULL, 0} /* Mark end. */
++};
++
++static char *logs2html_close()
++{
++ logs2htmlchan *p, *q;
++
++ Context;
++ q = p = logs2htmlchanlist;
++ while (q != NULL) {
++ q = p->next;
++ nfree(p);
++ p = q;
++ }
++ logs2htmlchanlist = p = q = NULL;
++
++ del_lang_section(MODULE_NAME);
++ rem_help_reference(MODULE_NAME ".help");
++
++ del_hook(HOOK_DAILY, (Function)logs2html_hook_daily);
++ del_hook(HOOK_5MINUTELY, (Function)logs2html_hook_5minutely);
++ del_hook(HOOK_PRE_REHASH, (Function)logs2html_hook_pre_rehash);
++ del_hook(HOOK_REHASH, (Function)logs2html_hook_rehash);
++
++ rem_builtins(H_dcc, mydcc);
++ rem_tcl_ints(my_tcl_ints);
++ rem_tcl_strings(my_tcl_strings);
++
++ module_undepend(MODULE_NAME);
++ return NULL;
++}
++
++EXPORT_SCOPE char *logs2html_start();
++
++static Function logs2html_table[] = {
++ (Function) logs2html_start,
++ (Function) logs2html_close,
++ (Function) logs2html_expmem,
++ (Function) logs2html_report,
++};
++
++char *logs2html_start(Function *global_funcs)
++{
++ logs2htmlchan *ptr;
++
++ global = global_funcs;
++
++ Context;
++ /* Register the module. */
++ module_register(MODULE_NAME, logs2html_table, MODULE_MAJOR, MODULE_MINOR);
++
++ if (!module_depend(MODULE_NAME, "eggdrop", 106, 15)) {
++ module_undepend(MODULE_NAME);
++ return "This module requires Eggdrop 1.6.15 or later.";
++ }
++
++ add_tcl_ints(my_tcl_ints);
++
++ if (!keep_all_logs) {
++ rem_tcl_ints(my_tcl_ints);
++ module_undepend(MODULE_NAME);
++ return "This module requires \"keep-all-logs\" set to \"1\". Please check settings in your config file.";
++ }
++ logs2htmlchanlist = NULL; if (lines_per_page < 0) lines_per_page = 0;
++
++ add_tcl_strings(my_tcl_strings);
++
++ if (addchannels()) {
++ ptr = logs2htmlchanlist;
++ while (ptr != NULL) {
++ putlog(LOG_CMDS, "*", "logs2html: added channel %s, with path to logfiles \"%s\"...\n", ptr->channame, ptr->logfilename);
++ ptr = ptr->next;
++ }
++ } else {
++ rem_tcl_strings(my_tcl_strings);
++ rem_tcl_ints(my_tcl_ints);
++ module_undepend(MODULE_NAME);
++ return "No channels added. Please check settings in your eggdrop config file to be sure there is logfiles with mode set to \"p\".";
++ }
++
++ add_builtins(H_dcc, mydcc);
++
++ add_hook(HOOK_5MINUTELY, (Function)logs2html_hook_5minutely); //Для обновления лога за текущий день
++ add_hook(HOOK_DAILY, (Function)logs2html_hook_daily); //Последние 5 минут за прошлый день
++ add_hook(HOOK_PRE_REHASH, (Function)logs2html_hook_pre_rehash);
++ add_hook(HOOK_REHASH, (Function)logs2html_hook_rehash);
++
++ add_lang_section(MODULE_NAME);
++ add_help_reference(MODULE_NAME ".help");
++
++ month_names[0][0] = month_names[1][0] = month_names[2][0] = month_names[3][0] =
++ month_names[4][0] = month_names[5][0] = month_names[6][0] = month_names[7][0] =
++ month_names[8][0] = month_names[9][0] = month_names[10][0] = month_names[11][0] =
++ days_names[0][0] = days_names[1][0] = days_names[2][0] = days_names[3][0] =
++ days_names[4][0] = days_names[5][0] = days_names[6][0] = '\0';
++
++ strncpyz(month_names[0], LOGS2HTML_JANUARY, sizeof month_names[0]);
++ strncpyz(month_names[1], LOGS2HTML_FEBRIARY, sizeof month_names[1]);
++ strncpyz(month_names[2], LOGS2HTML_MARCH, sizeof month_names[2]);
++ strncpyz(month_names[3], LOGS2HTML_APRIL, sizeof month_names[3]);
++ strncpyz(month_names[4], LOGS2HTML_MAY, sizeof month_names[4]);
++ strncpyz(month_names[5], LOGS2HTML_JUNE, sizeof month_names[5]);
++ strncpyz(month_names[6], LOGS2HTML_JULY, sizeof month_names[6]);
++ strncpyz(month_names[7], LOGS2HTML_AUGUST, sizeof month_names[7]);
++ strncpyz(month_names[8], LOGS2HTML_SEPTEMBER, sizeof month_names[8]);
++ strncpyz(month_names[9], LOGS2HTML_OCTOBER, sizeof month_names[9]);
++ strncpyz(month_names[10], LOGS2HTML_NOVEMBER, sizeof month_names[10]);
++ strncpyz(month_names[11], LOGS2HTML_DECEMBER, sizeof month_names[11]);
++ strncpyz(days_names[0], LOGS2HTML_MONDAY, sizeof days_names[0]);
++ strncpyz(days_names[1], LOGS2HTML_TUESDAY, sizeof days_names[1]);
++ strncpyz(days_names[2], LOGS2HTML_WEDNESDAY, sizeof days_names[2]);
++ strncpyz(days_names[3], LOGS2HTML_THURSDAY, sizeof days_names[3]);
++ strncpyz(days_names[4], LOGS2HTML_FRIDAY, sizeof days_names[4]);
++ strncpyz(days_names[5], LOGS2HTML_SATURDAY, sizeof days_names[5]);
++ strncpyz(days_names[6], LOGS2HTML_SUNDAY, sizeof days_names[6]);
++
++ return NULL;
++}
++
++/*
++ * Code starts here
++ */
++
++/*
++ * getdayofweek()
++ * Input:
++ * year - год
++ * month - месяц
++ * day - день
++ *
++ * Output:
++ * 0 - Понедельник, 1 - Вторник, 2 - Среда и.т.д.
++ *
++ * Description:
++ * Функция вычисляет на какой день недели приходится переданная дата
++ */
++static int getdayofweek(int year, int month, int day)
++{
++ long int d1, d2, d3;
++
++ long int tdays = year * 12 + month - 3;
++ month = tdays % 12;
++ year = (tdays - month) / 12;
++
++ d1 = ((146097 * (year - (year % 100)) / 100) - ((146097 * (year - (year % 100)) / 100) % 4)) / 4;
++ d2 = ((1461 * (year % 100)) - ((1461 * (year % 100)) % 4)) / 4;
++ d3 = ((153 * month + 2) - ((153 * month + 2) % 5)) / 5 + day + 1721119;
++
++ return (d1 + d2 + d3) % 7;
++} /* getdayofweek() */
++/****************************************************************************/
++
++
++/****************************************************************************/
++/*
++ * function static int addchannels() {
++ *
++ * Input:
++ *
++ *
++ *
++ *
++ * Output:
++ *
++ *
++ * Discription:
++ *
++ *
++ */
++static int addchannels() {
++ char buffer[512];
++ char *buf_ptr, *p;
++ logs2htmlchan *newchan, *ptr;
++ FILE *src_file;
++ int field_num;
++ int filelineread;
++ int channels_count = 0;
++
++
++ Context;
++ filelineread = 0;
++ if ((src_file = openfile(chanlist_filename, "r", true)) == NULL) return 0;
++ while (!feof(src_file)) {
++ buf_ptr = fgets(buffer, sizeof(buffer)-1, src_file);
++ if (buf_ptr == NULL) break;
++ filelineread++;
++
++ p = strrchr(buf_ptr, '\n');
++ if (p) *p = '\0';
++ p = strrchr(buf_ptr, '\r');
++ if (p) *p = '\0';
++ if (strlen(buf_ptr) == 0) continue;
++ while (isspace(*buf_ptr)) buf_ptr++;
++/* while (egg_isspace(*buf_ptr)) buf_ptr++; */
++ if (strncmp(buf_ptr, "addlogs2htmlchan", 16) == 0) p = buf_ptr += 16; else continue;
++ while (*p++) if (isspace(*p)) *p = ' ';
++/* while (*p++) if (egg_isspace(*p)) *p = ' '; */
++ if (!isspace(*buf_ptr)) continue;
++/* if (!egg_isspace(*buf_ptr)) continue; */
++ /*
++ At that point we have string of params of command "addlogs2htmlchan"
++ pointed by buf_ptr, with leading ' ';
++ */
++
++ newchan = (logs2htmlchan *)nmalloc(sizeof(logs2htmlchan) * 1);
++ if (newchan == NULL) {
++ nfree(newchan); newchan = NULL;
++ fclose(src_file);
++ putlog(LOG_MISC, "*", "Can't allocate enough space to add new channel!");
++ break;
++ }
++
++ newchan->next = NULL;
++ newchan->logfilename[0] = '\0';
++ newchan->channame[0] = '\0';
++ newchan->outputpath[0] = '\0';
++ newchan->mainpagename[0] = '\0';
++ newchan->mainpagetitle[0] = '\0';
++ newchan->logspagename[0] = '\0';
++ newchan->logspagetitle[0] = '\0';
++
++ field_num = 0;
++ while (*buf_ptr) {
++ while ((*buf_ptr) && (*buf_ptr == ' ')) buf_ptr++;
++ p = ++buf_ptr;
++ while ((*p) && (*p != '"')) p++;
++ *p = '\0';
++ switch (field_num) {
++ case 0: /* newchan->channame */
++ strncpyz(newchan->channame, buf_ptr, sizeof newchan->channame);
++ field_num = 1;
++ break;
++ case 1: /* newchan->outputpath */
++ strncpyz(newchan->outputpath, buf_ptr, sizeof newchan->outputpath);
++ field_num = 2;
++ break;
++ case 2: /* newchan->mainpagename */
++ strncpyz(newchan->mainpagename, buf_ptr, sizeof newchan->mainpagename);
++ field_num = 3;
++ break;
++ case 3: /* newchan->mainpagetitle */
++ strncpyz(newchan->mainpagetitle, buf_ptr, sizeof newchan->mainpagetitle);
++ field_num = 4;
++ break;
++ case 4: /* newchan->logspagename */
++ strncpyz(newchan->logspagename, buf_ptr, sizeof newchan->logspagename);
++ field_num = 5;
++ break;
++ case 5: /* newchan->logspagetitle */
++ strncpyz(newchan->logspagetitle, buf_ptr, sizeof newchan->logspagetitle);
++ field_num = 6;
++ break;
++ }
++ buf_ptr = ++p;
++ }
++
++
++ if (field_num != 6) {
++ nfree(newchan); newchan = NULL;
++ putlog(LOG_MISC, "*", "Error in file %s. String %d. Invalid expression \"addlogs2htmlchan\".", chanlist_filename, filelineread);
++ continue;
++ }
++
++ ptr = logs2htmlchanlist;
++ if (ptr == NULL) {
++ logs2htmlchanlist = newchan;
++ } else {
++ while (ptr->next != NULL) {
++ ptr = ptr->next;
++ }
++ ptr->next = newchan;
++ }
++ newchan = NULL;
++
++ }
++ fclose(src_file);
++
++ if ((src_file = openfile(configfile, "r", true)) == NULL) return 0;
++ while (!feof(src_file)) {
++ buf_ptr = fgets(buffer, sizeof(buffer)-1, src_file);
++ if (buf_ptr == NULL) break;
++
++ p = strrchr(buf_ptr, '\n');
++ if (p) *p = '\0';
++ p = strrchr(buf_ptr, '\r');
++ if (p) *p = '\0';
++ if (strlen(buf_ptr) == 0) continue;
++ while (isspace(*buf_ptr)) buf_ptr++;
++/* while (egg_isspace(*buf_ptr)) buf_ptr++; */
++ if (strncmp(buf_ptr, "logfile", 7) == 0) p = buf_ptr += 7; else continue;
++ while (*p++) if (isspace(*p)) *p = ' ';
++/* while (*p++) if (egg_isspace(*p)) *p = ' '; */
++ if (!isspace(*buf_ptr)) continue;
++/* if (!egg_isspace(*buf_ptr)) continue; */
++ /*
++ At that point we have string of params of command "logfile"
++ pointed by buf_ptr, with leading ' ';
++ */
++
++ p = newsplit(&buf_ptr);
++ if (logmodes(p) & LOG_PUBLIC) {
++ p = newsplit(&buf_ptr);
++ /*
++ At that point we have channel of command "logfile" pointed by p,
++ and the rest of the string (path to file), pointed by buf_ptr and enclosed with "
++ */
++ buf_ptr++; buf_ptr[strlen(buf_ptr)-1] = '\0';
++
++ ptr = logs2htmlchanlist;
++ while (ptr != NULL) {
++ if ((*p == '*') || (!rfc_casecmp(ptr->channame, p))) {
++ egg_snprintf(ptr->logfilename, sizeof ptr->logfilename, "%s", buf_ptr);
++ }
++ ptr = ptr->next;
++ }
++ }
++
++
++ }
++ fclose(src_file);
++
++
++ while (logs2htmlchanlist != NULL) {
++ if (strlen(logs2htmlchanlist->logfilename) == 0) {
++ ptr = logs2htmlchanlist;
++ logs2htmlchanlist = logs2htmlchanlist->next;
++ nfree(ptr);
++ } else {
++ ptr = logs2htmlchanlist;
++ while (ptr->next != NULL) {
++ if (strlen(ptr->next->logfilename) == 0) {
++ newchan = ptr->next;
++ ptr->next = ptr->next->next;
++ nfree(newchan);
++ } else {
++ ptr = ptr->next;
++ }
++ }
++ break;
++ }
++ }
++
++ ptr = logs2htmlchanlist;
++ while (ptr != NULL) {
++ channels_count++;
++ ptr = ptr->next;
++ }
++
++ ptr = newchan = NULL;
++
++ return channels_count;
++} /* addchannels() */
++/****************************************************************************/
++
++
++/****************************************************************************/
++/*
++ * function int convertfile(int year, int month, int day, bool convertifexists)
++ *
++ * Input:
++ *
++ *
++ *
++ * Output:
++ *
++ *
++ * Discription:
++ * переводит файлы из текстового вида в формат HTML
++ */
++static void convertfile(struct tm *t, logs2htmlchan *ch) {
++ char *buf_ptr, *p, *q, *r;
++ char buffer[LOGLINELEN], data[LOGLINELEN], ct[81], stamp[33];
++ char f_color[3], bg_color[3], text_style[6];
++ int mc_openteg_count = 0, pages_count = 0;
++ int i, j, k, lines_count, tsl;
++ char src_filename[257], dst_filename[257];
++ FILE *src_file, *dst_file;
++ bool bold_isopen, underline_isopen;
++ bool noneedtoclose, skipemail, linkfound;
++ char openspace[3] = " ( ";
++ char closespace[3] = " ),";
++ char *cuted_string = NULL;
++ int cuted_string_len, old_cuted_string_len, delta_cuted_string_len;
++ int r_offset, q_offset;
++ int *original_idx = NULL;
++
++ Context;
++ /* Let first write our default CSS file */
++ egg_snprintf(dst_filename, sizeof dst_filename, "%s%sdefault.css", ch->outputpath, SEP);
++ if ((dst_file = openfile(dst_filename, "wb", false)) != NULL) {
++ str_write(dst_file, "BODY {\n");
++ str_write(dst_file, "font-family: serif;\n");
++ str_write(dst_file, "font-style: normal\n");
++ str_write(dst_file, "font-variant: normal;\n");
++ str_write(dst_file, "font-weight: normal;\n");
++ str_write(dst_file, "font-stretch: normal;\n");
++ str_write(dst_file, "font-size: 12pt;\n");
++ str_write(dst_file, "text-align: left;\n");
++ str_write(dst_file, "color: rgb(0,0,0);\n");
++ str_write(dst_file, "background-color: transparent;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "BODY.mainpage {\n");
++ str_write(dst_file, "background-color: whitesmoke;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "BODY.logspage {\n");
++ str_write(dst_file, "background-color: lightyellow;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "TD {\n");
++ str_write(dst_file, "width: %d%%;\n", (int)floor(100.0/((double)month_cols_count * 8)));
++ str_write(dst_file, "background-color: lavender;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "TD.space {\n");
++ str_write(dst_file, "width: auto;\n");
++ str_write(dst_file, "background-color: whitesmoke;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "TD.dayname {\n");
++ str_write(dst_file, "width: auto;\n");
++ str_write(dst_file, "font-weight: bold;\n");
++ str_write(dst_file, "text-align: right;\n");
++ str_write(dst_file, "background-color: lightskyblue;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "TD.weekend {\n");
++ str_write(dst_file, "width: auto;\n");
++ str_write(dst_file, "font-weight: bold;\n");
++ str_write(dst_file, "text-align: right;\n");
++ str_write(dst_file, "color: red;\n");
++ str_write(dst_file, "background-color: lightskyblue;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "TH {\n");
++ str_write(dst_file, "font-weight: bold;\n");
++ str_write(dst_file, "text-align: center;\n");
++ str_write(dst_file, "background-color: lavender;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "SPAN.time {\n");
++ str_write(dst_file, "color: silver;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "SPAN.nick {\n");
++ str_write(dst_file, "color: mediumblue;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "SPAN.else {\n");
++ str_write(dst_file, "color: green;\n");
++ str_write(dst_file, "font-style: italic;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "SPAN.action {\n");
++ str_write(dst_file, "color: violet;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "#footer {\n");
++ str_write(dst_file, " font-size: 10px;\n");
++ str_write(dst_file, " text-align: center;\n");
++ str_write(dst_file, " border-top-width: 1px;\n");
++ str_write(dst_file, " border-top-style: solid;\n");
++ str_write(dst_file, " border-color: #CCCCCC;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "h1 {\n");
++ str_write(dst_file, " text-align: center;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "#calendar {\n");
++ str_write(dst_file, " margin-right: auto;\n");
++ str_write(dst_file, " margin-left: auto;\n");
++ str_write(dst_file, " width: 90%;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "#navtop {\n");
++ str_write(dst_file, " text-align: center;\n");
++ str_write(dst_file, " margin-top: 10px;\n");
++ str_write(dst_file, " margin-bottom: 10px;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "#navbottom {\n");
++ str_write(dst_file, " text-align: center;\n");
++ str_write(dst_file, " margin-top: 10px;\n");
++ str_write(dst_file, " margin-bottom: 5px;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "#totop {\n");
++ str_write(dst_file, " text-align: center;\n");
++ str_write(dst_file, " margin-top: 5px;\n");
++ str_write(dst_file, " margin-bottom: 10px;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "#log {\n");
++ str_write(dst_file, " margin-top: 10px;\n");
++ str_write(dst_file, " margin-bottom: 10px;\n");
++ str_write(dst_file, " padding-top: 10px;\n");
++ str_write(dst_file, " padding-bottom: 10px;\n");
++ str_write(dst_file, " border-width: thin;\n");
++ str_write(dst_file, " border-top-style: solid;\n");
++ str_write(dst_file, " border-bottom-style: solid;\n");
++ str_write(dst_file, " border-color: #CCCCCC;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, ".mainpage #title {\n");
++ str_write(dst_file, " font-size: 36px;\n");
++ str_write(dst_file, " text-align: center;\n");
++ str_write(dst_file, " margin-bottom: 20px;\n");
++ str_write(dst_file, " border-width: thin;\n");
++ str_write(dst_file, " border-color: #CCCCCC;\n");
++ str_write(dst_file, " border-bottom-style: solid;\n");
++ str_write(dst_file, "}\n");
++ str_write(dst_file, "SPAN.c0000, SPAN.c0100, SPAN.c0200, SPAN.c0300, SPAN.c0400, SPAN.c0500, SPAN.c0600, SPAN.c0700, SPAN.c0800, SPAN.c0900, SPAN.c1000, SPAN.c1100, SPAN.c1200, SPAN.c1300, SPAN.c1400, SPAN.c1500 {background-color: white;}\n");
++ str_write(dst_file, "SPAN.c0001, SPAN.c0101, SPAN.c0201, SPAN.c0301, SPAN.c0401, SPAN.c0501, SPAN.c0601, SPAN.c0701, SPAN.c0801, SPAN.c0901, SPAN.c1001, SPAN.c1101, SPAN.c1201, SPAN.c1301, SPAN.c1401, SPAN.c1501 {background-color: black;}\n");
++ str_write(dst_file, "SPAN.c0002, SPAN.c0102, SPAN.c0202, SPAN.c0302, SPAN.c0402, SPAN.c0502, SPAN.c0602, SPAN.c0702, SPAN.c0802, SPAN.c0902, SPAN.c1002, SPAN.c1102, SPAN.c1202, SPAN.c1302, SPAN.c1402, SPAN.c1502 {background-color: navy;}\n");
++ str_write(dst_file, "SPAN.c0003, SPAN.c0103, SPAN.c0203, SPAN.c0303, SPAN.c0403, SPAN.c0503, SPAN.c0603, SPAN.c0703, SPAN.c0803, SPAN.c0903, SPAN.c1003, SPAN.c1103, SPAN.c1203, SPAN.c1303, SPAN.c1403, SPAN.c1503 {background-color: green;}\n");
++ str_write(dst_file, "SPAN.c0004, SPAN.c0104, SPAN.c0204, SPAN.c0304, SPAN.c0404, SPAN.c0504, SPAN.c0604, SPAN.c0704, SPAN.c0804, SPAN.c0904, SPAN.c1004, SPAN.c1104, SPAN.c1204, SPAN.c1304, SPAN.c1404, SPAN.c1504 {background-color: red;}\n");
++ str_write(dst_file, "SPAN.c0005, SPAN.c0105, SPAN.c0205, SPAN.c0305, SPAN.c0405, SPAN.c0505, SPAN.c0605, SPAN.c0705, SPAN.c0805, SPAN.c0905, SPAN.c1005, SPAN.c1105, SPAN.c1205, SPAN.c1305, SPAN.c1405, SPAN.c1505 {background-color: maroon;}\n");
++ str_write(dst_file, "SPAN.c0006, SPAN.c0106, SPAN.c0206, SPAN.c0306, SPAN.c0406, SPAN.c0506, SPAN.c0606, SPAN.c0706, SPAN.c0806, SPAN.c0906, SPAN.c1006, SPAN.c1106, SPAN.c1206, SPAN.c1306, SPAN.c1406, SPAN.c1506 {background-color: purple;}\n");
++ str_write(dst_file, "SPAN.c0007, SPAN.c0107, SPAN.c0207, SPAN.c0307, SPAN.c0407, SPAN.c0507, SPAN.c0607, SPAN.c0707, SPAN.c0807, SPAN.c0907, SPAN.c1007, SPAN.c1107, SPAN.c1207, SPAN.c1307, SPAN.c1407, SPAN.c1507 {background-color: orange;}\n");
++ str_write(dst_file, "SPAN.c0008, SPAN.c0108, SPAN.c0208, SPAN.c0308, SPAN.c0408, SPAN.c0508, SPAN.c0608, SPAN.c0708, SPAN.c0808, SPAN.c0908, SPAN.c1008, SPAN.c1108, SPAN.c1208, SPAN.c1308, SPAN.c1408, SPAN.c1508 {background-color: yellow;}\n");
++ str_write(dst_file, "SPAN.c0009, SPAN.c0109, SPAN.c0209, SPAN.c0309, SPAN.c0409, SPAN.c0509, SPAN.c0609, SPAN.c0709, SPAN.c0809, SPAN.c0909, SPAN.c1009, SPAN.c1109, SPAN.c1209, SPAN.c1309, SPAN.c1409, SPAN.c1509 {background-color: lime;}\n");
++ str_write(dst_file, "SPAN.c0010, SPAN.c0110, SPAN.c0210, SPAN.c0310, SPAN.c0410, SPAN.c0510, SPAN.c0610, SPAN.c0710, SPAN.c0810, SPAN.c0910, SPAN.c1010, SPAN.c1110, SPAN.c1210, SPAN.c1310, SPAN.c1410, SPAN.c1510 {background-color: teal;}\n");
++ str_write(dst_file, "SPAN.c0011, SPAN.c0111, SPAN.c0211, SPAN.c0311, SPAN.c0411, SPAN.c0511, SPAN.c0611, SPAN.c0711, SPAN.c0811, SPAN.c0911, SPAN.c1011, SPAN.c1111, SPAN.c1211, SPAN.c1311, SPAN.c1411, SPAN.c1511 {background-color: cyan;}\n");
++ str_write(dst_file, "SPAN.c0012, SPAN.c0112, SPAN.c0212, SPAN.c0312, SPAN.c0412, SPAN.c0512, SPAN.c0612, SPAN.c0712, SPAN.c0812, SPAN.c0912, SPAN.c1012, SPAN.c1112, SPAN.c1212, SPAN.c1312, SPAN.c1412, SPAN.c1512 {background-color: blue;}\n");
++ str_write(dst_file, "SPAN.c0013, SPAN.c0113, SPAN.c0213, SPAN.c0313, SPAN.c0413, SPAN.c0513, SPAN.c0613, SPAN.c0713, SPAN.c0813, SPAN.c0913, SPAN.c1013, SPAN.c1113, SPAN.c1213, SPAN.c1313, SPAN.c1413, SPAN.c1513 {background-color: magenta;}\n");
++ str_write(dst_file, "SPAN.c0014, SPAN.c0114, SPAN.c0214, SPAN.c0314, SPAN.c0414, SPAN.c0514, SPAN.c0614, SPAN.c0714, SPAN.c0814, SPAN.c0914, SPAN.c1014, SPAN.c1114, SPAN.c1214, SPAN.c1314, SPAN.c1414, SPAN.c1514 {background-color: silver;}\n");
++ str_write(dst_file, "SPAN.c0015, SPAN.c0115, SPAN.c0215, SPAN.c0315, SPAN.c0415, SPAN.c0515, SPAN.c0615, SPAN.c0715, SPAN.c0815, SPAN.c0915, SPAN.c1015, SPAN.c1114, SPAN.c1215, SPAN.c1315, SPAN.c1415, SPAN.c1515 {background-color: gray;}\n");
++ str_write(dst_file, "SPAN.c0000, SPAN.c0001, SPAN.c0002, SPAN.c0003, SPAN.c0004, SPAN.c0005, SPAN.c0006, SPAN.c0007, SPAN.c0008, SPAN.c0009, SPAN.c0010, SPAN.c0011, SPAN.c0012, SPAN.c0013, SPAN.c0014, SPAN.c0015 {color: white;}\n");
++ str_write(dst_file, "SPAN.c0100, SPAN.c0101, SPAN.c0102, SPAN.c0103, SPAN.c0104, SPAN.c0105, SPAN.c0106, SPAN.c0107, SPAN.c0108, SPAN.c0109, SPAN.c0110, SPAN.c0111, SPAN.c0112, SPAN.c0113, SPAN.c0114, SPAN.c0115 {color: black;}\n");
++ str_write(dst_file, "SPAN.c0200, SPAN.c0201, SPAN.c0202, SPAN.c0203, SPAN.c0204, SPAN.c0205, SPAN.c0206, SPAN.c0207, SPAN.c0208, SPAN.c0209, SPAN.c0210, SPAN.c0211, SPAN.c0212, SPAN.c0213, SPAN.c0214, SPAN.c0215 {color: navy;}\n");
++ str_write(dst_file, "SPAN.c0300, SPAN.c0301, SPAN.c0302, SPAN.c0303, SPAN.c0304, SPAN.c0305, SPAN.c0306, SPAN.c0307, SPAN.c0308, SPAN.c0309, SPAN.c0310, SPAN.c0311, SPAN.c0312, SPAN.c0313, SPAN.c0314, SPAN.c0315 {color: green;}\n");
++ str_write(dst_file, "SPAN.c0400, SPAN.c0401, SPAN.c0402, SPAN.c0403, SPAN.c0404, SPAN.c0405, SPAN.c0406, SPAN.c0407, SPAN.c0408, SPAN.c0409, SPAN.c0410, SPAN.c0411, SPAN.c0412, SPAN.c0413, SPAN.c0414, SPAN.c0415 {color: red;}\n");
++ str_write(dst_file, "SPAN.c0500, SPAN.c0501, SPAN.c0502, SPAN.c0503, SPAN.c0504, SPAN.c0505, SPAN.c0506, SPAN.c0507, SPAN.c0508, SPAN.c0509, SPAN.c0510, SPAN.c0511, SPAN.c0512, SPAN.c0513, SPAN.c0514, SPAN.c0515 {color: maroon;}\n");
++ str_write(dst_file, "SPAN.c0600, SPAN.c0601, SPAN.c0602, SPAN.c0603, SPAN.c0604, SPAN.c0605, SPAN.c0606, SPAN.c0607, SPAN.c0608, SPAN.c0609, SPAN.c0610, SPAN.c0611, SPAN.c0612, SPAN.c0613, SPAN.c0614, SPAN.c0615 {color: purple;}\n");
++ str_write(dst_file, "SPAN.c0700, SPAN.c0701, SPAN.c0702, SPAN.c0703, SPAN.c0704, SPAN.c0705, SPAN.c0706, SPAN.c0707, SPAN.c0708, SPAN.c0709, SPAN.c0710, SPAN.c0711, SPAN.c0712, SPAN.c0713, SPAN.c0714, SPAN.c0715 {color: orange;}\n");
++ str_write(dst_file, "SPAN.c0800, SPAN.c0801, SPAN.c0802, SPAN.c0803, SPAN.c0804, SPAN.c0805, SPAN.c0806, SPAN.c0807, SPAN.c0808, SPAN.c0809, SPAN.c0810, SPAN.c0811, SPAN.c0812, SPAN.c0813, SPAN.c0814, SPAN.c0815 {color: yellow;}\n");
++ str_write(dst_file, "SPAN.c0900, SPAN.c0901, SPAN.c0902, SPAN.c0903, SPAN.c0904, SPAN.c0905, SPAN.c0906, SPAN.c0907, SPAN.c0908, SPAN.c0909, SPAN.c0910, SPAN.c0911, SPAN.c0912, SPAN.c0913, SPAN.c0914, SPAN.c0915 {color: lime;}\n");
++ str_write(dst_file, "SPAN.c1000, SPAN.c1001, SPAN.c1002, SPAN.c1003, SPAN.c1004, SPAN.c1005, SPAN.c1006, SPAN.c1007, SPAN.c1008, SPAN.c1009, SPAN.c1010, SPAN.c1011, SPAN.c1012, SPAN.c1013, SPAN.c1014, SPAN.c1015 {color: teal;}\n");
++ str_write(dst_file, "SPAN.c1100, SPAN.c1101, SPAN.c1102, SPAN.c1103, SPAN.c1104, SPAN.c1105, SPAN.c1106, SPAN.c1107, SPAN.c1108, SPAN.c1109, SPAN.c1110, SPAN.c1111, SPAN.c1112, SPAN.c1113, SPAN.c1114, SPAN.c1115 {color: cyan;}\n");
++ str_write(dst_file, "SPAN.c1200, SPAN.c1201, SPAN.c1202, SPAN.c1203, SPAN.c1204, SPAN.c1205, SPAN.c1206, SPAN.c1207, SPAN.c1208, SPAN.c1209, SPAN.c1210, SPAN.c1211, SPAN.c1212, SPAN.c1213, SPAN.c1214, SPAN.c1215 {color: blue;}\n");
++ str_write(dst_file, "SPAN.c1300, SPAN.c1301, SPAN.c1302, SPAN.c1303, SPAN.c1304, SPAN.c1305, SPAN.c1306, SPAN.c1307, SPAN.c1308, SPAN.c1309, SPAN.c1310, SPAN.c1311, SPAN.c1312, SPAN.c1313, SPAN.c1314, SPAN.c1315 {color: magenta;}\n");
++ str_write(dst_file, "SPAN.c1400, SPAN.c1401, SPAN.c1402, SPAN.c1403, SPAN.c1404, SPAN.c1405, SPAN.c1406, SPAN.c1407, SPAN.c1408, SPAN.c1409, SPAN.c1410, SPAN.c1411, SPAN.c1412, SPAN.c1413, SPAN.c1414, SPAN.c1415 {color: silver;}\n");
++ str_write(dst_file, "SPAN.c1500, SPAN.c1501, SPAN.c1502, SPAN.c1503, SPAN.c1504, SPAN.c1505, SPAN.c1506, SPAN.c1507, SPAN.c1508, SPAN.c1509, SPAN.c1510, SPAN.c1511, SPAN.c1512, SPAN.c1513, SPAN.c1514, SPAN.c1515 {color: gray;}\n");
++ str_write(dst_file, "SPAN.f00 {color: white;}\n");
++ str_write(dst_file, "SPAN.f01 {color: black;}\n");
++ str_write(dst_file, "SPAN.f02 {color: navy;}\n");
++ str_write(dst_file, "SPAN.f03 {color: green;}\n");
++ str_write(dst_file, "SPAN.f04 {color: red;}\n");
++ str_write(dst_file, "SPAN.f05 {color: maroon;}\n");
++ str_write(dst_file, "SPAN.f06 {color: purple;}\n");
++ str_write(dst_file, "SPAN.f07 {color: orange;}\n");
++ str_write(dst_file, "SPAN.f08 {color: yellow;}\n");
++ str_write(dst_file, "SPAN.f09 {color: lime;}\n");
++ str_write(dst_file, "SPAN.f10 {color: teal;}\n");
++ str_write(dst_file, "SPAN.f11 {color: cyan;}\n");
++ str_write(dst_file, "SPAN.f12 {color: blue;}\n");
++ str_write(dst_file, "SPAN.f13 {color: magenta;}\n");
++ str_write(dst_file, "SPAN.f14 {color: silver;}\n");
++ str_write(dst_file, "SPAN.f15 {color: gray;}\n");
++ str_write(dst_file, "SPAN.b00 {background-color: white;}\n");
++ str_write(dst_file, "SPAN.b01 {background-color: black;}\n");
++ str_write(dst_file, "SPAN.b02 {background-color: navy;}\n");
++ str_write(dst_file, "SPAN.b03 {background-color: green;}\n");
++ str_write(dst_file, "SPAN.b04 {background-color: red;}\n");
++ str_write(dst_file, "SPAN.b05 {background-color: maroon;}\n");
++ str_write(dst_file, "SPAN.b06 {background-color: purple;}\n");
++ str_write(dst_file, "SPAN.b07 {background-color: orange;}\n");
++ str_write(dst_file, "SPAN.b08 {background-color: yellow;}\n");
++ str_write(dst_file, "SPAN.b09 {background-color: lime;}\n");
++ str_write(dst_file, "SPAN.b10 {background-color: teal;}\n");
++ str_write(dst_file, "SPAN.b11 {background-color: cyan;}\n");
++ str_write(dst_file, "SPAN.b12 {background-color: blue;}\n");
++ str_write(dst_file, "SPAN.b13 {background-color: magenta;}\n");
++ str_write(dst_file, "SPAN.b14 {background-color: silver;}\n");
++ str_write(dst_file, "SPAN.b15 {background-color: gray;}\n");
++ fclose(dst_file);
++ }
++ /* Now write user CSS file */
++ egg_snprintf(dst_filename, sizeof dst_filename, "%s%suser.css", ch->outputpath, SEP);
++ if ((dst_file = openfile(dst_filename, "wb", false)) != NULL) {
++ writefromexfile(dst_file, userstyle_filename);
++ fclose(dst_file);
++ }
++
++ if (!logfile_suffix[0])
++ egg_strftime(ct, 12, ".%d%b%Y", t);
++ else
++ egg_strftime(ct, 80, logfile_suffix, t);
++ ct[80] = '\0';
++
++ egg_snprintf(src_filename, sizeof src_filename, "%s%s", ch->logfilename, ct);
++ if ((src_file = openfile(src_filename, "r", true)) == NULL) return;
++
++ while(!feof(src_file)) {
++ lines_count = lines_per_page; pages_count++;
++
++ egg_snprintf(dst_filename, sizeof dst_filename, "%s%s%s%d%02d%02d_pg%d.html", ch->outputpath, SEP, ch->logspagename, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, pages_count);
++ if ((dst_file = openfile(dst_filename, "wb", false)) == NULL) {
++ fclose(src_file);
++ putlog(LOG_MISC, "*", "logs2html: Error occured on converting %d page of file \"%s\"!", pages_count, dst_filename);
++ return;
++ }
++
++ str_write(dst_file, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n");
++ str_write(dst_file, "<html>\n");
++ str_write(dst_file, "<head>\n");
++ if (strlen(encoding_string) > 0) {
++ str_write(dst_file, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n", encoding_string);
++ }
++ str_write(dst_file, "<meta name=\"title\" content=\"%s\">\n", ch->logspagetitle);
++ str_write(dst_file, "<meta name=\"Description\" content=\"%s\">\n", ch->logspagetitle);
++ str_write(dst_file, "<meta name=\"Generator\" content=\"logs2html module for Eggdrop v.%d.%d.%d\">\n", MODULE_MAJOR, MODULE_MINOR, MODULE_SUBMINOR);
++ str_write(dst_file, "<meta name=\"Author\" content=\"Fedotov Alexander aka Gray_Angel\">\n");
++ str_write(dst_file, "<meta name=\"Keywords\" content=\"logs, logging, channel, irc, bot, eggdrop, windrop, module, logs2html\">\n");
++ str_write(dst_file, "<meta name=\"robots\" content= \"index,all\">\n");
++ str_write(dst_file, "<link rel=stylesheet type=\"text/css\" href=\"default.css\">\n");
++ if (strlen(userstyle_filename) > 0) str_write(dst_file, "<link rel=stylesheet type=\"text/css\" href=\"user.css\">\n");
++ str_write(dst_file, "<title>%s. %d/%d/%d</title>\n", ch->logspagetitle, t->tm_mday, t->tm_mon + 1, t->tm_year + 1900);
++ str_write(dst_file, "</head>\n");
++ str_write(dst_file, "<body class=\"logspage\">\n");
++
++ writefromexfile(dst_file, logspage_top_filename);
++
++ str_write(dst_file, "<a name=\"top\"></a>\n");
++ str_write(dst_file, "<div id=\"navtop\"><a href=\"%s.html\">%s</a></div>\n", ch->mainpagename, LOGS2HTML_MAINPAGE);
++ str_write(dst_file, "<div id=\"log\">\n");
++
++ while ((lines_count > 0) || (lines_per_page == 0)) {
++ lines_count--;
++
++ buf_ptr = fgets(buffer, sizeof buffer, src_file);
++ if (buf_ptr == NULL) break;
++
++ p = strrchr(buf_ptr, '\n');
++ if (p) *p = '\0';
++ p = strrchr(buf_ptr, '\r');
++ if (p) *p = '\0';
++ if (!buf_ptr[0]) continue;
++
++ /* if timestamp exist cut time from string */
++ data[0] = '\0';
++ if (shtime) {
++ egg_strftime(stamp, sizeof(stamp) - 1, LOG_TS, t); /* Print dummy time */
++ tsl = strlen(stamp);
++ strncat(data, buf_ptr, tsl);
++ buf_ptr += (++tsl);
++ }
++ if (strncmp(buf_ptr, "--- ", 4) == 0) { /* we don't really need this string I think */
++ continue;
++ }
++ if (data && data[0]) {
++ str_write(dst_file, "<span class=\"time\">%s</span>", data);
++ }
++
++ data[0] = '\0'; noneedtoclose = false; skipemail = false;
++ if (strncmp(buf_ptr, "Action: ", 8) == 0) { /* command: /me */
++ buf_ptr += 7;
++ str_write(dst_file, "<span class=\"action\"> ***");
++ } else { /* nick */
++ p = strstr(buf_ptr, "> ");
++ if ((strncmp(buf_ptr, "<", 1) == 0) && (p != NULL)) {
++ buf_ptr++;
++ strncat(data, buf_ptr, p - buf_ptr);
++ str_write(dst_file, "<span class=\"nick\"> &lt;%s&gt;</span>", data);
++ buf_ptr += (p - buf_ptr + 1);
++ noneedtoclose = true;
++ } else {
++ str_write(dst_file, "<span class=\"else\"> ");
++ skipemail = true;
++ }
++ }
++
++ cuted_string_len = strlen(buf_ptr) + 2;
++ original_idx = (int *)nmalloc(sizeof(int) * (cuted_string_len + 1));
++ for (i = 0; i < cuted_string_len; i++) original_idx[i] = i - 1; original_idx[cuted_string_len] = -1;
++
++ cuted_string = (char *)nmalloc(cuted_string_len + 1);
++ egg_memset(cuted_string, 0, cuted_string_len + 1);
++ snprintf(cuted_string, cuted_string_len + 1, " %s ", buf_ptr);
++ q = r = cuted_string + 1;
++ /*
++ * Code copied from Eggdrop's src/dcc.c and then modified...
++ * Copyright (C) 1997 Robey Pointer
++ * Copyright (C) 1999, 2000, 2001, 2002, 2003 Eggheads Development Team
++ */
++ while (*r) {
++ switch (*r) {
++ case 0xf:
++ case 3: /* mIRC colors? */
++ if (isdigit(r[1])) { /* Is the first char a number? */
++ r += 2; /* Skip over the ^C and the first digit */
++ if (isdigit(*r)) r++; /* Is this a double digit number? */
++ if (*r == ',') { /* Do we have a background color next? */
++ if (isdigit(r[1])) r += 2; /* Skip over the first background digit */
++ if (isdigit(*r)) r++; /* Is it a double digit? */
++ }
++ } else {
++ r++;
++ }
++ continue;
++ case 2: /* Bold text */
++ case 7:
++ case 0x16: /* Reverse video */
++ case 0x1f: /* Underlined text */
++ case 0x3c: /* < */
++ case 0x3e: /* > */
++ r++;
++ continue;
++ case 033:
++ r++;
++ if (*r == '[') {
++ r++;
++ while ((*r == ';') || isdigit(*r)) r++;
++ if (*r) r++; /* also kill the following char */
++ }
++ continue;
++ }
++ original_idx[q - cuted_string] = r - cuted_string - 1;
++ *q++ = *r++; /* Move on to the next char */
++ }
++ *q = '\0';
++ /*
++ * Ends here...
++ */
++
++ linkfound = false;
++ q = cuted_string;
++ while (*q) {
++ for (k = 0; k < MIN(sizeof openspace, sizeof closespace); k++) {
++ if (openspace[k] == *q) {
++ r = q + 1;
++ while (*r) {
++ if (closespace[k] == *r) {
++ *r = '\0';
++ p = q + 1;
++ switch (whatisit(p)) {
++ case ITS_NOTHING:
++ break;
++ case ITS_EMAIL:
++ if (!skipemail) { /* If class = "else", lets skip mail, because it ident@host */
++ egg_memset(data, 0, sizeof data);
++ sprintf(data, "<a href=\"mailto:%s\">", p);
++ linkfound = true;
++ }
++ break;
++ case ITS_LINK:
++ egg_memset(data, 0, sizeof data);
++ sprintf(data, "<a href=\"%s\">", p);
++ linkfound = true;
++ break;
++ case ITS_TRUNKLINK:
++ egg_memset(data, 0, sizeof data);
++ sprintf(data, "<a href=\"http://%s\">", p);
++ linkfound = true;
++ break;
++ }
++ *r = closespace[k];
++ if (linkfound) {
++ /* will after reallocation r & q pointers be aviable?
++ * i.e. will cuted_string begin with the same adress?
++ * to make them aviable let's count offset.
++ */
++ r_offset = r - cuted_string; q_offset = q - cuted_string;
++ old_cuted_string_len = strlen(cuted_string); delta_cuted_string_len = strlen(data) + strlen(T_LINKC);
++ cuted_string = (char *)nrealloc(cuted_string, old_cuted_string_len + delta_cuted_string_len + 1);
++ original_idx = (int *)nrealloc(original_idx, sizeof(int) * (old_cuted_string_len + delta_cuted_string_len + 1));
++ r = cuted_string + r_offset; q = cuted_string + q_offset;
++ for (i = old_cuted_string_len; i < (old_cuted_string_len + delta_cuted_string_len + 1); i++) {
++ original_idx[i] = -1; cuted_string[i] = '\0';
++ }
++ for (i = old_cuted_string_len - 1; i >= r_offset; i--) {
++ cuted_string[i + delta_cuted_string_len] = cuted_string[i];
++ original_idx[i + delta_cuted_string_len] = original_idx[i]; original_idx[i] = -1;
++ }
++ for (i = strlen(T_LINKC); i--; ) {
++ cuted_string[r_offset + delta_cuted_string_len - strlen(T_LINKC) + i] = T_LINKC[i];
++ }
++ for (i = r_offset - 1; i > q_offset; i--) {
++ cuted_string[i + delta_cuted_string_len - strlen(T_LINKC)] = cuted_string[i];
++ original_idx[i + delta_cuted_string_len - strlen(T_LINKC)] = original_idx[i]; original_idx[i] = -1;
++ }
++ for (i = 0; i < strlen(data); i++) {
++ cuted_string[q_offset + i + 1] = data[i];
++ original_idx[q_offset + i + 1] = -1;
++ }
++ }
++ if (linkfound) q = r - 1;
++ break;
++ }
++ r++;
++ }
++ }
++ if (linkfound) {
++ linkfound = false;
++ break;
++ }
++ }
++ q++;
++ }
++
++ cuted_string[strlen(cuted_string) - 1] = '\0';
++ egg_memset(data, 0, sizeof data);
++ bold_isopen = false; underline_isopen = false;
++ p = cuted_string + 1; /* Don't need first char cause it = ' ' */
++ q = buf_ptr;
++ while (*p) {
++ if ((original_idx[p - cuted_string] == -1) || (original_idx[p - cuted_string] == (q - buf_ptr))) {
++ if ((strlen(data) + 1) > (sizeof data - 1)) {
++ str_write(dst_file, "%s", data);
++ egg_memset(data, 0, sizeof data);
++ }
++ strncat(data, p, 1);
++ p++;
++ if (original_idx[p - cuted_string] != -1) q++;
++ } else {
++ /*
++ * Code copied from Eggdrop's src/dcc.c and then modified...
++ * Copyright (C) 1997 Robey Pointer
++ * Copyright (C) 1999, 2000, 2001, 2002, 2003 Eggheads Development Team
++ */
++ switch (*q) {
++ case 2: /* Bold text */
++ if (bold_isopen) {
++ bold_isopen = false;
++ str_write(dst_file, "%s%s", data, T_BOLDC);
++ egg_memset(data, 0, sizeof data);
++ } else {
++ bold_isopen = true;
++ str_write(dst_file, "%s%s", data, T_BOLDO);
++ egg_memset(data, 0, sizeof data);
++ }
++ q++;
++ break;
++ case 3: /* mIRC colors? */
++ case 0xf: /* don't know, maybe broken client? but also mIRC colors */
++ egg_memset(f_color, 0, sizeof f_color); egg_memset(bg_color, 0, sizeof bg_color);
++ if (isdigit(q[1])) { /* Is the first char a number? */
++ f_color[0] = q[1];
++ if (isdigit(q[2])) {f_color[1] = q[2]; q++;}
++ q += 2;
++ if (*q == ',') {
++ if (isdigit(q[1])) { /* Is the first char a number? */
++ bg_color[0] = q[1];
++ if (isdigit(q[2])) {bg_color[1] = q[2]; q++;}
++ q += 2;
++ }
++ }
++ if (strlen(bg_color) > 0) {
++ /* If we have background color - let's close all previous "SPAN" */
++ for (mc_openteg_count++; --mc_openteg_count; ) {
++ if ((strlen(data) + strlen(T_SPANC)) > (sizeof data - 1)) {
++ str_write(dst_file, "%s", data);
++ egg_memset(data, 0, sizeof data);
++ }
++ strncat(data, T_SPANC, strlen(T_SPANC));
++ }
++ str_write(dst_file, "%s", data);
++ egg_memset(data, 0, sizeof data);
++ }
++
++ if ((strlen(f_color) > 0) && ((atoi(f_color) < 0) || (atoi(f_color) > 15))) {
++ egg_memset(f_color, 0, sizeof f_color);
++ }
++ if ((strlen(bg_color) > 0) && ((atoi(bg_color) < 0) || (atoi(bg_color) > 15))) {
++ egg_memset(bg_color, 0, sizeof bg_color);
++ }
++ if ((strlen(f_color) > 0) || (strlen(bg_color) > 0)) {
++ mc_openteg_count++;
++ text_style[0] = '\0';
++ if ((strlen(f_color) > 0) && (strlen(bg_color) > 0)) egg_snprintf(text_style, sizeof text_style, "c%02.2d%02.2d", atoi(f_color), atoi(bg_color));
++ if (strlen(bg_color) == 0) egg_snprintf(text_style, sizeof text_style, "f%02.2d", atoi(f_color));
++ if (strlen(f_color) == 0) egg_snprintf(text_style, sizeof text_style, "b%02.2d", atoi(bg_color));
++ str_write(dst_file, "%s<span class=\"%s\">", data, text_style);
++ egg_memset(data, 0, sizeof data);
++ }
++ } else {
++ /* It was "close-color" tag -> let's close all "SPAN" */
++ for (mc_openteg_count++; --mc_openteg_count; ) {
++ if ((strlen(data) + strlen(T_SPANC)) > (sizeof data - 1)) {
++ str_write(dst_file, "%s", data);
++ egg_memset(data, 0, sizeof data);
++ }
++ strncat(data, T_SPANC, strlen(T_SPANC));
++ }
++ str_write(dst_file, "%s", data);
++ egg_memset(data, 0, sizeof data);
++ q++;
++ }
++ break;
++ case 7: /* Bells */
++ q++;
++ break;
++ case 0x16: /* Reverse video */
++ q++;
++ break;
++ case 0x1f: /* Underlined text */
++ if (underline_isopen) {
++ underline_isopen = false;
++ str_write(dst_file, "%s%s", data, T_UNDERLINEC);
++ egg_memset(data, 0, sizeof data);
++ } else {
++ underline_isopen = true;
++ str_write(dst_file, "%s%s", data, T_UNDERLINEO);
++ egg_memset(data, 0, sizeof data);
++ }
++ q++;
++ break;
++ case 0x3c: /* < */
++ str_write(dst_file, "%s%s", data, T_LT);
++ egg_memset(data, 0, sizeof data);
++ q++;
++ break;
++ case 0x3e: /* > */
++ str_write(dst_file, "%s%s", data, T_GT);
++ egg_memset(data, 0, sizeof data);
++ q++;
++ break;
++ case 033:
++ q++;
++ if (*q == '[') {
++ q++;
++ while ((*q == ';') || isdigit(*q)) q++;
++ if (*q) q++; /* also kill the following char */
++ }
++ break;
++ default:
++ /* I think we should never get here, but if so... */
++ strncat(data, q, 1);
++ q++;
++ break;
++ }
++ /*
++ * Ends here...
++ */
++ }
++ }
++ str_write(dst_file, "%s", data);
++ egg_memset(data, 0, sizeof data);
++ if (bold_isopen) str_write(dst_file, "%s", T_BOLDC);
++ if (underline_isopen) str_write(dst_file, "%s", T_UNDERLINEC);
++ for (mc_openteg_count++; --mc_openteg_count; ) {
++ if ((strlen(data) + strlen(T_SPANC)) > (sizeof data - 1)) {
++ str_write(dst_file, "%s", data);
++ egg_memset(data, 0, sizeof data);
++ }
++ strncat(data, T_SPANC, strlen(T_SPANC));
++ }
++ str_write(dst_file, "%s", data);
++ egg_memset(data, 0, sizeof data);
++
++ nfree(original_idx); original_idx = NULL;
++ nfree(cuted_string); cuted_string = NULL;
++
++ str_write(dst_file, "%s", data); data[0] = '\0';
++ str_write(dst_file, noneedtoclose ? "<br />\n":"</span><br />\n");
++ }
++ fclose(dst_file);
++ }
++ fclose(src_file);
++
++ for (i = 1; i <= pages_count; i++) {
++ egg_snprintf(dst_filename, sizeof dst_filename, "%s%s%s%d%02d%02d_pg%d.html", ch->outputpath, SEP, ch->logspagename, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, i);
++ if ((dst_file = openfile(dst_filename, "ab", false)) == NULL) {
++ putlog(LOG_MISC, "*", "logs2html: Error occured on converting %d page of file \"%s\"!", i, dst_filename);
++ return;
++ }
++ str_write(dst_file, "</div>\n");
++ if (pages_count > 1) {
++ str_write(dst_file, "<div id=\"navbottom\">");
++ if (i == 1) {
++ str_write(dst_file, LOGS2HTML_BACK);
++ } else {
++ egg_snprintf(dst_filename, sizeof dst_filename, "%s%d%02d%02d_pg%d.html", ch->logspagename, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, i-1);
++ str_write(dst_file, "<span>&larr;</span>&nbsp;<a href=\"%s\">%s</a>", dst_filename, LOGS2HTML_BACK);
++ }
++ str_write(dst_file, "&nbsp;");
++ if (i == pages_count) {
++ str_write(dst_file, LOGS2HTML_NEXT);
++ } else {
++ egg_snprintf(dst_filename, sizeof dst_filename, "%s%d%02d%02d_pg%d.html", ch->logspagename, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, i+1);
++ str_write(dst_file, "<a href=\"%s\">%s</a>&nbsp;<span>&rarr;</span>", dst_filename, LOGS2HTML_NEXT);
++ }
++ str_write(dst_file, "<br />");
++ for (j = 1; j <= pages_count; j++) {
++ egg_snprintf(dst_filename, sizeof dst_filename, "%s%d%02d%02d_pg%d.html", ch->logspagename, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, j);
++ if (j != i) {
++ str_write(dst_file, "<span> <a href=\"%s\">%d</a> </span>", dst_filename, j);
++ } else {
++ str_write(dst_file, "<b>%d</b>", j);
++ }
++ }
++ str_write(dst_file, "</div>\n");
++ }
++
++ str_write(dst_file, "<div id=\"totop\"><a href=\"#top\">%s</a></div>\n", LOGS2HTML_UP);
++
++ writefromexfile(dst_file, logspage_bottom_filename);
++
++ str_write(dst_file, "<div id=\"footer\">\nGenerated by logs2html module for eggdrop v.%d.%d.%d<br />\n", MODULE_MAJOR, MODULE_MINOR, MODULE_SUBMINOR);
++ str_write(dst_file, "Find latest version at <a href=\"http://sourceforge.net/projects/logs2html\">http://sourceforge.net/projects/logs2html</a> or <a href=\"http://shmupsik.osetia.org\">http://shmupsik.osetia.org</a>\n</div>\n");
++
++ str_write(dst_file, "</body>\n");
++ str_write(dst_file, "</html>");
++
++ fclose(dst_file);
++ }
++
++ return;
++} /* convertfile() */
++/****************************************************************************/
++
++
++/****************************************************************************/
++/*
++ * function void makemainpage(logs2htmlchan *ch)
++ *
++ * Input:
++ * ничего
++ *
++ * Output:
++ * ничего
++ *
++ * Discription:
++ * Генерация основной страницы (календаря)
++ * + вызывается процедура для конвертирования логов в формат HTML
++ */
++static void makemainpage(logs2htmlchan *ch) {
++ int i, j, k, m, l;
++ int add_day;
++ FILE *file;
++ char filename[256];
++ bool endofyear;
++ struct tm tblock;
++ int delta_day = 0;
++ time_t t = time(NULL);
++ struct tm *st = localtime(&t);
++
++ tblock.tm_year = st->tm_year;
++ tblock.tm_isdst = st->tm_isdst;
++ tblock.tm_hour = 0;
++ tblock.tm_min = 0;
++ tblock.tm_sec = 1;
++ /* Other fields not necessary here
++ tblock.tm_mday = st->tm_mday;
++ tblock.tm_mon = st->tm_mon;
++ tblock.tm_wday = st->tm_wday;
++ tblock.tm_yday = st->tm_yday;
++ */
++
++ egg_snprintf(filename, sizeof filename, "%s%s%s.html", ch->outputpath, SEP, ch->mainpagename);
++ if ((file = openfile(filename, "wb", false)) == NULL) return;
++
++ str_write(file, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n");
++ str_write(file, "<html>\n");
++ str_write(file, "<head>\n");
++ if (strlen(encoding_string) > 0) {
++ str_write(file, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n", encoding_string);
++ }
++ str_write(file, "<meta name=\"title\" content=\"%s\">\n", ch->mainpagetitle);
++ str_write(file, "<meta name=\"Description\" content=\"%s\">\n", ch->mainpagetitle);
++ str_write(file, "<meta name=\"Generator\" content=\"logs2html module for Eggdrop v.%d.%d.%d\">\n", MODULE_MAJOR, MODULE_MINOR, MODULE_SUBMINOR);
++ str_write(file, "<meta name=\"Author\" content=\"Fedotov Alexander aka Gray_Angel\">\n");
++ str_write(file, "<meta name=\"Keywords\" content=\"logs, logging, channel, irc, bot, eggdrop, windrop, module, logs2html\">\n");
++ str_write(file, "<meta name=\"robots\" content= \"index,all\">\n");
++ str_write(file, "<link rel=stylesheet type=\"text/css\" href=\"default.css\">\n");
++ if (strlen(userstyle_filename) > 0) str_write(file, "<link rel=stylesheet type=\"text/css\" href=\"user.css\">\n");
++ str_write(file, "<title>%s %d %s</title>\n", ch->mainpagetitle, tblock.tm_year + 1900, LOGS2HTML_YEAR);
++ str_write(file, "</head>\n");
++ str_write(file, "<body class=\"mainpage\">\n");
++
++ writefromexfile(file, mainpage_top_filename);
++
++ str_write(file, "<div id=\"title\">%s<br />%d %s</div>\n", ch->mainpagetitle, tblock.tm_year + 1900, LOGS2HTML_YEAR);
++ str_write(file, "<div id=\"calendar\">\n");
++
++
++ /* Вычисляем сколько рядов потребуется для размещения 12 месяцев при заданном числе столбцов */
++ month_rows_count = (int)ceil(12.0 / (double)month_cols_count);
++
++ str_write(file, "<table width=\"100%%\" cellspacing=\"2\" cellpadding=\"1\" border=\"0\">\n");
++
++ /* цикл по рядам месяцев */
++ for(i = 0; i < month_rows_count; i++) {
++
++ /* в каждом ряду: 1 строка (название месяца) + 7 строк (дни недели) */
++ for (j = 0; j < 8; j++) {
++ str_write(file, "<tr align=center>\n");
++
++ /* цикл по столбцам месяцев */
++ for (k = 0; k < month_cols_count; k++) {
++ tblock.tm_mon = i * month_cols_count + k;
++ endofyear = (tblock.tm_mon > 11 ) ? true : false;
++
++ if (j == 0) {
++ str_write(file, "<td class=\"space\"></td>");
++ str_write(file, "<th colspan=7>%s</th>\n", endofyear ? "&nbsp;": month_names[tblock.tm_mon]);
++ continue;
++ }
++
++ /* Если год високосный, то добавляем к февралю 1 день */
++ add_day = ((tblock.tm_mon == 1) && isleap(tblock.tm_year)) ? 1 : 0;
++
++ /* Для данного месяца данного года вычисляем на какой день недели приходится 1 число */
++ if (!endofyear) {
++ delta_day = getdayofweek(tblock.tm_year + 1900, tblock.tm_mon + 1, 1);
++ }
++
++ /* в каждом месяце: 2 столбца (название дней недели) + 6 столбцов (недели) */
++ for (m = 0; m < 7; m++) {
++ if (endofyear) {
++ str_write(file, "<td>&nbsp;</td>\n");
++ continue;
++ }
++ if (m == 0) {
++ str_write(file, "<td class=\"space\"></td>");
++ switch (j)
++ {
++ case 1:
++ case 2:
++ case 3:
++ case 4:
++ case 5:
++ case 6:
++ str_write(file, "<td class=\"dayname\">%s</td>\n", days_names[j-1]);
++ break;
++ case 7:
++ str_write(file, "<td class=\"weekend\">%s</td>\n", days_names[j-1]);
++ break;
++ default:
++ /* Uuups.. We should never get to this point.. and if it so, that mean's something wrong...:( */
++ str_write(file, "<td>&nbsp;</td>\n");
++ }
++ continue;
++ }
++ tblock.tm_mday = (m - 1) * 7 + j - delta_day;
++ if ((tblock.tm_mday >= 1) && (tblock.tm_mday <= (days_in_month[tblock.tm_mon] + add_day))) {
++ tblock.tm_wday = (getdayofweek(tblock.tm_year + 1900, tblock.tm_mon + 1, tblock.tm_mday) + 1) % 7;
++ tblock.tm_yday = 0;
++ for (l = 0; l < tblock.tm_mon; l++) tblock.tm_yday += days_in_month[l];
++ tblock.tm_yday += tblock.tm_mday - 1;
++
++ egg_snprintf(filename, sizeof filename, "%s%s%s%d%02d%02d_pg%d.html", ch->outputpath, SEP, ch->logspagename, tblock.tm_year + 1900, tblock.tm_mon + 1, tblock.tm_mday, 1);
++ if (!file_readable(filename)) convertfile(&tblock, ch);
++ if (file_readable(filename)) {
++ /* let write withount full path */
++ egg_snprintf(filename, sizeof filename, "%s%d%02d%02d_pg%d.html", ch->logspagename, tblock.tm_year + 1900, tblock.tm_mon + 1, tblock.tm_mday, 1);
++ str_write(file, "<td><a href=\"%s\">%d</A></td>\n", filename, tblock.tm_mday);
++ } else {
++ str_write(file, "<td>%d</td>\n", tblock.tm_mday);
++ }
++ } else {
++ str_write(file, "<td>&nbsp;</td>\n");
++ }
++ }
++ }
++ str_write(file, "</tr>\n");
++ }
++ str_write(file, "<tr><td class=\"space\" colspan=%d>&nbsp;</td></tr>\n", month_cols_count * 8);
++ }
++
++ str_write(file, "</table>\n");
++ str_write(file, "</div>\n");
++
++ writefromexfile(file, mainpage_bottom_filename);
++
++ str_write(file, "<div id=\"footer\">\nGenerated by logs2html module for eggdrop v.%d.%d.%d<br />\n", MODULE_MAJOR, MODULE_MINOR, MODULE_SUBMINOR);
++ str_write(file, "Find latest version at <a href=\"http://sourceforge.net/projects/logs2html\">http://sourceforge.net/projects/logs2html</a> or <a href=\"http://shmupsik.osetia.org\">http://shmupsik.osetia.org</a>\n</div>\n");
++
++ str_write(file, "</body>\n");
++ str_write(file, "</html>");
++
++ fclose(file);
++
++ return;
++} /* makemainpage() */
++/****************************************************************************/
++
++
++/****************************************************************************/
++static void logs2html_hook_5minutely(void)
++{
++ logs2htmlchan *p;
++ struct tm tblock;
++ time_t t = time(NULL);
++ struct tm *st = localtime(&t);
++
++ tblock.tm_year = st->tm_year;
++ tblock.tm_isdst = st->tm_isdst;
++ tblock.tm_mday = st->tm_mday;
++ tblock.tm_mon = st->tm_mon;
++ tblock.tm_wday = st->tm_wday;
++ tblock.tm_yday = st->tm_yday;
++ tblock.tm_hour = 0;
++ tblock.tm_min = 0;
++ tblock.tm_sec = 1;
++
++ p = logs2htmlchanlist;
++ while (p != NULL) {
++ convertfile(&tblock, p);
++ makemainpage(p);
++ p = p->next;
++ }
++
++ return;
++}
++/****************************************************************************/
++
++/****************************************************************************/
++static void logs2html_hook_daily(void)
++{
++ logs2htmlchan *p;
++ struct tm tblock;
++ time_t t = time(NULL);
++ struct tm *st = localtime(&t);
++
++ tblock.tm_year = st->tm_year;
++ tblock.tm_isdst = st->tm_isdst;
++ tblock.tm_mday = st->tm_mday;
++ tblock.tm_mon = st->tm_mon;
++ tblock.tm_wday = st->tm_wday;
++ tblock.tm_yday = st->tm_yday;
++ tblock.tm_hour = 0;
++ tblock.tm_min = 0;
++ tblock.tm_sec = 1;
++
++ if (!--tblock.tm_mday) {
++ if (!tblock.tm_mon) {
++ /* it's 1st January; lets go one day before */
++ tblock.tm_year--;
++ tblock.tm_mon = 11;
++ tblock.tm_mday = 31;
++ tblock.tm_yday = 364 + isleap(tblock.tm_year) ? 1 : 0;
++ } else {
++ tblock.tm_mon--;
++ tblock.tm_mday = days_in_month[tblock.tm_mon] + ((tblock.tm_mon == 1) && isleap(tblock.tm_year)) ? 1 : 0;
++ tblock.tm_yday--;
++ }
++ } else {
++ tblock.tm_yday--;
++ }
++ tblock.tm_wday = (tblock.tm_wday + 6) % 7;
++
++ p = logs2htmlchanlist;
++ while (p != NULL) {
++ convertfile(&tblock, p);
++ makemainpage(p);
++ p = p->next;
++ }
++
++ return;
++}
++/****************************************************************************/
++
++
++/****************************************************************************/
++static void logs2html_hook_pre_rehash(void)
++{
++ logs2htmlchan *p, *q;
++
++ q = p = logs2htmlchanlist;
++ while (q != NULL) {
++ q = p->next;
++ nfree(p);
++ p = q;
++ }
++ logs2htmlchanlist = p = q = NULL;
++
++ return;
++}
++/****************************************************************************/
++
++/****************************************************************************/
++static void logs2html_hook_rehash(void)
++{
++ if (lines_per_page < 0) lines_per_page = 0;
++
++ return;
++}
++/****************************************************************************/
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/logs2html.conf eggdrop1.6.19/src/mod/logs2html.mod/logs2html.conf
+--- src/mod/logs2html.mod/logs2html.conf 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/logs2html.conf 2009-03-28 01:43:58.000000000 +0000
+@@ -0,0 +1,38 @@
++######
++#####
++### General Settings
++#####
++######
++
++# number of rows the month'll put out
++set col-count 3
++
++# set how many lines there'll be on one page of converted logfile. If it
++# set to 0 then all converted log will dispalay as one page.
++set lines-per-page 200
++
++# Here specify file with "addlogs2html" expressions.
++set channels-list chan.list
++
++# Uncomment line if you want specify file with your color settings for
++# generated pages
++#set user-style user.css
++
++# This setting allows you to insert meta tag
++# <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=..."> in your
++# HTML files. You can comment next line, that will mean that this tag'll not
++# be insert in the resulting page.
++set insert-encoding-str "windows-1251"
++
++# Here you can specify path to files which content will be put to converted
++# logfile (or mainpage). Uncomment the line you need and put there
++# filename with path for example: set mainpage-top "top100.tpl"
++# Note: the content of specified file puts to the resulting file "as is",
++# without any transformation
++#set mainpage-top ""
++#set mainpage-bottom ""
++#set logspage-top ""
++#set logspage-bottom ""
++
++# now load the module
++loadmodule logs2html
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/logs2html.h eggdrop1.6.19/src/mod/logs2html.mod/logs2html.h
+--- src/mod/logs2html.mod/logs2html.h 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/logs2html.h 2009-03-28 01:46:28.000000000 +0000
+@@ -0,0 +1,76 @@
++/*
++ * logs2html.h -- part of logs2html.mod
++ *
++ * Written by Fedotov Alexander aka Gray_Angel aka Shmupsik <shurikvz@mail.ru>
++ *
++ * 2004-2005 year
++ */
++/*
++ * 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 "src/lang.h"
++
++#define SEP "/"
++
++#undef false
++#undef true
++typedef enum {false = 0, true} bool;
++
++#ifdef MIN
++# undef MIN
++#endif
++#define MIN(p,q) ((p < q) ? p : q)
++
++#ifndef isleap
++ /* Nonzero if YEAR is a leap year (every 4 years,
++ except every 100th isn't, and every 400th is). */
++#define isleap(year) \
++ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
++#endif
++
++#define T_LT "&lt;"
++#define T_GT "&gt;"
++#define T_BOLDO "<B>"
++#define T_BOLDC "</B>"
++#define T_UNDERLINEO "<U>"
++#define T_UNDERLINEC "</U>"
++#define T_SPANC "</SPAN>"
++#define T_LINKC "</A>"
++
++typedef struct logs2html_data {
++ struct logs2html_data *next;
++ char channame[81]; /* Имя канала для которого конвертировать логи */
++ char logfilename[128]; /* Имя (префикс) данного канала (вычисляется из выражения logfile в конфиге бота) */
++ char outputpath[128]; /* Куда выводить конвертированные логи */
++ char mainpagename[61]; /* Имя главной страницы */
++ char mainpagetitle[256]; /* Заголовок на главной странице */
++ char logspagename[61]; /* Имя (префикс) страницы переконвертированного логфайла */
++ char logspagetitle[256]; /* Заголовок страницы логфайла */
++} logs2htmlchan;
++
++typedef enum {ITS_NOTHING, ITS_EMAIL, ITS_LINK, ITS_TRUNKLINK} patternkind;
++
++static int cmd_convertalllogs(struct userrec *u, int idx, char *par);
++static int cmd_makemainpage(struct userrec *u, int idx, char *par);
++static int getdayofweek(int year, int month, int day);
++static FILE *openfile(char *newfilename, const char *mode, bool silent);
++static void str_write(FILE *file, char *fstr, ... );
++static void makemainpage(logs2htmlchan *ch);
++static void convertfile(struct tm *t, logs2htmlchan *ch);
++static void writefromexfile(FILE *dst_file, char *exfilename);
++static int addchannels(void);
++static patternkind whatisit(char *pattern);
++
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/logs2html.mod.desc eggdrop1.6.19/src/mod/logs2html.mod/logs2html.mod.desc
+--- src/mod/logs2html.mod/logs2html.mod.desc 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/logs2html.mod.desc 2009-03-28 01:46:37.000000000 +0000
+@@ -0,0 +1 @@
++This module convert all existing log files (and those, that will be created after module installation) of your eggdrop for givving channel to their html representation to be show in the web. and also the calendar page with links to that converted files.
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/modinfo eggdrop1.6.19/src/mod/logs2html.mod/modinfo
+--- src/mod/logs2html.mod/modinfo 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/modinfo 2009-03-28 01:32:21.000000000 +0000
+@@ -0,0 +1,4 @@
++DESC:This module convert all existing log files (and those, that will be
++DESC:created after module installation) of your eggdrop for givving channel
++DESC:to their html representation to be show in the web. and also the
++DESC:calendar page with links to that converted files.
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/readme.txt eggdrop1.6.19/src/mod/logs2html.mod/readme.txt
+--- src/mod/logs2html.mod/readme.txt 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/readme.txt 2009-03-28 01:44:20.000000000 +0000
+@@ -0,0 +1,85 @@
++logs2html.mod v.2.3.4 by Alexander Fedotov aka Gray_Angel aka Shmupsik #ircnet.ru
++e-mail: shurikvz@mail.ru
++
++This module convert all existing log files (and those, that will be created after module installation) of your eggdrop for giving channels to their html representation to be show in the web. and also the calendar page with links to that converted files.
++
++*******************************************************************************
++To install this module:
++ - copy the contents of "language" directory to your eggdrops "language" directory
++ - copy the contents of "help" directory to your eggdrops "help" directory
++ - copy *.dll (for windows) or *.so (for unix/linux) module to your eggdrops "modules" directory (only if you have download an arhive file with precompiled modules)
++ - copy logs2html.conf, chan.list files to your eggdrop directory
++ - if you decide to use your own style for pages copy user.css file to your eggdrop directory
++ - (Of course don't forget to edit logs2html.conf, chan.list and user.css files)
++ - put "source logs2html.conf" into your eggdrop config file and restart your bot
++
++Module has 2 commands, which you can use from dcc chat:
++ - convertalllogs - reconverts ALL your logs for current year (you'll need it for example if you decided to change style of your output logs even those which was created before your decision)
++ - makemainpage - redraws mainpages
++*******************************************************************************
++
++
++
++*******************************************************************************
++You can see the example of output on http://www.osetia.org/logs
++You can try to find latest version on http://sourceforge.net/projects/logs2html or http://shmupsik.osetia.org
++*******************************************************************************
++
++
++
++*******************************************************************************
++So, if you want to make a donation (for some strange reason I probably will never understand...) to me,
++you can do it using WebMoney (http://www.webmoney.ru).
++And here (http://webmoney.ru/wminouttrans1.shtml) a list of sites where you can exchange your money if you are not using WebMoney system.. :)
++
++My cash number: WMID:215902813411
++ U838552550724
++ E023251651728
++ R735356625383
++*******************************************************************************
++
++
++
++*******************************************************************************
++Version history:
++15.10.05 - version 2.3.4
++ - added setting in config file, which allow to change value of meta tag <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=..."> in generated HTML files.
++
++
++04.10.05 - version 2.3.3
++ - fixed bug with broken e-mail (strings with broken e-mails were truncated)
++
++
++ 24.09.05 - version 2.3.2
++ - added support for <B> and <U> tags
++ - fixed bug with [%H:%M:%S] timestamp (I hope.. :-))
++
++
++ 26.07.05 - version 2.3.1
++ - bug fixed
++
++
++ 24.07.05 - version 2.3.0
++ - page style discriptions moved to CSS file
++ - no need for tcl now
++ - small bugs fixed
++
++
++ 25.04.05 - version 2.1.0
++ - added support for http links and e-mail.
++
++
++ 27.02.05 - version 2.0
++ - converting logs for multiply channels
++ - possibility to change colors of HTML pages
++ - now you can aim number of lines on the one generated HTML page
++
++
++ 16.12.04 - version 1.0
++ - initial realise
++*******************************************************************************
++
++
++
++P.S. Sorry for my bad english
++
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/top100.tpl eggdrop1.6.19/src/mod/logs2html.mod/top100.tpl
+--- src/mod/logs2html.mod/top100.tpl 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/top100.tpl 2009-03-28 01:43:24.000000000 +0000
+@@ -0,0 +1,50 @@
++
++<p align="center">
++<!--Rating@Mail.ru COUNTER--><script language="JavaScript"><!--
++d=document;a='';a+=';r='+escape(d.referrer)
++js=10//--></script><script language="JavaScript1.1"><!--
++a+=';j='+navigator.javaEnabled()
++js=11//--></script><script language="JavaScript1.2"><!--
++s=screen;a+=';s='+s.width+'*'+s.height
++a+=';d='+(s.colorDepth?s.colorDepth:s.pixelDepth)
++js=12//--></script><script language="JavaScript1.3"><!--
++js=13//--></script><script language="JavaScript"><!--
++d.write('<img src="http://top.list.ru/counter'+'?id=606627;js='+js+a+';rand='+Math.random()+'" height=1 width=1>')
++if(js>11)d.write('<'+'!-- ')//--></script><noscript><img
++src="http://top.list.ru/counter?js=na;id=606627"
++height=1 width=1 alt=""></noscript><script language="JavaScript"><!--
++if(js>11)d.write('--'+'>')//--></script><!--/COUNTER-->
++<!-- SpyLOG f:0210 -->
++<script language="javascript"><!--
++Mu="u5131.64.spylog.com";Md=document;Mnv=navigator;Mp=1;
++Mn=(Mnv.appName.substring(0,2)=="Mi")?0:1;Mrn=Math.random();
++Mt=(new Date()).getTimezoneOffset();
++Mz="p="+Mp+"&rn="+Mrn+"&tl=0&ls=0&ln=0&t="+Mt;
++My="";
++My+="<img src='http://"+Mu+"/cnt?cid=513164&"+Mz+"&r="+escape(Md.referrer)+"&pg="+escape(window.location.href)+"'border=0 width=1 height=1 alt='SpyLOG'>";
++Md.write(My);//--></script><noscript>
++<img src="http://u5131.64.spylog.com/cnt?cid=513164&p=1" alt='SpyLOG' border='0' width=1
++height=1 >
++</noscript>
++<!-- SpyLOG -->
++<!--begin of Top100 logo-->
++<a href="http://top100.rambler.ru/top100/">
++<img src="http://top100-images.rambler.ru/top100/banner-88x31-rambler-blue.gif" alt="Rambler's Top100" width=88 height=31 border=0></a>
++<!--end of Top100 logo -->
++<script language="javascript"><!--
++Mrn=Math.random();Mz="";
++My="<a href='http://u5131.64.spylog.com/cnt?cid=513164&f=3&rn="+Mrn+"' target='_blank'><img src='http://u5131.64.spylog.com/cnt?cid=513164&";
++My+="p=1&f=4&rn="+Mrn+Mz+"' border=0 width=88 height=31 alt='SpyLOG'></a>";Md.write(My);
++//--></script><noscript>
++<a href="http://u5131.64.spylog.com/cnt?cid=513164&f=3&p=1" target="_blank">
++<img src="http://u5131.64.spylog.com/cnt?cid=513164&p=1&f=4" alt='SpyLOG' border='0' width=88 height=31 ></a>
++</noscript>
++<!--Rating@Mail.ru LOGO--><a target=_top
++href="http://top.mail.ru/jump?from=606627"><img src="http://top.list.ru/counter?id=606627;t=231;l=1"
++border=0 height=31 width=88
++alt="Рейтинг@Mail.ru"></a><!--/LOGO-->
++<!--begin of Rambler's Top100 code -->
++<a href="http://top100.rambler.ru/top100/">
++<img src="http://counter.rambler.ru/top100.cnt?503447" alt="" width=1 height=1 border=0></a>
++<!--end of Top100 code-->
++</p>
+diff -urpN eggdrop1.6.19-orig/src/mod/logs2html.mod/user.css eggdrop1.6.19/src/mod/logs2html.mod/user.css
+--- src/mod/logs2html.mod/user.css 1970-01-01 00:00:00.000000000 +0000
++++ src/mod/logs2html.mod/user.css 2009-03-28 01:32:53.000000000 +0000
+@@ -0,0 +1,121 @@
++BODY {
++font-family: serif;
++font-style: normal
++font-variant: normal;
++font-weight: normal;
++font-stretch: normal;
++font-size: 12pt;
++text-align: left;
++color: rgb(0,0,0);
++background-color: transparent;
++}
++BODY.mainpage {
++background-color: whitesmoke;
++}
++BODY.logspage {
++background-color: lightyellow;
++}
++TD {
++width: 4%;
++background-color: lavender;
++}
++TD.space {
++width: auto;
++background-color: whitesmoke;
++}
++TD.dayname {
++width: auto;
++font-weight: bold;
++text-align: right;
++background-color: lightskyblue;
++}
++TD.weekend {
++color: red;
++width: auto;
++font-weight: bold;
++text-align: right;
++width: auto;
++background-color: lightskyblue;
++}
++TH {
++font-weight: bold;
++text-align: center;
++background-color: lavender;
++}
++SPAN.time {
++color: silver;
++}
++SPAN.nick {
++color: mediumblue;
++}
++SPAN.else {
++color: green;
++font-style: italic;
++}
++SPAN.action {
++color: violet;
++}
++SPAN.c0000, SPAN.c0100, SPAN.c0200, SPAN.c0300, SPAN.c0400, SPAN.c0500, SPAN.c0600, SPAN.c0700, SPAN.c0800, SPAN.c0900, SPAN.c1000, SPAN.c1100, SPAN.c1200, SPAN.c1300, SPAN.c1400, SPAN.c1500 {background-color: white;}
++SPAN.c0001, SPAN.c0101, SPAN.c0201, SPAN.c0301, SPAN.c0401, SPAN.c0501, SPAN.c0601, SPAN.c0701, SPAN.c0801, SPAN.c0901, SPAN.c1001, SPAN.c1101, SPAN.c1201, SPAN.c1301, SPAN.c1401, SPAN.c1501 {background-color: black;}
++SPAN.c0002, SPAN.c0102, SPAN.c0202, SPAN.c0302, SPAN.c0402, SPAN.c0502, SPAN.c0602, SPAN.c0702, SPAN.c0802, SPAN.c0902, SPAN.c1002, SPAN.c1102, SPAN.c1202, SPAN.c1302, SPAN.c1402, SPAN.c1502 {background-color: navy;}
++SPAN.c0003, SPAN.c0103, SPAN.c0203, SPAN.c0303, SPAN.c0403, SPAN.c0503, SPAN.c0603, SPAN.c0703, SPAN.c0803, SPAN.c0903, SPAN.c1003, SPAN.c1103, SPAN.c1203, SPAN.c1303, SPAN.c1403, SPAN.c1503 {background-color: green;}
++SPAN.c0004, SPAN.c0104, SPAN.c0204, SPAN.c0304, SPAN.c0404, SPAN.c0504, SPAN.c0604, SPAN.c0704, SPAN.c0804, SPAN.c0904, SPAN.c1004, SPAN.c1104, SPAN.c1204, SPAN.c1304, SPAN.c1404, SPAN.c1504 {background-color: red;}
++SPAN.c0005, SPAN.c0105, SPAN.c0205, SPAN.c0305, SPAN.c0405, SPAN.c0505, SPAN.c0605, SPAN.c0705, SPAN.c0805, SPAN.c0905, SPAN.c1005, SPAN.c1105, SPAN.c1205, SPAN.c1305, SPAN.c1405, SPAN.c1505 {background-color: maroon;}
++SPAN.c0006, SPAN.c0106, SPAN.c0206, SPAN.c0306, SPAN.c0406, SPAN.c0506, SPAN.c0606, SPAN.c0706, SPAN.c0806, SPAN.c0906, SPAN.c1006, SPAN.c1106, SPAN.c1206, SPAN.c1306, SPAN.c1406, SPAN.c1506 {background-color: purple;}
++SPAN.c0007, SPAN.c0107, SPAN.c0207, SPAN.c0307, SPAN.c0407, SPAN.c0507, SPAN.c0607, SPAN.c0707, SPAN.c0807, SPAN.c0907, SPAN.c1007, SPAN.c1107, SPAN.c1207, SPAN.c1307, SPAN.c1407, SPAN.c1507 {background-color: orange;}
++SPAN.c0008, SPAN.c0108, SPAN.c0208, SPAN.c0308, SPAN.c0408, SPAN.c0508, SPAN.c0608, SPAN.c0708, SPAN.c0808, SPAN.c0908, SPAN.c1008, SPAN.c1108, SPAN.c1208, SPAN.c1308, SPAN.c1408, SPAN.c1508 {background-color: yellow;}
++SPAN.c0009, SPAN.c0109, SPAN.c0209, SPAN.c0309, SPAN.c0409, SPAN.c0509, SPAN.c0609, SPAN.c0709, SPAN.c0809, SPAN.c0909, SPAN.c1009, SPAN.c1109, SPAN.c1209, SPAN.c1309, SPAN.c1409, SPAN.c1509 {background-color: lime;}
++SPAN.c0010, SPAN.c0110, SPAN.c0210, SPAN.c0310, SPAN.c0410, SPAN.c0510, SPAN.c0610, SPAN.c0710, SPAN.c0810, SPAN.c0910, SPAN.c1010, SPAN.c1110, SPAN.c1210, SPAN.c1310, SPAN.c1410, SPAN.c1510 {background-color: teal;}
++SPAN.c0011, SPAN.c0111, SPAN.c0211, SPAN.c0311, SPAN.c0411, SPAN.c0511, SPAN.c0611, SPAN.c0711, SPAN.c0811, SPAN.c0911, SPAN.c1011, SPAN.c1111, SPAN.c1211, SPAN.c1311, SPAN.c1411, SPAN.c1511 {background-color: cyan;}
++SPAN.c0012, SPAN.c0112, SPAN.c0212, SPAN.c0312, SPAN.c0412, SPAN.c0512, SPAN.c0612, SPAN.c0712, SPAN.c0812, SPAN.c0912, SPAN.c1012, SPAN.c1112, SPAN.c1212, SPAN.c1312, SPAN.c1412, SPAN.c1512 {background-color: blue;}
++SPAN.c0013, SPAN.c0113, SPAN.c0213, SPAN.c0313, SPAN.c0413, SPAN.c0513, SPAN.c0613, SPAN.c0713, SPAN.c0813, SPAN.c0913, SPAN.c1013, SPAN.c1113, SPAN.c1213, SPAN.c1313, SPAN.c1413, SPAN.c1513 {background-color: magenta;}
++SPAN.c0014, SPAN.c0114, SPAN.c0214, SPAN.c0314, SPAN.c0414, SPAN.c0514, SPAN.c0614, SPAN.c0714, SPAN.c0814, SPAN.c0914, SPAN.c1014, SPAN.c1114, SPAN.c1214, SPAN.c1314, SPAN.c1414, SPAN.c1514 {background-color: silver;}
++SPAN.c0015, SPAN.c0115, SPAN.c0215, SPAN.c0315, SPAN.c0415, SPAN.c0515, SPAN.c0615, SPAN.c0715, SPAN.c0815, SPAN.c0915, SPAN.c1015, SPAN.c1114, SPAN.c1215, SPAN.c1315, SPAN.c1415, SPAN.c1515 {background-color: gray;}
++SPAN.c0000, SPAN.c0001, SPAN.c0002, SPAN.c0003, SPAN.c0004, SPAN.c0005, SPAN.c0006, SPAN.c0007, SPAN.c0008, SPAN.c0009, SPAN.c0010, SPAN.c0011, SPAN.c0012, SPAN.c0013, SPAN.c0014, SPAN.c0015 {color: white;}
++SPAN.c0100, SPAN.c0101, SPAN.c0102, SPAN.c0103, SPAN.c0104, SPAN.c0105, SPAN.c0106, SPAN.c0107, SPAN.c0108, SPAN.c0109, SPAN.c0110, SPAN.c0111, SPAN.c0112, SPAN.c0113, SPAN.c0114, SPAN.c0115 {color: black;}
++SPAN.c0200, SPAN.c0201, SPAN.c0202, SPAN.c0203, SPAN.c0204, SPAN.c0205, SPAN.c0206, SPAN.c0207, SPAN.c0208, SPAN.c0209, SPAN.c0210, SPAN.c0211, SPAN.c0212, SPAN.c0213, SPAN.c0214, SPAN.c0215 {color: navy;}
++SPAN.c0300, SPAN.c0301, SPAN.c0302, SPAN.c0303, SPAN.c0304, SPAN.c0305, SPAN.c0306, SPAN.c0307, SPAN.c0308, SPAN.c0309, SPAN.c0310, SPAN.c0311, SPAN.c0312, SPAN.c0313, SPAN.c0314, SPAN.c0315 {color: green;}
++SPAN.c0400, SPAN.c0401, SPAN.c0402, SPAN.c0403, SPAN.c0404, SPAN.c0405, SPAN.c0406, SPAN.c0407, SPAN.c0408, SPAN.c0409, SPAN.c0410, SPAN.c0411, SPAN.c0412, SPAN.c0413, SPAN.c0414, SPAN.c0415 {color: red;}
++SPAN.c0500, SPAN.c0501, SPAN.c0502, SPAN.c0503, SPAN.c0504, SPAN.c0505, SPAN.c0506, SPAN.c0507, SPAN.c0508, SPAN.c0509, SPAN.c0510, SPAN.c0511, SPAN.c0512, SPAN.c0513, SPAN.c0514, SPAN.c0515 {color: maroon;}
++SPAN.c0600, SPAN.c0601, SPAN.c0602, SPAN.c0603, SPAN.c0604, SPAN.c0605, SPAN.c0606, SPAN.c0607, SPAN.c0608, SPAN.c0609, SPAN.c0610, SPAN.c0611, SPAN.c0612, SPAN.c0613, SPAN.c0614, SPAN.c0615 {color: purple;}
++SPAN.c0700, SPAN.c0701, SPAN.c0702, SPAN.c0703, SPAN.c0704, SPAN.c0705, SPAN.c0706, SPAN.c0707, SPAN.c0708, SPAN.c0709, SPAN.c0710, SPAN.c0711, SPAN.c0712, SPAN.c0713, SPAN.c0714, SPAN.c0715 {color: orange;}
++SPAN.c0800, SPAN.c0801, SPAN.c0802, SPAN.c0803, SPAN.c0804, SPAN.c0805, SPAN.c0806, SPAN.c0807, SPAN.c0808, SPAN.c0809, SPAN.c0810, SPAN.c0811, SPAN.c0812, SPAN.c0813, SPAN.c0814, SPAN.c0815 {color: yellow;}
++SPAN.c0900, SPAN.c0901, SPAN.c0902, SPAN.c0903, SPAN.c0904, SPAN.c0905, SPAN.c0906, SPAN.c0907, SPAN.c0908, SPAN.c0909, SPAN.c0910, SPAN.c0911, SPAN.c0912, SPAN.c0913, SPAN.c0914, SPAN.c0915 {color: lime;}
++SPAN.c1000, SPAN.c1001, SPAN.c1002, SPAN.c1003, SPAN.c1004, SPAN.c1005, SPAN.c1006, SPAN.c1007, SPAN.c1008, SPAN.c1009, SPAN.c1010, SPAN.c1011, SPAN.c1012, SPAN.c1013, SPAN.c1014, SPAN.c1015 {color: teal;}
++SPAN.c1100, SPAN.c1101, SPAN.c1102, SPAN.c1103, SPAN.c1104, SPAN.c1105, SPAN.c1106, SPAN.c1107, SPAN.c1108, SPAN.c1109, SPAN.c1110, SPAN.c1111, SPAN.c1112, SPAN.c1113, SPAN.c1114, SPAN.c1115 {color: cyan;}
++SPAN.c1200, SPAN.c1201, SPAN.c1202, SPAN.c1203, SPAN.c1204, SPAN.c1205, SPAN.c1206, SPAN.c1207, SPAN.c1208, SPAN.c1209, SPAN.c1210, SPAN.c1211, SPAN.c1212, SPAN.c1213, SPAN.c1214, SPAN.c1215 {color: blue;}
++SPAN.c1300, SPAN.c1301, SPAN.c1302, SPAN.c1303, SPAN.c1304, SPAN.c1305, SPAN.c1306, SPAN.c1307, SPAN.c1308, SPAN.c1309, SPAN.c1310, SPAN.c1311, SPAN.c1312, SPAN.c1313, SPAN.c1314, SPAN.c1315 {color: magenta;}
++SPAN.c1400, SPAN.c1401, SPAN.c1402, SPAN.c1403, SPAN.c1404, SPAN.c1405, SPAN.c1406, SPAN.c1407, SPAN.c1408, SPAN.c1409, SPAN.c1410, SPAN.c1411, SPAN.c1412, SPAN.c1413, SPAN.c1414, SPAN.c1415 {color: silver;}
++SPAN.c1500, SPAN.c1501, SPAN.c1502, SPAN.c1503, SPAN.c1504, SPAN.c1505, SPAN.c1506, SPAN.c1507, SPAN.c1508, SPAN.c1509, SPAN.c1510, SPAN.c1511, SPAN.c1512, SPAN.c1513, SPAN.c1514, SPAN.c1515 {color: gray;}
++SPAN.f00 {color: white;}
++SPAN.f01 {color: black;}
++SPAN.f02 {color: navy;}
++SPAN.f03 {color: green;}
++SPAN.f04 {color: red;}
++SPAN.f05 {color: maroon;}
++SPAN.f06 {color: purple;}
++SPAN.f07 {color: orange;}
++SPAN.f08 {color: yellow;}
++SPAN.f09 {color: lime;}
++SPAN.f10 {color: teal;}
++SPAN.f11 {color: cyan;}
++SPAN.f12 {color: blue;}
++SPAN.f13 {color: magenta;}
++SPAN.f14 {color: silver;}
++SPAN.f15 {color: gray;}
++SPAN.b00 {background-color: white;}
++SPAN.b01 {background-color: black;}
++SPAN.b02 {background-color: navy;}
++SPAN.b03 {background-color: green;}
++SPAN.b04 {background-color: red;}
++SPAN.b05 {background-color: maroon;}
++SPAN.b06 {background-color: purple;}
++SPAN.b07 {background-color: orange;}
++SPAN.b08 {background-color: yellow;}
++SPAN.b09 {background-color: lime;}
++SPAN.b10 {background-color: teal;}
++SPAN.b11 {background-color: cyan;}
++SPAN.b12 {background-color: blue;}
++SPAN.b13 {background-color: magenta;}
++SPAN.b14 {background-color: silver;}
++SPAN.b15 {background-color: gray;}