diff --git a/.gitattributes b/.gitattributes index 37c3be3f..15382ae1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,26 +2,26 @@ /.indent.pro -text /AUTHORS -text /BUGS -text -/Bahamut.c -text -/Bahamut.h -text +/Bahamut.c eol=lf +/Bahamut.h eol=lf /COPYING -text /CREDITS -text /ChangeLog -text /Config -text -/Ircu.c -text -/Ircu.h -text +/Ircu.c eol=lf +/Ircu.h eol=lf /Makefile.in -text /Makefile.inc.in -text -/QuantumIRCd.c -text -/QuantumIRCd.h -text +/QuantumIRCd.c eol=lf +/QuantumIRCd.h eol=lf /README -text /RELNOTES -text /TODO -text -/Ultimate.c -text -/Ultimate.h -text -/Unreal.c -text -/Unreal.h -text -/acconfig.h -text +/Ultimate.c eol=lf +/Ultimate.h eol=lf +/Unreal.c eol=lf +/Unreal.h eol=lf +/acconfig.h eol=lf /aclocal.m4 -text adns/.indent.pro -text adns/Makefile -text @@ -39,9 +39,10 @@ adns/setup.c -text adns/transmit.c -text adns/tvarith.h -text adns/types.c -text -/chans.c -text -/conf.c -text -/conf.h -text +/chans.c eol=lf +/chans.h eol=lf +/conf.c eol=lf +/conf.h eol=lf /config.guess -text /config.h.in -text /config.sub -text @@ -50,8 +51,8 @@ adns/types.c -text /cronchk -text data/niconfig.db -text data/tlds.nfo -text -/dl.c -text -/dl.h -text +/dl.c eol=lf +/dl.h eol=lf dl/.indent.pro -text dl/Makefile -text dl/README -text @@ -117,7 +118,8 @@ dl/template/Makefile -text dl/template/template.c -text dl/version/Makefile -text dl/version/version.c -text -/dns.c -text +/dns.c eol=lf +/dns.h eol=lf doc/FAQ -text doc/FAQ.pt -text doc/USERMAN -text @@ -127,19 +129,19 @@ doc/old/INSTALL.pt -text doc/old/README.pt -text doc/read-faq -text doc/read-userman -text -/dotconf.c -text -/dotconf.h -text -/events.h -text -/hash.c -text -/hash.h -text -/hybrid7.c -text -/hybrid7.h -text +/dotconf.c eol=lf +/dotconf.h eol=lf +/events.h eol=lf +/hash.c eol=lf +/hash.h eol=lf +/hybrid7.c eol=lf +/hybrid7.h eol=lf /install-sh -text -/ircd.c -text -/ircd.h -text -/ircstring.c -text -/ircstring.h -text -/keeper.c -text +/ircd.c eol=lf +/ircd.h eol=lf +/ircstring.c eol=lf +/ircstring.h eol=lf +/keeper.c eol=lf keeper/.indent.pro -text keeper/Makefile -text keeper/keeper.h -text @@ -153,28 +155,46 @@ keeper/kp_set.c -text keeper/kp_sort.c -text keeper/kp_util.c -text keeper/kp_util.h -text -/list.c -text -/list.h -text -/log.c -text -/log.h -text +/liquidircd.c -text +/liquidircd.h -text +/list.c eol=lf +/list.h eol=lf +/log.c eol=lf +/log.h eol=lf logs/.keepme -text -/main.c -text +/main.c eol=lf /makeconf -text -/misc.c -text -/mystic.c -text -/mystic.h -text -/neoircd.c -text -/neoircd.h -text +/misc.c eol=lf +/mystic.c eol=lf +/mystic.h eol=lf +/neoircd.c eol=lf +/neoircd.h eol=lf /neostats.motd -text -/ns_help.c -text -/server.c -text -/services.c -text -/sock.c -text -/sock.h -text -/stats.h -text -/support.c -text -/support.h -text -/timer.c -text +/ns_help.c eol=lf +/ns_help.h eol=lf +/numeric.h -text +pcre/COPYING -text +pcre/Makefile.in eol=lf +pcre/README -text +pcre/config.h.in eol=lf +pcre/dftables.c eol=lf +pcre/get.c eol=lf +pcre/internal.h eol=lf +pcre/maketables.c eol=lf +pcre/pcre.c eol=lf +pcre/pcre.in eol=lf +pcre/study.c eol=lf +/server.c eol=lf +/server.h eol=lf +/services.c eol=lf +/services.h eol=lf +/sock.c eol=lf +/sock.h eol=lf +/stats.h eol=lf +/support.c eol=lf +/support.h eol=lf +/timer.c eol=lf +/timer.h eol=lf tools/Makefile.in -text tools/README.kptool -text tools/cfgtool/Makefile -text @@ -198,4 +218,5 @@ tools/kp_exp.c -text tools/kp_imp.c -text tools/kptool.c -text tools/kptool.h -text -/users.c -text +/users.c eol=lf +/users.h eol=lf diff --git a/Bahamut.c b/Bahamut.c index 81b8815b..b1439778 100644 --- a/Bahamut.c +++ b/Bahamut.c @@ -29,10 +29,13 @@ #include "Bahamut.h" #include "dl.h" #include "log.h" +#include "server.h" +#include "chans.h" static char ircd_buf[BUFSIZE]; const char ircd_version[] = "(B)"; +const char services_bot_modes[]= "+oS"; IntCommands cmd_list[] = { /* Command Function srvmsg */ @@ -141,14 +144,14 @@ Oper_Modes usr_mds[] = { , {UMODE_REGNICK, 'r', 10} , - {UMODE_SERVICESADMIN, 'a', 200} + {UMODE_SERVICESADMIN, 'a', NS_ULEVEL_ROOT} , {UMODE_SERVADMIN, 'A', 100} , {UMODE_REGONLY, 'R', 0} , /* this is needed for bot support */ - {UMODE_SERVICES, 'S', 200} + {UMODE_SERVICES, 'S', NS_ULEVEL_ROOT} , {0, 0, 0} }; @@ -441,6 +444,13 @@ sakill_cmd (const char *host, const char *ident, const char *setby, const int le return 1; } +int +sinvite_cmd (const char *from, const char *to, const char *chan) { + sts (":%s INVITE %s %s", from, to, chan); + return 1; +} + + int srakill_cmd (const char *host, const char *ident) { @@ -637,7 +647,7 @@ Srv_Sjoin (char *origin, char **argv, int argc) } c = findchan (argv[1]); /* update the TS time */ - Change_Chan_Ts (c, atoi (argv[0])); + ChangeChanTS (c, atoi (argv[0])); c->modes |= mode1; if (!list_isempty (tl)) { if (!list_isfull (c->modeparms)) { @@ -779,7 +789,7 @@ Usr_Away (char *origin, char **argv, int argc) } else { Buf = NULL; } - Do_Away (u, Buf); + UserAway (u, Buf); if (argc > 0) { free (Buf); } @@ -803,7 +813,7 @@ Usr_Topic (char *origin, char **argv, int argc) c = findchan (argv[0]); if (c) { buf = joinbuf (argv, argc, 3); - Change_Topic (argv[1], c, atoi (argv[2]), buf); + ChangeTopic (argv[1], c, atoi (argv[2]), buf); free (buf); } else { nlog (LOG_WARNING, LOG_CORE, "Ehhh, Can't find Channel %s", argv[0]); diff --git a/Bahamut.h b/Bahamut.h index 6bbaac04..d05766af 100644 --- a/Bahamut.h +++ b/Bahamut.h @@ -28,6 +28,17 @@ /* we support tokens */ #undef HAVE_TOKEN_SUP +/* we dont have svshost support */ +#undef GOTSVSHOST + +/* we don't have svsjoin support */ +#undef GOTSVSJOIN + + +/* Moved from connectserv so we can use elsewhere */ +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define SERVERADMIN_MODE 'a' #define MSG_PRIVATE "PRIVMSG" /* PRIV */ #define MSG_WHO "WHO" /* WHO -> WHOC */ @@ -277,6 +288,7 @@ extern int sakill_cmd (const char *host, const char *ident, const char *setby, c extern int srakill_cmd (const char *host, const char *ident); extern int ssvshost_cmd (const char *who, const char *vhost); extern int ssvskill_cmd (const char *who, const char *reason, ...); +extern int sinvite_cmd (const char *from, const char *to, const char *chan); void Usr_Version (char *, char **, int argc); void Usr_ShowMOTD (char *, char **, int argc); diff --git a/ChangeLog b/ChangeLog index 54e128cc..dac1ffd9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,24 @@ NeoStats ChangeLog - Anything we add/remove/fix/change is in here (even our rants) ===================================================================================== +* NeoStats * Fish (F) & Mark (M)* Version 2.5.9 + - import of libpcre into core (M) + - added some defines for common user levels (M) + - added some defines for common errors so functions can better handle failure conditions (M) + - cleanup of default settings and configs - defaults were often set twice! (M) + - fixed debug mode messages given to users under certain conditions (M) + - main command list now in a table to make it easy to add new commands (M) + - shorted some command text by stripping superfluous prefixes (M) + - ns_set_debug disable bug fixed (M) + - ns_set_debug function parameters changed to support command table (M) + - ns_set_debug now requires explicit ON/OFF to make consistent with other commands (M) + - removed chan from __Chan_Message parameters since it is in av[0] anyway (M) + - cleanups of core functions and module API including cleanup of module types and references (M) + - Updated command user levels and associated help text (M) + - Made M's bot message handling and help functions dynamic.. so modules can add/remove commands to the services bot... should make these functions a bit more generic so they could be called from any modules to handle messages. (F) + - imported LiquidIRCd support from ???? (F) + - Admin text filename changed from "stats.admin" to "neostats.admin" (M) + - Added services_bot_modes to each IRCd. This should contain the required ircd specific modes for a services bot (M) + * NeoStats * Fish (F) & Mark (M)* Version 2.5.8 - strncpy replaced by strlcpy - faster and safer (M) - Replaced redundant strlen calls with faster and safer alternatives (M) diff --git a/Config b/Config index 72998c0e..1663c71e 100755 --- a/Config +++ b/Config @@ -8,7 +8,6 @@ IRCD= #module vars MODULELIST="" MSTATSERV=1 -MSPAM=0 MLOVESERV=1 MHOSTSERV=1 MMORALESERV=1 @@ -139,37 +138,6 @@ while [ $validselection -eq 0 ] ; do done echo "" -if [ "$MSPAM" = 1 ] ; then - YN_DEFAULT=yes -else - YN_DEFAULT=no -fi - -validselection=0 -echo "Do you want to build spam?" -while [ $validselection -eq 0 ] ; do - echo_no_lf "[$YN_DEFAULT] " - if read INPUT ; then : ; else echo "" ; exit 1 ; fi - if [ ! "$INPUT" ] ; then - INPUT=$YN_DEFAULT - fi - case $INPUT in - n*|N*) - MSPAM=0 - validselection=1 - ;; - y*|Y*) - MSPAM=1 - HAVEMODS=1 - validselection=1 - ;; - *) - echo 'Please enter "yes" or "no".' - ;; - esac -done -echo "" - if [ "$MLOVESERV" = 1 ] ; then YN_DEFAULT=yes else @@ -341,13 +309,6 @@ fi HAVEMOD=1 MODULELIST=$MODULELIST"statserv" fi -if [ "$MSPAM" = 1 ] ; then -if [ "$HAVEMOD" = 1 ] ; then -MODULELIST="$MODULELIST " -fi -HAVEMOD=1 -MODULELIST=$MODULELIST"spam" -fi if [ "$MLOVESERV" = 1 ] ; then if [ "$HAVEMOD" = 1 ] ; then MODULELIST="$MODULELIST " diff --git a/Ircu.c b/Ircu.c index 8807781d..bc5b6d73 100644 --- a/Ircu.c +++ b/Ircu.c @@ -27,10 +27,14 @@ #include "Ircu.h" #include "dl.h" #include "log.h" +#include "users.h" +#include "server.h" +#include "chans.h" static char ircd_buf[BUFSIZE]; const char ircd_version[] = "(IRCU)"; +const char services_bot_modes[]= "+oS"; /* this is the command list and associated functions to run */ IntCommands cmd_list[] = { @@ -136,7 +140,7 @@ Oper_Modes usr_mds[] = { , {UMODE_CCONN, 'c', 0} , - {UMODE_DEBUG, 'd', 200} + {UMODE_DEBUG, 'd', NS_ULEVEL_ROOT} , {UMODE_FULL, 'f', 0} , @@ -798,7 +802,7 @@ Usr_Away (char *origin, char **argv, int argc) } else { buf = NULL; } - Do_Away (u, buf); + UserAway (u, buf); if (argc > 0) { free (buf); } @@ -825,7 +829,7 @@ Usr_Topic (char *origin, char **argv, int argc) c = findchan (argv[0]); if (c) { buf = joinbuf (argv, argc, 2); - Change_Topic (origin, c, me.now, buf); + ChangeTopic (origin, c, me.now, buf); free (buf); } else { nlog (LOG_WARNING, LOG_CORE, "Ehhh, Can't find Channel %s", argv[0]); @@ -884,7 +888,7 @@ Srv_Netinfo (char *origin, char **argv, int argc) strlcpy (me.netname, argv[7], MAXPASS); init_ServBot (); globops (me.name, "Link with Network \2Complete!\2"); - Module_Event (EVENT_NETINFO, NULL, 0); + ModuleEvent (EVENT_NETINFO, NULL, 0); me.synced = 1; } diff --git a/Ircu.h b/Ircu.h index 8e9071ad..9e86b027 100644 --- a/Ircu.h +++ b/Ircu.h @@ -25,7 +25,9 @@ #ifndef IRCU_H #define IRCU_H - +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define SERVERADMIN_MODE 'a' #define MSG_EOB "EOB" /* end of burst */ diff --git a/Makefile.in b/Makefile.in index b857fd9b..b3e53ebe 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4,6 +4,7 @@ # makefile originally created by Andy Church. include Makefile.inc +PCRE_OBJS = pcre/pcre.o pcre/get.o pcre/study.o OBJS = dns.o chans.o dotconf.o services.o main.o sock.o conf.o ircd.o timer.o \ users.o ns_help.o dl.o list.o hash.o server.o keeper.o log.o misc.o \ @@ -23,30 +24,31 @@ DATA = data/tlds.nfo INCLUDES = config.h dl.h dotconf.h hash.h list.h stats.h Ultimate.h Unreal.h \ adns/adns.h hybrid7.h neoircd.h conf.h log.h Bahamut.h \ Ircu.h mystic.h QuantumIRCd.h sock.h ircd.h \ - support.h ircstring.h events.h + support.h ircstring.h events.h liquidircd.h numeric.h pcre.h BUILDFILES = configure *.in adns/Makefile adns/config.h.in dl/Makefile \ dl/modules.txt RELNOTES install-sh neoircd.c neoircd.h \ hybrid7.c hybrid7.h Ultimate.c Ultimate.h Unreal.c Unreal.h \ makeconf cronchk tools/Makefile.in tools/cfgtool/Makefile \ keeper/Makefile doc/old/* Bahamut.c Ircu.c mystic.c \ - QuantumIRCd.c Config + QuantumIRCd.c Config liquidircd.c TOOLFILES = tools/*.c tools/*.h tools/cfgtool/*.h tools/cfgtool/*.c \ tools/cfgtool/pixmaps/*.xpm DISTFILES = $(INCLUDES) $(SRCS) $(DATA) $(DOCS) $(DOCS_PROGS) $(CONF) \ $(BUILDFILES) $(TOOLFILES) keeper/*.c keeper/*.h adns/*.c \ - adns/adns.h adns/dlist.h adns/internal.h adns/tvarith.h + adns/adns.h adns/dlist.h adns/internal.h adns/tvarith.h \ + ns_help.h timer.h users.h chans.h server.h distdir = @PACKAGE@-@VERSION@ SUBDIRS = doc doc/old data adns logs keeper tools tools/cfgtool \ - tools/cfgtool/pixmaps + tools/cfgtool/pixmaps pcre DISTMOD = cs extauth hostserv loveserv ms spam statserv version template .c.o: $(CC) $(NEOINCLUDES) $(CFLAGS) -c $< -all: libadns.a libkeeper.a neostats modules utils +all: libadns.a libkeeper.a libpcre neostats modules utils modules: (cd dl; $(MAKE) $@) @@ -57,6 +59,9 @@ libadns.a: libkeeper.a: (cd keeper; $(MAKE) $@) +libpcre: + (cd pcre; $(MAKE)) + utils: (cd tools; $(MAKE) $@) @@ -66,11 +71,12 @@ clean: (cd adns; $(MAKE) $@) (cd keeper; $(MAKE) $@) (cd tools; $(MAKE) $@) + (cd pcre; $(MAKE) $@) /bin/rm -rf *.o neostats *.cache Makefile config.h Makefile.inc *.log *.a neostats: $(OBJS) - $(CC) $(LDFLAGS) $(OBJS) adns/libadns.a keeper/libkeeper.a -o $@ + $(CC) $(LDFLAGS) $(OBJS) adns/libadns.a keeper/libkeeper.a $(PCRE_OBJS) -o $@ install: neostats modules @rm -rf @prefix@/dl/cs.so diff --git a/QuantumIRCd.c b/QuantumIRCd.c index f6779fa7..09d728b1 100644 --- a/QuantumIRCd.c +++ b/QuantumIRCd.c @@ -29,10 +29,14 @@ #include "QuantumIRCd.h" #include "dl.h" #include "log.h" +#include "users.h" +#include "server.h" +#include "chans.h" static char ircd_buf[BUFSIZE]; const char ircd_version[] = "(Q)"; +const char services_bot_modes[]= "+oS"; IntCommands cmd_list[] = { /* Command Function srvmsg */ @@ -261,11 +265,11 @@ Oper_Modes usr_mds[] = { , {UMODE_HIDE, 'x', 0} , - {UMODE_IRCADMIN, 'Z', 200} + {UMODE_IRCADMIN, 'Z', NS_ULEVEL_ROOT} , - {UMODE_SERVICESADMIN, 'P', 185} + {UMODE_SERVICESADMIN, 'P', NS_ULEVEL_ADMIN} , - {UMODE_SERVICES, 'S', 200} + {UMODE_SERVICES, 'S', NS_ULEVEL_ROOT} , {UMODE_PROT, 'p', 0} , @@ -599,6 +603,12 @@ ssvshost_cmd (const char *who, const char *vhost) return 1; } } +int +sinvite_cmd (const char *from, const char *to, const char *chan) { + sts (":%s INVITE %s %s", from, to, chan); + return 1; +} + int sakill_cmd (const char *host, const char *ident, const char *setby, const int length, const char *reason, ...) { @@ -973,7 +983,7 @@ Usr_Away (char *origin, char **argv, int argc) } else { Buf = NULL; } - Do_Away (u, Buf); + UserAway (u, Buf); if (argc > 0) { free (Buf); } @@ -997,7 +1007,7 @@ Usr_Topic (char *origin, char **argv, int argc) c = findchan (argv[0]); if (c) { buf = joinbuf (argv, argc, 3); - Change_Topic (argv[1], c, atoi (argv[2]), buf); + ChangeTopic (argv[1], c, atoi (argv[2]), buf); free (buf); } else { nlog (LOG_WARNING, LOG_CORE, "Ehhh, Can't find Channel %s", argv[0]); diff --git a/QuantumIRCd.h b/QuantumIRCd.h index 362c559f..7cd384b0 100644 --- a/QuantumIRCd.h +++ b/QuantumIRCd.h @@ -31,6 +31,21 @@ /* we have vhost support */ #define GOTSVSVHOST +/* we dont have svsjoin */ +#undef GOTSVSJOIN + +/* Moved from connectserv so we can use elsewhere */ +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define GUESTADMIN_MODE 'G' +#define COSERVERADMIN_MODE 'J' +#define SERVERADMIN_MODE 'A' +#define CONETADMIN_MODE 'n' +#define NETADMIN_MODE 'N' +#define COTECHADMIN_MODE 't' +#define TECHADMIN_MODE 'T' /* Set to a number as we dont use */ +#define SERVICESADMIN_MODE 'a' +#define NETSERVICE_MODE 'S' #define MSG_PRIVATE "PRIVMSG" /* PRIV */ #define TOK_PRIVATE "!" /* 33 */ @@ -468,6 +483,7 @@ extern int sakill_cmd (const char *host, const char *ident, const char *setby, c extern int srakill_cmd (const char *host, const char *ident); extern int ssvshost_cmd (const char *who, const char *vhost); extern int ssvskill_cmd (const char *who, const char *reason, ...); +extern int sinvite_cmd (const char *from, const char *to, const char *chan); void Usr_Version (char *, char **, int argc); void Usr_ShowMOTD (char *, char **, int argc); diff --git a/README b/README index b3bf184f..31a27db6 100644 --- a/README +++ b/README @@ -231,14 +231,13 @@ NeoStats ships with the following modules: HostServ --enable-modules="hostserv" LoveServ --enable-modules="loveserv" MoraleServ --enable-modules="ms" - spam --enable-modules="spam" StatServ --enable-modules="statserv" To compile multiple modules combine the enable-modules options together as follows: - --enable-modules="statserv spam" - (this would enable the statserv and spam modules). + --enable-modules="statserv hostserv" + (this would enable the statserv and hostserv modules). If you have downloaded additional modules from the NeoStats website, DO NOT specify them in the enable-modules option. Each module has its diff --git a/Ultimate.c b/Ultimate.c index c4ea4f0b..f4aaa393 100644 --- a/Ultimate.c +++ b/Ultimate.c @@ -29,13 +29,18 @@ #include "Ultimate.h" #include "dl.h" #include "log.h" +#include "users.h" +#include "server.h" +#include "chans.h" static char ircd_buf[BUFSIZE]; #ifndef ULTIMATE3 const char ircd_version[] = "(UL)"; +const char services_bot_modes[]= "+oS"; #else const char ircd_version[] = "(UL3)"; +const char services_bot_modes[]= "+oS"; #endif IntCommands cmd_list[] = { @@ -270,11 +275,11 @@ Oper_Modes usr_mds[] = { , {UMODE_HIDE, 'x', 0} , - {UMODE_IRCADMIN, 'Z', 200} + {UMODE_IRCADMIN, 'Z', NS_ULEVEL_ROOT} , - {UMODE_SERVICESADMIN, 'P', 185} + {UMODE_SERVICESADMIN, 'P', NS_ULEVEL_ADMIN} , - {UMODE_SERVICES, 'S', 200} + {UMODE_SERVICES, 'S', NS_ULEVEL_ROOT} , {UMODE_PROT, 'p', 0} , @@ -327,9 +332,9 @@ Oper_Modes usr_mds[] = { , {UMODE_KILLS, 'k', 0} , - {UMODE_SERVICES, 'S', 200} + {UMODE_SERVICES, 'S', NS_ULEVEL_ROOT} , - {UMODE_SERVICESADMIN, 'P', 200} + {UMODE_SERVICESADMIN, 'P', NS_ULEVEL_ROOT} , {UMODE_RBOT, 'B', 0} , @@ -337,7 +342,7 @@ Oper_Modes usr_mds[] = { , {UMODE_ADMIN, 'z', 70} , - {UMODE_NETADMIN, 'N', 185} + {UMODE_NETADMIN, 'N', NS_ULEVEL_ADMIN} , {UMODE_TECHADMIN, 'T', 190} , @@ -680,6 +685,15 @@ ssvshost_cmd (const char *who, const char *vhost) return 1; } } + +int +sinvite_cmd (const char *from, const char *to, const char *chan) { + sts (":%s INVITE %s %s", from, to, chan); + return 1; +} + + + int sakill_cmd (const char *host, const char *ident, const char *setby, const int length, const char *reason, ...) { @@ -1076,7 +1090,7 @@ Usr_Away (char *origin, char **argv, int argc) } else { Buf = NULL; } - Do_Away (u, Buf); + UserAway (u, Buf); if (argc > 0) { free (Buf); } @@ -1100,7 +1114,7 @@ Usr_Topic (char *origin, char **argv, int argc) c = findchan (argv[0]); if (c) { buf = joinbuf (argv, argc, 3); - Change_Topic (argv[1], c, atoi (argv[2]), buf); + ChangeTopic (argv[1], c, atoi (argv[2]), buf); free (buf); } else { nlog (LOG_WARNING, LOG_CORE, "Ehhh, Can't find Channel %s", argv[0]); @@ -1179,9 +1193,9 @@ Srv_Netinfo (char *origin, char **argv, int argc) init_ServBot (); globops (me.name, "Link with Network \2Complete!\2"); #ifdef DEBUG - ns_set_debug (me.chan); + me.debug_mode = 1; #endif - Module_Event (EVENT_NETINFO, NULL, 0); + ModuleEvent (EVENT_NETINFO, NULL, 0); me.synced = 1; } #endif diff --git a/Ultimate.h b/Ultimate.h index 536d8fa7..45612b4d 100644 --- a/Ultimate.h +++ b/Ultimate.h @@ -31,6 +31,38 @@ /* we have vhost support */ #define GOTSVSVHOST +#ifdef ULTIMATE3 + +/* we have svsjoin from a30 onwards */ +#define GOTSVSJOIN + +/* Moved from connectserv so we can use elsewhere */ +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define GUESTADMIN_MODE 'G' +#define COSERVERADMIN_MODE 'J' +#define SERVERADMIN_MODE 'A' +#define CONETADMIN_MODE 'n' +#define NETADMIN_MODE 'N' +#define COTECHADMIN_MODE 't' +#define TECHADMIN_MODE 'T' /* Set to a number as we dont use */ +#define SERVICESADMIN_MODE 'a' +#define NETSERVICE_MODE 'S' +#else +/* old Ultimate2 doesn't have svsjoin */ +#undef SVSJOIN +/* Moved from connectserv so we can use elsewhere */ +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define COSERVERADMIN_MODE 'J' +#define SERVERADMIN_MODE 'A' +#define CONETADMIN_MODE 't' +#define NETADMIN_MODE 'N' +#define TECHADMIN_MODE 'T' +#define SERVICESADMIN_MODE 'P' +#define NETSERVICE_MODE 'S' +#define BOT_MODE 'B' +#endif #define MSG_PRIVATE "PRIVMSG" /* PRIV */ @@ -494,6 +526,8 @@ extern int sakill_cmd (const char *host, const char *ident, const char *setby, c extern int srakill_cmd (const char *host, const char *ident); extern int ssvshost_cmd (const char *who, const char *vhost); extern int ssvskill_cmd (const char *who, const char *reason, ...); +extern int sinvite_cmd (const char *from, const char *to, const char *chan); + void Usr_Version (char *, char **, int argc); void Usr_ShowMOTD (char *, char **, int argc); diff --git a/Unreal.c b/Unreal.c index 670c6692..3dcec902 100644 --- a/Unreal.c +++ b/Unreal.c @@ -29,10 +29,14 @@ #include "Unreal.h" #include "dl.h" #include "log.h" +#include "users.h" +#include "server.h" +#include "chans.h" static char ircd_buf[BUFSIZE]; const char ircd_version[] = "(U)"; +const char services_bot_modes[]= "+oS"; IntCommands cmd_list[] = { /* Command Function srvmsg */ @@ -247,7 +251,7 @@ Oper_Modes usr_mds[] = { , {UMODE_KILLS, 'k', 0} , - {UMODE_SERVICES, 'S', 200} + {UMODE_SERVICES, 'S', NS_ULEVEL_ROOT} , {UMODE_SADMIN, 'a', 100} , @@ -267,7 +271,7 @@ Oper_Modes usr_mds[] = { , {UMODE_ADMIN, 'A', 70} , - {UMODE_NETADMIN, 'N', 185} + {UMODE_NETADMIN, 'N', NS_ULEVEL_ADMIN} , {UMODE_TECHADMIN, 'T', 190} , @@ -513,6 +517,13 @@ ssvshost_cmd (const char *who, const char *vhost) return 1; } +int +sinvite_cmd (const char *from, const char *to, const char *chan) { + sts (":%s INVITE %s %s", from, to, chan); + return 1; +} + + int ssvsmode_cmd (const char *target, const char *modes) { @@ -792,7 +803,7 @@ Usr_Away (char *origin, char **argv, int argc) } else { buf = NULL; } - Do_Away (u, buf); + UserAway (u, buf); if (argc > 0) { free (buf); } @@ -818,7 +829,7 @@ Usr_Topic (char *origin, char **argv, int argc) c = findchan (argv[0]); if (c) { buf = joinbuf (argv, argc, 3); - Change_Topic (argv[1], c, atoi (argv[2]), buf); + ChangeTopic (argv[1], c, atoi (argv[2]), buf); free (buf); } else { nlog (LOG_WARNING, LOG_CORE, "Ehhh, Can't find Channel %s", argv[0]); @@ -873,7 +884,7 @@ Srv_Netinfo (char *origin, char **argv, int argc) snetinfo_cmd (); init_ServBot (); globops (me.name, "Link with Network \2Complete!\2"); - Module_Event (EVENT_NETINFO, NULL, 0); + ModuleEvent (EVENT_NETINFO, NULL, 0); me.synced = 1; } diff --git a/Unreal.h b/Unreal.h index b48a79b3..93cce93d 100644 --- a/Unreal.h +++ b/Unreal.h @@ -60,6 +60,20 @@ /* we have vhost support */ #define GOTSVSVHOST +/* we have svsjoin */ +#define GOTSVSJOIN + +/* Moved from connectserv so we can use elsewhere */ +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define COSERVERADMIN_MODE 'C' +#define SERVERADMIN_MODE 'A' +#define NETADMIN_MODE 'N' +#define TECHADMIN_MODE 'T' +#define SERVICESADMIN_MODE 'a' +#define NETSERVICE_MODE 'S' +#define INVISIBLE_MODE 'I' +#define BOT_MODE 'B' /* * The tokens are in the ascii character range of 33-127, and we start @@ -486,6 +500,7 @@ extern int ssvskill_cmd (const char *target, const char *reason, ...); extern int sakill_cmd (const char *host, const char *ident, const char *setby, const int length, const char *reason, ...); extern int srakill_cmd (const char *host, const char *ident); extern int SignOn_NewBot (const char *, const char *, const char *, const char *, long); +extern int sinvite_cmd (const char *from, const char *to, const char *chan); void Usr_Version (char *, char **, int argc); void Usr_ShowMOTD (char *, char **, int argc); diff --git a/chans.c b/chans.c index 369932f7..769e7572 100644 --- a/chans.c +++ b/chans.c @@ -26,6 +26,8 @@ #include "dl.h" #include "hash.h" #include "log.h" +#include "users.h" +#include "chans.h" /** @brief Chanmem structure * @@ -37,6 +39,8 @@ typedef struct Chanmem { void *moddata[NUM_MODULES]; } Chanmem; +hash_t *ch; + /** @brief initialize the channel data * * initializes the channel data and channel hash ch. @@ -65,7 +69,7 @@ init_chan_hash () * */ void -Change_Chan_Ts (Chans * c, time_t tstime) +ChangeChanTS (Chans * c, time_t tstime) { if (!c) { nlog (LOG_WARNING, LOG_CORE, "Warning, Called Change_Change_Ts with null channel"); @@ -90,7 +94,7 @@ Change_Chan_Ts (Chans * c, time_t tstime) */ extern void -Change_Topic (char *owner, Chans * c, time_t time, char *topic) +ChangeTopic (char *owner, Chans * c, time_t time, char *topic) { char **av; int ac = 0; @@ -100,9 +104,8 @@ Change_Topic (char *owner, Chans * c, time_t time, char *topic) AddStringToList (&av, c->name, &ac); AddStringToList (&av, owner, &ac); AddStringToList (&av, topic, &ac); - Module_Event (EVENT_TOPICCHANGE, av, ac); + ModuleEvent (EVENT_TOPICCHANGE, av, ac); free (av); -// FreeList(av, ac); } /** @brief Check if a mode is set on a Channel @@ -309,7 +312,7 @@ ChangeChanUserMode (Chans * c, User * u, int add, long mode) if (!cmn) { if (me.debug_mode) { chanalert (s_Services, "ChangeChanUserMode() %s doesn't seem to be in the Chan %s", u->nick, c->name); - chandump (c->name); + ChanDump (c->name); UserDump (u->nick); } return; @@ -412,7 +415,7 @@ kick_chan (User * u, char *chan, User * k) nlog (LOG_WARNING, LOG_CORE, "NULL user passed to kick_chan u=NULL, chan=%s: %s", chan, recbuf); if (me.debug_mode) { chanalert (s_Services, "NULL user passed to kick_chan u=NULL, chan=%s: %s", chan, recbuf); - chandump (chan); + ChanDump (chan); } return; } @@ -420,7 +423,7 @@ kick_chan (User * u, char *chan, User * k) nlog (LOG_WARNING, LOG_CORE, "NULL user passed to kick_chan k=NULL, chan=%s: %s", chan, recbuf); if (me.debug_mode) { chanalert (s_Services, "NULL user passed to kick_chan k=NULL, chan=%s: %s", chan, recbuf); - chandump (chan); + ChanDump (chan); } return; } @@ -435,7 +438,7 @@ kick_chan (User * u, char *chan, User * k) nlog (LOG_WARNING, LOG_CORE, "Kick: hu, User %s isn't a member of this channel %s", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "Kick: hu, User %s isn't a member of this channel %s", u->nick, chan); - chandump (c->name); + ChanDump (c->name); UserDump (u->nick); } } else { @@ -444,7 +447,8 @@ kick_chan (User * u, char *chan, User * k) free (cm); AddStringToList (&av, c->name, &ac); AddStringToList (&av, u->nick, &ac); - Module_Event (EVENT_KICK, av, ac); + AddStringToList (&av, k->nick, &ac); + ModuleEvent (EVENT_KICK, av, ac); free (av); ac = 0; c->cur_users--; @@ -454,7 +458,8 @@ kick_chan (User * u, char *chan, User * k) del_bot_from_chan (u->nick, c->name); AddStringToList (&av, c->name, &ac); AddStringToList (&av, u->nick, &ac); - Module_Event (EVENT_KICKBOT, av, ac); + AddStringToList (&av, k->nick, &ac); + ModuleEvent (EVENT_KICKBOT, av, ac); free (av); ac = 0; @@ -464,7 +469,7 @@ kick_chan (User * u, char *chan, User * k) nlog (LOG_WARNING, LOG_CORE, "Kick:Hu, User %s claims not to be part of Chan %s", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "Kick: Hu, User %s claims not to be part of Chan %s", u->nick, chan); - chandump (c->name); + ChanDump (c->name); UserDump (u->nick); } } else { @@ -473,7 +478,7 @@ kick_chan (User * u, char *chan, User * k) nlog (LOG_DEBUG3, LOG_CORE, "Cur Users %s %d (list %d)", c->name, c->cur_users, list_count (c->chanmembers)); if (c->cur_users <= 0) { AddStringToList (&av, c->name, &ac); - Module_Event (EVENT_DELCHAN, av, ac); + ModuleEvent (EVENT_DELCHAN, av, ac); free (av); ac = 0; del_chan (c); @@ -508,7 +513,7 @@ part_chan (User * u, char *chan) nlog (LOG_WARNING, LOG_CORE, "NULL user passed to part_chan u=NULL, chan=%s: %s", chan, recbuf); if (me.debug_mode) { chanalert (s_Services, "NULL user passed to part_chan u=NULL, chan=%s: %s", chan, recbuf); - chandump (chan); + ChanDump (chan); } return; } @@ -523,7 +528,7 @@ part_chan (User * u, char *chan) nlog (LOG_WARNING, LOG_CORE, "hu, User %s isn't a member of this channel %s", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "hu, User %s isn't a member of this channel %s", u->nick, chan); - chandump (c->name); + ChanDump (c->name); UserDump (u->nick); } } else { @@ -532,10 +537,9 @@ part_chan (User * u, char *chan) free (cm); AddStringToList (&av, c->name, &ac); AddStringToList (&av, u->nick, &ac); - Module_Event (EVENT_PARTCHAN, av, ac); + ModuleEvent (EVENT_PARTCHAN, av, ac); free (av); ac = 0; -// FreeList(av, ac); c->cur_users--; } if (findbot (u->nick)) { @@ -543,7 +547,7 @@ part_chan (User * u, char *chan) del_bot_from_chan (u->nick, c->name); AddStringToList (&av, c->name, &ac); AddStringToList (&av, u->nick, &ac); - Module_Event (EVENT_PARTBOT, av, ac); + ModuleEvent (EVENT_PARTBOT, av, ac); free (av); ac = 0; } @@ -552,7 +556,7 @@ part_chan (User * u, char *chan) nlog (LOG_WARNING, LOG_CORE, "Hu, User %s claims not to be part of Chan %s", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "Hu, User %s claims not to be part of Chan %s", u->nick, chan); - chandump (c->name); + ChanDump (c->name); UserDump (u->nick); } return; @@ -562,10 +566,9 @@ part_chan (User * u, char *chan) nlog (LOG_DEBUG3, LOG_CORE, "Cur Users %s %d (list %d)", c->name, c->cur_users, list_count (c->chanmembers)); if (c->cur_users <= 0) { AddStringToList (&av, c->name, &ac); - Module_Event (EVENT_DELCHAN, av, ac); + ModuleEvent (EVENT_DELCHAN, av, ac); free (av); ac = 0; -// FreeList(av, ac); del_chan (c); } } @@ -594,7 +597,7 @@ change_user_nick (Chans * c, char *newnick, char *oldnick) nlog (LOG_WARNING, LOG_CORE, "change_user_nick() %s isn't a member of %s", oldnick, c->name); if (me.debug_mode) { chanalert (s_Services, "change_user_nick() %s isn't a member of %s", oldnick, c->name); - chandump (c->name); + ChanDump (c->name); UserDump (oldnick); } return; @@ -651,7 +654,7 @@ join_chan (User * u, char *chan) c->modes = 0; c->tstime = 0; AddStringToList (&av, c->name, &ac); - Module_Event (EVENT_NEWCHAN, av, ac); + ModuleEvent (EVENT_NEWCHAN, av, ac); free (av); ac = 0; } @@ -666,7 +669,7 @@ join_chan (User * u, char *chan) nlog (LOG_WARNING, LOG_CORE, "Adding %s to Chan %s when he is already a member?", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "Adding %s to Chan %s when he is already a member?", u->nick, chan); - chandump (c->name); + ChanDump (c->name); UserDump (u->nick); } return; @@ -689,7 +692,7 @@ join_chan (User * u, char *chan) } AddStringToList (&av, c->name, &ac); AddStringToList (&av, u->nick, &ac); - Module_Event (EVENT_JOINCHAN, av, ac); + ModuleEvent (EVENT_JOINCHAN, av, ac); free (av); nlog (LOG_DEBUG3, LOG_CORE, "Cur Users %s %d (list %d)", c->name, c->cur_users, list_count (c->chanmembers)); if (findbot (u->nick)) { @@ -711,7 +714,7 @@ join_chan (User * u, char *chan) void -chandump (char *chan) +ChanDump (char *chan) { hnode_t *cn; lnode_t *cmn; diff --git a/chans.h b/chans.h new file mode 100644 index 00000000..7298d8d0 --- /dev/null +++ b/chans.h @@ -0,0 +1,38 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond, Mark Hetherington +** http://www.neostats.net/ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ + +#ifndef _CHANS_H_ +#define _CHANS_H_ + +void ChanDump (char *chan); +void part_chan (User * u, char *chan); +void join_chan (User * u, char *chan); +void change_user_nick (Chans * c, char *newnick, char *oldnick); +int ChanMode (char *origin, char **av, int ac); +void ChangeTopic (char *, Chans *, time_t t, char *); +void ChangeChanUserMode (Chans * c, User * u, int add, long mode); +void kick_chan (User *, char *, User *); +void ChangeChanTS (Chans * c, time_t tstime); +int init_chan_hash (void); + +#endif /* _CHANS_H_ */ diff --git a/config.h.in b/config.h.in index 185c6f73..c9ebf4e8 100644 --- a/config.h.in +++ b/config.h.in @@ -138,6 +138,9 @@ /* enable Ircu support */ #undef IRCU +/* enable Ircu support */ +#undef LIQUID + /* Version number of package */ #undef VERSION diff --git a/configure b/configure index efd45df5..e8ca29b3 100755 --- a/configure +++ b/configure @@ -309,7 +309,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP RANLIB ac_ct_RANLIB EGREP GTK_CONFIG build_configtool IRCD_FILES_SRC IRCD_FILES_OBJS MODULES EXTAUTH_SRC EXTAUTH_OBJS PACKAGE VERSION LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP RANLIB ac_ct_RANLIB EGREP GTK_CONFIG build_configtool DIRINST LINK_SIZE MATCH_LIMIT NEWLINE PCRE_MAJOR PCRE_MINOR PCRE_DATE PCRE_VERSION PCRE_LIB_VERSION PCRE_POSIXLIB_VERSION POSIX_MALLOC_THRESHOLD UTF8 IRCD_FILES_SRC IRCD_FILES_OBJS MODULES EXTAUTH_SRC EXTAUTH_OBJS PACKAGE VERSION LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -850,10 +850,11 @@ Optional Features: --enable-mystic - enable Mystic IRCD Support --enable-bahamut - enable Bahamut IRCD Support --enable-ircu - enable IRCu IRCD Support + --enable-liquid - enable Liquid IRCD Support --enable-auth=TYPE - what type of Authentication to use. See Readme File --enable-raw - Enable Raw command --enable-modules="MODULES" - space seperated list of modules to compile ---enable-configtool - Endable building of the configtool program (Needs X windows) +--enable-configtool - Enable building of the configtool program (Needs X windows) Some influential environment variables: CC C compiler command @@ -1282,10 +1283,10 @@ cat >>confdefs.h <<\_ACEOF _ACEOF cat >>confdefs.h <<\_ACEOF -#define REV 8 +#define REV 9 _ACEOF -VERSION=2.5.8 +VERSION=2.5.9 CFLAGS="$CFLAGS -O2 -g" @@ -3332,7 +3333,8 @@ done -for ac_header in sys/time.h unistd.h + +for ac_header in sys/time.h unistd.h limits.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then @@ -3474,6 +3476,812 @@ fi done +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +struct tm *tp; tp->tm_sec; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_struct_tm=sys/time.h +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6 +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for working POSIX fnmatch" >&5 +echo $ECHO_N "checking for working POSIX fnmatch... $ECHO_C" >&6 +if test "${ac_cv_func_fnmatch_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Some versions of Solaris, SCO, and the GNU C Library + # have a broken or incompatible fnmatch. + # So we run a test program. If we are cross-compiling, take no chance. + # Thanks to John Oleynick, Franc,ois Pinard, and Paul Eggert for this test. + if test "$cross_compiling" = yes; then + ac_cv_func_fnmatch_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +# define y(a, b, c) (fnmatch (a, b, c) == 0) +# define n(a, b, c) (fnmatch (a, b, c) == FNM_NOMATCH) + +int +main () +{ +exit + (!(y ("a*", "abc", 0) + && n ("d*/*1", "d/s/1", FNM_PATHNAME) + && y ("a\\\\bc", "abc", 0) + && n ("a\\\\bc", "abc", FNM_NOESCAPE) + && y ("*x", ".x", 0) + && n ("*x", ".x", FNM_PERIOD) + && 1)); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fnmatch_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fnmatch_works=no +fi +rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_fnmatch_works" >&5 +echo "${ECHO_T}$ac_cv_func_fnmatch_works" >&6 +if test $ac_cv_func_fnmatch_works = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_FNMATCH 1 +_ACEOF + +fi + + + +echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifdef signal +# undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_signal=void +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_signal=int +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + +for ac_func in strftime +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ +#ifdef __STDC__ +# include +#else +# include +#endif +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + # strftime is in -lintl on SCO UNIX. +echo "$as_me:$LINENO: checking for strftime in -lintl" >&5 +echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 +if test "${ac_cv_lib_intl_strftime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lintl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime (); +int +main () +{ +strftime (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_intl_strftime=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_intl_strftime=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5 +echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 +if test $ac_cv_lib_intl_strftime = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_STRFTIME 1 +_ACEOF + +LIBS="-lintl $LIBS" +fi + +fi +done + + + + +for ac_func in strlcpy strlcat strnlen +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ +#ifdef __STDC__ +# include +#else +# include +#endif +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + +for ac_func in socket strdup +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ +#ifdef __STDC__ +# include +#else +# include +#endif +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + + +for ac_func in bcopy memmove strerror +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ +#ifdef __STDC__ +# include +#else +# include +#endif +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + + echo "$as_me:$LINENO: checking for inet_aton" >&5 echo $ECHO_N "checking for inet_aton... $ECHO_C" >&6 @@ -4274,724 +5082,19 @@ fi fi -echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 -echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 -if test "${ac_cv_c_const+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -int -main () -{ -/* FIXME: Include the comments suggested by Paul. */ -#ifndef __cplusplus - /* Ultrix mips cc rejects this. */ - typedef int charset[2]; - const charset x; - /* SunOS 4.1.1 cc rejects this. */ - char const *const *ccp; - char **p; - /* NEC SVR4.0.2 mips cc rejects this. */ - struct point {int x, y;}; - static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. - It does not let you subtract one const X* pointer from another in - an arm of an if-expression whose if-part is not a constant - expression */ - const char *g = "string"; - ccp = &g + (g ? g-g : 0); - /* HPUX 7.0 cc rejects these. */ - ++ccp; - p = (char**) ccp; - ccp = (char const *const *) p; - { /* SCO 3.2v4 cc rejects this. */ - char *t; - char const *s = 0 ? (char *) 0 : (char const *) 0; +PCRE_MAJOR=4 +PCRE_MINOR=3 +PCRE_DATE=21-May-2003 +PCRE_VERSION=${PCRE_MAJOR}.${PCRE_MINOR} - *t++ = 0; - } - { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ - int x[] = {25, 17}; - const int *foo = &x[0]; - ++foo; - } - { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ - typedef const int *iptr; - iptr p = 0; - ++p; - } - { /* AIX XL C 1.02.0.0 rejects this saying - "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; }; - struct s *b; b->j = 5; - } - { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ - const int foo = 10; - } -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_c_const=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_c_const=no -fi -rm -f conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 -echo "${ECHO_T}$ac_cv_c_const" >&6 -if test $ac_cv_c_const = no; then cat >>confdefs.h <<\_ACEOF -#define const -_ACEOF - -fi - -echo "$as_me:$LINENO: checking for size_t" >&5 -echo $ECHO_N "checking for size_t... $ECHO_C" >&6 -if test "${ac_cv_type_size_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -if ((size_t *) 0) - return 0; -if (sizeof (size_t)) - return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_type_size_t=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_type_size_t=no -fi -rm -f conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 -echo "${ECHO_T}$ac_cv_type_size_t" >&6 -if test $ac_cv_type_size_t = yes; then - : -else - -cat >>confdefs.h <<_ACEOF -#define size_t unsigned -_ACEOF - -fi - -echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 -echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 -if test "${ac_cv_header_time+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include - -int -main () -{ -if ((struct tm *) 0) -return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_header_time=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_header_time=no -fi -rm -f conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 -echo "${ECHO_T}$ac_cv_header_time" >&6 -if test $ac_cv_header_time = yes; then - -cat >>confdefs.h <<\_ACEOF -#define TIME_WITH_SYS_TIME 1 -_ACEOF - -fi - -echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 -echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6 -if test "${ac_cv_struct_tm+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include - -int -main () -{ -struct tm *tp; tp->tm_sec; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_struct_tm=time.h -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_struct_tm=sys/time.h -fi -rm -f conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 -echo "${ECHO_T}$ac_cv_struct_tm" >&6 -if test $ac_cv_struct_tm = sys/time.h; then - -cat >>confdefs.h <<\_ACEOF -#define TM_IN_SYS_TIME 1 -_ACEOF - -fi - - -echo "$as_me:$LINENO: checking for working POSIX fnmatch" >&5 -echo $ECHO_N "checking for working POSIX fnmatch... $ECHO_C" >&6 -if test "${ac_cv_func_fnmatch_works+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # Some versions of Solaris, SCO, and the GNU C Library - # have a broken or incompatible fnmatch. - # So we run a test program. If we are cross-compiling, take no chance. - # Thanks to John Oleynick, Franc,ois Pinard, and Paul Eggert for this test. - if test "$cross_compiling" = yes; then - ac_cv_func_fnmatch_works=cross -else - cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -# define y(a, b, c) (fnmatch (a, b, c) == 0) -# define n(a, b, c) (fnmatch (a, b, c) == FNM_NOMATCH) - -int -main () -{ -exit - (!(y ("a*", "abc", 0) - && n ("d*/*1", "d/s/1", FNM_PATHNAME) - && y ("a\\\\bc", "abc", 0) - && n ("a\\\\bc", "abc", FNM_NOESCAPE) - && y ("*x", ".x", 0) - && n ("*x", ".x", FNM_PERIOD) - && 1)); - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_fnmatch_works=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_func_fnmatch_works=no -fi -rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_func_fnmatch_works" >&5 -echo "${ECHO_T}$ac_cv_func_fnmatch_works" >&6 -if test $ac_cv_func_fnmatch_works = yes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_FNMATCH 1 -_ACEOF - -fi - - - -echo "$as_me:$LINENO: checking return type of signal handlers" >&5 -echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 -if test "${ac_cv_type_signal+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#ifdef signal -# undef signal -#endif -#ifdef __cplusplus -extern "C" void (*signal (int, void (*)(int)))(int); -#else -void (*signal ()) (); -#endif - -int -main () -{ -int i; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_type_signal=void -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_type_signal=int -fi -rm -f conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 -echo "${ECHO_T}$ac_cv_type_signal" >&6 - -cat >>confdefs.h <<_ACEOF -#define RETSIGTYPE $ac_cv_type_signal +#define POSIX_MALLOC_THRESHOLD 10 _ACEOF -for ac_func in strftime -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ -#ifdef __STDC__ -# include -#else -# include -#endif -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -else - # strftime is in -lintl on SCO UNIX. -echo "$as_me:$LINENO: checking for strftime in -lintl" >&5 -echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 -if test "${ac_cv_lib_intl_strftime+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lintl $LIBS" -cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char strftime (); -int -main () -{ -strftime (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_intl_strftime=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_intl_strftime=no -fi -rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5 -echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 -if test $ac_cv_lib_intl_strftime = yes; then - cat >>confdefs.h <<\_ACEOF -#define HAVE_STRFTIME 1 -_ACEOF - -LIBS="-lintl $LIBS" -fi - -fi -done - - - - -for ac_func in strlcpy strlcat strnlen -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ -#ifdef __STDC__ -# include -#else -# include -#endif -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - - - -for ac_func in socket strdup -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -#line $LINENO "configure" -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ -#ifdef __STDC__ -# include -#else -# include -#endif -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - echo "$as_me:$LINENO: checking Whether to Enable Debuging..." >&5 echo $ECHO_N "checking Whether to Enable Debuging...... $ECHO_C" >&6 @@ -5272,6 +5375,39 @@ else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi; + +echo "$as_me:$LINENO: checking Whether to Enable Liquid IRCD Support..." >&5 +echo $ECHO_N "checking Whether to Enable Liquid IRCD Support...... $ECHO_C" >&6 +# Check whether --enable-liquid or --disable-liquid was given. +if test "${enable_liquid+set}" = set; then + enableval="$enable_liquid" + case "$enableval" in + yes) + +cat >>confdefs.h <<\_ACEOF +#define LIQUID 1 +_ACEOF + + IRCD_FILES_SRC="liquidircd.c" + IRCD_FILES_OBJS="liquidircd.o" + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + *) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; +esac +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + +fi; + + + + + do_serviceroots=no echo "$as_me:$LINENO: checking A compatible IRCD is specified" >&5 echo $ECHO_N "checking A compatible IRCD is specified... $ECHO_C" >&6 @@ -5319,6 +5455,10 @@ echo "${ECHO_T}yes" >&6 ;; "neoircd.c") echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + ;; + "liquidircd.c") + echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 ;; *) @@ -5505,9 +5645,29 @@ echo "${ECHO_T}$MODULES" >&6 + + + +cat >>confdefs.h <<\_ACEOF +#define POSIX_MALLOC_THRESHOLD 10 +_ACEOF + + + + + + + + + + + + ac_config_headers="$ac_config_headers adns/config.h" - ac_config_files="$ac_config_files Makefile.inc Makefile tools/Makefile" + ac_config_headers="$ac_config_headers pcre/config.h" + + ac_config_files="$ac_config_files Makefile.inc Makefile tools/Makefile pcre/Makefile pcre.h:pcre/pcre.in" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -6035,8 +6195,11 @@ do "Makefile.inc" ) CONFIG_FILES="$CONFIG_FILES Makefile.inc" ;; "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; "tools/Makefile" ) CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;; + "pcre/Makefile" ) CONFIG_FILES="$CONFIG_FILES pcre/Makefile" ;; + "pcre.h" ) CONFIG_FILES="$CONFIG_FILES pcre.h:pcre/pcre.in" ;; "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "adns/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS adns/config.h" ;; + "pcre/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS pcre/config.h" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; @@ -6137,6 +6300,18 @@ s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t s,@EGREP@,$EGREP,;t t s,@GTK_CONFIG@,$GTK_CONFIG,;t t s,@build_configtool@,$build_configtool,;t t +s,@DIRINST@,$DIRINST,;t t +s,@LINK_SIZE@,$LINK_SIZE,;t t +s,@MATCH_LIMIT@,$MATCH_LIMIT,;t t +s,@NEWLINE@,$NEWLINE,;t t +s,@PCRE_MAJOR@,$PCRE_MAJOR,;t t +s,@PCRE_MINOR@,$PCRE_MINOR,;t t +s,@PCRE_DATE@,$PCRE_DATE,;t t +s,@PCRE_VERSION@,$PCRE_VERSION,;t t +s,@PCRE_LIB_VERSION@,$PCRE_LIB_VERSION,;t t +s,@PCRE_POSIXLIB_VERSION@,$PCRE_POSIXLIB_VERSION,;t t +s,@POSIX_MALLOC_THRESHOLD@,$POSIX_MALLOC_THRESHOLD,;t t +s,@UTF8@,$UTF8,;t t s,@IRCD_FILES_SRC@,$IRCD_FILES_SRC,;t t s,@IRCD_FILES_OBJS@,$IRCD_FILES_OBJS,;t t s,@MODULES@,$MODULES,;t t diff --git a/configure.in b/configure.in index 2b195200..6e2370e4 100644 --- a/configure.in +++ b/configure.in @@ -4,8 +4,8 @@ AC_CONFIG_HEADER(config.h) PACKAGE=NeoStats AC_DEFINE(MAJOR, 2) AC_DEFINE(MINOR, 5) -AC_DEFINE(REV, 8) -VERSION=2.5.8 +AC_DEFINE(REV, 9) +VERSION=2.5.9 AC_PREFIX_DEFAULT(~/NeoStats) CFLAGS="$CFLAGS -O2 -g" @@ -33,7 +33,22 @@ LIBS="-g $LIBS"] dnl Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS(sys/time.h unistd.h) +AC_CHECK_HEADERS(sys/time.h unistd.h limits.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM + +dnl Checks for library functions. +AC_FUNC_FNMATCH +AC_TYPE_SIGNAL +AC_FUNC_STRFTIME +AC_CHECK_FUNCS(strlcpy strlcat strnlen) +AC_CHECK_FUNCS(socket strdup) +AC_CHECK_FUNCS(bcopy memmove strerror) + ADNS_C_GETFUNC(inet_aton,resolv,[ LIBS="-lresolv $LIBS"; @@ -72,18 +87,17 @@ else fi -dnl Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_TYPE_SIZE_T -AC_HEADER_TIME -AC_STRUCT_TM -dnl Checks for library functions. -AC_FUNC_FNMATCH -AC_TYPE_SIGNAL -AC_FUNC_STRFTIME -AC_CHECK_FUNCS(strlcpy strlcat strnlen) -AC_CHECK_FUNCS(socket strdup) +PCRE_MAJOR=4 +PCRE_MINOR=3 +PCRE_DATE=21-May-2003 +PCRE_VERSION=${PCRE_MAJOR}.${PCRE_MINOR} + +dnl Default values for miscellaneous macros + +AC_DEFINE(POSIX_MALLOC_THRESHOLD,10) + + dnl check if we are running with Debug.... AC_MSG_CHECKING(Whether to Enable Debuging...) @@ -249,6 +263,28 @@ dnl AC_MSG_ERROR('Sorry IRCu support isnt complete yet') ;; esac], AC_MSG_RESULT(no)) + +dnl check to see if we should enable liquid IRCD Support +AC_MSG_CHECKING(Whether to Enable Liquid IRCD Support...) +AC_ARG_ENABLE(liquid, [ --enable-liquid - enable Liquid IRCD Support], +[ case "$enableval" in + yes) + AC_DEFINE(LIQUID, 1, 'Enable Liquid IRCd Support') + IRCD_FILES_SRC="liquidircd.c" + IRCD_FILES_OBJS="liquidircd.o" + AC_MSG_RESULT(yes) + ;; + *) + AC_MSG_RESULT(no) + ;; +esac], +AC_MSG_RESULT(no) +) + + + + + do_serviceroots=no AC_MSG_CHECKING(A compatible IRCD is specified) case "$IRCD_FILES_SRC" in @@ -283,6 +319,9 @@ case "$IRCD_FILES_SRC" in "neoircd.c") AC_MSG_RESULT(yes) ;; + "liquidircd.c") + AC_MSG_RESULT(yes) + ;; *) AC_MSG_RESULT(no) AC_MSG_ERROR('You must define a IRCD to use. See ./configure --help for more information') @@ -333,7 +372,7 @@ AC_MSG_RESULT(none) configtool=no AC_MSG_CHECKING(Enable Building of ConfigTool?) AC_ARG_ENABLE(configtool, -[--enable-configtool - Endable building of the configtool program (Needs X windows)], +[--enable-configtool - Enable building of the configtool program (Needs X windows)], [if test "$enableval" = yes; then configtool=yes; else configtool=no; fi]) if test "$configtool" = yes; then @@ -374,6 +413,21 @@ AC_MSG_RESULT(Thats Impressive!!!) AC_MSG_CHECKING(The Modules that will be compiled) AC_MSG_RESULT($MODULES) +AC_SUBST(DIRINST) +AC_SUBST(CFLAGS) +AC_SUBST(LDFLAGS) +AC_SUBST(LINK_SIZE) +AC_SUBST(MATCH_LIMIT) +AC_SUBST(NEWLINE) +AC_SUBST(PCRE_MAJOR) +AC_SUBST(PCRE_MINOR) +AC_SUBST(PCRE_DATE) +AC_SUBST(PCRE_VERSION) +AC_SUBST(PCRE_LIB_VERSION) +AC_SUBST(PCRE_POSIXLIB_VERSION) +AC_DEFINE(POSIX_MALLOC_THRESHOLD,10) +AC_SUBST(POSIX_MALLOC_THRESHOLD) +AC_SUBST(UTF8) AC_SUBST(IRCD_FILES_SRC) AC_SUBST(IRCD_FILES_OBJS) @@ -384,7 +438,8 @@ AC_SUBST(EXTAUTH_OBJS) AC_SUBST(PACKAGE) AC_SUBST(VERSION) AC_CONFIG_HEADER(adns/config.h) -AC_OUTPUT(Makefile.inc Makefile tools/Makefile) +AC_CONFIG_HEADER(pcre/config.h) +AC_OUTPUT(Makefile.inc Makefile tools/Makefile pcre/Makefile pcre.h:pcre/pcre.in) echo "(*----------------------------------------------------------*)" echo "(| Important Instructions |)" echo "(*----------------------------------------------------------*)" diff --git a/dl.c b/dl.c index e15a8898..0ac2d1f3 100644 --- a/dl.c +++ b/dl.c @@ -41,6 +41,21 @@ */ Module *ModList[NUM_MODULES]; +char segv_location[SEGV_LOCATION_BUFSIZE]; +char segv_inmodule[SEGV_INMODULE_BUFSIZE]; + + +/* @brief Module hash list */ +static hash_t *mh; +/* @brief Module Socket List hash */ +hash_t *sockh; +/* @brief Module Timer hash list */ +static hash_t *th; +/* @brief Module Bot hash list */ +static hash_t *bh; +/* @brief Module Chan Bot hash list */ +static hash_t *bch; + /** @brief Initialise module list hashes * * For core use only, initialises module list hashes @@ -50,7 +65,7 @@ Module *ModList[NUM_MODULES]; * @return none */ int -__init_mod_list () +InitModuleHash () { SET_SEGV_LOCATION(); mh = hash_create (NUM_MODULES, 0, 0); @@ -71,6 +86,24 @@ __init_mod_list () return NS_SUCCESS; } +#ifdef DEBUG +void verify_hashes(void) +{ + if (hash_verify (sockh) == 0) { + nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the socket hash"); + } + if (hash_verify (mh) == 0) { + nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the Module hash"); + } + if (hash_verify (bh) == 0) { + nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the Bot hash"); + } + if (hash_verify (th) == 0) { + nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the Timer hash"); + } +} +#endif /* DEBUG */ + /** @brief create new timer * * For core use only, creates a timer @@ -79,26 +112,26 @@ __init_mod_list () * * @return pointer to new timer on success, NULL on error */ -static Mod_Timer * +static ModTimer * new_timer (char *timer_name) { - Mod_Timer *t; + ModTimer *mod_tmr; hnode_t *tn; SET_SEGV_LOCATION(); nlog (LOG_DEBUG2, LOG_CORE, "New Timer: %s", timer_name); - t = malloc (sizeof (Mod_Timer)); + mod_tmr = malloc (sizeof (ModTimer)); if (!timer_name) - strsetnull (t->timername); + strsetnull (mod_tmr->timername); else - strlcpy (t->timername, timer_name, MAX_MOD_NAME); - tn = hnode_create (t); + strlcpy (mod_tmr->timername, timer_name, MAX_MOD_NAME); + tn = hnode_create (mod_tmr); if (hash_isfull (th)) { nlog (LOG_WARNING, LOG_CORE, "new_timer(): Couldn't add new Timer, Hash is Full!"); return NULL; } hash_insert (th, tn, timer_name); - return t; + return mod_tmr; } /** @brief find timer @@ -109,14 +142,14 @@ new_timer (char *timer_name) * * @return pointer to timer if found, NULL if not found */ -Mod_Timer * +ModTimer * findtimer (char *timer_name) { hnode_t *tn; tn = hash_lookup (th, timer_name); if (tn) - return (Mod_Timer *) hnode_get (tn); + return (ModTimer *) hnode_get (tn); return NULL; } @@ -134,19 +167,19 @@ findtimer (char *timer_name) int add_mod_timer (char *func_name, char *timer_name, char *mod_name, int interval) { - Mod_Timer *Mod_timer_list; + ModTimer *mod_tmr; SET_SEGV_LOCATION(); if (dlsym ((int *) get_dl_handle (mod_name), func_name) == NULL) { nlog (LOG_WARNING, LOG_CORE, "Oh Oh, The Timer Function doesn't exist"); return NS_FAILURE; } - Mod_timer_list = new_timer (timer_name); - if (Mod_timer_list) { - Mod_timer_list->interval = interval; - Mod_timer_list->lastrun = me.now; - strlcpy (Mod_timer_list->modname, mod_name, MAX_MOD_NAME); - Mod_timer_list->function = dlsym ((int *) get_dl_handle (mod_name), func_name); + mod_tmr = new_timer (timer_name); + if (mod_tmr) { + mod_tmr->interval = interval; + mod_tmr->lastrun = me.now; + strlcpy (mod_tmr->modname, mod_name, MAX_MOD_NAME); + mod_tmr->function = dlsym ((int *) get_dl_handle (mod_name), func_name); nlog (LOG_DEBUG2, LOG_CORE, "Registered Module %s with timer for Function %s", mod_name, func_name); return NS_SUCCESS; } @@ -164,17 +197,17 @@ add_mod_timer (char *func_name, char *timer_name, char *mod_name, int interval) int del_mod_timer (char *timer_name) { - Mod_Timer *list; + ModTimer *mod_tmr; hnode_t *tn; SET_SEGV_LOCATION(); tn = hash_lookup (th, timer_name); if (tn) { - list = hnode_get (tn); - nlog (LOG_DEBUG2, LOG_CORE, "Unregistered Timer function %s from Module %s", timer_name, list->modname); + mod_tmr = hnode_get (tn); + nlog (LOG_DEBUG2, LOG_CORE, "Unregistered Timer function %s from Module %s", timer_name, mod_tmr->modname); hash_delete (th, tn); hnode_destroy (tn); - free (list); + free (mod_tmr); return NS_SUCCESS; } return NS_FAILURE; @@ -189,9 +222,9 @@ del_mod_timer (char *timer_name) * @return none */ void -list_module_timer (User * u) +list_timers (User * u, char **av, int ac) { - Mod_Timer *mod_ptr = NULL; + ModTimer *mod_tmr = NULL; hscan_t ts; hnode_t *tn; @@ -199,15 +232,55 @@ list_module_timer (User * u) prefmsg (u->nick, s_Services, "Module timer List:"); hash_scan_begin (&ts, th); while ((tn = hash_scan_next (&ts)) != NULL) { - mod_ptr = hnode_get (tn); - prefmsg (u->nick, s_Services, "%s:--------------------------------", mod_ptr->modname); - prefmsg (u->nick, s_Services, "Module Timer Name: %s", mod_ptr->timername); - prefmsg (u->nick, s_Services, "Module Interval: %d", mod_ptr->interval); - prefmsg (u->nick, s_Services, "Time till next Run: %d", mod_ptr->interval - (me.now - mod_ptr->lastrun)); + mod_tmr = hnode_get (tn); + prefmsg (u->nick, s_Services, "%s:--------------------------------", mod_tmr->modname); + prefmsg (u->nick, s_Services, "Module Timer Name: %s", mod_tmr->timername); + prefmsg (u->nick, s_Services, "Module Interval: %d", mod_tmr->interval); + prefmsg (u->nick, s_Services, "Time till next Run: %d", mod_tmr->interval - (me.now - mod_tmr->lastrun)); } prefmsg (u->nick, s_Services, "End of Module timer List"); } +/** @brief run pending module timer functions + * + * NeoStats command to run pending timer functions + * + * @param none + * + * @return none +*/ +void +run_mod_timers (void) +{ + ModTimer *mod_tmr = NULL; + hscan_t ts; + hnode_t *tn; + +/* First, lets see if any modules have a function that is due to run..... */ + hash_scan_begin (&ts, th); + while ((tn = hash_scan_next (&ts)) != NULL) { + SET_SEGV_LOCATION(); + mod_tmr = hnode_get (tn); + if (me.now - mod_tmr->lastrun > mod_tmr->interval) { + strlcpy (segv_location, mod_tmr->modname, SEGV_LOCATION_BUFSIZE); + SET_SEGV_INMODULE(mod_tmr->modname); + if (setjmp (sigvbuf) == 0) { + if (mod_tmr->function () < 0) { + nlog(LOG_DEBUG2, LOG_CORE, "Deleting Timer %s for Module %s as requested", mod_tmr->timername, mod_tmr->modname); + hash_scan_delete(th, tn); + hnode_destroy(tn); + free(mod_tmr); + } else { + mod_tmr->lastrun = (int) me.now; + } + } else { + nlog (LOG_CRITICAL, LOG_CORE, "setjmp() Failed, Can't call Module %s\n", mod_tmr->modname); + } + CLEAR_SEGV_INMODULE(); + } + } +} + /** @brief create a new socket * * For core use only, creates a new socket for a module @@ -216,26 +289,26 @@ list_module_timer (User * u) * * @return pointer to created socket on success, NULL on error */ -static Sock_List * +static ModSock * new_sock (char *sock_name) { - Sock_List *s; + ModSock *mod_sock; hnode_t *sn; SET_SEGV_LOCATION(); - nlog (LOG_DEBUG2, LOG_CORE, "New Socket: %s", sock_name); - s = smalloc (sizeof (Sock_List)); + nlog (LOG_DEBUG2, LOG_CORE, "New Socket: %mod_sock", sock_name); + mod_sock = smalloc (sizeof (ModSock)); if (!sock_name) - strsetnull (s->sockname); + strsetnull (mod_sock->sockname); else - strlcpy (s->sockname, sock_name, MAX_MOD_NAME); - sn = hnode_create (s); + strlcpy (mod_sock->sockname, sock_name, MAX_MOD_NAME); + sn = hnode_create (mod_sock); if (hash_isfull (sockh)) { nlog (LOG_CRITICAL, LOG_CORE, "Eeek, SocketHash is full, can not add a new socket"); return NULL; } - hash_insert (sockh, sn, s->sockname); - return s; + hash_insert (sockh, sn, mod_sock->sockname); + return mod_sock; } /** \fn @brief find socket @@ -246,7 +319,7 @@ new_sock (char *sock_name) * * @return pointer to socket if found, NULL if not found */ -Sock_List * +ModSock * findsock (char *sock_name) { hnode_t *sn; @@ -273,7 +346,7 @@ findsock (char *sock_name) int add_socket (char *readfunc, char *writefunc, char *errfunc, char *sock_name, int socknum, char *mod_name) { - Sock_List *Sockets_mod_list; + ModSock *mod_sock; SET_SEGV_LOCATION(); if (readfunc) { @@ -294,15 +367,15 @@ add_socket (char *readfunc, char *writefunc, char *errfunc, char *sock_name, int return NS_FAILURE; } } - Sockets_mod_list = new_sock (sock_name); - Sockets_mod_list->sock_no = socknum; - strlcpy (Sockets_mod_list->modname, mod_name, MAX_MOD_NAME); - Sockets_mod_list->readfnc = dlsym ((int *) get_dl_handle (mod_name), readfunc); - Sockets_mod_list->writefnc = dlsym ((int *) get_dl_handle (mod_name), writefunc); - Sockets_mod_list->errfnc = dlsym ((int *) get_dl_handle (mod_name), errfunc); - Sockets_mod_list->socktype = SOCK_STANDARD; + mod_sock = new_sock (sock_name); + mod_sock->sock_no = socknum; + strlcpy (mod_sock->modname, mod_name, MAX_MOD_NAME); + mod_sock->readfnc = dlsym ((int *) get_dl_handle (mod_name), readfunc); + mod_sock->writefnc = dlsym ((int *) get_dl_handle (mod_name), writefunc); + mod_sock->errfnc = dlsym ((int *) get_dl_handle (mod_name), errfunc); + mod_sock->socktype = SOCK_STANDARD; - nlog (LOG_DEBUG2, LOG_CORE, "Registered Module %s with Standard Socket functions %s", mod_name, Sockets_mod_list->sockname); + nlog (LOG_DEBUG2, LOG_CORE, "Registered Module %s with Standard Socket functions %s", mod_name, mod_sock->sockname); return NS_SUCCESS; } @@ -320,7 +393,7 @@ add_socket (char *readfunc, char *writefunc, char *errfunc, char *sock_name, int int add_sockpoll (char *beforepoll, char *afterpoll, char *sock_name, char *mod_name, void *data) { - Sock_List *Sockets_mod_list; + ModSock *mod_sock; SET_SEGV_LOCATION(); if (beforepoll) { @@ -335,13 +408,13 @@ add_sockpoll (char *beforepoll, char *afterpoll, char *sock_name, char *mod_name return NS_FAILURE; } } - Sockets_mod_list = new_sock (sock_name); - strlcpy (Sockets_mod_list->modname, mod_name, MAX_MOD_NAME); - Sockets_mod_list->socktype = SOCK_POLL; - Sockets_mod_list->beforepoll = dlsym ((int *) get_dl_handle (mod_name), beforepoll); - Sockets_mod_list->afterpoll = dlsym ((int *) get_dl_handle (mod_name), afterpoll); - Sockets_mod_list->data = data; - nlog (LOG_DEBUG2, LOG_CORE, "Registered Module %s with Poll Socket functions %s", mod_name, Sockets_mod_list->sockname); + mod_sock = new_sock (sock_name); + strlcpy (mod_sock->modname, mod_name, MAX_MOD_NAME); + mod_sock->socktype = SOCK_POLL; + mod_sock->beforepoll = dlsym ((int *) get_dl_handle (mod_name), beforepoll); + mod_sock->afterpoll = dlsym ((int *) get_dl_handle (mod_name), afterpoll); + mod_sock->data = data; + nlog (LOG_DEBUG2, LOG_CORE, "Registered Module %s with Poll Socket functions %s", mod_name, mod_sock->sockname); return NS_SUCCESS; } @@ -356,17 +429,17 @@ add_sockpoll (char *beforepoll, char *afterpoll, char *sock_name, char *mod_name int del_socket (char *sock_name) { - Sock_List *list; + ModSock *mod_sock; hnode_t *sn; SET_SEGV_LOCATION(); sn = hash_lookup (sockh, sock_name); if (sn) { - list = hnode_get (sn); - nlog (LOG_DEBUG2, LOG_CORE, "Unregistered Socket function %s from Module %s", sock_name, list->modname); + mod_sock = hnode_get (sn); + nlog (LOG_DEBUG2, LOG_CORE, "Unregistered Socket function %s from Module %s", sock_name, mod_sock->modname); hash_scan_delete (sockh, sn); hnode_destroy (sn); - free (list); + free (mod_sock); return NS_SUCCESS; } return NS_FAILURE; @@ -381,9 +454,9 @@ del_socket (char *sock_name) * @return none */ void -list_sockets (User * u) +list_sockets (User * u, char **av, int ac) { - Sock_List *mod_ptr = NULL; + ModSock *mod_sock = NULL; hscan_t ss; hnode_t *sn; @@ -391,11 +464,11 @@ list_sockets (User * u) prefmsg (u->nick, s_Services, "Sockets List: (%d)", hash_count (sockh)); hash_scan_begin (&ss, sockh); while ((sn = hash_scan_next (&ss)) != NULL) { - mod_ptr = hnode_get (sn); - prefmsg (u->nick, s_Services, "%s:--------------------------------", mod_ptr->modname); - prefmsg (u->nick, s_Services, "Socket Name: %s", mod_ptr->sockname); - if (mod_ptr->socktype == SOCK_STANDARD) { - prefmsg (u->nick, s_Services, "Socket Number: %d", mod_ptr->sock_no); + mod_sock = hnode_get (sn); + prefmsg (u->nick, s_Services, "%s:--------------------------------", mod_sock->modname); + prefmsg (u->nick, s_Services, "Socket Name: %s", mod_sock->sockname); + if (mod_sock->socktype == SOCK_STANDARD) { + prefmsg (u->nick, s_Services, "Socket Number: %d", mod_sock->sock_no); } else { prefmsg (u->nick, s_Services, "Poll Interface"); } @@ -414,31 +487,31 @@ void add_bot_to_chan (char *bot, char *chan) { hnode_t *cbn; - Chan_Bot *bc; + ModChanBot *mod_chan_bot; lnode_t *bmn; char *botname; cbn = hash_lookup (bch, chan); if (!cbn) { - bc = malloc (sizeof (Chan_Bot)); - strlcpy (bc->chan, chan, CHANLEN); - bc->bots = list_create (B_TABLE_SIZE); - cbn = hnode_create (bc); + mod_chan_bot = malloc (sizeof (ModChanBot)); + strlcpy (mod_chan_bot->chan, chan, CHANLEN); + mod_chan_bot->bots = list_create (B_TABLE_SIZE); + cbn = hnode_create (mod_chan_bot); if (hash_isfull (bch)) { nlog (LOG_CRITICAL, LOG_CORE, "eek, bot channel hash is full"); return; } - hash_insert (bch, cbn, bc->chan); + hash_insert (bch, cbn, mod_chan_bot->chan); } else { - bc = hnode_get (cbn); + mod_chan_bot = hnode_get (cbn); } - if (list_isfull (bc->bots)) { + if (list_isfull (mod_chan_bot->bots)) { nlog (LOG_CRITICAL, LOG_CORE, "Eeek, Bot Channel List is full for Chan %s", chan); return; } botname = sstrdup (bot); bmn = lnode_create (botname); - list_append (bc->bots, bmn); + list_append (mod_chan_bot->bots, bmn); return; } @@ -453,7 +526,7 @@ void del_bot_from_chan (char *bot, char *chan) { hnode_t *cbn; - Chan_Bot *bc; + ModChanBot *mod_chan_bot; lnode_t *bmn; char *botname; @@ -462,21 +535,21 @@ del_bot_from_chan (char *bot, char *chan) nlog (LOG_WARNING, LOG_CORE, "Hu? Can't Find Channel %s for botchanhash", chan); return; } - bc = hnode_get (cbn); - bmn = list_find (bc->bots, bot, comparef); + mod_chan_bot = hnode_get (cbn); + bmn = list_find (mod_chan_bot->bots, bot, comparef); if (!bmn) { nlog (LOG_WARNING, LOG_CORE, "Hu? Can't find bot %s in %s in botchanhash", bot, chan); return; } - list_delete (bc->bots, bmn); + list_delete (mod_chan_bot->bots, bmn); botname = lnode_get(bmn); free (botname); lnode_destroy (bmn); - if (list_isempty (bc->bots)) { + if (list_isempty (mod_chan_bot->bots)) { /* delete the hash and list because its all over */ hash_delete (bch, cbn); - list_destroy (bc->bots); - free (bc->chan); + list_destroy (mod_chan_bot->bots); + free (mod_chan_bot->chan); hnode_destroy (cbn); } } @@ -484,35 +557,34 @@ del_bot_from_chan (char *bot, char *chan) /** @brief send a message to a channel bot * * @param origin - * @param chan string containing channel name - * @param av + * @param av (note chan string in av[0]) * @param ac * * @return none */ void -bot_chan_message (char *origin, char *chan, char **av, int ac) +bot_chan_message (char *origin, char **av, int ac) { hnode_t *cbn; - Chan_Bot *bc; + ModChanBot *mod_chan_bot; lnode_t *bmn; - Mod_User *u; + ModUser *mod_usr; - cbn = hash_lookup (bch, chan); + cbn = hash_lookup (bch, av[0]); if (!cbn) { /* this isn't bad, just means our bot parted the channel? */ - nlog (LOG_DEBUG1, LOG_CORE, "eeeh, Can't find channel %s for BotChanMessage", chan); + nlog (LOG_DEBUG1, LOG_CORE, "eeeh, Can't find channel %s for BotChanMessage", av[0]); return; } - bc = hnode_get (cbn); - bmn = list_first (bc->bots); + mod_chan_bot = hnode_get (cbn); + bmn = list_first (mod_chan_bot->bots); while (bmn) { - u = findbot (lnode_get (bmn)); - if (u->chanfunc) { - nlog (LOG_DEBUG2, LOG_CORE, "Running Module for Chanmessage %s", chan); - u->chanfunc (origin, chan, av, ac); + mod_usr = findbot (lnode_get (bmn)); + if (mod_usr->chanfunc) { + nlog (LOG_DEBUG2, LOG_CORE, "Running Module for Chanmessage %s", av[0]); + mod_usr->chanfunc (origin, av, ac); } - bmn = list_next (bc->bots, bmn); + bmn = list_next (mod_chan_bot->bots, bmn); } } @@ -523,22 +595,22 @@ bot_chan_message (char *origin, char *chan, char **av, int ac) * @return none */ void -botchandump (User * u) +list_bot_chans (User * u, char **av, int ac) { hscan_t hs; hnode_t *hn; lnode_t *ln; - Chan_Bot *bc; + ModChanBot *mod_chan_bot; prefmsg (u->nick, s_Services, "BotChanDump:"); hash_scan_begin (&hs, bch); while ((hn = hash_scan_next (&hs)) != NULL) { - bc = hnode_get (hn); - prefmsg (u->nick, s_Services, "%s:--------------------------------", bc->chan); - ln = list_first (bc->bots); + mod_chan_bot = hnode_get (hn); + prefmsg (u->nick, s_Services, "%s:--------------------------------", mod_chan_bot->chan); + ln = list_first (mod_chan_bot->bots); while (ln) { prefmsg (u->nick, s_Services, "Bot Name: %s", lnode_get (ln)); - ln = list_next (bc->bots, ln); + ln = list_next (mod_chan_bot->bots, ln); } } } @@ -549,26 +621,27 @@ botchandump (User * u) * * @return none */ -static Mod_User * +static ModUser * new_bot (char *bot_name) { - Mod_User *u; + ModUser *mod_usr; hnode_t *bn; SET_SEGV_LOCATION(); nlog (LOG_DEBUG2, LOG_CORE, "New Bot: %s", bot_name); - u = malloc (sizeof (Mod_User)); + mod_usr = malloc (sizeof (ModUser)); + if (!bot_name) - strsetnull (u->nick); + strsetnull (mod_usr->nick); else - strlcpy (u->nick, bot_name, MAXNICK); - bn = hnode_create (u); + strlcpy (mod_usr->nick, bot_name, MAXNICK); + bn = hnode_create (mod_usr); if (hash_isfull (bh)) { chanalert (s_Services, "Warning ModuleBotlist is full"); return NULL; } - hash_insert (bh, bn, bot_name); - return u; + hash_insert (bh, bn, mod_usr->nick); + return mod_usr; } /** @brief @@ -580,21 +653,21 @@ new_bot (char *bot_name) int add_mod_user (char *nick, char *mod_name) { - Mod_User *Mod_Usr_list; - Module *list_ptr; + ModUser *mod_usr; + Module *mod_ptr; hnode_t *mn; SET_SEGV_LOCATION(); - Mod_Usr_list = new_bot (nick); + mod_usr = new_bot (nick); /* add a brand new user */ - strlcpy (Mod_Usr_list->nick, nick, MAXNICK); - strlcpy (Mod_Usr_list->modname, mod_name, MAX_MOD_NAME); + strlcpy (mod_usr->nick, nick, MAXNICK); + strlcpy (mod_usr->modname, mod_name, MAX_MOD_NAME); mn = hash_lookup (mh, mod_name); if (mn) { - list_ptr = hnode_get (mn); - Mod_Usr_list->function = dlsym (list_ptr->dl_handle, "__Bot_Message"); - Mod_Usr_list->chanfunc = dlsym (list_ptr->dl_handle, "__Chan_Message"); + mod_ptr = hnode_get (mn); + mod_usr->function = dlsym (mod_ptr->dl_handle, "__BotMessage"); + mod_usr->chanfunc = dlsym (mod_ptr->dl_handle, "__ChanMessage"); return NS_SUCCESS; } nlog (LOG_WARNING, LOG_CORE, "add_mod_user(): Couldn't Add ModuleBot to List"); @@ -607,7 +680,7 @@ add_mod_user (char *nick, char *mod_name) * * @return */ -Mod_User * +ModUser * findbot (char *bot_name) { hnode_t *bn; @@ -615,7 +688,7 @@ findbot (char *bot_name) SET_SEGV_LOCATION(); bn = hash_lookup (bh, bot_name); if (bn) { - return (Mod_User *) hnode_get (bn); + return (ModUser *) hnode_get (bn); } return NULL; } @@ -629,16 +702,16 @@ findbot (char *bot_name) int del_mod_user (char *bot_name) { - Mod_User *list; + ModUser *mod_usr; hnode_t *bn; SET_SEGV_LOCATION(); bn = hash_lookup (bh, bot_name); if (bn) { hash_delete (bh, bn); - list = hnode_get (bn); + mod_usr = hnode_get (bn); hnode_destroy (bn); - free (list); + free (mod_usr); return NS_SUCCESS; } return NS_FAILURE; @@ -654,7 +727,7 @@ int bot_nick_change (char *oldnick, char *newnick) { User *u; - Mod_User *mod_tmp, *mod_ptr; + ModUser *mod_usr_new, *mod_usr; SET_SEGV_LOCATION(); /* First, try to find out if the newnick is unique! */ @@ -665,14 +738,14 @@ bot_nick_change (char *oldnick, char *newnick) } u = finduser (newnick); if (!u) { - if ((mod_ptr = findbot (oldnick)) != NULL) { + if ((mod_usr = findbot (oldnick)) != NULL) { nlog (LOG_DEBUG3, LOG_CORE, "Bot %s Changed its nick to %s", oldnick, newnick); - mod_tmp = new_bot (newnick); + mod_usr_new = new_bot (newnick); /* add a brand new user */ - strlcpy (mod_tmp->nick, newnick, MAXNICK); - strlcpy (mod_tmp->modname, mod_ptr->modname, MAX_MOD_NAME); - mod_tmp->function = mod_ptr->function; + strlcpy (mod_usr_new->nick, newnick, MAXNICK); + strlcpy (mod_usr_new->modname, mod_usr->modname, MAX_MOD_NAME); + mod_usr_new->function = mod_usr->function; /* Now Delete the Old bot nick */ del_mod_user (oldnick); @@ -691,9 +764,9 @@ bot_nick_change (char *oldnick, char *newnick) * @return */ void -list_module_bots (User * u) +list_bots (User * u, char **av, int ac) { - Mod_User *mod_ptr; + ModUser *mod_usr; hnode_t *bn; hscan_t bs; @@ -701,13 +774,94 @@ list_module_bots (User * u) prefmsg (u->nick, s_Services, "Module Bot List:"); hash_scan_begin (&bs, bh); while ((bn = hash_scan_next (&bs)) != NULL) { - mod_ptr = hnode_get (bn); - prefmsg (u->nick, s_Services, "Module: %s", mod_ptr->modname); - prefmsg (u->nick, s_Services, "Module Bots: %s", mod_ptr->nick); + mod_usr = hnode_get (bn); + prefmsg (u->nick, s_Services, "Module: %s", mod_usr->modname); + prefmsg (u->nick, s_Services, "Module Bots: %s", mod_usr->nick); } prefmsg (u->nick, s_Services, "End of Module Bot List"); } +/** @brief ModuleEvent + * + * + * + * @return none + */ +void +ModuleEvent (char *event, char **av, int ac) +{ + Module *module_ptr; + EventFnList *ev_list; + hscan_t ms; + hnode_t *mn; + + SET_SEGV_LOCATION(); + hash_scan_begin (&ms, mh); + while ((mn = hash_scan_next (&ms)) != NULL) { + module_ptr = hnode_get (mn); + ev_list = module_ptr->event_list; + if (ev_list) { + while (ev_list->cmd_name != NULL) { + /* This goes through each Command */ + if (!strcasecmp (ev_list->cmd_name, event)) { + nlog (LOG_DEBUG1, LOG_CORE, "Running Module %s for Comamnd %s -> %s", module_ptr->info->module_name, event, ev_list->cmd_name); + SET_SEGV_LOCATION(); + SET_SEGV_INMODULE(module_ptr->info->module_name); + if (setjmp (sigvbuf) == 0) { + if (ev_list->function) ev_list->function (av, ac); + } else { + nlog (LOG_CRITICAL, LOG_CORE, "setjmp() Failed, Can't call Module %s\n", module_ptr->info->module_name); + } + CLEAR_SEGV_INMODULE(); + SET_SEGV_LOCATION(); +#ifndef VALGRIND + break; +#endif + } + ev_list++; + } + } + } +} + +/** @brief + * + * + * + * @return none + */ +void +ModuleFunction (int cmdptr, char *cmd, char* origin, char **av, int ac) +{ + Module *module_ptr; + Functions *fn_list; + hscan_t ms; + hnode_t *mn; + + hash_scan_begin (&ms, mh); + while ((mn = hash_scan_next (&ms)) != NULL) { + module_ptr = hnode_get (mn); + fn_list = module_ptr->function_list; + while (fn_list->cmd_name != NULL) { + /* This goes through each Command */ + if (!strcmp (fn_list->cmd_name, cmd)) { + if (fn_list->srvmsg == cmdptr) { + nlog (LOG_DEBUG1, LOG_CORE, "Running Module %s for Function %s", module_ptr->info->module_name, fn_list->cmd_name); + SET_SEGV_LOCATION(); + SET_SEGV_INMODULE(module_ptr->info->module_name); + if (setjmp (sigvbuf) == 0) { + fn_list->function (origin, av, ac); + } + CLEAR_SEGV_INMODULE(); + SET_SEGV_LOCATION(); + break; + } + } + fn_list++; + } + } +} + /** @brief * * @param @@ -730,11 +884,11 @@ load_module (char *modfilename, User * u) int ac = 0; int i = 0; #ifdef OLD_MODULE_EXPORT_SUPPORT - Module_Info *(*mod_get_info) () = NULL; + ModuleInfo *(*mod_get_info) () = NULL; Functions *(*mod_get_funcs) () = NULL; EventFnList *(*mod_get_events) () = NULL; #endif /* OLD_MODULE_EXPORT_SUPPORT */ - Module_Info *mod_info_ptr = NULL; + ModuleInfo *mod_info_ptr = NULL; Functions *mod_funcs_ptr = NULL; EventFnList *event_fn_ptr = NULL; Module *mod_ptr = NULL; @@ -886,7 +1040,7 @@ load_module (char *modfilename, User * u) mod_ptr->info = mod_info_ptr; mod_ptr->function_list = mod_funcs_ptr; mod_ptr->dl_handle = dl_handle; - mod_ptr->other_funcs = event_fn_ptr; + mod_ptr->event_list = event_fn_ptr; /* assign a module number to this module */ i = 0; @@ -943,13 +1097,13 @@ load_module (char *modfilename, User * u) int get_dl_handle (char *mod_name) { - Module *list_ptr; + Module *mod_ptr; hnode_t *mn; mn = hash_lookup (mh, mod_name); if (mn) { - list_ptr = hnode_get (mn); - return (int) list_ptr->dl_handle; + mod_ptr = hnode_get (mn); + return (int) mod_ptr->dl_handle; } return 0; } @@ -984,7 +1138,7 @@ get_mod_num (char *mod_name) * @return */ void -list_module (User * u) +list_modules (User * u, char **av, int ac) { Module *mod_ptr = NULL; hnode_t *mn; @@ -1010,10 +1164,10 @@ list_module (User * u) int unload_module (char *module_name, User * u) { - Module *list; - Mod_User *mod_ptr = NULL; - Mod_Timer *mod_tmr = NULL; - Sock_List *mod_sock = NULL; + Module *mod_ptr; + ModUser *mod_usr; + ModTimer *mod_tmr; + ModSock *mod_sock; hnode_t *modnode; hscan_t hscan; int i; @@ -1056,10 +1210,10 @@ unload_module (char *module_name, User * u) /* now, see if this Module has any bots with it */ hash_scan_begin (&hscan, bh); while ((modnode = hash_scan_next (&hscan)) != NULL) { - mod_ptr = hnode_get (modnode); - if (!strcasecmp (mod_ptr->modname, module_name)) { - nlog (LOG_DEBUG1, LOG_CORE, "Module %s had bot %s Registered. Deleting..", module_name, mod_ptr->nick); - del_bot (mod_ptr->nick, "Module Unloaded"); + mod_usr = hnode_get (modnode); + if (!strcasecmp (mod_usr->modname, module_name)) { + nlog (LOG_DEBUG1, LOG_CORE, "Module %s had bot %s Registered. Deleting..", module_name, mod_usr->nick); + del_bot (mod_usr->nick, "Module Unloaded"); } } @@ -1071,11 +1225,11 @@ unload_module (char *module_name, User * u) i = get_mod_num (module_name); - list = hnode_get (modnode); + mod_ptr = hnode_get (modnode); SET_SEGV_INMODULE(module_name); /* call __ModFini (replacement for library __fini() call */ - dofini = dlsym ((int *) list->dl_handle, "__ModFini"); + dofini = dlsym ((int *) mod_ptr->dl_handle, "__ModFini"); if (dofini) { (*dofini) (); } @@ -1087,7 +1241,7 @@ unload_module (char *module_name, User * u) /* Close module */ SET_SEGV_INMODULE(module_name); #ifndef VALGRIND - dlclose (list->dl_handle); + dlclose (mod_ptr->dl_handle); #endif CLEAR_SEGV_INMODULE(); @@ -1096,7 +1250,7 @@ unload_module (char *module_name, User * u) /* free the module number */ ModList[i] = NULL; } - free (list); + free (mod_ptr); return NS_SUCCESS; } return NS_FAILURE; diff --git a/dl.h b/dl.h index 541fa374..998ab166 100644 --- a/dl.h +++ b/dl.h @@ -62,19 +62,18 @@ * */ typedef int (*message_function) (char *origin, char **av, int ac); -typedef int (*chan_message_function) (char *origin, char *chan, char **av, int ac); /** @brief Socket function types * */ -typedef int (*socket_function) (int sock_no, char *sockname); +typedef int (*socket_function) (int sock_no, char *sockname); typedef int (*before_poll_function) (void *data, struct pollfd *); typedef void (*after_poll_function) (void *data, struct pollfd *, unsigned int); /** @brief Module socket list structure * */ -typedef struct Sock_List { +typedef struct ModSock { /** Hash code */ long hash; /** Socket number */ @@ -103,17 +102,12 @@ typedef struct Sock_List { long rmsgs; /** rbytes */ long rbytes; -}Sock_List; - -/* @brief Module Socket List hash - * - */ -hash_t *sockh; +}ModSock; /** @brief Module Timer structure * */ -typedef struct Mod_Timer { +typedef struct ModTimer { /** Hash code */ long hash; /** Module name */ @@ -126,14 +120,12 @@ typedef struct Mod_Timer { time_t lastrun; /** Timer function */ int (*function) (); -}Mod_Timer; - -hash_t *th; +}ModTimer; /** @brief Module User structure * */ -typedef struct { +typedef struct ModUser { /** Hash code */ long hash; /** Nick */ @@ -143,27 +135,23 @@ typedef struct { /** function */ message_function function; /** function */ - chan_message_function chanfunc; -}Mod_User; - -hash_t *bh; + message_function chanfunc; +}ModUser; /** @brief Channel bot structure * */ -typedef struct { +typedef struct ModChanBot { /** channel name */ char chan[CHANLEN]; /** bot list */ list_t *bots; -}Chan_Bot; - -hash_t *bch; +}ModChanBot; /** @brief Module functions structure * */ -typedef struct { +typedef struct Functions { char *cmd_name; message_function function; int srvmsg; @@ -172,15 +160,17 @@ typedef struct { /** @brief Module Event functions structure * */ -typedef struct { +typedef int (*event_function) (char **av, int ac); + +typedef struct EventFnList { char *cmd_name; - int (*function) (char **av, int ac); + event_function function; }EventFnList; /** @brief Module Info structure (old style) * */ -typedef struct { +typedef struct Module_Info { char *module_name; char *module_description; char *module_version; @@ -189,7 +179,7 @@ typedef struct { /** @brief Module Info structure (new style) * */ -typedef struct { +typedef struct ModuleInfo { char *module_name; char *module_description; char *module_version; @@ -200,50 +190,63 @@ typedef struct { /** @brief Module structure * */ -typedef struct { - Module_Info *info; +typedef struct Module { + ModuleInfo *info; Functions *function_list; - EventFnList *other_funcs; + EventFnList *event_list; void *dl_handle; -}Module ; +}Module; -hash_t *mh; +/* @brief Module Socket List hash + * + */ +extern hash_t *sockh; /* * Prototypes */ -int __init_mod_list (void); +int InitModuleHash (void); +void ModuleEvent (char * event, char **av, int ac); +/* Temp define for secureserv */ +#define Module_Event ModuleEvent +void ModuleFunction (int cmdptr, char *cmd, char* origin, char **av, int ac); int load_module (char *path, User * u); int unload_module (char *module_name, User * u); int add_ld_path (char *path); -void list_module (User * u); -void list_module_bots (User * u); +void list_modules (User * u, char **av, int ac); +void list_bots (User * u, char **av, int ac); int add_mod_user (char *nick, char *mod_name); int del_mod_user (char *nick); int add_mod_timer (char *func_name, char *timer_name, char *mod_name, int interval); int del_mod_timer (char *timer_name); -Mod_Timer *findtimer(char *timer_name); -void list_module_timer (User * u); +ModTimer *findtimer(char *timer_name); +void list_timers (User * u, char **av, int ac); +void run_mod_timers (void); int add_socket (char *readfunc, char *writefunc, char *errfunc, char *sock_name, int socknum, char *mod_name); int add_sockpoll (char *beforepoll, char *afterpoll, char *sock_name, char *mod_name, void *data); int del_socket (char *sockname); -void list_sockets (User * u); -Sock_List *findsock (char *sock_name); -Mod_User *findbot (char * bot_name); +void list_sockets (User * u, char **av, int ac); +ModSock *findsock (char *sock_name); +ModUser *findbot (char * bot_name); int get_dl_handle (char *mod_name); void add_bot_to_chan (char *bot, char *chan); void del_bot_from_chan (char *bot, char *chan); -void bot_chan_message (char *origin, char *chan, char **av, int ac); -void botchandump (User * u); +void bot_chan_message (char *origin, char **av, int ac); +void list_bot_chans (User * u, char **av, int ac); int get_mod_num (char *mod_name); void unload_modules(void); +int bot_nick_change (char * oldnick, char *newnick); +void verify_hashes(void); + /* * Module Interface */ int __ModInit(int modnum, int apiver); void __ModFini(void); -int __Bot_Message(char *origin, char **av, int ac); -int __Chan_Message(char *origin, char *chan, char **argv, int argc); +int __BotMessage(char *origin, char **av, int ac); +/* temp define while rename propogates */ +#define __Bot_Message __BotMessage +int __ChanMessage(char *origin, char **argv, int argc); /* temporary define to support for module API backwards compatibility * old system used function addresses to call into a module to get data addresses diff --git a/dl/cs/ChangeLog b/dl/cs/ChangeLog index f47730c9..0bc89baf 100644 --- a/dl/cs/ChangeLog +++ b/dl/cs/ChangeLog @@ -1,6 +1,9 @@ ConnectServ Module for NeoStats 2.x ChangeLog Shmad & ^Enigma^ ============================================================================== +* Version 1.10 * Mark (M) * November 13, 2003 + - Moved mode defines to ircd header files (M) + * Version 1.9 * Mark (M) * November 1, 2003 - Message system cleanup (M) - Nick change messages now include user@host for spotting nick flooders (M) diff --git a/dl/cs/cs.c b/dl/cs/cs.c index 61a0df22..884f9727 100644 --- a/dl/cs/cs.c +++ b/dl/cs/cs.c @@ -34,7 +34,7 @@ /* Uncomment this line to disable colours in ConnectServ channel messages */ -/*#define DISABLE_COLOUR_SUPPORT*/ +/* #define DISABLE_COLOUR_SUPPORT */ #ifdef DISABLE_COLOUR_SUPPORT char msg_nickchange[]="\2NICK\2 %s (%s@%s) Changed their nick to %s"; @@ -104,19 +104,18 @@ char msg_invisible[]="\2%s\2 Is Using \2Invisible Mode\2 (+%c)"; char msg_invisibleoff[]="\2%s\2 Is no longer using \2Invisible Mode\2 (-%c)"; #endif +char s_ConnectServ[MAXNICK]; - -char *s_ConnectServ; - -int cs_new_user(char **av, int ac); -int cs_user_modes(char **av, int ac); -int cs_user_smodes(char **av, int ac); -int cs_del_user(char **av, int ac); -int cs_user_kill(char **av, int ac); -int cs_user_nick(char **av, int ac); -void do_set(User * u, char **av, int ac); -void SaveSettings(); -void Loadconfig(); +static int cs_new_user(char **av, int ac); +static int cs_user_modes(char **av, int ac); +#ifdef ULTIMATE3 +static int cs_user_smodes(char **av, int ac); +#endif +static int cs_del_user(char **av, int ac); +static int cs_user_kill(char **av, int ac); +static int cs_user_nick(char **av, int ac); +static void do_set(User * u, char **av, int ac); +static void LoadConfig(void); static void cs_version(User * u); static void cs_set_list(User * u, char **av, int ac); @@ -124,17 +123,31 @@ static void cs_set_signwatch(User * u, char **av, int ac); static void cs_set_killwatch(User * u, char **av, int ac); static void cs_set_modewatch(User * u, char **av, int ac); static void cs_set_nickwatch(User * u, char **av, int ac); +#if 0 +/* work in progress */ +static void cs_set_nick(User * u, char **av, int ac); +static void cs_set_user(User * u, char **av, int ac); +static void cs_set_host(User * u, char **av, int ac); +static void cs_set_rname(User * u, char **av, int ac); +#endif + +struct cs_cfg { + int sign_watch; + int kill_watch; + int mode_watch; + int nick_watch; + int modnum; + char user[MAXUSER]; + char host[MAXHOST]; + char rname[MAXREALNAME]; +} cs_cfg; -static int sign_watch; -static int kill_watch; -static int mode_watch; -static int nick_watch; static int cs_online = 0; ModuleInfo __module_info = { "ConnectServ", "Network Connection & Mode Monitoring Service", - "1.9", + "1.10", __DATE__, __TIME__ }; @@ -158,7 +171,7 @@ Functions __module_functions[] = { {NULL, NULL, 0} }; -int __Bot_Message(char *origin, char **av, int ac) +int __BotMessage(char *origin, char **av, int ac) { User *u; u = finduser(origin); @@ -168,7 +181,7 @@ int __Bot_Message(char *origin, char **av, int ac) if (!cs_online) return 1; - if ((UserLevel(u) < 185)) { + if ((UserLevel(u) < NS_ULEVEL_ADMIN)) { prefmsg(u->nick, s_ConnectServ, "Permission Denied!"); return 1; } @@ -217,6 +230,17 @@ void do_set(User * u, char **av, int ac) "Invalid Syntax. /msg %s help set for more info", s_ConnectServ); return; +#if 0 +/* work in progress */ + } else if (!strcasecmp(av[2], "NICK")) { + cs_set_nick(u, av, ac); + } else if (!strcasecmp(av[2], "USER")) { + cs_set_user(u, av, ac); + } else if (!strcasecmp(av[2], "HOST")) { + cs_set_host(u, av, ac); + } else if (!strcasecmp(av[2], "REALNAME")) { + cs_set_rname(u, av, ac); +#endif } else if (!strcasecmp(av[2], "NICKWATCH")) { cs_set_nickwatch(u, av, ac); } else if (!strcasecmp(av[2], "SIGNWATCH")) { @@ -237,42 +261,15 @@ void do_set(User * u, char **av, int ac) int Online(char **av, int ac) { - char *user = NULL; - char *host = NULL; - char *rname = NULL; - - if (GetConf((void *) &s_ConnectServ, CFGSTR, "Nick") < 0) { - s_ConnectServ = "ConnectServ"; - } - if (GetConf((void *) &user, CFGSTR, "User") < 0) { - user = malloc(MAXUSER); - strlcpy(user, "CS", MAXUSER); - } - if (GetConf((void *) &host, CFGSTR, "Host") < 0) { - host = malloc(MAXHOST); - strlcpy(host, me.name, MAXHOST); - } - if (GetConf((void *) &rname, CFGSTR, "RealName") < 0) { - rname = malloc(MAXREALNAME); - strlcpy(rname, "Connection Monitoring Service", MAXREALNAME); - } - - if (init_bot - (s_ConnectServ, user, host, rname, "+oS", + (s_ConnectServ, cs_cfg.user, cs_cfg.host, cs_cfg.rname, services_bot_modes, __module_info.module_name) == -1) { /* Nick was in use */ strlcat(s_ConnectServ, "_", MAXREALNAME); - init_bot(s_ConnectServ, user, host, rname, "+oS", + init_bot(s_ConnectServ, cs_cfg.user, cs_cfg.host, cs_cfg.rname, services_bot_modes, __module_info.module_name); } cs_online = 1; - if(user) - free(user); - if(host) - free(host); - if(rname) - free(rname); return 1; }; @@ -298,7 +295,7 @@ EventFnList __module_events[] = { int __ModInit(int modnum, int apiver) { - Loadconfig(); + LoadConfig(); return 1; } @@ -346,7 +343,7 @@ int cs_new_user(char **av, int ac) } /* Print Connection Notice */ - if (u && sign_watch) { + if (u && cs_cfg.sign_watch) { chanalert(s_ConnectServ, msg_signon, u->nick, u->username, u->hostname, u->server->name); @@ -387,7 +384,7 @@ int cs_del_user(char **av, int ac) QuitMsg = joinbuf(Quit, QuitCount, 2); /* Local Kill Watch For Signoff */ - if (kill_watch) { + if (cs_cfg.kill_watch) { if (strstr(cmd, "Local kill by") && strstr(cmd, "[") && strstr(cmd, "]")) { @@ -407,7 +404,7 @@ int cs_del_user(char **av, int ac) } /* Print Disconnection Notice */ - if (sign_watch) { + if (cs_cfg.sign_watch) { chanalert(s_ConnectServ, msg_signoff, u->nick, u->username, u->hostname, @@ -434,7 +431,7 @@ int cs_user_modes(char **av, int ac) if (!cs_online) return 1; - if (mode_watch != 1) + if (cs_cfg.mode_watch != 1) return -1; u = finduser(av[0]); @@ -633,7 +630,7 @@ int cs_user_smodes(char **av, int ac) SET_SEGV_LOCATION(); - if (mode_watch != 1) + if (cs_cfg.mode_watch != 1) return -1; u = finduser(av[0]); @@ -787,13 +784,13 @@ int cs_user_kill(char **av, int ac) if (finduser(Kill[2])) { /* it was a User who was killed */ - if (kill_watch) + if (cs_cfg.kill_watch) chanalert(s_ConnectServ, msg_globalkill, u->nick, u->username, u->hostname, Kill[0], GlobalMsg); } else if (findserver(Kill[2])) { - if (kill_watch) + if (cs_cfg.kill_watch) chanalert(s_ConnectServ, msg_serverkill, u->nick, Kill[0], GlobalMsg); @@ -814,7 +811,7 @@ int cs_user_nick(char **av, int ac) if (!cs_online) return 1; - if (nick_watch) { + if (cs_cfg.nick_watch) { u = finduser(av[1]); if (!u) return -1; @@ -842,7 +839,7 @@ static void cs_set_nickwatch(User * u, char **av, int ac) return; } if ((!strcasecmp(av[3], "YES")) || (!strcasecmp(av[3], "ON"))) { - nick_watch = 1; + cs_cfg.nick_watch = 1; chanalert(s_ConnectServ, "\2NICK WATCH\2 Activated by \2%s\2", u->nick); nlog(LOG_NORMAL, LOG_MOD, "%s!%s@%s Activated NICK WATCH", @@ -852,7 +849,7 @@ static void cs_set_nickwatch(User * u, char **av, int ac) "\2NICK WATCH\2 Activated"); } else if ((!strcasecmp(av[3], "NO")) || (!strcasecmp(av[3], "OFF"))) { - nick_watch = 0; + cs_cfg.nick_watch = 0; chanalert(s_ConnectServ, "\2NICK WATCH\2 Deactivated by \2%s\2", u->nick); nlog(LOG_NORMAL, LOG_MOD, @@ -883,7 +880,7 @@ static void cs_set_signwatch(User * u, char **av, int ac) return; } if ((!strcasecmp(av[3], "YES")) || (!strcasecmp(av[3], "ON"))) { - sign_watch = 1; + cs_cfg.sign_watch = 1; chanalert(s_ConnectServ, "\2SIGNON/SIGNOFF WATCH\2 Activated by \2%s\2", u->nick); @@ -895,7 +892,7 @@ static void cs_set_signwatch(User * u, char **av, int ac) "\2SIGNON/SIGNOFF WATCH\2 Activated"); } else if ((!strcasecmp(av[3], "NO")) || (!strcasecmp(av[3], "OFF"))) { - sign_watch = 0; + cs_cfg.sign_watch = 0; chanalert(s_ConnectServ, "\2SIGNON/SIGNOFF WATCH\2 Deactivated by \2%s\2", u->nick); @@ -926,7 +923,7 @@ static void cs_set_killwatch(User * u, char **av, int ac) return; } if ((!strcasecmp(av[3], "YES")) || (!strcasecmp(av[3], "ON"))) { - kill_watch = 1; + cs_cfg.kill_watch = 1; chanalert(s_ConnectServ, "\2KILL WATCH\2 Activated by \2%s\2", u->nick); nlog(LOG_NORMAL, LOG_MOD, "%s!%s@%s Activated KILL WATCH", @@ -936,7 +933,7 @@ static void cs_set_killwatch(User * u, char **av, int ac) "\2KILL WATCH\2 Activated"); } else if ((!strcasecmp(av[3], "NO")) || (!strcasecmp(av[3], "OFF"))) { - kill_watch = 0; + cs_cfg.kill_watch = 0; chanalert(s_ConnectServ, "\2KILL WATCH\2 Deactivated by \2%s\2", u->nick); nlog(LOG_NORMAL, LOG_MOD, @@ -966,7 +963,7 @@ static void cs_set_modewatch(User * u, char **av, int ac) return; } if ((!strcasecmp(av[3], "YES")) || (!strcasecmp(av[3], "ON"))) { - mode_watch = 1; + cs_cfg.mode_watch = 1; chanalert(s_ConnectServ, "\2MODE WATCH\2 Activated by \2%s\2", u->nick); nlog(LOG_NORMAL, LOG_MOD, "%s!%s@%s Activated MODE WATCH", @@ -976,7 +973,7 @@ static void cs_set_modewatch(User * u, char **av, int ac) "\2MODE WATCH\2 Activated"); } else if ((!strcasecmp(av[3], "NO")) || (!strcasecmp(av[3], "OFF"))) { - mode_watch = 0; + cs_cfg.mode_watch = 0; chanalert(s_ConnectServ, "\2MODE WATCH\2 Deactivated by \2%s\2", u->nick); nlog(LOG_NORMAL, LOG_MOD, @@ -993,6 +990,78 @@ static void cs_set_modewatch(User * u, char **av, int ac) } } +#if 0 +/* work in progress */ +/* + * Set ConnectServ Nick + */ +static void cs_set_nick(User * u, char **av, int ac) +{ + char old_nick[MAXNICK]; + + SET_SEGV_LOCATION(); + if (ac < 4) { + prefmsg(u->nick, s_ConnectServ, + "Invalid Syntax. /msg %s help set for more info", + s_ConnectServ); + return; + } + strlcpy(old_nick, s_ConnectServ, MAXNICK); + strlcpy(s_ConnectServ, av[3], MAXNICK); + SetConf((void *) s_ConnectServ, CFGSTR, "Nick"); + if( bot_nick_change(old_nick, s_ConnectServ) == NS_FAILURE) + prefmsg(u->nick, s_ConnectServ, "bot_nick_change failed %s %s", old_nick, s_ConnectServ); +} + +/* + * Set ConnectServ User + */ +static void cs_set_user(User * u, char **av, int ac) +{ + SET_SEGV_LOCATION(); + if (ac < 4) { + prefmsg(u->nick, s_ConnectServ, + "Invalid Syntax. /msg %s help set for more info", + s_ConnectServ); + return; + } + strlcpy(cs_cfg.user, av[3], MAXUSER); + SetConf((void *) cs_cfg.user, CFGSTR, "User"); +} + +/* + * Set ConnectServ Host + */ +static void cs_set_host(User * u, char **av, int ac) +{ + SET_SEGV_LOCATION(); + if (ac < 4) { + prefmsg(u->nick, s_ConnectServ, + "Invalid Syntax. /msg %s help set for more info", + s_ConnectServ); + return; + } + strlcpy(cs_cfg.host, av[3], MAXHOST); + SetConf((void *) cs_cfg.host, CFGSTR, "Host"); +} + +/* + * Set ConnectServ Real Name + */ +static void cs_set_rname(User * u, char **av, int ac) +{ + SET_SEGV_LOCATION(); + if (ac < 4) { + prefmsg(u->nick, s_ConnectServ, + "Invalid Syntax. /msg %s help set for more info", + s_ConnectServ); + return; + } + strlcpy(cs_cfg.rname, av[3], MAXREALNAME); + SetConf((void *) cs_cfg.rname, CFGSTR, "RealName"); +} +#endif + /* * SET LIST - List current settings */ @@ -1002,30 +1071,60 @@ static void cs_set_list(User * u, char **av, int ac) prefmsg(u->nick, s_ConnectServ, "Current %s Settings:", s_ConnectServ); prefmsg(u->nick, s_ConnectServ, "SIGNWATCH: %s", - sign_watch ? "Enabled" : "Disabled"); + cs_cfg.sign_watch ? "Enabled" : "Disabled"); prefmsg(u->nick, s_ConnectServ, "KILLWATCH: %s", - kill_watch ? "Enabled" : "Disabled"); + cs_cfg.kill_watch ? "Enabled" : "Disabled"); prefmsg(u->nick, s_ConnectServ, "MODEWATCH: %s", - mode_watch ? "Enabled" : "Disabled"); + cs_cfg.mode_watch ? "Enabled" : "Disabled"); prefmsg(u->nick, s_ConnectServ, "NICKWATCH: %s", - nick_watch ? "Enabled" : "Disabled"); + cs_cfg.nick_watch ? "Enabled" : "Disabled"); } /* * Load ConnectServ Configuration file and set defaults if does not exist */ -void Loadconfig() +static void LoadConfig(void) { + char *temp = NULL; SET_SEGV_LOCATION(); - - /* some defaults */ - sign_watch = 1; - kill_watch = 1; - mode_watch = 1; - nick_watch = 1; - - GetConf((void *) &sign_watch, CFGBOOL, "SignWatch"); - GetConf((void *) &kill_watch, CFGBOOL, "KillWatch"); - GetConf((void *) &mode_watch, CFGBOOL, "ModeWatch"); - GetConf((void *) &nick_watch, CFGBOOL, "NickWatch"); + if(GetConf((void *) &cs_cfg.sign_watch, CFGBOOL, "SignWatch")<= 0) { + cs_cfg.sign_watch = 1; + } + if(GetConf((void *) &cs_cfg.kill_watch, CFGBOOL, "KillWatch")<= 0) { + cs_cfg.kill_watch = 1; + } + if(GetConf((void *) &cs_cfg.mode_watch, CFGBOOL, "ModeWatch")<= 0) { + cs_cfg.mode_watch = 1; + } + if(GetConf((void *) &cs_cfg.nick_watch, CFGBOOL, "NickWatch")<= 0) { + cs_cfg.nick_watch = 1; + } + if(GetConf((void *) &temp, CFGSTR, "Nick") < 0) { + strlcpy(s_ConnectServ , "ConnectServ", MAXNICK); + } + else { + strlcpy(s_ConnectServ, temp, MAXNICK); + free(temp); + } + if(GetConf((void *) &temp, CFGSTR, "User") < 0) { + strlcpy(cs_cfg.user, "CS", MAXUSER); + } + else { + strlcpy(cs_cfg.user, temp, MAXUSER); + free(temp); + } + if(GetConf((void *) &temp, CFGSTR, "Host") < 0) { + strlcpy(cs_cfg.host, me.name, MAXHOST); + } + else { + strlcpy(cs_cfg.host, temp, MAXHOST); + free(temp); + } + if(GetConf((void *) &temp, CFGSTR, "RealName") < 0) { + strlcpy(cs_cfg.rname, "Connection Monitoring Service", MAXREALNAME); + } + else { + strlcpy(cs_cfg.rname, temp, MAXREALNAME); + free(temp); + } } diff --git a/dl/cs/cs.h b/dl/cs/cs.h index 3557bf16..e806af6d 100644 --- a/dl/cs/cs.h +++ b/dl/cs/cs.h @@ -22,81 +22,3 @@ ** NeoStats CVS Identification ** $Id$ */ - - -/* -** If we're compiled for Ultimate 3.x.x IRCd, use these modes and flags -*/ -#if defined(ULTIMATE3) || defined(QUANTUM) - #define LOCOP_MODE 'O' - #define OPER_MODE 'o' - #define GUESTADMIN_MODE 'G' - #define COSERVERADMIN_MODE 'J' - #define SERVERADMIN_MODE 'A' - #define CONETADMIN_MODE 'n' - #define NETADMIN_MODE 'N' - #define COTECHADMIN_MODE 't' - #define TECHADMIN_MODE 'T' /* Set to a number as we dont use */ - #define SERVICESADMIN_MODE 'a' - #define NETSERVICE_MODE 'S' -#elif ULTIMATE -/* -** If we are compiled to Use Ultimate 2.8.x use these modes and flags -*/ - #define LOCOP_MODE 'O' - #define OPER_MODE 'o' - #define COSERVERADMIN_MODE 'J' - #define SERVERADMIN_MODE 'A' - #define CONETADMIN_MODE 't' - #define NETADMIN_MODE 'N' - #define TECHADMIN_MODE 'T' - #define SERVICESADMIN_MODE 'P' - #define NETSERVICE_MODE 'S' - #define BOT_MODE 'B' -#endif -#ifdef MYSTIC -/* -** If we are compiled to Use Ultimate 2.8.x use these modes and flags -*/ - #define LOCOP_MODE 'O' - #define OPER_MODE 'o' - #define COSERVERADMIN_MODE 'J' - #define SERVERADMIN_MODE 'A' - #define CONETADMIN_MODE 't' - #define NETADMIN_MODE 'N' - #define TECHADMIN_MODE 'T' - #define SERVICESADMIN_MODE 'P' - #define NETSERVICE_MODE 'S' - #define BOT_MODE 'B' -#endif - -#ifdef UNREAL - #define LOCOP_MODE 'O' - #define OPER_MODE 'o' - #define COSERVERADMIN_MODE 'C' - #define SERVERADMIN_MODE 'A' - #define NETADMIN_MODE 'N' - #define TECHADMIN_MODE 'T' - #define SERVICESADMIN_MODE 'a' - #define NETSERVICE_MODE 'S' - #define INVISIBLE_MODE 'I' - #define BOT_MODE 'B' -#endif - -#ifdef HYBRID7 - #define LOCOP_MODE 'O' - #define OPER_MODE 'o' - #define SERVERADMIN_MODE 'a' -#endif - -#ifdef NEOIRCD - #define LOCOP_MODE 'O' - #define OPER_MODE 'o' - #define SERVERADMIN_MODE 'a' -#endif - -#ifdef BAHAMUT - #define LOCOP_MODE 'O' - #define OPER_MODE 'o' - #define SERVERADMIN_MODE 'a' -#endif diff --git a/dl/extauth/serviceroots.c b/dl/extauth/serviceroots.c index 5ccf661a..0258bb74 100644 --- a/dl/extauth/serviceroots.c +++ b/dl/extauth/serviceroots.c @@ -33,11 +33,19 @@ static int new_m_version(char *origin, char **av, int ac); void sr_cb_config(char *arg, int configtype); +extern void ext_auth_list(User *u, char **av, int ac); + +const char *sr_help_list[] = { + "Syntax: \2SRLIST\2", + "", + "This command lists the ServiceRoots defined in the configuration file", + NULL +}; ModuleInfo __module_info = { "extauth", "ServiceRoots Authentication Module", - "1.1", + "1.2", __DATE__, __TIME__ }; @@ -89,6 +97,7 @@ int __ModInit(int modnum, int apiver) "ServiceRoots: ehh, config failed"); /* we can't unload the extauth module so don't return -1 */ } + add_services_cmd("SRLIST", ext_auth_list, 0, NS_ULEVEL_OPER, sr_help_list, "ServiceRoots List"); return 1; } @@ -101,6 +110,7 @@ void __ModFini() un = list_next(srconf.ul, un); } list_destroy_nodes(srconf.ul); + del_services_cmd("SRLIST"); } void sr_cb_config(char *arg, int configtype) @@ -133,14 +143,14 @@ void sr_cb_config(char *arg, int configtype) strlcpy(sru->nick, nick, MAXNICK); strlcpy(sru->ident, user, MAXUSER); strlcpy(sru->host, host, MAXHOST); - sru->lvl = 200; + sru->lvl = NS_ULEVEL_ROOT; } else { /* old format... Warn, but keep going */ sru = malloc(sizeof(users)); strlcpy(sru->nick, arg, MAXNICK); strlcpy(sru->ident, "*", MAXUSER); strlcpy(sru->host, "*", MAXHOST); - sru->lvl = 200; + sru->lvl = NS_ULEVEL_ROOT; nlog(LOG_WARNING, LOG_CORE, "Old ServiceRoots Entry Detected. Suggest you upgrade ASAP to !@ (WildCards are allowed)"); } @@ -181,3 +191,19 @@ extern int __list_auth(User * u) } return 1; } + +extern void ext_auth_list(User *u, char **av, int ac) { + lnode_t *un; + struct users *sru; + + SET_SEGV_LOCATION(); + un = list_first(srconf.ul); + prefmsg(u->nick, s_Services, "ServiceRoots:"); + while (un) { + sru = lnode_get(un); + prefmsg(u->nick, s_Services, "%s!%s@%s %d", sru->nick, sru->ident, + sru->host, sru->lvl); + un = list_next(srconf.ul, un); + } + return; +} \ No newline at end of file diff --git a/dl/hostserv/hostserv.c b/dl/hostserv/hostserv.c index 23b6ddf7..5ed9fcba 100644 --- a/dl/hostserv/hostserv.c +++ b/dl/hostserv/hostserv.c @@ -168,10 +168,19 @@ void hs_Config() int hostlen; SET_SEGV_LOCATION(); + hs_cfg.old = 60; GetConf((void *) &hs_cfg.view, CFGINT, "ViewLevel"); + if ((hs_cfg.view > NS_ULEVEL_ROOT) || (hs_cfg.list <= 0)) + hs_cfg.view = 100; GetConf((void *) &hs_cfg.add, CFGINT, "AddLevel"); + if ((hs_cfg.add > NS_ULEVEL_ROOT) || (hs_cfg.view <= 0)) + hs_cfg.add = 40; GetConf((void *) &hs_cfg.del, CFGINT, "DelLevel"); + if ((hs_cfg.del > NS_ULEVEL_ROOT) || (hs_cfg.del <= 0)) + hs_cfg.del = 40; GetConf((void *) &hs_cfg.list, CFGINT, "ListLevel"); + if ((hs_cfg.list > NS_ULEVEL_ROOT) || (hs_cfg.list <= 0)) + hs_cfg.list = 40; GetConf((void *) &hs_cfg.old, CFGINT, "ExpireDays"); if (GetConf((void *) &hs_cfg.regnick, CFGINT, "UnetVhosts") > 0) { if (GetConf((void *) &ban, CFGSTR, "UnetDomain") > 0) { @@ -184,16 +193,6 @@ void hs_Config() hs_cfg.vhostdom[0] = '\0'; } - - if ((hs_cfg.list > 200) || (hs_cfg.list <= 0)) - hs_cfg.list = 40; - if ((hs_cfg.add > 200) || (hs_cfg.view <= 0)) - hs_cfg.add = 40; - if ((hs_cfg.del > 200) || (hs_cfg.del <= 0)) - hs_cfg.del = 40; - if ((hs_cfg.view > 200) || (hs_cfg.list <= 0)) - hs_cfg.view = 100; - /* banned vhosts */ ban = NULL; GetConf((void *) &ban, CFGSTR, "BannedVhosts"); @@ -281,7 +280,7 @@ Functions __module_functions[] = { {NULL, NULL, 0} }; -int __Bot_Message(char *origin, char **av, int ac) +int __BotMessage(char *origin, char **av, int ac) { int t = 0; User *u; @@ -328,7 +327,7 @@ int __Bot_Message(char *origin, char **av, int ac) } else if (!strcasecmp(av[2], "ABOUT")) { privmsg_list(u->nick, s_HostServ, hs_help_about); return 1; - } else if (!strcasecmp(av[2], "SET") && (UserLevel(u) >= 185)) { + } else if (!strcasecmp(av[2], "SET") && (UserLevel(u) >= NS_ULEVEL_ADMIN)) { privmsg_list(u->nick, s_HostServ, hs_help_set); return 1; } else @@ -385,7 +384,7 @@ int __Bot_Message(char *origin, char **av, int ac) hs_listban(u); return 1; } else if (ac == 4) { - if (UserLevel(u) >= 185) { + if (UserLevel(u) >= NS_ULEVEL_ADMIN) { if (!strcasecmp(av[2], "ADD")) { hs_addban(u, av[3]); return 1; @@ -417,7 +416,7 @@ int __Bot_Message(char *origin, char **av, int ac) hs_cfg.view); return 1; } else if (ac == 3) { - if (UserLevel(u) >= 185) { + if (UserLevel(u) >= NS_ULEVEL_ADMIN) { if (!strcasecmp(av[2], "RESET")) { hs_cfg.add = 40; SetConf((void *) t, CFGINT, "AddLevel"); @@ -431,11 +430,11 @@ int __Bot_Message(char *origin, char **av, int ac) } prefmsg(u->nick, s_HostServ, "Permission Denied"); } else if (ac == 4) { - if (UserLevel(u) >= 185) { + if (UserLevel(u) >= NS_ULEVEL_ADMIN) { t = atoi(av[3]); - if ((t <= 0) || (t > 200)) { + if ((t <= 0) || (t > NS_ULEVEL_ROOT)) { prefmsg(u->nick, s_HostServ, - "Invalid Level. Must be between 1 and 200"); + "Invalid Level. Must be between 1 and %d", NS_ULEVEL_ROOT); return -1; } if (!strcasecmp(av[2], "ADD")) { @@ -513,7 +512,7 @@ int __Bot_Message(char *origin, char **av, int ac) } hs_chpass(u, av[2], av[3], av[4]); } else if (!strcasecmp(av[1], "SET")) { - if (UserLevel(u) < 185) { + if (UserLevel(u) < NS_ULEVEL_ADMIN) { prefmsg(u->nick, s_HostServ, "Permission Denied"); chanalert(s_HostServ, "%s tried set, but permission was denied", u->nick); return -1; @@ -607,11 +606,11 @@ int Online(char **av, int ac) if (init_bot - (s_HostServ, user, host, rname, "+oS", + (s_HostServ, user, host, rname, services_bot_modes, __module_info.module_name) == -1) { /* Nick was in use */ strlcat(s_HostServ, "_", MAXNICK); - init_bot(s_HostServ, user, host, rname, "+oS", + init_bot(s_HostServ, user, host, rname, services_bot_modes, __module_info.module_name); } if(user) @@ -658,11 +657,6 @@ int __ModInit(int modnum, int apiver) return -1; } hs_cfg.modnum = modnum; - hs_cfg.add = 40; - hs_cfg.del = 40; - hs_cfg.list = 40; - hs_cfg.view = 100; - hs_cfg.old = 60; hs_Config(); return 1; } diff --git a/dl/loveserv/loveserv.c b/dl/loveserv/loveserv.c index 3d66b0f0..6dca8ccb 100644 --- a/dl/loveserv/loveserv.c +++ b/dl/loveserv/loveserv.c @@ -73,7 +73,7 @@ Functions __module_functions[] = { }; -int __Bot_Message(char *origin, char **av, int ac) +int __BotMessage(char *origin, char **av, int ac) { User *u; char *cmd; @@ -258,12 +258,12 @@ int __Bot_Message(char *origin, char **av, int ac) int Online(char **av, int ac) { if (init_bot - (s_LoveServ, "love", me.name, "Network Love Service", "+oS", + (s_LoveServ, "love", me.name, "Network Love Service", services_bot_modes, __module_info.module_name) == -1) { /* Nick was in use!!!! */ s_LoveServ = strcat(s_LoveServ, "_"); init_bot(s_LoveServ, "love", me.name, - "Network Love Service", "+oS", + "Network Love Service", services_bot_modes, __module_info.module_name); } return 1; diff --git a/dl/modules.txt b/dl/modules.txt index a3ab16ef..190362af 100644 --- a/dl/modules.txt +++ b/dl/modules.txt @@ -300,7 +300,7 @@ To delete a timer: -----------------------------------<>----------------------------------- TODO: -int __Chan_Message(char *origin, char *chan, char **argv, int argc) +int __Chan_Message(char *origin, char **argv, int argc) -----------------------------------<>----------------------------------- TODO: diff --git a/dl/ms/ms.c b/dl/ms/ms.c index 4b24e8fb..53110326 100644 --- a/dl/ms/ms.c +++ b/dl/ms/ms.c @@ -69,7 +69,7 @@ Functions __module_functions[] = { }; -int __Bot_Message(char *origin, char **av, int ac) +int __BotMessage(char *origin, char **av, int ac) { User *u; u = finduser(origin); @@ -237,11 +237,11 @@ int Online(char **av, int ac) { if (init_bot (s_MoraleServ, "MS", me.name, "A Network Morale Service", - "+oS", __module_info.module_name) == -1) { + services_bot_modes, __module_info.module_name) == -1) { /* Nick was in use */ s_MoraleServ = strcat(s_MoraleServ, "_"); init_bot(s_MoraleServ, "MS", me.name, - "A Network Morale Service", "+oS", + "A Network Morale Service", services_bot_modes, __module_info.module_name); } return 1; diff --git a/dl/spam/spam.c b/dl/spam/spam.c index 7beb614c..a8e90d66 100644 --- a/dl/spam/spam.c +++ b/dl/spam/spam.c @@ -62,10 +62,11 @@ Functions __module_functions[] = { /* an easter egg for all the Neo users */ -int __Chan_Message(char *origin, char *chan, char **argv, int argc) +int __ChanMessage(char *origin, char **argv, int argc) { FILE *fort; char *fortune; + char *chan = argv[0]; if (!strcasecmp(argv[1], s_Spam)) { fort = popen("/usr/games/fortune", "r"); if (fort) { @@ -82,7 +83,7 @@ int __Chan_Message(char *origin, char *chan, char **argv, int argc) -int __Bot_Message(char *origin, char **argv, int argc) +int __BotMessage(char *origin, char **argv, int argc) { User *u; char *buf; diff --git a/dl/statserv/stats.c b/dl/statserv/stats.c index c58e442c..43ba370c 100755 --- a/dl/statserv/stats.c +++ b/dl/statserv/stats.c @@ -416,7 +416,7 @@ void re_init_bot() SET_SEGV_LOCATION(); chanalert(s_Services, "Re-Initilizing %s Bot", s_StatServ); init_bot(s_StatServ, StatServ.user, StatServ.host, - "/msg Statserv HELP", "+oS", SSMNAME); + "/msg Statserv HELP", services_bot_modes, SSMNAME); } int s_del_user(char **av, int ac) { @@ -559,7 +559,7 @@ int Online(char **av, int ac) { SET_SEGV_LOCATION(); init_bot(s_StatServ, StatServ.user, StatServ.host, StatServ.rname, - "+oS", SSMNAME); + services_bot_modes, SSMNAME); StatServ.onchan = 1; /* now that we are online, setup the timer to save the Stats database every so often */ add_mod_timer("SaveStats", "Save_Stats_DB", SSMNAME, DBSAVETIME); diff --git a/dl/statserv/statserv.c b/dl/statserv/statserv.c index ac567a47..26490443 100644 --- a/dl/statserv/statserv.c +++ b/dl/statserv/statserv.c @@ -239,7 +239,7 @@ void __ModFini() } -int __Bot_Message(char *origin, char **av, int ac) +int __BotMessage(char *origin, char **av, int ac) { User *u; @@ -313,9 +313,9 @@ int __Bot_Message(char *origin, char **av, int ac) else if (!strcasecmp(av[2], "ABOUT")) privmsg_list(u->nick, s_StatServ, ss_about_help); else if (!strcasecmp(av[2], "STATS") - && UserLevel(u) >= 185) + && UserLevel(u) >= NS_ULEVEL_ADMIN) privmsg_list(u->nick, s_StatServ, ss_stats_help); - else if (!strcasecmp(av[2], "SET") && UserLevel(u) >= 185) + else if (!strcasecmp(av[2], "SET") && UserLevel(u) >= NS_ULEVEL_ADMIN) privmsg_list(u->nick, s_StatServ, ss_set_help); else prefmsg(u->nick, s_StatServ, @@ -329,7 +329,7 @@ int __Bot_Message(char *origin, char **av, int ac) chanalert(s_StatServ, "%s Wanted to see Channel Statistics", u->nick); } else if (!strcasecmp(av[1], "SET")) { - if (UserLevel(u) >= 185) { + if (UserLevel(u) >= NS_ULEVEL_ADMIN) { ss_set(u, av, ac); } else { prefmsg(u->nick, s_StatServ, "Permission Denied"); @@ -368,7 +368,7 @@ int __Bot_Message(char *origin, char **av, int ac) chanalert(s_StatServ, "%s Wanted to see the Daily NetStats ", u->nick); } else if (!strcasecmp(av[1], "FORCEHTML") - && (UserLevel(u) >= 185)) { + && (UserLevel(u) >= NS_ULEVEL_ADMIN)) { nlog(LOG_NOTICE, LOG_MOD, "%s!%s@%s Forced an update of the NeoStats Statistics HTML file with the most current statistics", u->nick, u->username, u->hostname); @@ -400,7 +400,7 @@ int __Bot_Message(char *origin, char **av, int ac) chanalert(s_StatServ, "%s Wanted to see the Bot List", u->nick); #endif - } else if (!strcasecmp(av[1], "STATS") && (UserLevel(u) >= 185)) { + } else if (!strcasecmp(av[1], "STATS") && (UserLevel(u) >= NS_ULEVEL_ADMIN)) { ss_stats(u, av[2], av[3], av[4]); if (ac < 3) { chanalert(s_StatServ, @@ -886,7 +886,7 @@ static void makemap(char *uplink, User * u, int level) s = hnode_get(sn); ss = findstats(s->name); - if ((level == 0) && (strlen(s->uplink) <= 0)) { + if ((level == 0) && (s->uplink[0] != 0)) { /* its the root server */ prefmsg(u->nick, s_StatServ, "\2%-45s [ %d/%d ] [ %d/%d ] [ %ld/%ld ]", @@ -1125,7 +1125,7 @@ static void ss_stats(User * u, char *cmd, char *arg, char *arg2) hscan_t scan; SET_SEGV_LOCATION(); - if (UserLevel(u) < 185) { + if (UserLevel(u) < NS_ULEVEL_ADMIN) { nlog(LOG_NORMAL, LOG_MOD, "Access Denied (STATS) to %s", u->nick); prefmsg(u->nick, s_StatServ, "Access Denied."); diff --git a/dl/template/template.c b/dl/template/template.c index 7b91779c..a796ea1e 100755 --- a/dl/template/template.c +++ b/dl/template/template.c @@ -30,8 +30,9 @@ */ #include -#include "dl.h" -#include "stats.h" +#include "dl.h" /* Required for module */ +#include "stats.h" /* Required for bot support */ +#include "log.h" /* Log systems support */ /** * A string to hold the name of our bot @@ -80,8 +81,9 @@ Functions __module_functions[] = { * What do we do with messages in channels * This is required if you want your module to respond to channel messages */ -int __Chan_Message(char *origin, char *chan, char **argv, int argc) +int __ChanMessage(char *origin, char **argv, int argc) { + char *chan = av[0]; return 1; } @@ -89,18 +91,34 @@ int __Chan_Message(char *origin, char *chan, char **argv, int argc) * What do we do with messages sent to our bot with /mag * This is required if you want your module to respond to /msg */ -int __Bot_Message(char *origin, char **argv, int argc) +int __BotMessage(char *origin, char **argv, int argc) { + User *u; + char *buf; + u = finduser(origin); + if (!u) { + nlog(LOG_WARNING, LOG_CORE, "Unable to find user %s ", origin); + return -1; + } + buf = joinbuf(argv, argc, 1); + globops(me.name, "Bot recieved %s from (%s!%s@%s)", buf, u->nick, u->username, u->hostname); + chanalert(s_module_bot_name, "Bot recieved %s from (%s!%s@%s)", buf, u->nick, u->username, u->hostname); + nlog(LOG_NORMAL, LOG_MOD, "Bot recieved %s from (%s!%s@%s)", buf, u->nick, u->username, u->hostname); + free(buf); return 1; } /** Online event processing * What we do when we first come online - * This is required if you want your module to respond to an event on IRC - * see modules.txt for a list of all events available */ int Online(char **av, int ac) { + /* Introduce a bot onto the network */ + if (init_bot(s_module_bot_name, "user", me.name, "Real Name", "-x", + __module_info.module_name) == -1) { + /* Nick was in use */ + return 0; + } return 1; }; @@ -120,7 +138,7 @@ EventFnList __module_events[] = { */ int __ModInit(int modnum, int apiver) { - s_module_bot_name = "NeoBot"; + s_module_bot_name = "TemplateBot"; return 1; } diff --git a/dns.c b/dns.c index 3b6f953a..99f72cef 100644 --- a/dns.c +++ b/dns.c @@ -34,8 +34,9 @@ #include "stats.h" #include "log.h" #include +#include "dns.h" - +adns_state ads; /** @brief DNS lookup Struct * structure containing all pending DNS lookups and the callback functions diff --git a/dns.h b/dns.h new file mode 100644 index 00000000..3113b1dc --- /dev/null +++ b/dns.h @@ -0,0 +1,30 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond, Mark Hetherington +** http://www.neostats.net/ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ + +#ifndef _DNS_H_ +#define _DNS_H_ + +int init_dns (void); +void do_dns (void); + +#endif /* _DNS_H_ */ diff --git a/doc/USERMAN b/doc/USERMAN index 0b7aa504..8c8a0b94 100644 --- a/doc/USERMAN +++ b/doc/USERMAN @@ -127,10 +127,8 @@ Contents 7.3.8 BEHAPPY 7.3.9 WONDERFUL 7.3.10 VERSION -8. spam - 8.1 About spam -9. ExtAuth - 9.1 About ExtAuth +8. ExtAuth + 8.1 About ExtAuth -----------------------------------<>----------------------------------- @@ -348,7 +346,7 @@ Example: 2.3.13 DEBUG ------------ Syntax: - /msg NeoStats DEBUG + /msg NeoStats DEBUG Description: This toggles the debug command, On a large network, this command should be used considered dangerous as a large amount of information @@ -1205,25 +1203,9 @@ Example: -----------------------------------<>----------------------------------- -8. spam -------- - -8.1 About spam --------------- - -Spam provides a bot to try and catch spammers and mass messagers. It -will sit on the network and echo to the services channel and by wallops -when someone sends it a message and what message they sent. - -Currently the spam bot has a fixed name of "sumyungguy". - -Send it a message, and watch what happens. - ------------------------------------<>----------------------------------- - -9. ExtAuth +8. ExtAuth ---------- -9.1 About ExtAuth +8.1 About ExtAuth ----------------- ExtAuth may be listed as a loaded module, but its not a true module as diff --git a/hybrid7.c b/hybrid7.c index 114eaa2b..7845d4d6 100644 --- a/hybrid7.c +++ b/hybrid7.c @@ -27,10 +27,14 @@ #include "hybrid7.h" #include "dl.h" #include "log.h" +#include "users.h" +#include "server.h" +#include "chans.h" static char ircd_buf[BUFSIZE]; const char ircd_version[] = "(H)"; +const char services_bot_modes[]= "+oS"; /* this is the command list and associated functions to run */ IntCommands cmd_list[] = { @@ -136,7 +140,7 @@ Oper_Modes usr_mds[] = { , {UMODE_CCONN, 'c', 0} , - {UMODE_DEBUG, 'd', 200} + {UMODE_DEBUG, 'd', NS_ULEVEL_ROOT} , {UMODE_FULL, 'f', 0} , @@ -421,6 +425,11 @@ ssvshost_cmd (const char *who, const char *vhost) nlog (LOG_NOTICE, LOG_CORE, "Warning. Module %s tried to SVSHOST, which is not supported in Hybrid", segvinmodule); return 1; } +int +sinvite_cmd (const char *from, const char *to, const char *chan) { + sts (":%s INVITE %s %s", from, to, chan); + return 1; +} int ssvinfo_cmd () @@ -778,7 +787,7 @@ Usr_Away (char *origin, char **argv, int argc) } else { buf = NULL; } - Do_Away (u, buf); + UserAway (u, buf); if (argc > 0) { free (buf); } @@ -805,7 +814,7 @@ Usr_Topic (char *origin, char **argv, int argc) c = findchan (argv[0]); if (c) { buf = joinbuf (argv, argc, 2); - Change_Topic (origin, c, me.now, buf); + ChangeTopic (origin, c, me.now, buf); free (buf); } else { nlog (LOG_WARNING, LOG_CORE, "Ehhh, Can't find Channel %s", argv[0]); diff --git a/hybrid7.h b/hybrid7.h index 9f21fe10..c2816dbb 100644 --- a/hybrid7.h +++ b/hybrid7.h @@ -25,8 +25,20 @@ #ifndef HYBRID7_H #define HYBRID7_H +/* we dont support tokens */ +#undef HAVE_TOKEN_SUP + +/* we dont have svshost support */ +#undef GOTSVSHOST + +/* we don't have svsjoin support */ +#undef GOTSVSJOIN +/* Moved from connectserv so we can use elsewhere */ +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define SERVERADMIN_MODE 'a' #define MSG_EOB "EOB" /* end of burst */ #define MSG_PRIVATE "PRIVMSG" /* PRIV */ diff --git a/ircd.c b/ircd.c index de375e19..97158230 100644 --- a/ircd.c +++ b/ircd.c @@ -29,6 +29,7 @@ #include "ircd.h" #include "dl.h" #include "log.h" +#include "services.h" /** @brief init_bot * @@ -37,7 +38,7 @@ * @return NS_SUCCESS if suceeds, NS_FAILURE if not */ int -init_bot (char *nick, char *user, char *host, char *rname, char *modes, char *mod_name) +init_bot (char *nick, char *user, char *host, char *rname, const char *modes, char *mod_name) { User *u; char **av; @@ -84,7 +85,7 @@ init_bot (char *nick, char *user, char *host, char *rname, char *modes, char *mo } SignOn_NewBot (nick, user, host, rname, Umode); AddStringToList (&av, nick, &ac); - Module_Event (EVENT_SIGNON, av, ac); + ModuleEvent (EVENT_SIGNON, av, ac); free (av); /* restore segv_inmodule from SIGNON */ SET_SEGV_INMODULE(mod_name); @@ -115,49 +116,6 @@ del_bot (char *nick, char *reason) return NS_SUCCESS; } -/** @brief Module_Event - * - * - * - * @return none - */ -void -Module_Event (char *event, char **av, int ac) -{ - Module *module_ptr; - EventFnList *ev_list; - hscan_t ms; - hnode_t *mn; - - SET_SEGV_LOCATION(); - hash_scan_begin (&ms, mh); - while ((mn = hash_scan_next (&ms)) != NULL) { - module_ptr = hnode_get (mn); - ev_list = module_ptr->other_funcs; - if (ev_list) { - while (ev_list->cmd_name != NULL) { - /* This goes through each Command */ - if (!strcasecmp (ev_list->cmd_name, event)) { - nlog (LOG_DEBUG1, LOG_CORE, "Running Module %s for Comamnd %s -> %s", module_ptr->info->module_name, event, ev_list->cmd_name); - SET_SEGV_LOCATION(); - SET_SEGV_INMODULE(module_ptr->info->module_name); - if (setjmp (sigvbuf) == 0) { - if (ev_list->function) ev_list->function (av, ac); - } else { - nlog (LOG_CRITICAL, LOG_CORE, "setjmp() Failed, Can't call Module %s\n", module_ptr->info->module_name); - } - CLEAR_SEGV_INMODULE(); - SET_SEGV_LOCATION(); -#ifndef VALGRIND - break; -#endif - } - ev_list++; - } - } - } -} - /** @brief split_buf * Taken from Epona - Thanks! * Split a buffer into arguments and store the arguments in an @@ -249,11 +207,7 @@ parse (char *line) int I = 0; int ac; char **av; - Module *module_ptr; - Functions *fn_list; - Mod_User *list; - hscan_t ms; - hnode_t *mn; + ModUser *mod_usr; SET_SEGV_LOCATION(); strip (line); @@ -286,11 +240,11 @@ parse (char *line) coreLine = line + strlen (line); #ifdef IRCU if ((!strcasecmp(line, "SERVER")) || (!strcasecmp(line, "PASS"))) { - strncpy(cmd, line, sizeof(cmd)); + strlcpy(cmd, line, sizeof(cmd)); ac = split_buf(coreLine, &av, 1); cmdptr = 0; } else { - strncpy(origin, line, sizeof(origin)); + strlcpy(origin, line, sizeof(origin)); cmdptr = 1; line = strpbrk (coreLine, " "); if (line) { @@ -302,7 +256,7 @@ parse (char *line) } #else - strncpy (cmd, line, sizeof (cmd)); + strlcpy (cmd, line, sizeof (cmd)); ac = split_buf (coreLine, &av, 1); #endif @@ -334,10 +288,10 @@ parse (char *line) free (av); return; } else { - list = findbot (av[0]); + mod_usr = findbot (av[0]); /* Check to see if any of the Modules have this nick Registered */ - if (list) { - nlog (LOG_DEBUG1, LOG_CORE, "nicks: %s", list->nick); + if (mod_usr) { + nlog (LOG_DEBUG1, LOG_CORE, "nicks: %s", mod_usr->nick); if (flood (finduser (origin))) { free (av); return; @@ -354,16 +308,16 @@ parse (char *line) } SET_SEGV_LOCATION(); - SET_SEGV_INMODULE(list->modname); + SET_SEGV_INMODULE(mod_usr->modname); if (setjmp (sigvbuf) == 0) { - list->function (origin, av, ac); + mod_usr->function (origin, av, ac); } CLEAR_SEGV_INMODULE(); SET_SEGV_LOCATION(); free (av); return; } else { - bot_chan_message (origin, av[0], av, ac); + bot_chan_message (origin, av, ac); free (av); return; } @@ -383,28 +337,7 @@ parse (char *line) } /* K, now Parse it to the Module functions */ SET_SEGV_LOCATION(); - hash_scan_begin (&ms, mh); - while ((mn = hash_scan_next (&ms)) != NULL) { - module_ptr = hnode_get (mn); - fn_list = module_ptr->function_list; - while (fn_list->cmd_name != NULL) { - /* This goes through each Command */ - if (!strcmp (fn_list->cmd_name, cmd)) { - if (fn_list->srvmsg == cmdptr) { - nlog (LOG_DEBUG1, LOG_CORE, "Running Module %s for Function %s", module_ptr->info->module_name, fn_list->cmd_name); - SET_SEGV_LOCATION(); - SET_SEGV_INMODULE(module_ptr->info->module_name); - if (setjmp (sigvbuf) == 0) { - fn_list->function (origin, av, ac); - } - CLEAR_SEGV_INMODULE(); - SET_SEGV_LOCATION(); - break; - } - } - fn_list++; - } - } + ModuleFunction (cmdptr, cmd, origin, av, ac); free (av); } @@ -430,11 +363,11 @@ init_ServBot (void) SignOn_NewBot (s_Services, Servbot.user, Servbot.host, rname, UMODE_SERVICES); me.onchan = 1; AddStringToList (&av, me.uplink, &ac); - Module_Event (EVENT_ONLINE, av, ac); + ModuleEvent (EVENT_ONLINE, av, ac); free (av); ac = 0; AddStringToList (&av, s_Services, &ac); - Module_Event (EVENT_SIGNON, av, ac); + ModuleEvent (EVENT_SIGNON, av, ac); free (av); } @@ -457,7 +390,7 @@ dopong (Server * s) if (!strcmp (me.s->name, s->name)) ping.ulag = me.s->ping; AddStringToList (&av, s->name, &ac); - Module_Event (EVENT_PONG, av, ac); + ModuleEvent (EVENT_PONG, av, ac); free (av); } else { nlog (LOG_NOTICE, LOG_CORE, "Received PONG from unknown server: %s", recbuf); @@ -479,7 +412,7 @@ flood (User * u) nlog (LOG_WARNING, LOG_CORE, "Warning, Can't find user for FLOODcheck"); return 0; } - if (UserLevel (u) >= 40) /* locop or higher */ + if (UserLevel (u) >= NS_ULEVEL_OPER) /* locop or higher */ return 0; if (current - u->t_flood > 10) { u->t_flood = me.now; diff --git a/liquidircd.c b/liquidircd.c new file mode 100644 index 00000000..7fb01a34 --- /dev/null +++ b/liquidircd.c @@ -0,0 +1,1029 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond +** http://www.neostats.net/ +** +** Portions Copyright (c) 2000-2001 ^Enigma^ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ + +#include "stats.h" +#include "ircd.h" +#include "sock.h" +#include "liquidircd.h" +#include "dl.h" +#include "log.h" +#include "users.h" +#include "server.h" +#include "chans.h" + +static char ircd_buf[BUFSIZE]; + +const char ircd_version[] = "(L)"; +const char services_bot_modes[]= "+oS"; + +IntCommands cmd_list[] = { + /* Command Function srvmsg */ + {MSG_STATS, Usr_Stats, 1, 0} + , + {MSG_SETHOST, Usr_Vhost, 1, 0} + , + {MSG_VERSION, Usr_Version, 1, 0} + , + {MSG_MOTD, Usr_ShowMOTD, 1, 0} + , + {MSG_CREDITS, Usr_Showcredits, 1, 0} + , + {MSG_SERVER, Usr_AddServer, 1, 0} + , + {MSG_SQUIT, Usr_DelServer, 1, 0} + , + {MSG_QUIT, Usr_DelUser, 1, 0} + , + {MSG_MODE, Usr_Mode, 1, 0} + , + {MSG_SVSMODE, Usr_Smode, 1, 0} + , + {MSG_KILL, Usr_Kill, 1, 0} + , + {MSG_PONG, Usr_Pong, 1, 0} + , + {MSG_AWAY, Usr_Away, 1, 0} + , + {MSG_NICK, Usr_Nick, 1, 0} + , + {MSG_TOPIC, Usr_Topic, 1, 0} + , + {MSG_KICK, Usr_Kick, 1, 0} + , + {MSG_JOIN, Usr_Join, 1, 0} + , + {MSG_PART, Usr_Part, 1, 0} + , + {MSG_PING, Srv_Ping, 0, 0} + , + {MSG_SVINFO, Srv_Svinfo, 0, 0} + , + {MSG_CAPAB, Srv_Connect, 0, 0} + , + {MSG_BURST, Srv_Burst, 0, 0} + , + {MSG_SJOIN, Srv_Sjoin, 1, 0} + , + {MSG_PASS, Srv_Pass, 0, 0} + , + {MSG_SERVER, Srv_Server, 0, 0} + , + {MSG_SQUIT, Srv_Squit, 0, 0} + , + {MSG_NICK, Srv_Nick, 0, 0} + , + {MSG_SVSNICK, Srv_Svsnick, 0, 0} + , + {MSG_KILL, Srv_Kill, 0, 0} + , + {NULL, NULL, 0, 0} +}; + + + + +aCtab cFlagTab[] = { + {MODE_CHANOWNER, 'q', 1, 0, '!'} + , + {MODE_CHANPROT, 'a', 1, 0, '*'} + , + {MODE_CHANOP, 'o', 1, 0, '@'} + , + {MODE_VIP, 'V', 1, 0, '='} + , + {MODE_HALFOP, 'h', 1, 0, '%'} + , + {MODE_VOICE, 'v', 1, 0, '+'} + , + {MODE_UOP, 'u', 1, 0, '-'} + , + {MODE_BAN, 'b', 0, 1, 0} + , + {MODE_NOCOLOR, 'c', 0, 0, 0} + , + {MODE_INVITEONLY, 'i', 0, 0, 0} + , + {MODE_KEY, 'k', 0, 1, 0} + , + {MODE_LIMIT, 'l', 0, 1, 0} + , + {MODE_LINK, 'L', 0, 1, 0} + , + {MODE_MODERATED, 'm', 0, 0, 0} + , + {MODE_NOPRIVMSGS, 'n', 0, 0, 0} + , + {MODE_NONICKCHANGE, 'N', 0, 0, 0} + , + {MODE_PRIVATE, 'p', 0, 0, 0} + , + {MODE_RGSTR, 'r', 0, 0, 0} + , + {MODE_RGSTRONLY, 'R', 0, 0, 0} + , + {MODE_SECRET, 's', 0, 0, 0} + , + {MODE_STRIP, 'S', 0, 0, 0} + , + {MODE_TOPICLIMIT, 't', 0, 0, 0} + , + {MODE_OPERONLY, 'O', 0, 0, 0} + , + /* XXX todo, Mode COLOR */ + {0x0, 0x0, 0x0, 0x0, 0x0} +}; + + +Oper_Modes usr_mds[] = { + {UMODE_OPER, 'o', 50} + , + {UMODE_LOCOP, 'O', 40} + , + {UMODE_INVISIBLE, 'i', 0} + , + {UMODE_WALLOP, 'w', 0} + , + {UMODE_FAILOP, 'g', 0} + , + {UMODE_HELPOP, 'h', 30} + , + {UMODE_REGNICK, 'r', 10} + , + {UMODE_SERVNOTICE, 's', 0} + , + {UMODE_KILLS, 'k', 0} + , + {UMODE_CLIENT, 'c', 0} + , + {UMODE_FLOOD, 'f', 0} + , + {UMODE_SERVICESADMIN, 'a', 200} + , + {UMODE_SERVADMIN, 'A', 100} + , + {UMODE_NETADMIN, 'N', 185} + , + {UMODE_TECHADMIN, 'T', 190} + , + {UMODE_REGONLY, 'R', 0} + , + {UMODE_KIX, 'q', 0} + , + {UMODE_BOT, 'B', 0} + , + {UMODE_HIDE, 'z', 0} + , + {UMODE_WHOIS, 'W', 0} + , + /* this is needed for bot support */ + {UMODE_SERVICES, 'S', 200} + , + {0, 0, 0} +}; +Oper_Modes susr_mds[] = { + {SMODE_SSL, 's', 0} + , + {0, 0, 0} +}; + +void +init_ircd () +{ + /* count the number of commands */ + ircd_srv.cmdcount = ((sizeof (cmd_list) / sizeof (cmd_list[0])) - 1); +}; + +int +sserver_cmd (const char *name, const int numeric, const char *infoline) +{ + sts (":%s %s %s %d :%s", me.name, MSG_SERVER, name, numeric, infoline); + return 1; +} + +int +slogin_cmd (const char *name, const int numeric, const char *infoline, const char *pass) +{ + sts ("%s %s :TS", MSG_PASS, pass); + sts ("CAPAB TS3 SSJOIN BURST NICKIP"); + sts ("%s %s %d :%s", MSG_SERVER, name, numeric, infoline); + return 1; +} + +int +ssquit_cmd (const char *server) +{ + sts ("%s %s", MSG_SQUIT, server); + return 1; +} + +int +sprotocol_cmd (const char *option) +{ + return 1; +} + +int +squit_cmd (const char *who, const char *quitmsg) +{ + sts (":%s %s :%s", who, MSG_QUIT, quitmsg); + DelUser (who); + return 1; +} + +int +spart_cmd (const char *who, const char *chan) +{ + sts (":%s %s %s", who, MSG_PART, chan); + part_chan (finduser (who), (char *) chan); + return 1; +} + +int +sjoin_cmd (const char *who, const char *chan, unsigned long chflag) +{ + char flag; + char mode; + char **av; + int ac; + time_t tstime; + Chans *c; + + c = findchan ((char *) chan); + if (!c) { + tstime = me.now; + } else { + tstime = c->tstime; + } + switch (chflag) { + case MODE_CHANOWNER: + flag = '!'; + mode= 'q'; + break; + case MODE_CHANPROT: + flag = '*'; + mode= 'a'; + break; + case MODE_VIP: + flag = '='; + mode= 'V'; + break; + case MODE_CHANOP: + flag = '@'; + mode= 'o'; + break; + case MODE_HALFOP: + flag = '%'; + mode= 'h'; + break; + case MODE_VOICE: + flag = '+'; + mode= 'v'; + break; + case MODE_UOP: + flag = '-'; + mode= 'u'; + break; + default: + flag = ' '; + mode= '\0'; + } + sts (":%s %s %d %s + :%c%s", me.name, MSG_SJOIN, tstime, chan, flag, who); + join_chan (finduser (who), (char *) chan); + ircsnprintf (ircd_buf, BUFSIZE, "%s +%c %s", chan, mode, who); + ac = split_buf (ircd_buf, &av, 0); + ChanMode (me.name, av, ac); + free (av); + return 1; +} + +int +schmode_cmd (const char *who, const char *chan, const char *mode, const char *args) +{ + char **av; + int ac; + + sts (":%s %s %s %s %s %lu", me.name, MSG_MODE, chan, mode, args, me.now); + ircsnprintf (ircd_buf, BUFSIZE, "%s %s %s", chan, mode, args); + ac = split_buf (ircd_buf, &av, 0); + ChanMode ("", av, ac); + free (av); + return 1; +} + +int +snewnick_cmd (const char *nick, const char *ident, const char *host, const char *realname, long mode) +{ + int i, j; + char newmode[20]; + newmode[0] = '+'; + j = 1; + for (i = 0; i < ((sizeof (usr_mds) / sizeof (usr_mds[0])) - 1); i++) { + if (mode & usr_mds[i].umodes) { + newmode[j] = usr_mds[i].mode; + j++; + } + + } + newmode[j] = '\0'; + sts ("%s %s 1 %lu %s %s %s %s 0 %lu :%s", MSG_NICK, nick, me.now, newmode, ident, host, me.name, me.now, realname); + AddUser (nick, ident, host, me.name, 0, me.now); + UserMode (nick, newmode, 0); + return 1; +} + +int +sping_cmd (const char *from, const char *reply, const char *to) +{ + sts (":%s %s %s :%s", from, MSG_PING, reply, to); + return 1; +} + +int +sumode_cmd (const char *who, const char *target, long mode) +{ + int i, j; + char newmode[20]; + newmode[0] = '+'; + j = 1; + for (i = 0; i < ((sizeof (usr_mds) / sizeof (usr_mds[0])) - 1); i++) { + if (mode & usr_mds[i].umodes) { + newmode[j] = usr_mds[i].mode; + j++; + } + + } + newmode[j] = '\0'; + sts (":%s %s %s :%s", who, MSG_MODE, target, newmode); + UserMode (target, newmode, 0); + return 1; +} + +int +snumeric_cmd (const int numeric, const char *target, const char *data, ...) +{ + va_list ap; + char buf[BUFSIZE]; + + va_start (ap, data); + ircvsnprintf (buf, BUFSIZE, data, ap); + va_end (ap); + sts (":%s %d %s :%s", me.name, numeric, target, buf); + return 1; +} + +int +spong_cmd (const char *reply) +{ + sts ("%s %s", MSG_PONG, reply); + return 1; +} + +int +skill_cmd (const char *from, const char *target, const char *reason, ...) +{ + va_list ap; + char buf[BUFSIZE]; + + va_start (ap, reason); + ircvsnprintf (buf, BUFSIZE, reason, ap); + va_end (ap); + sts (":%s %s %s :%s", from, MSG_KILL, target, buf); + DelUser (target); + return 1; +} + +int +ssvskill_cmd (const char *who, const char *reason, ...) +{ + va_list ap; + char buf[BUFSIZE]; + + va_start (ap, reason); + ircvsnprintf (buf, BUFSIZE, reason, ap); + va_end (ap); + sts (":%s %s %s :%s", me.name, MSG_SVSKILL, who, buf); + return 1; +} + +int +ssmo_cmd (const char *from, const char *umodetarget, const char *msg) +{ + chanalert (s_Services, "Warning, Module %s tried to SMO, which is not supported in Bahamut", segvinmodule); + nlog (LOG_NOTICE, LOG_CORE, "Warning, Module %s tried to SMO, which is not supported in Bahamut", segvinmodule); + return 1; +} + +int +snick_cmd (const char *oldnick, const char *newnick) +{ + Change_User (finduser (oldnick), newnick); + sts (":%s %s %s %d", oldnick, MSG_NICK, newnick, me.now); + return 1; +} + +int +sswhois_cmd (const char *target, const char *swhois) +{ + chanalert (s_Services, "Warning Module %s tried to SWHOIS, which is not supported in Bahamut", segvinmodule); + nlog (LOG_NOTICE, LOG_CORE, "Warning. Module %s tried to SWHOIS, which is not supported in Bahamut", segvinmodule); + return 1; +} + +int +ssvsnick_cmd (const char *target, const char *newnick) +{ + sts ("%s %s %s :%d", MSG_SVSNICK, target, newnick, me.now); + return 1; +} + +int +ssvsjoin_cmd (const char *target, const char *chan) +{ + chanalert (s_Services, "Warning Module %s tried to SVSJOIN, which is not supported in Bahamut", segvinmodule); + nlog (LOG_NOTICE, LOG_CORE, "Warning. Module %s tried to SVSJOIN, which is not supported in Bahamut", segvinmodule); + return 1; +} + +int +ssvspart_cmd (const char *target, const char *chan) +{ + chanalert (s_Services, "Warning Module %s tried to SVSPART, which is not supported in Bahamut", segvinmodule); + nlog (LOG_NOTICE, LOG_CORE, "Warning. Module %s tried to SVSPART, which is not supported in Bahamut", segvinmodule); + return 1; +} + +int +skick_cmd (const char *who, const char *target, const char *chan, const char *reason) +{ + sts (":%s %s %s %s :%s", who, MSG_KICK, chan, target, (reason ? reason : "No Reason Given")); + part_chan (finduser (target), (char *) chan); + return 1; +} + +int +swallops_cmd (const char *who, const char *msg, ...) +{ + va_list ap; + char buf[BUFSIZE]; + + va_start (ap, msg); + ircvsnprintf (buf, BUFSIZE, msg, ap); + va_end (ap); + sts (":%s %s :%s", who, MSG_WALLOPS, buf); + return 1; +} + +int +ssvshost_cmd (const char *who, const char *vhost) +{ + User *u; + + u = finduser (who); + if (!u) { + nlog (LOG_WARNING, LOG_CORE, "Can't Find user %s for ssvshost_cmd", who); + return 0; + } + strlcpy (u->vhost, vhost, MAXHOST); + sts (":%s SVSCHGHOST %s %s", me.name, who, vhost); + return 1; +} +int +sinvite_cmd (const char *from, const char *to, const char *chan) { + sts (":%s INVITE %s %s", from, to, chan); + return 1; +} + +int +sakill_cmd (const char *host, const char *ident, const char *setby, const int length, const char *reason, ...) +{ + va_list ap; + char buf[BUFSIZE]; + + va_start (ap, reason); + ircvsnprintf (buf, BUFSIZE, reason, ap); + va_end (ap); + sts (":%s %s %s %s %d %s %d :%s", me.name, MSG_AKILL, host, ident, length, setby, me.now, buf); + return 1; +} + +int +srakill_cmd (const char *host, const char *ident) +{ + sts (":%s %s %s %s", me.name, MSG_RAKILL, host, ident); + return 1; +} + + +int +ssvinfo_cmd () +{ + sts ("SVINFO 3 3 0 :%d", me.now); + return 1; +} + +int +sburst_cmd (int b) +{ + if (b == 0) { + sts ("BURST 0"); + } else { + sts ("BURST"); + } + return 1; +} + +void +chanalert (char *who, char *buf, ...) +{ + va_list ap; + + if (!me.onchan) + return; + + va_start (ap, buf); + ircvsnprintf (ircd_buf, BUFSIZE, buf, ap); + va_end (ap); + sts (":%s PRIVMSG %s :%s", who, me.chan, ircd_buf); +} + +void +prefmsg (char *to, const char *from, char *fmt, ...) +{ + va_list ap; + + if (findbot (to)) { + chanalert (s_Services, "Message From our Bot(%s) to Our Bot(%s), Dropping Message", from, to); + return; + } + + va_start (ap, fmt); + ircvsnprintf (ircd_buf, BUFSIZE, fmt, ap); + va_end (ap); + if (me.want_privmsg) { + sts (":%s PRIVMSG %s :%s", from, to, ircd_buf); + } else { + sts (":%s NOTICE %s :%s", from, to, ircd_buf); + } +} + +void +privmsg (char *to, const char *from, char *fmt, ...) +{ + va_list ap; + + if (findbot (to)) { + chanalert (s_Services, "Message From our Bot(%s) to Our Bot(%s), Dropping Message", from, to); + return; + } + + va_start (ap, fmt); + ircvsnprintf (ircd_buf, BUFSIZE, fmt, ap); + va_end (ap); + sts (":%s PRIVMSG %s :%s", from, to, ircd_buf); +} + +void +notice (char *to, const char *from, char *fmt, ...) +{ + va_list ap; + + if (findbot (to)) { + chanalert (s_Services, "Message From our Bot(%s) to Our Bot(%s), Dropping Message", from, to); + return; + } + + va_start (ap, fmt); + ircvsnprintf (ircd_buf, BUFSIZE, fmt, ap); + va_end (ap); + sts (":%s NOTICE %s :%s", from, to, ircd_buf); +} + + +void +privmsg_list (char *to, char *from, const char **text) +{ + while (*text) { + if (**text) + prefmsg (to, from, (char*)*text); + else + prefmsg (to, from, " "); + text++; + } +} + + +void +globops (char *from, char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + ircvsnprintf (ircd_buf, BUFSIZE, fmt, ap); + va_end (ap); + + if (me.onchan) { + sts (":%s GLOBOPS :%s", from, ircd_buf); + } else { + nlog (LOG_NORMAL, LOG_CORE, ircd_buf); + } +} + +void +Srv_Sjoin (char *origin, char **argv, int argc) +{ + char nick[MAXNICK]; + long mode = 0; + long mode1 = 0; + char *modes; + int ok = 1, i, j = 3; + ModesParm *m; + Chans *c; + lnode_t *mn = NULL; + list_t *tl; + if (argc > 4) { + modes = argv[2]; + } else { + modes = argv[1]; + } + if (*modes == '#') { + join_chan (finduser (origin), modes); + return; + } + tl = list_create (10); + if (*modes != '+') { + goto nomodes; + } + while (*modes) { + for (i = 0; i < ((sizeof (cFlagTab) / sizeof (cFlagTab[0])) - 1); i++) { + if (*modes == cFlagTab[i].flag) { + if (cFlagTab[i].parameters) { + m = smalloc (sizeof (ModesParm)); + m->mode = cFlagTab[i].mode; + strlcpy (m->param, argv[j], PARAMSIZE); + mn = lnode_create (m); + if (!list_isfull (tl)) { + list_append (tl, mn); + } else { + nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, tl list is full in Svr_Sjoin(ircd.c)"); + do_exit (NS_EXIT_ERROR, "List full - see log file"); + } + j++; + } else { + mode1 |= cFlagTab[i].mode; + } + } + } + modes++; + } + nomodes: + while (argc > j) { + modes = argv[j]; + mode = 0; + while (ok == 1) { + for (i = 0; i < ((sizeof (cFlagTab) / sizeof (cFlagTab[0])) - 1); i++) { + if (cFlagTab[i].sjoin != 0) { + if (*modes == cFlagTab[i].sjoin) { + mode |= cFlagTab[i].mode; + modes++; + i = -1; + } + } else { + /* sjoin's should be at the top of the list */ + ok = 0; + strlcpy (nick, modes, MAXNICK); + break; + } + } + } + join_chan (finduser (nick), argv[1]); + ChangeChanUserMode (findchan (argv[1]), finduser (nick), 1, mode); + j++; + ok = 1; + } + c = findchan (argv[1]); + /* update the TS time */ + ChangeChanTS (c, atoi (argv[0])); + c->modes |= mode1; + if (!list_isempty (tl)) { + if (!list_isfull (c->modeparms)) { + list_transfer (c->modeparms, tl, list_first (tl)); + } else { + /* eeeeeeek, list is full! */ + nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, c->modeparms list is full in Svr_Sjoin(ircd.c)"); + do_exit (NS_EXIT_ERROR, "List full - see log file"); + } + } + list_destroy (tl); +} + +void +Srv_Burst (char *origin, char **argv, int argc) +{ + if (argc > 0) { + if (ircd_srv.burst == 1) { + sburst_cmd (0); + ircd_srv.burst = 0; + me.synced = 1; + init_ServBot (); + } + } else { + ircd_srv.burst = 1; + } +} + +void +Srv_Connect (char *origin, char **argv, int argc) +{ +} + +void +Usr_Stats (char *origin, char **argv, int argc) +{ + User *u; + u = finduser (origin); + if (!u) { + nlog (LOG_WARNING, LOG_CORE, "Received a Message from an Unknown User! (%s)", origin); + return; + } + ShowStats (argv[0], u); +} + +void +Usr_Version (char *origin, char **argv, int argc) +{ + snumeric_cmd (RPL_VERSION, origin, "%d.%d.%d%s :%s -> %s %s", MAJOR, MINOR, REV, ircd_version, me.name, version_date, version_time); +} + +void +Usr_ShowMOTD (char *origin, char **argv, int argc) +{ + ShowMOTD (origin); +} + +void +Usr_ShowADMIN (char *origin, char **argv, int argc) +{ + ShowADMIN (origin); +} + +void +Usr_Showcredits (char *origin, char **argv, int argc) +{ + Showcredits (origin); +} + +void +Usr_AddServer (char *origin, char **argv, int argc) +{ + AddServer (argv[0], origin, atoi (argv[1])); +} + +void +Usr_DelServer (char *origin, char **argv, int argc) +{ + DelServer (argv[0]); +} + +void +Usr_DelUser (char *origin, char **argv, int argc) +{ + DelUser (origin); +} + +void +Usr_Smode (char *origin, char **argv, int argc) +{ + if (!strchr (argv[0], '#')) { + /* its user svsmode change */ + UserMode (argv[0], argv[2], 0); + } else { + /* its a channel svsmode change */ + ChanMode (origin, argv, argc); + } +} +void +Usr_Mode (char *origin, char **argv, int argc) +{ + if (!strchr (argv[0], '#')) { + nlog (LOG_DEBUG1, LOG_CORE, "Mode: UserMode: %s", argv[0]); + UserMode (argv[0], argv[1], 0); + } else { + ChanMode (origin, argv, argc); + } +} +void +Usr_Kill (char *origin, char **argv, int argc) +{ + User *u; + u = finduser (argv[0]); + if (u) { + KillUser (argv[0]); + } else { + nlog (LOG_WARNING, LOG_CORE, "Can't find user %s for Kill", argv[0]); + } +} +void +Usr_Vhost (char *origin, char **argv, int argc) +{ + User *u; + u = finduser (origin); + if (u) { + strlcpy (u->vhost, argv[0], MAXHOST); + } +} +void +Usr_Pong (char *origin, char **argv, int argc) +{ + Server *s; + s = findserver (argv[0]); + if (s) { + dopong (s); + } else { + nlog (LOG_NOTICE, LOG_CORE, "Received PONG from unknown server: %s", argv[0]); + } +} +void +Usr_Away (char *origin, char **argv, int argc) +{ + char *Buf; + User *u = finduser (origin); + if (u) { + if (argc > 0) { + Buf = joinbuf (argv, argc, 0); + } else { + Buf = NULL; + } + UserAway (u, Buf); + if (argc > 0) { + free (Buf); + } + } else { + nlog (LOG_NOTICE, LOG_CORE, "Warning, Unable to find User %s for Away", origin); + } +} +void +Usr_Nick (char *origin, char **argv, int argc) +{ + User *u = finduser (origin); + if (u) { + Change_User (u, argv[0]); + } +} +void +Usr_Topic (char *origin, char **argv, int argc) +{ + char *buf; + Chans *c; + c = findchan (argv[0]); + if (c) { + buf = joinbuf (argv, argc, 3); + ChangeTopic (argv[1], c, atoi (argv[2]), buf); + free (buf); + } else { + nlog (LOG_WARNING, LOG_CORE, "Ehhh, Can't find Channel %s", argv[0]); + } + +} + +void +Usr_Kick (char *origin, char **argv, int argc) +{ + User *u, *k; + u = finduser (argv[1]); + k = finduser (origin); + if (u) { + kick_chan (u, argv[0], k); + } else { + nlog (LOG_WARNING, LOG_CORE, "Warning, Can't find user %s for kick %s", argv[1], argv[0]); + } +} +void +Usr_Join (char *origin, char **argv, int argc) +{ + char *s, *t; + t = argv[0]; + while (*(s = t)) { + t = s + strcspn (s, ","); + if (*t) + *t++ = 0; + join_chan (finduser (origin), s); + } +} +void +Usr_Part (char *origin, char **argv, int argc) +{ + part_chan (finduser (origin), argv[0]); +} + +void +Srv_Ping (char *origin, char **argv, int argc) +{ + spong_cmd (argv[0]); + if (ircd_srv.burst) { + sping_cmd (me.name, argv[0], argv[0]); + } +} + +void +Srv_Svinfo (char *origin, char **argv, int argc) +{ + ssvinfo_cmd (); +} + + +void +Srv_Pass (char *origin, char **argv, int argc) +{ +} +void +Srv_Server (char *origin, char **argv, int argc) +{ + Server *s; + if (*origin == 0) { + AddServer (argv[0], me.name, atoi (argv[1])); + } else { + AddServer (argv[0], origin, atoi (argv[1])); + } + s = findserver (argv[0]); + me.s = s; +} + +void +Srv_Squit (char *origin, char **argv, int argc) +{ + Server *s; + s = findserver (argv[0]); + if (s) { + DelServer (argv[0]); + } else { + nlog (LOG_WARNING, LOG_CORE, "Warning, Squit from Unknown Server %s", argv[0]); + } + +} + +/* BE REALLY CAREFULL ABOUT THE ORDER OF THESE ifdef's */ + +void +Srv_Nick (char *origin, char **argv, int argc) +{ + char *realname; + AddUser (argv[0], argv[4], argv[5], argv[6], strtoul (argv[8], NULL, 10), strtoul (argv[2], NULL, 10)); + realname = joinbuf (argv, argc, 9); + AddRealName (argv[0], realname); + free (realname); + nlog (LOG_DEBUG1, LOG_CORE, "Mode: UserMode: %s", argv[3]); + UserMode (argv[0], argv[3], 0); +} + +void +Srv_Svsnick (char *origin, char **argv, int argc) +{ + User *u; + u = finduser (argv[0]); + if (u) { + Change_User (u, argv[1]); + } else { + nlog (LOG_WARNING, LOG_CORE, "Warning, Can't find user %s for SVSNICK", argv[0]); + } + +} +void +Srv_Kill (char *origin, char **argv, int argc) +{ +} + + + +int +SignOn_NewBot (const char *nick, const char *user, const char *host, const char *rname, long Umode) +{ + + snewnick_cmd (nick, user, host, rname, Umode); + if ((me.allbots > 0) || (Umode & UMODE_SERVICES)) { + sjoin_cmd (nick, me.chan, MODE_CHANOP); + /* all bots join */ + } + return 1; +} diff --git a/liquidircd.h b/liquidircd.h new file mode 100644 index 00000000..ec8953a5 --- /dev/null +++ b/liquidircd.h @@ -0,0 +1,356 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond +** http://www.neostats.net/ +** +** Portions Copyright (c) 2000-2001 ^Enigma^ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ +#ifndef LIQUIDIRCD_H +#define LIQUIDIRCD_H + +/* we support tokens */ +#undef HAVE_TOKEN_SUP + +/* we have vhost support */ +#define GOTSVSVHOST + +/* we don't have svsjoin support */ +#undef GOTSVSJOIN + +/* Moved from connectserv so we can use elsewhere */ +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define SERVERADMIN_MODE 'A' +#define NETADMIN_MODE 'N' +#define TECHADMIN_MODE 'T' +#define SERVICESADMIN_MODE 'a' +#define NETSERVICE_MODE 'S' +#define BOT_MODE 'B' + +#define MSG_PRIVATE "PRIVMSG" /* PRIV */ +#define MSG_WHO "WHO" /* WHO -> WHOC */ +#define MSG_WHOIS "WHOIS" /* WHOI */ +#define MSG_WHOWAS "WHOWAS" /* WHOW */ +#define MSG_USER "USER" /* USER */ +#define MSG_NICK "NICK" /* NICK */ +#define MSG_SERVER "SERVER" /* SERV */ +#define MSG_LIST "LIST" /* LIST */ +#define MSG_TOPIC "TOPIC" /* TOPI */ +#define MSG_INVITE "INVITE" /* INVI */ +#define MSG_VERSION "VERSION" /* VERS */ +#define MSG_QUIT "QUIT" /* QUIT */ +#define MSG_SQUIT "SQUIT" /* SQUI */ +#define MSG_KILL "KILL" /* KILL */ +#define MSG_INFO "INFO" /* INFO */ +#define MSG_LINKS "LINKS" /* LINK */ +#define MSG_WATCH "WATCH" /* WATCH */ +#define MSG_STATS "STATS" /* STAT */ +#define MSG_HELP "HELP" /* HELP */ +#define MSG_HELPOP "HELPOP" /* HELP */ +#define MSG_ERROR "ERROR" /* ERRO */ +#define MSG_AWAY "AWAY" /* AWAY */ +#define MSG_CONNECT "CONNECT" /* CONN */ +#define MSG_PING "PING" /* PING */ +#define MSG_PONG "PONG" /* PONG */ +#define MSG_OPER "OPER" /* OPER */ +#define MSG_PASS "PASS" /* PASS */ +#define MSG_WALLOPS "WALLOPS" /* WALL */ +#define MSG_TIME "TIME" /* TIME */ +#define MSG_NAMES "NAMES" /* NAME */ +#define MSG_ADMIN "ADMIN" /* ADMI */ +#define MSG_NOTICE "NOTICE" /* NOTI */ +#define MSG_JOIN "JOIN" /* JOIN */ +#define MSG_PART "PART" /* PART */ +#define MSG_LUSERS "LUSERS" /* LUSE */ +#define MSG_MOTD "MOTD" /* MOTD */ +#define MSG_MODE "MODE" /* MODE */ +#define MSG_KICK "KICK" /* KICK */ +#define MSG_SERVICE "SERVICE" /* SERV -> SRVI */ +#define MSG_USERHOST "USERHOST" /* USER -> USRH */ +#define MSG_ISON "ISON" /* ISON */ +#define MSG_SQUERY "SQUERY" /* SQUE */ +#define MSG_SERVLIST "SERVLIST" /* SERV -> SLIS */ +#define MSG_SERVSET "SERVSET" /* SERV -> SSET */ +#define MSG_REHASH "REHASH" /* REHA */ +#define MSG_RESTART "RESTART" /* REST */ +#define MSG_CLOSE "CLOSE" /* CLOS */ +#define MSG_DIE "DIE" /* DIE */ +#define MSG_HASH "HASH" /* HASH */ +#define MSG_DNS "DNS" /* DNS -> DNSS */ +#define MSG_SILENCE "SILENCE" /* SILE */ +#define MSG_AKILL "AKILL" /* AKILL */ +#define MSG_KLINE "KLINE" /* KLINE */ +#define MSG_UNKLINE "UNKLINE" /* UNKLINE */ +#define MSG_RAKILL "RAKILL" /* RAKILL */ +#define MSG_GNOTICE "GNOTICE" /* GNOTICE */ +#define MSG_GOPER "GOPER" /* GOPER */ +#define MSG_GLOBOPS "GLOBOPS" /* GLOBOPS */ +#define MSG_LOCOPS "LOCOPS" /* LOCOPS */ +#define MSG_PROTOCTL "PROTOCTL" /* PROTOCTL */ +#define MSG_TRACE "TRACE" /* TRAC */ +#define MSG_SQLINE "SQLINE" /* SQLINE */ +#define MSG_UNSQLINE "UNSQLINE" /* UNSQLINE */ +#define MSG_SVSNICK "SVSNICK" /* SVSNICK */ +#define MSG_SVSNOOP "SVSNOOP" /* SVSNOOP */ +#define MSG_IDENTIFY "IDENTIFY" /* IDENTIFY */ +#define MSG_SVSKILL "SVSKILL" /* SVSKILL */ +#define MSG_SVSMODE "SVSMODE" /* SVSMODE */ +#define MSG_SAMODE "SAMODE" /* SAMODE */ +#define MSG_CHATOPS "CHATOPS" /* CHATOPS */ +#define MSG_HELPSERV "HELPSERV" /* HELPSERV */ +#define MSG_ZLINE "ZLINE" /* ZLINE */ +#define MSG_UNZLINE "UNZLINE" /* UNZLINE */ +#define MSG_NETINFO "NETINFO" /* NETINFO */ +#define MSG_RULES "RULES" /* RULES */ +#define MSG_MAP "MAP" /* MAP */ +#define MSG_NETG "NETG" /* NETG */ +#define MSG_ADCHAT "ADCHAT" /* Adchat */ +#define MSG_MAKEPASS "MAKEPASS" /* MAKEPASS */ +#define MSG_ADDHUB "ADDHUB" /* ADDHUB */ +#define MSG_DELHUB "DELHUB" /* DELHUB */ +#define MSG_ADDCNLINE "ADDCNLINE" /* ADDCNLINE */ +#define MSG_DELCNLINE "DELCNLINE" /* DELCNLINE */ +#define MSG_ADDOPER "ADDOPER" /* ADDOPER */ +#define MSG_DELOPER "DELOPER" /* DELOPER */ +#define MSG_ADDQLINE "ADDQLINE" /* ADDQLINE */ +#define MSG_DELQLINE "DELQLINE" /* DELQLINE */ +#define MSG_GSOP "GSOP" /* GSOP */ +#define MSG_ISOPER "ISOPER" /* ISOPER */ +#define MSG_ADG "ADG" /* ADG */ +#define MSG_NMON "NMON" /* NMON */ +#define MSG_DALINFO "DALINFO" /* DALnet Credits */ +#define MSG_CREDITS "CREDITS" /* UltimateIRCd Credits and "Thanks To" */ +#define MSG_OPERMOTD "OPERMOTD" /* OPERMOTD */ +#define MSG_REMREHASH "REMREHASH" /* Remote Rehash */ +#define MSG_MONITOR "MONITOR" /* MONITOR */ +#define MSG_GLINE "GLINE" /* The awesome g-line */ +#define MSG_REMGLINE "REMGLINE" /* remove g-line */ +#define MSG_STATSERV "STATSERV" /* StatServ */ +#define MSG_RULESERV "RULESERV" /* RuleServ */ +#define MSG_SNETINFO "SNETINFO" /* SNetInfo */ +#define MSG_TSCTL "TSCTL" /* TSCTL */ +#define MSG_SVSJOIN "SVSJOIN" /* SVSJOIN */ +#define MSG_SAJOIN "SAJOIN" /* SAJOIN */ +#define MSG_SDESC "SDESC" /* SDESC */ +#define MSG_UNREALINFO "UNREALINFO" /* Unreal Info */ +#define MSG_SETHOST "SETHOST" /* sethost */ +#define MSG_SETIDENT "SETIDENT" /* set ident */ +#define MSG_SETNAME "SETNAME" /* set Realname */ +#define MSG_CHGHOST "CHGHOST" /* Changehost */ +#define MSG_CHGIDENT "CHGIDENT" /* Change Ident */ +#define MSG_RANDQUOTE "RANDQUOTE" /* Random Quote */ +#define MSG_ADDQUOTE "ADDQUOTE" /* Add Quote */ +#define MSG_ADDGQUOTE "ADDGQUOTE" /* Add Global Quote */ +#define MSG_ADDULINE "ADDULINE" /* Adds an U Line to ircd.conf file */ +#define MSG_DELULINE "DELULINE" /* Removes an U line from the ircd.conf */ +#define MSG_KNOCK "KNOCK" /* Knock Knock - Who's there? */ +#define MSG_SETTINGS "SETTINGS" /* Settings */ +#define MSG_IRCOPS "IRCOPS" /* Shows Online IRCOps */ +#define MSG_SVSPART "SVSPART" /* SVSPART */ +#define MSG_SAPART "SAPART" /* SAPART */ +#define MSG_VCTRL "VCTRL" /* VCTRL */ +#define MSG_GCLIENT "GCLIENT" /* GLIENT */ +#define MSG_CHANNEL "CHANNEL" /* CHANNEL */ +#define MSG_UPTIME "UPTIME" /* UPTIME */ +#define MSG_FAILOPS "FAILOPS" /* FAILOPS */ +#define MSG_RPING "RPING" /* RPING */ +#define MSG_RPONG "RPONG" /* RPONG */ +#define MSG_UPING "UPING" /* UPING */ +#define MSG_COPYRIGHT "COPYRIGHT" /* Copyright */ +#define MSG_BOTSERV "BOTSERV" /* BOTSERV */ +#define MSG_ROOTSERV "ROOTSERV" /* ROOTSERV */ +#define MSG_RS "RS" +#define MSG_SVINFO "SVINFO" +#define MSG_CAPAB "CAPAB" +#define MSG_BURST "BURST" +#define MSG_SJOIN "SJOIN" +#define MSG_CLIENT "CLIENT" +#define MSG_SMODE "SMODE" + + + +#define UMODE_INVISIBLE 0x0001 /* makes user invisible */ +#define UMODE_OPER 0x0002 /* Operator */ +#define UMODE_LOCOP 0x0004 /* Local Operator */ +#define UMODE_WALLOP 0x0008 /* */ +/* UMODE_SERVICES is actually UMODE_OPER on Bahamut !*/ +#define UMODE_SERVICES UMODE_OPER +#define UMODE_REGONLY 0x0010 /* only registered nicks may PM */ +#define UMODE_REGNICK 0x0020 /* Nick set by services as registered */ +#define UMODE_SERVADMIN 0x0040 /* server admin */ +#define UMODE_SERVICESADMIN 0x0080 /* Marks the client as a Services Administrator */ +#define UMODE_FAILOP 0x0100 /* */ +#define UMODE_HELPOP 0x0200 /* */ +#define UMODE_SERVNOTICE 0x0400 /* */ +#define UMODE_KILLS 0x0800 /* */ +#define UMODE_CLIENT 0x1000 /* */ +#define UMODE_FLOOD 0x2000 /* */ +#define UMODE_NETADMIN 0x4000 /* */ +#define UMODE_TECHADMIN 0x8000 /* */ +#define UMODE_KIX 0x10000 /* */ +#define UMODE_BOT 0x20000 /* */ +#define UMODE_WHOIS 0x40000 /* */ +#define UMODE_HIDE 0x80000 /* */ + +#define SMODE_SSL 0x1 /* */ + +#define MODE_CHANOP 0x0001 +#define MODE_CHANOWNER 0x0002 +#define MODE_VOICE 0x0004 +#define MODE_PRIVATE 0x0008 +#define MODE_SECRET 0x0010 +#define MODE_MODERATED 0x0020 +#define MODE_TOPICLIMIT 0x0040 +#define MODE_INVITEONLY 0x0080 +#define MODE_NOPRIVMSGS 0x0100 +#define MODE_KEY 0x0200 +#define MODE_NONICKCHANGE 0x0400 +#define MODE_BAN 0x0800 +#define MODE_LIMIT 0x1000 +#define MODE_RGSTR 0x2000 +#define MODE_RGSTRONLY 0x4000 +#define MODE_OPERONLY 0x8000 +#define MODE_STRIP 0x10000 +#define MODE_LINK 0x20000 +#define MODE_NOCOLOR 0x40000 +#define MODE_CHANPROT 0x80000 +#define MODE_VIP 0x100000 +#define MODE_UOP 0x200000 +#define MODE_HALFOP 0x400000 + + +#define is_hidden_chan(x) ((x) && (x->modes & (MODE_PRIVATE|MODE_SECRET|MODE_OPERONLY))) +#define is_oper(x) ((x) && ((x->Umode & UMODE_OPER) || (x->Umode & UMODE_LOCOP))) +#undef HAVE_BOT_MODE +#define is_bot(x) ((x) && (x->Umode & UMODE_BOT)) +#define is_pub_chan(x) ((x) && (CheckChanMode(x, MODE_PRIVATE) || CheckChanMode(x, MODE_SECRET) || CheckChanMode(x, MODE_RGSTRONLY) || CheckChanMode(x, MODE_OPERONLY) || CheckChanMode(x, MODE_INVITEONLY) || CheckChanMode(x, MODE_KEY))) + + +struct ircd_srv_ { + int uprot; + int modex; + int nicklg; + int gc; + char cloak[25]; + int burst; + int cmdcount; +} ircd_srv; + +typedef struct { + long mode; + char flag; + unsigned nickparam:1; /* 1 = yes 0 = no */ + unsigned parameters:1; + char sjoin; +} aCtab; + + + + + +typedef struct { + unsigned long umodes; + char mode; + int level; +} Oper_Modes; + + +aCtab cFlagTab[33]; +Oper_Modes usr_mds[22]; +Oper_Modes susr_mds[2]; + + + +/* function declarations */ +extern void init_ircd (); +extern void chanalert (char *, char *, ...); +extern int sserver_cmd (const char *, const int numeric, const char *); +extern int slogin_cmd (const char *, const int numeric, const char *, const char *); +extern int ssquit_cmd (const char *); +extern int sprotocol_cmd (const char *); +extern int squit_cmd (const char *, const char *); +extern int spart_cmd (const char *, const char *); +extern int sjoin_cmd (const char *, const char *, unsigned long flag); +extern int schmode_cmd (const char *, const char *, const char *, const char *); +extern int snewnick_cmd (const char *, const char *, const char *, const char *, long); +extern int sping_cmd (const char *from, const char *reply, const char *to); +extern int sumode_cmd (const char *who, const char *target, long mode); +extern int snumeric_cmd (const int numeric, const char *target, const char *data, ...); +extern int spong_cmd (const char *reply); +extern int snetinfo_cmd (); +extern int skill_cmd (const char *from, const char *target, const char *reason, ...); +extern int ssmo_cmd (const char *from, const char *umodetarget, const char *msg); +extern int snick_cmd (const char *oldnick, const char *newnick); +extern int sswhois_cmd (const char *target, const char *swhois); +extern int ssvsnick_cmd (const char *target, const char *newnick); +extern int ssvsjoin_cmd (const char *target, const char *chan); +extern int ssvspart_cmd (const char *target, const char *chan); +extern int ssvshost_cmd (const char *who, const char *vhost); +extern int skick_cmd (const char *who, const char *target, const char *chan, const char *reason); +extern int swallops_cmd (const char *who, const char *msg, ...); +extern int vctrl_cmd (); +extern int ssvinfo_cmd (); +extern int sburst_cmd (int b); +extern int sakill_cmd (const char *host, const char *ident, const char *setby, const int length, const char *reason, ...); +extern int srakill_cmd (const char *host, const char *ident); +extern int ssvshost_cmd (const char *who, const char *vhost); +extern int ssvskill_cmd (const char *who, const char *reason, ...); +extern int sinvite_cmd (const char *from, const char *to, const char *chan); + +void Usr_Version (char *, char **, int argc); +void Usr_ShowMOTD (char *, char **, int argc); +void Usr_ShowADMIN (char *, char **, int argc); +void Usr_Showcredits (char *, char **, int argc); +void Usr_AddServer (char *, char **, int argc); +void Usr_DelServer (char *, char **, int argc); +void Usr_DelUser (char *, char **, int argc); +void Usr_Mode (char *, char **, int argc); +void Usr_Smode (char *, char **, int argc); +void Usr_Kill (char *, char **, int argc); +void Usr_Pong (char *, char **, int argc); +void Usr_Away (char *, char **, int argc); +void Usr_Nick (char *, char **, int argc); +void Usr_Topic (char *, char **, int argc); +void Usr_Kick (char *, char **, int argc); +void Usr_Join (char *, char **, int argc); +void Usr_Part (char *, char **, int argc); +void Usr_Stats (char *, char **, int argc); +void Usr_Vhost (char *, char **, int argc); +void Srv_Topic (char *, char **, int argc); +void Srv_Ping (char *, char **, int argc); +void Srv_Netinfo (char *, char **, int argc); +void Srv_Pass (char *, char **, int argc); +void Srv_Server (char *, char **, int argc); +void Srv_Squit (char *, char **, int argc); +void Srv_Nick (char *, char **, int argc); +void Srv_Svsnick (char *, char **, int argc); +void Srv_Kill (char *, char **, int argc); +void Srv_Connect (char *, char **, int argc); +void Srv_Svinfo (char *, char **, int argc); +void Srv_Burst (char *origin, char **argv, int argc); +void Srv_Sjoin (char *origin, char **argv, int argc); +void Srv_Tburst (char *origin, char **argv, int argc); +void Srv_Vctrl (char *origin, char **argv, int argc); +void Srv_Client (char *origin, char **argv, int argc); +void Srv_Smode (char *origin, char **argv, int argc); +int SignOn_NewBot (const char *, const char *, const char *, const char *, long); + + +#endif diff --git a/main.c b/main.c index b10705f5..1573172e 100644 --- a/main.c +++ b/main.c @@ -36,6 +36,11 @@ #include "conf.h" #include "keeper.h" #include "log.h" +#include "sock.h" +#include "users.h" +#include "server.h" +#include "chans.h" +#include "dns.h" /* this is the name of the services bot */ char s_Services[MAXNICK] = "NeoStats"; @@ -51,6 +56,7 @@ static int get_options (int argc, char **argv); /*! have we forked */ int forked = 0; static int attempts = 0; +jmp_buf sigvbuf; /** @brief Main Entry point into program * @@ -93,7 +99,7 @@ main (int argc, char *argv[]) printf ("Copyright: NeoStats Group. 2000-2003\n"); printf ("Justin Hammond (fish@neostats.net)\n"); printf ("Adam Rutter (shmad@neostats.net)\n"); - printf ("Mark (m@neostats.net\n"); + printf ("Mark (m@neostats.net)\n"); printf ("-----------------------------------------------\n\n"); } /* set some defaults before we parse the config file */ @@ -119,12 +125,16 @@ main (int argc, char *argv[]) remove (RECV_LOG); /* initilze our Module subsystem */ - if(__init_mod_list () != NS_SUCCESS) + if(InitModuleHash () != NS_SUCCESS) return EXIT_FAILURE; /* prepare to catch errors */ setup_signals (); + /* this has to be done before we load modules */ + if(init_services () != NS_SUCCESS) + return EXIT_FAILURE; + /* load the config files */ if(ConfLoad () != NS_SUCCESS) return EXIT_FAILURE; @@ -285,6 +295,8 @@ get_options (int argc, char **argv) * * @todo Do a nice shutdown, no thtis crap :) */ +char msg_sigterm[]="SIGTERM received, shutting down server."; + RETSIGTYPE serv_die () { @@ -292,9 +304,11 @@ serv_die () exit(NS_SUCCESS); #else /* VALGRIND */ User *u; + u = finduser (s_Services); - nlog (LOG_CRITICAL, LOG_CORE, "SIGTERM received, shutting down server."); - ns_shutdown (u, "SIGTERM received"); + nlog (LOG_CRITICAL, LOG_CORE, msg_sigterm); + globops (s_Services, msg_sigterm); + do_exit (NS_EXIT_NORMAL, msg_sigterm); #endif /* VALGRIND */ } diff --git a/makeconf b/makeconf index 61e7a42a..4c8a5304 100755 --- a/makeconf +++ b/makeconf @@ -465,9 +465,6 @@ RECONNECT_TIME $NEOCONN # # Multiple instances of LOAD_MODULE may be specified -# this loads the Spam Module -#LOAD_MODULE spam - # this is the StatServ Module #LOAD_MODULE statserv diff --git a/misc.c b/misc.c index 24846742..c4ba0da0 100644 --- a/misc.c +++ b/misc.c @@ -130,28 +130,6 @@ HASH (const unsigned char *name, int size_of_table) return h % size_of_table; } -/** @brief convert a string to lowercase - * - * makes a string lowercase (DO NOT USE - * - * @param s the string to convert to lowercase. WARNING: the result overwrites this variable - * - * @returns pointer to the lowercase version of s - * - */ - -char * -strlower (char *s) -{ - char *t; - t = malloc (strlen (s)); - strlcpy (t, s, strlen (s)); - while (*t) { - *t++ = tolower (*t); - } - return t; -} - /** @brief convert a string to lowercase * * makes a string lowercase @@ -196,25 +174,6 @@ AddStringToList (char ***List, char S[], int *C) (*List)[*C - 1] = S; } -/** @brief Frees a list created with AddStringToList - * - * Frees the memory used for a string array used with AddStringToList - * - * @param List the array you wish to delete - * @param C the current size of the array as returned by AddStringToList - * - * @returns Nothing - * - */ -void -FreeList (char **List, int C) -{ - int i; - for (i = 0; i == C; i++) - free (List[i]); - C = 0; -} - /** @brief * * @param diff --git a/mystic.c b/mystic.c index 6ae694a6..cb1627c9 100644 --- a/mystic.c +++ b/mystic.c @@ -29,10 +29,14 @@ #include "mystic.h" #include "dl.h" #include "log.h" +#include "users.h" +#include "server.h" +#include "chans.h" static char ircd_buf[BUFSIZE]; const char ircd_version[] = "(M)"; +const char services_bot_modes[]= "+oS"; IntCommands cmd_list[] = { /* Command Function srvmsg */ @@ -223,9 +227,9 @@ Oper_Modes usr_mds[] = { , {UMODE_KILLS, 'k', 0} , - {UMODE_SERVICES, 'S', 200} + {UMODE_SERVICES, 'S', NS_ULEVEL_ROOT} , - {UMODE_SERVICESADMIN, 'P', 200} + {UMODE_SERVICESADMIN, 'P', NS_ULEVEL_ROOT} , {UMODE_RBOT, 'B', 0} , @@ -233,7 +237,7 @@ Oper_Modes usr_mds[] = { , {UMODE_COADMIN, 'z', 70} , - {UMODE_NETADMIN, 'N', 185} + {UMODE_NETADMIN, 'N', NS_ULEVEL_ADMIN} , {UMODE_TECHADMIN, 'T', 190} , @@ -503,6 +507,12 @@ ssvshost_cmd (const char *who, const char *vhost) sts (":%s CHGHOST %s %s", me.name, who, vhost); return 1; } +int +sinvite_cmd (const char *from, const char *to, const char *chan) { + sts (":%s INVITE %s %s", from, to, chan); + return 1; +} + int sakill_cmd (const char *host, const char *ident, const char *setby, const int length, const char *reason, ...) { @@ -866,7 +876,7 @@ Usr_Away (char *origin, char **argv, int argc) } else { Buf = NULL; } - Do_Away (u, Buf); + UserAway (u, Buf); if (argc > 0) { free (Buf); } @@ -890,7 +900,7 @@ Usr_Topic (char *origin, char **argv, int argc) c = findchan (argv[0]); if (c) { buf = joinbuf (argv, argc, 3); - Change_Topic (argv[1], c, atoi (argv[2]), buf); + ChangeTopic (argv[1], c, atoi (argv[2]), buf); free (buf); } else { nlog (LOG_WARNING, LOG_CORE, "Ehhh, Can't find Channel %s", argv[0]); @@ -963,9 +973,9 @@ Srv_Netinfo (char *origin, char **argv, int argc) init_ServBot (); globops (me.name, "Link with Network \2Complete!\2"); #ifdef DEBUG - ns_set_debug (me.chan); + me.debug_mode = 1; #endif - Module_Event (EVENT_NETINFO, NULL, 0); + ModuleEvent (EVENT_NETINFO, NULL, 0); me.synced = 1; } diff --git a/mystic.h b/mystic.h index d67952dc..37a10e2d 100644 --- a/mystic.h +++ b/mystic.h @@ -31,6 +31,22 @@ /* we have vhost support */ #define GOTSVSVHOST +/* we dont have svsjoin */ +#define GOTSVSJOIN + + +/* Moved from connectserv so we can use elsewhere */ +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define COSERVERADMIN_MODE 'J' +#define SERVERADMIN_MODE 'A' +#define CONETADMIN_MODE 't' +#define NETADMIN_MODE 'N' +#define TECHADMIN_MODE 'T' +#define SERVICESADMIN_MODE 'P' +#define NETSERVICE_MODE 'S' +#define BOT_MODE 'B' + #define MSG_PRIVATE "PRIVMSG" /* PRIV */ #define TOK_PRIVATE "!" /* 33 */ #define MSG_WHO "WHO" /* WHO -> WHOC */ @@ -453,6 +469,7 @@ extern int sburst_cmd (int b); extern int sakill_cmd (const char *host, const char *ident, const char *setby, const int length, const char *reason, ...); extern int srakill_cmd (const char *host, const char *ident); extern int ssvshost_cmd (const char *who, const char *vhost); +extern int sinvite_cmd (const char *from, const char *to, const char *chan); void Usr_Version (char *, char **, int argc); void Usr_ShowMOTD (char *, char **, int argc); diff --git a/neoircd.c b/neoircd.c index ef47f98a..582e3e6d 100644 --- a/neoircd.c +++ b/neoircd.c @@ -27,10 +27,14 @@ #include "neoircd.h" #include "dl.h" #include "log.h" +#include "users.h" +#include "server.h" +#include "chans.h" static char ircd_buf[BUFSIZE]; const char ircd_version[] = "(N)"; +const char services_bot_modes[]= "+oS"; /* this is the command list and associated functions to run */ IntCommands cmd_list[] = { @@ -146,7 +150,7 @@ Oper_Modes usr_mds[] = { , {UMODE_CCONN, 'c', 0} , - {UMODE_DEBUG, 'd', 200} + {UMODE_DEBUG, 'd', NS_ULEVEL_ROOT} , {UMODE_FULL, 'f', 0} , @@ -174,7 +178,7 @@ Oper_Modes usr_mds[] = { , {UMODE_OPERWALL, 'z', 0} , - {UMODE_SERVICES, 'S', 200} + {UMODE_SERVICES, 'S', NS_ULEVEL_ROOT} , {0, 0, 0} }; @@ -438,6 +442,12 @@ ssvshost_cmd (const char *who, const char *vhost) return 0; } +int +sinvite_cmd (const char *from, const char *to, const char *chan) { + sts (":%s INVITE %s %s", from, to, chan); + return 1; +} + int ssvinfo_cmd () { @@ -814,7 +824,7 @@ Usr_Away (char *origin, char **argv, int argc) } else { buf = NULL; } - Do_Away (u, buf); + UserAway (u, buf); if (argc > 0) { free (buf); } @@ -838,7 +848,7 @@ Usr_Topic (char *origin, char **argv, int argc) c = findchan (argv[0]); if (c) { buf = joinbuf (argv, argc, 2); - Change_Topic (argv[1], c, atoi (argv[2]), buf); + ChangeTopic (argv[1], c, atoi (argv[2]), buf); free (buf); } else { nlog (LOG_WARNING, LOG_CORE, "Ehhh, Can't find Channel %s", argv[0]); @@ -898,7 +908,7 @@ Srv_Netinfo (char *origin, char **argv, int argc) strlcpy (me.netname, argv[7], MAXPASS); init_ServBot (); globops (me.name, "Link with Network \2Complete!\2"); - Module_Event (EVENT_NETINFO, NULL, 0); + ModuleEvent (EVENT_NETINFO, NULL, 0); me.synced = 1; } @@ -979,7 +989,7 @@ Srv_Tburst (char *origin, char **argv, int argc) c = findchan (argv[1]); if (c) { buf = joinbuf (argv, argc, 4); - Change_Topic (argv[3], c, atoi (argv[2]), buf); + ChangeTopic (argv[3], c, atoi (argv[2]), buf); free (buf); } else { nlog (LOG_WARNING, LOG_CORE, "TopicBurst: Ehhh, Can't find Channel %s", argv[1]); diff --git a/neoircd.h b/neoircd.h index 15df67f7..27e76032 100644 --- a/neoircd.h +++ b/neoircd.h @@ -22,14 +22,18 @@ */ -#ifndef HYBRID7_H -#define HYBRID7_H +#ifndef NEOIRCD_H +#define NEOIRCD_H /* we have vhost support */ #define GOTSVSVHOST +#define GOTSVSJOIN - +/* Moved from connectserv so we can use elsewhere */ +#define LOCOP_MODE 'O' +#define OPER_MODE 'o' +#define SERVERADMIN_MODE 'a' #define MSG_EOB "EOB" /* end of burst */ #define MSG_PRIVATE "PRIVMSG" /* PRIV */ @@ -300,6 +304,7 @@ extern int seob_cmd (const char *server); extern int sakill_cmd (const char *host, const char *ident, const char *setby, const int length, const char *reason, ...); extern int srakill_cmd (const char *host, const char *ident); extern int SignOn_NewBot (const char *, const char *, const char *, const char *, long); +extern int sinvite_cmd (const char *from, const char *to, const char *chan); void Usr_Version (char *, char **, int argc); diff --git a/ns_help.c b/ns_help.c index 5dd1fd02..6e2e675a 100644 --- a/ns_help.c +++ b/ns_help.c @@ -36,9 +36,9 @@ const char *ns_help[] = { NULL }; -const char *ns_myuser_help[] = { +const char *ns_sa_help[] = { "", - "Additional commands for Service Roots", + "Additional commands for Service Admins", "", " SHUTDOWN Shutdown NeoStats", " RELOAD Force NeoStats to reload", @@ -49,24 +49,32 @@ const char *ns_myuser_help[] = { #ifdef USE_RAW " RAW Send a raw command from this Server", #endif + " JUPE Jupiter a Server", + NULL +}; + +const char *ns_sr_help[] = { + "", + "Additional commands for Service Roots", + "", " JUPE Jupiter a Server", " DEBUG Toggles debug mode", " USERDUMP Debug user table", " CHANDUMP Debug channel table", " SERVERDUMP Debug server table", - " MODBOTLIST List of current module bots", - " MODSOCKLIST List of current module sockets", - " MODTIMERLIST List of current module timers", - " MODBOTCHANLIST List of current module bot channels", + " BOTLIST List current module bots", + " SOCKLIST List current module sockets", + " TIMERLIST List current module timers", + " BOTCHANLIST List current module bot channels", NULL }; const char *ns_help_on_help[] = { "", - "To use a command, type", - " \2/msg NeoStats command\2", - "For for more information on a command, type", - " \2/msg NeoStats HELP command\2.", + "To use a command, type", + " \2/msg NeoStats command\2", + "For for more information on a command, type", + " \2/msg NeoStats HELP command\2.", NULL }; @@ -127,7 +135,7 @@ const char *ns_modlist_help[] = { }; const char *ns_debug_help[] = { - "Syntax: \2DEBUG\2", + "Syntax: \2DEBUG \2", "", "Toggles debug mode. When enabled, debugging information is", "sent to the services channel.", @@ -185,6 +193,7 @@ const char *ns_serverdump_help[] = { }; const char *ns_chandump_help[] = { + "Syntax: \2CHANDUMP\2", "Syntax: \2CHANDUMP \2", "", "When in debug mode, Neostats will echo its channel table to", @@ -201,32 +210,32 @@ const char *ns_logs_help[] = { NULL }; -const char *ns_modbotlist_help[] = { - "Syntax: \2MODBOTLIST\2", +const char *ns_botlist_help[] = { + "Syntax: \2BOTLIST\2", "", "NeoStats will send you by notice a list of the current bots", "being used on the network for each module.", NULL }; -const char *ns_modsocklist_help[] = { - "Syntax: \2MODSOCKLIST\2", +const char *ns_socklist_help[] = { + "Syntax: \2SOCKLIST\2", "", "NeoStats will send you by notice a list of the current", "sockets being used on the network for each module.", NULL }; -const char *ns_modtimerlist_help[] = { - "Syntax: \2MODTIMERLIST\2", +const char *ns_timerlist_help[] = { + "Syntax: \2TIMERLIST\2", "", "NeoStats will send you by notice a list of the current", "timer functions being used on the network by each module.", NULL }; -const char *ns_modbotchanlist_help[] = { - "Syntax: \2MODBOTCHANLIST\2", +const char *ns_botchanlist_help[] = { + "Syntax: \2BOTCHANLIST\2", "", "NeoStats will send you by notice a list of the current bots", "and the channels they are using for each module.", diff --git a/ns_help.h b/ns_help.h new file mode 100644 index 00000000..84c7835b --- /dev/null +++ b/ns_help.h @@ -0,0 +1,54 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond, Mark Hetherington +** http://www.neostats.net/ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ + +#ifndef _NS_HELP_H_ +#define _NS_HELP_H_ + +/* ns_help.c */ +extern const char *ns_help[]; +extern const char *ns_help_on_help[]; +extern const char *ns_sa_help[]; +extern const char *ns_sr_help[]; +extern const char *ns_shutdown_help[]; +extern const char *ns_reload_help[]; +extern const char *ns_logs_help[]; +#ifdef USE_RAW +extern const char *ns_raw_help[]; +#endif +extern const char *ns_debug_help[]; +extern const char *ns_userdump_help[]; +extern const char *ns_chandump_help[]; +extern const char *ns_serverdump_help[]; +extern const char *ns_version_help[]; +extern const char *ns_load_help[]; +extern const char *ns_unload_help[]; +extern const char *ns_modlist_help[]; +extern const char *ns_jupe_help[]; +extern const char *ns_level_help[]; +extern const char *ns_botlist_help[]; +extern const char *ns_socklist_help[]; +extern const char *ns_timerlist_help[]; +extern const char *ns_botchanlist_help[]; +extern const char *ns_info_help[]; + +#endif /* _NS_HELP_H_ */ diff --git a/numeric.h b/numeric.h new file mode 100755 index 00000000..cb8e958a --- /dev/null +++ b/numeric.h @@ -0,0 +1,56 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond +** http://www.neostats.net/ +** +** Portions Copyright (c) 2000-2001 ^Enigma^ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ + +#ifndef _NUMERIC_H_ +#define _NUMERIC_H_ + +/* Numeric used by NeoStats. Need to be in ircd code eventually + * + */ + +#define RPL_STATSLINKINFO 211 +#define RPL_STATSCOMMANDS 212 +#define RPL_STATSCLINE 213 +#define RPL_STATSOLDNLINE 214 +#define RPL_STATSNLINE RPL_STATSOLDNLINE + +#define RPL_ENDOFSTATS 219 + +#define RPL_STATSLLINE 241 +#define RPL_STATSUPTIME 242 +#define RPL_STATSOLINE 243 + +#define RPL_ADMINME 256 +#define RPL_ADMINLOC1 257 +#define RPL_ADMINLOC2 258 + +#define RPL_VERSION 351 + +#define RPL_MOTD 372 + +#define RPL_MOTDSTART 375 +#define RPL_ENDOFMOTD 376 + +#endif /* _NUMERIC_H_ */ diff --git a/pcre/COPYING b/pcre/COPYING new file mode 100644 index 00000000..8d680612 --- /dev/null +++ b/pcre/COPYING @@ -0,0 +1,54 @@ +PCRE LICENCE +------------ + +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Written by: Philip Hazel + +University of Cambridge Computing Service, +Cambridge, England. Phone: +44 1223 334714. + +Copyright (c) 1997-2001 University of Cambridge + +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software 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. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. In practice, this means that if you use + PCRE in software that you distribute to others, commercially or + otherwise, you must put a sentence like this + + Regular expression support is provided by the PCRE library package, + which is open source software, written by Philip Hazel, and copyright + by the University of Cambridge, England. + + somewhere reasonably visible in your documentation and in any relevant + files or online help data or similar. A reference to the ftp site for + the source, that is, to + + ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ + + should also be given in the documentation. However, this condition is not + intended to apply to whole chains of software. If package A includes PCRE, + it must acknowledge it, but if package B is software that includes package + A, the condition is not imposed on package B (unless it uses PCRE + independently). + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), or Lesser General Purpose Licence (LGPL), + then the terms of that licence shall supersede any condition above with + which it is incompatible. + +The documentation for PCRE, supplied in the "doc" directory, is distributed +under the same terms as the software itself. + +End diff --git a/pcre/Makefile.in b/pcre/Makefile.in new file mode 100644 index 00000000..ccc25c5f --- /dev/null +++ b/pcre/Makefile.in @@ -0,0 +1,37 @@ +#PCRE Makefile +CC=@CC@ +CFLAGS=-I.. @CFLAGS@ +LDFLAGS= @LDFLAGS@ +INCLUDES=-I.. -I. + +SOURCES= get.c pcre.c study.c +OBJECTS= get.o pcre.o study.o +DOCS=README.pcre COPYING + +all: ${OBJECTS} + +.c.o: + $(CC) -c $(CFLAGS) $(INCLUDES) $< + +dftables.o: dftables.c maketables.c internal.h ../pcre.h config.h Makefile + $(CC) -c $(CFLAGS) $(INCLUDES) dftables.c + +dftables: dftables.o + $(CC) $(LDFLAGS) dftables.o -o dftables + +chartables.c: dftables + ./dftables >./chartables.c + +clean: + /bin/rm -rf *.o Makefile dftables ../pcre.h chartables.c config.h + +install: + +dist: + +$(OBJECTS): Makefile +pcre.o: chartables.c ../pcre.h internal.h ../config.h +get.o: ../pcre.h internal.h ../config.h +study.o: ../pcre.h internal.h ../config.h +dftables.o: ../pcre.h internal.h ../config.h + diff --git a/pcre/README b/pcre/README new file mode 100644 index 00000000..4efa4008 --- /dev/null +++ b/pcre/README @@ -0,0 +1,364 @@ +NOTE: +This is a stripped down version of libpcre for NeoStats. Only the files +required for NeoStats are included in this distribution. IT IS NOT A FULL +COPY OF libpcre. You can obtain full copies of libpcre from www.pcre.org + +Original Readme Follows: + +README file for PCRE (Perl-compatible regular expression library) +----------------------------------------------------------------- + +The latest release of PCRE is always available from + + ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-xxx.tar.gz + +Please read the NEWS file if you are upgrading from a previous release. + +PCRE has its own native API, but a set of "wrapper" functions that are based on +the POSIX API are also supplied in the library libpcreposix. Note that this +just provides a POSIX calling interface to PCRE: the regular expressions +themselves still follow Perl syntax and semantics. The header file +for the POSIX-style functions is called pcreposix.h. The official POSIX name is +regex.h, but I didn't want to risk possible problems with existing files of +that name by distributing it that way. To use it with an existing program that +uses the POSIX API, it will have to be renamed or pointed at by a link. + + +Contributions by users of PCRE +------------------------------ + +You can find contributions from PCRE users in the directory + + ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/Contrib + +where there is also a README file giving brief descriptions of what they are. +Several of them provide support for compiling PCRE on various flavours of +Windows systems (I myself do not use Windows). Some are complete in themselves; +others are pointers to URLs containing relevant files. + + +Building PCRE on a Unix-like system +----------------------------------- + +To build PCRE on a Unix-like system, first run the "configure" command from the +PCRE distribution directory, with your current directory set to the directory +where you want the files to be created. This command is a standard GNU +"autoconf" configuration script, for which generic instructions are supplied in +INSTALL. + +Most commonly, people build PCRE within its own distribution directory, and in +this case, on many systems, just running "./configure" is sufficient, but the +usual methods of changing standard defaults are available. For example, + +CFLAGS='-O2 -Wall' ./configure --prefix=/opt/local + +specifies that the C compiler should be run with the flags '-O2 -Wall' instead +of the default, and that "make install" should install PCRE under /opt/local +instead of the default /usr/local. + +If you want to build in a different directory, just run "configure" with that +directory as current. For example, suppose you have unpacked the PCRE source +into /source/pcre/pcre-xxx, but you want to build it in /build/pcre/pcre-xxx: + +cd /build/pcre/pcre-xxx +/source/pcre/pcre-xxx/configure + +There are some optional features that can be included or omitted from the PCRE +library. You can read more about them in the pcrebuild man page. + +. If you want to make use of the support for UTF-8 character strings in PCRE, + you must add --enable-utf8 to the "configure" command. Without it, the code + for handling UTF-8 is not included in the library. (Even when included, it + still has to be enabled by an option at run time.) + +. You can build PCRE to recognized CR or NL as the newline character, instead + of whatever your compiler uses for "\n", by adding --newline-is-cr or + --newline-is-nl to the "configure" command, respectively. Only do this if you + really understand what you are doing. On traditional Unix-like systems, the + newline character is NL. + +. When called via the POSIX interface, PCRE uses malloc() to get additional + storage for processing capturing parentheses if there are more than 10 of + them. You can increase this threshold by setting, for example, + + --with-posix-malloc-threshold=20 + + on the "configure" command. + +. PCRE has a counter which can be set to limit the amount of resources it uses. + If the limit is exceeded during a match, the match fails. The default is ten + million. You can change the default by setting, for example, + + --with-match-limit=500000 + + on the "configure" command. This is just the default; individual calls to + pcre_exec() can supply their own value. There is discussion on the pcreapi + man page. + +. The default maximum compiled pattern size is around 64K. You can increase + this by adding --with-link-size=3 to the "configure" command. You can + increase it even more by setting --with-link-size=4, but this is unlikely + ever to be necessary. If you build PCRE with an increased link size, test 2 + (and 5 if you are using UTF-8) will fail. Part of the output of these tests + is a representation of the compiled pattern, and this changes with the link + size. + +The "configure" script builds five files: + +. libtool is a script that builds shared and/or static libraries +. Makefile is built by copying Makefile.in and making substitutions. +. config.h is built by copying config.in and making substitutions. +. pcre-config is built by copying pcre-config.in and making substitutions. +. RunTest is a script for running tests + +Once "configure" has run, you can run "make". It builds two libraries called +libpcre and libpcreposix, a test program called pcretest, and the pcregrep +command. You can use "make install" to copy these, the public header files +pcre.h and pcreposix.h, and the man pages to appropriate live directories on +your system, in the normal way. + +Running "make install" also installs the command pcre-config, which can be used +to recall information about the PCRE configuration and installation. For +example, + + pcre-config --version + +prints the version number, and + + pcre-config --libs + +outputs information about where the library is installed. This command can be +included in makefiles for programs that use PCRE, saving the programmer from +having to remember too many details. + + +Cross-compiling PCRE on a Unix-like system +------------------------------------------ + +PCRE needs to compile and run an auxiliary program as part of the building +process. Obviously, if the real compilation is for some other system, it can't +use the same CC and CFLAGS values when it is doing this. For cross compilation, +therefore, you must set CC_FOR_BUILD to the local host's compiler, and you can +set flags in CFLAGS_FOR_BUILD if you need to. + + +Shared libraries on Unix-like systems +------------------------------------- + +The default distribution builds PCRE as two shared libraries and two static +libraries, as long as the operating system supports shared libraries. Shared +library support relies on the "libtool" script which is built as part of the +"configure" process. + +The libtool script is used to compile and link both shared and static +libraries. They are placed in a subdirectory called .libs when they are newly +built. The programs pcretest and pcregrep are built to use these uninstalled +libraries (by means of wrapper scripts in the case of shared libraries). When +you use "make install" to install shared libraries, pcregrep and pcretest are +automatically re-built to use the newly installed shared libraries before being +installed themselves. However, the versions left in the source directory still +use the uninstalled libraries. + +To build PCRE using static libraries only you must use --disable-shared when +configuring it. For example + +./configure --prefix=/usr/gnu --disable-shared + +Then run "make" in the usual way. Similarly, you can use --disable-static to +build only shared libraries. + + +Cross-compiling on a Unix-like system +------------------------------------- + +You can specify CC and CFLAGS in the normal way to the "configure" command, in +order to cross-compile PCRE for some other host. However, during the building +process, the dftables.c source file is compiled *and run* on the local host, in +order to generate the default character tables (the chartables.c file). It +therefore needs to be compiled with the local compiler, not the cross compiler. +You can do this by specifying HOST_CC (and if necessary HOST_CFLAGS) when +calling the "configure" command. If they are not specified, they default to the +values of CC and CFLAGS. + + +Building on non-Unix systems +---------------------------- + +For a non-Unix system, read the comments in the file NON-UNIX-USE. PCRE has +been compiled on Windows systems and on Macintoshes, but I don't know the +details because I don't use those systems. It should be straightforward to +build PCRE on any system that has a Standard C compiler, because it uses only +Standard C functions. + + +Testing PCRE +------------ + +To test PCRE on a Unix system, run the RunTest script that is created by the +configuring process. (This can also be run by "make runtest", "make check", or +"make test".) For other systems, see the instruction in NON-UNIX-USE. + +The script runs the pcretest test program (which is documented in its own man +page) on each of the testinput files (in the testdata directory) in turn, +and compares the output with the contents of the corresponding testoutput file. +A file called testtry is used to hold the output from pcretest. To run pcretest +on just one of the test files, give its number as an argument to RunTest, for +example: + + RunTest 2 + +The first file can also be fed directly into the perltest script to check that +Perl gives the same results. The only difference you should see is in the first +few lines, where the Perl version is given instead of the PCRE version. + +The second set of tests check pcre_fullinfo(), pcre_info(), pcre_study(), +pcre_copy_substring(), pcre_get_substring(), pcre_get_substring_list(), error +detection, and run-time flags that are specific to PCRE, as well as the POSIX +wrapper API. It also uses the debugging flag to check some of the internals of +pcre_compile(). + +If you build PCRE with a locale setting that is not the standard C locale, the +character tables may be different (see next paragraph). In some cases, this may +cause failures in the second set of tests. For example, in a locale where the +isprint() function yields TRUE for characters in the range 128-255, the use of +[:isascii:] inside a character class defines a different set of characters, and +this shows up in this test as a difference in the compiled code, which is being +listed for checking. Where the comparison test output contains [\x00-\x7f] the +test will contain [\x00-\xff], and similarly in some other cases. This is not a +bug in PCRE. + +The third set of tests checks pcre_maketables(), the facility for building a +set of character tables for a specific locale and using them instead of the +default tables. The tests make use of the "fr" (French) locale. Before running +the test, the script checks for the presence of this locale by running the +"locale" command. If that command fails, or if it doesn't include "fr" in the +list of available locales, the third test cannot be run, and a comment is +output to say why. If running this test produces instances of the error + + ** Failed to set locale "fr" + +in the comparison output, it means that locale is not available on your system, +despite being listed by "locale". This does not mean that PCRE is broken. + +The fourth test checks the UTF-8 support. It is not run automatically unless +PCRE is built with UTF-8 support. To do this you must set --enable-utf8 when +running "configure". This file can be also fed directly to the perltest script, +provided you are running Perl 5.8 or higher. (For Perl 5.6, a small patch, +commented in the script, can be be used.) + +The fifth and final file tests error handling with UTF-8 encoding, and internal +UTF-8 features of PCRE that are not relevant to Perl. + + +Character tables +---------------- + +PCRE uses four tables for manipulating and identifying characters. The final +argument of the pcre_compile() function is a pointer to a block of memory +containing the concatenated tables. A call to pcre_maketables() can be used to +generate a set of tables in the current locale. If the final argument for +pcre_compile() is passed as NULL, a set of default tables that is built into +the binary is used. + +The source file called chartables.c contains the default set of tables. This is +not supplied in the distribution, but is built by the program dftables +(compiled from dftables.c), which uses the ANSI C character handling functions +such as isalnum(), isalpha(), isupper(), islower(), etc. to build the table +sources. This means that the default C locale which is set for your system will +control the contents of these default tables. You can change the default tables +by editing chartables.c and then re-building PCRE. If you do this, you should +probably also edit Makefile to ensure that the file doesn't ever get +re-generated. + +The first two 256-byte tables provide lower casing and case flipping functions, +respectively. The next table consists of three 32-byte bit maps which identify +digits, "word" characters, and white space, respectively. These are used when +building 32-byte bit maps that represent character classes. + +The final 256-byte table has bits indicating various character types, as +follows: + + 1 white space character + 2 letter + 4 decimal digit + 8 hexadecimal digit + 16 alphanumeric or '_' + 128 regular expression metacharacter or binary zero + +You should not alter the set of characters that contain the 128 bit, as that +will cause PCRE to malfunction. + + +Manifest +-------- + +The distribution should contain the following files: + +(A) The actual source files of the PCRE library functions and their + headers: + + dftables.c auxiliary program for building chartables.c + get.c ) + maketables.c ) + study.c ) source of + pcre.c ) the functions + pcreposix.c ) + printint.c ) + pcre.in "source" for the header for the external API; pcre.h + is built from this by "configure" + pcreposix.h header for the external POSIX wrapper API + internal.h header for internal use + config.in template for config.h, which is built by configure + +(B) Auxiliary files: + + AUTHORS information about the author of PCRE + ChangeLog log of changes to the code + INSTALL generic installation instructions + LICENCE conditions for the use of PCRE + COPYING the same, using GNU's standard name + Makefile.in template for Unix Makefile, which is built by configure + NEWS important changes in this release + NON-UNIX-USE notes on building PCRE on non-Unix systems + README this file + RunTest.in template for a Unix shell script for running tests + config.guess ) files used by libtool, + config.sub ) used only when building a shared library + configure a configuring shell script (built by autoconf) + configure.in the autoconf input used to build configure + doc/Tech.Notes notes on the encoding + doc/*.3 man page sources for the PCRE functions + doc/*.1 man page sources for pcregrep and pcretest + doc/html/* HTML documentation + doc/pcre.txt plain text version of the man pages + doc/pcretest.txt plain text documentation of test program + doc/perltest.txt plain text documentation of Perl test program + install-sh a shell script for installing files + ltmain.sh file used to build a libtool script + pcretest.c comprehensive test program + pcredemo.c simple demonstration of coding calls to PCRE + perltest Perl test program + pcregrep.c source of a grep utility that uses PCRE + pcre-config.in source of script which retains PCRE information + testdata/testinput1 test data, compatible with Perl + testdata/testinput2 test data for error messages and non-Perl things + testdata/testinput3 test data for locale-specific tests + testdata/testinput4 test data for UTF-8 tests compatible with Perl + testdata/testinput5 test data for other UTF-8 tests + testdata/testoutput1 test results corresponding to testinput1 + testdata/testoutput2 test results corresponding to testinput2 + testdata/testoutput3 test results corresponding to testinput3 + testdata/testoutput4 test results corresponding to testinput4 + testdata/testoutput5 test results corresponding to testinput5 + +(C) Auxiliary files for Win32 DLL + + dll.mk + pcre.def + +(D) Auxiliary file for VPASCAL + + makevp.bat + +Philip Hazel +February 2003 diff --git a/pcre/config.h.in b/pcre/config.h.in new file mode 100644 index 00000000..3bf89222 --- /dev/null +++ b/pcre/config.h.in @@ -0,0 +1,61 @@ +/* define this to enable debug code for this module */ +#undef DEBUG +/* Define to empty if the keyword does not work. */ + +#undef const + +/* Define to `unsigned' if doesn't define size_t. */ + +#undef size_t + +/* The following two definitions are mainly for the benefit of SunOS4, which +doesn't have the strerror() or memmove() functions that should be present in +all Standard C libraries. The macros HAVE_STRERROR and HAVE_MEMMOVE should +normally be defined with the value 1 for other systems, but unfortunately we +can't make this the default because "configure" files generated by autoconf +will only change 0 to 1; they won't change 1 to 0 if the functions are not +found. */ + +#define HAVE_STRERROR 0 +#define HAVE_MEMMOVE 0 + +/* There are some non-Unix systems that don't even have bcopy(). If this macro +is false, an emulation is used. If HAVE_MEMMOVE is set to 1, the value of +HAVE_BCOPY is not relevant. */ + +#define HAVE_BCOPY 0 + +/* The value of NEWLINE determines the newline character. The default is to +leave it up to the compiler, but some sites want to force a particular value. +On Unix systems, "configure" can be used to override this default. */ + +#ifndef NEWLINE +#define NEWLINE '\n' +#endif + +/* The value of LINK_SIZE determines the number of bytes used to store +links as offsets within the compiled regex. The default is 2, which allows for +compiled patterns up to 64K long. This covers the vast majority of cases. +However, PCRE can also be compiled to use 3 or 4 bytes instead. This allows for +longer patterns in extreme cases. On Unix systems, "configure" can be used to +override this default. */ + +#ifndef LINK_SIZE +#define LINK_SIZE 2 +#endif + +/* The value of MATCH_LIMIT determines the default number of times the match() +function can be called during a single execution of pcre_exec(). (There is a +runtime method of setting a different limit.) The limit exists in order to +catch runaway regular expressions that take for ever to determine that they do +not match. The default is set very large so that it does not accidentally catch +legitimate cases. On Unix systems, "configure" can be used to override this +default default. */ + +#ifndef MATCH_LIMIT +#define MATCH_LIMIT 10000000 +#endif + +#undef POSIX_MALLOC_THRESHOLD + +#undef HAVE_CRYPT_H diff --git a/pcre/dftables.c b/pcre/dftables.c new file mode 100644 index 00000000..9aa7b77e --- /dev/null +++ b/pcre/dftables.c @@ -0,0 +1,152 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Written by: Philip Hazel + + Copyright (c) 1997-2001 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software 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. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- + +See the file Tech.Notes for some information on the internals. +*/ + + +/* This is a support program to generate the file chartables.c, containing +character tables of various kinds. They are built according to the default C +locale and used as the default tables by PCRE. Now that pcre_maketables is +a function visible to the outside world, we make use of its code from here in +order to be consistent. */ + +#include +#include +#include + +#include "internal.h" + +#define DFTABLES /* maketables.c notices this */ +#include "maketables.c" + + +int main(void) +{ +int i; +const unsigned char *tables = pcre_maketables(); + +/* There are two printf() calls here, because gcc in pedantic mode complains +about the very long string otherwise. */ + +printf( + "/*************************************************\n" + "* Perl-Compatible Regular Expressions *\n" + "*************************************************/\n\n" + "/* This file is automatically written by the dftables auxiliary \n" + "program. If you edit it by hand, you might like to edit the Makefile to \n" + "prevent its ever being regenerated.\n\n"); +printf( + "This file is #included in the compilation of pcre.c to build the default\n" + "character tables which are used when no tables are passed to the compile\n" + "function. */\n\n" + "static unsigned char pcre_default_tables[] = {\n\n" + "/* This table is a lower casing table. */\n\n"); + +printf(" "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) printf("\n "); + printf("%3d", *tables++); + if (i != 255) printf(","); + } +printf(",\n\n"); + +printf("/* This table is a case flipping table. */\n\n"); + +printf(" "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) printf("\n "); + printf("%3d", *tables++); + if (i != 255) printf(","); + } +printf(",\n\n"); + +printf( + "/* This table contains bit maps for various character classes.\n" + "Each map is 32 bytes long and the bits run from the least\n" + "significant end of each byte. The classes that have their own\n" + "maps are: space, xdigit, digit, upper, lower, word, graph\n" + "print, punct, and cntrl. Other classes are built from combinations. */\n\n"); + +printf(" "); +for (i = 0; i < cbit_length; i++) + { + if ((i & 7) == 0 && i != 0) + { + if ((i & 31) == 0) printf("\n"); + printf("\n "); + } + printf("0x%02x", *tables++); + if (i != cbit_length - 1) printf(","); + } +printf(",\n\n"); + +printf( + "/* This table identifies various classes of character by individual bits:\n" + " 0x%02x white space character\n" + " 0x%02x letter\n" + " 0x%02x decimal digit\n" + " 0x%02x hexadecimal digit\n" + " 0x%02x alphanumeric or '_'\n" + " 0x%02x regular expression metacharacter or binary zero\n*/\n\n", + ctype_space, ctype_letter, ctype_digit, ctype_xdigit, ctype_word, + ctype_meta); + +printf(" "); +for (i = 0; i < 256; i++) + { + if ((i & 7) == 0 && i != 0) + { + printf(" /* "); + if (isprint(i-8)) printf(" %c -", i-8); + else printf("%3d-", i-8); + if (isprint(i-1)) printf(" %c ", i-1); + else printf("%3d", i-1); + printf(" */\n "); + } + printf("0x%02x", *tables++); + if (i != 255) printf(","); + } + +printf("};/* "); +if (isprint(i-8)) printf(" %c -", i-8); + else printf("%3d-", i-8); +if (isprint(i-1)) printf(" %c ", i-1); + else printf("%3d", i-1); +printf(" */\n\n/* End of chartables.c */\n"); + +return 0; +} + +/* End of dftables.c */ diff --git a/pcre/get.c b/pcre/get.c new file mode 100644 index 00000000..a20473cf --- /dev/null +++ b/pcre/get.c @@ -0,0 +1,349 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-2003 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software 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. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- +*/ + +/* This module contains some convenience functions for extracting substrings +from the subject string after a regex match has succeeded. The original idea +for these functions came from Scott Wimer . */ + + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + +#include "internal.h" + + +/************************************************* +* Find number for named string * +*************************************************/ + +/* This function is used by the two extraction functions below, as well +as being generally available. + +Arguments: + code the compiled regex + stringname the name whose number is required + +Returns: the number of the named parentheses, or a negative number + (PCRE_ERROR_NOSUBSTRING) if not found +*/ + +int +pcre_get_stringnumber(const pcre *code, const char *stringname) +{ +int rc; +int entrysize; +int top, bot; +uschar *nametable; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; + +bot = 0; +while (top > bot) + { + int mid = (top + bot) / 2; + uschar *entry = nametable + entrysize*mid; + int c = strcmp(stringname, (char *)(entry + 2)); + if (c == 0) return (entry[0] << 8) + entry[1]; + if (c > 0) bot = mid + 1; else top = mid; + } + +return PCRE_ERROR_NOSUBSTRING; +} + + + +/************************************************* +* Copy captured string to given buffer * +*************************************************/ + +/* This function copies a single captured substring into a given buffer. +Note that we use memcpy() rather than strncpy() in case there are binary zeros +in the string. + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringnumber the number of the required substring + buffer where to put the substring + size the size of the buffer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) buffer too small + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +int +pcre_copy_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, char *buffer, int size) +{ +int yield; +if (stringnumber < 0 || stringnumber >= stringcount) + return PCRE_ERROR_NOSUBSTRING; +stringnumber *= 2; +yield = ovector[stringnumber+1] - ovector[stringnumber]; +if (size < yield + 1) return PCRE_ERROR_NOMEMORY; +memcpy(buffer, subject + ovector[stringnumber], yield); +buffer[yield] = 0; +return yield; +} + + + +/************************************************* +* Copy named captured string to given buffer * +*************************************************/ + +/* This function copies a single captured substring into a given buffer, +identifying it by name. + +Arguments: + code the compiled regex + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringname the name of the required substring + buffer where to put the substring + size the size of the buffer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) buffer too small + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +int +pcre_copy_named_substring(const pcre *code, const char *subject, int *ovector, + int stringcount, const char *stringname, char *buffer, int size) +{ +int n = pcre_get_stringnumber(code, stringname); +if (n <= 0) return n; +return pcre_copy_substring(subject, ovector, stringcount, n, buffer, size); +} + + + +/************************************************* +* Copy all captured strings to new store * +*************************************************/ + +/* This function gets one chunk of store and builds a list of pointers and all +of the captured substrings in it. A NULL pointer is put on the end of the list. + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + listptr set to point to the list of pointers + +Returns: if successful: 0 + if not successful: + PCRE_ERROR_NOMEMORY (-6) failed to get store +*/ + +int +pcre_get_substring_list(const char *subject, int *ovector, int stringcount, + const char ***listptr) +{ +int i; +int size = sizeof(char *); +int double_count = stringcount * 2; +char **stringlist; +char *p; + +for (i = 0; i < double_count; i += 2) + size += sizeof(char *) + ovector[i+1] - ovector[i] + 1; + +stringlist = (char **)(pcre_malloc)(size); +if (stringlist == NULL) return PCRE_ERROR_NOMEMORY; + +*listptr = (const char **)stringlist; +p = (char *)(stringlist + stringcount + 1); + +for (i = 0; i < double_count; i += 2) + { + int len = ovector[i+1] - ovector[i]; + memcpy(p, subject + ovector[i], len); + *stringlist++ = p; + p += len; + *p++ = 0; + } + +*stringlist = NULL; +return 0; +} + + + +/************************************************* +* Free store obtained by get_substring_list * +*************************************************/ + +/* This function exists for the benefit of people calling PCRE from non-C +programs that can call its functions, but not free() or (pcre_free)() directly. + +Argument: the result of a previous pcre_get_substring_list() +Returns: nothing +*/ + +void +pcre_free_substring_list(const char **pointer) +{ +(pcre_free)((void *)pointer); +} + + + +/************************************************* +* Copy captured string to new store * +*************************************************/ + +/* This function copies a single captured substring into a piece of new +store + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringnumber the number of the required substring + stringptr where to put a pointer to the substring + +Returns: if successful: + the length of the string, not including the zero that + is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) failed to get store + PCRE_ERROR_NOSUBSTRING (-7) substring not present +*/ + +int +pcre_get_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, const char **stringptr) +{ +int yield; +char *substring; +if (stringnumber < 0 || stringnumber >= stringcount) + return PCRE_ERROR_NOSUBSTRING; +stringnumber *= 2; +yield = ovector[stringnumber+1] - ovector[stringnumber]; +substring = (char *)(pcre_malloc)(yield + 1); +if (substring == NULL) return PCRE_ERROR_NOMEMORY; +memcpy(substring, subject + ovector[stringnumber], yield); +substring[yield] = 0; +*stringptr = substring; +return yield; +} + + + +/************************************************* +* Copy named captured string to new store * +*************************************************/ + +/* This function copies a single captured substring, identified by name, into +new store. + +Arguments: + code the compiled regex + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringname the name of the required substring + stringptr where to put the pointer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) couldn't get memory + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +int +pcre_get_named_substring(const pcre *code, const char *subject, int *ovector, + int stringcount, const char *stringname, const char **stringptr) +{ +int n = pcre_get_stringnumber(code, stringname); +if (n <= 0) return n; +return pcre_get_substring(subject, ovector, stringcount, n, stringptr); +} + + + + +/************************************************* +* Free store obtained by get_substring * +*************************************************/ + +/* This function exists for the benefit of people calling PCRE from non-C +programs that can call its functions, but not free() or (pcre_free)() directly. + +Argument: the result of a previous pcre_get_substring() +Returns: nothing +*/ + +void +pcre_free_substring(const char *pointer) +{ +(pcre_free)((void *)pointer); +} + +/* End of get.c */ diff --git a/pcre/internal.h b/pcre/internal.h new file mode 100644 index 00000000..973e7eec --- /dev/null +++ b/pcre/internal.h @@ -0,0 +1,662 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + + +/* This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-2003 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software 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. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- +*/ + +/* This header contains definitions that are shared between the different +modules, but which are not relevant to the outside. */ + +/* Get the definitions provided by running "configure" */ + +#include "config.h" + +/* When compiling for use with the Virtual Pascal compiler, these functions +need to have their names changed. PCRE must be compiled with the -DVPCOMPAT +option on the command line. */ + +#ifdef VPCOMPAT +#define strncmp(s1,s2,m) _strncmp(s1,s2,m) +#define memcpy(d,s,n) _memcpy(d,s,n) +#define memmove(d,s,n) _memmove(d,s,n) +#define memset(s,c,n) _memset(s,c,n) +#else /* VPCOMPAT */ + +/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(), +define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY +is set. Otherwise, include an emulating function for those systems that have +neither (there some non-Unix environments where this is the case). This assumes +that all calls to memmove are moving strings upwards in store, which is the +case in PCRE. */ + +#if ! HAVE_MEMMOVE +#undef memmove /* some systems may have a macro */ +#if HAVE_BCOPY +#define memmove(a, b, c) bcopy(b, a, c) +#else /* HAVE_BCOPY */ +void * +pcre_memmove(unsigned char *dest, const unsigned char *src, size_t n) +{ +int i; +dest += n; +src += n; +for (i = 0; i < n; ++i) *(--dest) = *(--src); +} +#define memmove(a, b, c) pcre_memmove(a, b, c) +#endif /* not HAVE_BCOPY */ +#endif /* not HAVE_MEMMOVE */ +#endif /* not VPCOMPAT */ + + +/* PCRE keeps offsets in its compiled code as 2-byte quantities by default. +These are used, for example, to link from the start of a subpattern to its +alternatives and its end. The use of 2 bytes per offset limits the size of the +compiled regex to around 64K, which is big enough for almost everybody. +However, I received a request for an even bigger limit. For this reason, and +also to make the code easier to maintain, the storing and loading of offsets +from the byte string is now handled by the macros that are defined here. + +The macros are controlled by the value of LINK_SIZE. This defaults to 2 in +the config.h file, but can be overridden by using -D on the command line. This +is automated on Unix systems via the "configure" command. */ + +#if LINK_SIZE == 2 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 8), \ + (a[(n)+1] = (d) & 255) + +#define GET(a,n) \ + (((a)[n] << 8) | (a)[(n)+1]) + +#define MAX_PATTERN_SIZE (1 << 16) + + +#elif LINK_SIZE == 3 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 16), \ + (a[(n)+1] = (d) >> 8), \ + (a[(n)+2] = (d) & 255) + +#define GET(a,n) \ + (((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2]) + +#define MAX_PATTERN_SIZE (1 << 24) + + +#elif LINK_SIZE == 4 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 24), \ + (a[(n)+1] = (d) >> 16), \ + (a[(n)+2] = (d) >> 8), \ + (a[(n)+3] = (d) & 255) + +#define GET(a,n) \ + (((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3]) + +#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */ + + +#else +#error LINK_SIZE must be either 2, 3, or 4 +#endif + + +/* Convenience macro defined in terms of the others */ + +#define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE + + +/* PCRE uses some other 2-byte quantities that do not change when the size of +offsets changes. There are used for repeat counts and for other things such as +capturing parenthesis numbers in back references. */ + +#define PUT2(a,n,d) \ + a[n] = (d) >> 8; \ + a[(n)+1] = (d) & 255 + +#define GET2(a,n) \ + (((a)[n] << 8) | (a)[(n)+1]) + +#define PUT2INC(a,n,d) PUT2(a,n,d), a += 2 + + +/* Standard C headers plus the external interface definition */ + +#include +#include +#include +#include +#include +#include + +#ifndef PCRE_SPY +#define PCRE_DEFINITION /* Win32 __declspec(export) trigger for .dll */ +#endif + +#include "pcre.h" + +/* In case there is no definition of offsetof() provided - though any proper +Standard C system should have one. */ + +#ifndef offsetof +#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) +#endif + +/* These are the public options that can change during matching. */ + +#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) + +/* Private options flags start at the most significant end of the four bytes, +but skip the top bit so we can use ints for convenience without getting tangled +with negative values. The public options defined in pcre.h start at the least +significant end. Make sure they don't overlap, though now that we have expanded +to four bytes there is plenty of space. */ + +#define PCRE_FIRSTSET 0x40000000 /* first_byte is set */ +#define PCRE_REQCHSET 0x20000000 /* req_byte is set */ +#define PCRE_STARTLINE 0x10000000 /* start after \n for multiline */ +#define PCRE_ICHANGED 0x08000000 /* i option changes within regex */ + +/* Options for the "extra" block produced by pcre_study(). */ + +#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */ + +/* Masks for identifying the public options which are permitted at compile +time, run time or study time, respectively. */ + +#define PUBLIC_OPTIONS \ + (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ + PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \ + PCRE_NO_AUTO_CAPTURE) + +#define PUBLIC_EXEC_OPTIONS \ + (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY) + +#define PUBLIC_STUDY_OPTIONS 0 /* None defined */ + +/* Magic number to provide a small check against being handed junk. */ + +#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ + +/* Negative values for the firstchar and reqchar variables */ + +#define REQ_UNSET (-2) +#define REQ_NONE (-1) + +/* Flags added to firstbyte or reqbyte; a "non-literal" item is either a +variable-length repeat, or a anything other than literal characters. */ + +#define REQ_CASELESS 0x0100 /* indicates caselessness */ +#define REQ_VARY 0x0200 /* reqbyte followed non-literal item */ + +/* Miscellaneous definitions */ + +typedef int BOOL; + +#define FALSE 0 +#define TRUE 1 + +/* Escape items that are just an encoding of a particular data value. Note that +ESC_n is defined as yet another macro, which is set in config.h to either \n +(the default) or \r (which some people want). */ + +#ifndef ESC_e +#define ESC_e 27 +#endif + +#ifndef ESC_f +#define ESC_f '\f' +#endif + +#ifndef ESC_n +#define ESC_n NEWLINE +#endif + +#ifndef ESC_r +#define ESC_r '\r' +#endif + +/* We can't officially use ESC_t because it is a POSIX reserved identifier +(presumably because of all the others like size_t). */ + +#ifndef ESC_tee +#define ESC_tee '\t' +#endif + +/* These are escaped items that aren't just an encoding of a particular data +value such as \n. They must have non-zero values, as check_escape() returns +their negation. Also, they must appear in the same order as in the opcode +definitions below, up to ESC_z. There's a dummy for OP_ANY because it +corresponds to "." rather than an escape sequence. The final one must be +ESC_REF as subsequent values are used for \1, \2, \3, etc. There is are two +tests in the code for an escape greater than ESC_b and less than ESC_Z to +detect the types that may be repeated. These are the types that consume a +character. If any new escapes are put in between that don't consume a +character, that code will have to change. */ + +enum { ESC_A = 1, ESC_G, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, + ESC_w, ESC_dum1, ESC_C, ESC_Z, ESC_z, ESC_E, ESC_Q, ESC_REF }; + +/* Flag bits and data types for the extended class (OP_XCLASS) for classes that +contain UTF-8 characters with values greater than 255. */ + +#define XCL_NOT 0x01 /* Flag: this is a negative class */ +#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ + +#define XCL_END 0 /* Marks end of individual items */ +#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */ +#define XCL_RANGE 2 /* A range (two multibyte chars) follows */ + + +/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets +that extract substrings. Starting from 1 (i.e. after OP_END), the values up to +OP_EOD must correspond in order to the list of escapes immediately above. +Note that whenever this list is updated, the two macro definitions that follow +must also be updated to match. */ + +enum { + OP_END, /* 0 End of pattern */ + + /* Values corresponding to backslashed metacharacters */ + + OP_SOD, /* 1 Start of data: \A */ + OP_SOM, /* 2 Start of match (subject + offset): \G */ + OP_NOT_WORD_BOUNDARY, /* 3 \B */ + OP_WORD_BOUNDARY, /* 4 \b */ + OP_NOT_DIGIT, /* 5 \D */ + OP_DIGIT, /* 6 \d */ + OP_NOT_WHITESPACE, /* 7 \S */ + OP_WHITESPACE, /* 8 \s */ + OP_NOT_WORDCHAR, /* 9 \W */ + OP_WORDCHAR, /* 10 \w */ + OP_ANY, /* 11 Match any character */ + OP_ANYBYTE, /* 12 Match any byte (\C); different to OP_ANY for UTF-8 */ + OP_EODN, /* 13 End of data or \n at end of data: \Z. */ + OP_EOD, /* 14 End of data: \z */ + + OP_OPT, /* 15 Set runtime options */ + OP_CIRC, /* 16 Start of line - varies with multiline switch */ + OP_DOLL, /* 17 End of line - varies with multiline switch */ + OP_CHARS, /* 18 Match string of characters */ + OP_NOT, /* 19 Match anything but the following char */ + + OP_STAR, /* 20 The maximizing and minimizing versions of */ + OP_MINSTAR, /* 21 all these opcodes must come in pairs, with */ + OP_PLUS, /* 22 the minimizing one second. */ + OP_MINPLUS, /* 23 This first set applies to single characters */ + OP_QUERY, /* 24 */ + OP_MINQUERY, /* 25 */ + OP_UPTO, /* 26 From 0 to n matches */ + OP_MINUPTO, /* 27 */ + OP_EXACT, /* 28 Exactly n matches */ + + OP_NOTSTAR, /* 29 The maximizing and minimizing versions of */ + OP_NOTMINSTAR, /* 30 all these opcodes must come in pairs, with */ + OP_NOTPLUS, /* 31 the minimizing one second. */ + OP_NOTMINPLUS, /* 32 This set applies to "not" single characters */ + OP_NOTQUERY, /* 33 */ + OP_NOTMINQUERY, /* 34 */ + OP_NOTUPTO, /* 35 From 0 to n matches */ + OP_NOTMINUPTO, /* 36 */ + OP_NOTEXACT, /* 37 Exactly n matches */ + + OP_TYPESTAR, /* 38 The maximizing and minimizing versions of */ + OP_TYPEMINSTAR, /* 39 all these opcodes must come in pairs, with */ + OP_TYPEPLUS, /* 40 the minimizing one second. These codes must */ + OP_TYPEMINPLUS, /* 41 be in exactly the same order as those above. */ + OP_TYPEQUERY, /* 42 This set applies to character types such as \d */ + OP_TYPEMINQUERY, /* 43 */ + OP_TYPEUPTO, /* 44 From 0 to n matches */ + OP_TYPEMINUPTO, /* 45 */ + OP_TYPEEXACT, /* 46 Exactly n matches */ + + OP_CRSTAR, /* 47 The maximizing and minimizing versions of */ + OP_CRMINSTAR, /* 48 all these opcodes must come in pairs, with */ + OP_CRPLUS, /* 49 the minimizing one second. These codes must */ + OP_CRMINPLUS, /* 50 be in exactly the same order as those above. */ + OP_CRQUERY, /* 51 These are for character classes and back refs */ + OP_CRMINQUERY, /* 52 */ + OP_CRRANGE, /* 53 These are different to the three seta above. */ + OP_CRMINRANGE, /* 54 */ + + OP_CLASS, /* 55 Match a character class, chars < 256 only */ + OP_NCLASS, /* 56 Same, but the bitmap was created from a negative + class - the difference is relevant only when a UTF-8 + character > 255 is encountered. */ + + OP_XCLASS, /* 56 Extended class for handling UTF-8 chars within the + class. This does both positive and negative. */ + + OP_REF, /* 57 Match a back reference */ + OP_RECURSE, /* 58 Match a numbered subpattern (possibly recursive) */ + OP_CALLOUT, /* 59 Call out to external function if provided */ + + OP_ALT, /* 60 Start of alternation */ + OP_KET, /* 61 End of group that doesn't have an unbounded repeat */ + OP_KETRMAX, /* 62 These two must remain together and in this */ + OP_KETRMIN, /* 63 order. They are for groups the repeat for ever. */ + + /* The assertions must come before ONCE and COND */ + + OP_ASSERT, /* 64 Positive lookahead */ + OP_ASSERT_NOT, /* 65 Negative lookahead */ + OP_ASSERTBACK, /* 66 Positive lookbehind */ + OP_ASSERTBACK_NOT, /* 67 Negative lookbehind */ + OP_REVERSE, /* 68 Move pointer back - used in lookbehind assertions */ + + /* ONCE and COND must come after the assertions, with ONCE first, as there's + a test for >= ONCE for a subpattern that isn't an assertion. */ + + OP_ONCE, /* 69 Once matched, don't back up into the subpattern */ + OP_COND, /* 70 Conditional group */ + OP_CREF, /* 71 Used to hold an extraction string number (cond ref) */ + + OP_BRAZERO, /* 72 These two must remain together and in this */ + OP_BRAMINZERO, /* 73 order. */ + + OP_BRANUMBER, /* 74 Used for extracting brackets whose number is greater + than can fit into an opcode. */ + + OP_BRA /* 75 This and greater values are used for brackets that + extract substrings up to a basic limit. After that, + use is made of OP_BRANUMBER. */ +}; + +/* WARNING: There is an implicit assumption in study.c that all opcodes are +less than 128 in value. This makes handling UTF-8 character sequences easier. +*/ + + +/* This macro defines textual names for all the opcodes. There are used only +for debugging, in pcre.c when DEBUG is defined, and also in pcretest.c. The +macro is referenced only in printint.c. */ + +#define OP_NAME_LIST \ + "End", "\\A", "\\G", "\\B", "\\b", "\\D", "\\d", \ + "\\S", "\\s", "\\W", "\\w", "Any", "Anybyte", "\\Z", "\\z", \ + "Opt", "^", "$", "chars", "not", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", \ + "class", "nclass", "xclass", "Ref", "Recurse", "Callout", \ + "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", \ + "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cond ref",\ + "Brazero", "Braminzero", "Branumber", "Bra" + + +/* This macro defines the length of fixed length operations in the compiled +regex. The lengths are used when searching for specific things, and also in the +debugging printing of a compiled regex. We use a macro so that it can be +incorporated both into pcre.c and pcretest.c without being publicly exposed. + +As things have been extended, some of these are no longer fixed lenths, but are +minima instead. For example, the length of a single-character repeat may vary +in UTF-8 mode. The code that uses this table must know about such things. */ + +#define OP_LENGTHS \ + 1, /* End */ \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* \A, \G, \B, \B, \D, \d, \S, \s, \W, \w */ \ + 1, 1, 1, 1, 2, 1, 1, /* Any, Anybyte, \Z, \z, Opt, ^, $ */ \ + 2, /* Chars - the minimum length */ \ + 2, /* not */ \ + /* Positive single-char repeats */ \ + 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** These are */ \ + 4, 4, 4, /* upto, minupto, exact ** minima */ \ + /* Negative single-char repeats */ \ + 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \ + 4, 4, 4, /* NOT upto, minupto, exact */ \ + /* Positive type repeats */ \ + 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \ + 4, 4, 4, /* Type upto, minupto, exact */ \ + /* Character class & ref repeats */ \ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \ + 5, 5, /* CRRANGE, CRMINRANGE */ \ + 33, /* CLASS */ \ + 33, /* NCLASS */ \ + 0, /* XCLASS - variable length */ \ + 3, /* REF */ \ + 1+LINK_SIZE, /* RECURSE */ \ + 2, /* CALLOUT */ \ + 1+LINK_SIZE, /* Alt */ \ + 1+LINK_SIZE, /* Ket */ \ + 1+LINK_SIZE, /* KetRmax */ \ + 1+LINK_SIZE, /* KetRmin */ \ + 1+LINK_SIZE, /* Assert */ \ + 1+LINK_SIZE, /* Assert not */ \ + 1+LINK_SIZE, /* Assert behind */ \ + 1+LINK_SIZE, /* Assert behind not */ \ + 1+LINK_SIZE, /* Reverse */ \ + 1+LINK_SIZE, /* Once */ \ + 1+LINK_SIZE, /* COND */ \ + 3, /* CREF */ \ + 1, 1, /* BRAZERO, BRAMINZERO */ \ + 3, /* BRANUMBER */ \ + 1+LINK_SIZE /* BRA */ \ + + +/* The highest extraction number before we have to start using additional +bytes. (Originally PCRE didn't have support for extraction counts highter than +this number.) The value is limited by the number of opcodes left after OP_BRA, +i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional +opcodes. */ + +#define EXTRACT_BASIC_MAX 150 + +/* A magic value for OP_CREF to indicate the "in recursion" condition. */ + +#define CREF_RECURSE 0xffff + +/* The texts of compile-time error messages are defined as macros here so that +they can be accessed by the POSIX wrapper and converted into error codes. Yes, +I could have used error codes in the first place, but didn't feel like changing +just to accommodate the POSIX wrapper. */ + +#define ERR1 "\\ at end of pattern" +#define ERR2 "\\c at end of pattern" +#define ERR3 "unrecognized character follows \\" +#define ERR4 "numbers out of order in {} quantifier" +#define ERR5 "number too big in {} quantifier" +#define ERR6 "missing terminating ] for character class" +#define ERR7 "invalid escape sequence in character class" +#define ERR8 "range out of order in character class" +#define ERR9 "nothing to repeat" +#define ERR10 "operand of unlimited repeat could match the empty string" +#define ERR11 "internal error: unexpected repeat" +#define ERR12 "unrecognized character after (?" +#define ERR13 "POSIX named classes are supported only within a class" +#define ERR14 "missing )" +#define ERR15 "reference to non-existent subpattern" +#define ERR16 "erroffset passed as NULL" +#define ERR17 "unknown option bit(s) set" +#define ERR18 "missing ) after comment" +#define ERR19 "parentheses nested too deeply" +#define ERR20 "regular expression too large" +#define ERR21 "failed to get memory" +#define ERR22 "unmatched parentheses" +#define ERR23 "internal error: code overflow" +#define ERR24 "unrecognized character after (?<" +#define ERR25 "lookbehind assertion is not fixed length" +#define ERR26 "malformed number after (?(" +#define ERR27 "conditional group contains more than two branches" +#define ERR28 "assertion expected after (?(" +#define ERR29 "(?R or (?digits must be followed by )" +#define ERR30 "unknown POSIX class name" +#define ERR31 "POSIX collating elements are not supported" +#define ERR32 "this version of PCRE is not compiled with PCRE_UTF8 support" +#define ERR33 "spare error" +#define ERR34 "character value in \\x{...} sequence is too large" +#define ERR35 "invalid condition (?(0)" +#define ERR36 "\\C not allowed in lookbehind assertion" +#define ERR37 "PCRE does not support \\L, \\l, \\N, \\P, \\p, \\U, \\u, or \\X" +#define ERR38 "number after (?C is > 255" +#define ERR39 "closing ) for (?C expected" +#define ERR40 "recursive call could loop indefinitely" +#define ERR41 "unrecognized character after (?P" +#define ERR42 "syntax error after (?P" +#define ERR43 "two named groups have the same name" + +/* All character handling must be done as unsigned characters. Otherwise there +are problems with top-bit-set characters and functions such as isspace(). +However, we leave the interface to the outside world as char *, because that +should make things easier for callers. We define a short type for unsigned char +to save lots of typing. I tried "uchar", but it causes problems on Digital +Unix, where it is defined in sys/types, so use "uschar" instead. */ + +typedef unsigned char uschar; + +/* The real format of the start of the pcre block; the index of names and the +code vector run on as long as necessary after the end. */ + +typedef struct real_pcre { + unsigned long int magic_number; + size_t size; /* Total that was malloced */ + const unsigned char *tables; /* Pointer to tables */ + unsigned long int options; + unsigned short int top_bracket; + unsigned short int top_backref; + unsigned short int first_byte; + unsigned short int req_byte; + unsigned short int name_entry_size; /* Size of any name items; 0 => none */ + unsigned short int name_count; /* Number of name items */ +} real_pcre; + +/* The format of the block used to store data from pcre_study(). */ + +typedef struct pcre_study_data { + size_t size; /* Total that was malloced */ + uschar options; + uschar start_bits[32]; +} pcre_study_data; + +/* Structure for passing "static" information around between the functions +doing the compiling, so that they are thread-safe. */ + +typedef struct compile_data { + const uschar *lcc; /* Points to lower casing table */ + const uschar *fcc; /* Points to case-flipping table */ + const uschar *cbits; /* Points to character type table */ + const uschar *ctypes; /* Points to table of type maps */ + const uschar *start_code; /* The start of the compiled code */ + uschar *name_table; /* The name/number table */ + int names_found; /* Number of entries so far */ + int name_entry_size; /* Size of each entry */ + int top_backref; /* Maximum back reference */ + unsigned int backref_map; /* Bitmap of low back refs */ + int req_varyopt; /* "After variable item" flag for reqbyte */ +} compile_data; + +/* Structure for maintaining a chain of pointers to the currently incomplete +branches, for testing for left recursion. */ + +typedef struct branch_chain { + struct branch_chain *outer; + uschar *current; +} branch_chain; + +/* Structure for items in a linked list that represents an explicit recursive +call within the pattern. */ + +typedef struct recursion_info { + struct recursion_info *prev; /* Previous recursion record (or NULL) */ + int group_num; /* Number of group that was called */ + const uschar *after_call; /* "Return value": points after the call in the expr */ + const uschar *save_start; /* Old value of md->start_match */ + int *offset_save; /* Pointer to start of saved offsets */ + int saved_max; /* Number of saved offsets */ +} recursion_info; + +/* Structure for passing "static" information around between the functions +doing the matching, so that they are thread-safe. */ + +typedef struct match_data { + unsigned long int match_call_count; /* As it says */ + unsigned long int match_limit;/* As it says */ + int *offset_vector; /* Offset vector */ + int offset_end; /* One past the end */ + int offset_max; /* The maximum usable for return data */ + const uschar *lcc; /* Points to lower casing table */ + const uschar *ctypes; /* Points to table of type maps */ + BOOL offset_overflow; /* Set if too many extractions */ + BOOL notbol; /* NOTBOL flag */ + BOOL noteol; /* NOTEOL flag */ + BOOL utf8; /* UTF8 flag */ + BOOL endonly; /* Dollar not before final \n */ + BOOL notempty; /* Empty string match not wanted */ + const uschar *start_code; /* For use when recursing */ + const uschar *start_subject; /* Start of the subject string */ + const uschar *end_subject; /* End of the subject string */ + const uschar *start_match; /* Start of this match attempt */ + const uschar *end_match_ptr; /* Subject position at end match */ + int end_offset_top; /* Highwater mark at end of match */ + int capture_last; /* Most recent capture number */ + int start_offset; /* The start offset value */ + recursion_info *recursive; /* Linked list of recursion data */ + void *callout_data; /* To pass back to callouts */ +} match_data; + +/* Bit definitions for entries in the pcre_ctypes table. */ + +#define ctype_space 0x01 +#define ctype_letter 0x02 +#define ctype_digit 0x04 +#define ctype_xdigit 0x08 +#define ctype_word 0x10 /* alphameric or '_' */ +#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ + +/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set +of bits for a class map. Some classes are built by combining these tables. */ + +#define cbit_space 0 /* [:space:] or \s */ +#define cbit_xdigit 32 /* [:xdigit:] */ +#define cbit_digit 64 /* [:digit:] or \d */ +#define cbit_upper 96 /* [:upper:] */ +#define cbit_lower 128 /* [:lower:] */ +#define cbit_word 160 /* [:word:] or \w */ +#define cbit_graph 192 /* [:graph:] */ +#define cbit_print 224 /* [:print:] */ +#define cbit_punct 256 /* [:punct:] */ +#define cbit_cntrl 288 /* [:cntrl:] */ +#define cbit_length 320 /* Length of the cbits table */ + +/* Offsets of the various tables from the base tables pointer, and +total length. */ + +#define lcc_offset 0 +#define fcc_offset 256 +#define cbits_offset 512 +#define ctypes_offset (cbits_offset + cbit_length) +#define tables_length (ctypes_offset + 256) + +/* End of internal.h */ diff --git a/pcre/maketables.c b/pcre/maketables.c new file mode 100644 index 00000000..257fe89c --- /dev/null +++ b/pcre/maketables.c @@ -0,0 +1,136 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Written by: Philip Hazel + + Copyright (c) 1997-2003 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software 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. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- + +See the file Tech.Notes for some information on the internals. +*/ + + +/* This file is compiled on its own as part of the PCRE library. However, +it is also included in the compilation of dftables.c, in which case the macro +DFTABLES is defined. */ + +#ifndef DFTABLES +#include "internal.h" +#endif + + + +/************************************************* +* Create PCRE character tables * +*************************************************/ + +/* This function builds a set of character tables for use by PCRE and returns +a pointer to them. They are build using the ctype functions, and consequently +their contents will depend upon the current locale setting. When compiled as +part of the library, the store is obtained via pcre_malloc(), but when compiled +inside dftables, use malloc(). + +Arguments: none +Returns: pointer to the contiguous block of data +*/ + +const unsigned char * +pcre_maketables(void) +{ +unsigned char *yield, *p; +int i; + +#ifndef DFTABLES +yield = (unsigned char*)(pcre_malloc)(tables_length); +#else +yield = (unsigned char*)malloc(tables_length); +#endif + +if (yield == NULL) return NULL; +p = yield; + +/* First comes the lower casing table */ + +for (i = 0; i < 256; i++) *p++ = tolower(i); + +/* Next the case-flipping table */ + +for (i = 0; i < 256; i++) *p++ = islower(i)? toupper(i) : tolower(i); + +/* Then the character class tables. Don't try to be clever and save effort +on exclusive ones - in some locales things may be different. Note that the +table for "space" includes everything "isspace" gives, including VT in the +default locale. This makes it work for the POSIX class [:space:]. */ + +memset(p, 0, cbit_length); +for (i = 0; i < 256; i++) + { + if (isdigit(i)) + { + p[cbit_digit + i/8] |= 1 << (i&7); + p[cbit_word + i/8] |= 1 << (i&7); + } + if (isupper(i)) + { + p[cbit_upper + i/8] |= 1 << (i&7); + p[cbit_word + i/8] |= 1 << (i&7); + } + if (islower(i)) + { + p[cbit_lower + i/8] |= 1 << (i&7); + p[cbit_word + i/8] |= 1 << (i&7); + } + if (i == '_') p[cbit_word + i/8] |= 1 << (i&7); + if (isspace(i)) p[cbit_space + i/8] |= 1 << (i&7); + if (isxdigit(i))p[cbit_xdigit + i/8] |= 1 << (i&7); + if (isgraph(i)) p[cbit_graph + i/8] |= 1 << (i&7); + if (isprint(i)) p[cbit_print + i/8] |= 1 << (i&7); + if (ispunct(i)) p[cbit_punct + i/8] |= 1 << (i&7); + if (iscntrl(i)) p[cbit_cntrl + i/8] |= 1 << (i&7); + } +p += cbit_length; + +/* Finally, the character type table. In this, we exclude VT from the white +space chars, because Perl doesn't recognize it as such for \s and for comments +within regexes. */ + +for (i = 0; i < 256; i++) + { + int x = 0; + if (i != 0x0b && isspace(i)) x += ctype_space; + if (isalpha(i)) x += ctype_letter; + if (isdigit(i)) x += ctype_digit; + if (isxdigit(i)) x += ctype_xdigit; + if (isalnum(i) || i == '_') x += ctype_word; + if (strchr("*+?{^.$|()[", i) != 0) x += ctype_meta; + *p++ = x; + } + +return yield; +} + +/* End of maketables.c */ diff --git a/pcre/pcre.c b/pcre/pcre.c new file mode 100644 index 00000000..2a699af4 --- /dev/null +++ b/pcre/pcre.c @@ -0,0 +1,7596 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-2003 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software 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. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- +*/ + +/* Define PCREDEBUG to get debugging output on stdout. */ + +/* #define PCREDEBUG */ + +/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef +inline, and there are *still* stupid compilers about that don't like indented +pre-processor statements. I suppose it's only been 10 years... */ + +#ifdef PCREDEBUG +#define DPRINTF(p) printf p +#else +#define DPRINTF(p) /*nothing*/ +#endif + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + +#include "internal.h" + + +/* Allow compilation as C++ source code, should anybody want to do that. */ + +#ifdef __cplusplus +#define class pcre_class +#endif + + +/* Maximum number of items on the nested bracket stacks at compile time. This +applies to the nesting of all kinds of parentheses. It does not limit +un-nested, non-capturing parentheses. This number can be made bigger if +necessary - it is used to dimension one int and one unsigned char vector at +compile time. */ + +#define BRASTACK_SIZE 200 + + +/* Maximum number of ints of offset to save on the stack for recursive calls. +If the offset vector is bigger, malloc is used. This should be a multiple of 3, +because the offset vector is always a multiple of 3 long. */ + +#define REC_STACK_SAVE_MAX 30 + + +/* The number of bytes in a literal character string above which we can't add +any more is set at 250 in order to allow for UTF-8 characters. (In theory it +could be 255 when UTF-8 support is excluded, but that means that some of the +test output would be different, which just complicates things.) */ + +#define MAXLIT 250 + + +/* The maximum remaining length of subject we are prepared to search for a +req_byte match. */ + +#define REQ_BYTE_MAX 1000 + + +/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that +the definition is next to the definition of the opcodes in internal.h. */ + +static uschar OP_lengths[] = { OP_LENGTHS }; + +/* Min and max values for the common repeats; for the maxima, 0 => infinity */ + +static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; +static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; + +/* Table for handling escaped characters in the range '0'-'z'. Positive returns +are simple data values; negative values are for special things like \d and so +on. Zero means further processing is needed (for things like \x), or the escape +is invalid. */ + +static const short int escapes[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ + 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ + '@', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G, /* @ - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, -ESC_Q, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */ + 0, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */ + '`', 7, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0, /* ` - g */ + 0, 0, 0, 0, 0, 0, ESC_n, 0, /* h - o */ + 0, 0, ESC_r, -ESC_s, ESC_tee, 0, 0, -ESC_w, /* p - w */ + 0, 0, -ESC_z /* x - z */ +}; + +/* Tables of names of POSIX character classes and their lengths. The list is +terminated by a zero length entry. The first three must be alpha, upper, lower, +as this is assumed for handling case independence. */ + +static const char *posix_names[] = { + "alpha", "lower", "upper", + "alnum", "ascii", "blank", "cntrl", "digit", "graph", + "print", "punct", "space", "word", "xdigit" }; + +static const uschar posix_name_lengths[] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; + +/* Table of class bit maps for each POSIX class; up to three may be combined +to form the class. The table for [:blank:] is dynamically modified to remove +the vertical space characters. */ + +static const int posix_class_maps[] = { + cbit_lower, cbit_upper, -1, /* alpha */ + cbit_lower, -1, -1, /* lower */ + cbit_upper, -1, -1, /* upper */ + cbit_digit, cbit_lower, cbit_upper, /* alnum */ + cbit_print, cbit_cntrl, -1, /* ascii */ + cbit_space, -1, -1, /* blank - a GNU extension */ + cbit_cntrl, -1, -1, /* cntrl */ + cbit_digit, -1, -1, /* digit */ + cbit_graph, -1, -1, /* graph */ + cbit_print, -1, -1, /* print */ + cbit_punct, -1, -1, /* punct */ + cbit_space, -1, -1, /* space */ + cbit_word, -1, -1, /* word - a Perl extension */ + cbit_xdigit,-1, -1 /* xdigit */ +}; + +/* Table to identify ASCII digits and hex digits. This is used when compiling +patterns. Note that the tables in chartables are dependent on the locale, and +may mark arbitrary characters as digits - but the PCRE compiling code expects +to handle only 0-9, a-z, and A-Z as digits when compiling. That is why we have +a private table here. It costs 256 bytes, but it is a lot faster than doing +character value tests (at least in some simple cases I timed), and in some +applications one wants PCRE to compile efficiently as well as match +efficiently. + +For convenience, we use the same bit definitions as in chartables: + + 0x04 decimal digit + 0x08 hexadecimal digit + +Then we can use ctype_digit and ctype_xdigit in the code. */ + +static const unsigned char digitab[] = + { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - ' */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ( - / */ + 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /* 0 - 7 */ + 0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, /* 8 - ? */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* @ - G */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* H - O */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* P - W */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* X - _ */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* ` - g */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* h - o */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* p - w */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +/* Definition to allow mutual recursion */ + +static BOOL + compile_regex(int, int, int *, uschar **, const uschar **, const char **, + BOOL, int, int *, int *, branch_chain *, compile_data *); + +/* Structure for building a chain of data that actually lives on the +stack, for holding the values of the subject pointer at the start of each +subpattern, so as to detect when an empty string has been matched by a +subpattern - to break infinite loops. */ + +typedef struct eptrblock { + struct eptrblock *prev; + const uschar *saved_eptr; +} eptrblock; + +/* Flag bits for the match() function */ + +#define match_condassert 0x01 /* Called to check a condition assertion */ +#define match_isgroup 0x02 /* Set if start of bracketed group */ + +/* Non-error returns from the match() function. Error returns are externally +defined PCRE_ERROR_xxx codes, which are all negative. */ + +#define MATCH_MATCH 1 +#define MATCH_NOMATCH 0 + + + +/************************************************* +* Global variables * +*************************************************/ + +/* PCRE is thread-clean and doesn't use any global variables in the normal +sense. However, it calls memory allocation and free functions via the two +indirections below, and it can optionally do callouts. These values can be +changed by the caller, but are shared between all threads. However, when +compiling for Virtual Pascal, things are done differently (see pcre.in). */ + +#ifndef VPCOMPAT +void *(*pcre_malloc)(size_t) = malloc; +void (*pcre_free)(void *) = free; +int (*pcre_callout)(pcre_callout_block *) = NULL; +#endif + + +/************************************************* +* Macros and tables for character handling * +*************************************************/ + +/* When UTF-8 encoding is being used, a character is no longer just a single +byte. The macros for character handling generate simple sequences when used in +byte-mode, and more complicated ones for UTF-8 characters. */ + +#ifndef SUPPORT_UTF8 +#define GETCHAR(c, eptr) c = *eptr; +#define GETCHARINC(c, eptr) c = *eptr++; +#define GETCHARINCTEST(c, eptr) c = *eptr++; +#define GETCHARLEN(c, eptr, len) c = *eptr; +#define BACKCHAR(eptr) + +#else /* SUPPORT_UTF8 */ + +/* Get the next UTF-8 character, not advancing the pointer. This is called when +we know we are in UTF-8 mode. */ + +#define GETCHAR(c, eptr) \ + c = *eptr; \ + if ((c & 0xc0) == 0xc0) \ + { \ + int gcii; \ + int gcaa = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ + int gcss = 6*gcaa; \ + c = (c & utf8_table3[gcaa]) << gcss; \ + for (gcii = 1; gcii <= gcaa; gcii++) \ + { \ + gcss -= 6; \ + c |= (eptr[gcii] & 0x3f) << gcss; \ + } \ + } + +/* Get the next UTF-8 character, advancing the pointer. This is called when we +know we are in UTF-8 mode. */ + +#define GETCHARINC(c, eptr) \ + c = *eptr++; \ + if ((c & 0xc0) == 0xc0) \ + { \ + int gcaa = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ + int gcss = 6*gcaa; \ + c = (c & utf8_table3[gcaa]) << gcss; \ + while (gcaa-- > 0) \ + { \ + gcss -= 6; \ + c |= (*eptr++ & 0x3f) << gcss; \ + } \ + } + +/* Get the next character, testing for UTF-8 mode, and advancing the pointer */ + +#define GETCHARINCTEST(c, eptr) \ + c = *eptr++; \ + if (md->utf8 && (c & 0xc0) == 0xc0) \ + { \ + int gcaa = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ + int gcss = 6*gcaa; \ + c = (c & utf8_table3[gcaa]) << gcss; \ + while (gcaa-- > 0) \ + { \ + gcss -= 6; \ + c |= (*eptr++ & 0x3f) << gcss; \ + } \ + } + +/* Get the next UTF-8 character, not advancing the pointer, incrementing length +if there are extra bytes. This is called when we know we are in UTF-8 mode. */ + +#define GETCHARLEN(c, eptr, len) \ + c = *eptr; \ + if ((c & 0xc0) == 0xc0) \ + { \ + int gcii; \ + int gcaa = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ + int gcss = 6*gcaa; \ + c = (c & utf8_table3[gcaa]) << gcss; \ + for (gcii = 1; gcii <= gcaa; gcii++) \ + { \ + gcss -= 6; \ + c |= (eptr[gcii] & 0x3f) << gcss; \ + } \ + len += gcaa; \ + } + +/* If the pointer is not at the start of a character, move it back until +it is. Called only in UTF-8 mode. */ + +#define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr--; + +#endif + + + +/************************************************* +* Default character tables * +*************************************************/ + +/* A default set of character tables is included in the PCRE binary. Its source +is built by the maketables auxiliary program, which uses the default C ctypes +functions, and put in the file chartables.c. These tables are used by PCRE +whenever the caller of pcre_compile() does not provide an alternate set of +tables. */ + +#include "chartables.c" + + + +#ifdef SUPPORT_UTF8 +/************************************************* +* Tables for UTF-8 support * +*************************************************/ + +/* These are the breakpoints for different numbers of bytes in a UTF-8 +character. */ + +static const int utf8_table1[] = + { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff}; + +/* These are the indicator bits and the mask for the data bits to set in the +first byte of a character, indexed by the number of additional bytes. */ + +static const int utf8_table2[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}; +static const int utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; + +/* Table of the number of extra characters, indexed by the first character +masked with 0x3f. The highest number for a valid UTF-8 character is in fact +0x3d. */ + +static const uschar utf8_table4[] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; + + +/************************************************* +* Convert character value to UTF-8 * +*************************************************/ + +/* This function takes an integer value in the range 0 - 0x7fffffff +and encodes it as a UTF-8 character in 0 to 6 bytes. + +Arguments: + cvalue the character value + buffer pointer to buffer for result - at least 6 bytes long + +Returns: number of characters placed in the buffer +*/ + +static int +ord2utf8(int cvalue, uschar *buffer) +{ +register int i, j; +for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++) + if (cvalue <= utf8_table1[i]) break; +buffer += i; +for (j = i; j > 0; j--) + { + *buffer-- = 0x80 | (cvalue & 0x3f); + cvalue >>= 6; + } +*buffer = utf8_table2[i] | cvalue; +return i + 1; +} +#endif + + + +/************************************************* +* Print compiled regex * +*************************************************/ + +/* The code for doing this is held in a separate file that is also included in +pcretest.c. It defines a function called print_internals(). */ + +#ifdef PCREDEBUG +#include "printint.c" +#endif + + + +/************************************************* +* Return version string * +*************************************************/ + +#define STRING(a) # a +#define XSTRING(s) STRING(s) + +const char * +pcre_version(void) +{ +return XSTRING(PCRE_MAJOR) "." XSTRING(PCRE_MINOR) " " XSTRING(PCRE_DATE); +} + + + + +/************************************************* +* (Obsolete) Return info about compiled pattern * +*************************************************/ + +/* This is the original "info" function. It picks potentially useful data out +of the private structure, but its interface was too rigid. It remains for +backwards compatibility. The public options are passed back in an int - though +the re->options field has been expanded to a long int, all the public options +at the low end of it, and so even on 16-bit systems this will still be OK. +Therefore, I haven't changed the API for pcre_info(). + +Arguments: + external_re points to compiled code + optptr where to pass back the options + first_byte where to pass back the first character, + or -1 if multiline and all branches start ^, + or -2 otherwise + +Returns: number of capturing subpatterns + or negative values on error +*/ + +int +pcre_info(const pcre *external_re, int *optptr, int *first_byte) +{ +const real_pcre *re = (const real_pcre *)external_re; +if (re == NULL) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; +if (optptr != NULL) *optptr = (int)(re->options & PUBLIC_OPTIONS); +if (first_byte != NULL) + *first_byte = ((re->options & PCRE_FIRSTSET) != 0)? re->first_byte : + ((re->options & PCRE_STARTLINE) != 0)? -1 : -2; +return re->top_bracket; +} + + + +/************************************************* +* Return info about compiled pattern * +*************************************************/ + +/* This is a newer "info" function which has an extensible interface so +that additional items can be added compatibly. + +Arguments: + external_re points to compiled code + extra_data points extra data, or NULL + what what information is required + where where to put the information + +Returns: 0 if data returned, negative on error +*/ + +int +pcre_fullinfo(const pcre *external_re, const pcre_extra *extra_data, int what, + void *where) +{ +const real_pcre *re = (const real_pcre *)external_re; +const pcre_study_data *study = NULL; + +if (re == NULL || where == NULL) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; + +if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_STUDY_DATA) != 0) + study = extra_data->study_data; + +switch (what) + { + case PCRE_INFO_OPTIONS: + *((unsigned long int *)where) = re->options & PUBLIC_OPTIONS; + break; + + case PCRE_INFO_SIZE: + *((size_t *)where) = re->size; + break; + + case PCRE_INFO_STUDYSIZE: + *((size_t *)where) = (study == NULL)? 0 : study->size; + break; + + case PCRE_INFO_CAPTURECOUNT: + *((int *)where) = re->top_bracket; + break; + + case PCRE_INFO_BACKREFMAX: + *((int *)where) = re->top_backref; + break; + + case PCRE_INFO_FIRSTBYTE: + *((int *)where) = + ((re->options & PCRE_FIRSTSET) != 0)? re->first_byte : + ((re->options & PCRE_STARTLINE) != 0)? -1 : -2; + break; + + case PCRE_INFO_FIRSTTABLE: + *((const uschar **)where) = + (study != NULL && (study->options & PCRE_STUDY_MAPPED) != 0)? + study->start_bits : NULL; + break; + + case PCRE_INFO_LASTLITERAL: + *((int *)where) = + ((re->options & PCRE_REQCHSET) != 0)? re->req_byte : -1; + break; + + case PCRE_INFO_NAMEENTRYSIZE: + *((int *)where) = re->name_entry_size; + break; + + case PCRE_INFO_NAMECOUNT: + *((int *)where) = re->name_count; + break; + + case PCRE_INFO_NAMETABLE: + *((const uschar **)where) = (const uschar *)re + sizeof(real_pcre); + break; + + default: return PCRE_ERROR_BADOPTION; + } + +return 0; +} + + + +/************************************************* +* Return info about what features are configured * +*************************************************/ + +/* This is function which has an extensible interface so that additional items +can be added compatibly. + +Arguments: + what what information is required + where where to put the information + +Returns: 0 if data returned, negative on error +*/ + +int +pcre_config(int what, void *where) +{ +switch (what) + { + case PCRE_CONFIG_UTF8: + #ifdef SUPPORT_UTF8 + *((int *)where) = 1; + #else + *((int *)where) = 0; + #endif + break; + + case PCRE_CONFIG_NEWLINE: + *((int *)where) = NEWLINE; + break; + + case PCRE_CONFIG_LINK_SIZE: + *((int *)where) = LINK_SIZE; + break; + + case PCRE_CONFIG_POSIX_MALLOC_THRESHOLD: + *((int *)where) = POSIX_MALLOC_THRESHOLD; + break; + + case PCRE_CONFIG_MATCH_LIMIT: + *((unsigned int *)where) = MATCH_LIMIT; + break; + + default: return PCRE_ERROR_BADOPTION; + } + +return 0; +} + + + +#ifdef PCREDEBUG +/************************************************* +* Debugging function to print chars * +*************************************************/ + +/* Print a sequence of chars in printable format, stopping at the end of the +subject if the requested. + +Arguments: + p points to characters + length number to print + is_subject TRUE if printing from within md->start_subject + md pointer to matching data block, if is_subject is TRUE + +Returns: nothing +*/ + +static void +pchars(const uschar *p, int length, BOOL is_subject, match_data *md) +{ +int c; +if (is_subject && length > md->end_subject - p) length = md->end_subject - p; +while (length-- > 0) + if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c); +} +#endif + + + + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \n, or a negative value which +encodes one of the more complicated things such as \d. When UTF-8 is enabled, +a positive value greater than 255 may be returned. On entry, ptr is pointing at +the \. On exit, it is on the final character of the escape sequence. + +Arguments: + ptrptr points to the pattern position pointer + errorptr points to the pointer to the error message + bracount number of previous extracting brackets + options the options bits + isclass TRUE if inside a character class + cd pointer to char tables block + +Returns: zero or positive => a data character + negative => a special escape sequence + on error, errorptr is set +*/ + +static int +check_escape(const uschar **ptrptr, const char **errorptr, int bracount, + int options, BOOL isclass, compile_data *cd) +{ +const uschar *ptr = *ptrptr; +int c, i; + +/* If backslash is at the end of the pattern, it's an error. */ + +c = *(++ptr); +if (c == 0) *errorptr = ERR1; + +/* Digits or letters may have special meaning; all others are literals. */ + +else if (c < '0' || c > 'z') {} + +/* Do an initial lookup in a table. A non-zero result is something that can be +returned immediately. Otherwise further processing may be required. */ + +else if ((i = escapes[c - '0']) != 0) c = i; + +/* Escapes that need further processing, or are illegal. */ + +else + { + const uschar *oldptr; + switch (c) + { + /* A number of Perl escapes are not handled by PCRE. We give an explicit + error. */ + + case 'l': + case 'L': + case 'N': + case 'p': + case 'P': + case 'u': + case 'U': + case 'X': + *errorptr = ERR37; + break; + + /* The handling of escape sequences consisting of a string of digits + starting with one that is not zero is not straightforward. By experiment, + the way Perl works seems to be as follows: + + Outside a character class, the digits are read as a decimal number. If the + number is less than 10, or if there are that many previous extracting + left brackets, then it is a back reference. Otherwise, up to three octal + digits are read to form an escaped byte. Thus \123 is likely to be octal + 123 (cf \0123, which is octal 012 followed by the literal 3). If the octal + value is greater than 377, the least significant 8 bits are taken. Inside a + character class, \ followed by a digit is always an octal number. */ + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + + if (!isclass) + { + oldptr = ptr; + c -= '0'; + while ((digitab[ptr[1]] & ctype_digit) != 0) + c = c * 10 + *(++ptr) - '0'; + if (c < 10 || c <= bracount) + { + c = -(ESC_REF + c); + break; + } + ptr = oldptr; /* Put the pointer back and fall through */ + } + + /* Handle an octal number following \. If the first digit is 8 or 9, Perl + generates a binary zero byte and treats the digit as a following literal. + Thus we have to pull back the pointer by one. */ + + if ((c = *ptr) >= '8') + { + ptr--; + c = 0; + break; + } + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit. */ + + case '0': + c -= '0'; + while(i++ < 2 && ptr[1] >= '0' && ptr[1] <= '7') + c = c * 8 + *(++ptr) - '0'; + c &= 255; /* Take least significant 8 bits */ + break; + + /* \x is complicated when UTF-8 is enabled. \x{ddd} is a character number + which can be greater than 0xff, but only if the ddd are hex digits. */ + + case 'x': +#ifdef SUPPORT_UTF8 + if (ptr[1] == '{' && (options & PCRE_UTF8) != 0) + { + const uschar *pt = ptr + 2; + register int count = 0; + c = 0; + while ((digitab[*pt] & ctype_xdigit) != 0) + { + int cc = *pt++; + if (cc >= 'a') cc -= 32; /* Convert to upper case */ + count++; + c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10)); + } + if (*pt == '}') + { + if (c < 0 || count > 8) *errorptr = ERR34; + ptr = pt; + break; + } + /* If the sequence of hex digits does not end with '}', then we don't + recognize this construct; fall through to the normal \x handling. */ + } +#endif + + /* Read just a single hex char */ + + c = 0; + while (i++ < 2 && (digitab[ptr[1]] & ctype_xdigit) != 0) + { + int cc = *(++ptr); + if (cc >= 'a') cc -= 32; /* Convert to upper case */ + c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10)); + } + break; + + /* Other special escapes not starting with a digit are straightforward */ + + case 'c': + c = *(++ptr); + if (c == 0) + { + *errorptr = ERR2; + return 0; + } + + /* A letter is upper-cased; then the 0x40 bit is flipped. This coding + is ASCII-specific, but then the whole concept of \cx is ASCII-specific. */ + + if (c >= 'a' && c <= 'z') c -= 32; + c ^= 0x40; + break; + + /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any + other alphameric following \ is an error if PCRE_EXTRA was set; otherwise, + for Perl compatibility, it is a literal. This code looks a bit odd, but + there used to be some cases other than the default, and there may be again + in future, so I haven't "optimized" it. */ + + default: + if ((options & PCRE_EXTRA) != 0) switch(c) + { + default: + *errorptr = ERR3; + break; + } + break; + } + } + +*ptrptr = ptr; +return c; +} + + + +/************************************************* +* Check for counted repeat * +*************************************************/ + +/* This function is called when a '{' is encountered in a place where it might +start a quantifier. It looks ahead to see if it really is a quantifier or not. +It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} +where the ddds are digits. + +Arguments: + p pointer to the first char after '{' + cd pointer to char tables block + +Returns: TRUE or FALSE +*/ + +static BOOL +is_counted_repeat(const uschar *p, compile_data *cd) +{ +if ((digitab[*p++] && ctype_digit) == 0) return FALSE; +while ((digitab[*p] & ctype_digit) != 0) p++; +if (*p == '}') return TRUE; + +if (*p++ != ',') return FALSE; +if (*p == '}') return TRUE; + +if ((digitab[*p++] && ctype_digit) == 0) return FALSE; +while ((digitab[*p] & ctype_digit) != 0) p++; + +return (*p == '}'); +} + + + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values. This is called only +after is_counted_repeat() has confirmed that a repeat-count quantifier exists, +so the syntax is guaranteed to be correct, but we need to check the values. + +Arguments: + p pointer to first char after '{' + minp pointer to int for min + maxp pointer to int for max + returned as -1 if no max + errorptr points to pointer to error message + cd pointer to character tables clock + +Returns: pointer to '}' on success; + current ptr on error, with errorptr set +*/ + +static const uschar * +read_repeat_counts(const uschar *p, int *minp, int *maxp, + const char **errorptr, compile_data *cd) +{ +int min = 0; +int max = -1; + +while ((digitab[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0'; + +if (*p == '}') max = min; else + { + if (*(++p) != '}') + { + max = 0; + while((digitab[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0'; + if (max < min) + { + *errorptr = ERR4; + return p; + } + } + } + +/* Do paranoid checks, then fill in the required variables, and pass back the +pointer to the terminating '}'. */ + +if (min > 65535 || max > 65535) + *errorptr = ERR5; +else + { + *minp = min; + *maxp = max; + } +return p; +} + + + +/************************************************* +* Find first significant op code * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring op code etc. It skips over things +that do not influence this. For some calls, a change of option is important. + +Arguments: + code pointer to the start of the group + options pointer to external options + optbit the option bit whose changing is significant, or + zero if none are + +Returns: pointer to the first significant opcode +*/ + +static const uschar* +first_significant_code(const uschar *code, int *options, int optbit) +{ +for (;;) + { + switch ((int)*code) + { + case OP_OPT: + if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit)) + *options = (int)code[1]; + code += 2; + break; + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do code += GET(code, 1); while (*code == OP_ALT); + /* Fall through */ + + case OP_CALLOUT: + case OP_CREF: + case OP_BRANUMBER: + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + code += OP_lengths[*code]; + break; + + default: + return code; + } + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Find the fixed length of a pattern * +*************************************************/ + +/* Scan a pattern and compute the fixed length of subject that will match it, +if the length is fixed. This is needed for dealing with backward assertions. +In UTF8 mode, the result is in characters rather than bytes. + +Arguments: + code points to the start of the pattern (the bracket) + options the compiling options + +Returns: the fixed length, or -1 if there is no fixed length, + or -2 if \C was encountered +*/ + +static int +find_fixedlength(uschar *code, int options) +{ +int length = -1; + +register int branchlength = 0; +register uschar *cc = code + 1 + LINK_SIZE; + +/* Scan along the opcodes for this branch. If we get to the end of the +branch, check the length against that of the other branches. */ + +for (;;) + { + int d; + register int op = *cc; + if (op >= OP_BRA) op = OP_BRA; + + switch (op) + { + case OP_BRA: + case OP_ONCE: + case OP_COND: + d = find_fixedlength(cc, options); + if (d < 0) return d; + branchlength += d; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* Reached end of a branch; if it's a ket it is the end of a nested + call. If it's ALT it is an alternation in a nested call. If it is + END it's the end of the outer call. All can be handled by the same code. */ + + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_END: + if (length < 0) length = branchlength; + else if (length != branchlength) return -1; + if (*cc != OP_ALT) return length; + cc += 1 + LINK_SIZE; + branchlength = 0; + break; + + /* Skip over assertive subpatterns */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do cc += GET(cc, 1); while (*cc == OP_ALT); + /* Fall through */ + + /* Skip over things that don't match chars */ + + case OP_REVERSE: + case OP_BRANUMBER: + case OP_CREF: + case OP_OPT: + case OP_CALLOUT: + case OP_SOD: + case OP_SOM: + case OP_EOD: + case OP_EODN: + case OP_CIRC: + case OP_DOLL: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + cc += OP_lengths[*cc]; + break; + + /* Handle char strings. In UTF-8 mode we must count characters, not bytes. + This requires a scan of the string, unfortunately. We assume valid UTF-8 + strings, so all we do is reduce the length by one for every byte whose bits + are 10xxxxxx. */ + + case OP_CHARS: + branchlength += *(++cc); +#ifdef SUPPORT_UTF8 + if ((options & PCRE_UTF8) != 0) + for (d = 1; d <= *cc; d++) + if ((cc[d] & 0xc0) == 0x80) branchlength--; +#endif + cc += *cc + 1; + break; + + /* Handle exact repetitions. The count is already in characters, but we + need to skip over a multibyte character in UTF8 mode. */ + + case OP_EXACT: + branchlength += GET2(cc,1); + cc += 4; +#ifdef SUPPORT_UTF8 + if ((options & PCRE_UTF8) != 0) + { + while((*cc & 0x80) == 0x80) cc++; + } +#endif + break; + + case OP_TYPEEXACT: + branchlength += GET2(cc,1); + cc += 4; + break; + + /* Handle single-char matchers */ + + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + branchlength++; + cc++; + break; + + /* The single-byte matcher isn't allowed */ + + case OP_ANYBYTE: + return -2; + + /* Check a class for variable quantification */ + +#ifdef SUPPORT_UTF8 + case OP_XCLASS: + cc += GET(cc, 1) - 33; + /* Fall through */ +#endif + + case OP_CLASS: + case OP_NCLASS: + cc += 33; + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + return -1; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(cc,1) != GET2(cc,3)) return -1; + branchlength += GET2(cc,1); + cc += 5; + break; + + default: + branchlength++; + } + break; + + /* Anything else is variable length */ + + default: + return -1; + } + } +/* Control never gets here */ +} + + + + +/************************************************* +* Scan compiled regex for numbered bracket * +*************************************************/ + +/* This little function scans through a compiled pattern until it finds a +capturing bracket with the given number. + +Arguments: + code points to start of expression + utf8 TRUE in UTF-8 mode + number the required bracket number + +Returns: pointer to the opcode for the bracket, or NULL if not found +*/ + +static const uschar * +find_bracket(const uschar *code, BOOL utf8, int number) +{ +#ifndef SUPPORT_UTF8 +utf8 = utf8; /* Stop pedantic compilers complaining */ +#endif + +for (;;) + { + register int c = *code; + if (c == OP_END) return NULL; + else if (c == OP_CHARS) code += code[1] + OP_lengths[c]; + else if (c > OP_BRA) + { + int n = c - OP_BRA; + if (n > EXTRACT_BASIC_MAX) n = GET2(code, 2+LINK_SIZE); + if (n == number) return (uschar *)code; + code += OP_lengths[OP_BRA]; + } + else + { + code += OP_lengths[c]; + + /* In UTF-8 mode, opcodes that are followed by a character may be followed + by a multi-byte character. The length in the table is a minimum, so we have + to scan along to skip the extra characters. All opcodes are less than 128, + so we can use relatively efficient code. */ + +#ifdef SUPPORT_UTF8 + if (utf8) switch(c) + { + case OP_EXACT: + case OP_UPTO: + case OP_MINUPTO: + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + while ((*code & 0xc0) == 0x80) code++; + break; + } +#endif + } + } +} + + + +/************************************************* +* Scan compiled branch for non-emptiness * +*************************************************/ + +/* This function scans through a branch of a compiled pattern to see whether it +can match the empty string or not. It is called only from could_be_empty() +below. Note that first_significant_code() skips over assertions. If we hit an +unclosed bracket, we return "empty" - this means we've struck an inner bracket +whose current branch will already have been scanned. + +Arguments: + code points to start of search + endcode points to where to stop + utf8 TRUE if in UTF8 mode + +Returns: TRUE if what is matched could be empty +*/ + +static BOOL +could_be_empty_branch(const uschar *code, const uschar *endcode, BOOL utf8) +{ +register int c; +for (code = first_significant_code(code + 1 + LINK_SIZE, NULL, 0); + code < endcode; + code = first_significant_code(code + OP_lengths[c], NULL, 0)) + { + const uschar *ccode; + + c = *code; + + if (c >= OP_BRA) + { + BOOL empty_branch; + if (GET(code, 1) == 0) return TRUE; /* Hit unclosed bracket */ + + /* Scan a closed bracket */ + + empty_branch = FALSE; + do + { + if (!empty_branch && could_be_empty_branch(code, endcode, utf8)) + empty_branch = TRUE; + code += GET(code, 1); + } + while (*code == OP_ALT); + if (!empty_branch) return FALSE; /* All branches are non-empty */ + code += 1 + LINK_SIZE; + c = *code; + } + + else switch (c) + { + /* Check for quantifiers after a class */ + +#ifdef SUPPORT_UTF8 + case OP_XCLASS: + ccode = code + GET(code, 1); + goto CHECK_CLASS_REPEAT; +#endif + + case OP_CLASS: + case OP_NCLASS: + ccode = code + 33; + +#ifdef SUPPORT_UTF8 + CHECK_CLASS_REPEAT: +#endif + + switch (*ccode) + { + case OP_CRSTAR: /* These could be empty; continue */ + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + break; + + default: /* Non-repeat => class must match */ + case OP_CRPLUS: /* These repeats aren't empty */ + case OP_CRMINPLUS: + return FALSE; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(ccode, 1) > 0) return FALSE; /* Minimum > 0 */ + break; + } + break; + + /* Opcodes that must match a character */ + + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ANYBYTE: + case OP_CHARS: + case OP_NOT: + case OP_PLUS: + case OP_MINPLUS: + case OP_EXACT: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTEXACT: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEEXACT: + return FALSE; + + /* End of branch */ + + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_ALT: + return TRUE; + + /* In UTF-8 mode, STAR, MINSTAR, QUERY, MINQUERY, UPTO, and MINUPTO may be + followed by a multibyte character */ + +#ifdef SUPPORT_UTF8 + case OP_STAR: + case OP_MINSTAR: + case OP_QUERY: + case OP_MINQUERY: + case OP_UPTO: + case OP_MINUPTO: + if (utf8) while ((code[2] & 0xc0) == 0x80) code++; + break; +#endif + } + } + +return TRUE; +} + + + +/************************************************* +* Scan compiled regex for non-emptiness * +*************************************************/ + +/* This function is called to check for left recursive calls. We want to check +the current branch of the current pattern to see if it could match the empty +string. If it could, we must look outwards for branches at other levels, +stopping when we pass beyond the bracket which is the subject of the recursion. + +Arguments: + code points to start of the recursion + endcode points to where to stop (current RECURSE item) + bcptr points to the chain of current (unclosed) branch starts + utf8 TRUE if in UTF-8 mode + +Returns: TRUE if what is matched could be empty +*/ + +static BOOL +could_be_empty(const uschar *code, const uschar *endcode, branch_chain *bcptr, + BOOL utf8) +{ +while (bcptr != NULL && bcptr->current >= code) + { + if (!could_be_empty_branch(bcptr->current, endcode, utf8)) return FALSE; + bcptr = bcptr->outer; + } +return TRUE; +} + + + +/************************************************* +* Check for POSIX class syntax * +*************************************************/ + +/* This function is called when the sequence "[:" or "[." or "[=" is +encountered in a character class. It checks whether this is followed by an +optional ^ and then a sequence of letters, terminated by a matching ":]" or +".]" or "=]". + +Argument: + ptr pointer to the initial [ + endptr where to return the end pointer + cd pointer to compile data + +Returns: TRUE or FALSE +*/ + +static BOOL +check_posix_syntax(const uschar *ptr, const uschar **endptr, compile_data *cd) +{ +int terminator; /* Don't combine these lines; the Solaris cc */ +terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */ +if (*(++ptr) == '^') ptr++; +while ((cd->ctypes[*ptr] & ctype_letter) != 0) ptr++; +if (*ptr == terminator && ptr[1] == ']') + { + *endptr = ptr; + return TRUE; + } +return FALSE; +} + + + + +/************************************************* +* Check POSIX class name * +*************************************************/ + +/* This function is called to check the name given in a POSIX-style class entry +such as [:alnum:]. + +Arguments: + ptr points to the first letter + len the length of the name + +Returns: a value representing the name, or -1 if unknown +*/ + +static int +check_posix_name(const uschar *ptr, int len) +{ +register int yield = 0; +while (posix_name_lengths[yield] != 0) + { + if (len == posix_name_lengths[yield] && + strncmp((const char *)ptr, posix_names[yield], len) == 0) return yield; + yield++; + } +return -1; +} + + + + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the pattern, compiling it into the code vector. If the options are +changed during the branch, the pointer is used to change the external options +bits. + +Arguments: + optionsptr pointer to the option bits + brackets points to number of extracting brackets used + code points to the pointer to the current code point + ptrptr points to the current pattern pointer + errorptr points to pointer to error message + firstbyteptr set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE) + reqbyteptr set to the last literal character required, else < 0 + bcptr points to current branch chain + cd contains pointers to tables etc. + +Returns: TRUE on success + FALSE, with *errorptr set on error +*/ + +static BOOL +compile_branch(int *optionsptr, int *brackets, uschar **codeptr, + const uschar **ptrptr, const char **errorptr, int *firstbyteptr, + int *reqbyteptr, branch_chain *bcptr, compile_data *cd) +{ +int repeat_type, op_type; +int repeat_min = 0, repeat_max = 0; /* To please picky compilers */ +int bravalue = 0; +int length; +int greedy_default, greedy_non_default; +int firstbyte, reqbyte; +int zeroreqbyte, zerofirstbyte; +int req_caseopt, reqvary, tempreqvary; +int condcount = 0; +int options = *optionsptr; +register int c; +register uschar *code = *codeptr; +uschar *tempcode; +BOOL inescq = FALSE; +BOOL groupsetfirstbyte = FALSE; +const uschar *ptr = *ptrptr; +const uschar *tempptr; +uschar *previous = NULL; +uschar class[32]; + +#ifdef SUPPORT_UTF8 +BOOL class_utf8; +BOOL utf8 = (options & PCRE_UTF8) != 0; +uschar *class_utf8data; +uschar utf8_char[6]; +#else +BOOL utf8 = FALSE; +#endif + +/* Set up the default and non-default settings for greediness */ + +greedy_default = ((options & PCRE_UNGREEDY) != 0); +greedy_non_default = greedy_default ^ 1; + +/* Initialize no first char, no required char. REQ_UNSET means "no char +matching encountered yet". It gets changed to REQ_NONE if we hit something that +matches a non-fixed char first char; reqbyte just remains unset if we never +find one. + +When we hit a repeat whose minimum is zero, we may have to adjust these values +to take the zero repeat into account. This is implemented by setting them to +zerofirstbyte and zeroreqbyte when such a repeat is encountered. The individual +item types that can be repeated set these backoff variables appropriately. */ + +firstbyte = reqbyte = zerofirstbyte = zeroreqbyte = REQ_UNSET; + +/* The variable req_caseopt contains either the REQ_CASELESS value or zero, +according to the current setting of the caseless flag. REQ_CASELESS is a bit +value > 255. It is added into the firstbyte or reqbyte variables to record the +case status of the value. */ + +req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0; + +/* Switch on next character until the end of the branch */ + +for (;; ptr++) + { + BOOL negate_class; + BOOL possessive_quantifier; + int class_charcount; + int class_lastchar; + int newoptions; + int recno; + int skipbytes; + int subreqbyte; + int subfirstbyte; + + c = *ptr; + if (inescq && c != 0) goto NORMAL_CHAR; + + if ((options & PCRE_EXTENDED) != 0) + { + if ((cd->ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + /* The space before the ; is to avoid a warning on a silly compiler + on the Macintosh. */ + while ((c = *(++ptr)) != 0 && c != NEWLINE) ; + if (c != 0) continue; /* Else fall through to handle end of string */ + } + } + + switch(c) + { + /* The branch terminates at end of string, |, or ). */ + + case 0: + case '|': + case ')': + *firstbyteptr = firstbyte; + *reqbyteptr = reqbyte; + *codeptr = code; + *ptrptr = ptr; + return TRUE; + + /* Handle single-character metacharacters. In multiline mode, ^ disables + the setting of any following char as a first character. */ + + case '^': + if ((options & PCRE_MULTILINE) != 0) + { + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + } + previous = NULL; + *code++ = OP_CIRC; + break; + + case '$': + previous = NULL; + *code++ = OP_DOLL; + break; + + /* There can never be a first char if '.' is first, whatever happens about + repeats. The value of reqbyte doesn't change either. */ + + case '.': + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + previous = code; + *code++ = OP_ANY; + break; + + /* Character classes. If the included characters are all < 255 in value, we + build a 32-byte bitmap of the permitted characters, except in the special + case where there is only one such character. For negated classes, we build + the map as usual, then invert it at the end. However, we use a different + opcode so that data characters > 255 can be handled correctly. + + If the class contains characters outside the 0-255 range, a different + opcode is compiled. It may optionally have a bit map for characters < 256, + but those above are are explicitly listed afterwards. A flag byte tells + whether the bitmap is present, and whether this is a negated class or not. + */ + + case '[': + previous = code; + + /* PCRE supports POSIX class stuff inside a class. Perl gives an error if + they are encountered at the top level, so we'll do that too. */ + + if ((ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && + check_posix_syntax(ptr, &tempptr, cd)) + { + *errorptr = (ptr[1] == ':')? ERR13 : ERR31; + goto FAILED; + } + + /* If the first character is '^', set the negation flag and skip it. */ + + if ((c = *(++ptr)) == '^') + { + negate_class = TRUE; + c = *(++ptr); + } + else + { + negate_class = FALSE; + } + + /* Keep a count of chars with values < 256 so that we can optimize the case + of just a single character (as long as it's < 256). For higher valued UTF-8 + characters, we don't yet do any optimization. */ + + class_charcount = 0; + class_lastchar = -1; + +#ifdef SUPPORT_UTF8 + class_utf8 = FALSE; /* No chars >= 256 */ + class_utf8data = code + LINK_SIZE + 34; /* For UTF-8 items */ +#endif + + /* Initialize the 32-char bit map to all zeros. We have to build the + map in a temporary bit of store, in case the class contains only 1 + character (< 256), because in that case the compiled code doesn't use the + bit map. */ + + memset(class, 0, 32 * sizeof(uschar)); + + /* Process characters until ] is reached. By writing this as a "do" it + means that an initial ] is taken as a data character. The first pass + through the regex checked the overall syntax, so we don't need to be very + strict here. At the start of the loop, c contains the first byte of the + character. */ + + do + { +#ifdef SUPPORT_UTF8 + if (utf8 && c > 127) + { /* Braces are required because the */ + GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ + } +#endif + + /* Inside \Q...\E everything is literal except \E */ + + if (inescq) + { + if (c == '\\' && ptr[1] == 'E') + { + inescq = FALSE; + ptr++; + continue; + } + else goto LONE_SINGLE_CHARACTER; + } + + /* Handle POSIX class names. Perl allows a negation extension of the + form [:^name:]. A square bracket that doesn't match the syntax is + treated as a literal. We also recognize the POSIX constructions + [.ch.] and [=ch=] ("collating elements") and fault them, as Perl + 5.6 and 5.8 do. */ + + if (c == '[' && + (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && + check_posix_syntax(ptr, &tempptr, cd)) + { + BOOL local_negate = FALSE; + int posix_class, i; + register const uschar *cbits = cd->cbits; + + if (ptr[1] != ':') + { + *errorptr = ERR31; + goto FAILED; + } + + ptr += 2; + if (*ptr == '^') + { + local_negate = TRUE; + ptr++; + } + + posix_class = check_posix_name(ptr, tempptr - ptr); + if (posix_class < 0) + { + *errorptr = ERR30; + goto FAILED; + } + + /* If matching is caseless, upper and lower are converted to + alpha. This relies on the fact that the class table starts with + alpha, lower, upper as the first 3 entries. */ + + if ((options & PCRE_CASELESS) != 0 && posix_class <= 2) + posix_class = 0; + + /* Or into the map we are building up to 3 of the static class + tables, or their negations. The [:blank:] class sets up the same + chars as the [:space:] class (all white space). We remove the vertical + white space chars afterwards. */ + + posix_class *= 3; + for (i = 0; i < 3; i++) + { + BOOL isblank = strncmp((char *)ptr, "blank", 5) == 0; + int taboffset = posix_class_maps[posix_class + i]; + if (taboffset < 0) break; + if (local_negate) + { + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+taboffset]; + if (isblank) class[1] |= 0x3c; + } + else + { + for (c = 0; c < 32; c++) class[c] |= cbits[c+taboffset]; + if (isblank) class[1] &= ~0x3c; + } + } + + ptr = tempptr + 1; + class_charcount = 10; /* Set > 1; assumes more than 1 per class */ + continue; /* End of POSIX syntax handling */ + } + + /* Backslash may introduce a single character, or it may introduce one + of the specials, which just set a flag. Escaped items are checked for + validity in the pre-compiling pass. The sequence \b is a special case. + Inside a class (and only there) it is treated as backspace. Elsewhere + it marks a word boundary. Other escapes have preset maps ready to + or into the one we are building. We assume they have more than one + character in them, so set class_charcount bigger than one. */ + + if (c == '\\') + { + c = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); + if (-c == ESC_b) c = '\b'; /* \b is backslash in a class */ + + if (-c == ESC_Q) /* Handle start of quoted string */ + { + if (ptr[1] == '\\' && ptr[2] == 'E') + { + ptr += 2; /* avoid empty string */ + } + else inescq = TRUE; + continue; + } + + else if (c < 0) + { + register const uschar *cbits = cd->cbits; + class_charcount = 10; /* Greater than 1 is what matters */ + switch (-c) + { + case ESC_d: + for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_digit]; + continue; + + case ESC_D: + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_digit]; + continue; + + case ESC_w: + for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_word]; + continue; + + case ESC_W: + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_word]; + continue; + + case ESC_s: + for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_space]; + class[1] &= ~0x08; /* Perl 5.004 onwards omits VT from \s */ + continue; + + case ESC_S: + for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_space]; + class[1] |= 0x08; /* Perl 5.004 onwards omits VT from \s */ + continue; + + /* Unrecognized escapes are faulted if PCRE is running in its + strict mode. By default, for compatibility with Perl, they are + treated as literals. */ + + default: + if ((options & PCRE_EXTRA) != 0) + { + *errorptr = ERR7; + goto FAILED; + } + c = *ptr; /* The final character */ + } + } + + /* Fall through if we have a single character (c >= 0). This may be + > 256 in UTF-8 mode. */ + + } /* End of backslash handling */ + + /* A single character may be followed by '-' to form a range. However, + Perl does not permit ']' to be the end of the range. A '-' character + here is treated as a literal. */ + + if (ptr[1] == '-' && ptr[2] != ']') + { + int d; + ptr += 2; + +#ifdef SUPPORT_UTF8 + if (utf8) + { /* Braces are required because the */ + GETCHARLEN(d, ptr, ptr); /* macro generates multiple statements */ + } + else +#endif + d = *ptr; + + /* The second part of a range can be a single-character escape, but + not any of the other escapes. Perl 5.6 treats a hyphen as a literal + in such circumstances. */ + + if (d == '\\') + { + const uschar *oldptr = ptr; + d = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); + + /* \b is backslash; any other special means the '-' was literal */ + + if (d < 0) + { + if (d == -ESC_b) d = '\b'; else + { + ptr = oldptr - 2; + goto LONE_SINGLE_CHARACTER; /* A few lines below */ + } + } + } + + /* Check that the two values are in the correct order */ + + if (d < c) + { + *errorptr = ERR8; + goto FAILED; + } + + /* If d is greater than 255, we can't just use the bit map, so set up + for the UTF-8 supporting class type. If we are not caseless, we can + just set up a single range. If we are caseless, the characters < 256 + are handled with a bitmap, in order to get the case-insensitive + handling. */ + +#ifdef SUPPORT_UTF8 + if (d > 255) + { + class_utf8 = TRUE; + *class_utf8data++ = XCL_RANGE; + if ((options & PCRE_CASELESS) == 0) + { + class_utf8data += ord2utf8(c, class_utf8data); + class_utf8data += ord2utf8(d, class_utf8data); + continue; /* Go get the next char in the class */ + } + class_utf8data += ord2utf8(256, class_utf8data); + class_utf8data += ord2utf8(d, class_utf8data); + d = 255; + /* Fall through */ + } +#endif + /* We use the bit map if the range is entirely < 255, or if part of it + is < 255 and matching is caseless. */ + + for (; c <= d; c++) + { + class[c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + int uc = cd->fcc[c]; /* flip case */ + class[uc/8] |= (1 << (uc&7)); + } + class_charcount++; /* in case a one-char range */ + class_lastchar = c; + } + + continue; /* Go get the next char in the class */ + } + + /* Handle a lone single character - we can get here for a normal + non-escape char, or after \ that introduces a single character. */ + + LONE_SINGLE_CHARACTER: + + /* Handle a multibyte character */ + +#ifdef SUPPORT_UTF8 + if (utf8 && c > 255) + { + class_utf8 = TRUE; + *class_utf8data++ = XCL_SINGLE; + class_utf8data += ord2utf8(c, class_utf8data); + } + else +#endif + /* Handle a single-byte character */ + { + class [c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + c = cd->fcc[c]; /* flip case */ + class[c/8] |= (1 << (c&7)); + } + class_charcount++; + class_lastchar = c; + } + } + + /* Loop until ']' reached; the check for end of string happens inside the + loop. This "while" is the end of the "do" above. */ + + while ((c = *(++ptr)) != ']' || inescq); + + /* If class_charcount is 1, we saw precisely one character with a value < + 256. In UTF-8 mode, we can optimize if there were no characters >= 256 and + the one character is < 128. In non-UTF-8 mode we can always optimize. + + The optimization throws away the bit map. We turn the item into a + 1-character OP_CHARS if it's positive, or OP_NOT if it's negative. Note + that OP_NOT does not support multibyte characters. In the positive case, it + can cause firstbyte to be set. Otherwise, there can be no first char if + this item is first, whatever repeat count may follow. In the case of + reqbyte, save the previous value for reinstating. */ + +#ifdef SUPPORT_UTF8 + if (class_charcount == 1 && + (!utf8 || + (!class_utf8 && class_lastchar < 128))) +#else + if (class_charcount == 1) +#endif + { + zeroreqbyte = reqbyte; + if (negate_class) + { + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + *code++ = OP_NOT; + } + else + { + if (firstbyte == REQ_UNSET) + { + zerofirstbyte = REQ_NONE; + firstbyte = class_lastchar | req_caseopt; + } + else + { + zerofirstbyte = firstbyte; + reqbyte = class_lastchar | req_caseopt | cd->req_varyopt; + } + *code++ = OP_CHARS; + *code++ = 1; + } + *code++ = class_lastchar; + break; /* End of class handling */ + } /* End of 1-byte optimization */ + + /* Otherwise, if this is the first thing in the branch, there can be no + first char setting, whatever the repeat count. Any reqbyte setting must + remain unchanged after any kind of repeat. */ + + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + + /* If there are characters with values > 255, we have to compile an + extended class, with its own opcode. If there are no characters < 256, + we can omit the bitmap. */ + +#ifdef SUPPORT_UTF8 + if (class_utf8) + { + *class_utf8data++ = XCL_END; /* Marks the end of extra data */ + *code++ = OP_XCLASS; + code += LINK_SIZE; + *code = negate_class? XCL_NOT : 0; + + /* If the map is required, install it, and move on to the end of + the extra data */ + + if (class_charcount > 0) + { + *code++ |= XCL_MAP; + memcpy(code, class, 32); + code = class_utf8data; + } + + /* If the map is not required, slide down the extra data. */ + + else + { + int len = class_utf8data - (code + 33); + memmove(code + 1, code + 33, len); + code += len + 1; + } + + /* Now fill in the complete length of the item */ + + PUT(previous, 1, code - previous); + break; /* End of class handling */ + } +#endif + + /* If there are no characters > 255, negate the 32-byte map if necessary, + and copy it into the code vector. If this is the first thing in the branch, + there can be no first char setting, whatever the repeat count. Any reqbyte + setting must remain unchanged after any kind of repeat. */ + + if (negate_class) + { + *code++ = OP_NCLASS; + for (c = 0; c < 32; c++) code[c] = ~class[c]; + } + else + { + *code++ = OP_CLASS; + memcpy(code, class, 32); + } + code += 32; + break; + + /* Various kinds of repeat */ + + case '{': + if (!is_counted_repeat(ptr+1, cd)) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorptr, cd); + if (*errorptr != NULL) goto FAILED; + goto REPEAT; + + case '*': + repeat_min = 0; + repeat_max = -1; + goto REPEAT; + + case '+': + repeat_min = 1; + repeat_max = -1; + goto REPEAT; + + case '?': + repeat_min = 0; + repeat_max = 1; + + REPEAT: + if (previous == NULL) + { + *errorptr = ERR9; + goto FAILED; + } + + if (repeat_min == 0) + { + firstbyte = zerofirstbyte; /* Adjust for zero repeat */ + reqbyte = zeroreqbyte; /* Ditto */ + } + + /* Remember whether this is a variable length repeat */ + + reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; + + op_type = 0; /* Default single-char op codes */ + possessive_quantifier = FALSE; /* Default not possessive quantifier */ + + /* Save start of previous item, in case we have to move it up to make space + for an inserted OP_ONCE for the additional '+' extension. */ + + tempcode = previous; + + /* If the next character is '+', we have a possessive quantifier. This + implies greediness, whatever the setting of the PCRE_UNGREEDY option. + If the next character is '?' this is a minimizing repeat, by default, + but if PCRE_UNGREEDY is set, it works the other way round. We change the + repeat type to the non-default. */ + + if (ptr[1] == '+') + { + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + ptr++; + } + else if (ptr[1] == '?') + { + repeat_type = greedy_non_default; + ptr++; + } + else repeat_type = greedy_default; + + /* If previous was a recursion, we need to wrap it inside brackets so that + it can be replicated if necessary. */ + + if (*previous == OP_RECURSE) + { + memmove(previous + 1 + LINK_SIZE, previous, 1 + LINK_SIZE); + code += 1 + LINK_SIZE; + *previous = OP_BRA; + PUT(previous, 1, code - previous); + *code = OP_KET; + PUT(code, 1, code - previous); + code += 1 + LINK_SIZE; + } + + /* If previous was a string of characters, chop off the last one and use it + as the subject of the repeat. If there was only one character, we can + abolish the previous item altogether. If a one-char item has a minumum of + more than one, ensure that it is set in reqbyte - it might not be if a + sequence such as x{3} is the first thing in a branch because the x will + have gone into firstbyte instead. */ + + if (*previous == OP_CHARS) + { + /* Deal with UTF-8 characters that take up more than one byte. It's + easier to write this out separately than try to macrify it. Use c to + hold the length of the character in bytes, plus 0x80 to flag that it's a + length rather than a small character. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && (code[-1] & 0x80) != 0) + { + uschar *lastchar = code - 1; + while((*lastchar & 0xc0) == 0x80) lastchar--; + c = code - lastchar; /* Length of UTF-8 character */ + memcpy(utf8_char, lastchar, c); /* Save the char */ + if (lastchar == previous + 2) /* There was only one character */ + { + code = previous; /* Abolish the previous item */ + } + else + { + previous[1] -= c; /* Adjust length of previous */ + code = lastchar; /* Lost char off the end */ + tempcode = code; /* Adjust position to be moved for '+' */ + } + c |= 0x80; /* Flag c as a length */ + } + else +#endif + + /* Handle the case of a single byte - either with no UTF8 support, or + with UTF-8 disabled, or for a UTF-8 character < 128. */ + + { + c = *(--code); + if (code == previous + 2) /* There was only one character */ + { + code = previous; /* Abolish the previous item */ + if (repeat_min > 1) reqbyte = c | req_caseopt | cd->req_varyopt; + } + else + { + previous[1]--; /* adjust length */ + tempcode = code; /* Adjust position to be moved for '+' */ + } + } + + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + } + + /* If previous was a single negated character ([^a] or similar), we use + one of the special opcodes, replacing it. The code is shared with single- + character repeats by setting opt_type to add a suitable offset into + repeat_type. OP_NOT is currently used only for single-byte chars. */ + + else if (*previous == OP_NOT) + { + op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ + c = previous[1]; + code = previous; + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by setting op_type to add a suitable offset into repeat_type. */ + + else if (*previous < OP_EODN) + { + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + c = *previous; + code = previous; + + OUTPUT_SINGLE_REPEAT: + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) goto END_REPEAT; + + /* Combine the op_type with the repeat_type */ + + repeat_type += op_type; + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeat_min == 0) + { + if (repeat_max == -1) *code++ = OP_STAR + repeat_type; + else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; + else + { + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + + /* The case {1,} is handled as the special case + */ + + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_PLUS + repeat_type; + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO. An EXACT of 1 is optimized. */ + + else + { + if (repeat_min != 1) + { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + PUT2INC(code, 0, repeat_min); + } + + /* If the mininum is 1 and the previous item was a character string, + we either have to put back the item that got cancelled if the string + length was 1, or add the character back onto the end of a longer + string. For a character type nothing need be done; it will just get + put back naturally. Note that the final character is always going to + get added below, so we leave code ready for its insertion. */ + + else if (*previous == OP_CHARS) + { + if (code == previous) code += 2; else + + /* In UTF-8 mode, a multibyte char has its length in c, with the 0x80 + bit set as a flag. The length will always be between 2 and 6. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && c >= 128) previous[1] += c & 7; else +#endif + previous[1]++; + } + + /* For a single negated character we also have to put back the + item that got cancelled. At present this applies only to single byte + characters in any mode. */ + + else if (*previous == OP_NOT) code++; + + /* If the maximum is unlimited, insert an OP_STAR. Before doing so, + we have to insert the character for the previous code. In UTF-8 mode, + long characters have their length in c, with the 0x80 bit as a flag. */ + + if (repeat_max < 0) + { +#ifdef SUPPORT_UTF8 + if (utf8 && c >= 128) + { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } + else +#endif + *code++ = c; + *code++ = OP_STAR + repeat_type; + } + + /* Else insert an UPTO if the max is greater than the min, again + preceded by the character, for the previously inserted code. */ + + else if (repeat_max != repeat_min) + { +#ifdef SUPPORT_UTF8 + if (utf8 && c >= 128) + { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } + else +#endif + *code++ = c; + repeat_max -= repeat_min; + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + + /* The character or character type itself comes last in all cases. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && c >= 128) + { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } + else +#endif + + *code++ = c; + } + + /* If previous was a character class or a back reference, we put the repeat + stuff after it, but just skip the item if the repeat was {0,0}. */ + + else if (*previous == OP_CLASS || + *previous == OP_NCLASS || +#ifdef SUPPORT_UTF8 + *previous == OP_XCLASS || +#endif + *previous == OP_REF) + { + if (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } + if (repeat_min == 0 && repeat_max == -1) + *code++ = OP_CRSTAR + repeat_type; + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_CRPLUS + repeat_type; + else if (repeat_min == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeat_type; + else + { + *code++ = OP_CRRANGE + repeat_type; + PUT2INC(code, 0, repeat_min); + if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ + PUT2INC(code, 0, repeat_max); + } + } + + /* If previous was a bracket group, we may have to replicate it in certain + cases. */ + + else if (*previous >= OP_BRA || *previous == OP_ONCE || + *previous == OP_COND) + { + register int i; + int ketoffset = 0; + int len = code - previous; + uschar *bralink = NULL; + + /* If the maximum repeat count is unlimited, find the end of the bracket + by scanning through from the start, and compute the offset back to it + from the current code pointer. There may be an OP_OPT setting following + the final KET, so we can't find the end just by going back from the code + pointer. */ + + if (repeat_max == -1) + { + register uschar *ket = previous; + do ket += GET(ket, 1); while (*ket != OP_KET); + ketoffset = code - ket; + } + + /* The case of a zero minimum is special because of the need to stick + OP_BRAZERO in front of it, and because the group appears once in the + data, whereas in other cases it appears the minimum number of times. For + this reason, it is simplest to treat this case separately, as otherwise + the code gets far too messy. There are several special subcases when the + minimum is zero. */ + + if (repeat_min == 0) + { + /* If the maximum is also zero, we just omit the group from the output + altogether. */ + + if (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } + + /* If the maximum is 1 or unlimited, we just have to stick in the + BRAZERO and do no more at this point. */ + + if (repeat_max <= 1) + { + memmove(previous+1, previous, len); + code++; + *previous++ = OP_BRAZERO + repeat_type; + } + + /* If the maximum is greater than 1 and limited, we have to replicate + in a nested fashion, sticking OP_BRAZERO before each set of brackets. + The first one has to be handled carefully because it's the original + copy, which has to be moved up. The remainder can be handled by code + that is common with the non-zero minimum case below. We just have to + adjust the value or repeat_max, since one less copy is required. */ + + else + { + int offset; + memmove(previous + 2 + LINK_SIZE, previous, len); + code += 2 + LINK_SIZE; + *previous++ = OP_BRAZERO + repeat_type; + *previous++ = OP_BRA; + + /* We chain together the bracket offset fields that have to be + filled in later when the ends of the brackets are reached. */ + + offset = (bralink == NULL)? 0 : previous - bralink; + bralink = previous; + PUTINC(previous, 0, offset); + } + + repeat_max--; + } + + /* If the minimum is greater than zero, replicate the group as many + times as necessary, and adjust the maximum to the number of subsequent + copies that we need. If we set a first char from the group, and didn't + set a required char, copy the latter from the former. */ + + else + { + if (repeat_min > 1) + { + if (groupsetfirstbyte && reqbyte < 0) reqbyte = firstbyte; + for (i = 1; i < repeat_min; i++) + { + memcpy(code, previous, len); + code += len; + } + } + if (repeat_max > 0) repeat_max -= repeat_min; + } + + /* This code is common to both the zero and non-zero minimum cases. If + the maximum is limited, it replicates the group in a nested fashion, + remembering the bracket starts on a stack. In the case of a zero minimum, + the first one was set up above. In all cases the repeat_max now specifies + the number of additional copies needed. */ + + if (repeat_max >= 0) + { + for (i = repeat_max - 1; i >= 0; i--) + { + *code++ = OP_BRAZERO + repeat_type; + + /* All but the final copy start a new nesting, maintaining the + chain of brackets outstanding. */ + + if (i != 0) + { + int offset; + *code++ = OP_BRA; + offset = (bralink == NULL)? 0 : code - bralink; + bralink = code; + PUTINC(code, 0, offset); + } + + memcpy(code, previous, len); + code += len; + } + + /* Now chain through the pending brackets, and fill in their length + fields (which are holding the chain links pro tem). */ + + while (bralink != NULL) + { + int oldlinkoffset; + int offset = code - bralink + 1; + uschar *bra = code - offset; + oldlinkoffset = GET(bra, 1); + bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; + *code++ = OP_KET; + PUTINC(code, 0, offset); + PUT(bra, 1, offset); + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. We + can't just offset backwards from the current code point, because we + don't know if there's been an options resetting after the ket. The + correct offset was computed above. */ + + else code[-ketoffset] = OP_KETRMAX + repeat_type; + } + + /* Else there's some kind of shambles */ + + else + { + *errorptr = ERR11; + goto FAILED; + } + + /* If the character following a repeat is '+', we wrap the entire repeated + item inside OP_ONCE brackets. This is just syntactic sugar, taken from + Sun's Java package. The repeated item starts at tempcode, not at previous, + which might be the first part of a string whose (former) last char we + repeated. However, we don't support '+' after a greediness '?'. */ + + if (possessive_quantifier) + { + int len = code - tempcode; + memmove(tempcode + 1+LINK_SIZE, tempcode, len); + code += 1 + LINK_SIZE; + len += 1 + LINK_SIZE; + tempcode[0] = OP_ONCE; + *code++ = OP_KET; + PUTINC(code, 0, len); + PUT(tempcode, 1, len); + } + + /* In all case we no longer have a previous item. We also set the + "follows varying string" flag for subsequently encountered reqbytes if + it isn't already set and we have just passed a varying length item. */ + + END_REPEAT: + previous = NULL; + cd->req_varyopt |= reqvary; + break; + + + /* Start of nested bracket sub-expression, or comment or lookahead or + lookbehind or option setting or condition. First deal with special things + that can come after a bracket; all are introduced by ?, and the appearance + of any of them means that this is not a referencing group. They were + checked for validity in the first pass over the string, so we don't have to + check for syntax errors here. */ + + case '(': + newoptions = options; + skipbytes = 0; + + if (*(++ptr) == '?') + { + int set, unset; + int *optset; + + switch (*(++ptr)) + { + case '#': /* Comment; skip to ket */ + ptr++; + while (*ptr != ')') ptr++; + continue; + + case ':': /* Non-extracting bracket */ + bravalue = OP_BRA; + ptr++; + break; + + case '(': + bravalue = OP_COND; /* Conditional group */ + + /* Condition to test for recursion */ + + if (ptr[1] == 'R') + { + code[1+LINK_SIZE] = OP_CREF; + PUT2(code, 2+LINK_SIZE, CREF_RECURSE); + skipbytes = 3; + ptr += 3; + } + + /* Condition to test for a numbered subpattern match. We know that + if a digit follows ( then there will just be digits until ) because + the syntax was checked in the first pass. */ + + else if ((digitab[ptr[1]] && ctype_digit) != 0) + { + int condref; /* Don't amalgamate; some compilers */ + condref = *(++ptr) - '0'; /* grumble at autoincrement in declaration */ + while (*(++ptr) != ')') condref = condref*10 + *ptr - '0'; + if (condref == 0) + { + *errorptr = ERR35; + goto FAILED; + } + ptr++; + code[1+LINK_SIZE] = OP_CREF; + PUT2(code, 2+LINK_SIZE, condref); + skipbytes = 3; + } + /* For conditions that are assertions, we just fall through, having + set bravalue above. */ + break; + + case '=': /* Positive lookahead */ + bravalue = OP_ASSERT; + ptr++; + break; + + case '!': /* Negative lookahead */ + bravalue = OP_ASSERT_NOT; + ptr++; + break; + + case '<': /* Lookbehinds */ + switch (*(++ptr)) + { + case '=': /* Positive lookbehind */ + bravalue = OP_ASSERTBACK; + ptr++; + break; + + case '!': /* Negative lookbehind */ + bravalue = OP_ASSERTBACK_NOT; + ptr++; + break; + } + break; + + case '>': /* One-time brackets */ + bravalue = OP_ONCE; + ptr++; + break; + + case 'C': /* Callout - may be followed by digits */ + *code++ = OP_CALLOUT; + { + int n = 0; + while ((digitab[*(++ptr)] & ctype_digit) != 0) + n = n * 10 + *ptr - '0'; + if (n > 255) + { + *errorptr = ERR38; + goto FAILED; + } + *code++ = n; + } + previous = NULL; + continue; + + case 'P': /* Named subpattern handling */ + if (*(++ptr) == '<') /* Definition */ + { + int i, namelen; + uschar *slot = cd->name_table; + const uschar *name; /* Don't amalgamate; some compilers */ + name = ++ptr; /* grumble at autoincrement in declaration */ + + while (*ptr++ != '>'); + namelen = ptr - name - 1; + + for (i = 0; i < cd->names_found; i++) + { + int crc = memcmp(name, slot+2, namelen); + if (crc == 0) + { + if (slot[2+namelen] == 0) + { + *errorptr = ERR43; + goto FAILED; + } + crc = -1; /* Current name is substring */ + } + if (crc < 0) + { + memmove(slot + cd->name_entry_size, slot, + (cd->names_found - i) * cd->name_entry_size); + break; + } + slot += cd->name_entry_size; + } + + PUT2(slot, 0, *brackets + 1); + memcpy(slot + 2, name, namelen); + slot[2+namelen] = 0; + cd->names_found++; + goto NUMBERED_GROUP; + } + + if (*ptr == '=' || *ptr == '>') /* Reference or recursion */ + { + int i, namelen; + int type = *ptr++; + const uschar *name = ptr; + uschar *slot = cd->name_table; + + while (*ptr != ')') ptr++; + namelen = ptr - name; + + for (i = 0; i < cd->names_found; i++) + { + if (strncmp((char *)name, (char *)slot+2, namelen) == 0) break; + slot += cd->name_entry_size; + } + if (i >= cd->names_found) + { + *errorptr = ERR15; + goto FAILED; + } + + recno = GET2(slot, 0); + + if (type == '>') goto HANDLE_RECURSION; /* A few lines below */ + + /* Back reference */ + + previous = code; + *code++ = OP_REF; + PUT2INC(code, 0, recno); + cd->backref_map |= (recno < 32)? (1 << recno) : 1; + if (recno > cd->top_backref) cd->top_backref = recno; + continue; + } + + /* Should never happen */ + break; + + case 'R': /* Pattern recursion */ + ptr++; /* Same as (?0) */ + /* Fall through */ + + /* Recursion or "subroutine" call */ + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + const uschar *called; + recno = 0; + while((digitab[*ptr] & ctype_digit) != 0) + recno = recno * 10 + *ptr++ - '0'; + + /* Come here from code above that handles a named recursion */ + + HANDLE_RECURSION: + + previous = code; + + /* Find the bracket that is being referenced. Temporarily end the + regex in case it doesn't exist. */ + + *code = OP_END; + called = (recno == 0)? + cd->start_code : find_bracket(cd->start_code, utf8, recno); + + if (called == NULL) + { + *errorptr = ERR15; + goto FAILED; + } + + /* If the subpattern is still open, this is a recursive call. We + check to see if this is a left recursion that could loop for ever, + and diagnose that case. */ + + if (GET(called, 1) == 0 && could_be_empty(called, code, bcptr, utf8)) + { + *errorptr = ERR40; + goto FAILED; + } + + /* Insert the recursion/subroutine item */ + + *code = OP_RECURSE; + PUT(code, 1, called - cd->start_code); + code += 1 + LINK_SIZE; + } + continue; + + /* Character after (? not specially recognized */ + + default: /* Option setting */ + set = unset = 0; + optset = &set; + + while (*ptr != ')' && *ptr != ':') + { + switch (*ptr++) + { + case '-': optset = &unset; break; + + case 'i': *optset |= PCRE_CASELESS; break; + case 'm': *optset |= PCRE_MULTILINE; break; + case 's': *optset |= PCRE_DOTALL; break; + case 'x': *optset |= PCRE_EXTENDED; break; + case 'U': *optset |= PCRE_UNGREEDY; break; + case 'X': *optset |= PCRE_EXTRA; break; + } + } + + /* Set up the changed option bits, but don't change anything yet. */ + + newoptions = (options | set) & (~unset); + + /* If the options ended with ')' this is not the start of a nested + group with option changes, so the options change at this level. Compile + code to change the ims options if this setting actually changes any of + them. We also pass the new setting back so that it can be put at the + start of any following branches, and when this group ends (if we are in + a group), a resetting item can be compiled. + + Note that if this item is right at the start of the pattern, the + options will have been abstracted and made global, so there will be no + change to compile. */ + + if (*ptr == ')') + { + if ((options & PCRE_IMS) != (newoptions & PCRE_IMS)) + { + *code++ = OP_OPT; + *code++ = newoptions & PCRE_IMS; + } + + /* Change options at this level, and pass them back for use + in subsequent branches. Reset the greedy defaults and the case + value for firstbyte and reqbyte. */ + + *optionsptr = options = newoptions; + greedy_default = ((newoptions & PCRE_UNGREEDY) != 0); + greedy_non_default = greedy_default ^ 1; + req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0; + + previous = NULL; /* This item can't be repeated */ + continue; /* It is complete */ + } + + /* If the options ended with ':' we are heading into a nested group + with possible change of options. Such groups are non-capturing and are + not assertions of any kind. All we need to do is skip over the ':'; + the newoptions value is handled below. */ + + bravalue = OP_BRA; + ptr++; + } + } + + /* If PCRE_NO_AUTO_CAPTURE is set, all unadorned brackets become + non-capturing and behave like (?:...) brackets */ + + else if ((options & PCRE_NO_AUTO_CAPTURE) != 0) + { + bravalue = OP_BRA; + } + + /* Else we have a referencing group; adjust the opcode. If the bracket + number is greater than EXTRACT_BASIC_MAX, we set the opcode one higher, and + arrange for the true number to follow later, in an OP_BRANUMBER item. */ + + else + { + NUMBERED_GROUP: + if (++(*brackets) > EXTRACT_BASIC_MAX) + { + bravalue = OP_BRA + EXTRACT_BASIC_MAX + 1; + code[1+LINK_SIZE] = OP_BRANUMBER; + PUT2(code, 2+LINK_SIZE, *brackets); + skipbytes = 3; + } + else bravalue = OP_BRA + *brackets; + } + + /* Process nested bracketed re. Assertions may not be repeated, but other + kinds can be. We copy code into a non-register variable in order to be able + to pass its address because some compilers complain otherwise. Pass in a + new setting for the ims options if they have changed. */ + + previous = (bravalue >= OP_ONCE)? code : NULL; + *code = bravalue; + tempcode = code; + tempreqvary = cd->req_varyopt; /* Save value before bracket */ + + if (!compile_regex( + newoptions, /* The complete new option state */ + options & PCRE_IMS, /* The previous ims option state */ + brackets, /* Extracting bracket count */ + &tempcode, /* Where to put code (updated) */ + &ptr, /* Input pointer (updated) */ + errorptr, /* Where to put an error message */ + (bravalue == OP_ASSERTBACK || + bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ + skipbytes, /* Skip over OP_COND/OP_BRANUMBER */ + &subfirstbyte, /* For possible first char */ + &subreqbyte, /* For possible last char */ + bcptr, /* Current branch chain */ + cd)) /* Tables block */ + goto FAILED; + + /* At the end of compiling, code is still pointing to the start of the + group, while tempcode has been updated to point past the end of the group + and any option resetting that may follow it. The pattern pointer (ptr) + is on the bracket. */ + + /* If this is a conditional bracket, check that there are no more than + two branches in the group. */ + + else if (bravalue == OP_COND) + { + uschar *tc = code; + condcount = 0; + + do { + condcount++; + tc += GET(tc,1); + } + while (*tc != OP_KET); + + if (condcount > 2) + { + *errorptr = ERR27; + goto FAILED; + } + + /* If there is just one branch, we must not make use of its firstbyte or + reqbyte, because this is equivalent to an empty second branch. */ + + if (condcount == 1) subfirstbyte = subreqbyte = REQ_NONE; + } + + /* Handle updating of the required and first characters. Update for normal + brackets of all kinds, and conditions with two branches (see code above). + If the bracket is followed by a quantifier with zero repeat, we have to + back off. Hence the definition of zeroreqbyte and zerofirstbyte outside the + main loop so that they can be accessed for the back off. */ + + zeroreqbyte = reqbyte; + zerofirstbyte = firstbyte; + groupsetfirstbyte = FALSE; + + if (bravalue >= OP_BRA || bravalue == OP_ONCE || bravalue == OP_COND) + { + /* If we have not yet set a firstbyte in this branch, take it from the + subpattern, remembering that it was set here so that a repeat of more + than one can replicate it as reqbyte if necessary. If the subpattern has + no firstbyte, set "none" for the whole branch. In both cases, a zero + repeat forces firstbyte to "none". */ + + if (firstbyte == REQ_UNSET) + { + if (subfirstbyte >= 0) + { + firstbyte = subfirstbyte; + groupsetfirstbyte = TRUE; + } + else firstbyte = REQ_NONE; + zerofirstbyte = REQ_NONE; + } + + /* If firstbyte was previously set, convert the subpattern's firstbyte + into reqbyte if there wasn't one, using the vary flag that was in + existence beforehand. */ + + else if (subfirstbyte >= 0 && subreqbyte < 0) + subreqbyte = subfirstbyte | tempreqvary; + + /* If the subpattern set a required byte (or set a first byte that isn't + really the first byte - see above), set it. */ + + if (subreqbyte >= 0) reqbyte = subreqbyte; + } + + /* For a forward assertion, we take the reqbyte, if set. This can be + helpful if the pattern that follows the assertion doesn't set a different + char. For example, it's useful for /(?=abcde).+/. We can't set firstbyte + for an assertion, however because it leads to incorrect effect for patterns + such as /(?=a)a.+/ when the "real" "a" would then become a reqbyte instead + of a firstbyte. This is overcome by a scan at the end if there's no + firstbyte, looking for an asserted first char. */ + + else if (bravalue == OP_ASSERT && subreqbyte >= 0) reqbyte = subreqbyte; + + /* Now update the main code pointer to the end of the group. */ + + code = tempcode; + + /* Error if hit end of pattern */ + + if (*ptr != ')') + { + *errorptr = ERR14; + goto FAILED; + } + break; + + /* Check \ for being a real metacharacter; if not, fall through and handle + it as a data character at the start of a string. Escape items are checked + for validity in the pre-compiling pass. */ + + case '\\': + tempptr = ptr; + c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); + + /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values + are arranged to be the negation of the corresponding OP_values. For the + back references, the values are ESC_REF plus the reference number. Only + back references and those types that consume a character may be repeated. + We can test for values between ESC_b and ESC_Z for the latter; this may + have to change if any new ones are ever created. */ + + if (c < 0) + { + if (-c == ESC_Q) /* Handle start of quoted string */ + { + if (ptr[1] == '\\' && ptr[2] == 'E') ptr += 2; /* avoid empty string */ + else inescq = TRUE; + continue; + } + + /* For metasequences that actually match a character, we disable the + setting of a first character if it hasn't already been set. */ + + if (firstbyte == REQ_UNSET && -c > ESC_b && -c < ESC_Z) + firstbyte = REQ_NONE; + + /* Set values to reset to if this is followed by a zero repeat. */ + + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + + /* Back references are handled specially */ + + if (-c >= ESC_REF) + { + int number = -c - ESC_REF; + previous = code; + *code++ = OP_REF; + PUT2INC(code, 0, number); + } + else + { + previous = (-c > ESC_b && -c < ESC_Z)? code : NULL; + *code++ = -c; + } + continue; + } + + /* Data character: reset and fall through */ + + ptr = tempptr; + c = '\\'; + + /* Handle a run of data characters until a metacharacter is encountered. + The first character is guaranteed not to be whitespace or # when the + extended flag is set. */ + + NORMAL_CHAR: + default: + previous = code; + *code = OP_CHARS; + code += 2; + length = 0; + + do + { + /* If in \Q...\E, check for the end; if not, we always have a literal */ + + if (inescq) + { + if (c == '\\' && ptr[1] == 'E') + { + inescq = FALSE; + ptr++; + } + else + { + *code++ = c; + length++; + } + continue; + } + + /* Skip white space and comments for /x patterns */ + + if ((options & PCRE_EXTENDED) != 0) + { + if ((cd->ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + /* The space before the ; is to avoid a warning on a silly compiler + on the Macintosh. */ + while ((c = *(++ptr)) != 0 && c != NEWLINE) ; + if (c == 0) break; + continue; + } + } + + /* Backslash may introduce a data char or a metacharacter. Escaped items + are checked for validity in the pre-compiling pass. Stop the string + before a metaitem. */ + + if (c == '\\') + { + tempptr = ptr; + c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); + if (c < 0) { ptr = tempptr; break; } + + /* If a character is > 127 in UTF-8 mode, we have to turn it into + two or more characters in the UTF-8 encoding. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && c > 127) + { + uschar buffer[8]; + int len = ord2utf8(c, buffer); + for (c = 0; c < len; c++) *code++ = buffer[c]; + length += len; + continue; + } +#endif + } + + /* Ordinary character or single-char escape */ + + *code++ = c; + length++; + } + + /* This "while" is the end of the "do" above. */ + + while (length < MAXLIT && (cd->ctypes[c = *(++ptr)] & ctype_meta) == 0); + + /* Update the first and last requirements. These are always bytes, even in + UTF-8 mode. However, there is a special case to be considered when there + are only one or two characters. Because this gets messy in UTF-8 mode, the + code is kept separate. When we get here "length" contains the number of + bytes. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && length > 1) + { + uschar *t = previous + 3; /* After this code, t */ + while (t < code && (*t & 0xc0) == 0x80) t++; /* follows the 1st char */ + + /* Handle the case when there is only one multibyte character. It must + have at least two bytes because of the "length > 1" test above. */ + + if (t == code) + { + /* If no previous first byte, set it from this character, but revert to + none on a zero repeat. */ + + if (firstbyte == REQ_UNSET) + { + zerofirstbyte = REQ_NONE; + firstbyte = previous[2]; + } + + /* Otherwise, leave the first byte value alone, and don't change it on + a zero repeat */ + + else zerofirstbyte = firstbyte; + + /* In both cases, a zero repeat resets the previous required byte */ + + zeroreqbyte = reqbyte; + } + + /* Handle the case when there is more than one character. These may be + single-byte or multibyte characters */ + + else + { + t = code - 1; /* After this code, t is at the */ + while ((*t & 0xc0) == 0x80) t--; /* start of the last character */ + + /* If no previous first byte, set it from the first character, and + retain it on a zero repeat (of the last character). The required byte + is reset on a zero repeat, either to the byte before the last + character, unless this is the first byte of the string. In that case, + it reverts to its previous value. */ + + if (firstbyte == REQ_UNSET) + { + zerofirstbyte = firstbyte = previous[2] | req_caseopt; + zeroreqbyte = (t - 1 == previous + 2)? + reqbyte : t[-1] | req_caseopt | cd->req_varyopt; + } + + /* If there was a previous first byte, leave it alone, and don't change + it on a zero repeat. The required byte is reset on a zero repeat to the + byte before the last character. */ + + else + { + zerofirstbyte = firstbyte; + zeroreqbyte = t[-1] | req_caseopt | cd->req_varyopt; + } + } + + /* In all cases (we know length > 1), the new required byte is the last + byte of the string. */ + + reqbyte = code[-1] | req_caseopt | cd->req_varyopt; + } + + else /* End of UTF-8 coding */ +#endif + + /* This is the code for non-UTF-8 operation, either without UTF-8 support, + or when UTF-8 is not enabled. */ + + { + /* firstbyte was not previously set; take it from this string */ + + if (firstbyte == REQ_UNSET) + { + if (length == 1) + { + zerofirstbyte = REQ_NONE; + firstbyte = previous[2] | req_caseopt; + zeroreqbyte = reqbyte; + } + else + { + zerofirstbyte = firstbyte = previous[2] | req_caseopt; + zeroreqbyte = (length > 2)? + (code[-2] | req_caseopt | cd->req_varyopt) : reqbyte; + reqbyte = code[-1] | req_caseopt | cd->req_varyopt; + } + } + + /* firstbyte was previously set */ + + else + { + zerofirstbyte = firstbyte; + zeroreqbyte = (length == 1)? reqbyte : + code[-2] | req_caseopt | cd->req_varyopt; + reqbyte = code[-1] | req_caseopt | cd->req_varyopt; + } + } + + /* Set the length in the data vector, and advance to the next state. */ + + previous[1] = length; + if (length < MAXLIT) ptr--; + break; + } + } /* end of big loop */ + +/* Control never reaches here by falling through, only by a goto for all the +error states. Pass back the position in the pattern so that it can be displayed +to the user for diagnosing the error. */ + +FAILED: +*ptrptr = ptr; +return FALSE; +} + + + + +/************************************************* +* Compile sequence of alternatives * +*************************************************/ + +/* On entry, ptr is pointing past the bracket character, but on return +it points to the closing bracket, or vertical bar, or end of string. +The code variable is pointing at the byte into which the BRA operator has been +stored. If the ims options are changed at the start (for a (?ims: group) or +during any branch, we need to insert an OP_OPT item at the start of every +following branch to ensure they get set correctly at run time, and also pass +the new options into every subsequent branch compile. + +Argument: + options option bits, including any changes for this subpattern + oldims previous settings of ims option bits + brackets -> int containing the number of extracting brackets used + codeptr -> the address of the current code pointer + ptrptr -> the address of the current pattern pointer + errorptr -> pointer to error message + lookbehind TRUE if this is a lookbehind assertion + skipbytes skip this many bytes at start (for OP_COND, OP_BRANUMBER) + firstbyteptr place to put the first required character, or a negative number + reqbyteptr place to put the last required character, or a negative number + bcptr pointer to the chain of currently open branches + cd points to the data block with tables pointers etc. + +Returns: TRUE on success +*/ + +static BOOL +compile_regex(int options, int oldims, int *brackets, uschar **codeptr, + const uschar **ptrptr, const char **errorptr, BOOL lookbehind, int skipbytes, + int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, compile_data *cd) +{ +const uschar *ptr = *ptrptr; +uschar *code = *codeptr; +uschar *last_branch = code; +uschar *start_bracket = code; +uschar *reverse_count = NULL; +int firstbyte, reqbyte; +int branchfirstbyte, branchreqbyte; +branch_chain bc; + +bc.outer = bcptr; +bc.current = code; + +firstbyte = reqbyte = REQ_UNSET; + +/* Offset is set zero to mark that this bracket is still open */ + +PUT(code, 1, 0); +code += 1 + LINK_SIZE + skipbytes; + +/* Loop for each alternative branch */ + +for (;;) + { + /* Handle a change of ims options at the start of the branch */ + + if ((options & PCRE_IMS) != oldims) + { + *code++ = OP_OPT; + *code++ = options & PCRE_IMS; + } + + /* Set up dummy OP_REVERSE if lookbehind assertion */ + + if (lookbehind) + { + *code++ = OP_REVERSE; + reverse_count = code; + PUTINC(code, 0, 0); + } + + /* Now compile the branch */ + + if (!compile_branch(&options, brackets, &code, &ptr, errorptr, + &branchfirstbyte, &branchreqbyte, &bc, cd)) + { + *ptrptr = ptr; + return FALSE; + } + + /* If this is the first branch, the firstbyte and reqbyte values for the + branch become the values for the regex. */ + + if (*last_branch != OP_ALT) + { + firstbyte = branchfirstbyte; + reqbyte = branchreqbyte; + } + + /* If this is not the first branch, the first char and reqbyte have to + match the values from all the previous branches, except that if the previous + value for reqbyte didn't have REQ_VARY set, it can still match, and we set + REQ_VARY for the regex. */ + + else + { + /* If we previously had a firstbyte, but it doesn't match the new branch, + we have to abandon the firstbyte for the regex, but if there was previously + no reqbyte, it takes on the value of the old firstbyte. */ + + if (firstbyte >= 0 && firstbyte != branchfirstbyte) + { + if (reqbyte < 0) reqbyte = firstbyte; + firstbyte = REQ_NONE; + } + + /* If we (now or from before) have no firstbyte, a firstbyte from the + branch becomes a reqbyte if there isn't a branch reqbyte. */ + + if (firstbyte < 0 && branchfirstbyte >= 0 && branchreqbyte < 0) + branchreqbyte = branchfirstbyte; + + /* Now ensure that the reqbytes match */ + + if ((reqbyte & ~REQ_VARY) != (branchreqbyte & ~REQ_VARY)) + reqbyte = REQ_NONE; + else reqbyte |= branchreqbyte; /* To "or" REQ_VARY */ + } + + /* If lookbehind, check that this branch matches a fixed-length string, + and put the length into the OP_REVERSE item. Temporarily mark the end of + the branch with OP_END. */ + + if (lookbehind) + { + int length; + *code = OP_END; + length = find_fixedlength(last_branch, options); + DPRINTF(("fixed length = %d\n", length)); + if (length < 0) + { + *errorptr = (length == -2)? ERR36 : ERR25; + *ptrptr = ptr; + return FALSE; + } + PUT(reverse_count, 0, length); + } + + /* Reached end of expression, either ')' or end of pattern. Go back through + the alternative branches and reverse the chain of offsets, with the field in + the BRA item now becoming an offset to the first alternative. If there are + no alternatives, it points to the end of the group. The length in the + terminating ket is always the length of the whole bracketed item. If any of + the ims options were changed inside the group, compile a resetting op-code + following, except at the very end of the pattern. Return leaving the pointer + at the terminating char. */ + + if (*ptr != '|') + { + int length = code - last_branch; + do + { + int prev_length = GET(last_branch, 1); + PUT(last_branch, 1, length); + length = prev_length; + last_branch -= length; + } + while (length > 0); + + /* Fill in the ket */ + + *code = OP_KET; + PUT(code, 1, code - start_bracket); + code += 1 + LINK_SIZE; + + /* Resetting option if needed */ + + if ((options & PCRE_IMS) != oldims && *ptr == ')') + { + *code++ = OP_OPT; + *code++ = oldims; + } + + /* Set values to pass back */ + + *codeptr = code; + *ptrptr = ptr; + *firstbyteptr = firstbyte; + *reqbyteptr = reqbyte; + return TRUE; + } + + /* Another branch follows; insert an "or" node. Its length field points back + to the previous branch while the bracket remains open. At the end the chain + is reversed. It's done like this so that the start of the bracket has a + zero offset until it is closed, making it possible to detect recursion. */ + + *code = OP_ALT; + PUT(code, 1, code - last_branch); + bc.current = last_branch = code; + code += 1 + LINK_SIZE; + ptr++; + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Check for anchored expression * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket +all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then +it's anchored. However, if this is a multiline pattern, then only OP_SOD +counts, since OP_CIRC can match in the middle. + +We can also consider a regex to be anchored if OP_SOM starts all its branches. +This is the code for \G, which means "match at start of match position, taking +into account the match offset". + +A branch is also implicitly anchored if it starts with .* and DOTALL is set, +because that will try the rest of the pattern at all possible matching points, +so there is no point trying again.... er .... + +.... except when the .* appears inside capturing parentheses, and there is a +subsequent back reference to those parentheses. We haven't enough information +to catch that case precisely. + +At first, the best we could do was to detect when .* was in capturing brackets +and the highest back reference was greater than or equal to that level. +However, by keeping a bitmap of the first 31 back references, we can catch some +of the more common cases more precisely. + +Arguments: + code points to start of expression (the bracket) + options points to the options setting + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + backref_map the back reference bitmap + +Returns: TRUE or FALSE +*/ + +static BOOL +is_anchored(register const uschar *code, int *options, unsigned int bracket_map, + unsigned int backref_map) +{ +do { + const uschar *scode = + first_significant_code(code + 1+LINK_SIZE, options, PCRE_MULTILINE); + register int op = *scode; + + /* Capturing brackets */ + + if (op > OP_BRA) + { + int new_map; + op -= OP_BRA; + if (op > EXTRACT_BASIC_MAX) op = GET2(scode, 2+LINK_SIZE); + new_map = bracket_map | ((op < 32)? (1 << op) : 1); + if (!is_anchored(scode, options, new_map, backref_map)) return FALSE; + } + + /* Other brackets */ + + else if (op == OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) + { + if (!is_anchored(scode, options, bracket_map, backref_map)) return FALSE; + } + + /* .* is not anchored unless DOTALL is set and it isn't in brackets that + are or may be referenced. */ + + else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR) && + (*options & PCRE_DOTALL) != 0) + { + if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE; + } + + /* Check for explicit anchoring */ + + else if (op != OP_SOD && op != OP_SOM && + ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC)) + return FALSE; + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Check for starting with ^ or .* * +*************************************************/ + +/* This is called to find out if every branch starts with ^ or .* so that +"first char" processing can be done to speed things up in multiline +matching and for non-DOTALL patterns that start with .* (which must start at +the beginning or after \n). As in the case of is_anchored() (see above), we +have to take account of back references to capturing brackets that contain .* +because in that case we can't make the assumption. + +Arguments: + code points to start of expression (the bracket) + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + backref_map the back reference bitmap + +Returns: TRUE or FALSE +*/ + +static BOOL +is_startline(const uschar *code, unsigned int bracket_map, + unsigned int backref_map) +{ +do { + const uschar *scode = first_significant_code(code + 1+LINK_SIZE, NULL, 0); + register int op = *scode; + + /* Capturing brackets */ + + if (op > OP_BRA) + { + int new_map; + op -= OP_BRA; + if (op > EXTRACT_BASIC_MAX) op = GET2(scode, 2+LINK_SIZE); + new_map = bracket_map | ((op < 32)? (1 << op) : 1); + if (!is_startline(scode, new_map, backref_map)) return FALSE; + } + + /* Other brackets */ + + else if (op == OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) + { if (!is_startline(scode, bracket_map, backref_map)) return FALSE; } + + /* .* is not anchored unless DOTALL is set and it isn't in brackets that + may be referenced. */ + + else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR) + { + if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE; + } + + /* Check for explicit circumflex */ + + else if (op != OP_CIRC) return FALSE; + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Check for asserted fixed first char * +*************************************************/ + +/* During compilation, the "first char" settings from forward assertions are +discarded, because they can cause conflicts with actual literals that follow. +However, if we end up without a first char setting for an unanchored pattern, +it is worth scanning the regex to see if there is an initial asserted first +char. If all branches start with the same asserted char, or with a bracket all +of whose alternatives start with the same asserted char (recurse ad lib), then +we return that char, otherwise -1. + +Arguments: + code points to start of expression (the bracket) + options pointer to the options (used to check casing changes) + inassert TRUE if in an assertion + +Returns: -1 or the fixed first char +*/ + +static int +find_firstassertedchar(const uschar *code, int *options, BOOL inassert) +{ +register int c = -1; +do { + int d; + const uschar *scode = + first_significant_code(code + 1+LINK_SIZE, options, PCRE_CASELESS); + register int op = *scode; + + if (op >= OP_BRA) op = OP_BRA; + + switch(op) + { + default: + return -1; + + case OP_BRA: + case OP_ASSERT: + case OP_ONCE: + case OP_COND: + if ((d = find_firstassertedchar(scode, options, op == OP_ASSERT)) < 0) + return -1; + if (c < 0) c = d; else if (c != d) return -1; + break; + + case OP_EXACT: /* Fall through */ + scode++; + + case OP_CHARS: /* Fall through */ + scode++; + + case OP_PLUS: + case OP_MINPLUS: + if (!inassert) return -1; + if (c < 0) + { + c = scode[1]; + if ((*options & PCRE_CASELESS) != 0) c |= REQ_CASELESS; + } + else if (c != scode[1]) return -1; + break; + } + + code += GET(code, 1); + } +while (*code == OP_ALT); +return c; +} + + + + +/************************************************* +* Compile a Regular Expression * +*************************************************/ + +/* This function takes a string and returns a pointer to a block of store +holding a compiled version of the expression. + +Arguments: + pattern the regular expression + options various option bits + errorptr pointer to pointer to error text + erroroffset ptr offset in pattern where error was detected + tables pointer to character tables or NULL + +Returns: pointer to compiled data block, or NULL on error, + with errorptr and erroroffset set +*/ + +pcre * +pcre_compile(const char *pattern, int options, const char **errorptr, + int *erroroffset, const unsigned char *tables) +{ +real_pcre *re; +int length = 1 + LINK_SIZE; /* For initial BRA plus length */ +int runlength; +int c, firstbyte, reqbyte; +int bracount = 0; +int branch_extra = 0; +int branch_newextra; +int item_count = -1; +int name_count = 0; +int max_name_size = 0; +#ifdef SUPPORT_UTF8 +int lastcharlength = 0; +BOOL utf8; +BOOL class_utf8; +#endif +BOOL inescq = FALSE; +unsigned int brastackptr = 0; +size_t size; +uschar *code; +const uschar *codestart; +const uschar *ptr; +compile_data compile_block; +int brastack[BRASTACK_SIZE]; +uschar bralenstack[BRASTACK_SIZE]; + +/* We can't pass back an error message if errorptr is NULL; I guess the best we +can do is just return NULL. */ + +if (errorptr == NULL) return NULL; +*errorptr = NULL; + +/* However, we can give a message for this error */ + +if (erroroffset == NULL) + { + *errorptr = ERR16; + return NULL; + } +*erroroffset = 0; + +/* Can't support UTF8 unless PCRE has been compiled to include the code. */ + +#ifdef SUPPORT_UTF8 +utf8 = (options & PCRE_UTF8) != 0; +#else +if ((options & PCRE_UTF8) != 0) + { + *errorptr = ERR32; + return NULL; + } +#endif + +if ((options & ~PUBLIC_OPTIONS) != 0) + { + *errorptr = ERR17; + return NULL; + } + +/* Set up pointers to the individual character tables */ + +if (tables == NULL) tables = pcre_default_tables; +compile_block.lcc = tables + lcc_offset; +compile_block.fcc = tables + fcc_offset; +compile_block.cbits = tables + cbits_offset; +compile_block.ctypes = tables + ctypes_offset; + +/* Maximum back reference and backref bitmap. This is updated for numeric +references during the first pass, but for named references during the actual +compile pass. The bitmap records up to 31 back references to help in deciding +whether (.*) can be treated as anchored or not. */ + +compile_block.top_backref = 0; +compile_block.backref_map = 0; + +/* Reflect pattern for debugging output */ + +DPRINTF(("------------------------------------------------------------------\n")); +DPRINTF(("%s\n", pattern)); + +/* The first thing to do is to make a pass over the pattern to compute the +amount of store required to hold the compiled code. This does not have to be +perfect as long as errors are overestimates. At the same time we can detect any +flag settings right at the start, and extract them. Make an attempt to correct +for any counted white space if an "extended" flag setting appears late in the +pattern. We can't be so clever for #-comments. */ + +ptr = (const uschar *)(pattern - 1); +while ((c = *(++ptr)) != 0) + { + int min, max; + int class_optcount; + int bracket_length; + int duplength; + + /* If we are inside a \Q...\E sequence, all chars are literal */ + + if (inescq) goto NORMAL_CHAR; + + /* Otherwise, first check for ignored whitespace and comments */ + + if ((options & PCRE_EXTENDED) != 0) + { + if ((compile_block.ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + /* The space before the ; is to avoid a warning on a silly compiler + on the Macintosh. */ + while ((c = *(++ptr)) != 0 && c != NEWLINE) ; + if (c == 0) break; + continue; + } + } + + item_count++; /* Is zero for the first non-comment item */ + + switch(c) + { + /* A backslashed item may be an escaped "normal" character or a + character type. For a "normal" character, put the pointers and + character back so that tests for whitespace etc. in the input + are done correctly. */ + + case '\\': + { + const uschar *save_ptr = ptr; + c = check_escape(&ptr, errorptr, bracount, options, FALSE, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (c >= 0) + { + ptr = save_ptr; + c = '\\'; + goto NORMAL_CHAR; + } + } + + /* If \Q, enter "literal" mode */ + + if (-c == ESC_Q) + { + inescq = TRUE; + continue; + } + + /* Other escapes need one byte, and are of length one for repeats */ + + length++; +#ifdef SUPPORT_UTF8 + lastcharlength = 1; +#endif + + /* A back reference needs an additional 2 bytes, plus either one or 5 + bytes for a repeat. We also need to keep the value of the highest + back reference. */ + + if (c <= -ESC_REF) + { + int refnum = -c - ESC_REF; + compile_block.backref_map |= (refnum < 32)? (1 << refnum) : 1; + if (refnum > compile_block.top_backref) + compile_block.top_backref = refnum; + length += 2; /* For single back reference */ + if (ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block)) + { + ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else length += 5; + if (ptr[1] == '?') ptr++; + } + } + continue; + + case '^': /* Single-byte metacharacters */ + case '.': + case '$': + length++; +#ifdef SUPPORT_UTF8 + lastcharlength = 1; +#endif + continue; + + case '*': /* These repeats won't be after brackets; */ + case '+': /* those are handled separately */ + case '?': + length++; + goto POSESSIVE; /* A few lines below */ + + /* This covers the cases of braced repeats after a single char, metachar, + class, or back reference. */ + + case '{': + if (!is_counted_repeat(ptr+1, &compile_block)) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + + /* These special cases just insert one extra opcode */ + + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + + /* These cases might insert additional copies of a preceding character. */ + + else + { +#ifdef SUPPORT_UTF8 + /* In UTF-8 mode, we should find the length in lastcharlength */ + if (utf8) + { + if (min != 1) + { + length -= lastcharlength; /* Uncount the original char or metachar */ + if (min > 0) length += 3 + lastcharlength; + } + length += lastcharlength + ((max > 0)? 3 : 1); + } + else +#endif + + /* Not UTF-8 mode: all characters are one byte */ + { + if (min != 1) + { + length--; /* Uncount the original char or metachar */ + if (min > 0) length += 4; + } + + length += (max > 0)? 4 : 2; + } + } + + if (ptr[1] == '?') ptr++; /* Needs no extra length */ + + POSESSIVE: /* Test for possessive quantifier */ + if (ptr[1] == '+') + { + ptr++; + length += 2 + 2*LINK_SIZE; /* Allow for atomic brackets */ + } + continue; + + /* An alternation contains an offset to the next branch or ket. If any ims + options changed in the previous branch(es), and/or if we are in a + lookbehind assertion, extra space will be needed at the start of the + branch. This is handled by branch_extra. */ + + case '|': + length += 1 + LINK_SIZE + branch_extra; + continue; + + /* A character class uses 33 characters provided that all the character + values are less than 256. Otherwise, it uses a bit map for low valued + characters, and individual items for others. Don't worry about character + types that aren't allowed in classes - they'll get picked up during the + compile. A character class that contains only one single-byte character + uses 2 or 3 bytes, depending on whether it is negated or not. Notice this + where we can. (In UTF-8 mode we can do this only for chars < 128.) */ + + case '[': + class_optcount = 0; + +#ifdef SUPPORT_UTF8 + class_utf8 = FALSE; +#endif + + if (*(++ptr) == '^') ptr++; + + /* Written as a "do" so that an initial ']' is taken as data */ + + if (*ptr != 0) do + { + /* Inside \Q...\E everything is literal except \E */ + + if (inescq) + { + if (*ptr != '\\' || ptr[1] != 'E') goto NON_SPECIAL_CHARACTER; + inescq = FALSE; + ptr += 1; + continue; + } + + /* Outside \Q...\E, check for escapes */ + + if (*ptr == '\\') + { +#ifdef SUPPORT_UTF8 + int prevchar = ptr[-1]; +#endif + int ch = check_escape(&ptr, errorptr, bracount, options, TRUE, + &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + + /* \b is backspace inside a class */ + + if (-ch == ESC_b) ch = '\b'; + + /* \Q enters quoting mode */ + + if (-ch == ESC_Q) + { + inescq = TRUE; + continue; + } + + /* Handle escapes that turn into characters */ + + if (ch >= 0) + { +#ifdef SUPPORT_UTF8 + if (utf8) + { + if (ch > 127) class_optcount = 10; /* Ensure > 1 */ + if (ch > 255) + { + uschar buffer[6]; + if (!class_utf8) + { + class_utf8 = TRUE; + length += LINK_SIZE + 1 + 1; + } + length += 1 + ord2utf8(ch, buffer); + + /* If this wide character is preceded by '-', add an extra 2 to + the length in case the previous character was < 128, because in + this case the whole range will be put into the list. */ + + if (prevchar == '-') length += 2; + } + } +#endif + class_optcount++; /* for possible optimization */ + } + else class_optcount = 10; /* \d, \s etc; make sure > 1 */ + } + + /* Check the syntax for POSIX stuff. The bits we actually handle are + checked during the real compile phase. */ + + else if (*ptr == '[' && check_posix_syntax(ptr, &ptr, &compile_block)) + { + ptr++; + class_optcount = 10; /* Make sure > 1 */ + } + + /* Anything else just increments the possible optimization count. If + there are wide characters, we are going to have to use an XCLASS. */ + + else + { + NON_SPECIAL_CHARACTER: + class_optcount++; + +#ifdef SUPPORT_UTF8 + if (utf8) + { + int ch; + int extra = 0; + GETCHARLEN(ch, ptr, extra); + if (ch > 127) class_optcount = 10; /* No optimization possible */ + if (ch > 255) + { + if (!class_utf8) + { + class_utf8 = TRUE; + length += LINK_SIZE + 1 + 1; + } + length += 2 + extra; + + /* If this wide character is preceded by '-', add an extra 2 to + the length in case the previous character was < 128, because in + this case the whole range will be put into the list. */ + + if (ptr[-1] == '-') length += 2; + + /* Advance to the end of this character */ + + ptr += extra; + } + } +#endif + } + } + while (*(++ptr) != 0 && (inescq || *ptr != ']')); /* Concludes "do" above */ + + if (*ptr == 0) /* Missing terminating ']' */ + { + *errorptr = ERR6; + goto PCRE_ERROR_RETURN; + } + + /* We can optimize when there was only one optimizable character. Repeats + for positive and negated single one-byte chars are handled by the general + code. Here, we handle repeats for the class opcodes. */ + + if (class_optcount == 1) length += 3; else + { + length += 33; + + /* A repeat needs either 1 or 5 bytes. */ + + if (*ptr != 0 && ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block)) + { + ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else length += 5; + if (ptr[1] == '?') ptr++; + } + } + continue; + + /* Brackets may be genuine groups or special things */ + + case '(': + branch_newextra = 0; + bracket_length = 1 + LINK_SIZE; + + /* Handle special forms of bracket, which all start (? */ + + if (ptr[1] == '?') + { + int set, unset; + int *optset; + + switch (c = ptr[2]) + { + /* Skip over comments entirely */ + case '#': + ptr += 3; + while (*ptr != 0 && *ptr != ')') ptr++; + if (*ptr == 0) + { + *errorptr = ERR18; + goto PCRE_ERROR_RETURN; + } + continue; + + /* Non-referencing groups and lookaheads just move the pointer on, and + then behave like a non-special bracket, except that they don't increment + the count of extracting brackets. Ditto for the "once only" bracket, + which is in Perl from version 5.005. */ + + case ':': + case '=': + case '!': + case '>': + ptr += 2; + break; + + /* (?R) specifies a recursive call to the regex, which is an extension + to provide the facility which can be obtained by (?p{perl-code}) in + Perl 5.6. In Perl 5.8 this has become (??{perl-code}). + + From PCRE 4.00, items such as (?3) specify subroutine-like "calls" to + the appropriate numbered brackets. This includes both recursive and + non-recursive calls. (?R) is now synonymous with (?0). */ + + case 'R': + ptr++; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + ptr += 2; + if (c != 'R') + while ((digitab[*(++ptr)] & ctype_digit) != 0); + if (*ptr != ')') + { + *errorptr = ERR29; + goto PCRE_ERROR_RETURN; + } + length += 1 + LINK_SIZE; + + /* If this item is quantified, it will get wrapped inside brackets so + as to use the code for quantified brackets. We jump down and use the + code that handles this for real brackets. */ + + if (ptr[1] == '+' || ptr[1] == '*' || ptr[1] == '?' || ptr[1] == '{') + { + length += 2 + 2 * LINK_SIZE; /* to make bracketed */ + duplength = 5 + 3 * LINK_SIZE; + goto HANDLE_QUANTIFIED_BRACKETS; + } + continue; + + /* (?C) is an extension which provides "callout" - to provide a bit of + the functionality of the Perl (?{...}) feature. An optional number may + follow (default is zero). */ + + case 'C': + ptr += 2; + while ((digitab[*(++ptr)] & ctype_digit) != 0); + if (*ptr != ')') + { + *errorptr = ERR39; + goto PCRE_ERROR_RETURN; + } + length += 2; + continue; + + /* Named subpatterns are an extension copied from Python */ + + case 'P': + ptr += 3; + if (*ptr == '<') + { + const uschar *p; /* Don't amalgamate; some compilers */ + p = ++ptr; /* grumble at autoincrement in declaration */ + while ((compile_block.ctypes[*ptr] & ctype_word) != 0) ptr++; + if (*ptr != '>') + { + *errorptr = ERR42; + goto PCRE_ERROR_RETURN; + } + name_count++; + if (ptr - p > max_name_size) max_name_size = (ptr - p); + break; + } + + if (*ptr == '=' || *ptr == '>') + { + while ((compile_block.ctypes[*(++ptr)] & ctype_word) != 0); + if (*ptr != ')') + { + *errorptr = ERR42; + goto PCRE_ERROR_RETURN; + } + break; + } + + /* Unknown character after (?P */ + + *errorptr = ERR41; + goto PCRE_ERROR_RETURN; + + /* Lookbehinds are in Perl from version 5.005 */ + + case '<': + ptr += 3; + if (*ptr == '=' || *ptr == '!') + { + branch_newextra = 1 + LINK_SIZE; + length += 1 + LINK_SIZE; /* For the first branch */ + break; + } + *errorptr = ERR24; + goto PCRE_ERROR_RETURN; + + /* Conditionals are in Perl from version 5.005. The bracket must either + be followed by a number (for bracket reference) or by an assertion + group, or (a PCRE extension) by 'R' for a recursion test. */ + + case '(': + if (ptr[3] == 'R' && ptr[4] == ')') + { + ptr += 4; + length += 3; + } + else if ((digitab[ptr[3]] & ctype_digit) != 0) + { + ptr += 4; + length += 3; + while ((digitab[*ptr] & ctype_digit) != 0) ptr++; + if (*ptr != ')') + { + *errorptr = ERR26; + goto PCRE_ERROR_RETURN; + } + } + else /* An assertion must follow */ + { + ptr++; /* Can treat like ':' as far as spacing is concerned */ + if (ptr[2] != '?' || + (ptr[3] != '=' && ptr[3] != '!' && ptr[3] != '<') ) + { + ptr += 2; /* To get right offset in message */ + *errorptr = ERR28; + goto PCRE_ERROR_RETURN; + } + } + break; + + /* Else loop checking valid options until ) is met. Anything else is an + error. If we are without any brackets, i.e. at top level, the settings + act as if specified in the options, so massage the options immediately. + This is for backward compatibility with Perl 5.004. */ + + default: + set = unset = 0; + optset = &set; + ptr += 2; + + for (;; ptr++) + { + c = *ptr; + switch (c) + { + case 'i': + *optset |= PCRE_CASELESS; + continue; + + case 'm': + *optset |= PCRE_MULTILINE; + continue; + + case 's': + *optset |= PCRE_DOTALL; + continue; + + case 'x': + *optset |= PCRE_EXTENDED; + continue; + + case 'X': + *optset |= PCRE_EXTRA; + continue; + + case 'U': + *optset |= PCRE_UNGREEDY; + continue; + + case '-': + optset = &unset; + continue; + + /* A termination by ')' indicates an options-setting-only item; if + this is at the very start of the pattern (indicated by item_count + being zero), we use it to set the global options. This is helpful + when analyzing the pattern for first characters, etc. Otherwise + nothing is done here and it is handled during the compiling + process. + + [Historical note: Up to Perl 5.8, options settings at top level + were always global settings, wherever they appeared in the pattern. + That is, they were equivalent to an external setting. From 5.8 + onwards, they apply only to what follows (which is what you might + expect).] */ + + case ')': + if (item_count == 0) + { + options = (options | set) & (~unset); + set = unset = 0; /* To save length */ + item_count--; /* To allow for several */ + } + + /* Fall through */ + + /* A termination by ':' indicates the start of a nested group with + the given options set. This is again handled at compile time, but + we must allow for compiled space if any of the ims options are + set. We also have to allow for resetting space at the end of + the group, which is why 4 is added to the length and not just 2. + If there are several changes of options within the same group, this + will lead to an over-estimate on the length, but this shouldn't + matter very much. We also have to allow for resetting options at + the start of any alternations, which we do by setting + branch_newextra to 2. Finally, we record whether the case-dependent + flag ever changes within the regex. This is used by the "required + character" code. */ + + case ':': + if (((set|unset) & PCRE_IMS) != 0) + { + length += 4; + branch_newextra = 2; + if (((set|unset) & PCRE_CASELESS) != 0) options |= PCRE_ICHANGED; + } + goto END_OPTIONS; + + /* Unrecognized option character */ + + default: + *errorptr = ERR12; + goto PCRE_ERROR_RETURN; + } + } + + /* If we hit a closing bracket, that's it - this is a freestanding + option-setting. We need to ensure that branch_extra is updated if + necessary. The only values branch_newextra can have here are 0 or 2. + If the value is 2, then branch_extra must either be 2 or 5, depending + on whether this is a lookbehind group or not. */ + + END_OPTIONS: + if (c == ')') + { + if (branch_newextra == 2 && + (branch_extra == 0 || branch_extra == 1+LINK_SIZE)) + branch_extra += branch_newextra; + continue; + } + + /* If options were terminated by ':' control comes here. Fall through + to handle the group below. */ + } + } + + /* Extracting brackets must be counted so we can process escapes in a + Perlish way. If the number exceeds EXTRACT_BASIC_MAX we are going to + need an additional 3 bytes of store per extracting bracket. However, if + PCRE_NO_AUTO)CAPTURE is set, unadorned brackets become non-capturing, so we + must leave the count alone (it will aways be zero). */ + + else if ((options & PCRE_NO_AUTO_CAPTURE) == 0) + { + bracount++; + if (bracount > EXTRACT_BASIC_MAX) bracket_length += 3; + } + + /* Save length for computing whole length at end if there's a repeat that + requires duplication of the group. Also save the current value of + branch_extra, and start the new group with the new value. If non-zero, this + will either be 2 for a (?imsx: group, or 3 for a lookbehind assertion. */ + + if (brastackptr >= sizeof(brastack)/sizeof(int)) + { + *errorptr = ERR19; + goto PCRE_ERROR_RETURN; + } + + bralenstack[brastackptr] = branch_extra; + branch_extra = branch_newextra; + + brastack[brastackptr++] = length; + length += bracket_length; + continue; + + /* Handle ket. Look for subsequent max/min; for certain sets of values we + have to replicate this bracket up to that many times. If brastackptr is + 0 this is an unmatched bracket which will generate an error, but take care + not to try to access brastack[-1] when computing the length and restoring + the branch_extra value. */ + + case ')': + length += 1 + LINK_SIZE; + if (brastackptr > 0) + { + duplength = length - brastack[--brastackptr]; + branch_extra = bralenstack[brastackptr]; + } + else duplength = 0; + + /* The following code is also used when a recursion such as (?3) is + followed by a quantifier, because in that case, it has to be wrapped inside + brackets so that the quantifier works. The value of duplength must be + set before arrival. */ + + HANDLE_QUANTIFIED_BRACKETS: + + /* Leave ptr at the final char; for read_repeat_counts this happens + automatically; for the others we need an increment. */ + + if ((c = ptr[1]) == '{' && is_counted_repeat(ptr+2, &compile_block)) + { + ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + } + else if (c == '*') { min = 0; max = -1; ptr++; } + else if (c == '+') { min = 1; max = -1; ptr++; } + else if (c == '?') { min = 0; max = 1; ptr++; } + else { min = 1; max = 1; } + + /* If the minimum is zero, we have to allow for an OP_BRAZERO before the + group, and if the maximum is greater than zero, we have to replicate + maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting + bracket set. */ + + if (min == 0) + { + length++; + if (max > 0) length += (max - 1) * (duplength + 3 + 2*LINK_SIZE); + } + + /* When the minimum is greater than zero, we have to replicate up to + minval-1 times, with no additions required in the copies. Then, if there + is a limited maximum we have to replicate up to maxval-1 times allowing + for a BRAZERO item before each optional copy and nesting brackets for all + but one of the optional copies. */ + + else + { + length += (min - 1) * duplength; + if (max > min) /* Need this test as max=-1 means no limit */ + length += (max - min) * (duplength + 3 + 2*LINK_SIZE) + - (2 + 2*LINK_SIZE); + } + + /* Allow space for once brackets for "possessive quantifier" */ + + if (ptr[1] == '+') + { + ptr++; + length += 2 + 2*LINK_SIZE; + } + continue; + + /* Non-special character. For a run of such characters the length required + is the number of characters + 2, except that the maximum run length is + MAXLIT. We won't get a skipped space or a non-data escape or the start of a + # comment as the first character, so the length can't be zero. */ + + NORMAL_CHAR: + default: + length += 2; + runlength = 0; + do + { +#ifdef SUPPORT_UTF8 + lastcharlength = 1; /* Need length of last char for UTF-8 repeats */ +#endif + + /* If in a \Q...\E sequence, check for end; otherwise it's a literal */ + if (inescq) + { + if (c == '\\' && ptr[1] == 'E') + { + inescq = FALSE; + ptr++; + } + else runlength++; + continue; + } + + /* Skip whitespace and comments for /x */ + + if ((options & PCRE_EXTENDED) != 0) + { + if ((compile_block.ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + /* The space before the ; is to avoid a warning on a silly compiler + on the Macintosh. */ + while ((c = *(++ptr)) != 0 && c != NEWLINE) ; + continue; + } + } + + /* Backslash may introduce a data char or a metacharacter; stop the + string before the latter. */ + + if (c == '\\') + { + const uschar *saveptr = ptr; + c = check_escape(&ptr, errorptr, bracount, options, FALSE, + &compile_block); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (c < 0) { ptr = saveptr; break; } + + /* In UTF-8 mode, add on the number of additional bytes needed to + encode this character, and save the total length in case this is a + final char that is repeated. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && c > 127) + { + int i; + for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++) + if (c <= utf8_table1[i]) break; + runlength += i; + lastcharlength += i; + } +#endif + } + + /* Ordinary character or single-char escape */ + + runlength++; + } + + /* This "while" is the end of the "do" above. */ + + while (runlength < MAXLIT && + (compile_block.ctypes[c = *(++ptr)] & ctype_meta) == 0); + + /* If we hit a meta-character, back off to point to it */ + + if (runlength < MAXLIT) ptr--; + + /* If the last char in the string is a UTF-8 multibyte character, we must + set lastcharlength correctly. If it was specified as an escape, this will + already have been done above. However, we also have to support in-line + UTF-8 characters, so check backwards from where we are. */ + +#ifdef SUPPORT_UTF8 + if (utf8) + { + const uschar *lastptr = ptr - 1; + if ((*lastptr & 0x80) != 0) + { + while((*lastptr & 0xc0) == 0x80) lastptr--; + lastcharlength = ptr - lastptr; + } + } +#endif + + length += runlength; + continue; + } + } + +length += 2 + LINK_SIZE; /* For final KET and END */ + +if (length > MAX_PATTERN_SIZE) + { + *errorptr = ERR20; + return NULL; + } + +/* Compute the size of data block needed and get it, either from malloc or +externally provided function. */ + +size = length + sizeof(real_pcre) + name_count * (max_name_size + 3); +re = (real_pcre *)(pcre_malloc)(size); + +if (re == NULL) + { + *errorptr = ERR21; + return NULL; + } + +/* Put in the magic number, and save the size, options, and table pointer */ + +re->magic_number = MAGIC_NUMBER; +re->size = size; +re->options = options; +re->tables = tables; +re->name_entry_size = max_name_size + 3; +re->name_count = name_count; + +/* The starting points of the name/number translation table and of the code are +passed around in the compile data block. */ + +compile_block.names_found = 0; +compile_block.name_entry_size = max_name_size + 3; +compile_block.name_table = (uschar *)re + sizeof(real_pcre); +codestart = compile_block.name_table + re->name_entry_size * re->name_count; +compile_block.start_code = codestart; +compile_block.req_varyopt = 0; + +/* Set up a starting, non-extracting bracket, then compile the expression. On +error, *errorptr will be set non-NULL, so we don't need to look at the result +of the function here. */ + +ptr = (const uschar *)pattern; +code = (uschar *)codestart; +*code = OP_BRA; +bracount = 0; +(void)compile_regex(options, options & PCRE_IMS, &bracount, &code, &ptr, + errorptr, FALSE, 0, &firstbyte, &reqbyte, NULL, &compile_block); +re->top_bracket = bracount; +re->top_backref = compile_block.top_backref; + +/* If not reached end of pattern on success, there's an excess bracket. */ + +if (*errorptr == NULL && *ptr != 0) *errorptr = ERR22; + +/* Fill in the terminating state and check for disastrous overflow, but +if debugging, leave the test till after things are printed out. */ + +*code++ = OP_END; + +#ifndef PCREDEBUG +if (code - codestart > length) *errorptr = ERR23; +#endif + +/* Give an error if there's back reference to a non-existent capturing +subpattern. */ + +if (re->top_backref > re->top_bracket) *errorptr = ERR15; + +/* Failed to compile, or error while post-processing */ + +if (*errorptr != NULL) + { + (pcre_free)(re); + PCRE_ERROR_RETURN: + *erroroffset = ptr - (const uschar *)pattern; + return NULL; + } + +/* If the anchored option was not passed, set the flag if we can determine that +the pattern is anchored by virtue of ^ characters or \A or anything else (such +as starting with .* when DOTALL is set). + +Otherwise, if we know what the first character has to be, save it, because that +speeds up unanchored matches no end. If not, see if we can set the +PCRE_STARTLINE flag. This is helpful for multiline matches when all branches +start with ^. and also when all branches start with .* for non-DOTALL matches. +*/ + +if ((options & PCRE_ANCHORED) == 0) + { + int temp_options = options; + if (is_anchored(codestart, &temp_options, 0, compile_block.backref_map)) + re->options |= PCRE_ANCHORED; + else + { + if (firstbyte < 0) + firstbyte = find_firstassertedchar(codestart, &temp_options, FALSE); + if (firstbyte >= 0) /* Remove caseless flag for non-caseable chars */ + { + int ch = firstbyte & 255; + re->first_byte = ((firstbyte & REQ_CASELESS) != 0 && + compile_block.fcc[ch] == ch)? ch : firstbyte; + re->options |= PCRE_FIRSTSET; + } + else if (is_startline(codestart, 0, compile_block.backref_map)) + re->options |= PCRE_STARTLINE; + } + } + +/* For an anchored pattern, we use the "required byte" only if it follows a +variable length item in the regex. Remove the caseless flag for non-caseable +chars. */ + +if (reqbyte >= 0 && + ((re->options & PCRE_ANCHORED) == 0 || (reqbyte & REQ_VARY) != 0)) + { + int ch = reqbyte & 255; + re->req_byte = ((reqbyte & REQ_CASELESS) != 0 && + compile_block.fcc[ch] == ch)? (reqbyte & ~REQ_CASELESS) : reqbyte; + re->options |= PCRE_REQCHSET; + } + +/* Print out the compiled data for debugging */ + +#ifdef PCREDEBUG + +printf("Length = %d top_bracket = %d top_backref = %d\n", + length, re->top_bracket, re->top_backref); + +if (re->options != 0) + { + printf("%s%s%s%s%s%s%s%s%s\n", + ((re->options & PCRE_ANCHORED) != 0)? "anchored " : "", + ((re->options & PCRE_CASELESS) != 0)? "caseless " : "", + ((re->options & PCRE_ICHANGED) != 0)? "case state changed " : "", + ((re->options & PCRE_EXTENDED) != 0)? "extended " : "", + ((re->options & PCRE_MULTILINE) != 0)? "multiline " : "", + ((re->options & PCRE_DOTALL) != 0)? "dotall " : "", + ((re->options & PCRE_DOLLAR_ENDONLY) != 0)? "endonly " : "", + ((re->options & PCRE_EXTRA) != 0)? "extra " : "", + ((re->options & PCRE_UNGREEDY) != 0)? "ungreedy " : ""); + } + +if ((re->options & PCRE_FIRSTSET) != 0) + { + int ch = re->first_byte & 255; + char *caseless = ((re->first_byte & REQ_CASELESS) == 0)? "" : " (caseless)"; + if (isprint(ch)) printf("First char = %c%s\n", ch, caseless); + else printf("First char = \\x%02x%s\n", ch, caseless); + } + +if ((re->options & PCRE_REQCHSET) != 0) + { + int ch = re->req_byte & 255; + char *caseless = ((re->req_byte & REQ_CASELESS) == 0)? "" : " (caseless)"; + if (isprint(ch)) printf("Req char = %c%s\n", ch, caseless); + else printf("Req char = \\x%02x%s\n", ch, caseless); + } + +print_internals(re, stdout); + +/* This check is done here in the debugging case so that the code that +was compiled can be seen. */ + +if (code - codestart > length) + { + *errorptr = ERR23; + (pcre_free)(re); + *erroroffset = ptr - (uschar *)pattern; + return NULL; + } +#endif + +return (pcre *)re; +} + + + +/************************************************* +* Match a back-reference * +*************************************************/ + +/* If a back reference hasn't been set, the length that is passed is greater +than the number of characters left in the string, so the match fails. + +Arguments: + offset index into the offset vector + eptr points into the subject + length length to be matched + md points to match data block + ims the ims flags + +Returns: TRUE if matched +*/ + +static BOOL +match_ref(int offset, register const uschar *eptr, int length, match_data *md, + unsigned long int ims) +{ +const uschar *p = md->start_subject + md->offset_vector[offset]; + +#ifdef PCREDEBUG +if (eptr >= md->end_subject) + printf("matching subject "); +else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + } +printf(" against backref "); +pchars(p, length, FALSE, md); +printf("\n"); +#endif + +/* Always fail if not enough characters left */ + +if (length > md->end_subject - eptr) return FALSE; + +/* Separate the caselesss case for speed */ + +if ((ims & PCRE_CASELESS) != 0) + { + while (length-- > 0) + if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE; + } +else + { while (length-- > 0) if (*p++ != *eptr++) return FALSE; } + +return TRUE; +} + + +#ifdef SUPPORT_UTF8 +/************************************************* +* Match character against an XCLASS * +*************************************************/ + +/* This function is called from within the XCLASS code below, to match a +character against an extended class which might match values > 255. + +Arguments: + c the character + data points to the flag byte of the XCLASS data + +Returns: TRUE if character matches, else FALSE +*/ + +static BOOL +match_xclass(int c, const uschar *data) +{ +int t; +BOOL negated = (*data & XCL_NOT) != 0; + +/* Character values < 256 are matched against a bitmap, if one is present. If +not, we still carry on, because there may be ranges that start below 256 in the +additional data. */ + +if (c < 256) + { + if ((*data & XCL_MAP) != 0 && (data[1 + c/8] & (1 << (c&7))) != 0) + return !negated; /* char found */ + } + +/* Now match against the list of large chars or ranges that end with a large +char. First skip the bit map if present. */ + +if ((*data++ & XCL_MAP) != 0) data += 32; + +while ((t = *data++) != XCL_END) + { + int x, y; + GETCHARINC(x, data); + if (t == XCL_SINGLE) + { + if (c == x) return !negated; + } + else + { + GETCHARINC(y, data); + if (c >= x && c <= y) return !negated; + } + } + +return negated; /* char was not found */ +} +#endif + + + + +/************************************************* +* Match from current position * +*************************************************/ + +/* On entry ecode points to the first opcode, and eptr to the first character +in the subject string, while eptrb holds the value of eptr at the start of the +last bracketed group - used for breaking infinite loops matching zero-length +strings. This function is called recursively in many circumstances. Whenever it +returns a negative (error) response, the outer incarnation must also return the +same response. + +Performance note: It might be tempting to extract commonly used fields from the +md structure (e.g. utf8, end_subject) into individual variables to improve +performance. Tests using gcc on a SPARC disproved this; in the first case, it +made performance worse. + +Arguments: + eptr pointer in subject + ecode position in code + offset_top current top pointer + md pointer to "static" info for the match + ims current /i, /m, and /s options + eptrb pointer to chain of blocks containing eptr at start of + brackets - for testing for empty matches + flags can contain + match_condassert - this is an assertion condition + match_isgroup - this is the start of a bracketed group + +Returns: MATCH_MATCH if matched ) these values are >= 0 + MATCH_NOMATCH if failed to match ) + a negative PCRE_ERROR_xxx value if aborted by an error condition + (e.g. stopped by recursion limit) +*/ + +static int +match(register const uschar *eptr, register const uschar *ecode, + int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb, + int flags) +{ +unsigned long int original_ims = ims; /* Save for resetting on ')' */ +register int rrc; +eptrblock newptrb; + +if (md->match_call_count++ >= md->match_limit) return PCRE_ERROR_MATCHLIMIT; + +/* At the start of a bracketed group, add the current subject pointer to the +stack of such pointers, to be re-instated at the end of the group when we hit +the closing ket. When match() is called in other circumstances, we don't add to +the stack. */ + +if ((flags & match_isgroup) != 0) + { + newptrb.prev = eptrb; + newptrb.saved_eptr = eptr; + eptrb = &newptrb; + } + +/* Now start processing the operations. */ + +for (;;) + { + int op = (int)*ecode; + int min, max, ctype; + register int i; + register int c; + BOOL minimize = FALSE; + + /* Opening capturing bracket. If there is space in the offset vector, save + the current subject position in the working slot at the top of the vector. We + mustn't change the current values of the data slot, because they may be set + from a previous iteration of this group, and be referred to by a reference + inside the group. + + If the bracket fails to match, we need to restore this value and also the + values of the final offsets, in case they were set by a previous iteration of + the same bracket. + + If there isn't enough space in the offset vector, treat this as if it were a + non-capturing bracket. Don't worry about setting the flag for the error case + here; that is handled in the code for KET. */ + + if (op > OP_BRA) + { + int offset; + int number = op - OP_BRA; + + /* For extended extraction brackets (large number), we have to fish out the + number from a dummy opcode at the start. */ + + if (number > EXTRACT_BASIC_MAX) + number = GET2(ecode, 2+LINK_SIZE); + offset = number << 1; + +#ifdef PCREDEBUG + printf("start bracket %d subject=", number); + pchars(eptr, 16, TRUE, md); + printf("\n"); +#endif + + if (offset < md->offset_max) + { + int save_offset1 = md->offset_vector[offset]; + int save_offset2 = md->offset_vector[offset+1]; + int save_offset3 = md->offset_vector[md->offset_end - number]; + int save_capture_last = md->capture_last; + + DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); + md->offset_vector[md->offset_end - number] = eptr - md->start_subject; + + do + { + if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, + eptrb, match_isgroup)) != MATCH_NOMATCH) return rrc; + md->capture_last = save_capture_last; + ecode += GET(ecode, 1); + } + while (*ecode == OP_ALT); + + DPRINTF(("bracket %d failed\n", number)); + + md->offset_vector[offset] = save_offset1; + md->offset_vector[offset+1] = save_offset2; + md->offset_vector[md->offset_end - number] = save_offset3; + + return MATCH_NOMATCH; + } + + /* Insufficient room for saving captured contents */ + + else op = OP_BRA; + } + + /* Other types of node can be handled by a switch */ + + switch(op) + { + case OP_BRA: /* Non-capturing bracket: optimized */ + DPRINTF(("start bracket 0\n")); + do + { + if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, + match_isgroup)) != MATCH_NOMATCH) return rrc; + ecode += GET(ecode, 1); + } + while (*ecode == OP_ALT); + DPRINTF(("bracket 0 failed\n")); + return MATCH_NOMATCH; + + /* Conditional group: compilation checked that there are no more than + two branches. If the condition is false, skipping the first branch takes us + past the end if there is only one branch, but that's OK because that is + exactly what going to the ket would do. */ + + case OP_COND: + if (ecode[LINK_SIZE+1] == OP_CREF) /* Condition extract or recurse test */ + { + int offset = GET2(ecode, LINK_SIZE+2) << 1; /* Doubled ref number */ + BOOL condition = (offset == CREF_RECURSE * 2)? + (md->recursive != NULL) : + (offset < offset_top && md->offset_vector[offset] >= 0); + return match(eptr, ecode + (condition? + (LINK_SIZE + 4) : (LINK_SIZE + 1 + GET(ecode, 1))), + offset_top, md, ims, eptrb, match_isgroup); + } + + /* The condition is an assertion. Call match() to evaluate it - setting + the final argument TRUE causes it to stop at the end of an assertion. */ + + else + { + if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, + match_condassert | match_isgroup)) == MATCH_MATCH) + { + ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE+2); + while (*ecode == OP_ALT) ecode += GET(ecode, 1); + } + else if (rrc != MATCH_NOMATCH) return rrc; + else ecode += GET(ecode, 1); + return match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, + match_isgroup); + } + /* Control never reaches here */ + + /* Skip over conditional reference or large extraction number data if + encountered. */ + + case OP_CREF: + case OP_BRANUMBER: + ecode += 3; + break; + + /* End of the pattern. If we are in a recursion, we should restore the + offsets appropriately and continue from after the call. */ + + case OP_END: + if (md->recursive != NULL && md->recursive->group_num == 0) + { + recursion_info *rec = md->recursive; + DPRINTF(("Hit the end in a (?0) recursion\n")); + md->recursive = rec->prev; + memmove(md->offset_vector, rec->offset_save, + rec->saved_max * sizeof(int)); + md->start_match = rec->save_start; + ims = original_ims; + ecode = rec->after_call; + break; + } + + /* Otherwise, if PCRE_NOTEMPTY is set, fail if we have matched an empty + string - backtracking will then try other alternatives, if any. */ + + if (md->notempty && eptr == md->start_match) return MATCH_NOMATCH; + md->end_match_ptr = eptr; /* Record where we ended */ + md->end_offset_top = offset_top; /* and how many extracts were taken */ + return MATCH_MATCH; + + /* Change option settings */ + + case OP_OPT: + ims = ecode[1]; + ecode += 2; + DPRINTF(("ims set to %02lx\n", ims)); + break; + + /* Assertion brackets. Check the alternative branches in turn - the + matching won't pass the KET for an assertion. If any one branch matches, + the assertion is true. Lookbehind assertions have an OP_REVERSE item at the + start of each branch to move the current point backwards, so the code at + this level is identical to the lookahead case. */ + + case OP_ASSERT: + case OP_ASSERTBACK: + do + { + if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, + match_isgroup)) == MATCH_MATCH) break; + if (rrc != MATCH_NOMATCH) return rrc; + ecode += GET(ecode, 1); + } + while (*ecode == OP_ALT); + if (*ecode == OP_KET) return MATCH_NOMATCH; + + /* If checking an assertion for a condition, return MATCH_MATCH. */ + + if ((flags & match_condassert) != 0) return MATCH_MATCH; + + /* Continue from after the assertion, updating the offsets high water + mark, since extracts may have been taken during the assertion. */ + + do ecode += GET(ecode,1); while (*ecode == OP_ALT); + ecode += 1 + LINK_SIZE; + offset_top = md->end_offset_top; + continue; + + /* Negative assertion: all branches must fail to match */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK_NOT: + do + { + if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, + match_isgroup)) == MATCH_MATCH) return MATCH_NOMATCH; + if (rrc != MATCH_NOMATCH) return rrc; + ecode += GET(ecode,1); + } + while (*ecode == OP_ALT); + + if ((flags & match_condassert) != 0) return MATCH_MATCH; + + ecode += 1 + LINK_SIZE; + continue; + + /* Move the subject pointer back. This occurs only at the start of + each branch of a lookbehind assertion. If we are too close to the start to + move back, this match function fails. When working with UTF-8 we move + back a number of characters, not bytes. */ + + case OP_REVERSE: +#ifdef SUPPORT_UTF8 + if (md->utf8) + { + c = GET(ecode,1); + for (i = 0; i < c; i++) + { + eptr--; + if (eptr < md->start_subject) return MATCH_NOMATCH; + BACKCHAR(eptr) + } + } + else +#endif + + /* No UTF-8 support, or not in UTF-8 mode: count is byte count */ + + { + eptr -= GET(ecode,1); + if (eptr < md->start_subject) return MATCH_NOMATCH; + } + + /* Skip to next op code */ + + ecode += 1 + LINK_SIZE; + break; + + /* The callout item calls an external function, if one is provided, passing + details of the match so far. This is mainly for debugging, though the + function is able to force a failure. */ + + case OP_CALLOUT: + if (pcre_callout != NULL) + { + pcre_callout_block cb; + cb.version = 0; /* Version 0 of the callout block */ + cb.callout_number = ecode[1]; + cb.offset_vector = md->offset_vector; + cb.subject = (const char *)md->start_subject; + cb.subject_length = md->end_subject - md->start_subject; + cb.start_match = md->start_match - md->start_subject; + cb.current_position = eptr - md->start_subject; + cb.capture_top = offset_top/2; + cb.capture_last = md->capture_last; + cb.callout_data = md->callout_data; + if ((rrc = (*pcre_callout)(&cb)) > 0) return MATCH_NOMATCH; + if (rrc < 0) return rrc; + } + ecode += 2; + break; + + /* Recursion either matches the current regex, or some subexpression. The + offset data is the offset to the starting bracket from the start of the + whole pattern. However, it is possible that a BRAZERO was inserted before + this bracket after we took the offset - we just skip it if encountered. + + If there are any capturing brackets started but not finished, we have to + save their starting points and reinstate them after the recursion. However, + we don't know how many such there are (offset_top records the completed + total) so we just have to save all the potential data. There may be up to + 65535 such values, which is too large to put on the stack, but using malloc + for small numbers seems expensive. As a compromise, the stack is used when + there are no more than REC_STACK_SAVE_MAX values to store; otherwise malloc + is used. A problem is what to do if the malloc fails ... there is no way of + returning to the top level with an error. Save the top REC_STACK_SAVE_MAX + values on the stack, and accept that the rest may be wrong. + + There are also other values that have to be saved. We use a chained + sequence of blocks that actually live on the stack. Thanks to Robin Houston + for the original version of this logic. */ + + case OP_RECURSE: + { + int stacksave[REC_STACK_SAVE_MAX]; + recursion_info new_recursive; + const uschar *callpat = md->start_code + GET(ecode, 1); + + if (*callpat == OP_BRAZERO) callpat++; + + new_recursive.group_num = *callpat - OP_BRA; + + /* For extended extraction brackets (large number), we have to fish out + the number from a dummy opcode at the start. */ + + if (new_recursive.group_num > EXTRACT_BASIC_MAX) + new_recursive.group_num = GET2(callpat, 2+LINK_SIZE); + + /* Add to "recursing stack" */ + + new_recursive.prev = md->recursive; + md->recursive = &new_recursive; + + /* Find where to continue from afterwards */ + + ecode += 1 + LINK_SIZE; + new_recursive.after_call = ecode; + + /* Now save the offset data. */ + + new_recursive.saved_max = md->offset_end; + if (new_recursive.saved_max <= REC_STACK_SAVE_MAX) + new_recursive.offset_save = stacksave; + else + { + new_recursive.offset_save = + (int *)(pcre_malloc)(new_recursive.saved_max * sizeof(int)); + if (new_recursive.offset_save == NULL) return PCRE_ERROR_NOMEMORY; + } + + memcpy(new_recursive.offset_save, md->offset_vector, + new_recursive.saved_max * sizeof(int)); + new_recursive.save_start = md->start_match; + md->start_match = eptr; + + /* OK, now we can do the recursion. For each top-level alternative we + restore the offset and recursion data. */ + + DPRINTF(("Recursing into group %d\n", new_recursive.group_num)); + do + { + if ((rrc = match(eptr, callpat + 1 + LINK_SIZE, offset_top, md, ims, + eptrb, match_isgroup)) == MATCH_MATCH) + { + md->recursive = new_recursive.prev; + if (new_recursive.offset_save != stacksave) + (pcre_free)(new_recursive.offset_save); + return MATCH_MATCH; + } + else if (rrc != MATCH_NOMATCH) return rrc; + + md->recursive = &new_recursive; + memcpy(md->offset_vector, new_recursive.offset_save, + new_recursive.saved_max * sizeof(int)); + callpat += GET(callpat, 1); + } + while (*callpat == OP_ALT); + + DPRINTF(("Recursion didn't match\n")); + md->recursive = new_recursive.prev; + if (new_recursive.offset_save != stacksave) + (pcre_free)(new_recursive.offset_save); + return MATCH_NOMATCH; + } + /* Control never reaches here */ + + /* "Once" brackets are like assertion brackets except that after a match, + the point in the subject string is not moved back. Thus there can never be + a move back into the brackets. Friedl calls these "atomic" subpatterns. + Check the alternative branches in turn - the matching won't pass the KET + for this kind of subpattern. If any one branch matches, we carry on as at + the end of a normal bracket, leaving the subject pointer. */ + + case OP_ONCE: + { + const uschar *prev = ecode; + const uschar *saved_eptr = eptr; + + do + { + if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, + eptrb, match_isgroup)) == MATCH_MATCH) break; + if (rrc != MATCH_NOMATCH) return rrc; + ecode += GET(ecode,1); + } + while (*ecode == OP_ALT); + + /* If hit the end of the group (which could be repeated), fail */ + + if (*ecode != OP_ONCE && *ecode != OP_ALT) return MATCH_NOMATCH; + + /* Continue as from after the assertion, updating the offsets high water + mark, since extracts may have been taken. */ + + do ecode += GET(ecode,1); while (*ecode == OP_ALT); + + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*ecode == OP_KET || eptr == saved_eptr) + { + ecode += 1+LINK_SIZE; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. We need to reset any options + that changed within the bracket before re-running it, so check the next + opcode. */ + + if (ecode[1+LINK_SIZE] == OP_OPT) + { + ims = (ims & ~PCRE_IMS) | ecode[4]; + DPRINTF(("ims set to %02lx at group repeat\n", ims)); + } + + if (*ecode == OP_KETRMIN) + { + if ((rrc = match(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, + eptrb, 0)) != MATCH_NOMATCH) return rrc; + if ((rrc = match(eptr, prev, offset_top, md, ims, eptrb, + match_isgroup)) != MATCH_NOMATCH) return rrc; + } + else /* OP_KETRMAX */ + { + if ((rrc = match(eptr, prev, offset_top, md, ims, eptrb, + match_isgroup)) != MATCH_NOMATCH) return rrc; + if ((rrc = match(eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, + 0)) != MATCH_NOMATCH) return rrc; + } + } + return MATCH_NOMATCH; + + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group and go to there. */ + + case OP_ALT: + do ecode += GET(ecode,1); while (*ecode == OP_ALT); + break; + + /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating + that it may occur zero times. It may repeat infinitely, or not at all - + i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper + repeat limits are compiled as a number of copies, with the optional ones + preceded by BRAZERO or BRAMINZERO. */ + + case OP_BRAZERO: + { + const uschar *next = ecode+1; + if ((rrc = match(eptr, next, offset_top, md, ims, eptrb, match_isgroup)) + != MATCH_NOMATCH) return rrc; + do next += GET(next,1); while (*next == OP_ALT); + ecode = next + 1+LINK_SIZE; + } + break; + + case OP_BRAMINZERO: + { + const uschar *next = ecode+1; + do next += GET(next,1); while (*next == OP_ALT); + if ((rrc = match(eptr, next + 1+LINK_SIZE, offset_top, md, ims, eptrb, + match_isgroup)) != MATCH_NOMATCH) return rrc; + ecode++; + } + break; + + /* End of a group, repeated or non-repeating. If we are at the end of + an assertion "group", stop matching and return MATCH_MATCH, but record the + current high water mark for use by positive assertions. Do this also + for the "once" (not-backup up) groups. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + { + const uschar *prev = ecode - GET(ecode, 1); + const uschar *saved_eptr = eptrb->saved_eptr; + + eptrb = eptrb->prev; /* Back up the stack of bracket start pointers */ + + if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || + *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT || + *prev == OP_ONCE) + { + md->end_match_ptr = eptr; /* For ONCE */ + md->end_offset_top = offset_top; + return MATCH_MATCH; + } + + /* In all other cases except a conditional group we have to check the + group number back at the start and if necessary complete handling an + extraction by setting the offsets and bumping the high water mark. */ + + if (*prev != OP_COND) + { + int offset; + int number = *prev - OP_BRA; + + /* For extended extraction brackets (large number), we have to fish out + the number from a dummy opcode at the start. */ + + if (number > EXTRACT_BASIC_MAX) number = GET2(prev, 2+LINK_SIZE); + offset = number << 1; + +#ifdef PCREDEBUG + printf("end bracket %d", number); + printf("\n"); +#endif + + /* Test for a numbered group. This includes groups called as a result + of recursion. Note that whole-pattern recursion is coded as a recurse + into group 0, so it won't be picked up here. Instead, we catch it when + the OP_END is reached. */ + + if (number > 0) + { + md->capture_last = number; + if (offset >= md->offset_max) md->offset_overflow = TRUE; else + { + md->offset_vector[offset] = + md->offset_vector[md->offset_end - number]; + md->offset_vector[offset+1] = eptr - md->start_subject; + if (offset_top <= offset) offset_top = offset + 2; + } + + /* Handle a recursively called group. Restore the offsets + appropriately and continue from after the call. */ + + if (md->recursive != NULL && md->recursive->group_num == number) + { + recursion_info *rec = md->recursive; + DPRINTF(("Recursion (%d) succeeded - continuing\n", number)); + md->recursive = rec->prev; + md->start_match = rec->save_start; + memcpy(md->offset_vector, rec->offset_save, + rec->saved_max * sizeof(int)); + ecode = rec->after_call; + ims = original_ims; + break; + } + } + } + + /* Reset the value of the ims flags, in case they got changed during + the group. */ + + ims = original_ims; + DPRINTF(("ims reset to %02lx\n", ims)); + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*ecode == OP_KET || eptr == saved_eptr) + { + ecode += 1 + LINK_SIZE; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. */ + + if (*ecode == OP_KETRMIN) + { + if ((rrc = match(eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, + 0)) != MATCH_NOMATCH) return rrc; + if ((rrc = match(eptr, prev, offset_top, md, ims, eptrb, + match_isgroup)) != MATCH_NOMATCH) return rrc; + } + else /* OP_KETRMAX */ + { + if ((rrc = match(eptr, prev, offset_top, md, ims, eptrb, + match_isgroup)) != MATCH_NOMATCH) return rrc; + if ((rrc = match(eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, + 0)) != MATCH_NOMATCH) return rrc; + } + } + return MATCH_NOMATCH; + + /* Start of subject unless notbol, or after internal newline if multiline */ + + case OP_CIRC: + if (md->notbol && eptr == md->start_subject) return MATCH_NOMATCH; + if ((ims & PCRE_MULTILINE) != 0) + { + if (eptr != md->start_subject && eptr[-1] != NEWLINE) + return MATCH_NOMATCH; + ecode++; + break; + } + /* ... else fall through */ + + /* Start of subject assertion */ + + case OP_SOD: + if (eptr != md->start_subject) return MATCH_NOMATCH; + ecode++; + break; + + /* Start of match assertion */ + + case OP_SOM: + if (eptr != md->start_subject + md->start_offset) return MATCH_NOMATCH; + ecode++; + break; + + /* Assert before internal newline if multiline, or before a terminating + newline unless endonly is set, else end of subject unless noteol is set. */ + + case OP_DOLL: + if ((ims & PCRE_MULTILINE) != 0) + { + if (eptr < md->end_subject) + { if (*eptr != NEWLINE) return MATCH_NOMATCH; } + else + { if (md->noteol) return MATCH_NOMATCH; } + ecode++; + break; + } + else + { + if (md->noteol) return MATCH_NOMATCH; + if (!md->endonly) + { + if (eptr < md->end_subject - 1 || + (eptr == md->end_subject - 1 && *eptr != NEWLINE)) + return MATCH_NOMATCH; + ecode++; + break; + } + } + /* ... else fall through */ + + /* End of subject assertion (\z) */ + + case OP_EOD: + if (eptr < md->end_subject) return MATCH_NOMATCH; + ecode++; + break; + + /* End of subject or ending \n assertion (\Z) */ + + case OP_EODN: + if (eptr < md->end_subject - 1 || + (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return MATCH_NOMATCH; + ecode++; + break; + + /* Word boundary assertions */ + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + { + BOOL prev_is_word, cur_is_word; + + /* Find out if the previous and current characters are "word" characters. + It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to + be "non-word" characters. */ + +#ifdef SUPPORT_UTF8 + if (md->utf8) + { + if (eptr == md->start_subject) prev_is_word = FALSE; else + { + const uschar *lastptr = eptr - 1; + while((*lastptr & 0xc0) == 0x80) lastptr--; + GETCHAR(c, lastptr); + prev_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0; + } + if (eptr >= md->end_subject) cur_is_word = FALSE; else + { + GETCHAR(c, eptr); + cur_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0; + } + } + else +#endif + + /* More streamlined when not in UTF-8 mode */ + + { + prev_is_word = (eptr != md->start_subject) && + ((md->ctypes[eptr[-1]] & ctype_word) != 0); + cur_is_word = (eptr < md->end_subject) && + ((md->ctypes[*eptr] & ctype_word) != 0); + } + + /* Now see if the situation is what we want */ + + if ((*ecode++ == OP_WORD_BOUNDARY)? + cur_is_word == prev_is_word : cur_is_word != prev_is_word) + return MATCH_NOMATCH; + } + break; + + /* Match a single character type; inline for speed */ + + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == NEWLINE) + return MATCH_NOMATCH; + if (eptr++ >= md->end_subject) return MATCH_NOMATCH; +#ifdef SUPPORT_UTF8 + if (md->utf8) + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; +#endif + ecode++; + break; + + /* Match a single byte, even in UTF-8 mode. This opcode really does match + any byte, even newline, independent of the setting of PCRE_DOTALL. */ + + case OP_ANYBYTE: + if (eptr++ >= md->end_subject) return MATCH_NOMATCH; + ecode++; + break; + + case OP_NOT_DIGIT: + if (eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c < 256 && +#endif + (md->ctypes[c] & ctype_digit) != 0 + ) + return MATCH_NOMATCH; + ecode++; + break; + + case OP_DIGIT: + if (eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c >= 256 || +#endif + (md->ctypes[c] & ctype_digit) == 0 + ) + return MATCH_NOMATCH; + ecode++; + break; + + case OP_NOT_WHITESPACE: + if (eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c < 256 && +#endif + (md->ctypes[c] & ctype_space) != 0 + ) + return MATCH_NOMATCH; + ecode++; + break; + + case OP_WHITESPACE: + if (eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c >= 256 || +#endif + (md->ctypes[c] & ctype_space) == 0 + ) + return MATCH_NOMATCH; + ecode++; + break; + + case OP_NOT_WORDCHAR: + if (eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c < 256 && +#endif + (md->ctypes[c] & ctype_word) != 0 + ) + return MATCH_NOMATCH; + ecode++; + break; + + case OP_WORDCHAR: + if (eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c >= 256 || +#endif + (md->ctypes[c] & ctype_word) == 0 + ) + return MATCH_NOMATCH; + ecode++; + break; + + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The code is similar + to that for character classes, but repeated for efficiency. Then obey + similar code to character type repeats - written out again for speed. + However, if the referenced string is the empty string, always treat + it as matched, any number of times (otherwise there could be infinite + loops). */ + + case OP_REF: + { + int length; + int offset = GET2(ecode, 1) << 1; /* Doubled ref number */ + ecode += 3; /* Advance past item */ + + /* If the reference is unset, set the length to be longer than the amount + of subject left; this ensures that every attempt at a match fails. We + can't just fail here, because of the possibility of quantifiers with zero + minima. */ + + length = (offset >= offset_top || md->offset_vector[offset] < 0)? + md->end_subject - eptr + 1 : + md->offset_vector[offset+1] - md->offset_vector[offset]; + + /* Set up for repetition, or handle the non-repeated case */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 3); + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + if (!match_ref(offset, eptr, length, md, ims)) return MATCH_NOMATCH; + eptr += length; + continue; /* With the main loop */ + } + + /* If the length of the reference is zero, just continue with the + main loop. */ + + if (length == 0) continue; + + /* First, ensure the minimum number of matches are present. We get back + the length of the reference string explicitly rather than passing the + address of eptr, so that eptr can be a register variable. */ + + for (i = 1; i <= min; i++) + { + if (!match_ref(offset, eptr, length, md, ims)) return MATCH_NOMATCH; + eptr += length; + } + + /* If min = max, continue at the same level without recursion. + They are not both allowed to be zero. */ + + if (min == max) continue; + + /* If minimizing, keep trying and advancing the pointer */ + + if (minimize) + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || !match_ref(offset, eptr, length, md, ims)) + return MATCH_NOMATCH; + eptr += length; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest string and work backwards */ + + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (!match_ref(offset, eptr, length, md, ims)) break; + eptr += length; + } + while (eptr >= pp) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + eptr -= length; + } + return MATCH_NOMATCH; + } + } + /* Control never gets here */ + + + + /* Match a bit-mapped character class, possibly repeatedly. This op code is + used when all the characters in the class have values in the range 0-255. + The only difference between OP_CLASS and OP_NCLASS occurs when a data + character outside the range is encountered. + + First, look past the end of the item to see if there is repeat information + following. Then obey similar code to character type repeats - written out + again for speed. */ + + case OP_NCLASS: + case OP_CLASS: + { + const uschar *data = ecode + 1; /* Save for matching */ + ecode += 33; /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 3); + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + min = max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINC(c, eptr); + if (c > 255) + { + if (op == OP_CLASS) return MATCH_NOMATCH; + } + else + { + if ((data[c/8] & (1 << (c&7))) == 0) return MATCH_NOMATCH; + } + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) return MATCH_NOMATCH; + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) == 0) return MATCH_NOMATCH; + } + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINC(c, eptr); + if (c > 255) + { + if (op == OP_CLASS) return MATCH_NOMATCH; + } + else + { + if ((data[c/8] & (1 << (c&7))) == 0) return MATCH_NOMATCH; + } + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || eptr >= md->end_subject) return MATCH_NOMATCH; + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) == 0) return MATCH_NOMATCH; + } + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + const uschar *pp = eptr; + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c > 255) + { + if (op == OP_CLASS) break; + } + else + { + if ((data[c/8] & (1 << (c&7))) == 0) break; + } + eptr += len; + } + for (;;) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if ((data[c/8] & (1 << (c&7))) == 0) break; + eptr++; + } + while (eptr >= pp) + { + if ((rrc = match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + } + } + + return MATCH_NOMATCH; + } + } + /* Control never gets here */ + + + /* Match an extended character class. This opcode is encountered only + in UTF-8 mode, because that's the only time it is compiled. */ + +#ifdef SUPPORT_UTF8 + case OP_XCLASS: + { + const uschar *data = ecode + 1 + LINK_SIZE; /* Save for matching */ + ecode += GET(ecode, 1); /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 3); + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + min = max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINC(c, eptr); + if (!match_xclass(c, data)) return MATCH_NOMATCH; + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINC(c, eptr); + if (!match_xclass(c, data)) return MATCH_NOMATCH; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (!match_xclass(c, data)) break; + eptr += len; + } + for(;;) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr) + } + return MATCH_NOMATCH; + } + + /* Control never gets here */ + } +#endif /* End of XCLASS */ + + /* Match a run of characters */ + + case OP_CHARS: + { + register int length = ecode[1]; + ecode += 2; + +#ifdef PCREDEBUG /* Sigh. Some compilers never learn. */ + if (eptr >= md->end_subject) + printf("matching subject against pattern "); + else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + printf(" against pattern "); + } + pchars(ecode, length, FALSE, md); + printf("\n"); +#endif + + if (length > md->end_subject - eptr) return MATCH_NOMATCH; + if ((ims & PCRE_CASELESS) != 0) + { + while (length-- > 0) + if (md->lcc[*ecode++] != md->lcc[*eptr++]) + return MATCH_NOMATCH; + } + else + { + while (length-- > 0) if (*ecode++ != *eptr++) return MATCH_NOMATCH; + } + } + break; + + /* Match a single character repeatedly; different opcodes share code. */ + + case OP_EXACT: + min = max = GET2(ecode, 1); + ecode += 3; + goto REPEATCHAR; + + case OP_UPTO: + case OP_MINUPTO: + min = 0; + max = GET2(ecode, 1); + minimize = *ecode == OP_MINUPTO; + ecode += 3; + goto REPEATCHAR; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + c = *ecode++ - OP_STAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATCHAR: +#ifdef SUPPORT_UTF8 + if (md->utf8) + { + int len = 1; + const uschar *charptr = ecode; + GETCHARLEN(c, ecode, len); + if (min * len > md->end_subject - eptr) return MATCH_NOMATCH; + ecode += len; + + /* Handle multibyte character matching specially here. There is no + support for any kind of casing for multibyte characters. */ + + if (len > 1) + { + for (i = 1; i <= min; i++) + { + if (memcmp(eptr, charptr, len) != 0) return MATCH_NOMATCH; + eptr += len; + } + + if (min == max) continue; + + if (minimize) + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || + eptr >= md->end_subject || + memcmp(eptr, charptr, len) != 0) + return MATCH_NOMATCH; + eptr += len; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr > md->end_subject - len || + memcmp(eptr, charptr, len) != 0) + break; + eptr += len; + } + while (eptr >= pp) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + eptr -= len; + } + return MATCH_NOMATCH; + } + /* Control never gets here */ + } + + /* If the length of a UTF-8 character is 1, we fall through here, and + obey the code as for non-UTF-8 characters below, though in this case the + value of c will always be < 128. */ + } + else +#endif + + /* When not in UTF-8 mode, load a single-byte character. */ + { + if (min > md->end_subject - eptr) return MATCH_NOMATCH; + c = *ecode++; + } + + /* The value of c at this point is always less than 256, though we may or + may not be in UTF-8 mode. The code is duplicated for the caseless and + caseful cases, for speed, since matching characters is likely to be quite + common. First, ensure the minimum number of matches are present. If min = + max, continue at the same level without recursing. Otherwise, if + minimizing, keep trying the rest of the expression and advancing one + matching character if failing, up to the maximum. Alternatively, if + maximizing, find the maximum number of characters and work backwards. */ + + DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max, + max, eptr)); + + if ((ims & PCRE_CASELESS) != 0) + { + c = md->lcc[c]; + for (i = 1; i <= min; i++) + if (c != md->lcc[*eptr++]) return MATCH_NOMATCH; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || eptr >= md->end_subject || + c != md->lcc[*eptr++]) + return MATCH_NOMATCH; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c != md->lcc[*eptr]) break; + eptr++; + } + while (eptr >= pp) + if ((rrc = match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + return MATCH_NOMATCH; + } + /* Control never gets here */ + } + + /* Caseful comparisons (includes all multi-byte characters) */ + + else + { + for (i = 1; i <= min; i++) if (c != *eptr++) return MATCH_NOMATCH; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || eptr >= md->end_subject || c != *eptr++) + return MATCH_NOMATCH; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c != *eptr) break; + eptr++; + } + while (eptr >= pp) + if ((rrc = match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + return MATCH_NOMATCH; + } + } + /* Control never gets here */ + + /* Match a negated single one-byte character. The character we are + checking can be multibyte. */ + + case OP_NOT: + if (eptr >= md->end_subject) return MATCH_NOMATCH; + ecode++; + GETCHARINCTEST(c, eptr); + if ((ims & PCRE_CASELESS) != 0) + { +#ifdef SUPPORT_UTF8 + if (c < 256) +#endif + c = md->lcc[c]; + if (md->lcc[*ecode++] == c) return MATCH_NOMATCH; + } + else + { + if (*ecode++ == c) return MATCH_NOMATCH; + } + break; + + /* Match a negated single one-byte character repeatedly. This is almost a + repeat of the code for a repeated single character, but I haven't found a + nice way of commoning these up that doesn't require a test of the + positive/negative option for each character match. Maybe that wouldn't add + very much to the time taken, but character matching *is* what this is all + about... */ + + case OP_NOTEXACT: + min = max = GET2(ecode, 1); + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTUPTO: + case OP_NOTMINUPTO: + min = 0; + max = GET2(ecode, 1); + minimize = *ecode == OP_NOTMINUPTO; + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + c = *ecode++ - OP_NOTSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character (less than 255) matches. + We can give up quickly if there are fewer than the minimum number of + characters left in the subject. */ + + REPEATNOTCHAR: + if (min > md->end_subject - eptr) return MATCH_NOMATCH; + c = *ecode++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, max, + max, eptr)); + + if ((ims & PCRE_CASELESS) != 0) + { + c = md->lcc[c]; + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + register int d; + for (i = 1; i <= min; i++) + { + GETCHARINC(d, eptr); + if (d < 256) d = md->lcc[d]; + if (c == d) return MATCH_NOMATCH; + } + } + else +#endif + + /* Not UTF-8 mode */ + { + for (i = 1; i <= min; i++) + if (c == md->lcc[*eptr++]) return MATCH_NOMATCH; + } + + if (min == max) continue; + + if (minimize) + { +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + register int d; + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + GETCHARINC(d, eptr); + if (d < 256) d = md->lcc[d]; + if (i >= max || eptr >= md->end_subject || c == d) + return MATCH_NOMATCH; + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || eptr >= md->end_subject || c == md->lcc[*eptr++]) + return MATCH_NOMATCH; + } + } + /* Control never gets here */ + } + + /* Maximize case */ + + else + { + const uschar *pp = eptr; + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + register int d; + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(d, eptr, len); + if (d < 256) d = md->lcc[d]; + if (c == d) break; + eptr += len; + } + for(;;) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c == md->lcc[*eptr]) break; + eptr++; + } + while (eptr >= pp) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + eptr--; + } + } + + return MATCH_NOMATCH; + } + /* Control never gets here */ + } + + /* Caseful comparisons */ + + else + { +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + register int d; + for (i = 1; i <= min; i++) + { + GETCHARINC(d, eptr); + if (c == d) return MATCH_NOMATCH; + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = 1; i <= min; i++) + if (c == *eptr++) return MATCH_NOMATCH; + } + + if (min == max) continue; + + if (minimize) + { +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + register int d; + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + GETCHARINC(d, eptr); + if (i >= max || eptr >= md->end_subject || c == d) + return MATCH_NOMATCH; + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || eptr >= md->end_subject || c == *eptr++) + return MATCH_NOMATCH; + } + } + /* Control never gets here */ + } + + /* Maximize case */ + + else + { + const uschar *pp = eptr; + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + register int d; + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(d, eptr, len); + if (c == d) break; + eptr += len; + } + for(;;) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c == *eptr) break; + eptr++; + } + while (eptr >= pp) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + eptr--; + } + } + + return MATCH_NOMATCH; + } + } + /* Control never gets here */ + + /* Match a single character type repeatedly; several different opcodes + share code. This is very similar to the code for single characters, but we + repeat it in the interests of efficiency. */ + + case OP_TYPEEXACT: + min = max = GET2(ecode, 1); + minimize = TRUE; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + min = 0; + max = GET2(ecode, 1); + minimize = *ecode == OP_TYPEMINUPTO; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + c = *ecode++ - OP_TYPESTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single character type matches. Note that + in UTF-8 mode, '.' matches a character of any length, but for the other + character types, the valid characters are all one-byte long. */ + + REPEATTYPE: + ctype = *ecode++; /* Code for the character type */ + + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). Also we can test that there are at least + the minimum number of bytes before we start. This isn't as effective in + UTF-8 mode, but it does no harm. Separate the UTF-8 code completely as that + is tidier. */ + + if (min > md->end_subject - eptr) return MATCH_NOMATCH; + if (min > 0) + { +#ifdef SUPPORT_UTF8 + if (md->utf8) switch(ctype) + { + case OP_ANY: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + (*eptr++ == NEWLINE && (ims & PCRE_DOTALL) == 0)) + return MATCH_NOMATCH; + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + } + break; + + case OP_ANYBYTE: + eptr += min; + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) return MATCH_NOMATCH; + GETCHARINC(c, eptr); + if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) + return MATCH_NOMATCH; + } + break; + + case OP_DIGIT: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + *eptr >= 128 || (md->ctypes[*eptr++] & ctype_digit) == 0) + return MATCH_NOMATCH; + /* No need to skip more bytes - we know it's a 1-byte character */ + } + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + (*eptr < 128 && (md->ctypes[*eptr++] & ctype_space) != 0)) + return MATCH_NOMATCH; + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + } + break; + + case OP_WHITESPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + *eptr >= 128 || (md->ctypes[*eptr++] & ctype_space) == 0) + return MATCH_NOMATCH; + /* No need to skip more bytes - we know it's a 1-byte character */ + } + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + (*eptr < 128 && (md->ctypes[*eptr++] & ctype_word) != 0)) + return MATCH_NOMATCH; + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + } + break; + + case OP_WORDCHAR: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + *eptr >= 128 || (md->ctypes[*eptr++] & ctype_word) == 0) + return MATCH_NOMATCH; + /* No need to skip more bytes - we know it's a 1-byte character */ + } + break; + } + else +#endif + + /* Code for the non-UTF-8 case for minimum matching */ + + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0) + { + for (i = 1; i <= min; i++) + if (*eptr++ == NEWLINE) return MATCH_NOMATCH; + } + else eptr += min; + break; + + case OP_ANYBYTE: + eptr += min; + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_digit) != 0) return MATCH_NOMATCH; + break; + + case OP_DIGIT: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_digit) == 0) return MATCH_NOMATCH; + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_space) != 0) return MATCH_NOMATCH; + break; + + case OP_WHITESPACE: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_space) == 0) return MATCH_NOMATCH; + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_word) != 0) + return MATCH_NOMATCH; + break; + + case OP_WORDCHAR: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_word) == 0) + return MATCH_NOMATCH; + break; + } + } + + /* If min = max, continue at the same level without recursing */ + + if (min == max) continue; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match. Again, separate the UTF-8 case for speed. */ + + if (minimize) + { +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (md->utf8) + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || eptr >= md->end_subject) return MATCH_NOMATCH; + + GETCHARINC(c, eptr); + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) return MATCH_NOMATCH; + break; + + case OP_ANYBYTE: + break; + + case OP_NOT_DIGIT: + if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) + return MATCH_NOMATCH; + break; + + case OP_DIGIT: + if (c >= 256 || (md->ctypes[c] & ctype_digit) == 0) + return MATCH_NOMATCH; + break; + + case OP_NOT_WHITESPACE: + if (c < 256 && (md->ctypes[c] & ctype_space) != 0) + return MATCH_NOMATCH; + break; + + case OP_WHITESPACE: + if (c >= 256 || (md->ctypes[c] & ctype_space) == 0) + return MATCH_NOMATCH; + break; + + case OP_NOT_WORDCHAR: + if (c < 256 && (md->ctypes[c] & ctype_word) != 0) + return MATCH_NOMATCH; + break; + + case OP_WORDCHAR: + if (c >= 256 && (md->ctypes[c] & ctype_word) == 0) + return MATCH_NOMATCH; + break; + } + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min;; i++) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (i >= max || eptr >= md->end_subject) return MATCH_NOMATCH; + c = *eptr++; + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) return MATCH_NOMATCH; + break; + + case OP_ANYBYTE: + break; + + case OP_NOT_DIGIT: + if ((md->ctypes[c] & ctype_digit) != 0) return MATCH_NOMATCH; + break; + + case OP_DIGIT: + if ((md->ctypes[c] & ctype_digit) == 0) return MATCH_NOMATCH; + break; + + case OP_NOT_WHITESPACE: + if ((md->ctypes[c] & ctype_space) != 0) return MATCH_NOMATCH; + break; + + case OP_WHITESPACE: + if ((md->ctypes[c] & ctype_space) == 0) return MATCH_NOMATCH; + break; + + case OP_NOT_WORDCHAR: + if ((md->ctypes[c] & ctype_word) != 0) return MATCH_NOMATCH; + break; + + case OP_WORDCHAR: + if ((md->ctypes[c] & ctype_word) == 0) return MATCH_NOMATCH; + break; + } + } + } + /* Control never gets here */ + } + + /* If maximizing it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). Again, keep the + UTF-8 stuff separate. */ + + else + { + const uschar *pp = eptr; + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + + if (md->utf8) + { + switch(ctype) + { + case OP_ANY: + + /* Special code is required for UTF8, but when the maximum is unlimited + we don't need it, so we repeat the non-UTF8 code. This is probably + worth it, because .* is quite a common idiom. */ + + if (max < INT_MAX) + { + if ((ims & PCRE_DOTALL) == 0) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || *eptr == NEWLINE) break; + eptr++; + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + } + } + else + { + for (i = min; i < max; i++) + { + eptr++; + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + } + } + } + + /* Handle unlimited UTF-8 repeat */ + + else + { + if ((ims & PCRE_DOTALL) == 0) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || *eptr == NEWLINE) break; + eptr++; + } + break; + } + else + { + c = max - min; + if (c > md->end_subject - eptr) c = md->end_subject - eptr; + eptr += c; + } + } + break; + + /* The byte case is the same as non-UTF8 */ + + case OP_ANYBYTE: + c = max - min; + if (c > md->end_subject - eptr) c = md->end_subject - eptr; + eptr += c; + break; + + case OP_NOT_DIGIT: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) break; + eptr+= len; + } + break; + + case OP_DIGIT: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c >= 256 ||(md->ctypes[c] & ctype_digit) == 0) break; + eptr+= len; + } + break; + + case OP_NOT_WHITESPACE: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c < 256 && (md->ctypes[c] & ctype_space) != 0) break; + eptr+= len; + } + break; + + case OP_WHITESPACE: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c >= 256 ||(md->ctypes[c] & ctype_space) == 0) break; + eptr+= len; + } + break; + + case OP_NOT_WORDCHAR: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c < 256 && (md->ctypes[c] & ctype_word) != 0) break; + eptr+= len; + } + break; + + case OP_WORDCHAR: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) break; + eptr+= len; + } + break; + } + + /* eptr is now past the end of the maximum run */ + + for(;;) + { + if ((rrc = match(eptr, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + + /* Not UTF-8 mode */ + { + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || *eptr == NEWLINE) break; + eptr++; + } + break; + } + /* For DOTALL case, fall through and treat as \C */ + + case OP_ANYBYTE: + c = max - min; + if (c > md->end_subject - eptr) c = md->end_subject - eptr; + eptr += c; + break; + + case OP_NOT_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0) + break; + eptr++; + } + break; + + case OP_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0) + break; + eptr++; + } + break; + + case OP_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0) + break; + eptr++; + } + break; + + case OP_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0) + break; + eptr++; + } + break; + } + + /* eptr is now past the end of the maximum run */ + + while (eptr >= pp) + { + if ((rrc = match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) != + MATCH_NOMATCH) return rrc; + } + } + + /* Get here if we can't make it match with any permitted repetitions */ + + return MATCH_NOMATCH; + } + /* Control never gets here */ + + /* There's been some horrible disaster. Since all codes > OP_BRA are + for capturing brackets, and there shouldn't be any gaps between 0 and + OP_BRA, arrival here can only mean there is something seriously wrong + in the code above or the OP_xxx definitions. */ + + default: + DPRINTF(("Unknown opcode %d\n", *ecode)); + return PCRE_ERROR_UNKNOWN_NODE; + } + + /* Do not stick any code in here without much thought; it is assumed + that "continue" in the code above comes out to here to repeat the main + loop. */ + + } /* End of main loop */ +/* Control never reaches here */ +} + + + + +/************************************************* +* Execute a Regular Expression * +*************************************************/ + +/* This function applies a compiled re to a subject string and picks out +portions of the string if it matches. Two elements in the vector are set for +each substring: the offsets to the start and end of the substring. + +Arguments: + external_re points to the compiled expression + extra_data points to extra data or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + offsets points to a vector of ints to be filled in with offsets + offsetcount the number of elements in the vector + +Returns: > 0 => success; value is the number of elements filled in + = 0 => success, but offsets is not big enough + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +int +pcre_exec(const pcre *external_re, const pcre_extra *extra_data, + const char *subject, int length, int start_offset, int options, int *offsets, + int offsetcount) +{ +int rc, resetcount, ocount; +int first_byte = -1; +int req_byte = -1; +int req_byte2 = -1; +unsigned long int ims = 0; +BOOL using_temporary_offsets = FALSE; +BOOL anchored; +BOOL startline; +BOOL first_byte_caseless = FALSE; +BOOL req_byte_caseless = FALSE; +match_data match_block; +const uschar *start_bits = NULL; +const uschar *start_match = (const uschar *)subject + start_offset; +const uschar *end_subject; +const uschar *req_byte_ptr = start_match - 1; +const pcre_study_data *study; +const real_pcre *re = (const real_pcre *)external_re; + +/* Plausibility checks */ + +if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; +if (re == NULL || subject == NULL || + (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; + +/* Fish out the optional data from the extra_data structure, first setting +the default values. */ + +study = NULL; +match_block.match_limit = MATCH_LIMIT; +match_block.callout_data = NULL; + +if (extra_data != NULL) + { + register unsigned int flags = extra_data->flags; + if ((flags & PCRE_EXTRA_STUDY_DATA) != 0) + study = extra_data->study_data; + if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0) + match_block.match_limit = extra_data->match_limit; + if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0) + match_block.callout_data = extra_data->callout_data; + } + +/* Now we have re supposedly pointing to the regex */ + +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; + +anchored = ((re->options | options) & PCRE_ANCHORED) != 0; +startline = (re->options & PCRE_STARTLINE) != 0; + +match_block.start_code = + (const uschar *)re + sizeof(real_pcre) + re->name_count * re->name_entry_size; +match_block.start_subject = (const uschar *)subject; +match_block.start_offset = start_offset; +match_block.end_subject = match_block.start_subject + length; +end_subject = match_block.end_subject; + +match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; +match_block.utf8 = (re->options & PCRE_UTF8) != 0; + +match_block.notbol = (options & PCRE_NOTBOL) != 0; +match_block.noteol = (options & PCRE_NOTEOL) != 0; +match_block.notempty = (options & PCRE_NOTEMPTY) != 0; + +match_block.recursive = NULL; /* No recursion at top level */ + +match_block.lcc = re->tables + lcc_offset; +match_block.ctypes = re->tables + ctypes_offset; + +/* The ims options can vary during the matching as a result of the presence +of (?ims) items in the pattern. They are kept in a local variable so that +restoring at the exit of a group is easy. */ + +ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL); + +/* If the expression has got more back references than the offsets supplied can +hold, we get a temporary bit of working store to use during the matching. +Otherwise, we can use the vector supplied, rounding down its size to a multiple +of 3. */ + +ocount = offsetcount - (offsetcount % 3); + +if (re->top_backref > 0 && re->top_backref >= ocount/3) + { + ocount = re->top_backref * 3 + 3; + match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int)); + if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY; + using_temporary_offsets = TRUE; + DPRINTF(("Got memory to hold back references\n")); + } +else match_block.offset_vector = offsets; + +match_block.offset_end = ocount; +match_block.offset_max = (2*ocount)/3; +match_block.offset_overflow = FALSE; +match_block.capture_last = -1; + +/* Compute the minimum number of offsets that we need to reset each time. Doing +this makes a huge difference to execution time when there aren't many brackets +in the pattern. */ + +resetcount = 2 + re->top_bracket * 2; +if (resetcount > offsetcount) resetcount = ocount; + +/* Reset the working variable associated with each extraction. These should +never be used unless previously set, but they get saved and restored, and so we +initialize them to avoid reading uninitialized locations. */ + +if (match_block.offset_vector != NULL) + { + register int *iptr = match_block.offset_vector + ocount; + register int *iend = iptr - resetcount/2 + 1; + while (--iptr >= iend) *iptr = -1; + } + +/* Set up the first character to match, if available. The first_byte value is +never set for an anchored regular expression, but the anchoring may be forced +at run time, so we have to test for anchoring. The first char may be unset for +an unanchored pattern, of course. If there's no first char and the pattern was +studied, there may be a bitmap of possible first characters. */ + +if (!anchored) + { + if ((re->options & PCRE_FIRSTSET) != 0) + { + first_byte = re->first_byte & 255; + if ((first_byte_caseless = ((re->first_byte & REQ_CASELESS) != 0)) == TRUE) + first_byte = match_block.lcc[first_byte]; + } + else + if (!startline && study != NULL && + (study->options & PCRE_STUDY_MAPPED) != 0) + start_bits = study->start_bits; + } + +/* For anchored or unanchored matches, there may be a "last known required +character" set. */ + +if ((re->options & PCRE_REQCHSET) != 0) + { + req_byte = re->req_byte & 255; + req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0; + req_byte2 = (re->tables + fcc_offset)[req_byte]; /* case flipped */ + } + +/* Loop for handling unanchored repeated matching attempts; for anchored regexs +the loop runs just once. */ + +do + { + register int *iptr = match_block.offset_vector; + register int *iend = iptr + resetcount; + + /* Reset the maximum number of extractions we might see. */ + + while (iptr < iend) *iptr++ = -1; + + /* Advance to a unique first char if possible */ + + if (first_byte >= 0) + { + if (first_byte_caseless) + while (start_match < end_subject && + match_block.lcc[*start_match] != first_byte) + start_match++; + else + while (start_match < end_subject && *start_match != first_byte) + start_match++; + } + + /* Or to just after \n for a multiline match if possible */ + + else if (startline) + { + if (start_match > match_block.start_subject + start_offset) + { + while (start_match < end_subject && start_match[-1] != NEWLINE) + start_match++; + } + } + + /* Or to a non-unique first char after study */ + + else if (start_bits != NULL) + { + while (start_match < end_subject) + { + register int c = *start_match; + if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break; + } + } + +#ifdef PCREDEBUG /* Sigh. Some compilers never learn. */ + printf(">>>> Match against: "); + pchars(start_match, end_subject - start_match, TRUE, &match_block); + printf("\n"); +#endif + + /* If req_byte is set, we know that that character must appear in the subject + for the match to succeed. If the first character is set, req_byte must be + later in the subject; otherwise the test starts at the match point. This + optimization can save a huge amount of backtracking in patterns with nested + unlimited repeats that aren't going to match. Writing separate code for + cased/caseless versions makes it go faster, as does using an autoincrement + and backing off on a match. + + HOWEVER: when the subject string is very, very long, searching to its end can + take a long time, and give bad performance on quite ordinary patterns. This + showed up when somebody was matching /^C/ on a 32-megabyte string... so we + don't do this when the string is sufficiently long. */ + + if (req_byte >= 0 && end_subject - start_match < REQ_BYTE_MAX) + { + register const uschar *p = start_match + ((first_byte >= 0)? 1 : 0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > req_byte_ptr) + { + if (req_byte_caseless) + { + while (p < end_subject) + { + register int pp = *p++; + if (pp == req_byte || pp == req_byte2) { p--; break; } + } + } + else + { + while (p < end_subject) + { + if (*p++ == req_byte) { p--; break; } + } + } + + /* If we can't find the required character, break the matching loop */ + + if (p >= end_subject) break; + + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ + + req_byte_ptr = p; + } + } + + /* When a match occurs, substrings will be set for all internal extractions; + we just need to set up the whole thing as substring 0 before returning. If + there were too many extractions, set the return code to zero. In the case + where we had to get some local store to hold offsets for backreferences, copy + those back references that we can. In this case there need not be overflow + if certain parts of the pattern were not used. */ + + match_block.start_match = start_match; + match_block.match_call_count = 0; + + rc = match(start_match, match_block.start_code, 2, &match_block, ims, NULL, + match_isgroup); + + if (rc == MATCH_NOMATCH) + { + start_match++; +#ifdef SUPPORT_UTF8 + if (match_block.utf8) + while((*start_match & 0xc0) == 0x80) start_match++; +#endif + continue; + } + + if (rc != MATCH_MATCH) + { + DPRINTF((">>>> error: returning %d\n", rc)); + return rc; + } + + /* We have a match! Copy the offset information from temporary store if + necessary */ + + if (using_temporary_offsets) + { + if (offsetcount >= 4) + { + memcpy(offsets + 2, match_block.offset_vector + 2, + (offsetcount - 2) * sizeof(int)); + DPRINTF(("Copied offsets from temporary memory\n")); + } + if (match_block.end_offset_top > offsetcount) + match_block.offset_overflow = TRUE; + + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(match_block.offset_vector); + } + + rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2; + + if (offsetcount < 2) rc = 0; else + { + offsets[0] = start_match - match_block.start_subject; + offsets[1] = match_block.end_match_ptr - match_block.start_subject; + } + + DPRINTF((">>>> returning %d\n", rc)); + return rc; + } + +/* This "while" is the end of the "do" above */ + +while (!anchored && start_match <= end_subject); + +if (using_temporary_offsets) + { + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(match_block.offset_vector); + } + +DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n")); + +return PCRE_ERROR_NOMATCH; +} + +/* End of pcre.c */ diff --git a/pcre/pcre.in b/pcre/pcre.in new file mode 100644 index 00000000..2aa44b90 --- /dev/null +++ b/pcre/pcre.in @@ -0,0 +1,184 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* Copyright (c) 1997-2003 University of Cambridge */ + +#ifndef _PCRE_H +#define _PCRE_H + +/* The file pcre.h is build by "configure". Do not edit it; instead +make changes to pcre.in. */ + +#define PCRE_MAJOR @PCRE_MAJOR@ +#define PCRE_MINOR @PCRE_MINOR@ +#define PCRE_DATE @PCRE_DATE@ + +/* Win32 uses DLL by default */ + +#ifdef _WIN32 +# ifdef PCRE_DEFINITION +# ifdef DLL_EXPORT +# define PCRE_DATA_SCOPE __declspec(dllexport) +# endif +# else +# ifndef PCRE_STATIC +# define PCRE_DATA_SCOPE __declspec(dllimport) +# endif +# endif +#endif +#ifndef PCRE_DATA_SCOPE +# define PCRE_DATA_SCOPE extern +#endif + +/* Have to include stdlib.h in order to ensure that size_t is defined; +it is needed here for malloc. */ + +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options */ + +#define PCRE_CASELESS 0x0001 +#define PCRE_MULTILINE 0x0002 +#define PCRE_DOTALL 0x0004 +#define PCRE_EXTENDED 0x0008 +#define PCRE_ANCHORED 0x0010 +#define PCRE_DOLLAR_ENDONLY 0x0020 +#define PCRE_EXTRA 0x0040 +#define PCRE_NOTBOL 0x0080 +#define PCRE_NOTEOL 0x0100 +#define PCRE_UNGREEDY 0x0200 +#define PCRE_NOTEMPTY 0x0400 +#define PCRE_UTF8 0x0800 +#define PCRE_NO_AUTO_CAPTURE 0x1000 + +/* Exec-time and get/set-time error codes */ + +#define PCRE_ERROR_NOMATCH (-1) +#define PCRE_ERROR_NULL (-2) +#define PCRE_ERROR_BADOPTION (-3) +#define PCRE_ERROR_BADMAGIC (-4) +#define PCRE_ERROR_UNKNOWN_NODE (-5) +#define PCRE_ERROR_NOMEMORY (-6) +#define PCRE_ERROR_NOSUBSTRING (-7) +#define PCRE_ERROR_MATCHLIMIT (-8) +#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ + +/* Request types for pcre_fullinfo() */ + +#define PCRE_INFO_OPTIONS 0 +#define PCRE_INFO_SIZE 1 +#define PCRE_INFO_CAPTURECOUNT 2 +#define PCRE_INFO_BACKREFMAX 3 +#define PCRE_INFO_FIRSTBYTE 4 +#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */ +#define PCRE_INFO_FIRSTTABLE 5 +#define PCRE_INFO_LASTLITERAL 6 +#define PCRE_INFO_NAMEENTRYSIZE 7 +#define PCRE_INFO_NAMECOUNT 8 +#define PCRE_INFO_NAMETABLE 9 +#define PCRE_INFO_STUDYSIZE 10 + +/* Request types for pcre_config() */ + +#define PCRE_CONFIG_UTF8 0 +#define PCRE_CONFIG_NEWLINE 1 +#define PCRE_CONFIG_LINK_SIZE 2 +#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3 +#define PCRE_CONFIG_MATCH_LIMIT 4 + +/* Bit flags for the pcre_extra structure */ + +#define PCRE_EXTRA_STUDY_DATA 0x0001 +#define PCRE_EXTRA_MATCH_LIMIT 0x0002 +#define PCRE_EXTRA_CALLOUT_DATA 0x0004 + +/* Types */ + +struct real_pcre; /* declaration; the definition is private */ +typedef struct real_pcre pcre; + +/* The structure for passing additional data to pcre_exec(). This is defined in +such as way as to be extensible. */ + +typedef struct pcre_extra { + unsigned long int flags; /* Bits for which fields are set */ + void *study_data; /* Opaque data from pcre_study() */ + unsigned long int match_limit; /* Maximum number of calls to match() */ + void *callout_data; /* Data passed back in callouts */ +} pcre_extra; + +/* The structure for passing out data via the pcre_callout_function. We use a +structure so that new fields can be added on the end in future versions, +without changing the API of the function, thereby allowing old clients to work +without modification. */ + +typedef struct pcre_callout_block { + int version; /* Identifies version of block */ + /* ------------------------ Version 0 ------------------------------- */ + int callout_number; /* Number compiled into pattern */ + int *offset_vector; /* The offset vector */ + const char *subject; /* The subject being matched */ + int subject_length; /* The length of the subject */ + int start_match; /* Offset to start of this match attempt */ + int current_position; /* Where we currently are */ + int capture_top; /* Max current capture */ + int capture_last; /* Most recently closed capture */ + void *callout_data; /* Data passed in with the call */ + /* ------------------------------------------------------------------ */ +} pcre_callout_block; + +/* Indirection for store get and free functions. These can be set to +alternative malloc/free functions if required. There is also an optional +callout function that is triggered by the (?) regex item. Some magic is +required for Win32 DLL; it is null on other OS. For Virtual Pascal, these +have to be different again. */ + +#ifndef VPCOMPAT +PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t); +PCRE_DATA_SCOPE void (*pcre_free)(void *); +PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *); +#else /* VPCOMPAT */ +extern void *pcre_malloc(size_t); +extern void pcre_free(void *); +extern int pcre_callout(pcre_callout_block *); +#endif /* VPCOMPAT */ + +/* Exported PCRE functions */ + +extern pcre *pcre_compile(const char *, int, const char **, + int *, const unsigned char *); +extern int pcre_config(int, void *); +extern int pcre_copy_named_substring(const pcre *, const char *, + int *, int, const char *, char *, int); +extern int pcre_copy_substring(const char *, int *, int, int, + char *, int); +extern int pcre_exec(const pcre *, const pcre_extra *, + const char *, int, int, int, int *, int); +extern void pcre_free_substring(const char *); +extern void pcre_free_substring_list(const char **); +extern int pcre_fullinfo(const pcre *, const pcre_extra *, int, + void *); +extern int pcre_get_named_substring(const pcre *, const char *, + int *, int, const char *, const char **); +extern int pcre_get_stringnumber(const pcre *, const char *); +extern int pcre_get_substring(const char *, int *, int, int, + const char **); +extern int pcre_get_substring_list(const char *, int *, int, + const char ***); +extern int pcre_info(const pcre *, int *, int *); +extern const unsigned char *pcre_maketables(void); +extern pcre_extra *pcre_study(const pcre *, int, const char **); +extern const char *pcre_version(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcre.h */ diff --git a/pcre/study.c b/pcre/study.c new file mode 100644 index 00000000..4320bd23 --- /dev/null +++ b/pcre/study.c @@ -0,0 +1,438 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1997-2002 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software 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. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +----------------------------------------------------------------------------- +*/ + + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + +#include "internal.h" + + + +/************************************************* +* Set a bit and maybe its alternate case * +*************************************************/ + +/* Given a character, set its bit in the table, and also the bit for the other +version of a letter if we are caseless. + +Arguments: + start_bits points to the bit map + c is the character + caseless the caseless flag + cd the block with char table pointers + +Returns: nothing +*/ + +static void +set_bit(uschar *start_bits, int c, BOOL caseless, compile_data *cd) +{ +start_bits[c/8] |= (1 << (c&7)); +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) + start_bits[cd->fcc[c]/8] |= (1 << (cd->fcc[c]&7)); +} + + + +/************************************************* +* Create bitmap of starting chars * +*************************************************/ + +/* This function scans a compiled unanchored expression and attempts to build a +bitmap of the set of initial characters. If it can't, it returns FALSE. As time +goes by, we may be able to get more clever at doing this. + +Arguments: + code points to an expression + start_bits points to a 32-byte table, initialized to 0 + caseless the current state of the caseless flag + utf8 TRUE if in UTF-8 mode + cd the block with char table pointers + +Returns: TRUE if table built, FALSE otherwise +*/ + +static BOOL +set_start_bits(const uschar *code, uschar *start_bits, BOOL caseless, + BOOL utf8, compile_data *cd) +{ +register int c; + +/* This next statement and the later reference to dummy are here in order to +trick the optimizer of the IBM C compiler for OS/2 into generating correct +code. Apparently IBM isn't going to fix the problem, and we would rather not +disable optimization (in this module it actually makes a big difference, and +the pcre module can use all the optimization it can get). */ + +volatile int dummy; + +do + { + const uschar *tcode = code + 1 + LINK_SIZE; + BOOL try_next = TRUE; + + while (try_next) + { + /* If a branch starts with a bracket or a positive lookahead assertion, + recurse to set bits from within them. That's all for this branch. */ + + if ((int)*tcode >= OP_BRA || *tcode == OP_ASSERT) + { + if (!set_start_bits(tcode, start_bits, caseless, utf8, cd)) + return FALSE; + try_next = FALSE; + } + + else switch(*tcode) + { + default: + return FALSE; + + /* Skip over callout */ + + case OP_CALLOUT: + tcode += 2; + break; + + /* Skip over extended extraction bracket number */ + + case OP_BRANUMBER: + tcode += 3; + break; + + /* Skip over lookbehind and negative lookahead assertions */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do tcode += GET(tcode, 1); while (*tcode == OP_ALT); + tcode += 1+LINK_SIZE; + break; + + /* Skip over an option setting, changing the caseless flag */ + + case OP_OPT: + caseless = (tcode[1] & PCRE_CASELESS) != 0; + tcode += 2; + break; + + /* BRAZERO does the bracket, but carries on. */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + if (!set_start_bits(++tcode, start_bits, caseless, utf8, cd)) + return FALSE; + dummy = 1; + do tcode += GET(tcode,1); while (*tcode == OP_ALT); + tcode += 1+LINK_SIZE; + break; + + /* Single-char * or ? sets the bit and tries the next item */ + + case OP_STAR: + case OP_MINSTAR: + case OP_QUERY: + case OP_MINQUERY: + set_bit(start_bits, tcode[1], caseless, cd); + tcode += 2; +#ifdef SUPPORT_UTF8 + if (utf8) while ((*tcode & 0xc0) == 0x80) tcode++; +#endif + break; + + /* Single-char upto sets the bit and tries the next */ + + case OP_UPTO: + case OP_MINUPTO: + set_bit(start_bits, tcode[3], caseless, cd); + tcode += 4; +#ifdef SUPPORT_UTF8 + if (utf8) while ((*tcode & 0xc0) == 0x80) tcode++; +#endif + break; + + /* At least one single char sets the bit and stops */ + + case OP_EXACT: /* Fall through */ + tcode++; + + case OP_CHARS: /* Fall through */ + tcode++; + + case OP_PLUS: + case OP_MINPLUS: + set_bit(start_bits, tcode[1], caseless, cd); + try_next = FALSE; + break; + + /* Single character type sets the bits and stops */ + + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_digit]; + try_next = FALSE; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_digit]; + try_next = FALSE; + break; + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_space]; + try_next = FALSE; + break; + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_space]; + try_next = FALSE; + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_word]; + try_next = FALSE; + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_word]; + try_next = FALSE; + break; + + /* One or more character type fudges the pointer and restarts, knowing + it will hit a single character type and stop there. */ + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + tcode++; + break; + + case OP_TYPEEXACT: + tcode += 3; + break; + + /* Zero or more repeats of character types set the bits and then + try again. */ + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + tcode += 2; /* Fall through */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + switch(tcode[1]) + { + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_digit]; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_digit]; + break; + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_space]; + break; + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_space]; + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_word]; + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_word]; + break; + } + + tcode += 2; + break; + + /* Character class where all the information is in a bit map: set the + bits and either carry on or not, according to the repeat count. If it was + a negative class, and we are operating with UTF-8 characters, any byte + with the top-bit set is a potentially valid starter because it may start + a character with a value > 255. (This is sub-optimal in that the + character may be in the range 128-255, and those characters might be + unwanted, but that's as far as we go for the moment.) */ + + case OP_NCLASS: + if (utf8) memset(start_bits+16, 0xff, 16); + /* Fall through */ + + case OP_CLASS: + { + tcode++; + for (c = 0; c < 32; c++) start_bits[c] |= tcode[c]; + tcode += 32; + switch (*tcode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + tcode++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (((tcode[1] << 8) + tcode[2]) == 0) tcode += 5; + else try_next = FALSE; + break; + + default: + try_next = FALSE; + break; + } + } + break; /* End of bitmap class handling */ + + } /* End of switch */ + } /* End of try_next loop */ + + code += GET(code, 1); /* Advance to next branch */ + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Study a compiled expression * +*************************************************/ + +/* This function is handed a compiled expression that it must study to produce +information that will speed up the matching. It returns a pcre_extra block +which then gets handed back to pcre_exec(). + +Arguments: + re points to the compiled expression + options contains option bits + errorptr points to where to place error messages; + set NULL unless error + +Returns: pointer to a pcre_extra block, with study_data filled in and the + appropriate flag set; + NULL on error or if no optimization possible +*/ + +pcre_extra * +pcre_study(const pcre *external_re, int options, const char **errorptr) +{ +uschar start_bits[32]; +pcre_extra *extra; +pcre_study_data *study; +const real_pcre *re = (const real_pcre *)external_re; +uschar *code = (uschar *)re + sizeof(real_pcre) + + (re->name_count * re->name_entry_size); +compile_data compile_block; + +*errorptr = NULL; + +if (re == NULL || re->magic_number != MAGIC_NUMBER) + { + *errorptr = "argument is not a compiled regular expression"; + return NULL; + } + +if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) + { + *errorptr = "unknown or incorrect option bit(s) set"; + return NULL; + } + +/* For an anchored pattern, or an unanchored pattern that has a first char, or +a multiline pattern that matches only at "line starts", no further processing +at present. */ + +if ((re->options & (PCRE_ANCHORED|PCRE_FIRSTSET|PCRE_STARTLINE)) != 0) + return NULL; + +/* Set the character tables in the block which is passed around */ + +compile_block.lcc = re->tables + lcc_offset; +compile_block.fcc = re->tables + fcc_offset; +compile_block.cbits = re->tables + cbits_offset; +compile_block.ctypes = re->tables + ctypes_offset; + +/* See if we can find a fixed set of initial characters for the pattern. */ + +memset(start_bits, 0, 32 * sizeof(uschar)); +if (!set_start_bits(code, start_bits, (re->options & PCRE_CASELESS) != 0, + (re->options & PCRE_UTF8) != 0, &compile_block)) return NULL; + +/* Get a pcre_extra block and a pcre_study_data block. The study data is put in +the latter, which is pointed to by the former, which may also get additional +data set later by the calling program. At the moment, the size of +pcre_study_data is fixed. We nevertheless save it in a field for returning via +the pcre_fullinfo() function so that if it becomes variable in the future, we +don't have to change that code. */ + +extra = (pcre_extra *)(pcre_malloc) + (sizeof(pcre_extra) + sizeof(pcre_study_data)); + +if (extra == NULL) + { + *errorptr = "failed to get memory"; + return NULL; + } + +study = (pcre_study_data *)((char *)extra + sizeof(pcre_extra)); +extra->flags = PCRE_EXTRA_STUDY_DATA; +extra->study_data = study; + +study->size = sizeof(pcre_study_data); +study->options = PCRE_STUDY_MAPPED; +memcpy(study->start_bits, start_bits, sizeof(start_bits)); + +return extra; +} + +/* End of study.c */ diff --git a/server.c b/server.c index 84bcf3fd..8a3b2f85 100644 --- a/server.c +++ b/server.c @@ -26,9 +26,12 @@ */ #include "stats.h" +#include "dl.h" #include "hash.h" #include "log.h" +hash_t *sh; + static Server *new_server (char * name); static Server * @@ -76,7 +79,7 @@ AddServer (char *name, char *uplink, int hops) /* run the module event for a new server. */ AddStringToList (&av, s->name, &ac); - Module_Event (EVENT_NEWSERVER, av, ac); + ModuleEvent (EVENT_NEWSERVER, av, ac); free (av); } @@ -101,7 +104,7 @@ DelServer (char *name) /* run the event for delete server */ AddStringToList (&av, s->name, &ac); - Module_Event (EVENT_SQUIT, av, ac); + ModuleEvent (EVENT_SQUIT, av, ac); free (av); hash_delete (sh, sn); @@ -155,7 +158,7 @@ init_server_hash () } void -TimerPings () +PingServers (void) { Server *s; hscan_t ss; diff --git a/server.h b/server.h new file mode 100644 index 00000000..e0cdeb5d --- /dev/null +++ b/server.h @@ -0,0 +1,33 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond, Mark Hetherington +** http://www.neostats.net/ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ + +#ifndef _SERVER_H_ +#define _SERVER_H_ + +void AddServer (char *name, char *uplink, int hops); +void DelServer (char *name); +void ServerDump (void); +int init_server_hash (void); +void PingServers (void); + +#endif /* _SERVER_H_ */ diff --git a/services.c b/services.c index bebeccbe..b0cf3e7b 100644 --- a/services.c +++ b/services.c @@ -29,29 +29,142 @@ #include "dl.h" #include "log.h" #include "sock.h" +#include "ns_help.h" +#include "users.h" +#include "server.h" +#include "chans.h" +#include "hash.h" + +typedef struct bot_cmd { + char cmd[MAXCMDSIZE]; /* command string */ + bot_cmd_handler handler; /* handler */ + int minparams; /* min num params */ + unsigned int ulevel; /* min user level */ + const char** helptext; /* pointer to help text */ + int internal; /* is this a internal function? */ + char onelinehelp[255]; /* single line help for generic help function */ +}bot_cmd; + +/* hash for command list */ +hash_t *botcmds; + static char quitmsg[BUFSIZE]; static char no_reason[]="no reason given"; -static void ns_reload (User * u, char *reason); -static void ns_logs (User * u); -static void ns_jupe (User * u, char * server); -void ns_set_debug (char * u); +static void ns_set_debug (User * u, char **av, int ac); +static void ns_shutdown (User * u, char **av, int ac); +static void ns_reload (User * u, char **av, int ac); +static void ns_logs (User * u, char **av, int ac); +static void ns_jupe (User * u, char **av, int ac); #ifdef USE_RAW -static void ns_raw (User * u, char * message); +static void ns_raw (User * u, char **av, int ac); #endif -static void ns_user_dump (User * u, char * nick); -static void ns_server_dump (User * u); -static void ns_chan_dump (User * u, char * channel); -static void ns_uptime (User * u); -static void ns_version (User * u); +static void ns_user_dump (User * u, char **av, int ac); +static void ns_server_dump (User * u, char **av, int ac); +static void ns_chan_dump (User * u, char **av, int ac); +static void ns_info (User * u, char **av, int ac); +static void ns_version (User * u, char **av, int ac); +static void ns_show_level (User * u, char **av, int ac); +static void ns_do_help (User * u, char **av, int ac); +static void ns_load_module (User * u, char **av, int ac); +static void ns_unload_module (User * u, char **av, int ac); + +bot_cmd ns_commands[]= +{ + + {"HELP", ns_do_help, 0, 0, ns_help_on_help, 1, "Provides Help on Commands"}, + {"LEVEL", ns_show_level, 0, 0, ns_level_help, 1, "Show your permission level for NeoStats."}, + {"INFO", ns_info, 0, 0, ns_info_help, 1, "Stats info on NeoStats."}, + {"VERSION", ns_version, 0, 0, ns_version_help, 1, "Show NeoStats version information."}, + {"SHUTDOWN", ns_shutdown, 0, NS_ULEVEL_ADMIN, ns_shutdown_help, 1, "Shutdown NeoStats"}, + {"RELOAD", ns_reload, 0, NS_ULEVEL_ADMIN, ns_reload_help, 1, "Force NeoStats to reload"}, + {"LOGS", ns_logs, 0, NS_ULEVEL_OPER, ns_logs_help, 1, "View logfiles"}, + {"LOAD", ns_load_module, 1, NS_ULEVEL_ADMIN, ns_load_help, 1, "Load a module"}, + {"UNLOAD", ns_unload_module, 1, NS_ULEVEL_ADMIN, ns_unload_help, 1, "Unload a module"}, + {"JUPE", ns_jupe, 1, NS_ULEVEL_ADMIN, ns_jupe_help, 1, "Jupiter a Server"}, +#ifdef USE_RAW + {"RAW", ns_raw, 0, NS_ULEVEL_ADMIN, ns_raw_help, 1, "Send a raw command from this Server"}, +#endif + {"DEBUG", ns_set_debug, 1, NS_ULEVEL_ROOT, ns_debug_help, 1, "Toggles debug mode"}, + {"BOTLIST", list_bots, 0, NS_ULEVEL_ROOT, ns_botlist_help, 1, "List current module bots"}, + {"SOCKLIST", list_sockets, 0, NS_ULEVEL_ROOT, ns_socklist_help, 1, "List current module sockets"}, + {"TIMERLIST", list_timers, 0, NS_ULEVEL_ROOT, ns_timerlist_help, 1, "List current module timers"}, + {"BOTCHANLIST", list_bot_chans, 0, NS_ULEVEL_ROOT, ns_botchanlist_help, 1, "List current module bot channels"}, + {"MODLIST", list_modules, 0, NS_ULEVEL_ROOT, ns_modlist_help, 1, "List loaded modules"}, + {"USERDUMP", ns_user_dump, 0, NS_ULEVEL_ROOT, ns_userdump_help, 1, "Debug user table"}, + {"CHANDUMP", ns_chan_dump, 0, NS_ULEVEL_ROOT, ns_chandump_help, 1, "Debug channel table"}, + {"SERVERDUMP", ns_server_dump, 0, NS_ULEVEL_ROOT, ns_serverdump_help, 1, "Debug server table"}, + {"\0", NULL, 0, 0, NULL, 0, "\0"} +}; + +int +init_services() +{ + bot_cmd *cmd_ptr; + hnode_t *cmdnode; + + /* init bot hash first */ + + botcmds = hash_create(-1, 0, 0); + + /* Process command list */ + cmd_ptr = ns_commands; + while(cmd_ptr->handler) { + cmdnode = hnode_create(cmd_ptr); + hash_insert(botcmds, cmdnode, cmd_ptr->cmd); + cmd_ptr++; + } + return NS_SUCCESS; +} + +int +add_services_cmd(const char cmd[MAXCMDSIZE], bot_cmd_handler handler, int minparams, int ulevel, const char** helptext, const char onelinehelp[255]) +{ + bot_cmd *cmd_ptr; + hnode_t *cmdnode; + + cmd_ptr = malloc(sizeof(bot_cmd)); + strlcpy(cmd_ptr->cmd, cmd, MAXCMDSIZE); + cmd_ptr->handler = handler; + cmd_ptr->minparams = minparams; + cmd_ptr->ulevel = ulevel; + cmd_ptr->helptext = helptext; + cmd_ptr->internal = 0; + strlcpy(cmd_ptr->onelinehelp, onelinehelp, 255); + + cmdnode = hnode_create(cmd_ptr); + hash_insert(botcmds, cmdnode, cmd_ptr->cmd); + nlog(LOG_DEBUG2, LOG_CORE, "Added a new command %s to Services Bot", cmd); + return 1; +} + +int +del_services_cmd(const char cmd[MAXCMDSIZE]) +{ + bot_cmd *cmd_ptr; + hnode_t *cmdnode; + + cmdnode = hash_lookup(botcmds, cmd); + if (cmdnode) { + hash_delete(botcmds, cmdnode); + cmd_ptr = hnode_get(cmdnode); + hnode_destroy(cmdnode); + /* free if its a external (malloc'd) command */ + if (cmd_ptr->internal == 0) { + free(cmd_ptr); + } + return NS_SUCCESS; + } + return NS_FAILURE; +} void servicesbot (char *nick, char **av, int ac) { User *u; - int rval; - char *tmp; + bot_cmd* cmd_ptr; + hnode_t *cmdnode; u = finduser (nick); if (!u) { @@ -59,270 +172,86 @@ servicesbot (char *nick, char **av, int ac) return; } SET_SEGV_LOCATION(); + me.requests++; - if (me.onlyopers && (UserLevel (u) < 40)) { + /* Check user authority to use this command set */ + if (me.onlyopers && (UserLevel (u) < NS_ULEVEL_OPER)) { prefmsg (u->nick, s_Services, "This service is only available to IRCops."); chanalert (s_Services, "%s Requested %s, but he is Not an Operator!", u->nick, av[1]); return; } - if (!strcasecmp (av[1], "HELP")) { - if (ac > 2) { - chanalert (s_Services, "%s Requested %s Help on %s", u->nick, s_Services, av[2]); - } else { - chanalert (s_Services, "%s Requested %s Help", u->nick, s_Services); - } - if (ac < 3) { - privmsg_list (nick, s_Services, ns_help); - if (UserLevel (u) >= 180) - privmsg_list (nick, s_Services, ns_myuser_help); - privmsg_list (nick, s_Services, ns_help_on_help); - } else if (!strcasecmp (av[2], "VERSION")) - privmsg_list (nick, s_Services, ns_version_help); - else if (!strcasecmp (av[2], "SHUTDOWN") - && (UserLevel (u) >= 180)) - privmsg_list (nick, s_Services, ns_shutdown_help); - else if (!strcasecmp (av[2], "RELOAD") - && (UserLevel (u) >= 180)) - privmsg_list (nick, s_Services, ns_reload_help); - else if (!strcasecmp (av[2], "LOGS") - && (UserLevel (u) >= 180)) - privmsg_list (nick, s_Services, ns_logs_help); - else if (!strcasecmp (av[2], "LOAD") - && (UserLevel (u) >= 180)) - privmsg_list (nick, s_Services, ns_load_help); - else if (!strcasecmp (av[2], "UNLOAD") - && (UserLevel (u) >= 180)) - privmsg_list (nick, s_Services, ns_unload_help); - else if (!strcasecmp (av[2], "MODLIST") - && (UserLevel (u) >= 180)) - privmsg_list (nick, s_Services, ns_modlist_help); - else if (!strcasecmp (av[2], "USERDUMP") - && (UserLevel (u) >= 180) && (me.debug_mode)) - privmsg_list (nick, s_Services, ns_userdump_help); - else if (!strcasecmp (av[2], "CHANDUMP") - && (UserLevel (u) >= 180) && (me.debug_mode)) - privmsg_list (nick, s_Services, ns_chandump_help); - else if (!strcasecmp (av[2], "SERVERDUMP") - && (UserLevel (u) >= 180) && (me.debug_mode)) - privmsg_list (nick, s_Services, ns_serverdump_help); - else if (!strcasecmp (av[2], "JUPE") - && (UserLevel (u) >= 180)) - privmsg_list (nick, s_Services, ns_jupe_help); -#ifdef USE_RAW - else if (!strcasecmp (av[2], "RAW") - && (UserLevel (u) >= 180)) - privmsg_list (nick, s_Services, ns_raw_help); -#endif - else if (!strcasecmp (av[2], "LEVEL")) - privmsg_list (nick, s_Services, ns_level_help); - else if (!strcasecmp (av[2], "DEBUG")) - privmsg_list (nick, s_Services, ns_debug_help); - else if (!strcasecmp (av[2], "MODBOTLIST")) - privmsg_list (nick, s_Services, ns_modbotlist_help); - else if (!strcasecmp (av[2], "MODSOCKLIST")) - privmsg_list (nick, s_Services, ns_modsocklist_help); - else if (!strcasecmp (av[2], "MODTIMERLIST")) - privmsg_list (nick, s_Services, ns_modtimerlist_help); - else if (!strcasecmp (av[2], "MODBOTCHANLIST")) - privmsg_list (nick, s_Services, ns_modbotchanlist_help); - else if (!strcasecmp (av[2], "INFO")) - privmsg_list (nick, s_Services, ns_info_help); - else - prefmsg (nick, s_Services, "Unknown Help Topic: \2%s\2", av[2]); - } else if (!strcasecmp (av[1], "LEVEL")) { - prefmsg (nick, s_Services, "Your Level is %d", UserLevel (u)); - } else if (!strcasecmp (av[1], "LOAD")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s tried to LOAD, but is not authorised", nick); + /* Process command list */ + cmdnode = hash_lookup(botcmds, av[1]); + + if (cmdnode) { + cmd_ptr = hnode_get(cmdnode); + + /* Is user authorised to issue this command? */ + if (UserLevel (u) < cmd_ptr->ulevel) { + prefmsg (u->nick, s_Services, "Permission Denied"); + chanalert (s_Services, "%s tried to use %s, but is not authorised", u->nick, cmd_ptr->cmd); return; } - if (ac <= 2) { - prefmsg (nick, s_Services, "Please Specify a Module"); + /* First two parameters are bot name and command name so + * subtract 2 to get parameter count */ + if((ac - 2) < cmd_ptr->minparams ) { + prefmsg (u->nick, s_Services, "Syntax error: insufficient parameters"); + prefmsg (u->nick, s_Services, "/msg %s HELP %s for more information", s_Services, cmd_ptr->cmd); return; } - rval = load_module (av[2], u); - if (rval == NS_SUCCESS) { - chanalert (s_Services, "%s Loaded Module %s", u->nick, av[2]); - } else { - chanalert (s_Services, "%s Tried to Load Module %s, but Failed", u->nick, av[2]); - } - } else if (!strcasecmp (av[1], "MODLIST")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to MODLIST, but is not authorised", nick); + /* Missing handler?! */ + if(!cmd_ptr->handler) { return; } - list_module (u); - } else if (!strcasecmp (av[1], "UNLOAD")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to UNLOAD, but is not authorised", nick); - return; - } - if (ac <= 2) { - prefmsg (nick, s_Services, " Please Specify a Module Name"); - return; - } - rval = unload_module (av[2], u); - if (rval > 0) { - chanalert (s_Services, "%s Unloaded Module %s", u->nick, av[2]); - } + /* Seems OK so report the command call then call appropriate handler */ + chanalert (s_Services, "%s used %s", u->nick, cmd_ptr->cmd); + cmd_ptr->handler(u, av, ac); return; - } else if (!strcasecmp (av[1], "MODBOTLIST")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to MODBOTLIST, but is not authorised", nick); - return; - } - list_module_bots (u); - } else if (!strcasecmp (av[1], "MODSOCKLIST")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to MODSOCKLIST, but is not authorised", nick); - return; - } - list_sockets (u); - } else if (!strcasecmp (av[1], "MODTIMERLIST")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to MODTIMERLIST, but is not authorised", nick); - return; - } - list_module_timer (u); - } else if (!strcasecmp (av[1], "MODBOTCHANLIST")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s tried to MODBOTCHANLIST, but is not authorised", nick); - return; - } - botchandump (u); - } else if (!strcasecmp (av[1], "INFO")) { - ns_uptime (u); - chanalert (s_Services, "%s Wanted to see %s's info", u->nick, me.name); - } else if (!strcasecmp (av[1], "SHUTDOWN")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to SHUTDOWN, but is not authorised", nick); - return; - } - if (ac <= 2) { - chanalert (s_Services, "%s Requested SHUTDOWN", u->nick); - ns_shutdown (u, NULL); - } else { - tmp = joinbuf (av, ac, 2); - chanalert (s_Services, "%s Requested SHUTDOWN for: ", u->nick, tmp); - ns_shutdown (u, tmp); - free (tmp); - } - } else if (!strcasecmp (av[1], "VERSION")) { - ns_version (u); - chanalert (s_Services, "%s Wanted to know our version number ", u->nick); - } else if (!strcasecmp (av[1], "RELOAD")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to RELOAD, but is not authorised", nick); - return; - } - if (ac <= 2) { - prefmsg (nick, s_Services, "You must supply a Reason to Reload"); - return; - } - tmp = joinbuf (av, ac, 2); - chanalert (s_Services, "%s Wants me to RELOAD! for %s", u->nick, tmp); - ns_reload (u, tmp); - free (tmp); - } else if (!strcasecmp (av[1], "LOGS")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to view LOGS, but is not authorised", nick); - return; - } - ns_logs (u); - chanalert (s_Services, "%s Wants to Look at my Logs!!", u->nick); - } else if (!strcasecmp (av[1], "JUPE")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to JUPE, but is not authorised", nick); - return; - } - if (ac <= 2) { - prefmsg (nick, s_Services, "You must supply a ServerName to Jupe"); - return; - } - ns_jupe (u, av[2]); - } else if (!strcasecmp (av[1], "DEBUG")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (u->nick, s_Services, "Permission Denied, you need to be authorised to Enable Debug Mode!"); - return; - } - ns_set_debug (u->nick); - } else if (!strcasecmp (av[1], "USERDUMP")) { - if (!me.debug_mode) { - prefmsg (u->nick, s_Services, "\2Error:\2 Debug Mode Disabled"); - return; - } - if (ac <= 2) { - ns_user_dump (u, NULL); - } else { - ns_user_dump (u, av[2]); - } - } else if (!strcasecmp (av[1], "CHANDUMP")) { - if (!me.debug_mode) { - prefmsg (u->nick, s_Services, "\2Error:\2 Debug Mode Disabled"); - return; - } - if (ac < 3) { - ns_chan_dump (u, NULL); - } else { - ns_chan_dump (u, av[2]); - } - } else if (!strcasecmp (av[1], "SERVERDUMP")) { - if (!me.debug_mode) { - prefmsg (u->nick, s_Services, "\2Error:\2 Debug Mode Disabled"); - return; - } - ns_server_dump (u); - } else if (!strcasecmp (av[1], "RAW")) { - if (!(UserLevel (u) >= 180)) { - prefmsg (nick, s_Services, "Permission Denied"); - chanalert (s_Services, "%s Tried to use RAW, but is not authorised", nick); - return; - } -#ifdef USE_RAW - tmp = joinbuf (av, ac, 2); - ns_raw (u, tmp); - free (tmp); - return; -#else - prefmsg (nick, s_Services, "Raw is disabled"); - return; -#endif - } else { - prefmsg (nick, s_Services, "Unknown Command: \2%s\2", av[1]); - chanalert (s_Services, "%s Reqested %s, but that is an Unknown Command", u->nick, av[1]); } + + /* We have run out of commands so report failure */ + prefmsg (u->nick, s_Services, "Syntax error: unknown command: \2%s\2", av[1]); + chanalert (s_Services, "%s requested %s, but that is an unknown command", u->nick, av[1]); } -void -ns_shutdown (User * u, char *reason) +static void +ns_shutdown (User * u, char **av, int ac) { SET_SEGV_LOCATION(); - ircsnprintf (quitmsg, BUFSIZE, "%s [%s](%s) requested SHUTDOWN for %s.", - u->nick, u->username, u->hostname,(reason ? reason : no_reason)); + + char *tmp; + if (ac <= 2) { + ircsnprintf (quitmsg, BUFSIZE, "%s [%s](%s) requested SHUTDOWN for %s.", + u->nick, u->username, u->hostname, no_reason); + } else { + tmp = joinbuf (av, ac, 2); + chanalert (s_Services, "%s Wants me to SHUTDOWN for %s", u->nick, tmp); + ircsnprintf (quitmsg, BUFSIZE, "%s [%s](%s) requested SHUTDOWN for %s.", + u->nick, u->username, u->hostname, tmp); + free (tmp); + } globops (s_Services, quitmsg); nlog (LOG_NOTICE, LOG_CORE, quitmsg); - do_exit (NS_EXIT_NORMAL, quitmsg); } static void -ns_reload (User * u, char *reason) +ns_reload (User * u, char **av, int ac) { SET_SEGV_LOCATION(); + + char *tmp; + if (ac <= 2) { + prefmsg (u->nick, s_Services, "You must supply a Reason to Reload"); + return; + } + tmp = joinbuf (av, ac, 2); + chanalert (s_Services, "%s Wants me to RELOAD! for %s", u->nick, tmp); ircsnprintf (quitmsg, BUFSIZE, "%s [%s](%s) requested RELOAD for %s.", - u->nick, u->username, u->hostname, (reason ? reason : no_reason)); + u->nick, u->username, u->hostname, tmp); + free (tmp); + globops (s_Services, quitmsg); nlog (LOG_NOTICE, LOG_CORE, quitmsg); @@ -330,7 +259,7 @@ ns_reload (User * u, char *reason) } static void -ns_logs (User * u) +ns_logs (User * u, char **av, int ac) { #ifdef DEBUG prefmsg (u->nick, s_Services, "This command is disabled while in DEBUG."); @@ -353,85 +282,73 @@ ns_logs (User * u) } static void -ns_jupe (User * u, char *server) +ns_jupe (User * u, char **av, int ac) { char infoline[255]; + SET_SEGV_LOCATION(); ircsnprintf (infoline, 255, "[Jupitered by %s]", u->nick); - sserver_cmd (server, 1, infoline); - nlog (LOG_NOTICE, LOG_CORE, "%s!%s@%s jupitered %s", u->nick, u->username, u->hostname, server); - chanalert (s_Services, "%s Wants to JUPE this Server %s", u->nick, server); - prefmsg(u->nick, s_Services, "%s has been Jupitered", server); + sserver_cmd (av[2], 1, infoline); + nlog (LOG_NOTICE, LOG_CORE, "%s!%s@%s jupitered %s", u->nick, u->username, u->hostname, av[2]); + chanalert (s_Services, "%s Wants to JUPE this Server %s", u->nick, av[2]); + prefmsg(u->nick, s_Services, "%s has been Jupitered", av[2]); } -void -ns_set_debug (char *u) +static void +ns_set_debug (User * u, char **av, int ac) { SET_SEGV_LOCATION(); - if (!me.debug_mode) { + if ((!strcasecmp(av[2], "YES")) || (!strcasecmp(av[2], "ON"))) { me.debug_mode = 1; - if (u) { - globops (me.name, "\2DEBUG MODE\2 Activated by %s", u); - prefmsg (u, s_Services, "Debuging Mode Enabled!"); - } else { - globops (me.name, "\2DEBUG MODE\3 Active"); - } - } else { + globops (me.name, "\2DEBUG MODE\2 Activated by %s", u->nick); + prefmsg (u->nick, s_Services, "Debuging Mode Enabled!"); + } else if ((!strcasecmp(av[2], "NO")) || (!strcasecmp(av[2], "OFF"))) { me.debug_mode = 0; - if (!u) { - globops (me.name, "\2DEBUG MODE\2 Deactivated by %s", u); - prefmsg (u, s_Services, "Debuging Mode Disabled"); - } else { - globops (me.name, "\2DEBUG MODE\2 Deactivated"); - } + globops (me.name, "\2DEBUG MODE\2 Deactivated by %s", u->nick); + prefmsg (u->nick, s_Services, "Debuging Mode Disabled"); + } else { + prefmsg(u->nick, s_Services, + "Syntax Error: /msg %s HELP DEBUG for more info", + s_Services); + return; } } -#ifdef USE_RAW static void -ns_raw (User * u, char *message) +ns_user_dump (User * u, char **av, int ac) { - SET_SEGV_LOCATION(); - chanalert (s_Services, "\2RAW COMMAND\2 \2%s\2 Issued a Raw Command!(%s)", u->nick, message); - nlog (LOG_INFO, LOG_CORE, "RAW COMMAND %sIssued a Raw Command!(%s)", u->nick, message); - sts(message); -} -#endif -static void -ns_user_dump (User * u, char *nick) -{ - SET_SEGV_LOCATION(); - if (!(UserLevel (u) >= 180)) { - prefmsg (u->nick, s_Services, "Permission Denied, you do not have sufficient access rights for this command!"); + if (!me.debug_mode) { + prefmsg (u->nick, s_Services, "\2Error:\2 Debug Mode Disabled"); return; } chanalert (s_Services, "\2DEBUG\2 \2%s\2 Requested a UserDump!", u->nick); - UserDump (nick); + UserDump (((ac < 3) ? NULL : av[2])); } static void -ns_server_dump (User * u) +ns_server_dump (User * u, char **av, int ac) { SET_SEGV_LOCATION(); - if (!(UserLevel (u) >= 180)) { - prefmsg (u->nick, s_Services, "Permission Denied, you do not have sufficient access rights for this command!"); + if (!me.debug_mode) { + prefmsg (u->nick, s_Services, "\2Error:\2 Debug Mode Disabled"); return; } chanalert (s_Services, "\2DEBUG\2 \2%s\2 Requested a ServerDump!", u->nick); ServerDump (); } static void -ns_chan_dump (User * u, char *chan) +ns_chan_dump (User * u, char **av, int ac) { SET_SEGV_LOCATION(); - if (!(UserLevel (u) >= 180)) { - prefmsg (u->nick, s_Services, "Permission Denied, you do not have sufficient access rights for this command!"); + if (!me.debug_mode) { + prefmsg (u->nick, s_Services, "\2Error:\2 Debug Mode Disabled"); return; } chanalert (s_Services, "\2DEBUG\2 \2%s\2 Requested a ChannelDump!", u->nick); - chandump (chan); + ChanDump (((ac < 3) ? NULL : av[2])); } -static void -ns_uptime (User * u) + +static void +ns_info (User * u, char **av, int ac) { int uptime = me.now - me.t_start; @@ -458,10 +375,114 @@ ns_uptime (User * u) prefmsg (u->nick, s_Services, "End of Information."); } static void -ns_version (User * u) +ns_version (User * u, char **av, int ac) { SET_SEGV_LOCATION(); prefmsg (u->nick, s_Services, "\2NeoStats Version Information\2"); prefmsg (u->nick, s_Services, "NeoStats Version: %d.%d.%d%s", MAJOR, MINOR, REV, ircd_version); prefmsg (u->nick, s_Services, "http://www.neostats.net"); } + +static void +ns_show_level (User * u, char **av, int ac) +{ + prefmsg (u->nick, s_Services, "Your Level is %d", UserLevel (u)); +} + +static void +ns_do_help (User * u, char **av, int ac) +{ + bot_cmd* cmd_ptr; + int curlevel, lowlevel; + hnode_t *cmdnode; + hscan_t hs; + + if (ac < 3) { + lowlevel = 0; + curlevel = NS_ULEVEL_OPER; + chanalert (s_Services, "%s Requested %s Help", u->nick, s_Services); + prefmsg(u->nick, s_Services, "The following commands can be used with NeoStats:"); + + restartlevel: + hash_scan_begin(&hs, botcmds); + while ((cmdnode = hash_scan_next(&hs)) != NULL) { + cmd_ptr = hnode_get(cmdnode); + if ((cmd_ptr->ulevel < curlevel) && (cmd_ptr->ulevel >= lowlevel)) { + prefmsg(u->nick, s_Services, "%-20s %s", cmd_ptr->cmd, cmd_ptr->onelinehelp); + } + } + if (UserLevel(u) >= curlevel) { + switch (curlevel) { + case NS_ULEVEL_OPER: + curlevel = NS_ULEVEL_ADMIN; + lowlevel = NS_ULEVEL_OPER; + prefmsg(u->nick, s_Services, "\2Commands Available to Opers and Above:\2"); + goto restartlevel; + case NS_ULEVEL_ADMIN: + curlevel = NS_ULEVEL_ROOT; + lowlevel = NS_ULEVEL_ADMIN; + prefmsg(u->nick, s_Services, "\2Commands Available to Service Admins and Above:\2"); + goto restartlevel; + case NS_ULEVEL_ROOT: + curlevel = 201; + lowlevel = 200; + prefmsg(u->nick, s_Services, "\2Commands Available to Service Roots:\2"); + goto restartlevel; + default: + break; + } + } + privmsg_list (u->nick, s_Services, ns_help_on_help); + return; + } + chanalert (s_Services, "%s Requested %s Help on %s", u->nick, s_Services, av[2]); + + cmdnode = hash_lookup(botcmds, av[2]); + if (cmdnode) { + cmd_ptr = hnode_get(cmdnode); + if (UserLevel (u) < cmd_ptr->ulevel) { + prefmsg (u->nick, s_Services, "Permission Denied"); + return; + } + if(!cmd_ptr->helptext) { + /* Missing help text!!! */ + return; + } + privmsg_list (u->nick, s_Services, cmd_ptr->helptext); + return; + } + prefmsg (u->nick, s_Services, "Unknown Help Topic: \2%s\2", av[2]); +} + +static void +ns_load_module (User * u, char **av, int ac) +{ + if (load_module (av[2], u) == NS_SUCCESS) { + chanalert (s_Services, "%s Loaded Module %s", u->nick, av[2]); + } else { + chanalert (s_Services, "%s Tried to Load Module %s, but Failed", u->nick, av[2]); + } +} + +static void +ns_unload_module (User * u, char **av, int ac) +{ + if (unload_module (av[2], u) > 0) { + chanalert (s_Services, "%s Unloaded Module %s", u->nick, av[2]); + } +} + +#ifdef USE_RAW +static void +ns_raw (User * u, char **av, int ac) +{ + char *message; + + message = joinbuf (av, ac, 2); + SET_SEGV_LOCATION(); + chanalert (s_Services, "\2RAW COMMAND\2 \2%s\2 Issued a Raw Command!(%s)", u->nick, message); + nlog (LOG_INFO, LOG_CORE, "RAW COMMAND %sIssued a Raw Command!(%s)", u->nick, message); + sts(message); + free (message); +} +#endif diff --git a/services.h b/services.h new file mode 100644 index 00000000..0f8eb1b1 --- /dev/null +++ b/services.h @@ -0,0 +1,29 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond, Mark Hetherington +** http://www.neostats.net/ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ + +#ifndef _SERVICES_H_ +#define _SERVICES_H_ + +void servicesbot (char *nick, char **av, int ac); + +#endif /* _SERVICES_H_ */ diff --git a/sock.c b/sock.c index 9fff130b..3f658513 100644 --- a/sock.c +++ b/sock.c @@ -32,12 +32,17 @@ #include #include "conf.h" #include "log.h" +#include "timer.h" +#include "dns.h" static void recvlog (char *line); static struct sockaddr_in lsa; static int dobind; +int servsock; +char recbuf[BUFSIZE]; + /** @brief Connect to a server * * @param host to connect to @@ -49,6 +54,7 @@ static int dobind; int ConnectTo (char *host, int port) { + int ret; struct hostent *hp; struct sockaddr_in sa; int s; @@ -67,11 +73,14 @@ ConnectTo (char *host, int port) } if ((hp = gethostbyname (host)) == NULL) { + printf("1\n"); return NS_FAILURE; } - if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) + if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) { return NS_FAILURE; + printf("2\n"); + } if (dobind > 0) { if (bind (s, (struct sockaddr *) &lsa, sizeof (lsa)) < 0) { nlog (LOG_WARNING, LOG_CORE, "bind(): Warning, Couldn't bind to IP address %s", strerror (errno)); @@ -83,7 +92,9 @@ ConnectTo (char *host, int port) sa.sin_port = htons (port); bcopy (hp->h_addr, (char *) &sa.sin_addr, hp->h_length); - if (connect (s, (struct sockaddr *) &sa, sizeof (sa)) < 0) { + ret=connect (s, (struct sockaddr *) &sa, sizeof (sa)); + if (ret< 0) { + printf("%d %x\n",errno,ret); close (s); return NS_FAILURE; } @@ -105,14 +116,14 @@ read_loop () fd_set readfds, writefds, errfds; char c; char buf[BUFSIZE]; - Sock_List *mod_sock; + ModSock *mod_sock; struct pollfd *ufds; int pollsize, pollflag; hscan_t ss; hnode_t *sn; TimeOut = malloc (sizeof (struct timeval)); - ufds = malloc((sizeof *ufds) * getmaxsock()); + ufds = malloc((sizeof *ufds) * me.maxsocks); while (1) { SET_SEGV_LOCATION(); @@ -124,7 +135,7 @@ read_loop () FD_ZERO (&readfds); FD_ZERO (&writefds); FD_ZERO (&errfds); - memset(ufds, 0, (sizeof *ufds) * me.maxsocks); +// memset(ufds, 0, (sizeof *ufds) * me.maxsocks); pollsize = 0; FD_SET (servsock, &readfds); hash_scan_begin (&ss, sockh); @@ -363,20 +374,20 @@ sock_connect (int socktype, unsigned long ipaddr, int port, char *sockname, char int sock_disconnect (char *sockname) { - Sock_List *sock; + ModSock *mod_sock; fd_set fds; struct timeval tv; int i; - sock = findsock (sockname); - if (!sock) { + mod_sock = findsock (sockname); + if (!mod_sock) { nlog (LOG_WARNING, LOG_CORE, "Warning, Can not find Socket %s in list", sockname); return NS_FAILURE; } /* the following code makes sure its a valid file descriptor */ FD_ZERO (&fds); - FD_SET (sock->sock_no, &fds); + FD_SET (mod_sock->sock_no, &fds); tv.tv_sec = 0; tv.tv_usec = 0; i = select (1, &fds, NULL, NULL, &tv); @@ -384,8 +395,8 @@ sock_disconnect (char *sockname) nlog (LOG_WARNING, LOG_CORE, "Warning, Bad File Descriptor %s in list", sockname); return NS_FAILURE; } - nlog (LOG_DEBUG3, LOG_CORE, "Closing Socket %s with Number %d", sockname, sock->sock_no); - close (sock->sock_no); + nlog (LOG_DEBUG3, LOG_CORE, "Closing Socket %s with Number %d", sockname, mod_sock->sock_no); + close (mod_sock->sock_no); del_socket (sockname); return NS_SUCCESS; } diff --git a/sock.h b/sock.h index 61c739d8..66a59c67 100644 --- a/sock.h +++ b/sock.h @@ -26,8 +26,9 @@ #ifndef SOCK_H #define SOCK_H - +int ConnectTo (char * host, int port); +void read_loop (void); +int getmaxsock (void); void sts (char *fmt, ...); - #endif diff --git a/stats.h b/stats.h index 103847c2..c70a84e6 100644 --- a/stats.h +++ b/stats.h @@ -48,6 +48,7 @@ #include #include #include +#include "pcre.h" #include "list.h" #include "hash.h" #include "config.h" @@ -71,36 +72,13 @@ #include "Bahamut.h" #elif QUANTUM == 1 #include "QuantumIRCd.h" +#elif LIQUID == 1 +#include "liquidircd.h" #else #error Error, you must select an IRCD to use. See ./configure --help for more information #endif -/* Numeric used by NeoStats. Need to be in ircd code eventually - * - */ - -#define RPL_STATSLINKINFO 211 -#define RPL_STATSCOMMANDS 212 -#define RPL_STATSCLINE 213 -#define RPL_STATSOLDNLINE 214 -#define RPL_STATSNLINE RPL_STATSOLDNLINE - -#define RPL_ENDOFSTATS 219 - -#define RPL_STATSLLINE 241 -#define RPL_STATSUPTIME 242 -#define RPL_STATSOLINE 243 - -#define RPL_ADMINME 256 -#define RPL_ADMINLOC1 257 -#define RPL_ADMINLOC2 258 - -#define RPL_VERSION 351 - -#define RPL_MOTD 372 - -#define RPL_MOTDSTART 375 -#define RPL_ENDOFMOTD 376 +#include "numeric.h" /** this is a security hack to give the coders the right levels to debug NeoStats. * Don't define unless we ask you to @@ -111,7 +89,7 @@ #define MOD_PATH "dl" #define RECV_LOG "logs/recv.log" #define MOTD_FILENAME "neostats.motd" -#define ADMIN_FILENAME "stats.admin" +#define ADMIN_FILENAME "neostats.admin" #define PID_FILENAME "neostats.pid" #define CHANLEN 50 @@ -124,6 +102,7 @@ #define MAXREALNAME 50 #define MODESIZE 53 #define PARAMSIZE MAXNICK+MAXUSER+MAXHOST+10 +#define MAXCMDSIZE 15 /* MAXPATH * used to determine buffer sizes for file system operations @@ -172,7 +151,13 @@ #define NS_SUCCESS 1 #define NS_FAILURE -1 -#define NS_ERROR_xxx -2 +/* Specific errors beyond SUCCESS/FAILURE so that functions can handle errors + * Treat as unsigned with top bit set to give us a clear distinction from + * other values and use a typedef ENUM so that we can indicate return type */ +typedef enum NS_ERR { + NS_ERR_NICK_IN_USE = 0x8000001, + NS_ERR_OUT_OF_MEMORY = 0x8000002, +}NS_ERR ; /* do_exit call exit type definitions */ typedef enum { @@ -183,6 +168,11 @@ typedef enum { NS_EXIT_SEGFAULT, }NS_EXIT_TYPE; +/* NeoStats levels */ +#define NS_ULEVEL_ROOT 200 +#define NS_ULEVEL_ADMIN 185 +#define NS_ULEVEL_OPER 40 + #define SEGV_LOCATION_BUFSIZE 255 #ifdef LEAN_AND_MEAN #define SET_SEGV_LOCATION() @@ -211,20 +201,21 @@ typedef enum { #define ARRAY_COUNT (a) ((sizeof ((a)) / sizeof ((a)[0])) -int servsock; +extern int servsock; +extern char recbuf[BUFSIZE]; extern char s_Services[MAXNICK]; extern const char ircd_version[]; -char recbuf[BUFSIZE]; -char segv_location[SEGV_LOCATION_BUFSIZE]; -char segv_inmodule[SEGV_INMODULE_BUFSIZE]; -jmp_buf sigvbuf; +extern const char services_bot_modes[]; +extern char segv_location[SEGV_LOCATION_BUFSIZE]; +extern char segv_inmodule[SEGV_INMODULE_BUFSIZE]; +extern jmp_buf sigvbuf; -hash_t *sh; -hash_t *uh; -hash_t *ch; +extern hash_t *sh; +extern hash_t *uh; +extern hash_t *ch; /* this is the dns structure */ -adns_state ads; +extern adns_state ads; /* version info */ extern const char version_date[], version_time[]; @@ -355,9 +346,6 @@ struct ping { } ping; /* sock.c */ -int ConnectTo (char * host, int port); -void read_loop (void); -int getmaxsock (void); int sock_connect (int socktype, unsigned long ipaddr, int port, char *sockname, char *module, char *func_read, char *func_write, char *func_error); int sock_disconnect (char *sockname); @@ -371,15 +359,12 @@ void do_exit (NS_EXIT_TYPE exitcode, char* quitmsg); void fatal_error(char* file, int line, char* func, char* error_text); #define FATAL_ERROR(error_text) fatal_error(__FILE__, __LINE__, __PRETTY_FUNCTION__,(error_text)); - /* misc.c */ void strip (char * line); void *smalloc (long size); char *sstrdup (const char * s); -char *strlower (char * s); char *strlwr (char * s); void AddStringToList (char ***List, char S[], int *C); -void FreeList (char **List, int C); void strip_mirc_codes(char *text); char *sctime (time_t t); char *sftime (time_t t); @@ -390,9 +375,8 @@ void parse (char* line); char *joinbuf (char **av, int ac, int from); int split_buf (char *buf, char ***argv, int colon_special); int flood (User * u); -int init_bot (char * nick, char * user, char * host, char * rname, char *modes, char * modname); +int init_bot (char * nick, char * user, char * host, char * rname, const char *modes, char * modname); int del_bot (char * nick, char * reason); -void Module_Event (char * event, char **av, int ac); /* ircd specific files */ void prefmsg (char * to, const char * from, char * fmt, ...); @@ -401,83 +385,26 @@ void notice (char *to, const char *from, char *fmt, ...); void privmsg_list (char *to, char *from, const char **text); void globops (char * from, char * fmt, ...); -/* dl.c */ -int bot_nick_change (char * oldnick, char *newnick); - -/* timer.c */ -void CheckTimers (void); - /* users.c */ -void AddUser (const char *nick, const char *user, const char *host, const char *server, const unsigned long ip, const unsigned long TS); -void DelUser (const char *nick); -void AddRealName (const char *nick, const char *realname); -void Change_User (User *u, const char * newnick); User *finduser (const char *nick); -void UserDump (char *nick); -void part_u_chan (list_t *list, lnode_t *node, void *v); -void UserMode (const char *nick, const char *modes, int smode); -int init_user_hash (void); int UserLevel (User *u); -void Do_Away (User *u, const char *awaymsg); -void KillUser (const char *nick); /* server.c */ -void AddServer (char *name, char *uplink, int hops); -void DelServer (char *name); Server *findserver (const char *name); -void ServerDump (void); -int init_server_hash (void); -void TimerPings (void); - -/* ns_help.c */ -extern const char *ns_help[]; -extern const char *ns_help_on_help[]; -extern const char *ns_myuser_help[]; -extern const char *ns_shutdown_help[]; -extern const char *ns_reload_help[]; -extern const char *ns_logs_help[]; -#ifdef USE_RAW -extern const char *ns_raw_help[]; -#endif -extern const char *ns_debug_help[]; -extern const char *ns_userdump_help[]; -extern const char *ns_chandump_help[]; -extern const char *ns_serverdump_help[]; -extern const char *ns_version_help[]; -extern const char *ns_load_help[]; -extern const char *ns_unload_help[]; -extern const char *ns_modlist_help[]; -extern const char *ns_jupe_help[]; -extern const char *ns_level_help[]; -extern const char *ns_modbotlist_help[]; -extern const char *ns_modsocklist_help[]; -extern const char *ns_modtimerlist_help[]; -extern const char *ns_modbotchanlist_help[]; -extern const char *ns_info_help[]; - -/* services.c */ -void servicesbot (char *nick, char **av, int ac); -void ns_set_debug (char *u); -void ns_shutdown (User * u, char *reason); /* chans.c */ -void chandump (char *chan); -void part_chan (User * u, char *chan); -void join_chan (User * u, char *chan); -void change_user_nick (Chans * c, char *newnick, char *oldnick); Chans *findchan (char *chan); -int ChanMode (char *origin, char **av, int ac); -void Change_Topic (char *, Chans *, time_t t, char *); -void ChangeChanUserMode (Chans * c, User * u, int add, long mode); -void kick_chan (User *, char *, User *); -void Change_Chan_Ts (Chans * c, time_t tstime); int CheckChanMode (Chans * c, long mode); int IsChanMember(Chans *c, User *u); -int init_chan_hash (void); /* dns.c */ int dns_lookup (char *str, adns_rrtype type, void (*callback) (char *data, adns_answer * a), char *data); -int init_dns (void); -void do_dns (void); + +/* services.c */ +typedef void (*bot_cmd_handler) (User * u, char **av, int ac); +int add_services_cmd(const char cmd[MAXCMDSIZE], bot_cmd_handler handler, int minparams, int ulevel, const char** helptext, const char onelinehelp[255]); +int init_services(); +int del_services_cmd(const char cmd[MAXCMDSIZE]); + #endif diff --git a/timer.c b/timer.c index 51a9b47f..532f0542 100644 --- a/timer.c +++ b/timer.c @@ -27,6 +27,7 @@ #include "dl.h" #include "log.h" #include "conf.h" +#include "server.h" static int midnight = 0; @@ -36,52 +37,14 @@ static void TimerMidnight (void); void CheckTimers (void) { - Mod_Timer *mod_ptr = NULL; - time_t current = me.now; - hscan_t ts; - hnode_t *tn; - -/* First, lets see if any modules have a function that is due to run..... */ - hash_scan_begin (&ts, th); - while ((tn = hash_scan_next (&ts)) != NULL) { - SET_SEGV_LOCATION(); - mod_ptr = hnode_get (tn); - if (current - mod_ptr->lastrun > mod_ptr->interval) { - strlcpy (segv_location, mod_ptr->modname, SEGV_LOCATION_BUFSIZE); - SET_SEGV_INMODULE(mod_ptr->modname); - if (setjmp (sigvbuf) == 0) { - if (mod_ptr->function () < 0) { - nlog(LOG_DEBUG2, LOG_CORE, "Deleting Timer %s for Module %s as requested", mod_ptr->timername, mod_ptr->modname); - hash_scan_delete(th, tn); - hnode_destroy(tn); - free(mod_ptr); - } else { - mod_ptr->lastrun = (int) me.now; - } - } else { - nlog (LOG_CRITICAL, LOG_CORE, "setjmp() Failed, Can't call Module %s\n", mod_ptr->modname); - } - CLEAR_SEGV_INMODULE(); - } - } + run_mod_timers(); SET_SEGV_LOCATION(); - if (current - ping.last_sent > me.pingtime) { - TimerPings (); + if (me.now - ping.last_sent > me.pingtime) { + PingServers (); flush_keeper(); ping.last_sent = me.now; #ifdef DEBUG - if (hash_verify (sockh) == 0) { - nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the socket hash"); - } - if (hash_verify (mh) == 0) { - nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the Module hash"); - } - if (hash_verify (bh) == 0) { - nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the Bot hash"); - } - if (hash_verify (th) == 0) { - nlog (LOG_CRITICAL, LOG_CORE, "Eeeek, Corruption of the Timer hash"); - } + verify_hashes(); #endif /* flush log files */ fflush (NULL); @@ -95,7 +58,7 @@ CheckTimers (void) } } -void +static void TimerMidnight (void) { nlog (LOG_DEBUG1, LOG_CORE, "Its midnight!!! -> %s", sctime (me.now)); @@ -105,8 +68,7 @@ TimerMidnight (void) static int is_midnight (void) { - time_t current = me.now; - struct tm *ltm = localtime (¤t); + struct tm *ltm = localtime (&me.now); if (ltm->tm_hour == 0) return 1; diff --git a/timer.h b/timer.h new file mode 100644 index 00000000..f5adfaaf --- /dev/null +++ b/timer.h @@ -0,0 +1,29 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond, Mark Hetherington +** http://www.neostats.net/ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +void CheckTimers (void); + +#endif /* _TIMER_H_ */ diff --git a/users.c b/users.c index e45b56a5..6de8b24c 100644 --- a/users.c +++ b/users.c @@ -29,9 +29,13 @@ #include "hash.h" #include "dl.h" #include "log.h" +#include "users.h" +#include "chans.h" -static void doDelUser (const char *, int); -static User *new_user (const char *); +hash_t *uh; + +static void doDelUser (const char *nick, int killflag); +static User *new_user (const char *nick); static User * new_user (const char *nick) @@ -107,7 +111,7 @@ AddRealName (const char *nick, const char *realname) nlog (LOG_DEBUG2, LOG_CORE, "RealName(%s): %s", nick, realname); strlcpy (u->realname, realname, MAXREALNAME); AddStringToList (&av, u->nick, &ac); - Module_Event (EVENT_SIGNON, av, ac); + ModuleEvent (EVENT_SIGNON, av, ac); free (av); } @@ -131,7 +135,7 @@ DelUser (const char *nick) } static void -doDelUser (const char *nick, int i) +doDelUser (const char *nick, int killflag) { User *u; hnode_t *un; @@ -154,16 +158,16 @@ doDelUser (const char *nick, int i) /* run the event to delete a user */ AddStringToList (&av, u->nick, &ac); - if (i == 0) { - Module_Event (EVENT_SIGNOFF, av, ac); - } else if (i == 1) { - Module_Event (EVENT_KILL, av, ac); + if (killflag == 0) { + ModuleEvent (EVENT_SIGNOFF, av, ac); + } else if (killflag == 1) { + ModuleEvent (EVENT_KILL, av, ac); } free (av); /* if its one of our bots, remove it from the modlist */ if (findbot (u->nick)) { - if (i == 1) + if (killflag == 1) nlog (LOG_NOTICE, LOG_CORE, "Deleting Bot %s as it was killed", u->nick); del_mod_user (u->nick); } @@ -175,7 +179,7 @@ doDelUser (const char *nick, int i) } void -Do_Away (User * u, const char *awaymsg) +UserAway (User * u, const char *awaymsg) { char **av; int ac = 0; @@ -183,11 +187,11 @@ Do_Away (User * u, const char *awaymsg) AddStringToList (&av, u->nick, &ac); if ((u->is_away == 1) && (!awaymsg)) { u->is_away = 0; - Module_Event (EVENT_AWAY, av, ac); + ModuleEvent (EVENT_AWAY, av, ac); } else if ((u->is_away == 0) && (awaymsg)) { u->is_away = 1; AddStringToList (&av, (char *) awaymsg, &ac); - Module_Event (EVENT_AWAY, av, ac); + ModuleEvent (EVENT_AWAY, av, ac); } free (av); } @@ -223,7 +227,7 @@ Change_User (User * u, const char *newnick) hash_insert (uh, un, u->nick); AddStringToList (&av, u->nick, &ac); - Module_Event (EVENT_NICKCHANGE, av, ac); + ModuleEvent (EVENT_NICKCHANGE, av, ac); free (av); free (oldnick); } @@ -321,9 +325,9 @@ UserLevel (User * u) #ifdef CODERHACK /* this is only cause I dun have the right O lines on some of my "Beta" Networks, so I need to hack this in :) */ if (!strcasecmp (u->nick, "FISH")) - tmplvl = 200; + tmplvl = NS_ULEVEL_ROOT; if (!strcasecmp (u->nick, "SHMAD")) - tmplvl = 200; + tmplvl = NS_ULEVEL_ROOT; #endif #endif @@ -380,9 +384,9 @@ UserMode (const char *nick, const char *modes, int smode) AddStringToList (&av, (char *) modes, &ac); if (smode > 0) { - Module_Event (EVENT_SMODE, av, ac); + ModuleEvent (EVENT_SMODE, av, ac); } else { - Module_Event (EVENT_UMODE, av, ac); + ModuleEvent (EVENT_UMODE, av, ac); } diff --git a/users.h b/users.h new file mode 100644 index 00000000..004963f0 --- /dev/null +++ b/users.h @@ -0,0 +1,38 @@ +/* NeoStats - IRC Statistical Services +** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond, Mark Hetherington +** http://www.neostats.net/ +** +** 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 +** +** NeoStats CVS Identification +** $Id$ +*/ + +#ifndef _USERS_H_ +#define _USERS_H_ + +void AddUser (const char *nick, const char *user, const char *host, const char *server, const unsigned long ip, const unsigned long TS); +void DelUser (const char *nick); +void AddRealName (const char *nick, const char *realname); +void Change_User (User *u, const char * newnick); +void UserDump (char *nick); +void part_u_chan (list_t *list, lnode_t *node, void *v); +void UserMode (const char *nick, const char *modes, int smode); +int init_user_hash (void); +void UserAway (User *u, const char *awaymsg); +void KillUser (const char *nick); + +#endif /* _USERS_H_ */