Initial import of bahamut-1.8.3 BAHAMUT-1-8-3
authorMatthew Mondor <mmondor@pulsar-zone.net>
Wed, 12 Jan 2005 07:44:59 +0000 (07:44 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Wed, 12 Jan 2005 07:44:59 +0000 (07:44 +0000)
161 files changed:
CHANGES [new file with mode: 0644]
INSTALL [new file with mode: 0644]
LICENCE.pcre [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
config.guess [new file with mode: 0755]
config.sub [new file with mode: 0755]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
doc/Bahamut-team [new file with mode: 0644]
doc/CHANGES_1.4 [new file with mode: 0644]
doc/CODING_STANDARD [new file with mode: 0644]
doc/Makefile.in [new file with mode: 0644]
doc/clones.txt [new file with mode: 0644]
doc/modules.txt [new file with mode: 0644]
doc/old/Authors [new file with mode: 0644]
doc/old/FAQ [new file with mode: 0644]
doc/old/Hybrid-team [new file with mode: 0644]
doc/old/Operators [new file with mode: 0644]
doc/old/README [new file with mode: 0644]
doc/old/README.hybrid [new file with mode: 0644]
doc/opers.txt [new file with mode: 0755]
doc/pcrepattern.html [new file with mode: 0644]
doc/reference.conf [new file with mode: 0755]
doc/server-version-info [new file with mode: 0644]
doc/template.conf [new file with mode: 0644]
include/.indent.pro [new file with mode: 0644]
include/blalloc.h [new file with mode: 0644]
include/cdefs.h [new file with mode: 0644]
include/channel.h [new file with mode: 0644]
include/clones.h [new file with mode: 0644]
include/common.h [new file with mode: 0644]
include/config.h [new file with mode: 0644]
include/confparse.h [new file with mode: 0644]
include/dbuf.h [new file with mode: 0644]
include/defs.h [new file with mode: 0644]
include/dh.h [new file with mode: 0644]
include/fdlist.h [new file with mode: 0644]
include/fds.h [new file with mode: 0644]
include/find.h [new file with mode: 0644]
include/h.h [new file with mode: 0644]
include/hash.h [new file with mode: 0644]
include/hooks.h [new file with mode: 0644]
include/inet.h [new file with mode: 0644]
include/ircsprintf.h [new file with mode: 0644]
include/msg.h [new file with mode: 0644]
include/nameser.h [new file with mode: 0644]
include/numeric.h [new file with mode: 0644]
include/patchlevel.h [new file with mode: 0644]
include/pcre.h [new file with mode: 0644]
include/pcre_config.h [new file with mode: 0644]
include/pcre_internal.h [new file with mode: 0644]
include/queue.h [new file with mode: 0644]
include/res.h [new file with mode: 0644]
include/resolv.h [new file with mode: 0644]
include/sbuf.h [new file with mode: 0644]
include/send.h [new file with mode: 0644]
include/setup.h.in [new file with mode: 0644]
include/sock.h [new file with mode: 0644]
include/struct.h [new file with mode: 0644]
include/structfunc.h [new file with mode: 0644]
include/sys.h [new file with mode: 0644]
include/throttle.h [new file with mode: 0644]
include/userban.h [new file with mode: 0644]
include/whowas.h [new file with mode: 0644]
include/zlink.h [new file with mode: 0644]
install-sh [new file with mode: 0755]
src/Makefile.in [new file with mode: 0644]
src/blalloc.c [new file with mode: 0644]
src/bsd.c [new file with mode: 0644]
src/channel.c [new file with mode: 0644]
src/clientlist.c [new file with mode: 0644]
src/clones.c [new file with mode: 0644]
src/confparse.c [new file with mode: 0644]
src/dh.c [new file with mode: 0644]
src/fdlist.c [new file with mode: 0644]
src/fds.c [new file with mode: 0644]
src/hash.c [new file with mode: 0644]
src/hide.c [new file with mode: 0644]
src/inet_addr.c [new file with mode: 0644]
src/ircd.c [new file with mode: 0644]
src/ircsprintf.c [new file with mode: 0644]
src/list.c [new file with mode: 0644]
src/m_nick.c [new file with mode: 0644]
src/m_rwho.c [new file with mode: 0644]
src/m_server.c [new file with mode: 0644]
src/m_services.c [new file with mode: 0644]
src/m_stats.c [new file with mode: 0644]
src/m_who.c [new file with mode: 0644]
src/match.c [new file with mode: 0644]
src/modules.c [new file with mode: 0644]
src/packet.c [new file with mode: 0644]
src/parse.c [new file with mode: 0644]
src/pcre.c [new file with mode: 0644]
src/pcre_chartables.c [new file with mode: 0644]
src/rc4.c [new file with mode: 0644]
src/res.c [new file with mode: 0644]
src/res_comp.c [new file with mode: 0644]
src/res_init.c [new file with mode: 0644]
src/res_mkquery.c [new file with mode: 0644]
src/s_auth.c [new file with mode: 0644]
src/s_bsd.c [new file with mode: 0644]
src/s_conf.c [new file with mode: 0644]
src/s_debug.c [new file with mode: 0644]
src/s_err.c [new file with mode: 0644]
src/s_misc.c [new file with mode: 0644]
src/s_numeric.c [new file with mode: 0644]
src/s_serv.c [new file with mode: 0644]
src/s_user.c [new file with mode: 0644]
src/sbuf.c [new file with mode: 0644]
src/scache.c [new file with mode: 0644]
src/send.c [new file with mode: 0644]
src/socketengine_devpoll.c [new file with mode: 0644]
src/socketengine_epoll.c [new file with mode: 0644]
src/socketengine_kqueue.c [new file with mode: 0644]
src/socketengine_poll.c [new file with mode: 0644]
src/socketengine_select.c [new file with mode: 0644]
src/struct.c [new file with mode: 0644]
src/support.c [new file with mode: 0644]
src/throttle.c [new file with mode: 0644]
src/userban.c [new file with mode: 0644]
src/version.c.SH [new file with mode: 0755]
src/whowas.c [new file with mode: 0644]
src/zlink.c [new file with mode: 0644]
tools/Makefile.in [new file with mode: 0644]
tools/convert_conf.c [new file with mode: 0644]
tools/mkpasswd.c [new file with mode: 0644]
zlib/ChangeLog [new file with mode: 0644]
zlib/FAQ [new file with mode: 0644]
zlib/INDEX [new file with mode: 0644]
zlib/Makefile.in [new file with mode: 0644]
zlib/README [new file with mode: 0644]
zlib/adler32.c [new file with mode: 0644]
zlib/algorithm.txt [new file with mode: 0644]
zlib/compress.c [new file with mode: 0644]
zlib/configure [new file with mode: 0755]
zlib/crc32.c [new file with mode: 0644]
zlib/crc32.h [new file with mode: 0644]
zlib/deflate.c [new file with mode: 0644]
zlib/deflate.h [new file with mode: 0644]
zlib/example.c [new file with mode: 0644]
zlib/gzio.c [new file with mode: 0644]
zlib/infback.c [new file with mode: 0644]
zlib/inffast.c [new file with mode: 0644]
zlib/inffast.h [new file with mode: 0644]
zlib/inffixed.h [new file with mode: 0644]
zlib/inflate.c [new file with mode: 0644]
zlib/inflate.h [new file with mode: 0644]
zlib/inftrees.c [new file with mode: 0644]
zlib/inftrees.h [new file with mode: 0644]
zlib/minigzip.c [new file with mode: 0644]
zlib/trees.c [new file with mode: 0644]
zlib/trees.h [new file with mode: 0644]
zlib/uncompr.c [new file with mode: 0644]
zlib/zconf.in.h [new file with mode: 0644]
zlib/zlib.3 [new file with mode: 0644]
zlib/zlib.h [new file with mode: 0644]
zlib/zutil.c [new file with mode: 0644]
zlib/zutil.h [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..b53d508
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,164 @@
+Changes for 1.8.3
+-----------------
+
+- Fixed rehash bug with specific conf changes that caused memory
+  corruption and crashes
+- Fixed a possible buffer overflow issue in oper hostmasking
+- Fixed solaris devpoll socket engine logic problems.
+- Fixed class reference counting errors.
+- Fixed a bug with cmode +j propagation on resync
+- Added a second set of join counters to send warning notices
+  to +d opers when the default thresholds are met.  Still sends
+  a throttle notice when cmode +j thresholds are met.
+- Changed ERR_BANLISTFULL to respond depending on which list
+  (+b/+I/+e) is full.  From Unim4trix0 (Unim4trix0@gmail.com).
+- Readded clone limiting (now better!), see doc/clones.txt
+- Range limiting for cmode +j args (static 4:2 - 60:60, rate 1/8 - 2/1).
+- Fixed spamming redudant cmode changes.
+- Fixed bad handling of bogus oper messages (from Zeke Gomez).
+- Fixed rehash issue with module blocks.
+- Fixed samode with no mode arguments choosing one at random for you :)
+- Increased throughput on data sent to clients.
+- Changed ERR_CHANNELISFULL to respond depending on which limit
+  (+l/+j) is reached.  From Unim4trix0 (Unim4trix0@gmail.com),
+  discovered by Watchminister (watchminister@gmail.com).
+- Changed cmode +O join rejection to use ERR_INVITEONLY numeric.
+- Enabled umode +s (oper kills) for users, disabled +g (useless).
+- RWHO now sports clone and joined channel awareness.
+- Added flags token to allow blocks, see doc/reference.conf
+- Allow blocks with passwords are now hidden from nonopers in /stats I
+
+
+Changes for 1.8.2
+-----------------
+
+- fixed entropy generation on OpenBSD and cleaned up status messages 
+  (from Mark19960)
+- fixed cosmetic error in /stats C reply
+- fixed autoconnect blast when connect->port was specified but not 
+  class->connfreq (from Mark19960)
+- fixed crash on /stats request from a server (from Mark19960)
+- fixed invite exceptions not working at all (from wshs)
+- fixed ban exemptions not affecting bquiet (from wshs)
+- fixed -e/-I sync failure during netbursts of older channels
+- tweaked reference.conf
+- corrected new find_port() check
+- now removing simbans (restrict blocks) on rehash (from Ballsy)
+- fixed cosmetic errors in nick collision kills (from Goplat)
+- Converted old "X line" messages to "X block"
+- Changed stray free() to MyFree()
+- Corrected a tolower() reference I missed the first time around
+- Removed ancient prototype causing compile failures with gcc 3.4
+- Fixed 005 WATCH token (was using incorrect number)
+- Removed temporary debug code causing compile failure when !USE_SYSLOG
+- Finished correcting same port, multiple IP binds
+- stats L shows user IPs again (but still hides opers)
+- Numerics 477 and 487 no longer hardcoded dal.net
+- Fixed numeric responses for certain PRIVMSG/NOTICE targets
+- Updated reference.conf and template.conf with additional bits and better
+  documentation of Allow block quirks
+- Tweaked handling and display of removed-but-not-yet-deleted conf entries
+  after a rehash (Oper, Connect, Class)
+- Oper block host entries are now matched against the user@host that would
+  appear on IRC (previously what they matched was a bit convoluted)
+- Corrected class maxusers handling -- applies to the class, not the allow
+  block
+- Updated 004 and 005 numerics
+- trace now shows the class list again
+- bquiet is now using deferred cache concept from ratbox to deal with ban
+  exemptions correctly
+- Ban exemptions are now marked with type and checked during nick_is_banned()
+- Begin unbreaking redundant ban check -- will take at least one more release
+  to complete
+- Cleaned up related channel ban code
+- CHOOK_10SEC is now called when it should be (from wshs)
+- Unbroke modules command handling (from wshs)
+- Fixed modules load crash when there was no Modules block in ircd.conf 
+  (from wshs)
+- Fixed SVSKILL crash on no parameters (from Zeke Gomez)
+- Compile fixes for DNS_DEBUG and DEBUGMODE (but don't use DEBUGMODE)
+- Compile fix for missing LINE_MAX
+- Spellcheck: virii -> viruses (from The List)
+- HIDE_LINKS functionality inverted and attached to show_links token in 
+  Options block
+- NO_CHANOPS_WHEN_SPLIT functionality inverted and attached to allow_split_ops
+  token in Options block
+- Merging duplicate Allow blocks during rehash to avoid admin confusion
+- Boolean Options block items can now be disabled by rehash
+- Fixed "servtype hub" causing certain previously specified options to reset
+  to defaults
+- Fixed a loop error report that was broken when it was introduced 11 years ago
+- Ignoring SVSMODE +o/O attempts (not handled correctly)
+- Repaired showing of +s channels in LIST to +A users
+- Fixed and document DENY_SERVICES_MSGS (from wshs)
+- Corrected structfunc.h prototypes for modules (from wshs)
+- Added SVSMODE -e/-I support for channels
+- Fixed .maxclients not being read on startup
+- Rewrote m_invite(), behaves logically now
+- Enabled cmode +j (joins:secs rate limit)
+- Fixed resolver cache corruption (and crash), mostly affected newer RedHat
+  thanks huni and Sarcast for patience, analysis, and cores
+- More conf documentation clarity
+- Unresolved class reference in conf now reports class name
+- Updated RPL_VERSION, RPL_ISUPPORT; now running from a cache in s_debug.c
+- Removed sockhost data from RPL_YOURHOST, since it was always zeroed
+- Squished more memory corruption bugs
+  thanks Glitch and dev0 for more patience and cores
+- Module support is now unconditionally disabled under OS X
+- Build system now handles FD_SETSIZE properly when using the select engine
+- Internal maxconnections and SET MAX handling is rational again
+  ircd can run with as few as 20 FDs and support 10 clients
+- Ripped out obsolete sendto_match_servs()
+- Added support for non-noquit servers in #define NOQUIT
+- Updated zlib to version 1.2.1
+- Restricted stats q/Q/g to opers
+- Show IPs in stats C to services and local +A
+- Added RWHO, see /rwho ? and doc/pcrepattern.html for info
+- Set +k and +s umodes to opers only.
+- Fixed a bug in m_trace that caused cores when issuing a /trace when
+  connecting to a server.
+- ERR_WHOSYNTAX reply changed to ERR_NOPRIVILEGES for non opers trying oper
+  restricted who, but using correct syntax
+  fixed by Unim4trix0 (Unim4trix0@gmail.com)
+
+
+Changes for 1.8.0
+-----------------
+
+- Removed backwards compatibility for:
+       - NOQUIT
+       - UNCONNECT
+       - TSMODE
+       - NICKIP
+  All of these modes are now REQUIRED.
+- Removed Code and defines for outdated stuff:
+       - FOLLOW_IDENT_RFC
+       - NO_IDENT_SYSTYPE_OTHER
+       - NO_MIXED_CASE
+       - OLD_Y_LIMIT
+       - USE_REJECT_HOLD
+  This code is no longer used.
+- Removed define options for:
+       - CLIENT_COUNT
+  This code is now manditory.
+- Completely rewrote configuration structures.
+- Rewrote methods for connecting to servers.
+- Outdated Link structure config linking, now linking directly from
+  aClient(user/serv) structures to appropriate conf strucutures.
+- Backported lucas's aListener structure and routines from defunct
+  bahamut-devel tree.
+- Implemented +I/+e channel lists - thanks seddy.
+- Ports now can be opened and closed on /rehash
+- config file format changed to bind-style
+- klinefile outdated
+- replaced dbufs with sbufs - shared buffers.
+- removed old High Traffic Mode (HTM) code
+- config option block added to outdate use of ./config script
+- fixes for solaris and OSX compatibility
+- Added support for /dev/poll and epoll socket engines - thanks seddy.
+- Moved m_server and m_dkey into m_server.c
+- Moved m_stats into m_stats.c
+- Fixed memory counting routines.  Thanks Quension!
+- Outdated modules.ini and all inifile uses - now a modules {} conf block
+- Updated m_module to allow non-admin viewing of hooks and loaded modules
+- Added module hooks for user and channel targetted privmsgs
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..1bf9b85
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,29 @@
+HOW TO BUILD:
+------------- 
+1.  Run the configure script.  It will setup include/setup.h and the
+    Makefiles to match your system:
+       ./configure
+
+    Type ./configure --help to see different options.  Most people will not
+    need to alter these.
+
+2.  [DISCOURAGED] Edit the the "include/config.h" file.  This allows you to 
+    change various options of how the ircd will operate. Usually the defaults
+    are OK.
+
+3.  "make" should build ircd.
+
+4.  "make install" will install the ircd, config converter, and documents
+    to the directory set by ./configure
+
+5.  Edit example.conf in your install directory, and move it to "ircd.conf".
+
+6.  Run the binary!  ircd will look in the directory you are executing from
+    for an ircd.conf first, then it will look to the directory local to itself.
+    You may override these options by specifying a config file using:
+    ./ircd -f path/to/ircd.conf
+
+Best of luck!
+-The Bahamut Team
+
+$Id: INSTALL,v 1.1 2005/01/12 07:44:55 mmondor Exp $
diff --git a/LICENCE.pcre b/LICENCE.pcre
new file mode 100644 (file)
index 0000000..09a242c
--- /dev/null
@@ -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 <ph10@cam.ac.uk>
+
+University of Cambridge Computing Service,
+Cambridge, England. Phone: +44 1223 334714.
+
+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. 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/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..9a17037
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,249 @@
+
+                   GNU GENERAL PUBLIC LICENSE
+                    Version 1, February 1989
+
+ Copyright (C) 1989 Free Software Foundation, Inc.
+                    675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The license agreements of most software companies try to keep users
+at the mercy of those companies.  By contrast, our General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  The
+General Public License applies to the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+You can use it for your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Specifically, the General Public License is designed to make
+sure that you have the freedom to give away or sell copies of free
+software, that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of a such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must tell them their rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License.  The
+"Program", below, refers to any such program or work, and a "work based
+on the Program" means either the Program or any work containing the
+Program or a portion of it, either verbatim or with modifications.  Each
+licensee is addressed as "you".
+
+  1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this
+General Public License and to the absence of any warranty; and give any
+other recipients of the Program a copy of this General Public License
+along with the Program.  You may charge a fee for the physical act of
+transferring a copy.
+
+  2. You may modify your copy or copies of the Program or any portion of
+it, and copy and distribute such modifications under the terms of Paragraph
+1 above, provided that you also do the following:
+
+    a) cause the modified files to carry prominent notices stating that
+    you changed the files and the date of any change; and
+
+    b) cause the whole of any work that you distribute or publish, that
+    in whole or in part contains the Program or any part thereof, either
+    with or without modifications, to be licensed at no charge to all
+    third parties under the terms of this General Public License (except
+    that you may choose to grant warranty protection to some or all
+    third parties, at your option).
+
+    c) If the modified program normally reads commands interactively when
+    run, you must cause it, when started running for such interactive use
+    in the simplest and most usual way, to print or display an
+    announcement including an appropriate copyright notice and a notice
+    that there is no warranty (or else, saying that you provide a
+    warranty) and that users may redistribute the program under these
+    conditions, and telling the user how to view a copy of this General
+    Public License.
+
+    d) You may charge a fee for the physical act of transferring a
+    copy, and you may at your option offer warranty protection in
+    exchange for a fee.
+
+Mere aggregation of another independent work with the Program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other work under the scope of these terms.
+\f
+  3. You may copy and distribute the Program (or a portion or derivative of
+it, under Paragraph 2) in object code or executable form under the terms of
+Paragraphs 1 and 2 above provided that you also do one of the following:
+
+    a) accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    b) accompany it with a written offer, valid for at least three
+    years, to give any third party free (except for a nominal charge
+    for the cost of distribution) a complete machine-readable copy of the
+    corresponding source code, to be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    c) accompany it with the information you received as to where the
+    corresponding source code may be obtained.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form alone.)
+
+Source code for a work means the preferred form of the work for making
+modifications to it.  For an executable file, complete source code means
+all the source code for all modules it contains; but, as a special
+exception, it need not include source code for modules which are standard
+libraries that accompany the operating system on which the executable
+file runs, or for standard header files or definitions files that
+accompany that operating system.
+
+  4. You may not copy, modify, sublicense, distribute or transfer the
+Program except as expressly provided under this General Public License.
+Any attempt otherwise to copy, modify, sublicense, distribute or transfer
+the Program is void, and will automatically terminate your rights to use
+the Program under this License.  However, parties who have received
+copies, or rights to use copies, from you under this General Public
+License will not have their licenses terminated so long as such parties
+remain in full compliance.
+
+  5. By copying, distributing or modifying the Program (or any work based
+on the Program) you indicate your acceptance of this license to do so,
+and all its terms and conditions.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these
+terms and conditions.  You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein.
+\f
+  7. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of the license which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+the license, you may choose any version ever published by the Free Software
+Foundation.
+
+  8. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to humanity, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+  To do so, attach the following notices to the program.  It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19xx name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License.  Of course, the
+commands you use may be called something other than `show w' and `show
+c'; they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  program `Gnomovision' (a program to direct compilers to make passes
+  at assemblers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..366a1bd
--- /dev/null
@@ -0,0 +1,117 @@
+#   IRC - Internet Relay Chat, Makefile
+#   Copyright (C) 1990, Jarkko Oikarinen
+#
+#   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Id: Makefile.in,v 1.1 2005/01/12 07:44:55 mmondor Exp $
+
+RM=@RM@
+MV=@MV@
+
+ENGINE=@SENGINE@
+
+SHELL=/bin/sh
+SUBDIRS=zlib src doc tools
+INSTDIRS=src doc tools
+CLEANSUBDIRS=src tools
+
+CC=@CC@
+CFLAGS=@CFLAGS@
+LIBS=@LIBS@ 
+
+INSTALL_DIR=@INSTALL_DIR@
+
+all:   build
+
+build:
+       -@if [ ! -f include/setup.h ] ; then \
+               echo "Hmm...doesn't look like you've run configure..."; \
+               echo "Doing so now."; \
+               sh configure; \
+       fi
+       @for i in $(SUBDIRS); do \
+               echo "Building $$i";\
+               cd $$i;\
+               ${MAKE} build; cd ..;\
+       done
+       @echo "******************************************************************************"
+       @echo "* For help with bahamut, please refer to http://bahamut.dal.net/             *"
+       @echo "* If you encouter serious security related bugs, please mail coders@dal.net  *"
+       @echo "* For other bug reports and inquiries, please mail dalnet-src@dal.net        *"
+       @echo "* Thank you for choosing Bahamut!  - The DALnet coding team                  *"
+       @echo "******************************************************************************"
+
+profile:
+       @for i in $(SUBDIRS); do \
+               echo "Building $$i [profile]";\
+               cd $$i;\
+               ${MAKE} profile; cd ..;\
+       done
+
+clean:
+       ${RM} -f *~ core
+       @for i in $(CLEANSUBDIRS); do \
+               echo "Cleaning $$i";\
+               cd $$i;\
+               ${MAKE} clean; cd ..;\
+       done
+       -@if [ -f include/setup.h ] ; then \
+       echo "To really restart installation, make distclean" ; \
+       fi
+
+fullclean:
+       ${RM} -f *~ core
+       @for i in $(SUBDIRS); do \
+               echo "Cleaning $$i";\
+               cd $$i;\
+               ${MAKE} clean; cd ..;\
+       done
+       -@if [ -f include/setup.h ] ; then \
+       echo "To really restart installation, make distclean" ; \
+       fi
+
+distclean:
+       ${RM} -f Makefile *~ *.rej *.orig core ircd.core *.tmp
+       ${RM} -f config.status config.cache config.log
+       cd include; ${RM} -f setup.h *~ *.rej *.orig options.h; cd ..
+       @for i in $(SUBDIRS); do \
+               echo "Cleaning $$i";\
+               cd $$i;\
+               ${MAKE} distclean; cd ..;\
+       done
+
+depend:
+       @for i in $(SUBDIRS); do \
+               echo "Making dependencies in $$i";\
+               cd $$i;\
+               ${MAKE} depend; cd ..;\
+       done
+
+install: all
+       @if test ! -d $(INSTALL_DIR); then \
+               echo "Creating directory $(INSTALL_DIR)"; \
+               mkdir $(INSTALL_DIR); \
+       fi
+       @for i in $(INSTDIRS); do \
+               cd $$i; \
+               $(MAKE) install; \
+               cd ..; \
+       done
+       @echo ""
+       @echo "Now edit $(INSTALL_DIR)/template.conf"
+       @echo "and move it to ircd.conf - and you'll be all set."
+       @echo "See the doc/ directory and the INSTALL file for more assistance"
+       @echo "Thank you for choosing Bahamut!"
+       @echo ""
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..503c31b
--- /dev/null
+++ b/README
@@ -0,0 +1,18 @@
+Welcome to Bahamut 1.8
+----------------------
+
+Please refer to the INSTALL file for help instructions on building and
+installing ircd.  The doc/ directory may also be of interest.
+
+Please refer to the CHANGES file to see what has changed in the 1.8
+revision of Bahamut.
+
+*** IMPORTANT ***
+Please send any SECURITY RELATED bugs to coders@dal.net - any 
+non-critical bugs can be sent to dalnet-src@dal.net - a public list
+for code discussion.
+
+Thanks!
+-Bahamut Team
+
+$Id: README,v 1.1 2005/01/12 07:44:55 mmondor Exp $
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..0924fd6
--- /dev/null
+++ b/TODO
@@ -0,0 +1,15 @@
+Future TODO List
+----------------
+
+- Split up aClient into separate structures for local and remote users
+- Backport message tokenization from 1.6
+- Carve off throttling into its own hook module
+- Revamp hashing to use a generic hashing model (like throttling).
+- Implement IPV6
+
+List to Ponder
+--------------
+
+- Add support for multiple language sets via flatfile configuration
+- Switch encryption to RSA
+- Backport IDENTITY from 1.6
diff --git a/config.guess b/config.guess
new file mode 100755 (executable)
index 0000000..98eede0
--- /dev/null
@@ -0,0 +1,1388 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-02-22'
+
+# This file 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.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+       for c in cc gcc c89 c99 ; do
+         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+            CC_FOR_BUILD="$c"; break ;
+         fi ;
+       done ;
+       if test x"$CC_FOR_BUILD" = x ; then
+         CC_FOR_BUILD=no_compiler_found ;
+       fi
+       ;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+       # NetBSD (nbsd) targets should (where applicable) match one or
+       # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       #
+       # Note: NetBSD doesn't particularly care about the vendor
+       # portion of the name.  We always set it to "unknown".
+       sysctl="sysctl -n hw.machine_arch"
+       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+       case "${UNAME_MACHINE_ARCH}" in
+           armeb) machine=armeb-unknown ;;
+           arm*) machine=arm-unknown ;;
+           sh3el) machine=shl-unknown ;;
+           sh3eb) machine=sh-unknown ;;
+           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently, or will in the future.
+       case "${UNAME_MACHINE_ARCH}" in
+           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+               eval $set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep __ELF__ >/dev/null
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # The OS release
+       # Debian GNU/NetBSD machines have a different userland, and
+       # thus, need a distinct triplet. However, they do not need
+       # kernel version information, so it can be replaced with a
+       # suitable tag, in the style of linux-gnu.
+       case "${UNAME_VERSION}" in
+           Debian*)
+               release='-gnu'
+               ;;
+           *)
+               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+               ;;
+       esac
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       echo "${machine}-${os}${release}"
+       exit 0 ;;
+    amiga:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    arc:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    hp300:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mac68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    macppc:OpenBSD:*:*)
+       echo powerpc-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+       echo m88k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvmeppc:OpenBSD:*:*)
+       echo powerpc-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    pmax:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sgi:OpenBSD:*:*)
+       echo mipseb-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sun3:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    wgrisc:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    *:OpenBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    alpha:OSF1:*:*)
+       if test $UNAME_RELEASE = "V4.0"; then
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+       fi
+       # According to Compaq, /usr/sbin/psrinfo has been available on
+       # OSF/1 and Tru64 systems produced since 1995.  I hope that
+       # covers most systems running today.  This code pipes the CPU
+       # types through head -n 1, so we only detect the type of CPU 0.
+       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+       case "$ALPHA_CPU_TYPE" in
+           "EV4 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "EV4.5 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "LCA4 (21066/21068)")
+               UNAME_MACHINE="alpha" ;;
+           "EV5 (21164)")
+               UNAME_MACHINE="alphaev5" ;;
+           "EV5.6 (21164A)")
+               UNAME_MACHINE="alphaev56" ;;
+           "EV5.6 (21164PC)")
+               UNAME_MACHINE="alphapca56" ;;
+           "EV5.7 (21164PC)")
+               UNAME_MACHINE="alphapca57" ;;
+           "EV6 (21264)")
+               UNAME_MACHINE="alphaev6" ;;
+           "EV6.7 (21264A)")
+               UNAME_MACHINE="alphaev67" ;;
+           "EV6.8CB (21264C)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8AL (21264B)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8CX (21264D)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.9A (21264/EV69A)")
+               UNAME_MACHINE="alphaev69" ;;
+           "EV7 (21364)")
+               UNAME_MACHINE="alphaev7" ;;
+           "EV7.9 (21364A)")
+               UNAME_MACHINE="alphaev79" ;;
+       esac
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # Should we change UNAME_MACHINE based on the output of uname instead
+       # of the specific Alpha model?
+       echo alpha-pc-interix
+       exit 0 ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-unknown-sysv4
+       exit 0;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+      exit 0 ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-morphos
+       exit 0 ;;
+    *:OS/390:*:*)
+       echo i370-ibm-openedition
+      exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit 0;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit 0;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit 0 ;;
+    NILE*:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit 0 ;;
+    DRS?6000:UNIX_SV:4.2*:7*)
+       case `/usr/bin/uname -p` in
+           sparc) echo sparc-icl-nx7 && exit 0 ;;
+       esac ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    i86pc:SunOS:5.*:*)
+       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit 0 ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit 0 ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+       exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+       exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+       exit 0 ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit 0 ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit 0 ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c \
+         && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+         && exit 0
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit 0 ;;
+    Motorola:PowerMAX_OS:*:*)
+       echo powerpc-motorola-powermax
+       exit 0 ;;
+    Motorola:*:4.3:PL8-*)
+       echo powerpc-harris-powermax
+       exit 0 ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+       echo powerpc-harris-powermax
+       exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit 0 ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit 0 ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit 0 ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+       then
+           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+       else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+       fi
+       else
+           echo i586-dg-dgux${UNAME_RELEASE}
+        fi
+       exit 0 ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit 0 ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit 0 ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+       exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       echo i386-ibm-aix
+       exit 0 ;;
+    ia64:AIX:*:*)
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+       exit 0 ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               eval $set_cc_for_build
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+               echo rs6000-ibm-aix3.2.5
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit 0 ;;
+    *:AIX:*:[45])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit 0 ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+               if [ -x /usr/bin/getconf ]; then
+                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                        esac ;;
+       esac
+               fi
+               if [ "${HP_ARCH}" = "" ]; then
+                   eval $set_cc_for_build
+                   sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+               {
+               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+               case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+                   switch (bits)
+                       {
+                       case 64: puts ("hppa2.0w"); break;
+                       case 32: puts ("hppa2.0n"); break;
+                       default: puts ("hppa2.0"); break;
+                       } break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+                   puts ("hppa2.0"); break;
+              #endif
+               default: puts ("hppa1.0"); break;
+               }
+                  exit (0);
+              }
+EOF
+                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+                   test -z "$HP_ARCH" && HP_ARCH=hppa
+               fi ;;
+       esac
+       if [ ${HP_ARCH} = "hppa2.0w" ]
+       then
+           # avoid double evaluation of $set_cc_for_build
+           test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+           then
+               HP_ARCH="hppa2.0w"
+           else
+               HP_ARCH="hppa64"
+           fi
+       fi
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit 0 ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ia64-hp-hpux${HPUX_REV}
+       exit 0 ;;
+    3050*:HI-UX:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+       echo unknown-hitachi-hiuxwe2
+       exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit 0 ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       echo hppa1.0-hp-mpeix
+       exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit 0 ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit 0 ;;
+    i*86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit 0 ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*T3E:*:*:*)
+       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+        exit 0 ;;
+    CRAY*SV1:*:*:*)
+       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    *:UNICOS/mp:*:*)
+       echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' 
+       exit 0 ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+       exit 0 ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:FreeBSD:*:*)
+       # Determine whether the default compiler uses glibc.
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <features.h>
+       #if __GLIBC__ >= 2
+       LIBC=gnu
+       #else
+       LIBC=
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+       echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
+       exit 0 ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit 0 ;;
+    i*:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit 0 ;;
+    i*:PW*:*)
+       echo ${UNAME_MACHINE}-pc-pw32
+       exit 0 ;;
+    x86:Interix*:3*)
+       echo i586-pc-interix3
+       exit 0 ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+       echo i${UNAME_MACHINE}-pc-mks
+       exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+       # UNAME_MACHINE based on the output of uname instead of i386?
+       echo i586-pc-interix
+       exit 0 ;;
+    i*:UWIN*:*)
+       echo ${UNAME_MACHINE}-pc-uwin
+       exit 0 ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit 0 ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    *:GNU:*:*)
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit 0 ;;
+    i*86:Minix:*:*)
+       echo ${UNAME_MACHINE}-pc-minix
+       exit 0 ;;
+    arm*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    ia64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    m68*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    mips:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef mips
+       #undef mipsel
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=mipsel
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=mips
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+       test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+       ;;
+    mips64:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef mips64
+       #undef mips64el
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=mips64el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=mips64
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+       test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+       ;;
+    ppc:Linux:*:*)
+       echo powerpc-unknown-linux-gnu
+       exit 0 ;;
+    ppc64:Linux:*:*)
+       echo powerpc64-unknown-linux-gnu
+       exit 0 ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+       objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+       if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+       echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+       exit 0 ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) echo hppa1.1-unknown-linux-gnu ;;
+         PA8*) echo hppa2.0-unknown-linux-gnu ;;
+         *)    echo hppa-unknown-linux-gnu ;;
+       esac
+       exit 0 ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       echo hppa64-unknown-linux-gnu
+       exit 0 ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       echo ${UNAME_MACHINE}-ibm-linux
+       exit 0 ;;
+    sh*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    x86_64:Linux:*:*)
+       echo x86_64-unknown-linux-gnu
+       exit 0 ;;
+    i*86:Linux:*:*)
+       # The BFD linker knows what the default object file format is, so
+       # first see if it will tell us. cd to the root directory to prevent
+       # problems with other programs or directories called `ld' in the path.
+       # Set LC_ALL=C to ensure ld outputs messages in English.
+       ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+                        | sed -ne '/supported targets:/!d
+                                   s/[         ][      ]*/ /g
+                                   s/.*supported targets: *//
+                                   s/ .*//
+                                   p'`
+        case "$ld_supported_targets" in
+         elf32-i386)
+               TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+               ;;
+         a.out-i386-linux)
+               echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+               exit 0 ;;
+         coff-i386)
+               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+               exit 0 ;;
+         "")
+               # Either a pre-BFD a.out linker (linux-gnuoldld) or
+               # one that does not give us useful --help.
+               echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+               exit 0 ;;
+       esac
+         # Determine whether the default compiler is a.out or elf
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <features.h>
+       #ifdef __ELF__
+       # ifdef __GLIBC__
+       #  if __GLIBC__ >= 2
+       LIBC=gnu
+       #  else
+       LIBC=gnulibc1
+       #  endif
+       # else
+       LIBC=gnulibc1
+       # endif
+       #else
+       #ifdef __INTEL_COMPILER
+       LIBC=gnu
+       #else
+       LIBC=gnuaout
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+       test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+       test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+       ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       echo i386-sequent-sysv4
+       exit 0 ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit 0 ;;
+    i*86:OS/2:*:*)
+       # If we were able to find `uname', then EMX Unix compatibility
+       # is probably installed.
+       echo ${UNAME_MACHINE}-pc-os2-emx
+       exit 0 ;;
+    i*86:XTS-300:*:STOP)
+       echo ${UNAME_MACHINE}-unknown-stop
+       exit 0 ;;
+    i*86:atheos:*:*)
+       echo ${UNAME_MACHINE}-unknown-atheos
+       exit 0 ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    i*86:*DOS:*:*)
+       echo ${UNAME_MACHINE}-pc-msdosdjgpp
+       exit 0 ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+       fi
+       exit 0 ;;
+    i*86:*:5:[78]*)
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       exit 0 ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit 0 ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+       echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit 0 ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit 0 ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+       echo m68k-convergent-sysv
+       exit 0 ;;
+    M680?0:D-NIX:5.3:*)
+       echo m68k-diab-dnix
+       exit 0 ;;
+    M68*:*:R3V[567]*:*)
+       test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    rs6000:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+       echo powerpc-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit 0 ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit 0 ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit 0 ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo hppa1.1-stratus-vos
+       exit 0 ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit 0 ;;
+    news*:NEWS-OS:6*:*)
+       echo mips-sony-newsos6
+       exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit 0 ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit 0 ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    SX-6:SUPER-UX:*:*)
+       echo sx6-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+    *:Darwin:*:*)
+       case `uname -p` in
+           *86) UNAME_PROCESSOR=i686 ;;
+           powerpc) UNAME_PROCESSOR=powerpc ;;
+       esac
+       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+       exit 0 ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       UNAME_PROCESSOR=`uname -p`
+       if test "$UNAME_PROCESSOR" = "x86"; then
+               UNAME_PROCESSOR=i386
+               UNAME_MACHINE=pc
+       fi
+       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+       exit 0 ;;
+    *:QNX:*:4*)
+       echo i386-pc-qnx
+       exit 0 ;;
+    NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*)
+       echo nsr-tandem-nsk${UNAME_RELEASE}
+       exit 0 ;;
+    *:NonStop-UX:*:*)
+       echo mips-compaq-nonstopux
+       exit 0 ;;
+    BS2000:POSIX*:*:*)
+       echo bs2000-siemens-sysv
+       exit 0 ;;
+    DS/*:UNIX_System_V:*:*)
+       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+       exit 0 ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "$cputype" = "386"; then
+           UNAME_MACHINE=i386
+       else
+           UNAME_MACHINE="$cputype"
+       fi
+       echo ${UNAME_MACHINE}-unknown-plan9
+       exit 0 ;;
+    *:TOPS-10:*:*)
+       echo pdp10-unknown-tops10
+       exit 0 ;;
+    *:TENEX:*:*)
+       echo pdp10-unknown-tenex
+       exit 0 ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       echo pdp10-dec-tops20
+       exit 0 ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       echo pdp10-xkl-tops20
+       exit 0 ;;
+    *:TOPS-20:*:*)
+       echo pdp10-unknown-tops20
+       exit 0 ;;
+    *:ITS:*:*)
+       echo pdp10-unknown-its
+        exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+  printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+  printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+  printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit 0 ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit 0 ;;
+    c34*)
+       echo c34-convex-bsd
+       exit 0 ;;
+    c38*)
+       echo c38-convex-bsd
+       exit 0 ;;
+    c4*)
+       echo c4-convex-bsd
+       exit 0 ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+    ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.sub b/config.sub
new file mode 100755 (executable)
index 0000000..2bd3a7c
--- /dev/null
@@ -0,0 +1,1489 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-02-22'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file 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.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+       *local*)
+       # First pass through any local machine types.
+               echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple | -axis)
+               os=
+               basic_machine=$1
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       1750a | 580 \
+       | a29k \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+       | clipper \
+       | d10v | d30v | dlx | dsp16xx \
+       | fr30 | frv \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | i370 | i860 | i960 | ia64 \
+       | ip2k \
+       | m32r | m68000 | m68k | m88k | mcore \
+       | mips | mipsbe | mipseb | mipsel | mipsle \
+       | mips16 \
+       | mips64 | mips64el \
+       | mips64vr | mips64vrel \
+       | mips64orion | mips64orionel \
+       | mips64vr4100 | mips64vr4100el \
+       | mips64vr4300 | mips64vr4300el \
+       | mips64vr5000 | mips64vr5000el \
+       | mipsisa32 | mipsisa32el \
+       | mipsisa32r2 | mipsisa32r2el \
+       | mipsisa64 | mipsisa64el \
+       | mipsisa64sb1 | mipsisa64sb1el \
+       | mipsisa64sr71k | mipsisa64sr71kel \
+       | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | msp430 \
+       | ns16k | ns32k \
+       | openrisc | or32 \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+       | pyramid \
+       | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+       | sh64 | sh64le \
+       | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
+       | strongarm \
+       | tahoe | thumb | tic80 | tron \
+       | v850 | v850e \
+       | we32k \
+       | x86 | xscale | xstormy16 | xtensa \
+       | z8k)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m6811 | m68hc11 | m6812 | m68hc12)
+               # Motorola 68HC11/12.
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       580-* \
+       | a29k-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+       | avr-* \
+       | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+       | clipper-* | cydra-* \
+       | d10v-* | d30v-* | dlx-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | ip2k-* \
+       | m32r-* \
+       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | mcore-* \
+       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+       | mips16-* \
+       | mips64-* | mips64el-* \
+       | mips64vr-* | mips64vrel-* \
+       | mips64orion-* | mips64orionel-* \
+       | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* \
+       | mips64vr5000-* | mips64vr5000el-* \
+       | mipsisa32-* | mipsisa32el-* \
+       | mipsisa32r2-* | mipsisa32r2el-* \
+       | mipsisa64-* | mipsisa64el-* \
+       | mipsisa64sb1-* | mipsisa64sb1el-* \
+       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+       | mipstx39-* | mipstx39el-* \
+       | msp430-* \
+       | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+       | pyramid-* \
+       | romp-* | rs6000-* \
+       | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+       | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+       | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+       | tahoe-* | thumb-* \
+       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+       | tron-* \
+       | v850-* | v850e-* | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+       | xtensa-* \
+       | ymp-* \
+       | z8k-*)
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-unknown
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-unknown
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-unknown
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       c90)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | j90)
+               basic_machine=j90-cray
+               os=-unicos
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       decsystem10* | dec10*)
+               basic_machine=pdp10-dec
+               os=-tops10
+               ;;
+       decsystem20* | dec20*)
+               basic_machine=pdp10-dec
+               os=-tops20
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i*86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i*86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i*86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i*86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       mmix*)
+               basic_machine=mmix-knuth
+               os=-mmixware
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       morphos)
+               basic_machine=powerpc-unknown
+               os=-morphos
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       nv1)
+               basic_machine=nv1-cray
+               os=-unicosmp
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       or32 | or32-*)
+               basic_machine=or32-unknown
+               os=-coff
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+        pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pentium | p5 | k5 | k6 | nexgen | viac3)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | 6x86 | athlon | athlon_*)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2)
+               basic_machine=i686-pc
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=power-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       s390 | s390-*)
+               basic_machine=s390-ibm
+               ;;
+       s390x | s390x-*)
+               basic_machine=s390x-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sb1)
+               basic_machine=mipsisa64sb1-unknown
+               ;;
+       sb1el)
+               basic_machine=mipsisa64sb1el-unknown
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sparclite-wrs | simso-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=alphaev5-cray
+               os=-unicos
+               ;;
+       t90)
+               basic_machine=t90-cray
+               os=-unicos
+               ;;
+        tic4x | c4x*)
+               basic_machine=tic4x-unknown
+               os=-coff
+               ;;
+       tic54x | c54x*)
+               basic_machine=tic54x-unknown
+               os=-coff
+               ;;
+       tic55x | c55x*)
+               basic_machine=tic55x-unknown
+               os=-coff
+               ;;
+       tic6x | c6x*)
+               basic_machine=tic6x-unknown
+               os=-coff
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       toad1)
+               basic_machine=pdp10-xkl
+               os=-tops20
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+        xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+               basic_machine=sh-unknown
+               ;;
+       sh64)
+               basic_machine=sh64-unknown
+               ;;
+       sparc | sparcv9 | sparcv9b)
+               basic_machine=sparc-sun
+               ;;
+        cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+             | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+             | -powermax* | -dnix*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto-qnx*)
+               ;;
+       -nto*)
+               os=`echo $os | sed -e 's|nto|nto-qnx|'`
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -atheos*)
+               os=-atheos
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -nova*)
+               os=-rtmk-nova
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk*)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
+       -aros*)
+               os=-aros
+               ;;
+       -kaos*)
+               os=-kaos
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=-tops20
+               ;;
+        pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       or32-*)
+               os=-coff
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+        *-gould)
+               os=-sysv
+               ;;
+        *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+        *-sgi)
+               os=-irix
+               ;;
+        *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -vxsim* | -vxworks* | -windiss*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..7f5fb61
--- /dev/null
+++ b/configure
@@ -0,0 +1,8235 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.57.
+#
+# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002
+# Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+
+# Support unset when possible.
+if (FOO=FOO; unset FOO) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)$' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+         /^X\/\(\/\/\)$/{ s//\1/; q; }
+         /^X\/\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+        case $as_dir in
+        /*)
+          if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+            $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+            $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+            CONFIG_SHELL=$as_dir/$as_base
+            export CONFIG_SHELL
+            exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+          fi;;
+        esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='     ' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS="  $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#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 PACKAGE VERSION CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os SET_MAKE RM CP MV INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP EGREP ENCRYPT_SRC SSL_INCLUDES SSL_LIBS SENGINE INSTALL_DIR LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+              localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+         X"$0" : 'X\(//\)[^/]' \| \
+         X"$0" : 'X\(//\)$' \| \
+         X"$0" : 'X\(/\)' \| \
+         .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+  --target=TARGET   configure for building compilers for TARGET [HOST]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --disable-hookmodules   disable support for loadable hook modules
+  --enable-openssl=DIR       Enable OpenSSL support (DIR optional).
+  --disable-openssl            Disable OpenSSL support.
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-socketengine=TYPE       Set the socketengine type.  Choices are:
+                                kqueue poll select
+  --with-maxconnections=NUMBER   Set the maximum number of sockets.
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be
+# absolute.
+ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd`
+ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd`
+ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd`
+ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd`
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+           test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+
+Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002
+Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.57.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"   "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+        ac_must_keep_next=false # Got value, back to normal.
+      else
+        case $ac_arg in
+          *=* | --config-cache | -C | -disable-* | --disable-* \
+          | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+          | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+          | -with-* | --with-* | -without-* | --without-* | --x)
+            case "$ac_configure_args0 " in
+              "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+            esac
+            ;;
+          -* ) ac_must_keep_next=true ;;
+        esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+        "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+        "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=$`echo $ac_var`
+        echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core core.* *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+               sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+        { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+        { echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+        { echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+        ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"   "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+          ac_config_headers="$ac_config_headers include/setup.h"
+
+
+PACKAGE=bahamut-release
+VERSION=1.8
+
+
+
+# autoheader templates
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --enable-hookmodules or --disable-hookmodules was given.
+if test "${enable_hookmodules+set}" = set; then
+  enableval="$enable_hookmodules"
+
+else
+   check_hmodules="yes"
+fi;
+
+# Check whether --enable-openssl or --disable-openssl was given.
+if test "${enable_openssl+set}" = set; then
+  enableval="$enable_openssl"
+   cf_enable_openssl=$enableval
+else
+   cf_enable_openssl="auto"
+fi;
+
+
+# Check whether --with-socketengine or --without-socketengine was given.
+if test "${with_socketengine+set}" = set; then
+  withval="$with_socketengine"
+   check_sengine=$withval
+else
+   check_sengine="yes"
+fi;
+
+# Check whether --with-maxconnections or --without-maxconnections was given.
+if test "${with_maxconnections+set}" = set; then
+  withval="$with_maxconnections"
+   check_maxconnections=$withval
+else
+   check_maxconnections="auto"
+fi;
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+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 ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output" >&5
+echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+        ;;
+    conftest.$ac_ext )
+        # This is the source file.
+        ;;
+    [ab].out )
+        # We found the default executable, but exeext='' is most
+        # certainly right.
+        break;;
+    *.* )
+        ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+        # FIXME: I believe we export ac_cv_exeext for Libtool,
+        # but it would be cool to find out if it's true.  Does anybody
+        # maintain Libtool? --akim.
+        export ac_cv_exeext
+        break;;
+    * )
+        break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (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
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+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); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+          export ac_cv_exeext
+          break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+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 ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+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); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+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 ()
+{
+#ifndef __GNUC__
+       choke me
+#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_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+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 ()
+{
+
+  ;
+  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_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+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 <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX                  -qlanglvl=ansi
+# Ultrix and OSF/1     -std1
+# HP-UX 10.20 and later        -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4                 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  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_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_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
+  for ac_declaration in \
+   ''\
+   '#include <stdlib.h>' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  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 <stdlib.h>
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  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
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+  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_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  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
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking CFLAGS for maximum warnings" >&5
+echo $ECHO_N "checking CFLAGS for maximum warnings... $ECHO_C" >&6
+if test "${ac_cv_cflags_warn_all+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_cflags_warn_all="no, unknown"
+
+
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ ac_save_CFLAGS="$CFLAGS"
+for ac_arg in "-pedantic  % -Wall"          "-xstrconst % -v"             "-std1      % -verbose -w0 -warnprotos"    "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd"    "-ansi -ansiE % -fullwarn"    "+ESlit     % +w1"            "-Xc        % -pvctl,fullmsg"    "-h conform % -h msglevel 2"    #
+do CFLAGS="$ac_save_CFLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+   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 ()
+{
+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_cflags_warn_all=`echo $ac_arg | sed -e 's,.*% *,,'` ; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+done
+ CFLAGS="$ac_save_CFLAGS"
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_cflags_warn_all" >&5
+echo "${ECHO_T}$ac_cv_cflags_warn_all" >&6
+case ".$ac_cv_cflags_warn_all" in
+     .ok|.ok,*)  ;;
+   .|.no|.no,*)
+ ;;
+   *)
+   if echo " $CFLAGS " | grep " $ac_cv_cflags_warn_all " 2>&1 >/dev/null
+   then { (echo "$as_me:$LINENO: : CFLAGS does contain \$ac_cv_cflags_warn_all") >&5
+  (: CFLAGS does contain $ac_cv_cflags_warn_all) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+   else { (echo "$as_me:$LINENO: : CFLAGS=\"\$CFLAGS \$ac_cv_cflags_warn_all\"") >&5
+  (: CFLAGS="$CFLAGS $ac_cv_cflags_warn_all") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+                      CFLAGS="$CFLAGS $ac_cv_cflags_warn_all"
+   fi
+ ;;
+esac
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f $ac_dir/shtool; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Make sure we can run config.sub.
+$ac_config_sub sun4 >/dev/null 2>&1 ||
+  { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
+echo "$as_me: error: cannot run $ac_config_sub" >&2;}
+   { (exit 1); exit 1; }; }
+
+echo "$as_me:$LINENO: checking build system type" >&5
+echo $ECHO_N "checking build system type... $ECHO_C" >&6
+if test "${ac_cv_build+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_build_alias=$build_alias
+test -z "$ac_cv_build_alias" &&
+  ac_cv_build_alias=`$ac_config_guess`
+test -z "$ac_cv_build_alias" &&
+  { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+   { (exit 1); exit 1; }; }
+ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+echo "${ECHO_T}$ac_cv_build" >&6
+build=$ac_cv_build
+build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking host system type" >&5
+echo $ECHO_N "checking host system type... $ECHO_C" >&6
+if test "${ac_cv_host+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_host_alias=$host_alias
+test -z "$ac_cv_host_alias" &&
+  ac_cv_host_alias=$ac_cv_build_alias
+ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+echo "${ECHO_T}$ac_cv_host" >&6
+host=$ac_cv_host
+host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking target system type" >&5
+echo $ECHO_N "checking target system type... $ECHO_C" >&6
+if test "${ac_cv_target+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_target_alias=$target_alias
+test "x$ac_cv_target_alias" = "x" &&
+  ac_cv_target_alias=$ac_cv_host_alias
+ac_cv_target=`$ac_config_sub $ac_cv_target_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_target" >&5
+echo "${ECHO_T}$ac_cv_target" >&6
+target=$ac_cv_target
+target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+test -n "$target_alias" &&
+  test "$program_prefix$program_suffix$program_transform_name" = \
+    NONENONEs,x,x, &&
+  program_prefix=${target_alias}-
+echo "$as_me:$LINENO: checking for library containing strerror" >&5
+echo $ECHO_N "checking for library containing strerror... $ECHO_C" >&6
+if test "${ac_cv_search_strerror+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_strerror=no
+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 strerror ();
+int
+main ()
+{
+strerror ();
+  ;
+  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_search_strerror="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_strerror" = no; then
+  for ac_lib in cposix; do
+    LIBS="-l$ac_lib  $ac_func_search_save_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 strerror ();
+int
+main ()
+{
+strerror ();
+  ;
+  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_search_strerror="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_strerror" >&5
+echo "${ECHO_T}$ac_cv_search_strerror" >&6
+if test "$ac_cv_search_strerror" != no; then
+  test "$ac_cv_search_strerror" = "none required" || LIBS="$ac_cv_search_strerror $LIBS"
+
+fi
+
+echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'`
+if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.make <<\_ACEOF
+all:
+       @echo 'ac_maketemp="$(MAKE)"'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftest.make
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  SET_MAKE=
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_RM+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $RM in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_RM="$RM" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  ;;
+esac
+fi
+RM=$ac_cv_path_RM
+
+if test -n "$RM"; then
+  echo "$as_me:$LINENO: result: $RM" >&5
+echo "${ECHO_T}$RM" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+# Extract the first word of "cp", so it can be a program name with args.
+set dummy cp; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_CP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $CP in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_CP="$CP" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_CP="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  ;;
+esac
+fi
+CP=$ac_cv_path_CP
+
+if test -n "$CP"; then
+  echo "$as_me:$LINENO: result: $CP" >&5
+echo "${ECHO_T}$CP" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+# Extract the first word of "mv", so it can be a program name with args.
+set dummy mv; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_MV+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $MV in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_MV="$MV" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_MV="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  ;;
+esac
+fi
+MV=$ac_cv_path_MV
+
+if test -n "$MV"; then
+  echo "$as_me:$LINENO: result: $MV" >&5
+echo "${ECHO_T}$MV" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+        if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+          if test $ac_prog = install &&
+            grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+            # AIX install.  It has an incompatible calling convention.
+            :
+          elif test $ac_prog = install &&
+            grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+            # program-specific install script used by HP pwplus--don't use.
+            :
+          else
+            ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+            break 3
+          fi
+        fi
+      done
+    done
+    ;;
+esac
+done
+
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL=$ac_install_sh
+  fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo "$as_me:$LINENO: checking for inline" >&5
+echo $ECHO_N "checking for inline... $ECHO_C" >&6
+if test "${ac_cv_c_inline+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_c_inline=no
+for ac_kw in inline __inline__ __inline; do
+  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.  */
+#ifndef __cplusplus
+typedef int foo_t;
+static $ac_kw foo_t static_foo () {return 0; }
+$ac_kw foo_t foo () {return 0; }
+#endif
+
+_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_inline=$ac_kw; break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+done
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5
+echo "${ECHO_T}$ac_cv_c_inline" >&6
+case $ac_cv_c_inline in
+  inline | yes) ;;
+  no)
+cat >>confdefs.h <<\_ACEOF
+#define inline
+_ACEOF
+ ;;
+  *)  cat >>confdefs.h <<_ACEOF
+#define inline $ac_cv_c_inline
+_ACEOF
+ ;;
+esac
+
+
+
+echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5
+echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6
+if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl  $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 gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+  ;
+  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_nsl_gethostbyname=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_nsl_gethostbyname=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_nsl_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6
+if test $ac_cv_lib_nsl_gethostbyname = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBNSL 1
+_ACEOF
+
+  LIBS="-lnsl $LIBS"
+
+fi
+
+echo "$as_me:$LINENO: checking for res_mkquery" >&5
+echo $ECHO_N "checking for res_mkquery... $ECHO_C" >&6
+if test "${ac_cv_func_res_mkquery+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 res_mkquery (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 res_mkquery ();
+/* 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_res_mkquery) || defined (__stub___res_mkquery)
+choke me
+#else
+char (*f) () = res_mkquery;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != res_mkquery;
+  ;
+  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_func_res_mkquery=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_res_mkquery=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_res_mkquery" >&5
+echo "${ECHO_T}$ac_cv_func_res_mkquery" >&6
+if test $ac_cv_func_res_mkquery = yes; then
+  :
+else
+
+echo "$as_me:$LINENO: checking for res_mkquery in -lresolv" >&5
+echo $ECHO_N "checking for res_mkquery in -lresolv... $ECHO_C" >&6
+if test "${ac_cv_lib_resolv_res_mkquery+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lresolv  $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 res_mkquery ();
+int
+main ()
+{
+res_mkquery ();
+  ;
+  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_resolv_res_mkquery=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_resolv_res_mkquery=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_resolv_res_mkquery" >&5
+echo "${ECHO_T}$ac_cv_lib_resolv_res_mkquery" >&6
+if test $ac_cv_lib_resolv_res_mkquery = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBRESOLV 1
+_ACEOF
+
+  LIBS="-lresolv $LIBS"
+
+fi
+
+fi
+
+echo "$as_me:$LINENO: checking for __res_mkquery" >&5
+echo $ECHO_N "checking for __res_mkquery... $ECHO_C" >&6
+if test "${ac_cv_func___res_mkquery+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 __res_mkquery (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 __res_mkquery ();
+/* 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___res_mkquery) || defined (__stub_____res_mkquery)
+choke me
+#else
+char (*f) () = __res_mkquery;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != __res_mkquery;
+  ;
+  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_func___res_mkquery=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func___res_mkquery=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func___res_mkquery" >&5
+echo "${ECHO_T}$ac_cv_func___res_mkquery" >&6
+if test $ac_cv_func___res_mkquery = yes; then
+  :
+else
+
+echo "$as_me:$LINENO: checking for __res_mkquery in -lresolv" >&5
+echo $ECHO_N "checking for __res_mkquery in -lresolv... $ECHO_C" >&6
+if test "${ac_cv_lib_resolv___res_mkquery+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lresolv  $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 __res_mkquery ();
+int
+main ()
+{
+__res_mkquery ();
+  ;
+  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_resolv___res_mkquery=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_resolv___res_mkquery=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_resolv___res_mkquery" >&5
+echo "${ECHO_T}$ac_cv_lib_resolv___res_mkquery" >&6
+if test $ac_cv_lib_resolv___res_mkquery = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBRESOLV 1
+_ACEOF
+
+  LIBS="-lresolv $LIBS"
+
+fi
+
+fi
+
+
+echo "$as_me:$LINENO: checking for socket in -lsocket" >&5
+echo $ECHO_N "checking for socket in -lsocket... $ECHO_C" >&6
+if test "${ac_cv_lib_socket_socket+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsocket  $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 socket ();
+int
+main ()
+{
+socket ();
+  ;
+  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_socket_socket=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_socket_socket=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_socket_socket" >&5
+echo "${ECHO_T}$ac_cv_lib_socket_socket" >&6
+if test $ac_cv_lib_socket_socket = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSOCKET 1
+_ACEOF
+
+  LIBS="-lsocket $LIBS"
+
+fi
+
+echo "$as_me:$LINENO: checking for crypt" >&5
+echo $ECHO_N "checking for crypt... $ECHO_C" >&6
+if test "${ac_cv_func_crypt+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 crypt (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 crypt ();
+/* 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_crypt) || defined (__stub___crypt)
+choke me
+#else
+char (*f) () = crypt;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != crypt;
+  ;
+  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_func_crypt=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_crypt=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_crypt" >&5
+echo "${ECHO_T}$ac_cv_func_crypt" >&6
+if test $ac_cv_func_crypt = yes; then
+  :
+else
+
+echo "$as_me:$LINENO: checking for crypt in -ldescrypt" >&5
+echo $ECHO_N "checking for crypt in -ldescrypt... $ECHO_C" >&6
+if test "${ac_cv_lib_descrypt_crypt+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldescrypt  $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 crypt ();
+int
+main ()
+{
+crypt ();
+  ;
+  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_descrypt_crypt=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_descrypt_crypt=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_descrypt_crypt" >&5
+echo "${ECHO_T}$ac_cv_lib_descrypt_crypt" >&6
+if test $ac_cv_lib_descrypt_crypt = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBDESCRYPT 1
+_ACEOF
+
+  LIBS="-ldescrypt $LIBS"
+
+else
+
+echo "$as_me:$LINENO: checking for crypt in -lcrypt" >&5
+echo $ECHO_N "checking for crypt in -lcrypt... $ECHO_C" >&6
+if test "${ac_cv_lib_crypt_crypt+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcrypt  $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 crypt ();
+int
+main ()
+{
+crypt ();
+  ;
+  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_crypt_crypt=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_crypt_crypt=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_crypt_crypt" >&5
+echo "${ECHO_T}$ac_cv_lib_crypt_crypt" >&6
+if test $ac_cv_lib_crypt_crypt = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBCRYPT 1
+_ACEOF
+
+  LIBS="-lcrypt $LIBS"
+
+fi
+
+fi
+
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  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.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  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 <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  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.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                     Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  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 <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+    then ac_cv_prog_egrep='grep -E'
+    else ac_cv_prog_egrep='egrep'
+    fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+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 <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  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_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  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 <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  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 <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+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 <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                   (('a' <= (c) && (c) <= 'i') \
+                     || ('j' <= (c) && (c) <= 'r') \
+                     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+        || toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (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
+  :
+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_header_stdc=no
+fi
+rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                  inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+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
+
+#include <$ac_header>
+_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
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+
+
+for ac_header in fcntl.h limits.h malloc.h netdb.h netinet/in.h stddef.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+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
+#include <$ac_header>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+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 <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+for ac_header in stdlib.h string.h strings.h sys/file.h sys/ioctl.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+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
+#include <$ac_header>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+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 <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+for ac_header in sys/param.h sys/socket.h sys/time.h syslog.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+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
+#include <$ac_header>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+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 <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in utmp.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+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
+#include <$ac_header>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+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 <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+
+for ac_header in inttypes.h sys/resource.h errno.h stdlib.h stddef.h getopt.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+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
+#include <$ac_header>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+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 <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+echo "$as_me:$LINENO: checking MIN and MAX definition" >&5
+echo $ECHO_N "checking MIN and MAX definition... $ECHO_C" >&6
+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.  */
+
+#if defined( HAVE_SYS_PARAM_H )
+#include <sys/param.h>
+#ifdef MIN
+  yes
+#endif
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "yes" >/dev/null 2>&1; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_MINMAX 1
+_ACEOF
+ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+rm -f conftest*
+
+
+
+if test $ac_cv_c_compiler_gnu = yes; then
+    echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5
+echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6
+if test "${ac_cv_prog_gcc_traditional+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+    ac_pattern="Autoconf.*'x'"
+  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 <sgtty.h>
+Autoconf TIOCGETP
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1; then
+  ac_cv_prog_gcc_traditional=yes
+else
+  ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+  if test $ac_cv_prog_gcc_traditional = no; then
+    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 <termio.h>
+Autoconf TCGETA
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1; then
+  ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+  fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5
+echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6
+  if test $ac_cv_prog_gcc_traditional = yes; then
+    CC="$CC -traditional"
+  fi
+fi
+
+
+
+
+
+
+
+for ac_func in alarm dup2 gethostbyname gethostname getpass gettimeofday
+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 <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 inet_ntoa isascii memmove memset munmap setenv socket
+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 <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 strcasecmp strchr strdup strerror strncasecmp strrchr strtol
+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 <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 strtoul index strerror strtoken strtok inet_addr inet_netof
+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 <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 inet_aton gettimeofday lrand48 sigaction bzero bcmp bcopy
+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 <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 dn_skipname __dn_skipname getrusage times break
+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 <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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
+
+
+
+case "$target" in
+    *-solaris2*)
+        solaris2="yes"
+        cat >>confdefs.h <<\_ACEOF
+#define OS_SOLARIS2 1
+_ACEOF
+
+        cat >>confdefs.h <<\_ACEOF
+#define OS_SOLARIS 1
+_ACEOF
+
+        cat >>confdefs.h <<_ACEOF
+#define WRITEV_IOV 16
+_ACEOF
+
+        ;;
+    *-freebsd*)
+        freebsd="yes"
+        cat >>confdefs.h <<_ACEOF
+#define WRITEV_IOV 32
+_ACEOF
+
+        ;;
+    *-netbsd*)
+        cat >>confdefs.h <<_ACEOF
+#define WRITEV_IOV 32
+_ACEOF
+
+        ;;
+    *-openbsd*)
+        cat >>confdefs.h <<_ACEOF
+#define WRITEV_IOV 32
+_ACEOF
+
+        ;;
+    *-linux*)
+        linux="yes"
+        cat >>confdefs.h <<_ACEOF
+#define WRITEV_IOV 32
+_ACEOF
+
+        ;;
+    *aix*)
+        aix="yes"
+        cat >>confdefs.h <<\_ACEOF
+#define AIX 1
+_ACEOF
+
+        ;;
+    *-darwin*)
+        check_hmodules="no"
+        cat >>confdefs.h <<_ACEOF
+#define WRITEV_IOV 32
+_ACEOF
+
+        ;;
+esac
+
+
+set_hmodules="disabled"
+
+if test "$check_hmodules" = "yes"; then
+
+echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $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 dlopen ();
+int
+main ()
+{
+dlopen ();
+  ;
+  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_dl_dlopen=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_dl_dlopen=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_dl_dlopen" >&5
+echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6
+if test $ac_cv_lib_dl_dlopen = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBDL 1
+_ACEOF
+
+  LIBS="-ldl $LIBS"
+
+fi
+
+    if test "${ac_cv_header_dlfcn_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for dlfcn.h" >&5
+echo $ECHO_N "checking for dlfcn.h... $ECHO_C" >&6
+if test "${ac_cv_header_dlfcn_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_dlfcn_h" >&5
+echo "${ECHO_T}$ac_cv_header_dlfcn_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking dlfcn.h usability" >&5
+echo $ECHO_N "checking dlfcn.h usability... $ECHO_C" >&6
+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
+#include <dlfcn.h>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking dlfcn.h presence" >&5
+echo $ECHO_N "checking dlfcn.h presence... $ECHO_C" >&6
+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 <dlfcn.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: dlfcn.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: dlfcn.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: dlfcn.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: dlfcn.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: dlfcn.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: dlfcn.h: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: dlfcn.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for dlfcn.h" >&5
+echo $ECHO_N "checking for dlfcn.h... $ECHO_C" >&6
+if test "${ac_cv_header_dlfcn_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_dlfcn_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_dlfcn_h" >&5
+echo "${ECHO_T}$ac_cv_header_dlfcn_h" >&6
+
+fi
+
+
+
+
+for ac_func in dlopen dlsym
+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 <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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
+ set_hmodules="enabled"
+fi
+done
+
+    if test "$set_hmodules" = "enabled"; then
+        cat >>confdefs.h <<\_ACEOF
+#define USE_HOOKMODULES 1
+_ACEOF
+
+        MOD_LIBS="-Wl,-export-dynamic"
+    fi
+fi
+
+echo "$as_me:$LINENO: checking for sys_errlist declaration" >&5
+echo $ECHO_N "checking for sys_errlist declaration... $ECHO_C" >&6
+if test "$cross_compiling" = yes; then
+  echo "$as_me:$LINENO: result: cross compiling" >&5
+echo "${ECHO_T}cross compiling" >&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 <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+main()
+{
+  char *s = sys_errlist[0];
+         exit(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
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  cat >>confdefs.h <<\_ACEOF
+#define SYS_ERRLIST_DECLARED 1
+_ACEOF
+
+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 )
+echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+save_LIBS="$LIBS"
+
+echo "$as_me:$LINENO: checking for OpenSSL" >&5
+echo $ECHO_N "checking for OpenSSL... $ECHO_C" >&6
+
+if test "X$cf_enable_openssl" != "Xno" ; then
+  cf_openssl_basedir=""
+  if test "X$cf_enable_openssl" != "Xauto" &&
+     test "X$cf_enable_openssl" != "Xyes"; then
+          cf_openssl_basedir="${cf_enable_openssl}"
+  else
+        for dirs in /usr/local/ssl /usr/pkg /usr/local /usr/lib /usr/lib/ssl\
+                /opt /opt/openssl /usr/local/openssl ; do
+      if test -f "${dirs}/include/openssl/opensslv.h" ; then
+        cf_openssl_basedir="${dirs}"
+        break
+      fi
+    done
+    unset dirs
+  fi
+    if test "X$cf_openssl_basedir" != "X" ; then
+    if test -f "${cf_openssl_basedir}/include/openssl/opensslv.h" ; then
+      SSL_INCLUDES="-I${cf_openssl_basedir}/include"
+      SSL_LIBS="-L${cf_openssl_basedir}/lib"
+    else
+                  cf_openssl_basedir=""
+    fi
+  else
+                            if test -f "/usr/include/openssl/opensslv.h" ; then
+      cf_openssl_basedir="/usr"
+    fi
+  fi
+      if test "X$cf_openssl_basedir" != "X" ; then
+    LIBS="$save_LIBS $SSL_LIBS -lcrypto"
+    echo "$as_me:$LINENO: result: $cf_openssl_basedir" >&5
+echo "${ECHO_T}$cf_openssl_basedir" >&6
+    cf_enable_openssl="yes"
+    encryption="enabled"
+    cat >>confdefs.h <<\_ACEOF
+#define HAVE_ENCRYPTION_ON 1
+_ACEOF
+
+    ENCRYPT_SRC="rc4.c dh.c"
+
+  else
+    echo "$as_me:$LINENO: result: not found.  Please check your path." >&5
+echo "${ECHO_T}not found.  Please check your path." >&6
+    cf_enable_openssl="no"
+    encryption="disabled"
+  fi
+  unset cf_openssl_basedir
+else
+    encryption="disabled"
+  echo "$as_me:$LINENO: result: disabled" >&5
+echo "${ECHO_T}disabled" >&6
+fi
+
+unset save_LIBS
+
+
+
+if test "$check_sengine" = "yes"; then
+    engine="select"
+    if test "$freebsd" = "yes"; then
+        echo "$as_me:$LINENO: checking for kevent" >&5
+echo $ECHO_N "checking for kevent... $ECHO_C" >&6
+if test "${ac_cv_func_kevent+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 kevent (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 kevent ();
+/* 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_kevent) || defined (__stub___kevent)
+choke me
+#else
+char (*f) () = kevent;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != kevent;
+  ;
+  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_func_kevent=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_kevent=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_kevent" >&5
+echo "${ECHO_T}$ac_cv_func_kevent" >&6
+if test $ac_cv_func_kevent = yes; then
+   engine="kqueue"
+fi
+
+    elif test "$solaris2" = "yes"; then
+        engine="poll"
+        if test "${ac_cv_header_sys_devpoll_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for sys/devpoll.h" >&5
+echo $ECHO_N "checking for sys/devpoll.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_devpoll_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_devpoll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_devpoll_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking sys/devpoll.h usability" >&5
+echo $ECHO_N "checking sys/devpoll.h usability... $ECHO_C" >&6
+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
+#include <sys/devpoll.h>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking sys/devpoll.h presence" >&5
+echo $ECHO_N "checking sys/devpoll.h presence... $ECHO_C" >&6
+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 <sys/devpoll.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sys/devpoll.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/devpoll.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sys/devpoll.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sys/devpoll.h: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/devpoll.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for sys/devpoll.h" >&5
+echo $ECHO_N "checking for sys/devpoll.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_devpoll_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_sys_devpoll_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_devpoll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_devpoll_h" >&6
+
+fi
+if test $ac_cv_header_sys_devpoll_h = yes; then
+  engine="devpoll"
+fi
+
+
+    elif test "$linux" = "yes"; then
+        echo "$as_me:$LINENO: checking for poll" >&5
+echo $ECHO_N "checking for poll... $ECHO_C" >&6
+if test "${ac_cv_func_poll+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 poll (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 poll ();
+/* 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_poll) || defined (__stub___poll)
+choke me
+#else
+char (*f) () = poll;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != poll;
+  ;
+  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_func_poll=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_poll=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_poll" >&5
+echo "${ECHO_T}$ac_cv_func_poll" >&6
+if test $ac_cv_func_poll = yes; then
+   engine="poll"
+fi
+
+        if test "${ac_cv_header_sys_epoll_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for sys/epoll.h" >&5
+echo $ECHO_N "checking for sys/epoll.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_epoll_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_epoll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_epoll_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking sys/epoll.h usability" >&5
+echo $ECHO_N "checking sys/epoll.h usability... $ECHO_C" >&6
+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
+#include <sys/epoll.h>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking sys/epoll.h presence" >&5
+echo $ECHO_N "checking sys/epoll.h presence... $ECHO_C" >&6
+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 <sys/epoll.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sys/epoll.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/epoll.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sys/epoll.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sys/epoll.h: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/epoll.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for sys/epoll.h" >&5
+echo $ECHO_N "checking for sys/epoll.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_epoll_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_sys_epoll_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_epoll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_epoll_h" >&6
+
+fi
+if test $ac_cv_header_sys_epoll_h = yes; then
+  HAVE_EPOLL_H=yes
+else
+  HAVE_EPOLL_H=no
+fi
+
+
+        echo "$as_me:$LINENO: checking for epoll_create in -lepoll" >&5
+echo $ECHO_N "checking for epoll_create in -lepoll... $ECHO_C" >&6
+if test "${ac_cv_lib_epoll_epoll_create+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lepoll  $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 epoll_create ();
+int
+main ()
+{
+epoll_create ();
+  ;
+  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_epoll_epoll_create=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_epoll_epoll_create=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_epoll_epoll_create" >&5
+echo "${ECHO_T}$ac_cv_lib_epoll_epoll_create" >&6
+if test $ac_cv_lib_epoll_epoll_create = yes; then
+  LIBS="$LIBS -lepoll"; have_epoll_lib="yes"; engine="epoll"
+else
+  have_epoll_lib="no"
+fi
+
+        if test "$HAVE_EPOLL_H $have_epoll_lib" = "yes no"; then
+            echo "$as_me:$LINENO: checking for epoll_create without -lepoll" >&5
+echo $ECHO_N "checking for epoll_create without -lepoll... $ECHO_C" >&6
+            if test "$cross_compiling" = yes; then
+  { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run test program while cross compiling
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+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 <stdint.h>
+                    #include <sys/epoll.h>
+                    #include <errno.h>
+
+                    #include <asm/unistd.h>
+
+                    _syscall1(int, epoll_create, int, size)
+
+                    int main(int argc, char **argv) { return epoll_create(5) < 1; }
+
+_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
+  have_epoll_lib="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 )
+have_epoll_lib="no"
+fi
+rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+            echo "$as_me:$LINENO: result: $have_epoll_lib" >&5
+echo "${ECHO_T}$have_epoll_lib" >&6
+            if test "$have_epoll_lib" = "yes"; then
+                cat >>confdefs.h <<\_ACEOF
+#define NEED_EPOLL_DEFS 1
+_ACEOF
+
+            fi
+        fi
+        if test "$HAVE_EPOLL_H $have_epoll_lib" = "yes yes"; then
+            engine="epoll";
+        fi
+    else
+        echo "$as_me:$LINENO: checking for poll" >&5
+echo $ECHO_N "checking for poll... $ECHO_C" >&6
+if test "${ac_cv_func_poll+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 poll (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 poll ();
+/* 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_poll) || defined (__stub___poll)
+choke me
+#else
+char (*f) () = poll;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != poll;
+  ;
+  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_func_poll=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_poll=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_poll" >&5
+echo "${ECHO_T}$ac_cv_func_poll" >&6
+if test $ac_cv_func_poll = yes; then
+   engine="poll"
+fi
+
+    fi
+elif test "$check_sengine" = "kqueue"; then
+    echo "$as_me:$LINENO: checking for kevent" >&5
+echo $ECHO_N "checking for kevent... $ECHO_C" >&6
+if test "${ac_cv_func_kevent+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 kevent (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 kevent ();
+/* 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_kevent) || defined (__stub___kevent)
+choke me
+#else
+char (*f) () = kevent;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != kevent;
+  ;
+  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_func_kevent=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_kevent=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_kevent" >&5
+echo "${ECHO_T}$ac_cv_func_kevent" >&6
+if test $ac_cv_func_kevent = yes; then
+   engine="kqueue"
+fi
+
+    if test "X$engine" = "X"; then
+        { echo "$as_me:$LINENO: " >&5
+echo "$as_me: " >&6;}
+        { echo "$as_me:$LINENO: kqueue not supported on this platform!" >&5
+echo "$as_me: kqueue not supported on this platform!" >&6;}
+        { { echo "$as_me:$LINENO: error: " >&5
+echo "$as_me: error: " >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+    { echo "$as_me:$LINENO: setting socketengine type to kqueue" >&5
+echo "$as_me: setting socketengine type to kqueue" >&6;}
+elif test "$check_sengine" = "poll"; then
+    echo "$as_me:$LINENO: checking for poll" >&5
+echo $ECHO_N "checking for poll... $ECHO_C" >&6
+if test "${ac_cv_func_poll+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 poll (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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 poll ();
+/* 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_poll) || defined (__stub___poll)
+choke me
+#else
+char (*f) () = poll;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != poll;
+  ;
+  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_func_poll=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_func_poll=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_func_poll" >&5
+echo "${ECHO_T}$ac_cv_func_poll" >&6
+if test $ac_cv_func_poll = yes; then
+   engine="poll"
+fi
+
+    if test "X$engine" = "X"; then
+        { echo "$as_me:$LINENO: " >&5
+echo "$as_me: " >&6;}
+        { echo "$as_me:$LINENO: poll not supported on this platform!" >&5
+echo "$as_me: poll not supported on this platform!" >&6;}
+        { { echo "$as_me:$LINENO: error: " >&5
+echo "$as_me: error: " >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+    { echo "$as_me:$LINENO: setting socketengine type to poll" >&5
+echo "$as_me: setting socketengine type to poll" >&6;}
+elif test "$check_sengine" = "devpoll"; then
+    if test "${ac_cv_header_sys_devpoll_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for sys/devpoll.h" >&5
+echo $ECHO_N "checking for sys/devpoll.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_devpoll_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_devpoll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_devpoll_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking sys/devpoll.h usability" >&5
+echo $ECHO_N "checking sys/devpoll.h usability... $ECHO_C" >&6
+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
+#include <sys/devpoll.h>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking sys/devpoll.h presence" >&5
+echo $ECHO_N "checking sys/devpoll.h presence... $ECHO_C" >&6
+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 <sys/devpoll.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sys/devpoll.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/devpoll.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sys/devpoll.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sys/devpoll.h: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/devpoll.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/devpoll.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for sys/devpoll.h" >&5
+echo $ECHO_N "checking for sys/devpoll.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_devpoll_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_sys_devpoll_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_devpoll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_devpoll_h" >&6
+
+fi
+if test $ac_cv_header_sys_devpoll_h = yes; then
+  engine="devpoll"
+fi
+
+
+    if test "X$engine" = "X"; then
+        { echo "$as_me:$LINENO: " >&5
+echo "$as_me: " >&6;}
+        { echo "$as_me:$LINENO: /dev/poll not supported on this system" >&5
+echo "$as_me: /dev/poll not supported on this system" >&6;}
+        { { echo "$as_me:$LINENO: error: " >&5
+echo "$as_me: error: " >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+    { echo "$as_me:$LINENO: setting socketengine to devpoll" >&5
+echo "$as_me: setting socketengine to devpoll" >&6;}
+elif test "$check_sengine" = "epoll"; then
+    if test "${ac_cv_header_sys_epoll_h+set}" = set; then
+  echo "$as_me:$LINENO: checking for sys/epoll.h" >&5
+echo $ECHO_N "checking for sys/epoll.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_epoll_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_epoll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_epoll_h" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking sys/epoll.h usability" >&5
+echo $ECHO_N "checking sys/epoll.h usability... $ECHO_C" >&6
+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
+#include <sys/epoll.h>
+_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_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking sys/epoll.h presence" >&5
+echo $ECHO_N "checking sys/epoll.h presence... $ECHO_C" >&6
+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 <sys/epoll.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sys/epoll.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/epoll.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sys/epoll.h: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sys/epoll.h: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: sys/epoll.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/epoll.h: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf@gnu.org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for sys/epoll.h" >&5
+echo $ECHO_N "checking for sys/epoll.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_epoll_h+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_header_sys_epoll_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_epoll_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_epoll_h" >&6
+
+fi
+if test $ac_cv_header_sys_epoll_h = yes; then
+  HAVE_EPOLL_H=yes
+else
+  HAVE_EPOLL_H=no
+fi
+
+
+    echo "$as_me:$LINENO: checking for epoll_create in -lepoll" >&5
+echo $ECHO_N "checking for epoll_create in -lepoll... $ECHO_C" >&6
+if test "${ac_cv_lib_epoll_epoll_create+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lepoll  $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 epoll_create ();
+int
+main ()
+{
+epoll_create ();
+  ;
+  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_epoll_epoll_create=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_epoll_epoll_create=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_epoll_epoll_create" >&5
+echo "${ECHO_T}$ac_cv_lib_epoll_epoll_create" >&6
+if test $ac_cv_lib_epoll_epoll_create = yes; then
+  LIBS="$LIBS -lepoll"; have_epoll_lib="yes"; engine="epoll"
+else
+  have_epoll_lib="no"
+fi
+
+    if test "$HAVE_EPOLL_H $have_epoll_lib" = "yes no"; then
+        echo "$as_me:$LINENO: checking for epoll_create without -lepoll" >&5
+echo $ECHO_N "checking for epoll_create without -lepoll... $ECHO_C" >&6
+        if test "$cross_compiling" = yes; then
+  { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run test program while cross compiling
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+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 <stdint.h>
+                #include <sys/epoll.h>
+                #include <errno.h>
+
+                #include <asm/unistd.h>
+
+                _syscall1(int, epoll_create, int, size)
+
+                int main(int argc, char **argv) { return epoll_create(5) < 1; }
+
+_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
+  have_epoll_lib="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 )
+have_epoll_lib="no"
+fi
+rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+        echo "$as_me:$LINENO: result: $have_epoll_lib" >&5
+echo "${ECHO_T}$have_epoll_lib" >&6
+        if test "$have_epoll_lib" = "yes"; then
+            cat >>confdefs.h <<\_ACEOF
+#define NEED_EPOLL_DEFS 1
+_ACEOF
+
+        fi
+    fi
+    if test "$HAVE_EPOLL_H $have_epoll_lib" != "yes yes"; then
+        { echo "$as_me:$LINENO: " >&5
+echo "$as_me: " >&6;}
+        { { echo "$as_me:$LINENO: error: epoll is not supported on this machine" >&5
+echo "$as_me: error: epoll is not supported on this machine" >&2;}
+   { (exit 1); exit 1; }; }
+        { { echo "$as_me:$LINENO: error: " >&5
+echo "$as_me: error: " >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+    { echo "$as_me:$LINENO: setting socketengine type to epoll" >&5
+echo "$as_me: setting socketengine type to epoll" >&6;}
+    engine="epoll";
+elif test "$check_sengine" = "select"; then
+    { echo "$as_me:$LINENO: setting socketengine type to select.. for some stupid reason" >&5
+echo "$as_me: setting socketengine type to select.. for some stupid reason" >&6;}
+    engine="select"
+else
+    { echo "$as_me:$LINENO: " >&5
+echo "$as_me: " >&6;}
+    { echo "$as_me:$LINENO: socket engine type incorrect!" >&5
+echo "$as_me: socket engine type incorrect!" >&6;}
+    { { echo "$as_me:$LINENO: error: " >&5
+echo "$as_me: error: " >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+SENGINE="socketengine_$engine.c"
+
+if test $engine = "select"; then
+    echo "$as_me:$LINENO: checking to see if FD_SETSIZE is broken" >&5
+echo $ECHO_N "checking to see if FD_SETSIZE is broken... $ECHO_C" >&6
+    if test "$cross_compiling" = yes; then
+  echo "$as_me:$LINENO: result: can't tell" >&5
+echo "${ECHO_T}can't tell" >&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.  */
+
+    #define FD_SETSIZE 666
+    #include <sys/types.h>
+    #include <sys/time.h>
+
+    int main()
+    {
+        if(FD_SETSIZE != 666)
+            exit(1);
+        exit(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
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+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 )
+
+    echo "$as_me:$LINENO: result: YES" >&5
+echo "${ECHO_T}YES" >&6
+    { echo "$as_me:$LINENO: WARNING: FD_SETSIZE is hard set by your operating system" >&5
+echo "$as_me: WARNING: FD_SETSIZE is hard set by your operating system" >&2;}
+    { echo "$as_me:$LINENO: WARNING: MAXCONNECTIONS must be no higher than the hardwired FD_SETSIZE" >&5
+echo "$as_me: WARNING: MAXCONNECTIONS must be no higher than the hardwired FD_SETSIZE" >&2;}
+    cat >>confdefs.h <<\_ACEOF
+#define BROKEN_FD_SETSIZE 1
+_ACEOF
+
+    broken_fd_setsize="yes"
+
+fi
+rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+
+if test "$solaris2" != "yes" && test "$aix" != "yes" &&
+   test "$set_hmodules" = "enabled"; then
+    LIBS="$LIBS $MOD_LIBS"
+fi
+
+if test "$check_maxconnections" = "auto"; then
+
+    echo "$as_me:$LINENO: checking Maximum file descriptors" >&5
+echo $ECHO_N "checking Maximum file descriptors... $ECHO_C" >&6;
+    if test "${maxconnections+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 <sys/types.h>
+    #include <sys/time.h>
+    #include <sys/resource.h>
+    #include <stdio.h>
+    #ifdef RLIMIT_FDMAX
+    #define RLIMIT_FD_MAX   RLIMIT_FDMAX
+    #else
+    #ifdef RLIMIT_NOFILE
+    #define RLIMIT_FD_MAX RLIMIT_NOFILE
+    #else
+    #ifdef RLIMIT_OPEN_MAX
+    #define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
+    #else
+    #undef RLIMIT_FD_MAX
+    #endif
+    #endif
+    #endif
+
+
+    int main()
+    {
+    #ifndef BROKEN_FD_SETSIZE
+
+    #ifdef RLIMIT_FD_MAX
+        struct rlimit limit;
+
+        if(!getrlimit(RLIMIT_FD_MAX, &limit))
+        {
+            if(limit.rlim_max > 32768)
+                printf("32768");
+            else
+                printf("%ld", (long) limit.rlim_max);
+        }
+        else
+    #endif
+            printf("256");
+
+    #else
+        printf("%d", FD_SETSIZE);
+    #endif
+        return 0;
+    }
+
+
+_ACEOF
+
+    $CC -o conftest conftest.c >/dev/null 2>&1
+    maxconnections=`./conftest`
+    echo "$as_me:$LINENO: result: $maxconnections" >&5
+echo "${ECHO_T}$maxconnections" >&6
+    $RM -f conftest conftest.c
+
+fi
+
+    cat >>confdefs.h <<_ACEOF
+#define MAXCONNECTIONS ${maxconnections}
+_ACEOF
+
+
+else
+    maxconnections=$check_maxconnections
+    cat >>confdefs.h <<_ACEOF
+#define MAXCONNECTIONS ${maxconnections}
+_ACEOF
+
+fi
+
+if test "$engine" = "select" && test "$broken_fd_setsize" != "yes"; then
+    CFLAGS="$CFLAGS -DFD_SETSIZE=$maxconnections"
+fi
+
+if test $prefix = NONE; then
+    prefix=${HOME}/ircd
+fi
+
+INSTALL_DIR="${prefix}"
+
+
+
+
+
+
+
+                                        ac_config_files="$ac_config_files Makefile src/Makefile tools/Makefile doc/Makefile"
+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
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+        "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+        "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[   ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[     ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+         sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+
+# Support unset when possible.
+if (FOO=FOO; unset FOO) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -n "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)$' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+         /^X\/\(\/\/\)$/{ s//\1/; q; }
+         /^X\/\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+        case $as_dir in
+        /*)
+          if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+            $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+            $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+            CONFIG_SHELL=$as_dir/$as_base
+            export CONFIG_SHELL
+            exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+          fi;;
+        esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='     ' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS="  $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.57.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+  --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.57,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  "src/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
+  "tools/Makefile" ) CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;;
+  "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
+  "include/setup.h" ) CONFIG_HEADERS="$CONFIG_HEADERS include/setup.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; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@PACKAGE@,$PACKAGE,;t t
+s,@VERSION@,$VERSION,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@build@,$build,;t t
+s,@build_cpu@,$build_cpu,;t t
+s,@build_vendor@,$build_vendor,;t t
+s,@build_os@,$build_os,;t t
+s,@host@,$host,;t t
+s,@host_cpu@,$host_cpu,;t t
+s,@host_vendor@,$host_vendor,;t t
+s,@host_os@,$host_os,;t t
+s,@target@,$target,;t t
+s,@target_cpu@,$target_cpu,;t t
+s,@target_vendor@,$target_vendor,;t t
+s,@target_os@,$target_os,;t t
+s,@SET_MAKE@,$SET_MAKE,;t t
+s,@RM@,$RM,;t t
+s,@CP@,$CP,;t t
+s,@MV@,$MV,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@CPP@,$CPP,;t t
+s,@EGREP@,$EGREP,;t t
+s,@ENCRYPT_SRC@,$ENCRYPT_SRC,;t t
+s,@SSL_INCLUDES@,$SSL_INCLUDES,;t t
+s,@SSL_LIBS@,$SSL_LIBS,;t t
+s,@SENGINE@,$SENGINE,;t t
+s,@INSTALL_DIR@,$INSTALL_DIR,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+       ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+       ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+        cat >$tmp/stdin
+        ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+        ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+        ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+         X"$ac_file" : 'X\(//\)[^/]' \| \
+         X"$ac_file" : 'X\(//\)$' \| \
+         X"$ac_file" : 'X\(/\)' \| \
+         .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+         X"$as_dir" : 'X\(//\)[^/]' \| \
+         X"$as_dir" : 'X\(//\)$' \| \
+         X"$as_dir" : 'X\(/\)' \| \
+         .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be
+# absolute.
+ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd`
+ac_abs_top_builddir=`cd "$ac_dir" && cd ${ac_top_builddir}. && pwd`
+ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd`
+ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd`
+
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+  esac
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+                                     sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+         # Absolute (can't be DOS-style, as IFS=:)
+         test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+         echo $f;;
+      *) # Relative
+         if test -f "$f"; then
+           # Build tree
+           echo $f
+         elif test -f "$srcdir/$f"; then
+           # Source tree
+           echo $srcdir/$f
+         else
+           # /dev/null tree
+           { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+         fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='[       ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+        cat >$tmp/stdin
+        ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+        ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+        ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+         # Absolute (can't be DOS-style, as IFS=:)
+         test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+         echo $f;;
+      *) # Relative
+         if test -f "$f"; then
+           # Build tree
+           echo $f
+         elif test -f "$srcdir/$f"; then
+           # Source tree
+           echo $srcdir/$f
+         else
+           # /dev/null tree
+           { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+         fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+  # Remove the trailing spaces.
+  sed 's/[     ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h.  The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status.  Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[   ]*#[    ]*define[       ][      ]*\([^  (][^    (]*\)\(([^)]*)\)[       ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[   ]*#[    ]*define[       ][      ]*\([^  ][^     ]*\)[   ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless.  Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo '  if grep "^[    ]*#[    ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo '  # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo '  :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+  # Write a limited-size here document to $tmp/defines.sed.
+  echo '  cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#define' lines.
+  echo '/^[    ]*#[    ]*define/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/defines.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+  rm -f conftest.defines
+  mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo '  fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+  # Write a limited-size here document to $tmp/undefs.sed.
+  echo '  cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#undef'
+  echo '/^[    ]*#[    ]*undef/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+  rm -f conftest.undefs
+  mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    echo "/* Generated by configure.  */" >$tmp/config.h
+  else
+    echo "/* $ac_file.  Generated by configure.  */" >$tmp/config.h
+  fi
+  cat $tmp/in >>$tmp/config.h
+  rm -f $tmp/in
+  if test x"$ac_file" != x-; then
+    if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+         X"$ac_file" : 'X\(//\)[^/]' \| \
+         X"$ac_file" : 'X\(//\)$' \| \
+         X"$ac_file" : 'X\(/\)' \| \
+         .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+      { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+         X"$as_dir" : 'X\(//\)[^/]' \| \
+         X"$as_dir" : 'X\(//\)$' \| \
+         X"$as_dir" : 'X\(/\)' \| \
+         .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+      rm -f $ac_file
+      mv $tmp/config.h $ac_file
+    fi
+  else
+    cat $tmp/config.h
+    rm -f $tmp/config.h
+  fi
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+echo configuring zlib...
+(
+ cd zlib
+ ./configure
+ cd ..
+)
+
+echo ""
+echo "      ******* Bahamut Configuration Settings *******"
+echo "      System Build Type:              $target"
+echo "      Socket Engine Type:             $engine"
+echo "      Encryption:                     $encryption"
+echo "      Loadable Modules:               $set_hmodules"
+echo "      Maximum Connections:            $maxconnections"
+echo "      Install Directory:              $prefix"
+echo ""
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..547d900
--- /dev/null
@@ -0,0 +1,569 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl rewritten for my own personal sanity, and for more extensive
+dnl checks - feb04 -epi
+
+AC_INIT
+
+AC_CONFIG_HEADER(include/setup.h)
+
+PACKAGE=bahamut-release
+VERSION=1.8
+AC_SUBST(PACKAGE)
+AC_SUBST(VERSION)
+
+# autoheader templates
+
+AH_TEMPLATE([BROKEN_FD_SETSIZE],[If on a system with a broken FD_SETSIZE])
+AH_TEMPLATE([HAVE_ENCRYPTION_ON],[If we support encryption])
+AH_TEMPLATE([HAVE_MINMAX],[If we have the MIN and MAX macros])
+AH_TEMPLATE([OS_SOLARIS2],[If we're running on Solaris])
+AH_TEMPLATE([OS_SOLARIS],[Not quite sure why we have two of these])
+AH_TEMPLATE([SYS_ERRLIST_DECLARED],[If the sys_errlist array is defined])
+AH_TEMPLATE([USE_HOOKMODULES],[Do we support loadable modules])
+AH_TEMPLATE([MAXCONNECTIONS],[Maximum Connections we allow])
+AH_TEMPLATE([NEED_EPOLL_DEFS],[epoll behavior])
+AH_TEMPLATE([AIX],[AIX support])
+AH_TEMPLATE([WRITEV_IOV],[Maxmimum number of iovecs supported by writev()])
+
+dnl Put our options of here for ease of reading.
+
+AC_ARG_ENABLE(hookmodules,
+[  --disable-hookmodules   disable support for loadable hook modules],,
+[ check_hmodules="yes" ])
+
+AC_ARG_ENABLE(openssl,
+[  --enable-openssl[=DIR]       Enable OpenSSL support (DIR optional).
+  --disable-openssl            Disable OpenSSL support. ],
+[ cf_enable_openssl=$enableval ],
+[ cf_enable_openssl="auto" ])
+
+AC_ARG_WITH(socketengine,
+[  --with-socketengine=TYPE       Set the socketengine type.  Choices are:
+                                kqueue poll select ],
+[ check_sengine=$withval ],
+[ check_sengine="yes" ])
+AC_ARG_WITH(maxconnections,
+[  --with-maxconnections=NUMBER   Set the maximum number of sockets.],
+[ check_maxconnections=$withval ],
+[ check_maxconnections="auto" ])
+
+dnl -----------------------------------------------------------------
+dnl this adds our check for proper warnings checks
+dnl http://ac-archive.sourceforge.net/guidod/ax_cflags_warn_all.html
+dnl handy.
+
+AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl
+AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
+AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_warn_all])dnl
+AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings],
+VAR,[VAR="no, unknown"
+ AC_LANG_SAVE
+ AC_LANG_C
+ ac_save_[]FLAGS="$[]FLAGS"
+for ac_arg dnl
+in "-pedantic  % -Wall"       dnl   GCC
+   "-xstrconst % -v"          dnl Solaris C 
+   "-std1      % -verbose -w0 -warnprotos" dnl Digital Unix 
+   "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX
+   "-ansi -ansiE % -fullwarn" dnl IRIX
+   "+ESlit     % +w1"         dnl HP-UX C 
+   "-Xc        % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10)
+   "-h conform % -h msglevel 2" dnl Cray C (Unicos)
+   # 
+do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+   AC_TRY_COMPILE([],[return 0;],
+   [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+done
+ FLAGS="$ac_save_[]FLAGS"
+ AC_LANG_RESTORE
+])
+case ".$VAR" in
+     .ok|.ok,*) m4_ifvaln($3,$3) ;;
+   .|.no|.no,*) m4_ifvaln($4,$4,[m4_ifval($2,[
+        AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])
+                      m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])]) ;;
+   *) m4_ifvaln($3,$3,[
+   if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
+   then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
+   else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
+                      m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
+   fi ]) ;;
+esac
+AS_VAR_POPDEF([VAR])dnl
+AS_VAR_POPDEF([FLAGS])dnl
+])
+
+dnl the only difference - the LANG selection... and the default FLAGS
+
+AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl
+AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
+AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_warn_all])dnl
+AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings],
+VAR,[VAR="no, unknown"
+ AC_LANG_SAVE
+ AC_LANG_CXX
+ ac_save_[]FLAGS="$[]FLAGS"
+for ac_arg dnl
+in "-pedantic  % -Wall"       dnl   GCC
+   "-xstrconst % -v"          dnl Solaris C 
+   "-std1      % -verbose -w0 -warnprotos" dnl Digital Unix 
+   "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX
+   "-ansi -ansiE % -fullwarn" dnl IRIX
+   "+ESlit     % +w1"         dnl HP-UX C 
+   "-Xc        % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10)
+   "-h conform % -h msglevel 2" dnl Cray C (Unicos)
+   # 
+do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+   AC_TRY_COMPILE([],[return 0;],
+   [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+done
+ FLAGS="$ac_save_[]FLAGS"
+ AC_LANG_RESTORE
+])
+case ".$VAR" in
+     .ok|.ok,*) m4_ifvaln($3,$3) ;;
+   .|.no|.no,*) m4_ifvaln($4,$4,[m4_ifval($2,[
+        AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])
+                      m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $2"])]) ;;
+   *) m4_ifvaln($3,$3,[
+   if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
+   then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
+   else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
+                      m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
+   fi ]) ;;
+esac
+AS_VAR_POPDEF([VAR])dnl
+AS_VAR_POPDEF([FLAGS])dnl
+])
+
+dnl  implementation tactics:
+dnl   the for-argument contains a list of options. The first part of
+dnl   these does only exist to detect the compiler - usually it is
+dnl   a global option to enable -ansi or -extrawarnings. All other
+dnl   compilers will fail about it. That was needed since a lot of
+dnl   compilers will give false positives for some option-syntax
+dnl   like -Woption or -Xoption as they think of it is a pass-through
+dnl   to later compile stages or something. The "%" is used as a
+dnl   delimimiter. A non-option comment can be given after "%%" marks.
+
+dnl -------------------------------------------------------------------
+
+dnl Checks for programs.
+AC_PROG_CC
+AX_CFLAGS_WARN_ALL
+AC_CANONICAL_SYSTEM
+AC_ISC_POSIX
+AC_PROG_MAKE_SET
+AC_PATH_PROG(RM, rm)
+AC_PATH_PROG(CP, cp)
+AC_PATH_PROG(MV, mv)
+AC_PROG_INSTALL
+AC_C_INLINE
+
+dnl Checks for libraries.
+dnl Replace `main' with a function in -lnsl:
+AC_CHECK_LIB(nsl, gethostbyname)
+AC_CHECK_FUNC(res_mkquery,, AC_CHECK_LIB(resolv, res_mkquery))
+AC_CHECK_FUNC(__res_mkquery,, AC_CHECK_LIB(resolv, __res_mkquery))
+AC_CHECK_LIB(socket, socket)
+AC_CHECK_FUNC(crypt,, AC_CHECK_LIB(descrypt, crypt,,AC_CHECK_LIB(crypt, crypt,,)))
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([fcntl.h limits.h malloc.h netdb.h netinet/in.h stddef.h])
+AC_CHECK_HEADERS([stdlib.h string.h strings.h sys/file.h sys/ioctl.h])
+AC_CHECK_HEADERS([sys/param.h sys/socket.h sys/time.h syslog.h unistd.h])
+AC_CHECK_HEADERS([utmp.h])
+AC_CHECK_HEADERS(inttypes.h sys/resource.h errno.h stdlib.h stddef.h getopt.h)
+AC_MSG_CHECKING(MIN and MAX definition)
+AC_EGREP_CPP(yes,
+[
+#if defined( HAVE_SYS_PARAM_H )
+#include <sys/param.h>
+#ifdef MIN
+  yes
+#endif
+#endif
+], AC_DEFINE(HAVE_MINMAX) AC_MSG_RESULT(yes), AC_MSG_RESULT(no))
+
+
+dnl Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_CHECK_FUNCS([alarm dup2 gethostbyname gethostname getpass gettimeofday])
+AC_CHECK_FUNCS([inet_ntoa isascii memmove memset munmap setenv socket])
+AC_CHECK_FUNCS([strcasecmp strchr strdup strerror strncasecmp strrchr strtol])
+AC_CHECK_FUNCS([strtoul index strerror strtoken strtok inet_addr inet_netof])
+AC_CHECK_FUNCS([inet_aton gettimeofday lrand48 sigaction bzero bcmp bcopy])
+AC_CHECK_FUNCS([dn_skipname __dn_skipname getrusage times break])
+
+dnl check for various OSes
+
+case "$target" in
+    *-solaris2*)
+        solaris2="yes"
+        AC_DEFINE(OS_SOLARIS2)
+        AC_DEFINE(OS_SOLARIS)
+        AC_DEFINE_UNQUOTED(WRITEV_IOV, 16)
+        ;;
+    *-freebsd*)
+        freebsd="yes"
+        AC_DEFINE_UNQUOTED(WRITEV_IOV, 32)
+        ;;
+    *-netbsd*)
+        AC_DEFINE_UNQUOTED(WRITEV_IOV, 32)
+        ;;
+    *-openbsd*)
+        AC_DEFINE_UNQUOTED(WRITEV_IOV, 32)
+        ;;
+    *-linux*)
+        linux="yes"
+        AC_DEFINE_UNQUOTED(WRITEV_IOV, 32)
+        ;;
+    *aix*)
+        aix="yes"
+        AC_DEFINE(AIX)
+        ;;
+    *-darwin*)
+        check_hmodules="no"
+        AC_DEFINE_UNQUOTED(WRITEV_IOV, 32)
+        ;;
+esac
+
+dnl Checks for loadable hook module support
+
+set_hmodules="disabled"
+
+if test "$check_hmodules" = "yes"; then
+    AC_CHECK_LIB(dl, dlopen)
+    AC_CHECK_HEADER(dlfcn.h)
+    AC_CHECK_FUNCS(dlopen dlsym, [set_hmodules="enabled"])
+    if test "$set_hmodules" = "enabled"; then
+        AC_DEFINE(USE_HOOKMODULES)
+        MOD_LIBS="-Wl,-export-dynamic"
+    fi
+fi
+
+dnl Check for sys_errlist
+dnl Stolen from BitchX
+AC_MSG_CHECKING(for sys_errlist declaration)
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+main()
+{
+  char *s = sys_errlist[0];
+         exit(0);
+}
+],
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(SYS_ERRLIST_DECLARED),
+  AC_MSG_RESULT(no), AC_MSG_RESULT(cross compiling))
+
+dnl Openssl checks - combination of our old way and the hybrid7 method
+
+save_LIBS="$LIBS"
+
+AC_MSG_CHECKING(for OpenSSL)
+
+if test "X$cf_enable_openssl" != "Xno" ; then
+  cf_openssl_basedir=""
+  if test "X$cf_enable_openssl" != "Xauto" &&
+     test "X$cf_enable_openssl" != "Xyes"; then
+     dnl Support for --enable-openssl=/some/place
+     cf_openssl_basedir="${cf_enable_openssl}"
+  else
+    dnl Do the auto-probe here.  Check some common directory paths.
+    for dirs in /usr/local/ssl /usr/pkg /usr/local /usr/lib /usr/lib/ssl\
+                /opt /opt/openssl /usr/local/openssl ; do
+      if test -f "${dirs}/include/openssl/opensslv.h" ; then
+        cf_openssl_basedir="${dirs}"
+        break
+      fi
+    done
+    unset dirs
+  fi
+  dnl Now check cf_openssl_found to see if we found anything.
+  if test "X$cf_openssl_basedir" != "X" ; then
+    if test -f "${cf_openssl_basedir}/include/openssl/opensslv.h" ; then
+      SSL_INCLUDES="-I${cf_openssl_basedir}/include"
+      SSL_LIBS="-L${cf_openssl_basedir}/lib"
+    else
+      dnl OpenSSL wasn't found in the directory specified.  Naughty
+      dnl administrator...
+      cf_openssl_basedir=""
+    fi
+  else
+    dnl Check for stock FreeBSD 4.x and 5.x systems, since their files
+    dnl are in /usr/include and /usr/lib.  In this case, we don't want to
+    dnl change INCLUDES or LIBS, but still want to enable OpenSSL.
+    dnl We can't do this check above, because some people want two versions
+    dnl of OpenSSL installed (stock FreeBSD 4.x/5.x and /usr/local/ssl)
+    dnl and they want /usr/local/ssl to have preference.
+    if test -f "/usr/include/openssl/opensslv.h" ; then
+      cf_openssl_basedir="/usr"
+    fi
+  fi
+  dnl If we have a basedir defined, then everything is okay.  Otherwise,
+  dnl we have a problem.
+  if test "X$cf_openssl_basedir" != "X" ; then
+    LIBS="$save_LIBS $SSL_LIBS -lcrypto"
+    AC_MSG_RESULT($cf_openssl_basedir)
+    cf_enable_openssl="yes"
+    encryption="enabled"
+    AC_DEFINE(HAVE_ENCRYPTION_ON)
+    ENCRYPT_SRC="rc4.c dh.c"
+    AC_SUBST(ENCRYPT_SRC)
+  else
+    AC_MSG_RESULT(not found.  Please check your path.)
+    cf_enable_openssl="no"
+    encryption="disabled"
+  fi
+  unset cf_openssl_basedir
+else
+  dnl If --disable-openssl was specified
+  encryption="disabled"
+  AC_MSG_RESULT(disabled)
+fi
+
+unset save_LIBS
+
+dnl end of openssl library test
+
+dnl once we add epoll and /dev/poll support, these will be
+dnl updated with the appropriate checks
+
+if test "$check_sengine" = "yes"; then
+    engine="select"
+    if test "$freebsd" = "yes"; then
+        AC_CHECK_FUNC(kevent, [ engine="kqueue" ], )
+    elif test "$solaris2" = "yes"; then
+        engine="poll"
+        AC_CHECK_HEADER(sys/devpoll.h, engine="devpoll", )
+    elif test "$linux" = "yes"; then
+        AC_CHECK_FUNC(poll, [ engine="poll" ], )
+        AC_CHECK_HEADER(sys/epoll.h, HAVE_EPOLL_H=yes, HAVE_EPOLL_H=no)
+        AC_CHECK_LIB(epoll, epoll_create, 
+            [LIBS="$LIBS -lepoll"; have_epoll_lib="yes"; engine="epoll"], 
+            have_epoll_lib="no")
+        if test "$HAVE_EPOLL_H $have_epoll_lib" = "yes no"; then
+            AC_MSG_CHECKING(for epoll_create [without -lepoll])
+            AC_TRY_RUN([
+                    #include <stdint.h>
+                    #include <sys/epoll.h>
+                    #include <errno.h>
+                    
+                    #include <asm/unistd.h>
+                    
+                    _syscall1(int, epoll_create, int, size)
+                    
+                    int main(int argc, char **argv) { return epoll_create(5) < 1; }
+                    ], 
+                    have_epoll_lib="yes", have_epoll_lib="no")
+            AC_MSG_RESULT($have_epoll_lib)
+            if test "$have_epoll_lib" = "yes"; then
+                AC_DEFINE(NEED_EPOLL_DEFS)
+            fi
+        fi
+        if test "$HAVE_EPOLL_H $have_epoll_lib" = "yes yes"; then
+            engine="epoll";
+        fi
+    else
+        AC_CHECK_FUNC(poll, [ engine="poll"], )
+    fi
+elif test "$check_sengine" = "kqueue"; then
+    AC_CHECK_FUNC(kevent, [ engine="kqueue" ], )
+    if test "X$engine" = "X"; then
+        AC_MSG_NOTICE()
+        AC_MSG_NOTICE(kqueue not supported on this platform!)
+        AC_MSG_ERROR()
+    fi
+    AC_MSG_NOTICE(setting socketengine type to kqueue)
+elif test "$check_sengine" = "poll"; then
+    AC_CHECK_FUNC(poll, [ engine="poll" ], )
+    if test "X$engine" = "X"; then
+        AC_MSG_NOTICE()
+        AC_MSG_NOTICE(poll not supported on this platform!)
+        AC_MSG_ERROR()
+    fi
+    AC_MSG_NOTICE(setting socketengine type to poll)
+elif test "$check_sengine" = "devpoll"; then
+    AC_CHECK_HEADER(sys/devpoll.h, engine="devpoll", )
+    if test "X$engine" = "X"; then
+        AC_MSG_NOTICE()
+        AC_MSG_NOTICE(/dev/poll not supported on this system)
+        AC_MSG_ERROR()
+    fi
+    AC_MSG_NOTICE(setting socketengine to devpoll)
+elif test "$check_sengine" = "epoll"; then
+    AC_CHECK_HEADER(sys/epoll.h, HAVE_EPOLL_H=yes, HAVE_EPOLL_H=no)
+    AC_CHECK_LIB(epoll, epoll_create, 
+        [LIBS="$LIBS -lepoll"; have_epoll_lib="yes"; engine="epoll"], 
+        have_epoll_lib="no")
+    if test "$HAVE_EPOLL_H $have_epoll_lib" = "yes no"; then
+        AC_MSG_CHECKING(for epoll_create [without -lepoll])
+        AC_TRY_RUN([
+                #include <stdint.h>
+                #include <sys/epoll.h>
+                #include <errno.h>
+                 
+                #include <asm/unistd.h>
+                 
+                _syscall1(int, epoll_create, int, size)
+                
+                int main(int argc, char **argv) { return epoll_create(5) < 1; }
+                ], 
+                have_epoll_lib="yes", have_epoll_lib="no")
+        AC_MSG_RESULT($have_epoll_lib)
+        if test "$have_epoll_lib" = "yes"; then
+            AC_DEFINE(NEED_EPOLL_DEFS)
+        fi
+    fi
+    if test "$HAVE_EPOLL_H $have_epoll_lib" != "yes yes"; then
+        AC_MSG_NOTICE()
+        AC_MSG_ERROR(epoll is not supported on this machine)
+        AC_MSG_ERROR()
+    fi
+    AC_MSG_NOTICE(setting socketengine type to epoll)
+    engine="epoll";
+elif test "$check_sengine" = "select"; then
+    AC_MSG_NOTICE(setting socketengine type to select.. for some stupid reason)
+    engine="select"
+else
+    AC_MSG_NOTICE()
+    AC_MSG_NOTICE(socket engine type incorrect!)
+    AC_MSG_ERROR()
+fi
+
+SENGINE="socketengine_$engine.c"
+
+dnl Check to see if we have a broken FD_SETSIZE
+if test $engine = "select"; then
+    AC_MSG_CHECKING(to see if FD_SETSIZE is broken)
+    AC_TRY_RUN([
+    #define FD_SETSIZE 666
+    #include <sys/types.h>
+    #include <sys/time.h>
+
+    int main()
+    {
+        if(FD_SETSIZE != 666)
+            exit(1);
+        exit(0);
+    }
+    ],
+    AC_MSG_RESULT(no),
+    [
+    AC_MSG_RESULT(YES)
+    AC_MSG_WARN(FD_SETSIZE is hard set by your operating system)
+    AC_MSG_WARN(MAXCONNECTIONS must be no higher than the hardwired FD_SETSIZE)
+    AC_DEFINE(BROKEN_FD_SETSIZE)
+    broken_fd_setsize="yes"
+    ],
+    AC_MSG_RESULT(can't tell))
+fi
+
+if test "$solaris2" != "yes" && test "$aix" != "yes" && 
+   test "$set_hmodules" = "enabled"; then
+    LIBS="$LIBS $MOD_LIBS"
+fi
+
+if test "$check_maxconnections" = "auto"; then
+
+    AC_MSG_CHECKING(Maximum file descriptors);
+    AC_CACHE_VAL(maxconnections,
+[
+
+AC_LANG_CONFTEST(
+   [AC_LANG_SOURCE([[
+    #include <sys/types.h>
+    #include <sys/time.h>
+    #include <sys/resource.h>
+    #include <stdio.h>
+    #ifdef RLIMIT_FDMAX
+    #define RLIMIT_FD_MAX   RLIMIT_FDMAX
+    #else
+    #ifdef RLIMIT_NOFILE
+    #define RLIMIT_FD_MAX RLIMIT_NOFILE
+    #else
+    #ifdef RLIMIT_OPEN_MAX
+    #define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
+    #else
+    #undef RLIMIT_FD_MAX
+    #endif
+    #endif
+    #endif
+
+
+    int main()
+    {
+    #ifndef BROKEN_FD_SETSIZE
+
+    #ifdef RLIMIT_FD_MAX
+        struct rlimit limit;
+        
+        if(!getrlimit(RLIMIT_FD_MAX, &limit))
+        {
+            if(limit.rlim_max > 32768)
+                printf("32768");
+            else
+                printf("%ld", (long) limit.rlim_max);
+        }
+        else
+    #endif
+            printf("256");
+
+    #else
+        printf("%d", FD_SETSIZE);
+    #endif
+        return 0;
+    }
+   ]])
+])
+
+    $CC -o conftest conftest.c >/dev/null 2>&1
+    maxconnections=`./conftest`
+    AC_MSG_RESULT($maxconnections)
+    $RM -f conftest conftest.c
+])
+    AC_DEFINE_UNQUOTED(MAXCONNECTIONS, ${maxconnections})
+
+else
+    maxconnections=$check_maxconnections
+    AC_DEFINE_UNQUOTED(MAXCONNECTIONS, ${maxconnections})
+fi
+
+if test "$engine" = "select" && test "$broken_fd_setsize" != "yes"; then
+    CFLAGS="$CFLAGS -DFD_SETSIZE=$maxconnections"
+fi
+
+if test $prefix = NONE; then
+    prefix=${HOME}/ircd
+fi
+
+INSTALL_DIR="${prefix}"
+
+AC_SUBST(SSL_INCLUDES)
+AC_SUBST(SSL_LIBS)
+AC_SUBST(LIBS)
+AC_SUBST(SENGINE)
+AC_SUBST(INSTALL_DIR)
+
+AC_OUTPUT(Makefile src/Makefile tools/Makefile doc/Makefile)
+
+echo configuring zlib...
+(
+ cd zlib
+ ./configure
+ cd ..
+)
+
+echo ""
+echo "      ******* Bahamut Configuration Settings *******"
+echo "      System Build Type:              $target"
+echo "      Socket Engine Type:             $engine"
+echo "      Encryption:                     $encryption"
+echo "      Loadable Modules:               $set_hmodules"
+echo "      Maximum Connections:            $maxconnections"
+echo "      Install Directory:              $prefix"
+echo ""
diff --git a/doc/Bahamut-team b/doc/Bahamut-team
new file mode 100644 (file)
index 0000000..951b4eb
--- /dev/null
@@ -0,0 +1,45 @@
+Bahamut was taken on by a group of loosely knit coders that decided that
+Dreamforge just was eating up too much CPU to handle DALnet's growing user
+base.  Originally starting out as the DFhybrid project, it slowly grew
+into the Bahamut project as the peices started falling together.
+
+Bahamut is based upon the hybrid IRCd, developed for EFnet, and we most
+surely wouldn't be where we are today without the dedication of the coders
+that developed that IRCd.  The Hybrid team is outlined in the file
+included with this distribution, in doc/Hybrid-team.  Please look over
+that file, those people are as much to credit for this as the people
+listed here.
+
+People who have supported the Bahamut project with code, support, or
+testing are, in alphabetical order:
+
+Aaron Wiebe         epiphani@dal.net
+Chip Norkus         wd@dal.net
+David Friedman      driz@dal.net
+David Knepper       dakal@dal.net
+David Parton        sedition@dal.net
+Diane Bruce         db@koruna.varner.com
+Ian Westcott        rakarra@dal.net
+Jason Slagle        raistlin@bahamut.net
+Karthik Arumugham   karthik@karthik.com
+Kevin Turner        kevin@dal.net
+Lucas Madar         lucas@dal.net
+Mark Salerno        msofty@dal.net
+Peter Wood          doc_z@dal.net
+Ryan Smith          xpsycho@dal.net
+Sven Nielsen        dalvenjah@dal.net
+
+Thanks goes out to all those not listed here, your help is much
+appreciated.
+
+Feel free to contact the Bahamut team with any bugs, bug fixes, and the
+like at: coders@dal.net
+
+Also, there is a public mailing list for bahamut: dalnet-src@dal.net.
+This list would be the place to send suggestions and comments regarding
+Bahamut.
+
+And, last but not least, the Bahamut website is at http://bahamut.dal.net.
+
+Thanks for the Support,
+The Bahamut Coding Team
diff --git a/doc/CHANGES_1.4 b/doc/CHANGES_1.4
new file mode 100644 (file)
index 0000000..8923288
--- /dev/null
@@ -0,0 +1,266 @@
+Changes for 1.4.36
+- Fix bug that caused bans to be expired ~1 million times on startup
+- Remove opers from fdlists that are set -o via SVSMODE
+- Only show IPs of unknowns or clients to opers in /trace
+- Use OpenSSL instead of GMP for encryption (much cleaner! :)
+- Remove non-SSJOIN support
+- Allow for a server to detect it is out of synch, and request a 
+  channel resynch from an upstream hub
+- Fix remote /stats k/K/a bug (it works now :)
+- Optimize some sendto() things that are used a lot
+- Add usermode +I (server hiding)
+  - Fix /whois, /admin, /who, etc to not allow a user to discover which 
+    server a +I user is on
+- Don't let *MY* opers /kill U: lined things (but pass on kills destined for them already)
+- Chatops don't require you to be +gb (now, just +b)
+- Various typos fixed
+- No longer complain if a user is already on a +i channel, and they /join it
+- Use hash tables for very common res functions, which were taking up
+  as much as 80% of CPU time in a pooled server
+- add a command, /userip, for opers. Works just like /userhost.
+- Allow /who +c @#channel +#channel @+#channel for more refined /who listings
+- Add a /who flag for opers, +/-t, clients who have been on for more/less than <param> seconds
+- Don't let opers get stuck in weird anti-flooding bugs (oper recvq lock bug)
+- When an oper deopers, they are returned to the appropriate client class
+- Allow opers to /who +l <class> for local clients (class = Y: class)
+- Send optimizations for high-volume server connections
+- Remove fdlists for lists of clients and instead use linked lists for things:
+  - recvq clients, instead of searching through *all* clients
+  - listing clients, ditto
+- Remove certain things which actually caused poor server operation:
+  - HTM - relax sending out when we have high incoming traffic. 
+    This actually causes a nasty bursty cycle.
+  - busy clients
+    This was taking up a great deal of CPU time to save CPU time. Hmm.
+- Rewrite the entire base socket engine; now supports kqueue on freebsd.
+  - Poll() and select() use persistant states, so these should be twice as fast as well.
+  - Stop remapping fds. What's the point?
+- /stats w reports fd map (don't use this on a large server! :)
+- /stats z is more informative
+- Hide the source servers of numerics (mask them with me.name)
+- Hide /kill sources in quit messages (still in +s, though) and in messages
+  sent to killed client
+- Hide split sources in quit messages
+- Add a LINKSCONTROL command which allows a services-controlled /links list
+- Better server traffic accounting in /stats ? (uses a moving average instead of a plain average)
+- Fix a bug in which connecting servers will always remove a key if the channel exists (argh!)
+- Correctly propogate cmode +O
+- Show more squelched info in /whois to opers
+- Be smarter about broken FD_SETSIZE implementations
+- Crash if we try to free something twice in the block allocator, produces a useful coredump
+  instead of confused opers and an unrelated crash hours later.
+- ability to lock /lusers output via services for a dynamic amount of time
+- Fix 'ghost channels' where I create a channel but none of the users in it exist to me
+- Allow /who +i ip/cidr and /who +I (show IPs only) for opers
+- SAJOIN, like SAMODE for joining channels. Joins through all modes, sends a globop.
+- Local servers notify the channel of an invite, not the server the invitee is on
+- Remove the drone module, replace it with a powerful and generic module system that
+  has event-based hooks.
+- Streamline some privmsg stuff, fix a few bugs.
+- Fix a bug in which an IP ban on 1.2.3.4* wouldn't work.
+- Dehybridize some things.
+- SVSMODE +T, which sets a "client type" that can be seen in a /who +T <clienttype>
+- Don't allow ',' in channel keys
+- Fix umode +r/-r notification to clients
+- Pretty print some things in /stats ?
+- Remove support for non-NOQUIT servers
+- Modes sent from servers come from the local server now
+- Complain about /who +<flag>, where <flag> isn't valid, instead of sending the entire client list
+- Move SQLINES and SGLINES into another hash-based structure for fast lookups
+- Add SVSHOLD, which adds a local temp qline for a nick for N seconds, intended to replace
+  enforcers in services.
+- Make HTM work on outgoing traffic (the part of HTM that limits sending lots of expensive 
+  things like /list)
+- Don't complain about servers relinking that look 'juped'
+  (don't complain if the server that already exists is U: lined, unless I actually have a 
+   .conf U: line for it!)
+- Compile with GCC3!
+- Nicer umode separation, ie what used to come in as *** Notice -- comes in as:
+  +d : *** Debug --
+  +y : *** Spy --
+  +c : *** Client --
+- Channel join throttling (cmode +j, but disabled for user changes by default to be compatible with
+  pre-1.4.36), defaults to 8 network non-split joins in 4 seconds before saying channel is full.
+- Fix the DEBUGMODE pattern string exploit
+
+Changes for 1.4.35
+- Source address support on /connect problem fixed
+- Squelching added, umode +x or +X (+X is silent squelch), this
+  prevents the user from doing certain online tasks such as privmsg.
+  Meant to be set by a U: lined server or client using SVSMODE.
+- Complete rewrite of the kline/akill/zline system. (s)zlines no longer 
+  exist. K: lines set on IP addresses without username portions (or *)
+  are treated as Z: lines used to be. CIDR banning is now supported
+  in all K: line/akill bans (ie, 204.127.54.112/28).
+- +F usermode bypasses ircd's recvq throttling for opers
+- Improved handling under high load situations
+- Ban munching bug introduced in 1.4.34 is fixed
+- /rehash GC now works
+- lots of sendto_ops_lev changed to sendto_realops_lev for speed
+- Fix a bug where we'd try to target limit messages by a server and
+  write to locations of memory that weren't ours :)
+- Fix O: flags that just didn't work as advertised
+- Moved rejection notices to umode +j (re_j_ection)
+- Moved KILL messages from U: lined clients to umode +K (oper only)
+- Support for a loadable drone module, compile with --enable-dronemodule, 
+  ircd tries to load drone.so from DPATH on startup or on /rehash drones,
+  read src/drone.c to view the interface
+- /stats t shows more information
+- Various code cleanup and readability fixes
+- Users and opers will now recieve a different output to '/who ?'.
+- When 'HELP_FORWARD_HS' is defined, non-opers /quote help requests
+  will be forwarded to HelpServ.
+
+Changes for 1.4.34
+- Don't use FD_SETSIZE at all if USE_POLL is defined
+- Fixed a server negotiation bug that could break apart the network
+  when autoconnects and user connects were done at the same instant
+- Fix a bug where PART messages were not sent to 'userB' when 'userA's 
+  QUIT message was squelched on more than 1 channel
+- Fixed split riding ability left over from hybrid (where a channel
+  with a newer TS could 'take over' if it had ops and the older TS channel
+  did not)
+- Fixed 'topic riding' ability that allowed topics from newer TS'd channels
+  to propogate.
+- Fixed channel ban desync on netjoin
+- all channel modes are now sent with a TS
+- Target limiting anti-spam measure installed
+- Reject acebot-style drones by default
+- Minor fixes to prevent annoying auth error messages
+- Minor configuration/installation fixes
+
+-- WARNING --
+1.4.34 may cause desynch in net joins if used on a network with newer servers!
+This is because the algorithm for determining which channel modes to keep
+when servers rejoin has changed. You should upgrade your entire network 
+to 1.4.34.
+-- WARNING --
+
+Changes for 1.4.33
+- Only ops can invite on channels
+- Various desync-causing memory-corrupting bugs fixed
+- Fixed a problem with perm klines where they werent removing the user
+- Moved to zlib 1.1.4, as 1.1.3 was vulnerable (but not directly in ircd)
+- Fixed minor SVSKILL wrong direction issue.
+- Fixed a wierd desync issue with sqlined channels
+- Fixed /stats l's sendM statistic - it wasnt working properly with crypt
+
+Changes for 1.4.32
+- Throttling modified to be more friendly and punish repeat offenders
+- Major bug causing memory corruption (coredumps as well as
+  duplicate or missing messages) fixed
+- Lag on signon fixed (where a user connects, but the server chooses
+  to ignore the user until something is sent to them)
+- Fixed a netsplit detection bug when a leaf server splits from its hub
+  (for no_chanops_when_split)
+- Fix for .conf based K: lines (that just didn't work)
+
+Changes for 1.4.31
+- Connection Throttling Patch from Chip Norkus
+
+Changes for 1.4.30
+- Updated 'make profile'; now on glibc2 systems, you can kill -USR1 the
+  process and get a current profile dump, and restart profiling
+- Kline/akill/zline matching has been revamped, which should help greatly with
+  lag under heavy user loads with many klines/akills/zlines
+- Deny sending .zip files and . files (with a null extension) to prevent
+  the spreading of exploits
+
+Changes for 1.4.29
+- Umode +R to not allow msgs from non registered nicks
+- chanmode +R extended to deny msgs for -r clients in channel
+- +c/+R extended to squelch quit messages with color/from non reg'd nicks
+- wrapped HTM detection code in #ifndef HUB
+- Added channel qline reason display
+- Removed LITTLE_I_LINES as with services they are dumb
+- Massive code reformatting
+- Moved some oper notices around to reduce unnecessary traffic
+- klines match IP field too now, for NICKIP akills.
+- Changed default mask to @DALnet so it's known it's a fake address
+
+- This is the last 1.4 release planned.  For real this time.
+
+Changes for 1.4.28
+- Minor bug fixes
+
+Changes for 1.4.27
+- Fixed key bug
+- Fixed showstopper remoteclientsonlygetpiecesofaClient bug
+
+Changes for 1.4.25
+- Hide Nick Collisions (Revealing services location)
+- Make 005 show more info
+- Add who +i to search by IP
+- Hide stats L for non opers
+- Add NICKIP CAPAB for services to get nicks
+- Channel QLINES from Ryan
+- Removed * from valid key char
+
+Changes for 1.4.23
+- Some error message changes/fixes
+- Fixed a cloaking bug
+- Added support to source from a different local IP address when /connecting
+  out to another server; useful for servers which don't want to use the main
+  IP in the M: line as the server<->server IP
+  - To use: Add :<local IP> to the end of a C: line
+  - E.g.: C:192.168.0.1:pass:server.dal.net:7325:10:192.168.10.200
+- Block DCCs of .htm/.html files due to script viruses being spread in this
+  way
+- Prompt for max channels per user and oper pw encryption in the config
+  script
+- Example.conf brought up to date
+- Display proper error messages when you can't join a channel
+- Fixed the +a (away) who flag; its behavior was reversed from what it should
+  have been
+
+Changes for 1.4.22
+- Fixed a bug where a server with a bad name trying to connect or trying
+  to be used in /links could crash Bahamut
+
+Changes for 1.4.21
+- rate-squelch zipinbuf complain messages
+- turn off yet more DNS debug messages (forward<>reverse query error)
+- oper hostmasking no longer pollutes IPHASH (ouch)
+- configure script reworked to search better for libgmp.a
+- configure script bugs fixed
+
+Changes for 1.4.20
+- /whois works correctly for hostmasked opers. growl.
+- configure gives more information about gmp errors.
+
+Changes for 1.4.19 (released, but not an advised upgrade)
+- Fixed a crashing bug with /whois
+- Fixed a crashing bug with /oper (I need to remember how to handle nulls correctly :)
+- Imported res fixes from bahamut-stable
+- made configure work for solaris to find gmp
+
+Changes for 1.4.18 (never officially released)
+- Changed level of some debug messages to not go to all opers
+
+Changes for 1.4.17 (never officially released)
+- Oper hostmasking:
+  Your I: line password is oper<.password> where password is optional.
+  You then /server <yourserver> <yourport> <portpass:>opernick:operpass
+  Anyone +A can see your real username, host, and IP.
+
+Changes for 1.4.16
+- deal with long-time buffer overflow when more than MAXADDR
+  A DNS answers are received.
+
+Changes for 1.4.15 (never officially released)
+- Crash in res.c fixed (due to wrong question received under heavy load)
+- name resolution prevents ircd dns cache pollution
+
+Changes for 1.4.14 (previous release was 1.4.12)
+- RC4 streaming encryption added, diffie hellman key exchange
+  - Requires GMP library
+  - Vulnerable to man-in-the-middle attacks
+  - Requires 'E' flag in the N: line on both servers.
+- zlib-compressed links added
+  - Requires 'Z' flag in the N: line of a server
+    that wishes to zip outgoing links to that link.
+
+Changes for 1.4.12 (previous release was 1.4.8):
+- /stats ?, v fixed to hide u-lined servers
+- /lusers fixed to hide u-lined servers
+- name resolution is more strict
diff --git a/doc/CODING_STANDARD b/doc/CODING_STANDARD
new file mode 100644 (file)
index 0000000..91453bd
--- /dev/null
@@ -0,0 +1,10 @@
+The following are the Coding Standards for Bahamut.
+
+1)  No C++ style comments are allowed in the code tree.
+2)  All indentations shall be 4 characters long - no tab characters '\t'
+3)  All variable names shall begin with a lowercase letter.
+4)  All variables defined as macros shall be in all Upper Case
+5)  All preprocessor directive shall be at char posistion 0 with no
+    spaces after the #.
+6)  all conditional/loop statements shall have the brace on a line
+    by themselves
diff --git a/doc/Makefile.in b/doc/Makefile.in
new file mode 100644 (file)
index 0000000..6dfd794
--- /dev/null
@@ -0,0 +1,52 @@
+#************************************************************************
+#*   IRC - Internet Relay Chat, Makefile
+#*   Copyright (C) 1990, Jarkko Oikarinen
+#*
+#*   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#*/
+
+RM=@RM@
+MV=@MV@
+
+INSTALL=@INSTALL@
+INSTALL_DATA=@INSTALL_DATA@
+INSTALL_DIR=@INSTALL_DIR@
+
+FILES=opers.txt reference.conf template.conf
+
+all:
+
+build:
+
+clean:
+
+install: all
+       @for i in $(FILES); do \
+               if test -f $(INSTALL_DIR)/$$i; then \
+                       echo $(MV) $(INSTALL_DIR)/$$i $(INSTALL_DIR)/$$i.old; \
+                       $(MV) $(INSTALL_DIR)/$$i $(INSTALL_DIR)/$$i.old; \
+               fi; \
+               echo $(INSTALL_DATA) $$i $(INSTALL_DIR); \
+               $(INSTALL_DATA) $$i $(INSTALL_DIR); \
+       done
+
+       -@if [ ! -f $(INSTALL_DIR)/ircd.motd ] ; then \
+               echo "ircd.motd does not exist, creating..."; \
+               touch $(INSTALL_DIR)/ircd.motd; \
+               echo "This is the default Bahamut MOTD" >> $(INSTALL_DIR)/ircd.motd; \
+       fi
+
+distclean: clean
+       $(RM) -f Makefile
diff --git a/doc/clones.txt b/doc/clones.txt
new file mode 100644 (file)
index 0000000..e37be94
--- /dev/null
@@ -0,0 +1,180 @@
+$Id: clones.txt,v 1.1 2005/01/12 07:44:56 mmondor Exp $
+Originally written by Trevor Talbot (Quension) in October 2004.
+
+
+Clone Limiting
+==============
+Bahamut supports limiting the number of connections from a single place,
+and this file describes the configuration for that feature.  There are two
+kinds of places: a "host" is a single IP (10.0.0.1); a "site" is a single
+/24 block of IPs (10.0.0.*).  Clones are the number of connections from a
+given place.
+
+Two types of clone limits are supported: local clones are the number of
+connections from a place on this server only; global clones refer to
+connections on the entire network.  When a clone limit is reached, the
+server denies further connections from that place.
+
+
+Local clone limiting
+--------------------
+The default local clone limits are set using the local_clones token in the
+Options block in ircd.conf; see doc/reference.conf for details.  In the
+absence of more specific limits, the defaults will regulate all connections
+to the server.
+
+Local limits set using the maxclones token in a Class block override the
+defaults; this is typically used with Allow blocks to provide exemptions
+for specific hosts.  When a Class block defines a host limit, clients in
+that class are implicitly exempt from the default site limit.
+
+Example:
+
+        options { local_clones 1:10; };
+        class { name special; maxclones 5; };
+        allow { host *@10.0.0.1; class special; };
+
+    Suppose there is one client each connected from 10.0.0.2 - 9 (8 clients
+    from 10.0.0.*).  If another client from 10.0.0.2 tries to connect, it
+    will be rejected because the default limit of 1 connection per host was
+    reached.  Five more clients from 10.0.0.1 will be allowed to connect,
+    because the "special" class allows up to 5 connections from a single
+    host.
+
+    Note that because the class did not provide a site limit, five clients
+    from 10.0.0.1 will always be allowed to connect, despite the default
+    limit of 10 per site.  To maintain a site limit for these clients,
+    specify one in the class maxclones statement:
+
+        class { name special; maxclones 5:10; };
+
+The default limits can be changed online using the SET DEFLCLONES command.
+Note that this setting is not saved and will be lost when the server is
+restarted; it is useful for testing or setting temporary limits.
+
+    SET DEFLCLONES                            - to display both limits
+    SET DEFLCLONES <host limit>               - to set the host limit
+    SET DEFLCLONES <host limit>:<site limit>  - to set both limits
+
+    Supplying a limit of 0 will revert to the internal default.
+
+Specific local limits can be set online using the SET LCLONES command;
+these are called "soft" limits and override all other local limits.
+A soft host limit also provides an implicit exemption from class and
+default site limits.  Like DEFLCLONES, these are temporary.
+
+    SET LCLONES <host/site>                   - to display the limit
+    SET LCLONES <host/site> <limit>           - to set the limit
+
+    Use a specific host or site mask, e.g. "10.0.0.1".  Supplying a limit
+    of 0 will remove the soft limit; class and default limits will be used
+    instead.
+
+Note that it is possible to create some interesting configurations using
+the class setting.  For example, this would effectively allow 5 clones from
+a specific ident@host, since only clients matching the allow block would get
+the increased per-host limit:
+
+    options { local_clones 1; };
+    class { name special; maxclones 5; };
+    allow { host ident@host; class special; };
+
+
+Global clone limiting
+---------------------
+This works in basically the same way as local clone limiting, except that
+it considers connections on the entire network, and is intended to be
+managed by network services.
+
+The default global clone limits are set using the global_clones token in
+the Options block in ircd.conf; see doc/reference.conf for details.  In the
+absence of more specific limits, the defaults will regulate all connections
+to the server.  Note that to be effective, all servers must share the same
+global limits.  A server can only reject new connections to itself; it
+cannot tell other servers to do so.
+
+Example:
+
+        options { global_clones 2:6; };
+
+    If there are six clients on other servers on the network, from various
+    addresses in the 10.0.0.* block, this server will not allow another
+    connection from 10.0.0.1, because the global site limit has already
+    been reached.  However, if the remote servers have a higher default,
+    they will still allow more clients to connect.
+
+Network services can set specific limits using the SVSCLONE command; these
+are referred to as "hard" global clone limits.  A hard host limit provides
+an implicit exemption from the default site limit.  The command syntax is
+provided here for services authors to reference; it is not accessible to
+clients.
+
+    SVSCLONE <host/site> <limit>              - to set the limit
+
+    Use a specific host or site mask, e.g. "10.0.0.*".  Supplying a limit
+    of 0 will remove the hard limit.  This setting is stored by all servers
+    and propagated on netburst, in a manner similar to SQLINES.
+
+The default limits can be changed online using the SET DEFGCLONES command.
+Note that this setting is not saved and will be lost when the server is
+restarted; it is useful for testing or setting temporary limits.
+
+    SET DEFGCLONES                            - to display both limits
+    SET DEFGCLONES <host limit>               - to set the host limit
+    SET DEFGCLONES <host limit>:<site limit>  - to set both limits
+
+    Supplying a limit of 0 will revert to the internal default.
+
+Specific global limits can be set online using the SET GCLONES command;
+these are called "soft" global clone limits and override the defaults.
+A lower soft limit will also override a service-set hard limit, but it is
+not possible to set a soft limit higher than a hard one.  Like DEFGCLONES,
+these are temporary.
+
+    SET GCLONES <host/site>                   - to display the limits
+    SET GCLONES <host/site> <limit>           - to set the soft limit
+
+    Use a specific host or site mask, e.g. "10.0.0.1".  Supplying a limit
+    of 0 will remove the soft limit; hard and default limits will be used
+    instead.
+
+
+Monitoring
+----------
+Rejection notices for clone connections are sent to usermode +j, in the
+following format:
+
+    clone <nick!user@host> (<place> <online>/<limit> <type> <from>)
+
+    <nick!user@host> is the rejected client
+    <place> is the host or site mask, e.g. "10.0.0.*"
+    <online> is the number of clients currently online from that place
+    <limit> is the limit for that place
+    <type> is "local" or "global"
+    <from> is where the limit was set:
+           "default", "soft", "hard", "class <name>"
+
+General statistics about the clones system are available on STATS d.
+
+A list of specific limits is available on STATS D, returned on numeric 225:
+
+    D <host/site> <soft local limit> <soft global> <hard global>
+
+
+Notes
+-----
+When local and global limits differ, the lowest limit applies.  This means
+you should carefully choose the defaults and how you want to manage the
+specific limits, as working with both types of limits at once can be
+difficult.
+
+When a client is rejected, its exit reason is one of:
+
+    Too many connections from your host
+    Too many connections from your site
+
+It is not told which specific limit was reached.
+
+Very little host/site mask validation is done for the online commands; it
+is possible to query and set limits for completely bogus masks.  Such
+limits will be retained but never used.
diff --git a/doc/modules.txt b/doc/modules.txt
new file mode 100644 (file)
index 0000000..8603b98
--- /dev/null
@@ -0,0 +1,44 @@
+Bahamut Hook Module System [2003-10-13]
+
+First released in Bahamut 1.4.36, the hook module system permits outside modules
+to be 'hooked' into bahamut without having to modify the core source tree. 
+This is to allow other groups to write code to benefit IRCD that serve
+specific functions. As an example, some groups write modules to block
+common trojan / drone patterns.
+
+Once a module has been built, hooking it into the system is fairly simple.
+The components that are used, are:
+
+1. A modules/ directory where the shared modules themselves are stored
+
+To enable the hook module system, you must issue --enable-hook-modules
+at configuration time. Example:
+
+./configure --enable-hook-modules
+
+(As of bahamut 1.8, this is implicitely assumed.)
+
+---
+
+1. modules/ directory
+
+This directory stores the shared modules that you wish to hook into bahamut.
+The modules/ directory is found in the same directory as your server 
+configuration files (eg: bahamut-release/modules)
+
+---
+
+USER COMMANDS TO CONTROL MODULES
+
+There are a few new commands to control modules. They are all accessed
+by using /quote module <command>.
+
+       /quote module LIST      List all modules loaded in memory
+    /quote module HOOKS List where modules are hooked into ircd
+       /quote module LOAD <name>       Load a module into memory
+       /quote module UNLOAD <name>     Unload a module from memory
+
+To use the above commands, you must be an IRC Operator at the time
+of issuing the command (umode +o).  To issue LOAD/UNLOAD commands, you must be
+a Server Adminstrator (umode +A)
+
diff --git a/doc/old/Authors b/doc/old/Authors
new file mode 100644 (file)
index 0000000..0051d0b
--- /dev/null
@@ -0,0 +1,137 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, doc/AUTHORS
+ *   Copyright (C) 1990
+ *
+ * AUTHORS FILE:
+ *   This file attempts to remember all contributors to the IRC
+ *   developement. Names can be only added this file, no name
+ *   should never be removed. This file must be included into all
+ *   distributions of IRC and derived works.
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+IRC was conceived of and written by Jarkko Oikarinen <jto@tolsun.oulu.fi>.
+IRC was originally written in University of Oulu, Computing Center.
+Jan 1991 - IRC 2.6  jto@tolsun.oulu.fi
+       - Multiple Channels and protocol changes
+
+Contributions were made by a cast of dozens, including the following:
+
+Markku Jarvinen <mta@tut.fi>: Emacs-like editing facility for the client
+
+Kimmo Suominen <kim@kannel.lut.fi>: HP-UX port
+
+Jeff Trim <jtrim@orion.cair.du.edu>: enhancements and advice
+
+Vijay Subramaniam <vijay@lll-winken.llnl.gov>: advice and ruthless publicity
+
+Karl Kleinpaste <karl@cis.ohio-state.edu>: user's manual
+
+Greg Lindahl <gl8f@virginia.edu>: AUTOMATON code, the Wumpus GM automaton,
+myriad bug fixes
+
+Bill Wisner <wisner@hayes.fai.alaska.edu>: numerous bug fixes and code
+enhancements
+
+Tom Davis <conslt16@zeus.unl.edu> and Tim Russell <russell@zeus.unl.edu>:
+VMS modifications
+
+Markku Savela <msa@tel4.tel.vtt.fi>: advice, support, and being the
+incentive to do some of our *own* coding. :)
+
+Tom Hopkins <hoppie@buengf.bu.edu>: bug fixes, quarantine lines,
+consolidation of various patches.
+
+Christopher Davis <ckd@cs.bu.edu>: EFnet/Anet gateway coding,
+many automata ;), documentation fixing.
+
+Helen Rose <hrose@cs.bu.edu>: documentation updating, and fixing.
+
+Tom Hinds <rocker@bucsf.bu.edu>: emacs client updating.
+
+Tim Miller <cerebus@bu-pub.bu.edu>: various server and client-breaking
+features.
+
+Darren Reed <avalon@coombs.anu.edu.au>: various bug fixes and enhancements.
+Introduced nickname and channelname hash tables into the server.
+
+The version 2.2 release was coordinated by Mike Bolotski
+<mikeb@salmon.ee.ubc.ca>.
+
+The version 2.4 release was coordinated by Markku Savela and
+Chelsea Ashley Dyerman
+
+The version 2.5.2 release was coordinated by Christopher Davis, Helen Rose,
+and Tom Hopkins.
+
+The versions 2.6.2, 2.7 and 2.8 releases were coordinated by Darren Reed.
+
+Contributions for the 2.8 release from the following people:
+Matthew Green <phone@coombs.anu.edu.au>
+Chuck Kane <ckane@ece.uiuc.edu>
+Matt Lyle <matt@oc.com>
+Vesa Ruokonen <ruokonen@lut.fi>
+
+Markku Savela <Markku.Savela@vtt.fi> / April 1990
+Fixed various bugs in 2.2PL1 release server (2.2msa.4) and changed
+sockets to use non-blocking mode (2.2msa.9). [I have absolutely
+nothing to do with clients :-]
+
+Chelsea Ashley Dyerman <chelsea@earth.cchem.berkeley.edu> / April 1990
+Rewrote the Makefiles, restructuring of source tree. Added libIrcd.a to
+the Makefile macros, numerous reformatting of server text messages, and
+added mkversion.sh to keep track of compilation statistics. Numerous
+bug fixes and enhancements, and co-coordinator of the 2.4 release.
+
+jarlek@ifi.uio.no added mail functions to irc.
+
+Armin Gruner <gruner@informatik.tu-muenchen.de> / May, June 1990:
+* Patched KILL-line feature for ircd.conf, works now.
+  Enhancement:  Time intervals can be specified in passwd-field.
+  Result: KILL-Line is only active during these intervals
+* Patched PRIVMSG handling, now OPER can specify masks for sending
+  private messages, advantage: msg to all at a specified server or host.
+* Little tests on irc 2.5 alpha, fixed some little typos in client code.
+  Change: common/debug.c has been moved to ircd/s_debug.c, and a
+  irc/c_debug.c has been created, for the benefit that wrong server msg
+  are displayed if client does not recognize them. (strange, if a server
+  sends an 'unknown command', isn't it?)
+
+Tom Hopkins <hoppie@buengf.bu.edu> / September, October 1990:
+* Patched msa's K lines for servers (Q lines).
+* Consolidated several patches, including Stealth's logging patch. 
+* Fixed several minor bugs. 
+* Has done lots of other stuff that I can't seem to remember, but he
+  always works on code, so he has to have done alot more than three
+  lines worth. :) 
+
+Thanks go to those persons not mentioned here who have added their advice,
+opinions, and code to IRC.
+
+Various modifications, bugreports, cleanups and testing by:
+
+Hugo Calendar <hugo@ucscb.ucsc.edu>
+Bo Adler <adler@csvax.cs.caltech.edu>
+Michael Sandrof <ms5n+@andrew.cmu.edu>
+Jon Solomon <jsol@cs.bu.edu>
+Jan Peterson <jlp@hamblin.math.byu.edu>
+Nathan Glasser <nathan@brokaw.lcs.mit.edu>
+Helen Rose <hrose@eff.org>
+Mike Pelletier <stealth@caen.engin.umich.edu>
+Basalat Ali Raja <gwydion@tavi.rice.edu>
+Eric P. Scott <eps@toaster.sfsu.edu>
+Dan Goodwin <fornax@wpi.wpi.edu>
+Noah Friedman <friedman@ai.mit.edu>
diff --git a/doc/old/FAQ b/doc/old/FAQ
new file mode 100644 (file)
index 0000000..b997afc
--- /dev/null
@@ -0,0 +1,17 @@
+These are the two most frequently asked questions about IRC server
+compliation:
+
+* I compile and everything seems fine during the compiling, but when it
+comes time for the program to link, I get errors complaining about dns and
+res things. What causes this?
+
+You need to make sure IRCDLIBS in the toplevel Makefile includes -lresolv
+
+
+* I start the ircd program and I get an error about chdir, no such file or
+directory. What causes this?
+
+Make sure IRCDDIR in the toplevel Makefile is set to the same as DPATH in
+include/config.h
+
+(send suggestions to this FAQ to hrose@cs.bu.edu)
diff --git a/doc/old/Hybrid-team b/doc/old/Hybrid-team
new file mode 100644 (file)
index 0000000..7475496
--- /dev/null
@@ -0,0 +1,35 @@
+
+The hybrid team is a group of ircd coders who were frustrated
+with the instability and all-out "dirtiness" of the efnet ircd's
+available. "hybrid" is the name for the collective efforts of a group
+of people, all of us.
+
+Anyone is welcome to contribute to this effort..
+
+We have run the code through "purify", a commercial program designed
+to find memory leaks. The code has been mostly prototyped. Profiling has also
+been done to catch slow portions of the code. Portions of "crufty" code have
+been re-written. We have also looked at making the code as easy to setup
+up and run as possible using configure, checking header files etc.
+Finally speed enhancements and other enhancements have been made to this ircd. 
+
+That is not to say that there are still not bugs, There are always bugs.
+But we have reviewed each others work on this project, and are open
+to bug reports.
+
+The following people have contributed blood, sweat, and/or code to
+this release of hybrid, in nick alphabetical order.
+
+cbongo, Chris A. Bongaarts (cab@tc.umn.edu)
+comstud, Chris Behrens (cbehrens@concentric.net)
+Dianora, Diane Bruce (db@db.net)
+johan, Johannes Erdfelt (jerdfelt@eng.mindspring.net)
+orabidoo, Roger Espel LLima (espel@unix.bigots.org)
+Rodder, Jon Lusky (lusky@vol.com)
+Shadowfax, Michael Pearce (mpearce@varner.com)
+ThemBones, Brian Kraemer (kraemer@u.washington.edu)
+Wohali, Joan Touzet (joant@cadence.com)
+
+Others are welcome.
+
+email bug fixes/complaints/rotten tomatoes to ircd-hybrid@vol.com
diff --git a/doc/old/Operators b/doc/old/Operators
new file mode 100644 (file)
index 0000000..c950d7b
--- /dev/null
@@ -0,0 +1,242 @@
+Internet Relay Chat Operator Etiquette Guide
+
+May, 1992
+
+Welcome! You've either been selected to be an IRC Operator or you have set
+up your server and thus have taken on the dual task of IRC Server
+Administrator and IRC Operator. Your future days will be filled with hours
+of fun chatting on IRC, and then wondering why everyone you talked to went
+away, because the links had apparently broken. 
+
+Linking:
+========
+
+You will be assigned links from the IRC Routing Coordinators. Please
+use these links and these links ONLY. The links have been designed to
+maximize efficiency and make delays in chatting minimal. You will
+usually be given two links, one to each regional backbone site.
+Connect to the primary site first and then to the secondary site. You
+should not need to connect to any other sites. You will be informed if
+this policy changes.
+
+Kills 
+=====
+
+/kill is a special operator command. You should use it with
+care, and only if absolutely needed. The format is as follows:
+/kill NICKNAME comment. Comment can be a phrase of almost any length
+(within reason) and should be used for specifying the reason of the kill.
+Example: /kill Trillian She's a Ghost
+IRC Ghosts are created after a net split has occured and the net has yet to
+relink. 
+
+/wallops PHRASE This is used to talk to those users who have their
+user mode set to +w. /wallops used to be a way for operators to talk
+about important matters in linking etc., but it has little use
+nowadays. 
+
+/TRACE command /TRACE is useful to know what servers are connected to
+what. Sometimes /trace can be confusing, especially if you are using
+it for the first time.  Here is an example of a trace from
+stekt1.oulu.fi to cdc835.cdc.polimi.it.
+
+/TRACE cdc835.cdc.polimi.it
+
+*** Link stekt1.oulu.fi<2.7.2> ==> cdc835.cdc.polimi.it
+*** Link rieska.oulu.fi<2.7.1>e ==> cdc835.cdc.polimi.it
+*** Link nic.funet.fi<2.7.1>e ==> cdc835.cdc.polimi.it
+*** Link ircserver.et.tudelft.nl<2.7.1>e ==> cdc835.cdc.polimi.it
+*** Link vesuv.unisg.ch<2.7.1>e ==> cdc835.cdc.polimi.it
+*** Link apollo.di.unipi.it<2.7.1>e ==> cdc835.cdc.polimi.it
+*** Oper Class[10] ==> Allanon[cdc835.cdc.polimi.it]  
+*** User Class[11] ==> Lupandy[plus2.usr.dsi.unimi.it]  
+*** Serv Class[3] ==> apollo.di.unipi.it[131.114.4.36] 132S 445C
+*** User Class[11] ==> Punk[pluto.sm.dsi.unimi.it]  
+*** User Class[11] ==> TheEdge[pluto.sm.dsi.unimi.it]  
+*** User Class[10] ==> Mork[cdc835.cdc.polimi.it]  
+*** User Class[11] ==> Lollo[c700-2.sm.dsi.unimi.it]  
+*** User Class[11] ==> Attila[hp2.sm.dsi.unimi.it]  
+*** Class 0  Entries linked 1
+*** Class 11 Entries linked 5
+*** Class 10 Entries linked 2
+*** Class 3  Entries linked 1
+
+From this output you can see that the route goes first to
+rieska.oulu.fi (running version 2.7.1e), then nic.funet.fi,
+ircserver.et.tudelft.nl, vesuv.unisg.ch, and apollo.di.unipi.it, after
+which cdc835 is the next server. Then we see the connections on
+cdc835: One operator (Allanon) and 6 users are on line. The class of
+each connection is given. There is only one server connected to cdc835
+at the moment, and that server is apollo.di.unipi.it (cdc835 is said
+to be a "leaf" server at the moment). The numbers 132S 445C in the end
+of line tell us, that there are 132 servers and 445 clients connected
+to the servers from apollo onwards.  Finally we see a grand total of
+connections in each connection class. 
+
+
+/SQUIT server {comment}
+   /squit isolates a specified server from the next closest server, when
+you look at it along the trace path starting from your server. 
+This is usually used in conjunction with CONNECT (explained later) to
+reroute traffic. This will be described in detail in the section
+"routing", preceding CONNECT.
+
+   Usage (and examples): 
+
+      /squit E
+
+     If the network looks like this initially (and you are on server A)
+
+          A <---> B <---> C <---> D
+                          ^
+                          |
+                          v
+                  G <---> E <---> F <---> ... (rest of the net)
+                          
+
+    Then after issuing the previous /squit the network would look like this:
+
+          A <---> B <---> C <---> D
+                          
+                          
+                  G <---> E <---> F <---> ...
+
+
+    /squit E {comment}
+
+       It usually helps to give a reason why you are sending a
+       SQUIT for a server. This can be accomplished by sending
+       the command "/squit server This link is making the US route
+       through Finland". The SQUIT will then be sent out, and the 
+       server sending the squit will WALLOP sending the comment
+       so all operators can see it. 
+
+/CONNECT server {portnum server2}
+   /connect is used to establish a link between two servers. These
+connections must be authorized by each server's ircd.conf file, but
+any operator can issue a CONNECT between authorized servers. This
+command is most often used in conjunction with SQUIT to reroute
+traffic. 
+   If only one argument is given, this command causes the server you
+are on to attempt to connect to the server specified. For example,
+"/connect B" (in the previous example) would cause your server (A) to
+connect to B. 
+   Suppose you wanted to reconnect server F to server E? You cannot
+contact server F since it is no longer part of your network. However,
+you can tell server E to connect to it. A remote CONNECT can be issued
+to server E. 
+
+   Examples (assume you are on server A):
+
+   /connect B
+
+   If the network initially looks like this:
+
+         A      B <---> ... (rest of network)
+
+   Then afterwards (if the connection succeeds) the network will look
+   like this:
+
+        A <---> B <---> ... 
+
+
+   In the example where you wanted to reconnect server E to F, the
+   following syntax would be appropriate (note: we are assuming that
+   F's irc socket port is 6667, which is the default)
+
+   /connect F 6667 E
+
+   If the network initially looks like this:
+
+         A <---> B <---> C <---> D
+                         ^
+                         |
+                         v
+                 G <---> E      F <---> ... 
+
+   Then after your CONNECT request the network topology will look like this:
+
+         A <---> B <---> C <---> D
+                         ^
+                         |
+                         v
+                 G <---> E <---> F <---> ... 
+
+    Be careful when connecting servers that you know which command to
+    use! If you simply issued "/connect F" from your server, the
+    network would look like this:
+
+
+    ... <---> F <--->  A <---> B <---> C <---> D
+                                       ^
+                                       |
+                                       v
+                               G <---> E 
+
+    which for various reasons (discussed below) might be very
+    undesirable. 
+
+Routing
+=======
+
+   When and how should you do rerouting? This depends on where your
+server is topologically located and whether you route traffic. If you
+are a leaf node (i.e. only connect to one server at a time) then
+chances are you won't need to do any routing at all.  Your ircd.conf
+file should be written to connect to the best possible servers first
+before trying alternates. At the most, you may decide to squit an
+alternate server and connect to your primary if/when it goes back up.
+This only involves local squits, however.
+
+   If you are operating a backbone site, you may find yourself
+rerouting things quite often. If the servers badger.ugcs.caltech.edu
+(Pasadena, CA), irc.mit.edu (Boston, MA), minnie.cc.utexas.edu
+(Austin, TX) and ucsu.colorado.edu (Boulder, CO) were routing traffic
+in the following way:
+
+    ... <---> minnie <---> badger <---> bucsd <---> ucsu <---> ...
+
+It would make sense to either squit ucsu and reconnect it to minnie,
+or disconnect minnie from badger and connect to ucsu, because
+topologically (and geographically) ucsu and minnie are rather close.
+There are occasions when US traffic for some reasons winds up being
+routed through Australia. This is another case where traffic should
+definitely be rerouted. However, there are sometimes occasions when
+routing is going through "backdoor" methods. If you see something
+totally outrageous (like the east coast and the west coast being
+connected by eff.org) please ask for example on channel #twilight_zone
+before you send any squits, because chances are, it's like that for a
+reason.
+
+   Of course, any operator can remotely squit or connect servers, so
+if you see a problem and you're sure you know how to fix it, it's a
+good idea to do so. If the operator of a server which is is being
+routed poorly is online, it's probably best to contact him/her first,
+though.
+
+   Chances are that hub operators will be more familiar with the
+general topology of the network and which servers connect to which
+(which is why most of the manual routing is left to them), so if you
+have any problems, talk to the other operators on operator channels
+(#twilight_zone, #eu-opers etc.) That's what they are there for!
+   Also, be aware that servers will notify all the operators online of
+remote SQUITs and CONNECTs via WALLOPS.
+
+Please let us know if there should be any additions to this guide. Again,
+this is not MANDATORY, this is just a GUIDE. Please conduct yourself as 
+an IRC Operator would...you are looked upon for assistance, both emotional
+and mental. 
+
+Helen Rose             Christopher Davis       Noah Friedman
+<hrose@cs.bu.edu>      <ckd@cs.bu.edu>         <friedman@ai.mit.edu>
+
+January, 1991
+
+
+Updated by
+
+Mauri Haikola
+<mjh@stekt.oulu.fi>
+
+May, 1992
diff --git a/doc/old/README b/doc/old/README
new file mode 100644 (file)
index 0000000..744f661
--- /dev/null
@@ -0,0 +1,8 @@
+This stuff is so ancient it's not even funny anymore.  Take it with
+a grain of salt when you read it. --JRL
+
+The Bill Wisner example.conf has been updated, I have also included
+Helen Roses example.conf as example.conf.trillian (taken from
+the CSR tree ) as it is superiour in some respects to Bill's version.
+Updated as well to reflect hybrid . --Dianora
+
diff --git a/doc/old/README.hybrid b/doc/old/README.hybrid
new file mode 100644 (file)
index 0000000..a278092
--- /dev/null
@@ -0,0 +1,201 @@
++hybrid-5.2p1 (5/12/98)
+removed uneeded variable in aClient (Dianora)
+added missing #ifdef's for USE_SYSLOG (Dianora)
+reverted to old check_pings() to fix high cpu usage (Rodder)
+fixed DEBUGMODE (Dianora)
+
++hybrid-5.2 (4/21/98) - The inevitable bug fix release
+-fixes
+* changed many of the defaults in config.h
+* removed ident_fdlist it was never used anyway (comstud)
+* fixed quote kline for 10 character user names (Dianora)
+* obscure bug with KICK message in a desync channel situation (Dianora)
+* bug with max channel name fixed (comstud/Dianora)
+* stop spam from links command i.e. beeps (Dianora)
+* bug with default +i not showing MODE to user initially (Rodder)
+* ANTI_IP_SPOOF moved to "broken" section of conf (Rodder)
+* ident lookups now work properly for all interfaces of
+  multihomed (or "VIF"'d) machines, including Solaris 2.6 (Rodder)
+* stats l/L no longer shows -i clients to non-opers (Rodder)
+* exorcised evil timedelta variable
+* Fixed oper wallops broadcasting instead of routing (Dianora)
+* Covered up bogus "Got Ident" messages in read_authports() (Rodder)
+* Fixed missing RPL_ENDOFTRACE for non-opered clients (Dianora)
+* #undef IDLE_FROM_MSG fixed, ping/pong/ison do not reset idle time (Dianora)
+
+-feature adds
+* bumped HTM code to not kick in until 40K/s by default
+* stop people from laming up the kline conf file with # in comment (Dianora)
+* added comstuds SEPARATE_QUOTE_KLINES_BY_DATE
+* use block allocator in FLUD code (Shadowfax)
+* added HTM quiet or HTM noisy option -- see NOISY_HTM in config.h
+  for initial setting (Dianora)
+* added LOCOPS, each type of wallops/operwall/locops is now flagged (Dianora)
+* added signon time to RPL_WHOISIDLE (Dianora)
+* allow local opers to see kills requested by riss (Dianora)
+* added ANTI_SPAMBOT code, read config.h (Dianora)
+* added fixklines to tools dir, use it to check kline.conf for #'s inside
+  of K-line comments (Dianora)
+* added OLD_Y_LIMIT in config.h.dist to go back to old Y/I line behaviour
+  (Dianora)
+* M: line passwd field now used to specify address to bind to on
+  machines with multiple interfaces.  (Rodder)
+* P: line passwd field now used to specify address to bind to on  
+  machines with multiple interfaces. (Rodder)
+* added /quote set SPAMNUM SPAMTIME to control ANTI_SPAMBOT behaviour (Dianora)
+* hybrid option flags removed from m_version and replaced
+  by (for opers) full listing of server defines in m_info (Rodder)
+* poll() code mods ported from CSr30 (comstud) by cbongo
+* m_whois() rewritten to use hash table when possible (Dianora)
+* spambot sign on code added (ThemBones)
+* i line (LITTLE_I_LINE) support, restricted i lines (Dianora)
+* NO_CHANOPS_WHEN_SPLIT delays channel hacking until rejoin (Dianora)
+* ANTI_SPAMBOT on sign on code (ThemBones)
+* only allow 4 kicked nicks per kick (Dianora)
+* sped up kline processing, redid check_pings in ircd.c (Dianora) 
+* Server now checks for sane TS when connecting to other server,
+  see TS_MAX_DELTA and TS_WARN_DELTA in config.h (Rodder)
+* Server checks for backwards spinning clock and complains if
+  found (Rodder)
+* Check for error when timeofday is set (Shadowfax)
+* Announce TS changes after TS induced deops (Shadowfax)
+* stop redistributing away information to other servers
+* Prepared for TS4 mixed net by checking validity of channel mode changes
+  only at the local server (orabidoo)
+
++hybrid-5 (9/26/97)
+* LOTS AND LOTS OF CHANGES IN CONFIG.H
+* reorganized source tree, eliminating common subdir
+* reworked hash.c (Dianora)
+* reworked parse.c, tree prefix command lookups (Dianora/orabidoo)
+* all code is now function prototyped (Dianora)
+* improved bad host/nick handling, i.e. dns spoofers (ThemBones)
+* sped up send_umode_out in s_user.c (ThemBones)
+* sped up whowas processing, removed away from whowas (Dianora)
+* redid m_kline/cluster in s_serv.c (Dianora)
+* redid dline/kline handling (Dianora/Rodder/ThemBones)
+* removed lame #ifdefs in poll() (Dianora)
+* always defined D_LINES/E_LINES/F_LINES (Dianora)
+* TS_ONLY server now (Dianora) 
+* cleaned up s_auth.c (Dianora/ThemBones)
+* changed BAN_INFO handling (Dianora)
+* sanity testing in res.c (Dianora)
+* added orabidoo's scache code (orabidoo/Dianora)
+* added KLINE_WITH_REASON kline reason on klines/connects (Dianora)
+* added NON_REDUNDANT_KLINE code (Dianora)
+* added ANTI_IP_SPOOF code (Dianora)
+* added temporary kline code (Dianora)
+* added Jolo's block allocator into list.c (Jolo/Dianora)
+* added ANTI_NICK_FLOOD code (Dianora) 
+* removed UNIXPORT code (Dianora)
+* removed lame unused code in match.c (Jolo)
+* corrected many type errors, ran through purify (Jolo)
+* removed unused variables (Dianora)
+* folded in Shadowfax's FLUD code (Shadowfax/Dianora)
+* folded in Shadowfax's LOCKFILE code (Shadowfax/Dianora/Rodder/ThemBones)
+* added optional GLINE code (Dianora)
+* stats K now works for normal users (Dianora)
+* quote UNKLINE code as a option added (Dianora/common)
+* Reworked ACKPATCH, removed ONE kludge and use confeq field of Y instead,
+  used hash table of ip's (Dianora)
+* removed UNIXPORT support (Dianora)
+* added support for oper WALLOPS back in.  Use /quote OPERWALL to send operwalls.
+* reworked version.c.SH to track build number (Rodder)
+* make clean removes more junk (Rodder)
+* added check for adequate FD_SETSIZE
+* created tools subdir (Rodder)
+* added "Unauthorized connection" back in, under umode +c (Dianora/Rodder)
+* modified autoconf for Solaris 2.6 (Rodder)
+* modified LUSERS to ignore masks (Rodder)
+* fixed mysterious exit() in fdlist.c from fd leak in s_auth.c (Dianorans)
+* fixed overrun bugs in res.c (Dianora/Johan/Shadowfax)
+
+-----------------------------------------------------------------------------
++hybrid-4.3 (8/19/97) - more bug fixes
+* typo that caused immediate crash on HPUX fixed
+* IP/hostname mismatch code fixed, warns the user instead of opers
+* typo fixed in missing klinefile error message
+
++hybrid-4.2 (8/4/97) - minor bug fix, one new feature
+* rcs version string was clobbering server version string
+* Bans can now match by IP for hosts that resolv (db)
+  IP_BAN_ALL define added to config.h
+
++hybrid-4 (7/28/97)
+* LOTS OF NEW STUFF IN CONFIG.H, GO READ IT!!  THE WHOLE THING!!
+  EVEN IF YOU ARE UPGRADING TO THIS FROM A HYBRID-4 BETA!!
+* patch for yet another NICK bug
+* patch for m_server() bug
+* patch for TS3 bug
+* switched to GNU autoconf (thanks ns!)
+* WALLOPS now only go to real opers
+* ircsprintf() mod to check for NULL args (db)
+* fixed possible overflow res.c (db)
+# seperate klines file  - see config.h (db)
+* quote_dline support - see config.h (db)
+* fixed timezone snafu in date()
+* added RFC1035_ANAL define reject _ and / in hostnames
+* removed old DELAY_NICK define..  broken and unneeded
+* modified s_err.c and created s_err.h.  See CUSTOM_ERR in include/config.h
+* m_server() now quietly ignores clients that try to use SERVER
+* removed NOTE code, removed SUMMON code, gutted USERS
+* added automatic timestamps to comments of quote KLINE and DLINE
+* changed failure notice for clients bouncing off ACKPATCH
+  to "Only one connection at a time allowed from your hostname"
+* cleanup to channel.c
+* format() mod to check for NULL args (di)
+* removed more useless crap (UPHOST, VMS, etc) from config.h
+* #undef F_LINES fixed
+* oper WALLOPS changed to OPERWALL (code taken from csr30)
+* BSDI 3.0 FD_ALLOC support (db)
+# changed output of STATS D
+* Removed Q: line support
+* cleaned up find_conf_ip (db)
+* fixed crypt() usage to work on systems with longer salts
+  (ie FreeBSD without DES libs)
+* various spelling fixes (db)
+* N: line checking in m_server() (db)
+* WARN_NO_NLINE define added to config.h
+
+-----------------------------------------------------------------------------
++hybrid-3 (6/17/97):
+* Added USRIP command.  This command returns user@ip in a USERHOST
+  reply for local clients only.  Requests for remote nicks are treated
+  as though the nick doesn't exist.
+* Switched to Dianora's faster dich_conf.c
+* Check for invalid characters (see RFC1035) in hostnames
+* Fixed problem with coring due to invalid hostnames on non-local users
+* TSora version 3
+* Turned D_LINE support back on
+* Numerous changes to Config script, may actually work now
+* Additional OS defs in Makefile
+* Minor cleanup in include/config.h
+# created new INSTALL document
+
+-----------------------------------------------------------------------------
++hybrid-2 (4/12/97):
+This is ircd2.8/th.v5a.3 with the WHO, WHOWAS, and IsMember() code
+from Comstud's irc2.8.21CSr29.  Please send email to Jon Lusky
+<irc@vol.com> if you find problems with it.
+
+Relevant excerpt from irc2.8.21+CSr29/README.CS:
+3) /whowas code totally rewritten.  This code is better than Avalon's
+   ever was.  You can now raise NICKNAMEHISTORYLENGTH in config.h to
+   a value that makes sense with today's number of clients.  Without
+   rewriting, you could expect server lockup, mostly during splits.
+   These changes should lower CPU usage quite a bit, especially during
+   splits.
+4) /who code tweaked.  IsMember() = CPU hog...IsMember() has been changed
+   and m_who does less find_user_link()s now...much less.  This is
+   a big reducer of CPU usage.  Also another "bug" was removed from
+   m_who that can possibly cause server lockups.  The possibility still
+   remains, though...not sure how to improve it further without totally
+   breaking /who.
+5) This goes with #4, but...
+   /who nickname was optimized tremendously.  However, this breaks /who
+   if you do /who username and username happens to match a nickname.
+   Oh well...too bad.
+
+-----------------------------------------------------------------------------
++hybrid-1 never really existed
+
diff --git a/doc/opers.txt b/doc/opers.txt
new file mode 100755 (executable)
index 0000000..547a733
--- /dev/null
@@ -0,0 +1,200 @@
+The following outlines the features and commands
+that Bahamut has for operators of the server.
+- 11/13/02 - srd (srd@dal.net)
+- 10/02/04 - Quension (quension@dal.net)
+
+Based upon opers.txt for ircd-hybrid by:
+ThemBones (kraemer@u.washington.edu)
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+Usermodes: (* designates that the umode is oper only)
+
+ USERMODE      DESCRIPTION
+-----------------------------------------------------------------
+  +i        -  Is 'invisible'.
+  +w        -  Can see wallops messages.
+  +r        -  Is registered/identified to services.
+  +s        -  Can see oper kill messages.
+  +R        -  Will not receive messages from -r clients.
+ *+a        -  Designates the user as being a services administrator.
+ *+b        -  Can see chatops messages.
+ *+c        -  Can see client connections and exits.
+ *+d        -  Can see debug messages.
+ *+e        -  Can see dccallow failure notices.
+ *+f        -  Can see flood messages.
+ *+g        -  Can recieve globops messages.
+ *+h        -  Is available for help, being listed in /stats P
+ *+j        -  Can see rejected connection notices 
+ *+k        -  Can see server kill messages.
+ *+m        -  Can see spambot notices.
+ *+n        -  Can see routing notices.
+ *+o        -  Is a global IRC Operator.
+ *+y        -  Can see certain information requests. (e.g. /stats)
+ *+A        -  Is a server administrator.
+ *+F        -  Can bypass the ircd's recvq throttling
+ *+K        -  Can see U:lined kill messages
+ *+O        -  Is a local IRC Operator.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+Oper Commands: (+ indicates that /quote must be used with ircII)
+
+COMMAND         - USAGE and DESCRIPTION
+-------------------------------------------------------------------
+  SQUIT         - SQUIT <server> [reason]
+                  Splits <server> away from your side of the net with
+                  [reason].
+
+  KILL          - KILL <nick> <reason>
+                  Disconnects user <nick> from the IRC server he/she
+                  is connected to with reason <reason>.
+                  Local Opers can only kill clients on their servers.
+                  Global Opers can kill clients on any server on the
+                  IRC network.
+
+  STATS         - STATS <letter> [server|nick]
+                  Queries server [server] (or your own server if no
+                  server parameter is given) for info corresponing to
+                  <letter>.
+                  LETTER (* = Oper only.)
+                  ------ (^ = Can be configured to be oper only.)
+                  * a - Shows akills
+                  * c - Shows connect blocks
+                  * D - Shows special clone limits
+                  * d - Shows clone limit statistics
+                  * G - Shows local gcos restrictions
+                  * g - Shows network gcos restrictions (sglines)
+                    i - Shows allow blocks
+                  * K - Shows permanent K lines
+                  * k - Shows temporary K lines
+                    l - Shows hostname and generic info about [nick]
+                  * m - Shows commands and their usage
+                  ^ o - Shows oper bocks
+                    p - Shows opers connected and their idle times
+                  * Q - Shows local nick/chan restrictions
+                  * q - Shows network nick/chan restrictions (sqlines)
+                    r - Shows resource usage by ircd (only in DEBUGMODE)
+                  * s - Shows server name cache
+                  * T - Shows throttle stats
+                  * t - Shows generic server stats
+                  * U - Shows super (U-Lined) servers.
+                    u - Shows server uptime
+                    v - Shows connected servers and their idle times
+                  * w - Shows fd list (only in DEBUGMODE)
+                    y - Shows class blocks
+                  * z - Shows memory stats
+                    ? - Shows network traffic statistics bdefgjnx
+
+  CONNECT       - CONNECT <server_A> [port [server_B]]
+                  When [server_B] is used, CONNECT asks [server_B] to
+                  connect to <server_A>.  Global Opers only.
+
+                  When [server_B] is not used, CONNECT trys to connect
+                  your server to <server_A>.  Local and Global Opers.
+
+                  When [port] is used, the connection will be attempted
+                  to [port].  When [port] is not used, a default will be
+                  chosen from the conf.
+
++ WALLOPS       - WALLOPS :<message>
+                  Sends a WALLOPS message of <message> to all users
+                  who are umode +w (this includes non-opers).
+
++ LOCOPS        - LOCOPS :<message>
+                  Sends an LOCOPS message of <message> to all
+                  opers on local server who are umode +g
+
++ GLOBOPS       - GLOBOPS :<message>
+                  Sends an GLOBOPS message of <message> to all
+                  opers on the network who are umode +g
+
++ CHATOPS       - CHATOPS :<message>
+                  Sends an CHATOPS message of <message> to all
+                  opers on the network who are umode +b
+
+  TRACE         - TRACE [server|nick]
+                  TRACE shows client info about [server|nick] or about
+                  the server you are on if no [server|nick] is given.
+
+                  All users that use TRACE can see the path to
+                  [server|nick] if given and all servers and opers
+                  connected, as well as the connection class they
+                  fall into.
+
+                  Opers can see unregistered connections on a server
+                  with TRACE as well as connection class counts.
+
+  REHASH        - REHASH [option]
+                  When no [option] is given, ircd will re-read the
+                  ircd.conf file.
+
+                  [option] can be one of the following:
+                    DNS       - Re-read the /etc/resolv.conf file
+                    TKLINES   - Clears temporary klines
+                    GC        - Performs garbage collecting of excess
+                                memory.
+                    MOTD      - Re-reads MOTD files
+                    THROTTLES - Clears connection throttles
+                    AKILLS    - Clears akills
+                    SQLINES   - Clears sqlines
+                    SGLINES   - Clears sglines
+                    TSQGLINES - Clears temporary nick/channel/gcos blocks
+
+  RESTART       - RESTART
+                  Restarts the IRC server.
+
+  CLOSE         - CLOSE
+                  Close any connections from clients who have not fully
+                  registered yet.
+
+  DIE           - DIE
+                  Terminates the IRC server.
+
++ DNS           - DNS
+                  Shows some stats about ircd's asynchronous resolving
+                  code
+
++ KLINE         - KLINE [minutes] <nick|user@host> :[reason]
+                  Adds a KLINE to the ircd.conf or klines.conf file which
+                  will ban the specified user from using that server.
+                  The banned client will receive a message saying he/she
+                  is banned with reason [reason]
+
+                  For a temporary KLINE, length of kline is given in
+                  minutes as the first parameter i.e.
+                  KLINE 10 <nick|user@host> :cool off for 10 minutes
+
++ UNKLINE       - UNKLINE <user@host>
+                  -- If compiled with UNKLINE --
+                  Will attempt to unkline the given <user@host>
+
++ SET           - SET <option> <value>
+                  <option> can be one of the following:
+                    MAX       - Sets the number of max connections
+                                to <value>.
+
+                    -- The following three are if compiled with FLUD --
+                     FLUDNUM   - Sets the number of flud messages to 
+                                 trip flud alarm to <value>
+                     FLUDTIME  - Sets the number of seconds in which 
+                                  FLUDNUM msgs must occur to <value>
+                     FLUDBLOCK - Sets the number of seconds to block 
+                                 fluds to <value>
+
+                    -- The following are if compiled with THROTTLE_ENABLE --
+                     THROTTLE ENABLE     - Enables/disables connection
+                                           throttling
+                     THROTTLE COUNT      - Sets connection count to <value>
+                     THROTTLE TIME       - Sets time window to <value> seconds
+                     THROTTLE RECORDTIME - Sets host record time to <value>
+                                           seconds
+                     THROTTLE HASH       - Resizes throttle hashtable to
+                                           <value> buckets
+                     LCLONES <ip>        - Displays or sets soft local clone
+                                           limit for <ip> (1.2.3.4/1.2.3.*)
+                                           to <value> (0 to reset to default)
+                     GCLONES <ip>        - Displays or sets soft global clone
+                                           limit for <ip> to <value>
+                     DEFLCLONES          - Same as local_clones in conf
+                     DEFGCLONES          - Same as global_clones in conf
diff --git a/doc/pcrepattern.html b/doc/pcrepattern.html
new file mode 100644 (file)
index 0000000..65abcc2
--- /dev/null
@@ -0,0 +1,1607 @@
+<html>
+<head>
+<title>pcrepattern specification</title>
+</head>
+<body bgcolor="#FFFFFF" text="#00005A" link="#0066FF" alink="#3399FF" vlink="#2222BB">
+This HTML document has been generated automatically from the original man page.
+If there is any nonsense in it, please consult the man page, in case the
+conversion went wrong.<br>
+<ul>
+<li><a name="TOC1" href="#SEC1">PCRE REGULAR EXPRESSION DETAILS</a>
+<li><a name="TOC2" href="#SEC2">BACKSLASH</a>
+<li><a name="TOC3" href="#SEC3">CIRCUMFLEX AND DOLLAR</a>
+<li><a name="TOC4" href="#SEC4">FULL STOP (PERIOD, DOT)</a>
+<li><a name="TOC5" href="#SEC5">MATCHING A SINGLE BYTE</a>
+<li><a name="TOC6" href="#SEC6">SQUARE BRACKETS</a>
+<li><a name="TOC7" href="#SEC7">POSIX CHARACTER CLASSES</a>
+<li><a name="TOC8" href="#SEC8">VERTICAL BAR</a>
+<li><a name="TOC9" href="#SEC9">INTERNAL OPTION SETTING</a>
+<li><a name="TOC10" href="#SEC10">SUBPATTERNS</a>
+<li><a name="TOC11" href="#SEC11">NAMED SUBPATTERNS</a>
+<li><a name="TOC12" href="#SEC12">REPETITION</a>
+<li><a name="TOC13" href="#SEC13">ATOMIC GROUPING AND POSSESSIVE QUANTIFIERS</a>
+<li><a name="TOC14" href="#SEC14">BACK REFERENCES</a>
+<li><a name="TOC15" href="#SEC15">ASSERTIONS</a>
+<li><a name="TOC16" href="#SEC16">CONDITIONAL SUBPATTERNS</a>
+<li><a name="TOC17" href="#SEC17">COMMENTS</a>
+<li><a name="TOC18" href="#SEC18">RECURSIVE PATTERNS</a>
+<li><a name="TOC19" href="#SEC19">SUBPATTERNS AS SUBROUTINES</a>
+<li><a name="TOC20" href="#SEC20">CALLOUTS</a>
+</ul>
+<br><a name="SEC1" href="#TOC1">PCRE REGULAR EXPRESSION DETAILS</a><br>
+<P>
+The syntax and semantics of the regular expressions supported by PCRE are
+described below. Regular expressions are also described in the Perl
+documentation and in a number of other books, some of which have copious
+examples. Jeffrey Friedl's "Mastering Regular Expressions", published by
+O'Reilly, covers them in great detail. The description here is intended as
+reference documentation.
+</P>
+<P>
+The basic operation of PCRE is on strings of bytes. However, there is also
+support for UTF-8 character strings. To use this support you must build PCRE to
+include UTF-8 support, and then call <b>pcre_compile()</b> with the PCRE_UTF8
+option. How this affects the pattern matching is mentioned in several places
+below. There is also a summary of UTF-8 features in the
+<a href="pcre.html#utf8support">section on UTF-8 support</a>
+in the main
+<a href="pcre.html"><b>pcre</b></a>
+page.
+</P>
+<P>
+A regular expression is a pattern that is matched against a subject string from
+left to right. Most characters stand for themselves in a pattern, and match the
+corresponding characters in the subject. As a trivial example, the pattern
+</P>
+<P>
+<pre>
+  The quick brown fox
+</PRE>
+</P>
+<P>
+matches a portion of a subject string that is identical to itself. The power of
+regular expressions comes from the ability to include alternatives and
+repetitions in the pattern. These are encoded in the pattern by the use of
+<i>meta-characters</i>, which do not stand for themselves but instead are
+interpreted in some special way.
+</P>
+<P>
+There are two different sets of meta-characters: those that are recognized
+anywhere in the pattern except within square brackets, and those that are
+recognized in square brackets. Outside square brackets, the meta-characters are
+as follows:
+</P>
+<P>
+<pre>
+  \      general escape character with several uses
+  ^      assert start of string (or line, in multiline mode)
+  $      assert end of string (or line, in multiline mode)
+  .      match any character except newline (by default)
+  [      start character class definition
+  |      start of alternative branch
+  (      start subpattern
+  )      end subpattern
+  ?      extends the meaning of (
+         also 0 or 1 quantifier
+         also quantifier minimizer
+  *      0 or more quantifier
+  +      1 or more quantifier
+         also "possessive quantifier"
+  {      start min/max quantifier
+</PRE>
+</P>
+<P>
+Part of a pattern that is in square brackets is called a "character class". In
+a character class the only meta-characters are:
+</P>
+<P>
+<pre>
+  \      general escape character
+  ^      negate the class, but only if the first character
+  -      indicates character range
+  [      POSIX character class (only if followed by POSIX
+           syntax)
+  ]      terminates the character class
+</PRE>
+</P>
+<P>
+The following sections describe the use of each of the meta-characters.
+</P>
+<br><a name="SEC2" href="#TOC1">BACKSLASH</a><br>
+<P>
+The backslash character has several uses. Firstly, if it is followed by a
+non-alphameric character, it takes away any special meaning that character may
+have. This use of backslash as an escape character applies both inside and
+outside character classes.
+</P>
+<P>
+For example, if you want to match a * character, you write \* in the pattern.
+This escaping action applies whether or not the following character would
+otherwise be interpreted as a meta-character, so it is always safe to precede a
+non-alphameric with backslash to specify that it stands for itself. In
+particular, if you want to match a backslash, you write \\.
+</P>
+<P>
+If a pattern is compiled with the PCRE_EXTENDED option, whitespace in the
+pattern (other than in a character class) and characters between a # outside
+a character class and the next newline character are ignored. An escaping
+backslash can be used to include a whitespace or # character as part of the
+pattern.
+</P>
+<P>
+If you want to remove the special meaning from a sequence of characters, you
+can do so by putting them between \Q and \E. This is different from Perl in
+that $ and @ are handled as literals in \Q...\E sequences in PCRE, whereas in
+Perl, $ and @ cause variable interpolation. Note the following examples:
+</P>
+<P>
+<pre>
+  Pattern            PCRE matches   Perl matches
+</PRE>
+</P>
+<P>
+<pre>
+  \Qabc$xyz\E        abc$xyz        abc followed by the
+                                      contents of $xyz
+  \Qabc\$xyz\E       abc\$xyz       abc\$xyz
+  \Qabc\E\$\Qxyz\E   abc$xyz        abc$xyz
+</PRE>
+</P>
+<P>
+The \Q...\E sequence is recognized both inside and outside character classes.
+</P>
+<P>
+A second use of backslash provides a way of encoding non-printing characters
+in patterns in a visible manner. There is no restriction on the appearance of
+non-printing characters, apart from the binary zero that terminates a pattern,
+but when a pattern is being prepared by text editing, it is usually easier to
+use one of the following escape sequences than the binary character it
+represents:
+</P>
+<P>
+<pre>
+  \a        alarm, that is, the BEL character (hex 07)
+  \cx       "control-x", where x is any character
+  \e        escape (hex 1B)
+  \f        formfeed (hex 0C)
+  \n        newline (hex 0A)
+  \r        carriage return (hex 0D)
+  \t        tab (hex 09)
+  \ddd      character with octal code ddd, or backreference
+  \xhh      character with hex code hh
+  \x{hhh..} character with hex code hhh... (UTF-8 mode only)
+</PRE>
+</P>
+<P>
+The precise effect of \cx is as follows: if x is a lower case letter, it
+is converted to upper case. Then bit 6 of the character (hex 40) is inverted.
+Thus \cz becomes hex 1A, but \c{ becomes hex 3B, while \c; becomes hex
+7B.
+</P>
+<P>
+After \x, from zero to two hexadecimal digits are read (letters can be in
+upper or lower case). In UTF-8 mode, any number of hexadecimal digits may
+appear between \x{ and }, but the value of the character code must be less
+than 2**31 (that is, the maximum hexadecimal value is 7FFFFFFF). If characters
+other than hexadecimal digits appear between \x{ and }, or if there is no
+terminating }, this form of escape is not recognized. Instead, the initial
+\x will be interpreted as a basic hexadecimal escape, with no following
+digits, giving a byte whose value is zero.
+</P>
+<P>
+Characters whose value is less than 256 can be defined by either of the two
+syntaxes for \x when PCRE is in UTF-8 mode. There is no difference in the
+way they are handled. For example, \xdc is exactly the same as \x{dc}.
+</P>
+<P>
+After \0 up to two further octal digits are read. In both cases, if there
+are fewer than two digits, just those that are present are used. Thus the
+sequence \0\x\07 specifies two binary zeros followed by a BEL character
+(code value 7). Make sure you supply two digits after the initial zero if the
+character that follows is itself an octal digit.
+</P>
+<P>
+The handling of a backslash followed by a digit other than 0 is complicated.
+Outside a character class, PCRE reads it and any following digits as a decimal
+number. If the number is less than 10, or if there have been at least that many
+previous capturing left parentheses in the expression, the entire sequence is
+taken as a <i>back reference</i>. A description of how this works is given
+later, following the discussion of parenthesized subpatterns.
+</P>
+<P>
+Inside a character class, or if the decimal number is greater than 9 and there
+have not been that many capturing subpatterns, PCRE re-reads up to three octal
+digits following the backslash, and generates a single byte from the least
+significant 8 bits of the value. Any subsequent digits stand for themselves.
+For example:
+</P>
+<P>
+<pre>
+  \040   is another way of writing a space
+  \40    is the same, provided there are fewer than 40
+            previous capturing subpatterns
+  \7     is always a back reference
+  \11    might be a back reference, or another way of
+            writing a tab
+  \011   is always a tab
+  \0113  is a tab followed by the character "3"
+  \113   might be a back reference, otherwise the
+            character with octal code 113
+  \377   might be a back reference, otherwise
+            the byte consisting entirely of 1 bits
+  \81    is either a back reference, or a binary zero
+            followed by the two characters "8" and "1"
+</PRE>
+</P>
+<P>
+Note that octal values of 100 or greater must not be introduced by a leading
+zero, because no more than three octal digits are ever read.
+</P>
+<P>
+All the sequences that define a single byte value or a single UTF-8 character
+(in UTF-8 mode) can be used both inside and outside character classes. In
+addition, inside a character class, the sequence \b is interpreted as the
+backspace character (hex 08). Outside a character class it has a different
+meaning (see below).
+</P>
+<P>
+The third use of backslash is for specifying generic character types:
+</P>
+<P>
+<pre>
+  \d     any decimal digit
+  \D     any character that is not a decimal digit
+  \s     any whitespace character
+  \S     any character that is not a whitespace character
+  \w     any "word" character
+  \W     any "non-word" character
+</PRE>
+</P>
+<P>
+Each pair of escape sequences partitions the complete set of characters into
+two disjoint sets. Any given character matches one, and only one, of each pair.
+</P>
+<P>
+In UTF-8 mode, characters with values greater than 255 never match \d, \s, or
+\w, and always match \D, \S, and \W.
+</P>
+<P>
+For compatibility with Perl, \s does not match the VT character (code 11).
+This makes it different from the the POSIX "space" class. The \s characters
+are HT (9), LF (10), FF (12), CR (13), and space (32).
+</P>
+<P>
+A "word" character is any letter or digit or the underscore character, that is,
+any character which can be part of a Perl "word". The definition of letters and
+digits is controlled by PCRE's character tables, and may vary if locale-
+specific matching is taking place (see
+<a href="pcreapi.html#localesupport">"Locale support"</a>
+in the
+<a href="pcreapi.html"><b>pcreapi</b></a>
+page). For example, in the "fr" (French) locale, some character codes greater
+than 128 are used for accented letters, and these are matched by \w.
+</P>
+<P>
+These character type sequences can appear both inside and outside character
+classes. They each match one character of the appropriate type. If the current
+matching point is at the end of the subject string, all of them fail, since
+there is no character to match.
+</P>
+<P>
+The fourth use of backslash is for certain simple assertions. An assertion
+specifies a condition that has to be met at a particular point in a match,
+without consuming any characters from the subject string. The use of
+subpatterns for more complicated assertions is described below. The backslashed
+assertions are
+</P>
+<P>
+<pre>
+  \b     matches at a word boundary
+  \B     matches when not at a word boundary
+  \A     matches at start of subject
+  \Z     matches at end of subject or before newline at end
+  \z     matches at end of subject
+  \G     matches at first matching position in subject
+</PRE>
+</P>
+<P>
+These assertions may not appear in character classes (but note that \b has a
+different meaning, namely the backspace character, inside a character class).
+</P>
+<P>
+A word boundary is a position in the subject string where the current character
+and the previous character do not both match \w or \W (i.e. one matches
+\w and the other matches \W), or the start or end of the string if the
+first or last character matches \w, respectively.
+</P>
+<P>
+The \A, \Z, and \z assertions differ from the traditional circumflex and
+dollar (described below) in that they only ever match at the very start and end
+of the subject string, whatever options are set. Thus, they are independent of
+multiline mode.
+</P>
+<P>
+They are not affected by the PCRE_NOTBOL or PCRE_NOTEOL options. If the
+<i>startoffset</i> argument of <b>pcre_exec()</b> is non-zero, indicating that
+matching is to start at a point other than the beginning of the subject, \A
+can never match. The difference between \Z and \z is that \Z matches before
+a newline that is the last character of the string as well as at the end of the
+string, whereas \z matches only at the end.
+</P>
+<P>
+The \G assertion is true only when the current matching position is at the
+start point of the match, as specified by the <i>startoffset</i> argument of
+<b>pcre_exec()</b>. It differs from \A when the value of <i>startoffset</i> is
+non-zero. By calling <b>pcre_exec()</b> multiple times with appropriate
+arguments, you can mimic Perl's /g option, and it is in this kind of
+implementation where \G can be useful.
+</P>
+<P>
+Note, however, that PCRE's interpretation of \G, as the start of the current
+match, is subtly different from Perl's, which defines it as the end of the
+previous match. In Perl, these can be different when the previously matched
+string was empty. Because PCRE does just one match at a time, it cannot
+reproduce this behaviour.
+</P>
+<P>
+If all the alternatives of a pattern begin with \G, the expression is anchored
+to the starting match position, and the "anchored" flag is set in the compiled
+regular expression.
+</P>
+<br><a name="SEC3" href="#TOC1">CIRCUMFLEX AND DOLLAR</a><br>
+<P>
+Outside a character class, in the default matching mode, the circumflex
+character is an assertion which is true only if the current matching point is
+at the start of the subject string. If the <i>startoffset</i> argument of
+<b>pcre_exec()</b> is non-zero, circumflex can never match if the PCRE_MULTILINE
+option is unset. Inside a character class, circumflex has an entirely different
+meaning (see below).
+</P>
+<P>
+Circumflex need not be the first character of the pattern if a number of
+alternatives are involved, but it should be the first thing in each alternative
+in which it appears if the pattern is ever to match that branch. If all
+possible alternatives start with a circumflex, that is, if the pattern is
+constrained to match only at the start of the subject, it is said to be an
+"anchored" pattern. (There are also other constructs that can cause a pattern
+to be anchored.)
+</P>
+<P>
+A dollar character is an assertion which is true only if the current matching
+point is at the end of the subject string, or immediately before a newline
+character that is the last character in the string (by default). Dollar need
+not be the last character of the pattern if a number of alternatives are
+involved, but it should be the last item in any branch in which it appears.
+Dollar has no special meaning in a character class.
+</P>
+<P>
+The meaning of dollar can be changed so that it matches only at the very end of
+the string, by setting the PCRE_DOLLAR_ENDONLY option at compile time. This
+does not affect the \Z assertion.
+</P>
+<P>
+The meanings of the circumflex and dollar characters are changed if the
+PCRE_MULTILINE option is set. When this is the case, they match immediately
+after and immediately before an internal newline character, respectively, in
+addition to matching at the start and end of the subject string. For example,
+the pattern /^abc$/ matches the subject string "def\nabc" in multiline mode,
+but not otherwise. Consequently, patterns that are anchored in single line mode
+because all branches start with ^ are not anchored in multiline mode, and a
+match for circumflex is possible when the <i>startoffset</i> argument of
+<b>pcre_exec()</b> is non-zero. The PCRE_DOLLAR_ENDONLY option is ignored if
+PCRE_MULTILINE is set.
+</P>
+<P>
+Note that the sequences \A, \Z, and \z can be used to match the start and
+end of the subject in both modes, and if all branches of a pattern start with
+\A it is always anchored, whether PCRE_MULTILINE is set or not.
+</P>
+<br><a name="SEC4" href="#TOC1">FULL STOP (PERIOD, DOT)</a><br>
+<P>
+Outside a character class, a dot in the pattern matches any one character in
+the subject, including a non-printing character, but not (by default) newline.
+In UTF-8 mode, a dot matches any UTF-8 character, which might be more than one
+byte long, except (by default) for newline. If the PCRE_DOTALL option is set,
+dots match newlines as well. The handling of dot is entirely independent of the
+handling of circumflex and dollar, the only relationship being that they both
+involve newline characters. Dot has no special meaning in a character class.
+</P>
+<br><a name="SEC5" href="#TOC1">MATCHING A SINGLE BYTE</a><br>
+<P>
+Outside a character class, the escape sequence \C matches any one byte, both
+in and out of UTF-8 mode. Unlike a dot, it always matches a newline. The
+feature is provided in Perl in order to match individual bytes in UTF-8 mode.
+Because it breaks up UTF-8 characters into individual bytes, what remains in
+the string may be a malformed UTF-8 string. For this reason it is best avoided.
+</P>
+<P>
+PCRE does not allow \C to appear in lookbehind assertions (see below), because
+in UTF-8 mode it makes it impossible to calculate the length of the lookbehind.
+</P>
+<br><a name="SEC6" href="#TOC1">SQUARE BRACKETS</a><br>
+<P>
+An opening square bracket introduces a character class, terminated by a closing
+square bracket. A closing square bracket on its own is not special. If a
+closing square bracket is required as a member of the class, it should be the
+first data character in the class (after an initial circumflex, if present) or
+escaped with a backslash.
+</P>
+<P>
+A character class matches a single character in the subject. In UTF-8 mode, the
+character may occupy more than one byte. A matched character must be in the set
+of characters defined by the class, unless the first character in the class
+definition is a circumflex, in which case the subject character must not be in
+the set defined by the class. If a circumflex is actually required as a member
+of the class, ensure it is not the first character, or escape it with a
+backslash.
+</P>
+<P>
+For example, the character class [aeiou] matches any lower case vowel, while
+[^aeiou] matches any character that is not a lower case vowel. Note that a
+circumflex is just a convenient notation for specifying the characters which
+are in the class by enumerating those that are not. It is not an assertion: it
+still consumes a character from the subject string, and fails if the current
+pointer is at the end of the string.
+</P>
+<P>
+In UTF-8 mode, characters with values greater than 255 can be included in a
+class as a literal string of bytes, or by using the \x{ escaping mechanism.
+</P>
+<P>
+When caseless matching is set, any letters in a class represent both their
+upper case and lower case versions, so for example, a caseless [aeiou] matches
+"A" as well as "a", and a caseless [^aeiou] does not match "A", whereas a
+caseful version would. PCRE does not support the concept of case for characters
+with values greater than 255.
+</P>
+<P>
+The newline character is never treated in any special way in character classes,
+whatever the setting of the PCRE_DOTALL or PCRE_MULTILINE options is. A class
+such as [^a] will always match a newline.
+</P>
+<P>
+The minus (hyphen) character can be used to specify a range of characters in a
+character class. For example, [d-m] matches any letter between d and m,
+inclusive. If a minus character is required in a class, it must be escaped with
+a backslash or appear in a position where it cannot be interpreted as
+indicating a range, typically as the first or last character in the class.
+</P>
+<P>
+It is not possible to have the literal character "]" as the end character of a
+range. A pattern such as [W-]46] is interpreted as a class of two characters
+("W" and "-") followed by a literal string "46]", so it would match "W46]" or
+"-46]". However, if the "]" is escaped with a backslash it is interpreted as
+the end of range, so [W-\]46] is interpreted as a single class containing a
+range followed by two separate characters. The octal or hexadecimal
+representation of "]" can also be used to end a range.
+</P>
+<P>
+Ranges operate in the collating sequence of character values. They can also be
+used for characters specified numerically, for example [\000-\037]. In UTF-8
+mode, ranges can include characters whose values are greater than 255, for
+example [\x{100}-\x{2ff}].
+</P>
+<P>
+If a range that includes letters is used when caseless matching is set, it
+matches the letters in either case. For example, [W-c] is equivalent to
+[][\^_`wxyzabc], matched caselessly, and if character tables for the "fr"
+locale are in use, [\xc8-\xcb] matches accented E characters in both cases.
+</P>
+<P>
+The character types \d, \D, \s, \S, \w, and \W may also appear in a
+character class, and add the characters that they match to the class. For
+example, [\dABCDEF] matches any hexadecimal digit. A circumflex can
+conveniently be used with the upper case character types to specify a more
+restricted set of characters than the matching lower case type. For example,
+the class [^\W_] matches any letter or digit, but not underscore.
+</P>
+<P>
+All non-alphameric characters other than \, -, ^ (at the start) and the
+terminating ] are non-special in character classes, but it does no harm if they
+are escaped.
+</P>
+<br><a name="SEC7" href="#TOC1">POSIX CHARACTER CLASSES</a><br>
+<P>
+Perl supports the POSIX notation for character classes, which uses names
+enclosed by [: and :] within the enclosing square brackets. PCRE also supports
+this notation. For example,
+</P>
+<P>
+<pre>
+  [01[:alpha:]%]
+</PRE>
+</P>
+<P>
+matches "0", "1", any alphabetic character, or "%". The supported class names
+are
+</P>
+<P>
+<pre>
+  alnum    letters and digits
+  alpha    letters
+  ascii    character codes 0 - 127
+  blank    space or tab only
+  cntrl    control characters
+  digit    decimal digits (same as \d)
+  graph    printing characters, excluding space
+  lower    lower case letters
+  print    printing characters, including space
+  punct    printing characters, excluding letters and digits
+  space    white space (not quite the same as \s)
+  upper    upper case letters
+  word     "word" characters (same as \w)
+  xdigit   hexadecimal digits
+</PRE>
+</P>
+<P>
+The "space" characters are HT (9), LF (10), VT (11), FF (12), CR (13), and
+space (32). Notice that this list includes the VT character (code 11). This
+makes "space" different to \s, which does not include VT (for Perl
+compatibility).
+</P>
+<P>
+The name "word" is a Perl extension, and "blank" is a GNU extension from Perl
+5.8. Another Perl extension is negation, which is indicated by a ^ character
+after the colon. For example,
+</P>
+<P>
+<pre>
+  [12[:^digit:]]
+</PRE>
+</P>
+<P>
+matches "1", "2", or any non-digit. PCRE (and Perl) also recognize the POSIX
+syntax [.ch.] and [=ch=] where "ch" is a "collating element", but these are not
+supported, and an error is given if they are encountered.
+</P>
+<P>
+In UTF-8 mode, characters with values greater than 255 do not match any of
+the POSIX character classes.
+</P>
+<br><a name="SEC8" href="#TOC1">VERTICAL BAR</a><br>
+<P>
+Vertical bar characters are used to separate alternative patterns. For example,
+the pattern
+</P>
+<P>
+<pre>
+  gilbert|sullivan
+</PRE>
+</P>
+<P>
+matches either "gilbert" or "sullivan". Any number of alternatives may appear,
+and an empty alternative is permitted (matching the empty string).
+The matching process tries each alternative in turn, from left to right,
+and the first one that succeeds is used. If the alternatives are within a
+subpattern (defined below), "succeeds" means matching the rest of the main
+pattern as well as the alternative in the subpattern.
+</P>
+<br><a name="SEC9" href="#TOC1">INTERNAL OPTION SETTING</a><br>
+<P>
+The settings of the PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL, and
+PCRE_EXTENDED options can be changed from within the pattern by a sequence of
+Perl option letters enclosed between "(?" and ")". The option letters are
+</P>
+<P>
+<pre>
+  i  for PCRE_CASELESS
+  m  for PCRE_MULTILINE
+  s  for PCRE_DOTALL
+  x  for PCRE_EXTENDED
+</PRE>
+</P>
+<P>
+For example, (?im) sets caseless, multiline matching. It is also possible to
+unset these options by preceding the letter with a hyphen, and a combined
+setting and unsetting such as (?im-sx), which sets PCRE_CASELESS and
+PCRE_MULTILINE while unsetting PCRE_DOTALL and PCRE_EXTENDED, is also
+permitted. If a letter appears both before and after the hyphen, the option is
+unset.
+</P>
+<P>
+When an option change occurs at top level (that is, not inside subpattern
+parentheses), the change applies to the remainder of the pattern that follows.
+If the change is placed right at the start of a pattern, PCRE extracts it into
+the global options (and it will therefore show up in data extracted by the
+<b>pcre_fullinfo()</b> function).
+</P>
+<P>
+An option change within a subpattern affects only that part of the current
+pattern that follows it, so
+</P>
+<P>
+<pre>
+  (a(?i)b)c
+</PRE>
+</P>
+<P>
+matches abc and aBc and no other strings (assuming PCRE_CASELESS is not used).
+By this means, options can be made to have different settings in different
+parts of the pattern. Any changes made in one alternative do carry on
+into subsequent branches within the same subpattern. For example,
+</P>
+<P>
+<pre>
+  (a(?i)b|c)
+</PRE>
+</P>
+<P>
+matches "ab", "aB", "c", and "C", even though when matching "C" the first
+branch is abandoned before the option setting. This is because the effects of
+option settings happen at compile time. There would be some very weird
+behaviour otherwise.
+</P>
+<P>
+The PCRE-specific options PCRE_UNGREEDY and PCRE_EXTRA can be changed in the
+same way as the Perl-compatible options by using the characters U and X
+respectively. The (?X) flag setting is special in that it must always occur
+earlier in the pattern than any of the additional features it turns on, even
+when it is at top level. It is best put at the start.
+</P>
+<br><a name="SEC10" href="#TOC1">SUBPATTERNS</a><br>
+<P>
+Subpatterns are delimited by parentheses (round brackets), which can be nested.
+Marking part of a pattern as a subpattern does two things:
+</P>
+<P>
+1. It localizes a set of alternatives. For example, the pattern
+</P>
+<P>
+<pre>
+  cat(aract|erpillar|)
+</PRE>
+</P>
+<P>
+matches one of the words "cat", "cataract", or "caterpillar". Without the
+parentheses, it would match "cataract", "erpillar" or the empty string.
+</P>
+<P>
+2. It sets up the subpattern as a capturing subpattern (as defined above).
+When the whole pattern matches, that portion of the subject string that matched
+the subpattern is passed back to the caller via the <i>ovector</i> argument of
+<b>pcre_exec()</b>. Opening parentheses are counted from left to right (starting
+from 1) to obtain the numbers of the capturing subpatterns.
+</P>
+<P>
+For example, if the string "the red king" is matched against the pattern
+</P>
+<P>
+<pre>
+  the ((red|white) (king|queen))
+</PRE>
+</P>
+<P>
+the captured substrings are "red king", "red", and "king", and are numbered 1,
+2, and 3, respectively.
+</P>
+<P>
+The fact that plain parentheses fulfil two functions is not always helpful.
+There are often times when a grouping subpattern is required without a
+capturing requirement. If an opening parenthesis is followed by a question mark
+and a colon, the subpattern does not do any capturing, and is not counted when
+computing the number of any subsequent capturing subpatterns. For example, if
+the string "the white queen" is matched against the pattern
+</P>
+<P>
+<pre>
+  the ((?:red|white) (king|queen))
+</PRE>
+</P>
+<P>
+the captured substrings are "white queen" and "queen", and are numbered 1 and
+2. The maximum number of capturing subpatterns is 65535, and the maximum depth
+of nesting of all subpatterns, both capturing and non-capturing, is 200.
+</P>
+<P>
+As a convenient shorthand, if any option settings are required at the start of
+a non-capturing subpattern, the option letters may appear between the "?" and
+the ":". Thus the two patterns
+</P>
+<P>
+<pre>
+  (?i:saturday|sunday)
+  (?:(?i)saturday|sunday)
+</PRE>
+</P>
+<P>
+match exactly the same set of strings. Because alternative branches are tried
+from left to right, and options are not reset until the end of the subpattern
+is reached, an option setting in one branch does affect subsequent branches, so
+the above patterns match "SUNDAY" as well as "Saturday".
+</P>
+<br><a name="SEC11" href="#TOC1">NAMED SUBPATTERNS</a><br>
+<P>
+Identifying capturing parentheses by number is simple, but it can be very hard
+to keep track of the numbers in complicated regular expressions. Furthermore,
+if an expression is modified, the numbers may change. To help with the
+difficulty, PCRE supports the naming of subpatterns, something that Perl does
+not provide. The Python syntax (?P&#60;name&#62;...) is used. Names consist of
+alphanumeric characters and underscores, and must be unique within a pattern.
+</P>
+<P>
+Named capturing parentheses are still allocated numbers as well as names. The
+PCRE API provides function calls for extracting the name-to-number translation
+table from a compiled pattern. For further details see the
+<a href="pcreapi.html"><b>pcreapi</b></a>
+documentation.
+</P>
+<br><a name="SEC12" href="#TOC1">REPETITION</a><br>
+<P>
+Repetition is specified by quantifiers, which can follow any of the following
+items:
+</P>
+<P>
+<pre>
+  a literal data character
+  the . metacharacter
+  the \C escape sequence
+  escapes such as \d that match single characters
+  a character class
+  a back reference (see next section)
+  a parenthesized subpattern (unless it is an assertion)
+</PRE>
+</P>
+<P>
+The general repetition quantifier specifies a minimum and maximum number of
+permitted matches, by giving the two numbers in curly brackets (braces),
+separated by a comma. The numbers must be less than 65536, and the first must
+be less than or equal to the second. For example:
+</P>
+<P>
+<pre>
+  z{2,4}
+</PRE>
+</P>
+<P>
+matches "zz", "zzz", or "zzzz". A closing brace on its own is not a special
+character. If the second number is omitted, but the comma is present, there is
+no upper limit; if the second number and the comma are both omitted, the
+quantifier specifies an exact number of required matches. Thus
+</P>
+<P>
+<pre>
+  [aeiou]{3,}
+</PRE>
+</P>
+<P>
+matches at least 3 successive vowels, but may match many more, while
+</P>
+<P>
+<pre>
+  \d{8}
+</PRE>
+</P>
+<P>
+matches exactly 8 digits. An opening curly bracket that appears in a position
+where a quantifier is not allowed, or one that does not match the syntax of a
+quantifier, is taken as a literal character. For example, {,6} is not a
+quantifier, but a literal string of four characters.
+</P>
+<P>
+In UTF-8 mode, quantifiers apply to UTF-8 characters rather than to individual
+bytes. Thus, for example, \x{100}{2} matches two UTF-8 characters, each of
+which is represented by a two-byte sequence.
+</P>
+<P>
+The quantifier {0} is permitted, causing the expression to behave as if the
+previous item and the quantifier were not present.
+</P>
+<P>
+For convenience (and historical compatibility) the three most common
+quantifiers have single-character abbreviations:
+</P>
+<P>
+<pre>
+  *    is equivalent to {0,}
+  +    is equivalent to {1,}
+  ?    is equivalent to {0,1}
+</PRE>
+</P>
+<P>
+It is possible to construct infinite loops by following a subpattern that can
+match no characters with a quantifier that has no upper limit, for example:
+</P>
+<P>
+<pre>
+  (a?)*
+</PRE>
+</P>
+<P>
+Earlier versions of Perl and PCRE used to give an error at compile time for
+such patterns. However, because there are cases where this can be useful, such
+patterns are now accepted, but if any repetition of the subpattern does in fact
+match no characters, the loop is forcibly broken.
+</P>
+<P>
+By default, the quantifiers are "greedy", that is, they match as much as
+possible (up to the maximum number of permitted times), without causing the
+rest of the pattern to fail. The classic example of where this gives problems
+is in trying to match comments in C programs. These appear between the
+sequences /* and */ and within the sequence, individual * and / characters may
+appear. An attempt to match C comments by applying the pattern
+</P>
+<P>
+<pre>
+  /\*.*\*/
+</PRE>
+</P>
+<P>
+to the string
+</P>
+<P>
+<pre>
+  /* first command */  not comment  /* second comment */
+</PRE>
+</P>
+<P>
+fails, because it matches the entire string owing to the greediness of the .*
+item.
+</P>
+<P>
+However, if a quantifier is followed by a question mark, it ceases to be
+greedy, and instead matches the minimum number of times possible, so the
+pattern
+</P>
+<P>
+<pre>
+  /\*.*?\*/
+</PRE>
+</P>
+<P>
+does the right thing with the C comments. The meaning of the various
+quantifiers is not otherwise changed, just the preferred number of matches.
+Do not confuse this use of question mark with its use as a quantifier in its
+own right. Because it has two uses, it can sometimes appear doubled, as in
+</P>
+<P>
+<pre>
+  \d??\d
+</PRE>
+</P>
+<P>
+which matches one digit by preference, but can match two if that is the only
+way the rest of the pattern matches.
+</P>
+<P>
+If the PCRE_UNGREEDY option is set (an option which is not available in Perl),
+the quantifiers are not greedy by default, but individual ones can be made
+greedy by following them with a question mark. In other words, it inverts the
+default behaviour.
+</P>
+<P>
+When a parenthesized subpattern is quantified with a minimum repeat count that
+is greater than 1 or with a limited maximum, more store is required for the
+compiled pattern, in proportion to the size of the minimum or maximum.
+</P>
+<P>
+If a pattern starts with .* or .{0,} and the PCRE_DOTALL option (equivalent
+to Perl's /s) is set, thus allowing the . to match newlines, the pattern is
+implicitly anchored, because whatever follows will be tried against every
+character position in the subject string, so there is no point in retrying the
+overall match at any position after the first. PCRE normally treats such a
+pattern as though it were preceded by \A.
+</P>
+<P>
+In cases where it is known that the subject string contains no newlines, it is
+worth setting PCRE_DOTALL in order to obtain this optimization, or
+alternatively using ^ to indicate anchoring explicitly.
+</P>
+<P>
+However, there is one situation where the optimization cannot be used. When .*
+is inside capturing parentheses that are the subject of a backreference
+elsewhere in the pattern, a match at the start may fail, and a later one
+succeed. Consider, for example:
+</P>
+<P>
+<pre>
+  (.*)abc\1
+</PRE>
+</P>
+<P>
+If the subject is "xyz123abc123" the match point is the fourth character. For
+this reason, such a pattern is not implicitly anchored.
+</P>
+<P>
+When a capturing subpattern is repeated, the value captured is the substring
+that matched the final iteration. For example, after
+</P>
+<P>
+<pre>
+  (tweedle[dume]{3}\s*)+
+</PRE>
+</P>
+<P>
+has matched "tweedledum tweedledee" the value of the captured substring is
+"tweedledee". However, if there are nested capturing subpatterns, the
+corresponding captured values may have been set in previous iterations. For
+example, after
+</P>
+<P>
+<pre>
+  /(a|(b))+/
+</PRE>
+</P>
+<P>
+matches "aba" the value of the second captured substring is "b".
+</P>
+<br><a name="SEC13" href="#TOC1">ATOMIC GROUPING AND POSSESSIVE QUANTIFIERS</a><br>
+<P>
+With both maximizing and minimizing repetition, failure of what follows
+normally causes the repeated item to be re-evaluated to see if a different
+number of repeats allows the rest of the pattern to match. Sometimes it is
+useful to prevent this, either to change the nature of the match, or to cause
+it fail earlier than it otherwise might, when the author of the pattern knows
+there is no point in carrying on.
+</P>
+<P>
+Consider, for example, the pattern \d+foo when applied to the subject line
+</P>
+<P>
+<pre>
+  123456bar
+</PRE>
+</P>
+<P>
+After matching all 6 digits and then failing to match "foo", the normal
+action of the matcher is to try again with only 5 digits matching the \d+
+item, and then with 4, and so on, before ultimately failing. "Atomic grouping"
+(a term taken from Jeffrey Friedl's book) provides the means for specifying
+that once a subpattern has matched, it is not to be re-evaluated in this way.
+</P>
+<P>
+If we use atomic grouping for the previous example, the matcher would give up
+immediately on failing to match "foo" the first time. The notation is a kind of
+special parenthesis, starting with (?&#62; as in this example:
+</P>
+<P>
+<pre>
+  (?&#62;\d+)foo
+</PRE>
+</P>
+<P>
+This kind of parenthesis "locks up" the  part of the pattern it contains once
+it has matched, and a failure further into the pattern is prevented from
+backtracking into it. Backtracking past it to previous items, however, works as
+normal.
+</P>
+<P>
+An alternative description is that a subpattern of this type matches the string
+of characters that an identical standalone pattern would match, if anchored at
+the current point in the subject string.
+</P>
+<P>
+Atomic grouping subpatterns are not capturing subpatterns. Simple cases such as
+the above example can be thought of as a maximizing repeat that must swallow
+everything it can. So, while both \d+ and \d+? are prepared to adjust the
+number of digits they match in order to make the rest of the pattern match,
+(?&#62;\d+) can only match an entire sequence of digits.
+</P>
+<P>
+Atomic groups in general can of course contain arbitrarily complicated
+subpatterns, and can be nested. However, when the subpattern for an atomic
+group is just a single repeated item, as in the example above, a simpler
+notation, called a "possessive quantifier" can be used. This consists of an
+additional + character following a quantifier. Using this notation, the
+previous example can be rewritten as
+</P>
+<P>
+<pre>
+  \d++bar
+</PRE>
+</P>
+<P>
+Possessive quantifiers are always greedy; the setting of the PCRE_UNGREEDY
+option is ignored. They are a convenient notation for the simpler forms of
+atomic group. However, there is no difference in the meaning or processing of a
+possessive quantifier and the equivalent atomic group.
+</P>
+<P>
+The possessive quantifier syntax is an extension to the Perl syntax. It
+originates in Sun's Java package.
+</P>
+<P>
+When a pattern contains an unlimited repeat inside a subpattern that can itself
+be repeated an unlimited number of times, the use of an atomic group is the
+only way to avoid some failing matches taking a very long time indeed. The
+pattern
+</P>
+<P>
+<pre>
+  (\D+|&#60;\d+&#62;)*[!?]
+</PRE>
+</P>
+<P>
+matches an unlimited number of substrings that either consist of non-digits, or
+digits enclosed in &#60;&#62;, followed by either ! or ?. When it matches, it runs
+quickly. However, if it is applied to
+</P>
+<P>
+<pre>
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+</PRE>
+</P>
+<P>
+it takes a long time before reporting failure. This is because the string can
+be divided between the two repeats in a large number of ways, and all have to
+be tried. (The example used [!?] rather than a single character at the end,
+because both PCRE and Perl have an optimization that allows for fast failure
+when a single character is used. They remember the last single character that
+is required for a match, and fail early if it is not present in the string.)
+If the pattern is changed to
+</P>
+<P>
+<pre>
+  ((?&#62;\D+)|&#60;\d+&#62;)*[!?]
+</PRE>
+</P>
+<P>
+sequences of non-digits cannot be broken, and failure happens quickly.
+</P>
+<br><a name="SEC14" href="#TOC1">BACK REFERENCES</a><br>
+<P>
+Outside a character class, a backslash followed by a digit greater than 0 (and
+possibly further digits) is a back reference to a capturing subpattern earlier
+(that is, to its left) in the pattern, provided there have been that many
+previous capturing left parentheses.
+</P>
+<P>
+However, if the decimal number following the backslash is less than 10, it is
+always taken as a back reference, and causes an error only if there are not
+that many capturing left parentheses in the entire pattern. In other words, the
+parentheses that are referenced need not be to the left of the reference for
+numbers less than 10. See the section entitled "Backslash" above for further
+details of the handling of digits following a backslash.
+</P>
+<P>
+A back reference matches whatever actually matched the capturing subpattern in
+the current subject string, rather than anything matching the subpattern
+itself (see
+<a href="#subpatternsassubroutines">"Subpatterns as subroutines"</a>
+below for a way of doing that). So the pattern
+</P>
+<P>
+<pre>
+  (sens|respons)e and \1ibility
+</PRE>
+</P>
+<P>
+matches "sense and sensibility" and "response and responsibility", but not
+"sense and responsibility". If caseful matching is in force at the time of the
+back reference, the case of letters is relevant. For example,
+</P>
+<P>
+<pre>
+  ((?i)rah)\s+\1
+</PRE>
+</P>
+<P>
+matches "rah rah" and "RAH RAH", but not "RAH rah", even though the original
+capturing subpattern is matched caselessly.
+</P>
+<P>
+Back references to named subpatterns use the Python syntax (?P=name). We could
+rewrite the above example as follows:
+</P>
+<P>
+<pre>
+  (?&#60;p1&#62;(?i)rah)\s+(?P=p1)
+</PRE>
+</P>
+<P>
+There may be more than one back reference to the same subpattern. If a
+subpattern has not actually been used in a particular match, any back
+references to it always fail. For example, the pattern
+</P>
+<P>
+<pre>
+  (a|(bc))\2
+</PRE>
+</P>
+<P>
+always fails if it starts to match "a" rather than "bc". Because there may be
+many capturing parentheses in a pattern, all digits following the backslash are
+taken as part of a potential back reference number. If the pattern continues
+with a digit character, some delimiter must be used to terminate the back
+reference. If the PCRE_EXTENDED option is set, this can be whitespace.
+Otherwise an empty comment can be used.
+</P>
+<P>
+A back reference that occurs inside the parentheses to which it refers fails
+when the subpattern is first used, so, for example, (a\1) never matches.
+However, such references can be useful inside repeated subpatterns. For
+example, the pattern
+</P>
+<P>
+<pre>
+  (a|b\1)+
+</PRE>
+</P>
+<P>
+matches any number of "a"s and also "aba", "ababbaa" etc. At each iteration of
+the subpattern, the back reference matches the character string corresponding
+to the previous iteration. In order for this to work, the pattern must be such
+that the first iteration does not need to match the back reference. This can be
+done using alternation, as in the example above, or by a quantifier with a
+minimum of zero.
+</P>
+<br><a name="SEC15" href="#TOC1">ASSERTIONS</a><br>
+<P>
+An assertion is a test on the characters following or preceding the current
+matching point that does not actually consume any characters. The simple
+assertions coded as \b, \B, \A, \G, \Z, \z, ^ and $ are described above.
+More complicated assertions are coded as subpatterns. There are two kinds:
+those that look ahead of the current position in the subject string, and those
+that look behind it.
+</P>
+<P>
+An assertion subpattern is matched in the normal way, except that it does not
+cause the current matching position to be changed. Lookahead assertions start
+with (?= for positive assertions and (?! for negative assertions. For example,
+</P>
+<P>
+<pre>
+  \w+(?=;)
+</PRE>
+</P>
+<P>
+matches a word followed by a semicolon, but does not include the semicolon in
+the match, and
+</P>
+<P>
+<pre>
+  foo(?!bar)
+</PRE>
+</P>
+<P>
+matches any occurrence of "foo" that is not followed by "bar". Note that the
+apparently similar pattern
+</P>
+<P>
+<pre>
+  (?!foo)bar
+</PRE>
+</P>
+<P>
+does not find an occurrence of "bar" that is preceded by something other than
+"foo"; it finds any occurrence of "bar" whatsoever, because the assertion
+(?!foo) is always true when the next three characters are "bar". A
+lookbehind assertion is needed to achieve this effect.
+</P>
+<P>
+If you want to force a matching failure at some point in a pattern, the most
+convenient way to do it is with (?!) because an empty string always matches, so
+an assertion that requires there not to be an empty string must always fail.
+</P>
+<P>
+Lookbehind assertions start with (?&#60;= for positive assertions and (?&#60;! for
+negative assertions. For example,
+</P>
+<P>
+<pre>
+  (?&#60;!foo)bar
+</PRE>
+</P>
+<P>
+does find an occurrence of "bar" that is not preceded by "foo". The contents of
+a lookbehind assertion are restricted such that all the strings it matches must
+have a fixed length. However, if there are several alternatives, they do not
+all have to have the same fixed length. Thus
+</P>
+<P>
+<pre>
+  (?&#60;=bullock|donkey)
+</PRE>
+</P>
+<P>
+is permitted, but
+</P>
+<P>
+<pre>
+  (?&#60;!dogs?|cats?)
+</PRE>
+</P>
+<P>
+causes an error at compile time. Branches that match different length strings
+are permitted only at the top level of a lookbehind assertion. This is an
+extension compared with Perl (at least for 5.8), which requires all branches to
+match the same length of string. An assertion such as
+</P>
+<P>
+<pre>
+  (?&#60;=ab(c|de))
+</PRE>
+</P>
+<P>
+is not permitted, because its single top-level branch can match two different
+lengths, but it is acceptable if rewritten to use two top-level branches:
+</P>
+<P>
+<pre>
+  (?&#60;=abc|abde)
+</PRE>
+</P>
+<P>
+The implementation of lookbehind assertions is, for each alternative, to
+temporarily move the current position back by the fixed width and then try to
+match. If there are insufficient characters before the current position, the
+match is deemed to fail.
+</P>
+<P>
+PCRE does not allow the \C escape (which matches a single byte in UTF-8 mode)
+to appear in lookbehind assertions, because it makes it impossible to calculate
+the length of the lookbehind.
+</P>
+<P>
+Atomic groups can be used in conjunction with lookbehind assertions to specify
+efficient matching at the end of the subject string. Consider a simple pattern
+such as
+</P>
+<P>
+<pre>
+  abcd$
+</PRE>
+</P>
+<P>
+when applied to a long string that does not match. Because matching proceeds
+from left to right, PCRE will look for each "a" in the subject and then see if
+what follows matches the rest of the pattern. If the pattern is specified as
+</P>
+<P>
+<pre>
+  ^.*abcd$
+</PRE>
+</P>
+<P>
+the initial .* matches the entire string at first, but when this fails (because
+there is no following "a"), it backtracks to match all but the last character,
+then all but the last two characters, and so on. Once again the search for "a"
+covers the entire string, from right to left, so we are no better off. However,
+if the pattern is written as
+</P>
+<P>
+<pre>
+  ^(?&#62;.*)(?&#60;=abcd)
+</PRE>
+</P>
+<P>
+or, equivalently,
+</P>
+<P>
+<pre>
+  ^.*+(?&#60;=abcd)
+</PRE>
+</P>
+<P>
+there can be no backtracking for the .* item; it can match only the entire
+string. The subsequent lookbehind assertion does a single test on the last four
+characters. If it fails, the match fails immediately. For long strings, this
+approach makes a significant difference to the processing time.
+</P>
+<P>
+Several assertions (of any sort) may occur in succession. For example,
+</P>
+<P>
+<pre>
+  (?&#60;=\d{3})(?&#60;!999)foo
+</PRE>
+</P>
+<P>
+matches "foo" preceded by three digits that are not "999". Notice that each of
+the assertions is applied independently at the same point in the subject
+string. First there is a check that the previous three characters are all
+digits, and then there is a check that the same three characters are not "999".
+This pattern does <i>not</i> match "foo" preceded by six characters, the first
+of which are digits and the last three of which are not "999". For example, it
+doesn't match "123abcfoo". A pattern to do that is
+</P>
+<P>
+<pre>
+  (?&#60;=\d{3}...)(?&#60;!999)foo
+</PRE>
+</P>
+<P>
+This time the first assertion looks at the preceding six characters, checking
+that the first three are digits, and then the second assertion checks that the
+preceding three characters are not "999".
+</P>
+<P>
+Assertions can be nested in any combination. For example,
+</P>
+<P>
+<pre>
+  (?&#60;=(?&#60;!foo)bar)baz
+</PRE>
+</P>
+<P>
+matches an occurrence of "baz" that is preceded by "bar" which in turn is not
+preceded by "foo", while
+</P>
+<P>
+<pre>
+  (?&#60;=\d{3}(?!999)...)foo
+</PRE>
+</P>
+<P>
+is another pattern which matches "foo" preceded by three digits and any three
+characters that are not "999".
+</P>
+<P>
+Assertion subpatterns are not capturing subpatterns, and may not be repeated,
+because it makes no sense to assert the same thing several times. If any kind
+of assertion contains capturing subpatterns within it, these are counted for
+the purposes of numbering the capturing subpatterns in the whole pattern.
+However, substring capturing is carried out only for positive assertions,
+because it does not make sense for negative assertions.
+</P>
+<br><a name="SEC16" href="#TOC1">CONDITIONAL SUBPATTERNS</a><br>
+<P>
+It is possible to cause the matching process to obey a subpattern
+conditionally or to choose between two alternative subpatterns, depending on
+the result of an assertion, or whether a previous capturing subpattern matched
+or not. The two possible forms of conditional subpattern are
+</P>
+<P>
+<pre>
+  (?(condition)yes-pattern)
+  (?(condition)yes-pattern|no-pattern)
+</PRE>
+</P>
+<P>
+If the condition is satisfied, the yes-pattern is used; otherwise the
+no-pattern (if present) is used. If there are more than two alternatives in the
+subpattern, a compile-time error occurs.
+</P>
+<P>
+There are three kinds of condition. If the text between the parentheses
+consists of a sequence of digits, the condition is satisfied if the capturing
+subpattern of that number has previously matched. The number must be greater
+than zero. Consider the following pattern, which contains non-significant white
+space to make it more readable (assume the PCRE_EXTENDED option) and to divide
+it into three parts for ease of discussion:
+</P>
+<P>
+<pre>
+  ( \( )?    [^()]+    (?(1) \) )
+</PRE>
+</P>
+<P>
+The first part matches an optional opening parenthesis, and if that
+character is present, sets it as the first captured substring. The second part
+matches one or more characters that are not parentheses. The third part is a
+conditional subpattern that tests whether the first set of parentheses matched
+or not. If they did, that is, if subject started with an opening parenthesis,
+the condition is true, and so the yes-pattern is executed and a closing
+parenthesis is required. Otherwise, since no-pattern is not present, the
+subpattern matches nothing. In other words, this pattern matches a sequence of
+non-parentheses, optionally enclosed in parentheses.
+</P>
+<P>
+If the condition is the string (R), it is satisfied if a recursive call to the
+pattern or subpattern has been made. At "top level", the condition is false.
+This is a PCRE extension. Recursive patterns are described in the next section.
+</P>
+<P>
+If the condition is not a sequence of digits or (R), it must be an assertion.
+This may be a positive or negative lookahead or lookbehind assertion. Consider
+this pattern, again containing non-significant white space, and with the two
+alternatives on the second line:
+</P>
+<P>
+<pre>
+  (?(?=[^a-z]*[a-z])
+  \d{2}-[a-z]{3}-\d{2}  |  \d{2}-\d{2}-\d{2} )
+</PRE>
+</P>
+<P>
+The condition is a positive lookahead assertion that matches an optional
+sequence of non-letters followed by a letter. In other words, it tests for the
+presence of at least one letter in the subject. If a letter is found, the
+subject is matched against the first alternative; otherwise it is matched
+against the second. This pattern matches strings in one of the two forms
+dd-aaa-dd or dd-dd-dd, where aaa are letters and dd are digits.
+</P>
+<br><a name="SEC17" href="#TOC1">COMMENTS</a><br>
+<P>
+The sequence (?# marks the start of a comment which continues up to the next
+closing parenthesis. Nested parentheses are not permitted. The characters
+that make up a comment play no part in the pattern matching at all.
+</P>
+<P>
+If the PCRE_EXTENDED option is set, an unescaped # character outside a
+character class introduces a comment that continues up to the next newline
+character in the pattern.
+</P>
+<br><a name="SEC18" href="#TOC1">RECURSIVE PATTERNS</a><br>
+<P>
+Consider the problem of matching a string in parentheses, allowing for
+unlimited nested parentheses. Without the use of recursion, the best that can
+be done is to use a pattern that matches up to some fixed depth of nesting. It
+is not possible to handle an arbitrary nesting depth. Perl has provided an
+experimental facility that allows regular expressions to recurse (amongst other
+things). It does this by interpolating Perl code in the expression at run time,
+and the code can refer to the expression itself. A Perl pattern to solve the
+parentheses problem can be created like this:
+</P>
+<P>
+<pre>
+  $re = qr{\( (?: (?&#62;[^()]+) | (?p{$re}) )* \)}x;
+</PRE>
+</P>
+<P>
+The (?p{...}) item interpolates Perl code at run time, and in this case refers
+recursively to the pattern in which it appears. Obviously, PCRE cannot support
+the interpolation of Perl code. Instead, it supports some special syntax for
+recursion of the entire pattern, and also for individual subpattern recursion.
+</P>
+<P>
+The special item that consists of (? followed by a number greater than zero and
+a closing parenthesis is a recursive call of the subpattern of the given
+number, provided that it occurs inside that subpattern. (If not, it is a
+"subroutine" call, which is described in the next section.) The special item
+(?R) is a recursive call of the entire regular expression.
+</P>
+<P>
+For example, this PCRE pattern solves the nested parentheses problem (assume
+the PCRE_EXTENDED option is set so that white space is ignored):
+</P>
+<P>
+<pre>
+  \( ( (?&#62;[^()]+) | (?R) )* \)
+</PRE>
+</P>
+<P>
+First it matches an opening parenthesis. Then it matches any number of
+substrings which can either be a sequence of non-parentheses, or a recursive
+match of the pattern itself (that is a correctly parenthesized substring).
+Finally there is a closing parenthesis.
+</P>
+<P>
+If this were part of a larger pattern, you would not want to recurse the entire
+pattern, so instead you could use this:
+</P>
+<P>
+<pre>
+  ( \( ( (?&#62;[^()]+) | (?1) )* \) )
+</PRE>
+</P>
+<P>
+We have put the pattern into parentheses, and caused the recursion to refer to
+them instead of the whole pattern. In a larger pattern, keeping track of
+parenthesis numbers can be tricky. It may be more convenient to use named
+parentheses instead. For this, PCRE uses (?P&#62;name), which is an extension to
+the Python syntax that PCRE uses for named parentheses (Perl does not provide
+named parentheses). We could rewrite the above example as follows:
+</P>
+<P>
+<pre>
+  (?P&#60;pn&#62; \( ( (?&#62;[^()]+) | (?P&#62;pn) )* \) )
+</PRE>
+</P>
+<P>
+This particular example pattern contains nested unlimited repeats, and so the
+use of atomic grouping for matching strings of non-parentheses is important
+when applying the pattern to strings that do not match. For example, when this
+pattern is applied to
+</P>
+<P>
+<pre>
+  (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()
+</PRE>
+</P>
+<P>
+it yields "no match" quickly. However, if atomic grouping is not used,
+the match runs for a very long time indeed because there are so many different
+ways the + and * repeats can carve up the subject, and all have to be tested
+before failure can be reported.
+</P>
+<P>
+At the end of a match, the values set for any capturing subpatterns are those
+from the outermost level of the recursion at which the subpattern value is set.
+If you want to obtain intermediate values, a callout function can be used (see
+below and the
+<a href="pcrecallout.html"><b>pcrecallout</b></a>
+documentation). If the pattern above is matched against
+</P>
+<P>
+<pre>
+  (ab(cd)ef)
+</PRE>
+</P>
+<P>
+the value for the capturing parentheses is "ef", which is the last value taken
+on at the top level. If additional parentheses are added, giving
+</P>
+<P>
+<pre>
+  \( ( ( (?&#62;[^()]+) | (?R) )* ) \)
+     ^                        ^
+     ^                        ^
+</PRE>
+</P>
+<P>
+the string they capture is "ab(cd)ef", the contents of the top level
+parentheses. If there are more than 15 capturing parentheses in a pattern, PCRE
+has to obtain extra memory to store data during a recursion, which it does by
+using <b>pcre_malloc</b>, freeing it via <b>pcre_free</b> afterwards. If no
+memory can be obtained, the match fails with the PCRE_ERROR_NOMEMORY error.
+</P>
+<P>
+Do not confuse the (?R) item with the condition (R), which tests for recursion.
+Consider this pattern, which matches text in angle brackets, allowing for
+arbitrary nesting. Only digits are allowed in nested brackets (that is, when
+recursing), whereas any characters are permitted at the outer level.
+</P>
+<P>
+<pre>
+  &#60; (?: (?(R) \d++  | [^&#60;&#62;]*+) | (?R)) * &#62;
+</PRE>
+</P>
+<P>
+In this pattern, (?(R) is the start of a conditional subpattern, with two
+different alternatives for the recursive and non-recursive cases. The (?R) item
+is the actual recursive call.
+</P>
+<a name="subpatternsassubroutines"></a><br><a name="SEC19" href="#TOC1">SUBPATTERNS AS SUBROUTINES</a><br>
+<P>
+If the syntax for a recursive subpattern reference (either by number or by
+name) is used outside the parentheses to which it refers, it operates like a
+subroutine in a programming language. An earlier example pointed out that the
+pattern
+</P>
+<P>
+<pre>
+  (sens|respons)e and \1ibility
+</PRE>
+</P>
+<P>
+matches "sense and sensibility" and "response and responsibility", but not
+"sense and responsibility". If instead the pattern
+</P>
+<P>
+<pre>
+  (sens|respons)e and (?1)ibility
+</PRE>
+</P>
+<P>
+is used, it does match "sense and responsibility" as well as the other two
+strings. Such references must, however, follow the subpattern to which they
+refer.
+</P>
+<br><a name="SEC20" href="#TOC1">CALLOUTS</a><br>
+<P>
+Perl has a feature whereby using the sequence (?{...}) causes arbitrary Perl
+code to be obeyed in the middle of matching a regular expression. This makes it
+possible, amongst other things, to extract different substrings that match the
+same pair of parentheses when there is a repetition.
+</P>
+<P>
+PCRE provides a similar feature, but of course it cannot obey arbitrary Perl
+code. The feature is called "callout". The caller of PCRE provides an external
+function by putting its entry point in the global variable <i>pcre_callout</i>.
+By default, this variable contains NULL, which disables all calling out.
+</P>
+<P>
+Within a regular expression, (?C) indicates the points at which the external
+function is to be called. If you want to identify different callout points, you
+can put a number less than 256 after the letter C. The default value is zero.
+For example, this pattern has two callout points:
+</P>
+<P>
+<pre>
+  (?C1)\dabc(?C2)def
+</PRE>
+</P>
+<P>
+During matching, when PCRE reaches a callout point (and <i>pcre_callout</i> is
+set), the external function is called. It is provided with the number of the
+callout, and, optionally, one item of data originally supplied by the caller of
+<b>pcre_exec()</b>. The callout function may cause matching to backtrack, or to
+fail altogether. A complete description of the interface to the callout
+function is given in the
+<a href="pcrecallout.html"><b>pcrecallout</b></a>
+documentation.
+</P>
+<P>
+Last updated: 03 February 2003
+<br>
+Copyright &copy; 1997-2003 University of Cambridge.
diff --git a/doc/reference.conf b/doc/reference.conf
new file mode 100755 (executable)
index 0000000..b522adf
--- /dev/null
@@ -0,0 +1,675 @@
+/*
+ * Bahamut IRCd, doc/reference.conf
+ *
+ * Originally written by Trevor Talbot (Quension) in April 2004.
+ * $Id: reference.conf,v 1.1 2005/01/12 07:44:56 mmondor Exp $
+ * 
+ * The configuration format consists of blocks, each containing name-value
+ * pairs, tags, or string data.  It is designed to be easily readable by
+ * both human and ircd.
+ * 
+ * A block consists of a block name, an opening '{' brace, statements, a
+ * closing '}' brace, and a ';' semicolon.  A statement consists of a
+ * name, possibly followed by a value, and ending with a semicolon.
+ * Strings that contain special characters or whitespace can be surrounded
+ * by '"' double quotes.  All elements of the configuration are separated
+ * by whitespace, and can be packed on one line, or broken up over several
+ * lines.
+ *
+ */
+
+# Comments are supported in three forms:
+    /* C style
+       multi-line */
+    # shell style single-line
+    // C++ style single-line
+
+/*
+ * A sample block:
+ * 
+ *     block {
+ *         name value;            # A statement with a name value
+ *         name 123;              # A statement with a number value
+ *         name "hello world";    # A statement with a string value
+ *         tag;                   # A simple tag
+ *         "will code for food";  # A simple string
+ *     };
+ * 
+ * The parser also understands a special include directive outside of a
+ * block context:
+ * 
+ *     include path/to/file.conf;
+ */
+
+
+###########################################################################
+# Global [REQUIRED]
+# The Global block defines information about the server itself.
+# This block is required for the server to start.
+# 
+# Old conf format equivalents:
+#     M:name::info
+#     X:dpass:rpass
+
+global {
+    // required tokens
+    name    not.configured;     # The IRC name of the server
+    info    "located on earth"; # A short info line
+
+    // optional tokens
+    dpass   secret;             # Password for DIE command
+    rpass   secret;             # Password for RESTART command
+
+    // the optional Admin block may also be specified here
+};
+
+# The DIE and RESTART commands can optionally require passwords to be
+# supplied, to help prevent accidents.  If the dpass and rpass tokens are
+# not specified, no passwords are needed.
+# 
+# The Admin block may be nested here.  (This is currently a purely
+# organizational/astetic option.  This will probably change in future
+# releases.  -epi)
+# 
+# The server name may only be changed by a server restart.  The info line
+# can be changed on rehash, but will not propagate to other linked servers.
+# 
+# There must be exactly one Global block.
+###########################################################################
+
+
+###########################################################################
+# Admin [SUGGESTED]
+# The Admin block defines up to 3 information lines for the ADMIN command.
+# It may also exist inside the Global block.
+# 
+# Old conf format equivalents:
+#     A:line 1:line 2:line 3
+
+admin {
+    // optional tokens
+    "An unconfigured server";   # Info line #1
+    "An unknown administrator"; # Info line #2
+    "email@somewhere.earth";    # Info line #3
+};
+
+# Not all lines are required.  There may be only one Admin block.
+###########################################################################
+
+
+###########################################################################
+# Options [OPTIONAL]
+# The Options block configures various options for the server itself.
+# This block is recommended for networks other than DALnet.
+# 
+# Old conf format equivalents:
+#     T:wgmonhost:wgmonurl
+
+options {
+    // optional tokens
+    network_name    unconfigured;   # Name of the network
+    services_name   services.name;  # IRC name of services server
+    stats_name      stats.name;     # IRC name of stats server
+    staff_address   staff.net;      # Opermask hostname
+    nshelpurl       "http://foo";   # URL for nick registration help
+    wgmonhost       wgmon.host;     # Wingate monitor scan host
+    wgmonurl        "http://foo";   # URL for wingate monitor info
+    network_kline   "kline@net";    # Contact email for network bans
+    local_kline     "kline@server"; # Contact email for server bans
+    servtype        client;         # Set server type:
+                                    #     CLIENT, HUB, SERVICESHUB
+    maxchannels     10;             # Max chans user can join
+    ts_max_delta    300;            # Maximum timestamp delta
+    ts_warn_delta   30;             # Warn for TS deltas larger than this
+    local_clones    1:15;           # Default maximum local IP:site clones
+    global_clones   3:30;           # Default maximum global IP:site clones
+    crypt_oper_pass;                # Encrypted passwords in Oper blocks
+    short_motd;                     # Use ircd.smotd
+    show_links;                     # Show real LINKS output
+    allow_split_ops;                # Op in empty channels while unlinked
+};
+
+# The services and stats IRC server names are used for the shortform
+# commands, which are transformed into targeted name@server messages for
+# security.  The services server name is used by SERVICES, IDENTIFY,
+# NICKSERV, CHANSERV, MEMOSERV, ROOTSERV, NS, CS, MS, and RS.  The stats
+# server name is used by OPERSERV, STATSERV, HELPSERV, OS, SS, and HS.
+# >> defaults: services.dal.net, stats.dal.net
+# 
+# The staff_address token is used for operator hostmasking, as described
+# in the Allow block documentation.
+# >> default: staff.dalnet
+# 
+# The nshelpurl token is used to supply a URL for nick registration help.
+# It is sent to clients in the rejection numeric when they encounter a +R
+# channel and are not registered.
+# >> default: http://docs.dal.net/docs/nsemail.html
+# 
+# The maxchannels token limits the number of channels a user may join at
+# one time.  This limit does not apply to server operators.
+# >> default: 10
+# 
+# The server type changes some behavior to be appropriate to the role.
+# CLIENT servers can only link to one other server (their uplink hub).
+# HUB servers link several servers together, but are not intended to hold
+# general users.  The SERVICESHUB type enables some optimizations for
+# directly-connected services; it requires special services support and
+# should not be used in other cases.
+# >> default: CLIENT
+# 
+# The server is capable of sending a set of notices to users on connect,
+# providing some information about a proxy bot that scans them.  The
+# wgmonhost token is the host the bot will be connecting from (for users
+# with firewalls or other connection monitoring), and wgmonurl is a web
+# page containing additional information about the bot.  If neither token
+# is specified, the notices will not be sent.
+# 
+# Proper clock synchronization is required among connected servers.  This
+# requirement can be relaxed by using the ts_warn_delta and ts_max_delta
+# tokens.  The TS delta is the difference (in seconds) between the clocks
+# of this server and the one it is linked to.  These options should only
+# be used as a last resort; the correct fix for TS delta problems is to
+# synchronize the clocks on both server computers, such as with the ntpdate
+# tool.  Unsynchronized clocks may cause unpredictable network problems.
+# >> defaults: 15, 120
+# 
+# The local_clones and global_clones tokens control the maximum number of
+# connections the server will allow from the same place by default.  The
+# first number refers connections from the same IP (10.0.0.5).  This number
+# may optionally be followed by a colon ':' and a second number, which
+# refers to connections from the same site (10.0.0.*).  The local_clones
+# token sets the default limits for connections on this server only, and
+# may be overridden by the maxclones token in a Class block.  The
+# global_clones token sets the default limits for connections as seen on
+# the entire network, and may be overridden by network services.
+# >> defaults: 10:60, 25:150
+# 
+# The crypt_oper_pass token configures the server to use hashed passwords
+# in the Oper blocks, to avoid password discovery by someone reading the
+# conf file.  If this token is specified, passwords in the Oper blocks
+# must be generated by the tools/mkpasswd utility.
+# 
+# The short_motd token enables use of the ircd.smotd file, which is sent
+# to clients on connect instead of ircd.motd.  The MOTD command still sends
+# the contents of ircd.motd as normal.  This option is intended to reduce
+# traffic on connect, but still convince users to read the full motd when
+# appropriate.
+# 
+# The show_links token causes the LINKS command to display real server
+# links (though Super servers are still hidden).  If this token is not
+# specified, LINKS will display the list given to it by services.
+# 
+# The allow_split_ops token causes the server to always give chanop status
+# to users who join empty channels, even when no other servers are linked.
+# Normally op status will not be given when this server is alone, to help
+# prevent channel abuse during netsplits.  This token should be used if
+# your server is not part of a network.  Opers are always given op status
+# in empty channels regardless of this setting.
+# 
+# All of these tokens are optional, and can be specified at different times
+# in multiple Options blocks.  If a token is specified twice, the second
+# value overrides the first.
+###########################################################################
+
+
+###########################################################################
+# Port [REQUIRED]
+# The Port blocks define where the server will accept connections.  At
+# least one Port block is required to start.
+# 
+# Old conf format equivalents:
+#     M::bind::port
+#     P:ipmask:bind::port
+
+port {
+    // required tokens
+    port    6667;       # Port to listen on
+
+    // optional tokens
+    bind    127.0.0.1;  # IP address to listen on
+    ipmask  127.0.*.*;  # Mask to accept connections from
+};
+
+# If a bind address is not specified, the server listens on all available
+# interfaces.
+# 
+# The ipmask token is used to limit the port to connections from the
+# specified IP mask.  Only a simple mask is supported, consisting of a *
+# where one component of the address is (e.g. "192.168.*.*").
+# 
+# There may be multiple Port blocks.
+###########################################################################
+
+
+###########################################################################
+# Class [RECOMMENDED]
+# The Class blocks define the connection classes used by the Allow, Oper,
+# and Connect blocks.  While the server will start without a Class block,
+# it will not be usable.
+# 
+# Old conf format equivalents:
+#     Y:name:pingfreq::maxusers:maxsendq            (for clients)
+#     Y:name:pingfreq:connfreq:maxlinks:maxsendq    (for servers)
+
+class {
+    // required tokens
+    name        users;      # Name of the class
+    pingfreq    90;         # PING idle connection every N seconds
+    maxsendq    100000;     # Send buffer limit
+
+    // optional, for Allow classes only:
+    maxusers    100;        # Maximum number of clients
+    maxclones   2:20;       # Maximum IP:site clones
+
+    // optional, for Connect classes only:
+    connfreq    300;        # Try autoconnect every N seconds
+    maxlinks    1;          # Autoconnect if less than N links in class
+};
+
+# Idle connections are polled with the PING command every pingfreq seconds.
+# 
+# The maxsendq token controls the size of the internal send buffer, used
+# when a connection cannot accept large amounts of data at once.  Certain
+# server commands emit such large amounts of data.  As an example metric,
+# a 100KB user send queue can support a WHO <channel> query for a channel
+# with approximately 700 users.  Large amounts of data are also generated
+# when two servers link and synchronize network state.  If the send queue
+# limit is exceeded, the connection is terminated.
+# 
+# For classes used in the Allow blocks, the maxusers token limits the
+# number of clients that may exist in this class.  This is the most common
+# general user limit for the server.  If this limit is reached, additional
+# clients will be rejected with a "server busy" message.  This token must
+# not be specified for classes used in the Connect blocks.
+# 
+# For classes used in the Allow blocks, the maxclones token limits the
+# number of clients that may connect from the same place in this class.
+# The first number refers to connections from the same IP (10.0.0.5); it
+# may be optionally followed by a colon ':' and a second number, which
+# refers to connections from the same site (10.0.0.*).  If this limit is
+# reached, clients will be rejected with a "too many connections from your
+# host/site" message.  Limits defined here override the defaults as
+# configured in the Options block.  If a site limit is not supplied here,
+# clients in this class will effectively have no site limit; the default
+# limit will not be used.  This token must not be specified for classes
+# used in the Connect blocks.
+# 
+# For classes used in the Connect blocks, the connfreq token specifies the
+# frequency at which autoconnections are tried.  This token works together
+# with maxlinks, which specifies the maximum number of servers in this
+# class to autoconnect to.  For an autoconnection to take place, the
+# Connect block must have a valid port token, and there must be less than
+# maxlinks connected servers in this class.  The connfreq and maxlinks
+# tokens must not be specified for classes used in the Allow or Oper
+# blocks.
+# 
+# A "default" class is created internally using definitions in config.h.
+# This class is used when no other class is specified, but its settings are
+# not useful for most situations.  Custom classes are strongly suggested.
+# 
+# There may be multiple Class blocks; at least one is recommended.
+###########################################################################
+
+
+###########################################################################
+# Allow [RECOMMENDED]
+# The Allow blocks define the hosts connections are allowed from, and
+# places them into classes.  While the server will start without an Allow
+# block, it will not be usable.
+# 
+# Old conf format equivalents:
+#     I:ipmask:passwd:host:port:class
+
+allow {
+    // required tokens
+    host        *;          # Resolved host mask (optional if using ipmask)
+    ipmask      *;          # Unresolved IP mask (optional if using host)
+    
+    // optional tokens
+    port        6667;       # Apply block to this port only
+    passwd      secret;     # Require password for connection
+    flags       mCFT;       # Special flags for this connection
+    class       users;      # Place connections in this class
+};
+
+# The server uses a default-deny policy for incoming connections; at least
+# one Allow block must be supplied if you wish to use your server.
+# 
+# The host and ipmask tokens specify which connections this block matches.
+# The server always performs DNS and ident lookups for connections.  If DNS
+# cannot find a hostname, the IP is used instead.  If ident cannot get a
+# valid response, "unknown" is used during this stage.  The client's
+# resolved hostname, IP address, ident reply, and username (from the USER
+# line) are used according to the results of the matches described below.
+# 
+# The host token attempts to match first against the resolved hostname if
+# available, then against the IP address.  To include the connection's
+# ident response in the match, use a mask in the form "ident@host".  If a
+# client matches this token, it appears on IRC using its resolved hostname.
+# 
+# The ipmask token attempts to match against the IP address only.  To
+# include the connection's ident response in the match, use a mask in the
+# form "ident@host".  If a client matches this token, it appears on IRC
+# using its IP address, even if its hostname was resolved.
+# 
+# If the matching mask used ident ("ident@host" instead of "host"), and no
+# ident response was received from the client, it appears on IRC with its
+# username prefixed with '~'.  If the matching mask used only the "host"
+# form, the client's username is not prefixed.  If a valid ident response
+# was received, it is always used (without prefix), regardless of the mask
+# form.
+# 
+# Only one of the host and ipmask tokens is needed; if both are used, host
+# is matched first.  For typical configurations, using only the host token
+# with a "*@host" or "ident@host" mask is recommended.
+# 
+# Examples:
+#     // client with username "user", ident "ident", hostname "name"
+#     host ident@*;     # appears as ident@name
+#     ipmask *;         # appears as ident@10.0.0.1
+# 
+#     // same client without ident response
+#     host *;           # appears as user@name
+#     host *@*;         # appears as ~user@name
+#     ipmask unknown@*; # appears as ~user@10.0.0.1
+# 
+# The port token limits this block to match connections to the specified
+# port only.
+# 
+# The passwd token requires that matching connections supply a password to
+# use the server.  Clients can send a password string in multiple forms,
+# depending on which passwords need to be used.  The basic format is:
+# 
+#     [passwd][:][opername:operpass][:][nickpass]
+# 
+# If the Allow block requires a password, it must be supplied at the front
+# of the string.  If operator hostmasking is enabled (via the flags token
+# described below), the client can mask itself by supplying a "name:passwd"
+# string as defined in an Oper block.  When masked, a client appears on IRC
+# using the Oper block name for its ident, and the Options block
+# staff_address for its hostname.  Any remaining passwords are sent to
+# NickServ in an SIDENTIFY command.  All password components must be
+# separated from each other by a ':' colon.
+# 
+# Using the examples in this file, a client could connect with the password
+# string "secret:johndoe:secret" and be masked as johndoe@staff.net.
+# 
+# The flags token allows special behavior to be assigned to this
+# connection, using a set of single-letter options.  The available flags
+# are:
+# 
+#     m  Enable operator hostmasking
+#     C  Bypass clone limiting
+#     F  Force umode +F to bypass message flood checks
+#     T  Disable rapid (re)connection throttling
+# 
+# When operator hostmasking is enabled, a matching client can connect using
+# a special password to be masked, as described for the passwd token above.
+# 
+# Normally all clients are subjected to a clone limit check when they
+# connect, as configured in the Options and Class blocks.  The C flag skips
+# this check for matching clients, allowing them to have an unrestricted
+# number of connections.
+# 
+# The F flag forces matching clients to always have usermode +F, to avoid
+# various message flood checks.  This flag is intended for special bots and
+# should not be used for server operators; opers can make use of the F
+# access flag as described in the Oper block documentation.
+# 
+# By default, the server throttles rapid (re)connections from a single IP
+# address, to help reduce abuse and load.  The T flag disables this
+# mechanism for matching clients.  To qualify, a client must send valid
+# NICK and USER messages to register the connection, and stay connected
+# long enough to complete the ident and DNS lookups.  However, a correct
+# password is not required.
+#
+# The class token specifies the connection Class to place matching
+# connections in.  If not specified, the default class is used; see the
+# Class block description for details.
+# 
+# There may be multiple Allow blocks; they are matched in the order they
+# appear.
+###########################################################################
+
+
+###########################################################################
+# Oper [SUGGESTED]
+# The Oper blocks define server operators.  One or more of these blocks
+# is recommended if you intend to maintain your server.
+# 
+# Old conf format equivalents:
+#     O:host:passwd:name:access:class
+
+oper {
+    // required tokens
+    name    johndoe;        # Account name
+    passwd  secret;         # Account password (optionally encrypted)
+    host    ident@hostmask; # Restrict access to this mask
+    host    *@172.16.4.2;   # Up to 32 masks can be specified here
+    access  *Aa;            # Access flags
+
+    // optional tokens
+    class   opers;          # Place authenticated client in this class
+};
+
+# The name and passwd tokens match the parameters of the OPER command.  If
+# the crypt_oper_pass token is specified in the Options block, the passwd
+# string must be generated by the tools/mkpasswd utility.  The plaintext
+# password is still used in the OPER command.
+# 
+# To authenticate as an operator, a client must match one of the host
+# tokens specified in "user@host" mask form.  There can be up to 32 host
+# tokens.  The host part is matched against both resolved hostname and IP
+# address.
+# 
+# The access token specifies what access an operator has, using a set of
+# single-letter flags.  The available flags are:
+# 
+#     r  Can use REHASH
+#     h  Can use GLOBOPS
+#     w  Can use WALLOPS
+#     l  Can use LOCOPS
+#     b  Can use KLINE
+#     B  Can use UNKLINE
+#     c  Can route this server
+#     k  Can kill clients on this server
+#     n  Can send server notices
+#     u  Can use umode +c (see client connection notices)
+#     f  Can use umode +f (see flood notices)
+#     o  Server operator: includes all of above, plus umodes
+#                         +y (spy), +d (debug), +b (CHATOPS)
+# 
+#     C  Can route other servers
+#     K  Can kill clients on other servers
+#     N  Can send global notices
+#     O  Network operator: includes all of above
+# 
+#     D  Can use DIE
+#     R  Can use RESTART
+#     F  Can use umode +F (no flood limits)
+#     *  all of the above
+# 
+#     A  Can use umode +A (server administrator)
+#     a  Can use umode +a (services administrator)
+# 
+# The class token specifies the connection class the operator will be
+# placed in.  If not specified, the default class is used; see the Class
+# block description for details.
+# 
+# There may be multiple Oper blocks.
+###########################################################################
+
+
+###########################################################################
+# Connect [OPTIONAL]
+# The Connect blocks define links to other servers.
+# 
+# Old conf format equivalents:
+#     C:host:cpasswd:name:port:class:bind
+#     N:host:apasswd:name:flags:class
+#     H:*::name
+
+connect {
+    // required tokens
+    name    server.name;    # Other server's name
+    host    server.host;    # Other server's host
+    apasswd secret;         # Password to accept from other server
+    cpasswd secret;         # Password to send to other server
+
+    // optional tokens
+    port    7000;           # Port to autoconnect to other server on
+    bind    127.0.0.1;      # IP to connect from
+    flags   ZEH;            # Flags for this link
+    class   servers;        # Connection class to use for this link
+};
+
+# The name token specifies the IRC name of the other server, and the host
+# token specifies its hostname or IP address.  Using an IP address is
+# recommended.
+# 
+# The apasswd token defines the password this server will accept from the
+# other one, and cpasswd defines the password to be sent.
+# 
+# The port token is used to enable autoconnection.  See the Class block
+# description for details.
+# 
+# The bind token specifies the local IP to use when connecting to the
+# other server.
+# 
+# The flags token defines various options for the connection:
+# 
+#     H   other server is a hub (allowed to link other servers)
+#     Z   compress the link traffic
+#     E   encrypt the link using RC4 with DH key exchange
+# 
+# The class token specifies the connection class to use for the server
+# link.  If not specified, the default class is used; see the Class block
+# description for details.
+# 
+# There may be multiple Connect blocks.
+###########################################################################
+
+
+###########################################################################
+# Super [OPTIONAL]
+# The Super block defines up to 24 super ("U-lined") servers, allowed to
+# to do special network things.  Used for network services.
+# 
+# Old conf format equivalents:
+#     U:name:*:*
+
+super {
+    "server1.name";     # Super server #1
+    "server2.name";     # Super server #2
+                        # ...
+};
+
+# Super servers are permitted to do things typical network services would
+# want to do, such as apply network bans, manage channel modes, etc; the
+# details are too numerous and complex to provide here.  This block is a
+# simple list of up to 24 such servers, using their IRC names.
+# 
+# Super servers may be changed by a rehash, but will not take effect until
+# all existing server links have been broken and reconnected.
+# 
+# There may be multiple Super blocks; all blocks are combined into one
+# list.  There is a global limit of 24 super servers.
+###########################################################################
+
+
+###########################################################################
+# Restrict [OPTIONAL]
+# The Restrict blocks disallow nicknames, channelnames, or clients with
+# specific GCOS (real name) fields.
+# 
+# Old conf format equivalents:
+#     Q::reason:mask    (NICK, CHAN)
+#     G::reason:mask    (GCOS)
+
+restrict {
+    // required tokens
+    type    chan;           # Type of restriction: NICK, CHAN, GCOS
+    mask    "#botworld";    # Disallowed mask
+    
+    // optional tokens
+    reason  "evil bots";    # Reason for restriction
+};
+
+# Setting the type token to "nick" will create a nickname restriction,
+# using the specified wildcard mask.  Restricted nicknames cannot be used
+# by normal clients, but network operators may use them.
+# 
+# Setting the type token to "chan" will create a channel restriction,
+# using the specified wildcard mask.  Restricted channels cannot be used
+# by normal clients, but network operators may join them.
+# 
+# Setting the type token to "gcos" will create a real name field ban,
+# using the specified wildcard mask.  Clients with a matching real name
+# field will not be permitted to connect.
+# 
+# The reason token provides a single-line reason for the restriction, and
+# is sent to clients along with the rejection notice.
+# 
+# There may be multiple Restrict blocks.
+###########################################################################
+
+
+###########################################################################
+# Kill [OPTIONAL]
+# The Kill blocks disallow connections from clients based on specific
+# ident and host masks.
+# 
+# Old conf format equivalents:
+#     K:host:reason:username    (mask was split)
+
+kill {
+    // required tokens
+    mask    "*@192.168.0.0/24";     # Disallowed mask (wildcard or CIDR)
+    
+    // optional tokens
+    reason  "tourists only!";       # Reason for ban
+};
+
+# Kill blocks are a flexible general client ban mechanism.  The mask token
+# can be specified in several formats:
+# 
+#     mask *.bot.world;       # Wildcard hostname
+#     mask user@*.isp;        # Wildcard user@hostname
+#     mask 192.168.0.0/16;    # CIDR IP (all 4 parts needed)
+#     mask user@10.4.2.2/30;  # CIDR user@IP
+# 
+# CIDR format is the most efficient, and should be used when possible.  The
+# server will attempt to convert wildcard IP masks to CIDR form internally.
+# 
+# Clients that match a Kill block will be rejected from the server.
+# Existing clients will be scanned for possible matches when new blocks are
+# loaded during rehash.
+# 
+# There may be multiple Kill blocks.
+###########################################################################
+
+
+###########################################################################
+# Modules [OPTIONAL]
+# The Modules block configures dynamic module loading.
+# 
+# Old conf format equivalents:
+#     modules.ini
+
+modules {
+    // optional tokens
+    path        mods;   # Directory to look for modules in
+    autoload    mod1;   # Module to load at startup
+    autoload    mod2;   # Up to 128 modules can be specified here
+};
+
+# The path token specifies the directory to search for modules in, relative
+# to the ircd directory.  There can only be one path, and the default is
+# "modules".
+# 
+# The autoload token specifies the name of a module (without file
+# extension) to load automatically at startup.  There can be up to 128
+# autoload tokens.
+# 
+# There may be only one Modules block.
+###########################################################################
diff --git a/doc/server-version-info b/doc/server-version-info
new file mode 100644 (file)
index 0000000..82682bb
--- /dev/null
@@ -0,0 +1,57 @@
+When you type /version you will often see something like this:
+bahamut-1.8(02). server aABCdFhistTY/HoS TS5ow-r[RELEASE] RPL1 NP[U]
+
+Ever wondered what those funny chars mean after the version number? Well
+here they are:
+
+The first set before the '/' are compile-time flags:
+'a' config.h ANTI_SPAMBOT
+'A' config.h ALWAYS_SEND_DURING_SPLIT
+'B' config.h MAXBUFFERS
+'C' config.h CMDLINE_CONFIG
+'d' config.h DO_IDENTD
+'D' config.h DEBUGMODE
+'E' configure encryption support enabled
+'F' config.h FLUD
+'h' config.h SHOW_HEADERS
+'i' config.h SHOW_INVISIBLE_LUSERS
+'I' config.h NO_DEFAULT_INVISIBLE
+'J' config.h NO_DEFAULT_JOINRATE
+'M' configure module support enabled
+'N' config.h DNS_DEBUG
+'r' config.h DENY_SERVICES_MSGS
+'s' config.h SUPER_TARGETS_ONLY
+'t' config.h MSG_TARGET_LIMIT
+'T' config.h THROTTLE_ENABLE
+'u' config.h IRCII_KLUDGE
+'Y' config.h USE_SYSLOG
+
+The first set after the '/' are runtime flags:
+'H' ircd.conf servtype = hub
+'m' ircd.conf short_motd
+'o' ircd.conf allow_split_ops
+'p' ircd.conf crypt_oper_pass
+'S' ircd.conf servtype = serviceshub
+'w' ircd.conf wgmonhost/wgmonurl
+
+The next part, 'TS5ow', is mostly historical.  The release tag is provided
+after it.
+
+If 'RPL#' shows up, oper hostmasking is enabled.  The number is the level:
+ 1  All +A users can see real IP
+ 2  Local +A users can see real IP
+ 3  Noone can see real IP (but it's still logged)
+
+The last part, 'NP[]', contains network information hiding flags:
+'A' config.h FORCE_EVERYONE_HIDDEN
+'I' config.h ALLOW_HIDDEN_OPERS
+'K' config.h HIDE_KILL_ORIGINS
+'L' ircd.conf no show_links (hiding LINKS)
+'M' config.h HIDE_SERVERMODE_ORIGINS
+'N' config.h HIDE_NUMERIC_SOURCE
+'O' config.h NO_USER_OPERTARGETED_COMMANDS
+'P' config.h HIDE_SPLIT_SERVERS
+'S' config.h NO_USER_STATS
+'T' config.h NO_USER_TRACE
+'U' config.h HIDEULINEDSERVS
+'W' config.h NO_USER_SERVERNOTICES
diff --git a/doc/template.conf b/doc/template.conf
new file mode 100644 (file)
index 0000000..a37d7f6
--- /dev/null
@@ -0,0 +1,134 @@
+# =========================================================================
+# QUICKSTART: server configuration (see reference.conf for details)
+# =========================================================================
+
+/* server name and administration info */
+global {
+    name    not.configured;         # IRC name of the server
+    info    "located on earth";     # A short info line
+    admin {
+        "An unconfigured server";   # Three information lines sent
+        "An unknown administrator"; # in reply to ADMIN command
+        "email@somewhere.earth";
+    };
+};
+
+/* server options */
+options {
+    network_name    unconfigured;   # A name is needed even if not linked
+    local_kline     admin@server;   # Contact email for server bans
+    show_links;                     # Show servers in LINKS
+    allow_split_ops;                # Give ops in empty channels
+
+    // use these options when services is on the network
+    services_name   services.name;  # Name of services (NS/CS/MS/RS) server
+    stats_name      stats.name;     # Name of stats (OS/SS/HS) server
+    network_kline   admin@net;      # Contact email for network bans
+    nshelpurl       "http://help";  # Nick registration help page
+
+    // if you need to link more than 1 server, uncomment the following line
+    # servtype        hub;
+};
+
+/* where to listen for connections */
+port {
+    port    6667;         # Port to listen on
+    bind    127.0.0.1;    # IP address to listen on
+};
+
+/* more listening ports */
+port { port 6668; bind 127.0.0.1; };
+port { port 6669; bind 127.0.0.1; };
+port { port 7000; bind 127.0.0.1; };
+
+/* allow clients to connect */
+allow {
+    host    *@*;    # Allow anyone
+    class   users;  # Place them in the users class
+};
+
+/* connection class for users */
+class {
+    name        users;      # Class name
+    maxusers    100;        # Maximum connections
+    pingfreq    90;         # Check idle connections every N seconds
+    maxsendq    100000;     # 100KB send buffer limit
+};
+
+/* connection class for server operators */
+class {
+    name        opers;
+    pingfreq    90;
+    maxsendq    500000;     # 500KB limit for opers
+};
+
+/* the server administrator */
+oper {
+    name    admin;          # Username
+    passwd  secret;         # Password
+    access  OARD;           # Server Administrator
+    host    *@192.168.0.*;  # Must be connecting from here
+    host    *@10.64.64.*;   # Or from here
+    // insert other other hostmasks here
+    class   opers;          # Belongs in the opers class
+};
+
+/* for services */
+super {
+    "services.name";
+    "stats.name";
+    // insert any other special servers here
+};
+
+/* reserved nicknames */
+restrict { type nick; mask "NickServ"; reason "reserved for services"; };
+restrict { type nick; mask "ChanServ"; reason "reserved for services"; };
+restrict { type nick; mask "MemoServ"; reason "reserved for services"; };
+restrict { type nick; mask "RootServ"; reason "reserved for services"; };
+restrict { type nick; mask "OperServ"; reason "reserved for services"; };
+restrict { type nick; mask "StatServ"; reason "reserved for services"; };
+restrict { type nick; mask "HelpServ"; reason "reserved for services"; };
+restrict { type nick; mask "services"; reason "reserved for services"; };
+
+
+/* === these next two blocks are for linking to a hub === */
+
+/* class for uplink hub */
+class {
+    name        hub;
+    pingfreq    120;        # Idle check every 2 minutes
+    connfreq    300;        # Try autoconnect every 5 minutes
+    maxsendq    1000000;    # 1MB send queue
+    maxlinks    1;          # Autoconnect to only 1 hub at a time
+};
+
+/* our uplink hub */
+connect {
+    name        hub.name;   # Hub's IRC name
+    host        172.16.4.2; # Hub's IP address
+    port        7325;       # Autoconnect to hub's port 7325
+    bind        127.0.0.1;  # We connect from this IP
+    apasswd     secret;     # We accept this password from hub
+    cpasswd     secret;     # We send this password to hub
+    flags       H;          # It is a hub
+    class       hub;        # Use hub class
+};
+
+
+/* === these next two blocks are for linking to services === */
+
+/* class for services */
+class {
+    name        services;
+    pingfreq    60;         # Idle check every minute
+    maxsendq    5000000;    # 5MB backlog buffer
+};
+
+/* our services */
+connect {
+    name        services.name;  # Services' IRC name
+    host        127.0.0.1;      # IP address services connects from
+    apasswd     secret;         # Password services sends
+    cpasswd     secret;         # Same password
+    class       services;
+};
diff --git a/include/.indent.pro b/include/.indent.pro
new file mode 100644 (file)
index 0000000..ba50d16
--- /dev/null
@@ -0,0 +1,25 @@
+-bad
+-bap
+-nbc
+-br
+-cdb
+-nce
+-ci3
+-cli3
+-di12
+-dj
+-ei
+-fc1
+-i3
+-l72
+-lp
+-npcs
+-psl
+-sc
+-sob
+-TBlock -TBlockHeap -TClass -Tdbuf -Tdbufbuf -TaConfList -TaConfEntry
+-Tfdstruct -Thashentry -Treslist -Tcache -Tcachetable -TaConfItem
+-TaClient -TaChannel -TanUser -TaServer -TLink -TMode -Tts_val
+-TaMotd -TWhowas -Tmsg_tree -TBan -Tu_char -Tu_short -Tu_long
+-Tu_int -Taname -THEADER -Tip_entry -Tgline_pending -Tscache_entry
+-TNumeric
diff --git a/include/blalloc.h b/include/blalloc.h
new file mode 100644 (file)
index 0000000..b4e7003
--- /dev/null
@@ -0,0 +1,48 @@
+/************************************************************************* 
+ * File:   blalloc.h
+ * Owner:  jolo
+ *************************************************************************/
+
+/* $Id: blalloc.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#ifndef BLALLOC_H
+#define BLALLOC_H
+/* INCLUDES */
+#include <stddef.h>
+
+/* DEFINES */
+#define BlockHeapALLOC(bh, type)       ((type *) BlockHeapAlloc(bh))
+
+/* TYPEDEFS */
+
+/* Block contains status information for an allocated block in our heap. */
+
+typedef struct Block 
+{
+    void          *elems;              /* Points to allocated memory */
+    void          *endElem;            /* Points to last elem for boundck */
+    int            freeElems;          /* Number of available elems */
+    struct Block  *next;               /* Next in our chain of blocks */
+    unsigned long *allocMap;           /* Bitmap of allocated blocks */
+} Block;
+
+/* BlockHeap contains the information for the root node of the memory heap. */
+
+typedef struct BlockHeap 
+{
+    size_t         elemSize;           /* Size of each element to be stored */
+    int            elemsPerBlock;      /* Number of elements per block */
+    int            numlongs;           /* Size of Block's allocMap array */
+    int            blocksAllocated;    /* Number of blocks allocated */
+    int            freeElems;          /* Number of free elements */
+    Block         *base;                       /* Pointer to first block */
+} BlockHeap;
+
+/* FUNCTION PROTOTYPES */
+
+BlockHeap        *BlockHeapCreate(size_t elemsize, int elemsperblock);
+int               BlockHeapDestroy(BlockHeap *bh);
+void             *BlockHeapAlloc(BlockHeap *bh);
+int               BlockHeapFree(BlockHeap *bh, void *ptr);
+
+#endif
diff --git a/include/cdefs.h b/include/cdefs.h
new file mode 100644 (file)
index 0000000..57eb83f
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * ++Copyright++ 1991, 1993 - Copyright (c) 1991, 1993 The Regents of
+ * the University of California.  All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met: 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer. 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with
+ * the distribution. 3. All advertising materials mentioning features
+ * or use of this software must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors. 4. Neither the name of
+ * the University nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. - Portions Copyright (c) 1993 by Digital Equipment
+ * Corporation.
+ * 
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies, and that the name of Digital Equipment Corporation not be
+ * used in advertising or publicity pertaining to distribution of the
+ * document or software without specific, written prior permission.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL
+ * DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -
+ * --Copyright--
+ */
+/* @(#)cdefs.h        8.1 (Berkeley) 6/2/93 $Id: cdefs.h,v 1.1.1.1 1998/07/01
+ * 14:33:57 wd Exp $
+ */
+
+/* $Id: cdefs.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#ifndef        _CDEFS_H_
+#define        _CDEFS_H_
+
+#if defined(__cplusplus)
+#define        __BEGIN_DECLS   extern "C" {
+#define        __END_DECLS     };
+#else
+#define        __BEGIN_DECLS
+#define        __END_DECLS
+#endif
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names,
+ * e.g. with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces
+ * oldfoo. The __CONCAT macro is a bit tricky -- make sure you don't
+ * put spaces in between its arguments.  __CONCAT can also concatenate
+ * double-quoted strings produced by the __STRING macro, but this only
+ * works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus)
+#define        __P(protos)     protos  /* full-blown ANSI C */
+#define        __CONCAT(x,y)   x ## y
+#define        __STRING(x)     #x
+#else /* !(__STDC__ || __cplusplus) */
+#define        __P(protos)     ()      /* traditional C preprocessor */
+#define        __CONCAT(x,y)   x/**/y
+#define        __STRING(x)     "x"
+#ifdef __GNUC__
+#define        const           __const /* GCC: ANSI C with -traditional */
+#define        inline          __inline
+#define        signed          __signed
+#define        volatile        __volatile
+#else /* !__GNUC__ */
+#define        const                   /* delete ANSI C keywords */
+#define        inline
+#define        signed
+#define        volatile
+#endif /* !__GNUC__ */
+#endif /* !(__STDC__ || __cplusplus) */
+/*
+ * GCC has extensions for declaring functions as `pure' (always returns
+ * the same value given the same inputs, i.e., has no external state
+ * and no side effects) and `dead' (nonreturning).  These mainly affect
+ * optimization and warnings.  Unfortunately, GCC complains if these
+ * are used under strict ANSI mode (`gcc -ansi -pedantic'), hence we
+ * need to define them only if compiling without this.
+ */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define __dead __volatile
+#define __pure __const
+#else
+#define __dead
+#define __pure
+#endif
+#endif /* !_CDEFS_H_ */
diff --git a/include/channel.h b/include/channel.h
new file mode 100644 (file)
index 0000000..aa08c6e
--- /dev/null
@@ -0,0 +1,35 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/channel.h
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: channel.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#ifndef        __channel_include__
+#define __channel_include__
+#define find_channel(chname, chptr) (hash_find_channel(chname, chptr))
+#define CREATE 1               /* whether a channel should be created or just
+                                 * tested for existance */
+#define MODEBUFLEN             200    /* max modebuf we consider from users */
+#define REALMODEBUFLEN         512    /* max actual modebuf */
+#define NullChn                ((aChannel *) NULL)
+#define ChannelExists(n)       (find_channel(n, NullChn) != NullChn)
+#include "msg.h"
+#define        MAXMODEPARAMS   (MAXPARA-4) /* parv[0] .. parv[3] */
+#define        MAXTSMODEPARAMS (MAXPARA-5) /* parv[0] .. parv[4] */
+#define MAXMODEPARAMSUSER 6
+#endif
diff --git a/include/clones.h b/include/clones.h
new file mode 100644 (file)
index 0000000..ee6c8ae
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef CLONES_H
+#define CLONES_H
+/*
+ *   clones.h - Clone detection and limiting
+ *   Copyright (C) 2004 Trevor Talbot and
+ *                      the DALnet coding team
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: clones.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+
+
+#define CLIM_HARD_GLOBAL    1
+#define CLIM_SOFT_LOCAL     2
+#define CLIM_SOFT_GLOBAL    3
+
+typedef struct SCloneEnt CloneEnt;
+typedef struct SCloneStat CloneStat;
+
+struct SCloneEnt
+{
+    CloneEnt *prev;                 /* master list */
+    CloneEnt *next;                 /* master list */
+    aClient  *clients;              /* online clients, IP/32 only */
+    int       lcount;               /* local clones */
+    int       gcount;               /* global clones */
+    int       limit;                /* global limit (from services) */
+    int       sllimit;              /* soft local limit (from SET) */
+    int       sglimit;              /* soft global limit (from SET) */
+    char      ent[HOSTIPLEN+1];     /* IP entity */
+};
+
+struct SCloneStat {
+    unsigned long   rlh;    /* rejected local hosts */
+    unsigned long   rls;    /* rejected local sites */
+    unsigned long   rgh;    /* rejected global hosts */
+    unsigned long   rgs;    /* rejected global sites */
+};
+
+extern CloneEnt *clones_list;
+extern CloneStat clones_stat;
+
+void clones_init(void);
+int  clones_set(char *, int, int);
+void clones_get(char *, int *, int *, int *);
+void clones_send(aClient *);
+
+#ifdef THROTTLE_ENABLE
+
+int  clones_check(aClient *);
+void clones_add(aClient *);
+void clones_remove(aClient *);
+
+#else   /* THROTTLE_ENABLE */
+
+#define clones_check(x)     (0)
+#define clones_add(x)       ((void)0)
+#define clones_remove(x)    ((void)0)
+
+#endif
+
+#endif  /* CLONES_H */
diff --git a/include/common.h b/include/common.h
new file mode 100644 (file)
index 0000000..876ee0b
--- /dev/null
@@ -0,0 +1,118 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/common.h
+ *   Copyright (C) 1990 Armin Gruner
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* $Id: common.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#ifndef        __common_include__
+#define __common_include__
+#define IRCD_MIN(a, b)  ((a) < (b) ? (a) : (b))
+#if defined( HAVE_PARAM_H )
+#include <sys/param.h>
+#endif
+#ifndef NULL
+#define NULL 0
+#endif
+#ifdef TRUE
+#undef TRUE
+#endif
+#ifdef FALSE
+#undef FALSE
+#endif
+#define FALSE (0)
+#define TRUE  (!FALSE)
+#define HIDEME 2
+/* Blah. I use these a lot. -Dianora */
+#ifdef YES
+#undef YES
+#endif
+#define YES 1
+#ifdef NO
+#undef NO
+#endif
+#define NO  0
+#ifdef FOREVER
+#undef FOREVER
+#endif
+#define FOREVER for(;;)
+/* -Dianora */
+#if !defined(STDC_HEADERS)
+char        *malloc(), *calloc();
+void        free();
+#endif
+extern void flush_fdlist_connections();
+extern int  match(char *, char *);
+extern int  mycmp(char *, char *);
+extern int  mycmp_diff(char *, char *);
+extern int  myncmp(char *, char *, int);
+#if !defined( HAVE_STRTOK )
+extern char *strtok(char *, char *);
+#endif
+#if !defined( HAVE_STRTOKEN )
+extern char *strtoken(char **, char *, char *);
+#endif
+#if !defined( HAVE_INET_ADDR )
+extern unsigned long inet_addr(char *);
+#endif
+#if !defined(HAVE_INET_NTOA) || !defined(HAVE_INET_NETOF)
+#include <netinet/in.h>
+#endif
+#if !defined( HAVE_INET_NTOA )
+extern char *inet_ntoa(struct in_addr);
+#endif
+#if !defined( HAVE_INET_NETOF )
+extern int  inet_netof(struct in_addr);
+#endif
+extern char *myctime(time_t);
+extern char *strtoken(char **, char *, char *);
+#if !defined(HAVE_MINMAX)
+#ifndef MAX
+#define MAX(a, b)      ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b)      ((a) < (b) ? (a) : (b))
+#endif
+#endif /* !HAVE_MINMAX */
+#define DupString(x,y) do{x=MyMalloc(strlen(y)+1);(void)strcpy(x,y);}while(0)
+extern unsigned char tolowertab[];
+#define ToLower(c) (tolowertab[(u_char)(c)])
+extern unsigned char touppertab[];
+#define ToUpper(c) (touppertab[(u_char)(c)])
+extern unsigned char char_atribs[];
+#define PRINT 1
+#define CNTRL 2
+#define ALPHA 4
+#define PUNCT 8
+#define DIGIT 16
+#define SPACE 32
+#define        IsCntrl(c) (char_atribs[(u_char)(c)]&CNTRL)
+#define IsAlpha(c) (char_atribs[(u_char)(c)]&ALPHA)
+#define IsSpace(c) (char_atribs[(u_char)(c)]&SPACE)
+#define IsLower(c) ((char_atribs[(u_char)(c)]&ALPHA) && ((u_char)(c) > 0x5f))
+#define IsUpper(c) ((char_atribs[(u_char)(c)]&ALPHA) && ((u_char)(c) < 0x60))
+#define IsDigit(c) (char_atribs[(u_char)(c)]&DIGIT)
+#define        IsXDigit(c) (isdigit(c) || 'a' <= (c) && (c) <= 'f' || \
+                      'A' <= (c) && (c) <= 'F')
+#define IsAlnum(c) (char_atribs[(u_char)(c)]&(DIGIT|ALPHA))
+#define IsPrint(c) (char_atribs[(u_char)(c)]&PRINT)
+#define IsAscii(c) ((u_char)(c) >= 0 && (u_char)(c) <= 0x7f)
+#define IsGraph(c) ((char_atribs[(u_char)(c)]&PRINT) && ((u_char)(c) != 0x32))
+#define IsPunct(c) (!(char_atribs[(u_char)(c)]&(CNTRL|ALPHA|DIGIT)))
+extern struct SLink *find_user_link();
+#endif /* common_include */
diff --git a/include/config.h b/include/config.h
new file mode 100644 (file)
index 0000000..341b2cb
--- /dev/null
@@ -0,0 +1,873 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/config.h
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* $Id: config.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#ifndef        __config_include__
+#define        __config_include__
+
+#include "setup.h"
+#include "defs.h"
+
+
+/* READ THIS FIRST BEFORE EDITING!!!
+ *
+ * Most people will have no real reason to edit config.h, with the
+ * exception of perhaps turning off certain things such as throttling
+ * or flood protection options.  Most of the stuff you will have to edit
+ * can be found in the ircd.conf - see doc/reference.conf for information
+ * on that.
+ */
+
+/*
+ * NO_DEFAULT_INVISIBLE - clients not +i by default When defined, your
+ * users will not automatically be attributed with user mode "i" (i ==
+ * invisible). Invisibility means people dont showup in WHO or NAMES
+ * unless they are on the same channel as you.
+ */
+#undef NO_DEFAULT_INVISIBLE
+
+/*
+ * USE_SYSLOG - log errors and such to syslog() If you wish to have the
+ * server send 'vital' messages about server through syslog, define
+ * USE_SYSLOG. Only system errors and events critical to the server are
+ * logged although if this is defined with FNAME_USERLOG, syslog() is
+ * used instead of the above file. It is not recommended that this
+ * option is used unless you tell the system administrator beforehand
+ * and obtain their permission to send messages to the system log
+ * files.
+ * 
+ * IT IS STRONGLY RECOMMENDED THAT YOU *DO* USE SYSLOG.  Many fatal ircd
+ * errors are only logged to syslog.
+ */
+#ifdef HAVE_SYSLOG_H
+#define        USE_SYSLOG
+/*
+ * SYSLOG_KILL SYSLOG_SQUIT SYSLOG_CONNECT SYSLOG_USERS SYSLOG_OPER If
+ * you use syslog above, you may want to turn some (none) of the
+ * spurious log messages for KILL,SQUIT,etc off.
+ */
+#undef SYSLOG_KILL             /* log all operator kills */
+#undef SYSLOG_SQUIT            /* log all remote squits */
+#undef SYSLOG_CONNECT          /* log remote connect messages */
+#undef SYSLOG_USERS            /* send userlog stuff to syslog */
+#undef SYSLOG_OPER             /* log all users who successfully oper */
+#undef  SYSLOG_BLOCK_ALLOCATOR /* debug block allocator */
+
+/*
+ * LOG_FACILITY - facility to use for syslog() Define the facility you
+ * want to use for syslog().  Ask your sysadmin which one you should
+ * use.
+ */
+#define LOG_FACILITY LOG_LOCAL4
+#endif /* HAVE_SYSLOG_H  */
+
+/* Defaults for things in option block of ircd.conf */
+
+/* WGMON notices are sent to users to warn them about the proxy scans. */
+#define DEFAULT_WGMON_URL "http://kline.dal.net/proxy/"
+#define DEFAULT_WGMON_HOST "some.bot.host"
+
+/* Hostmasking address */
+#define DEFAULT_STAFF_ADDRESS "staff.dalnet"
+
+/* Sent to users in 001 and 005 numerics */
+#define DEFAULT_NETWORK "DALnet"
+
+/* used for services aliases */
+#define DEFAULT_SERVICES_NAME "services.dal.net"
+#define DEFAULT_STATS_NAME "stats.dal.net"
+
+/* sent to users when they have been klined from the server */
+#define DEFAULT_NKLINE_ADDY "admin@badly.configured.server"
+#define DEFAULT_LKLINE_ADDY "admin@badly.configured.server"
+
+/* Sent to users when they encounter mode +R */
+#define DEFAULT_NS_REGISTER_URL "http://docs.dal.net/docs/nsemail.html"
+
+/* self explanitory */
+#define DEFAULT_MAXCHANNELSPERUSER 10
+
+/* Default difference in time sync between servers before we complain */
+#define DEFAULT_TSMAXDELTA 120
+#define DEFAULT_TSWARNDELTA 15
+
+/* default clone limits */
+#define DEFAULT_LOCAL_IP_CLONES    10
+#define DEFAULT_LOCAL_IP24_CLONES  60
+#define DEFAULT_GLOBAL_IP_CLONES   25
+#define DEFAULT_GLOBAL_IP24_CLONES 150
+
+/* 
+ * HIDEULINEDSERVS 
+ * Define this if you want to hide the location of U:lined servers (and 
+ * then clients on them) from nonopers. With this defined, no non-oper 
+ * should be able to find out which server the U:lined server is connected
+ * to. If you are connected to the main DALnet network, you MUST have this
+ * enabled.
+ */
+#define HIDEULINEDSERVS 1
+
+#define THROTTLE_ENABLE /* enable throttling, see below */
+
+/* File names
+ * the server will look for these files
+ */
+#define        MPATH   "ircd.motd"
+#define        SMPATH  "ircd.smotd"
+#define        LPATH   "ircd.log"
+#define        PPATH   "ircd.pid"
+#define HPATH  "opers.txt"
+
+
+/* Services Definitions */
+#define CHANSERV "ChanServ"
+#define NICKSERV "NickServ"
+#define MEMOSERV "MemoServ"
+#define ROOTSERV "RootServ"
+#define OPERSERV "OperServ"
+#define STATSERV "StatServ"
+#define HELPSERV "HelpServ"
+
+/*
+ * DENY_SERVICES_MSGS
+ * Define this to cause PRIVMSG <service> to be rejected with numeric 487,
+ * explaining that "/msg <service>" is no longer supported, and to use
+ * "/msg <service>@<services_name>" or "/<service>" instead.
+ */
+#undef DENY_SERVICES_MSGS
+
+/*
+ * FNAME_USERLOG and FNAME_OPERLOG - logs of local USERS and OPERS
+ * Define this filename to maintain a list of persons who log into this
+ * server. Logging will stop when the file does not exist. Logging will
+ * be disable also if you do not define this. FNAME_USERLOG just logs
+ * user connections, FNAME_OPERLOG logs every successful use of /oper.
+ * These are either full paths or files within DPATH.
+ * 
+ */
+
+#undef FNAME_USERLOG
+#undef FNAME_OPERLOG
+
+/*
+#define FNAME_USERLOG "/usr/local/ircd/users"  
+#define FNAME_OPERLOG "/usr/local/ircd/opers"
+*/
+
+/* define this if you want to support non-noquit servers.  handy for
+ * services that are not noquit compliant.
+ */
+#undef NOQUIT
+
+/*
+ * DEFAULT_KLINE_TIME
+ *
+ * Define this to the default time for a kline (in minutes) for klines with
+ * unspecified times. undefine this for all klines with unspecified times
+ * to be perm. (if defined, a kline with zero time will be perm). -- lucas
+ */
+#define DEFAULT_KLINE_TIME 30
+
+/*
+ * Pretty self explanatory: These are shown in server notices and to the 
+ * recipient of a "you are banned" message.
+ */
+#define LOCAL_BAN_NAME "k-line"
+#define NETWORK_BAN_NAME "autokill"
+#define LOCAL_BANNED_NAME "k-lined"
+#define NETWORK_BANNED_NAME "autokilled"
+
+/*
+ * LOCKFILE - Exclusive use of ircd.conf and kline.conf during writes
+ * 
+ * This creates a lockfile prior to writes to ircd.conf or kline.conf, and
+ * can be used in conjunction with viconf (included in the tools
+ * directory). This prevents loss of data when klines are added while
+ * someone is manually editting the file.  File writes will be retried
+ * at the next KLINE, ZLINE, REHASH, or after CHECK_PENDING_KLINES
+ * minutes have elapsed.
+ * 
+ * If you do not wish to use this feature, leave LOCKFILE #undef
+ */
+#define LOCKFILE "/tmp/ircd.conf.lock"
+#define        CHECK_PENDING_KLINES    10      /* in minutes */
+
+/*
+ * RFC1035_ANAL Defining this causes ircd to reject hostnames with
+ * non-compliant chars. undef'ing it will allow hostnames with _ or /
+ * to connect
+ */
+#define RFC1035_ANAL
+
+/*
+ * IGNORE_FIRST_CHAR - define this for NO_MIXED_CASE if you wish to
+ * ignore the first character
+ */
+#define IGNORE_FIRST_CHAR
+
+/*
+ * USERNAMES_IN_TRACE - show usernames in trace Define this if you want
+ * to see usernames in /trace.
+ */
+#define USERNAMES_IN_TRACE
+
+/*
+ * DO_IDENTD - check identd if you undefine this, ircd will never check
+ * identd regardless of @'s in I:lines.  You must still use @'s in your
+ * I: lines to get ircd to do ident lookup even if you define this.
+ */
+#define DO_IDENTD
+
+/* IDENTD_COMPLAIN - yell at users that don't have identd installed */
+#undef IDENTD_COMPLAIN
+
+/*
+ * MOTD_WAIT - minimum seconds between use of MOTD, INFO, HELP, LINKS * 
+ * before max use count is reset * -Dianora 
+ */
+#define MOTD_WAIT 10
+
+/* MOTD_MAX * max use count before delay above comes into effect */
+#define MOTD_MAX 3
+
+/* SHOW_HEADERS - Shows messages like "looking up hostname" */
+#define SHOW_HEADERS
+
+/*
+ * NO_OPER_FLOOD - disable flood control for opers define this to
+ * remove flood control for opers
+ */
+#define NO_OPER_FLOOD
+
+/* SHOW_UH - show the user@host everywhere */
+#define SHOW_UH
+#ifdef SHOW_UH
+#define USERNAMES_IN_TRACE
+#endif
+
+/*
+ * SHOW_INVISIBLE_LUSERS - show invisible clients in LUSERS As defined
+ * this will show the correct invisible count for anyone who does
+ * LUSERS on your server. On a large net this doesnt mean much, but on
+ * a small net it might be an advantage to undefine it.
+ */
+#define        SHOW_INVISIBLE_LUSERS
+
+/*
+ * DEFAULT_HELP_MODE - default your opers to +h helper mode.  This
+ * is strongly recommended
+ */
+#define DEFAULT_HELP_MODE
+
+/*
+ * NICER_UMODENOTICE_SEPARATION
+ * By default, all usermode notices (+d, for instance) come as
+ * :servername NOTICE nick :*** Notice -- blah blah
+ * This makes them come as *** Debug, or *** Spy, etc.
+ */
+#define NICER_UMODENOTICE_SEPARATION
+
+/*
+ * MAXIMUM LINKS - max links for class 0 if no Y: line configured
+ * 
+ * This define is useful for leaf nodes and gateways. It keeps you from
+ * connecting to too many places. It works by keeping you from
+ * connecting to more than "n" nodes which you have C:blah::blah:6667
+ * lines for.
+ * 
+ * Note that any number of nodes can still connect to you. This only
+ * limits the number that you actively reach out to connect to.
+ * 
+ * Leaf nodes are nodes which are on the edge of the tree. If you want to
+ * have a backup link, then sometimes you end up connected to both your
+ * primary and backup, routing traffic between them. To prevent this,
+ * #define MAXIMUM_LINKS 1 and set up both primary and secondary with
+ * C:blah::blah:6667 lines. THEY SHOULD NOT TRY TO CONNECT TO YOU, YOU
+ * SHOULD CONNECT TO THEM.
+ * 
+ * Gateways such as the server which connects Australia to the US can do a
+ * similar thing. Put the American nodes you want to connect to in with
+ * C:blah::blah:6667 lines, and the Australian nodes with C:blah::blah
+ * lines. Have the Americans put you in with C:blah::blah lines. Then
+ * you will only connect to one of the Americans.
+ * 
+ * This value is only used if you don't have server classes defined, and a
+ * server is in class 0 (the default class if none is set).
+ * 
+ */
+#define MAXIMUM_LINKS 1
+
+/*
+ * IRCII_KLUDGE - leave it defined Define this if you want the server
+ * to accomplish ircII standard Sends an extra NOTICE in the beginning
+ * of client connection
+ */
+#undef IRCII_KLUDGE
+
+/*
+ * CLIENT_FLOOD - client excess flood threshold this controls the
+ * number of bytes the server will allow a client to send to the server
+ * without processing before disconnecting the client for flooding it.
+ * Values greater than 8000 make no difference to the server.
+ */
+#define        CLIENT_FLOOD    2560
+
+/*
+ * CMDLINE_CONFIG - allow conf-file to be specified on command line
+ * NOTE: defining CMDLINE_CONFIG and installing ircd SUID or SGID is a
+ * MAJOR security problem - they can use the "-f" option to read any
+ * files that the 'new' access lets them.
+ */
+#define        CMDLINE_CONFIG
+
+/*
+ * FAILED_OPER_NOTICE - send a notice to all opers when someone tries
+ * to /oper and uses an incorrect password.
+ */
+#define FAILED_OPER_NOTICE
+
+/*
+ * ANTI_NICK_FLOOD - prevents nick flooding define if you want to block
+ * local clients from nickflooding
+ */
+#define ANTI_NICK_FLOOD
+
+/*
+ * defaults allow 4 nick changes in 20 seconds 
+ */
+#define MAX_NICK_TIME 20
+#define MAX_NICK_CHANGES 4
+
+/* NO_AWAY_FLUD
+ * reallow propregation of AWAY messages, but do not allow AWAY flooding
+ * I reccomend a max of 5 AWAY's in 3 Minutes
+ */
+#define NO_AWAY_FLUD
+
+#ifdef NO_AWAY_FLUD
+# define MAX_AWAY_TIME 180  /* time in seconds */
+# define MAX_AWAY_COUNT 5
+#endif
+
+/*
+ * UNKLINE - /quote unkline - remove klines on the fly if you choose to
+ * support this, an oper can do a /quote UNKLINE of an exact matching
+ * KLINE to remove the kline
+ */
+#define UNKLINE
+
+/*
+ * WARN_NO_NLINE Define this if you want ops to get noticed about
+ * "things" trying to connect as servers that don't have N: lines.
+ * Twits with misconfigured servers can get really annoying with
+ * enabled.
+ */
+#define WARN_NO_NLINE
+
+/*
+ * RIDICULOUS_PARANOIA_LEVEL
+ *
+ * This indicates the level of ridiculous paranoia the admin has.
+ * The settings are as follows:
+ *
+ * 0 - No hostmasking is available.
+ * 1 - All +A users can see the real IP.
+ * 2 - Local +A can see the real IP.
+ * 3 - Noone can see the real IP.  It is still logged.
+ *
+ * WARNING:
+ * Running levels above 1 on DALnet will result in your server being juped
+ * from the network if a security breech is suspected.
+ *
+ * If level 3 is selected, USE_SYSLOG must be defined.
+ */
+#define RIDICULOUS_PARANOIA_LEVEL 1
+#if (RIDICULOUS_PARANOIA_LEVEL==3)
+#ifndef USE_SYSLOG
+#error "USE_SYSLOG MUST BE DEFINED FOR LEVEL 3"
+#endif
+#endif
+
+/*
+ * Forward /quote help to HelpServ
+ *
+ * If defined, any /quote help requests from users sent to the ircd
+ * will forward the help message over to HelpServ if defined, as
+ * well as the default HelpServ topic request command. -srd
+ */
+#define HELP_FORWARD_HS
+#ifdef HELP_FORWARD_HS
+# define DEF_HELP_CMD "?"
+#endif
+
+/*
+ * For all of these options below, #define NETWORK_PARANOIA
+ * and leave the individual ones alone.
+ */
+#undef NETWORK_PARANOIA
+
+/*
+ * NO_USER_SERVERKILLS
+ * Users can't set mode +k
+ *
+ * NO_USER_OPERKILLS
+ * Users can't set mode +s
+ *
+ * NO_USER_STATS
+ * Users can't get /stats from anything
+ *
+ * NO_LOCAL_USER_STATS
+ * Local users can't get /stats from anything, each server does its own 
+ * checking (not recommended)
+ * No effect if NO_USER_STATS is defined 
+ *
+ * NO_USER_TRACE
+ * Users can't use TRACE
+ *
+ * NO_USER_OPERTARGETED_COMMANDS
+ * Users can't do /motd oper, /admin oper, /whois oper oper, 
+ * /whois server.* oper on any oper that is set +I 
+ * (see oper hiding section)
+ *
+ * HIDE_NUMERIC_SOURCE
+ * All numerics going out to local clients come from the local server
+ * Necessary for numerics from remote servers not giving information away
+ * 
+ * HIDE_KILL_ORIGINS
+ * All /kills appear, from a user standpoint, to come from HIDDEN_SERVER_NAME
+ * Note that NO_USER_OPERKILLS and NO_USER_SERVERKILLS must be defined for
+ * this to actually provide any security.
+ *
+ * HIDE_SPLIT_SERVERS
+ * Hide the names of servers during netsplits
+ *
+ * HIDE_SERVERMODE_ORIGINS
+ * Hide the origins of server modes (ie, in netjoins).
+ * (They will all come from me.name)
+ */
+#define NO_USER_SERVERKILLS
+/* #undef NO_USER_OPERKILLS */
+/* #undef NO_USER_STATS */
+/* #undef NO_LOCAL_USER_STATS */
+/* #undef NO_USER_TRACE */
+/* #undef NO_USER_OPERTARGETED_COMMANDS */
+/* #undef HIDE_NUMERIC_SOURCE */
+/* #undef HIDE_KILL_ORIGINS */
+/* #undef HIDE_SPLIT_SERVERS */
+/* #undef HIDE_SERVERMODE_ORIGINS */
+
+/***********************/
+/* OPER HIDING SECTION */
+/***********************/
+
+/* 
+ * ALLOW_HIDDEN_OPERS
+ * 
+ * Allow your opers to be set +I (hidden) -- required for the commands below
+ * If not defined, everything below in the oper hiding section must be 
+ * undefined.
+ */
+#undef ALLOW_HIDDEN_OPERS
+
+/*
+ * DEFAULT_MASKED_HIDDEN
+ * 
+ * Makes all your opers that hostmasked +I (hidden) by default
+ * ALLOW_HIDDEN_OPERS must be defined with this enabled.
+ */
+#undef DEFAULT_MASKED_HIDDEN
+
+/*
+ * ALL_OPERS_HIDDEN
+ * 
+ * Makes all your opers on a 'hidden' server by default (sets +I at /oper)
+ * ALLOW_HIDDEN_OPERS must be defined with this enabled.
+ * DEFAULT_MASKED_HIDDEN is reccommended with this enabled.
+ */
+#undef ALL_OPERS_HIDDEN
+
+/*
+ * FORCE_OPERS_HIDDEN
+ *
+ * Makes it so that all opers can't set -I (not hidden)
+ *
+ * Define ALL_OPERS_HIDDEN, DEFAULT_MASKED_HIDDEN, ALLOW_HIDDEN_OPERS
+ * with this as well, or things will not work properly
+ */
+#undef FORCE_OPERS_HIDDEN
+
+/*
+ * FORCE_EVERYONE_HIDDEN
+ *
+ * Makes it so that everyone on your server is set +I and can't set -I
+ * 
+ * Every other hidden option in the oper hiding section must be 
+ * defined as well.
+ */
+#undef FORCE_EVERYONE_HIDDEN
+
+/* 
+ * Show these for hidden opers, self explanatory
+ * DO NOT CHANGE ON A SERVER TO SERVER BASIS
+ * THESE ARE NETWORK-WIDE!
+ */
+#define HIDDEN_SERVER_NAME "*.dal.net"
+#define HIDDEN_SERVER_DESC "DALnet IRC Network"
+
+/***************************/
+/* END OPER HIDING SECTION */
+/***************************/
+
+#ifdef NETWORK_PARANOIA
+# define NO_USER_SERVERKILLS
+# define NO_USER_OPERKILLS
+# define NO_USER_STATS
+# define NO_USER_TRACE
+# define NO_USER_OPERTARGETED_COMMANDS
+# define HIDE_NUMERIC_SOURCE
+# define HIDE_KILL_ORIGINS
+# define HIDE_SPLIT_SERVERS
+# define HIDE_SERVERMODE_ORIGINS
+#endif
+
+/* EXEMPT_LISTS and INVITE_LISTS
+ * Written by Sedition, Feb.04
+ */
+#define EXEMPT_LISTS
+#define INVITE_LISTS
+
+/******************************************************************
+ * STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP
+ *
+ * You shouldn't change anything below this line, unless absolutely
+ * needed.
+ */
+
+/*
+ * PING_NAZI
+ *
+ * be nazi-ish about pings (re-check every client connect, 
+ * user registration, etc)
+ */
+#undef PING_NAZI
+
+/*
+ * ALWAYS_SEND_DURING_SPLIT
+ * on a large network, if your server is carrying large amounts of clients,
+ * and your server splits from the main network, the amount of allocated
+ * dbufs will skyrocket as buffers fill up with QUIT messages. This code
+ * attempts to combat this by sending out data whenever possible during a
+ * split. - lucas
+ */
+#define ALWAYS_SEND_DURING_SPLIT
+
+/* INITIAL_DBUFS - how many dbufs to preallocate */
+#define INITIAL_DBUFS 1024     /* preallocate 2 megs of dbufs */
+
+
+/* INITIAL_SBUFS_X - how many bytes of sbufs to preallocate */
+#define INITIAL_SBUFS_SMALL 2 * (1 << 20) /* 2 meg */
+#define INITIAL_SBUFS_LARGE 2 * (1 << 20) /* 2 meg */
+#define INITIAL_SBUFS_USERS 256           /* number of sbuf user structs to pool */
+
+/*
+ * MAXBUFFERS - increase socket buffers
+ * 
+ * Increase send & receive socket buffer up to 64k, keeps clients at 8K
+ * and only raises servers to 64K
+ */
+#define MAXBUFFERS
+
+/*
+ * PORTNUM - default port where ircd resides Port where ircd resides.
+ * NOTE: This *MUST* be greater than 1024 if you plan to run ircd under
+ * any other uid than root.
+ */
+#define PORTNUM 7000 /* 7000 for DALnet */
+
+/*
+ * NICKNAMEHISTORYLENGTH - size of WHOWAS array this defines the length
+ * of the nickname history.  each time a user changes nickname or signs
+ * off, their old nickname is added to the top of the list. NOTE: this
+ * is directly related to the amount of memory ircd will use whilst
+ * resident and running - it hardly ever gets swapped to disk!  Memory
+ * will be preallocated for the entire whowas array when ircd is
+ * started.
+ */
+#define NICKNAMEHISTORYLENGTH 2048
+
+/*
+ * TIMESEC - Time interval to wait and if no messages have been
+ * received, then check for PINGFREQUENCY and CONNECTFREQUENCY
+ */
+#define TIMESEC  5             /* Recommended value: 5 */
+
+/*
+ * MAXSENDQLENGTH - Max amount of internal send buffering Max amount of
+ * internal send buffering when socket is stuck (bytes)
+ */
+#define MAXSENDQLENGTH 5050000
+
+/*
+ * PINGFREQUENCY - ping frequency for idle connections If daemon
+ * doesn't receive anything from any of its links within PINGFREQUENCY
+ * seconds, then the server will attempt to check for an active link
+ * with a PING message. If no reply is received within (PINGFREQUENCY *
+ * 2) seconds, then the connection will be closed.
+ */
+#define PINGFREQUENCY    120   /* Recommended value: 120 */
+
+/*
+ * CONNECTFREQUENCY - time to wait before auto-reconencting If the
+ * connection to to uphost is down, then attempt to reconnect every
+ * CONNECTFREQUENCY  seconds.
+ */
+#define CONNECTFREQUENCY 600   /* Recommended value: 600 */
+
+/*
+ * HANGONGOODLINK and HANGONGOODLINK Often net breaks for a short time
+ * and it's useful to try to establishing the same connection again
+ * faster than CONNECTFREQUENCY would allow. But, to keep trying on bad
+ * connection, we require that connection has been open for certain
+ * minimum time (HANGONGOODLINK) and we give the net few seconds to
+ * steady (HANGONRETRYDELAY). This latter has to be long enough that
+ * the other end of the connection has time to notice it broke too.
+ * 1997/09/18 recommended values by ThemBones for modern Efnet
+ */
+
+#define HANGONRETRYDELAY 60    /* Recommended value: 30-60 seconds */
+#define HANGONGOODLINK 3600    /* Recommended value: 30-60 minutes */
+
+/*
+ * WRITEWAITDELAY - Number of seconds to wait for write to complete if
+ * stuck.
+ */
+#define WRITEWAITDELAY     10  /* Recommended value: 15 */
+
+/*
+ * CONNECTTIMEOUT - Number of seconds to wait for a connect(2) call to
+ * complete. NOTE: this must be at *LEAST* 10.  When a client connects,
+ * it has CONNECTTIMEOUT - 10 seconds for its host to respond to an
+ * ident lookup query and for a DNS answer to be retrieved.
+ */
+#define        CONNECTTIMEOUT  30      /* Recommended value: 30 */
+
+/*
+ * KILLCHASETIMELIMIT - Max time from the nickname change that still
+ * causes KILL automaticly to switch for the current nick of that user.
+ * (seconds)
+ */
+#define KILLCHASETIMELIMIT 90  /* Recommended value: 90 */
+
+/*
+ * FLUD - CTCP Flood Detection and Protection
+ * 
+ * This enables server CTCP flood detection and protection for local
+ * clients. It works well against fludnets and flood clones.  The
+ * effect of this code on server CPU and memory usage is minimal,
+ * however you may not wish to take the risk, or be fundamentally
+ * opposed to checking the contents of PRIVMSG's (though no privacy is
+ * breached).  This code is not useful for routing only servers (ie,
+ * HUB's with little or no local client base), and the hybrid team
+ * strongly recommends that you do not use FLUD with HUB. The following
+ * default thresholds may be tweaked, but these seem to work well.
+ */
+#define FLUD
+
+/*
+ * ANTI_SPAMBOT if ANTI_SPAMBOT is defined try to discourage spambots
+ * The defaults =should= be fine for the timers/counters etc. but you
+ * can play with them. -Dianora
+ * 
+ * Defining this also does a quick check whether the client sends us a
+ * "user foo x x :foo" where x is just a single char.  More often than
+ * not, it's a bot if it did. -ThemBones
+ */
+#define ANTI_SPAMBOT
+
+/*
+ * ANTI_SPAMBOT parameters, don't touch these if you don't understand
+ * what is going on.
+ * 
+ * if a client joins MAX_JOIN_LEAVE_COUNT channels in a row, but spends
+ * less than MIN_JOIN_LEAVE_TIME seconds on each one, flag it as a
+ * possible spambot. disable JOIN for it and PRIVMSG but give no
+ * indication to the client that this is happening. every time it tries
+ * to JOIN OPER_SPAM_COUNTDOWN times, flag all opers on local server.
+ * If a client doesn't LEAVE a channel for at least 2 minutes the
+ * join/leave counter is decremented each time a LEAVE is done
+ * 
+ */
+#define MIN_JOIN_LEAVE_TIME  60
+#define MAX_JOIN_LEAVE_COUNT  25
+#define OPER_SPAM_COUNTDOWN   5
+#define JOIN_LEAVE_COUNT_EXPIRE_TIME 120
+
+/*
+ * If ANTI_SPAMBOT_WARN_ONLY is #define'd Warn opers about possible
+ * spambots only, do not disable JOIN and PRIVMSG if possible spambot
+ * is noticed Depends on your policies.
+ */
+#undef ANTI_SPAMBOT_WARN_ONLY
+
+#ifdef FLUD
+# define FLUD_NUM         4    /* Number of flud messages to trip alarm */
+# define FLUD_TIME     3       /* Seconds in which FLUD_NUM msgs must occur */
+# define FLUD_BLOCK    15      /* Seconds to block fluds */
+#endif
+
+/*
+ * If the OS has SOMAXCONN use that value, otherwise Use the value in
+ * HYBRID_SOMAXCONN for the listen(); backlog try 5 or 25. 5 for AIX
+ * and SUNOS, 25 should work better for other OS's
+ */
+#define HYBRID_SOMAXCONN 25
+
+/*
+ * Throttling support:
+ * THROTTLE_ENABLE    - enable throttling code, if undefined, the functions
+ *                      will be empty.  runtime settable.
+ * THROTTLE_TRIGCOUNT - number of connections to triggle throttle action
+ * THROTTLE_TRIGTIME  - number of seconds in which THROTTLE_TRIGCOUNT must
+ *                      happen
+ * THROTTLE_RECORDTIME- length to keep records for each ip (since last connect
+                        from this ip)
+ * THROTTLE_HASHSIZE  - size of the throttle hashtable, also tuneable
+ *
+ * Recommended values: 3, 15, 1800.  3+ connections in 15 or less seconds will
+ * result in a connection throttle z:line.  These are also
+ * z: line time grows, pseudo-exponentially 
+ *  first zline : 2 minutes
+ *  second zline: 5 minutes
+ *  third zline : 15 minutes
+ *  fourth zline: 30 minutes
+ *  anything more is an hour
+ * tuneable at runtime.  -wd */
+/* part of options.h now #define THROTTLE_ENABLE */
+#define THROTTLE_TRIGCOUNT 3
+#define THROTTLE_TRIGTIME 15
+#define THROTTLE_RECORDTIME 1800
+#define THROTTLE_HASHSIZE 25147
+
+/*
+ * Message-throttling support.
+ * MSG_TARGET_LIMIT: if defined, imposes limits on message targets
+ * MSG_TARGET_MIN: initial number of message targets allowed (recommend 5 or less)
+ * MSG_TARGET_MAX: maximum number of message targets stored (recommend 5 or
+ *                 less)
+ * MSG_TARGET_MINTOMAXTIME: number of seconds a user must be online
+ *                          before given MSG_TARGET_MAX targets
+ * MSG_TARGET_TIME: time before message targets expire (this is what you should
+ *                  tweak)
+ */
+
+#define MSG_TARGET_LIMIT
+#define MSG_TARGET_MIN  5
+#define MSG_TARGET_MAX  8 /* MUST BE >= MSG_TARGET_MIN!!! */
+#define MSG_TARGET_MINTOMAXTIME 300
+#define MSG_TARGET_TIME 45
+
+/*
+ * Channel joining rate-throttling support
+ *
+ * DEFAULT_JOIN_NUM:  number of joins to allow, network-wide, in a period of
+ *                    DEFAULT_JOIN_TIME seconds.
+ * DEFAULT_JOIN_TIME: time to collect joins.
+ * JOINRATE_SERVER_ONLY: Only let servers/U: lined things set +j.
+ *                       KEEP THIS IF USING A NETWORK WITH PRE-1.4.36 SERVERS!
+ */
+/* defaults are very forgiving. */
+#define DEFAULT_JOIN_NUM  8
+#define DEFAULT_JOIN_TIME 4
+#undef JOINRATE_SERVER_ONLY
+
+/* Debugging configs */
+
+#undef DNS_DEBUG
+
+/*
+ * DEBUGMODE is used mostly for internal development, it is likely to
+ * make your client server very sluggish. You usually shouldn't need
+ * this. -Dianora
+ *
+ * Currently, DEBUGMODE is pretty much useless.
+ * Don't use it. - lucas
+ */
+#undef  DEBUGMODE              /* define DEBUGMODE to enable */
+#undef DUMP_DEBUG
+
+/* DONT_CHECK_QLINE_REMOTE
+ * Don't check for Q:lines on remote clients.  We can't do anything
+ * if a remote client is using a nick q:lined locally, so
+ * why check?  If you don't care about the wasted CPU, and you're
+ * curious, feel free to #define this.  I recommend you don't
+ * on a client server unless it's got a lot of power.
+ * -wd */
+#define DONT_CHECK_QLINE_REMOTE
+
+/* ------------------------- END CONFIGURATION SECTION -------------------- */
+#ifdef APOLLO
+#define RESTARTING_SYSTEMCALLS
+#endif /*
+        * read/write are restarted after signals
+        * defining this 1, gets siginterrupt call
+        * compiled, which attempts to remove this
+        * behaviour (apollo sr10.1/bsd4.3 needs this) 
+        */
+
+#define HELPFILE HPATH
+#define MOTD MPATH
+#define SHORTMOTD SMPATH
+#define IRCD_PIDFILE PPATH
+
+/* enforce a minimum, even though it'll probably break at runtime */
+#if (MAXCONNECTIONS < 20)
+# undef MAXCONNECTIONS
+# define MAXCONNECTIONS 20
+#endif
+
+#if (MAXCONNECTIONS > 1000)
+# define MAX_BUFFER (MAXCONNECTIONS / 100)
+#else
+# define MAX_BUFFER 10
+#endif
+
+#define MAX_ACTIVECONN (MAXCONNECTIONS - MAX_BUFFER)
+
+#if defined(CLIENT_FLOOD) && ((CLIENT_FLOOD > 8000) || (CLIENT_FLOOD < 512))
+#error CLIENT_FLOOD needs redefining.
+#endif
+
+#if !defined(CLIENT_FLOOD)
+#error CLIENT_FLOOD undefined.
+#endif
+
+#if defined(DEBUGMODE) || defined(DNS_DEBUG)
+extern void debug(int level, char *pattern, ...);
+#define Debug(x) debug x
+#define LOGFILE LPATH
+#else
+#define Debug(x) ;
+#define LOGFILE "/dev/null"
+#endif
+
+#define CONFIG_H_LEVEL_183
+#endif                         /* __config_include__ */
diff --git a/include/confparse.h b/include/confparse.h
new file mode 100644 (file)
index 0000000..0f6b3b8
--- /dev/null
@@ -0,0 +1,330 @@
+/* Bahamut IRCd - include/confparse.h
+ * Copyright (c) 2004, Aaron Wiebe
+ *
+ * Apply the GPL here.
+ */
+
+/* $Id: confparse.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+/* our structures */
+
+typedef struct TopConf tConf;
+typedef struct SubConf sConf;
+typedef struct ConfVar cVar;
+
+/* our top level conf options */
+struct TopConf
+{
+    char *tok;              /* our token string             */
+    unsigned int   flag;    /* our token flag               */
+    unsigned int   nest;    /* tokens we allow to nest here */
+    sConf       *subtok;  /* sub-tokens allowed in here   */
+    int         (*func) (); /* function to call to add this */
+};
+
+/* sub-token options */
+struct SubConf
+{
+    char *tok;              /* our token string             */
+    unsigned long  flag;    /* our token flag               */
+    unsigned int   var;     /* our variable type            */
+};
+
+struct ConfVar
+{
+    sConf   *type;
+    char    *value;
+    int      loaded;        /* 1 - identified.  
+                             * 2 - variable loaded
+                             * 3 - delimited cleared */
+};
+
+/* tokens allowing subtokens */
+
+#define CONFT_GLOBAL    "GLOBAL"
+#define CONFF_GLOBAL                0x000001
+#define CONFT_OPTIONS   "OPTIONS"
+#define CONFF_OPTIONS               0x000002
+#define CONFT_CLASS     "CLASS"
+#define CONFF_CLASS                 0x000004
+#define CONFT_ALLOW     "ALLOW"
+#define CONFF_ALLOW                 0x000008
+#define CONFT_OPER      "OPER"
+#define CONFF_OPER                  0x000010
+#define CONFT_CONNECT   "CONNECT"
+#define CONFF_CONNECT               0x000020
+#define CONFT_RESTRICT  "RESTRICT"
+#define CONFF_RESTRICT              0x000040
+#define CONFT_SUPER     "SUPER"
+#define CONFF_SUPER                 0x000080
+#define CONFT_KILL      "KILL"
+#define CONFF_KILL                  0x000100
+#define CONFT_ADMIN     "ADMIN"
+#define CONFF_ADMIN                 0x000200
+#define CONFT_PORT      "PORT"
+#define CONFF_PORT                  0x000400
+#define CONFT_MODULES   "MODULES"
+#define CONFF_MODULES               0x000800
+
+/* subtokens */
+
+#define SCONFT_NAME     "NAME"
+#define SCONFF_NAME                 0x000001
+#define SCONFT_INFO     "INFO"
+#define SCONFF_INFO                 0x000002
+#define SCONFT_DPASS    "DPASS"
+#define SCONFF_DPASS                0x000004
+#define SCONFT_RPASS    "RPASS"
+#define SCONFF_RPASS                0x000008
+#define SCONFT_PINGFREQ "PINGFREQ"
+#define SCONFF_PINGFREQ             0x000010
+#define SCONFT_CONNFREQ "CONNFREQ"
+#define SCONFF_CONNFREQ             0x000020
+#define SCONFT_MAXUSERS "MAXUSERS"
+#define SCONFF_MAXUSERS             0x000040
+#define SCONFT_MAXSENDQ "MAXSENDQ"
+#define SCONFF_MAXSENDQ             0x000080
+#define SCONFT_CLASS     "CLASS"
+#define SCONFF_CLASS                0x000100
+#define SCONFT_HOST     "HOST"
+#define SCONFF_HOST                 0x000200
+#define SCONFT_PORT     "PORT"
+#define SCONFF_PORT                 0x000800
+#define SCONFT_PASSWD   "PASSWD"
+#define SCONFF_PASSWD               0x001000
+#define SCONFT_ACCESS   "ACCESS"
+#define SCONFF_ACCESS               0x002000
+#define SCONFT_BIND     "BIND"
+#define SCONFF_BIND                 0x004000
+#define SCONFT_APASSWD  "APASSWD"
+#define SCONFF_APASSWD              0x008000
+#define SCONFT_CPASSWD  "CPASSWD"
+#define SCONFF_CPASSWD              0x010000
+#define SCONFT_FLAGS    "FLAGS"
+#define SCONFF_FLAGS                0x020000
+#define SCONFT_REASON   "REASON"
+#define SCONFF_REASON               0x040000
+#define SCONFT_TYPE     "TYPE"
+#define SCONFF_TYPE                 0x080000
+#define SCONFT_MASK     "MASK"
+#define SCONFF_MASK                 0x100000
+#define SCONFT_IPMASK   "IPMASK"
+#define SCONFF_IPMASK               0x200000
+
+#define SCONFF_STRING               0x800000    /* allow freeform strings */
+
+/* subtoken aliases */
+
+#define SCONFT_MAXCLONE     "MAXCLONES"
+#define SCONFT_MAXLINKS     "MAXLINKS"
+
+/* these are the strings for options ONLY */
+
+#define OPTT_NETNAME    "NETWORK_NAME"
+#define OPTF_NETNAME                0x000002
+#define OPTT_STAFFADDY  "STAFF_ADDRESS"
+#define OPTF_STAFFADDY              0x000004
+#define OPTT_SERVNAME   "SERVICES_NAME"
+#define OPTF_SERVNAME               0x000010
+#define OPTT_MAXCHAN    "MAXCHANNELS"
+#define OPTF_MAXCHAN                0x000020
+#define OPTT_WGMONHOST  "WGMONHOST"
+#define OPTF_WGMONHOST              0x000040
+#define OPTT_WGMONURL   "WGMONURL"
+#define OPTF_WGMONURL               0x000080
+#define OPTT_NKLINEADDY "NETWORK_KLINE"
+#define OPTF_NKLINEADDY             0x000100
+#define OPTT_LKLINEADDY "LOCAL_KLINE"
+#define OPTF_LKLINEADDY             0x000200
+#define OPTT_CRYPTPASS  "CRYPT_OPER_PASS"
+#define OPTF_CRYPTPASS              0x000400
+#define OPTT_SMOTD      "SHORT_MOTD"
+#define OPTF_SMOTD                  0x000800
+#define OPTT_SERVTYPE   "SERVTYPE"
+#define OPTF_SERVTYPE               0x001000
+#define OPTT_STATSNAME  "STATS_NAME"
+#define OPTF_STATSNAME              0x002000
+#define OPTT_TSMAXDELTA "TS_MAX_DELTA"
+#define OPTF_TSMAXDELTA             0x004000
+#define OPTT_TSWARNDELTA "TS_WARN_DELTA"
+#define OPTF_TSWARNDELTA            0x008000
+#define OPTT_NSREGURL   "NSHELPURL"
+#define OPTF_NSREGURL               0x010000
+#define OPTT_SHOWLINKS  "SHOW_LINKS"
+#define OPTF_SHOWLINKS              0x020000
+#define OPTT_SPLITOPOK  "ALLOW_SPLIT_OPS"
+#define OPTF_SPLITOPOK              0x040000
+#define OPTT_LCLONES    "LOCAL_CLONES"
+#define OPTF_LCLONES                0x080000
+#define OPTT_GCLONES    "GLOBAL_CLONES"
+#define OPTF_GCLONES                0x100000
+
+/* module block definitions */
+
+#define MBTT_PATH       "PATH"
+#define MBTF_PATH                   0x0001
+#define MBTT_AUTOLOAD   "AUTOLOAD"
+#define MBTF_AUTOLOAD               0x0002
+#define MBTT_OPTLOAD    "OPTLOAD"
+#define MBTF_OPTLOAD                0x0004
+
+/* our variable types */
+
+#define VARTYPE_INT     0x0001  /* integers             */
+#define VARTYPE_STRING  0x0002  /* freeform strings     */
+#define VARTYPE_NAME    0x0004  /* non-free name        */
+#define VARTYPE_NONE    0x0008  /* doesnt take any var  */
+
+/* functions for parsing variables into appropriate variables */
+
+#ifdef CONF_TABS
+
+sConf confglobtab[] =
+{
+    {SCONFT_NAME, SCONFF_NAME, VARTYPE_NAME},
+    {SCONFT_INFO, SCONFF_INFO, VARTYPE_STRING},
+    {SCONFT_DPASS, SCONFF_DPASS, VARTYPE_NAME},
+    {SCONFT_RPASS, SCONFF_RPASS, VARTYPE_NAME},
+    {(char *) 0, 0, 0}
+};
+
+sConf confopttab[] =
+{
+    {OPTT_NETNAME, OPTF_NETNAME, VARTYPE_NAME},
+    {OPTT_SERVNAME, OPTF_SERVNAME, VARTYPE_NAME},
+    {OPTT_STATSNAME, OPTF_STATSNAME, VARTYPE_NAME},
+    {OPTT_MAXCHAN, OPTF_MAXCHAN, VARTYPE_INT},
+    {OPTT_WGMONHOST, OPTF_WGMONHOST, VARTYPE_NAME},
+    {OPTT_WGMONURL, OPTF_WGMONURL, VARTYPE_NAME},
+    {OPTT_NKLINEADDY, OPTF_NKLINEADDY, VARTYPE_NAME},
+    {OPTT_LKLINEADDY, OPTF_LKLINEADDY, VARTYPE_NAME},
+    {OPTT_CRYPTPASS, OPTF_CRYPTPASS, VARTYPE_NONE},
+    {OPTT_SMOTD, OPTF_SMOTD, VARTYPE_NONE},
+    {OPTT_SERVTYPE, OPTF_SERVTYPE, VARTYPE_NAME},
+    {OPTT_TSMAXDELTA, OPTF_TSMAXDELTA, VARTYPE_INT},
+    {OPTT_TSWARNDELTA, OPTF_TSWARNDELTA, VARTYPE_INT},
+    {OPTT_STAFFADDY, OPTF_STAFFADDY, VARTYPE_NAME},
+    {OPTT_NSREGURL, OPTF_NSREGURL, VARTYPE_NAME},
+    {OPTT_SHOWLINKS, OPTF_SHOWLINKS, VARTYPE_NONE},
+    {OPTT_SPLITOPOK, OPTF_SPLITOPOK, VARTYPE_NONE},
+    {OPTT_LCLONES, OPTF_LCLONES, VARTYPE_NAME},
+    {OPTT_GCLONES, OPTF_GCLONES, VARTYPE_NAME},
+    {(char *) 0, 0, 0}
+};
+
+sConf confmoduletab[] =
+{
+    {MBTT_PATH, MBTF_PATH, VARTYPE_NAME},
+    {MBTT_AUTOLOAD, MBTF_AUTOLOAD, VARTYPE_NAME},
+    {MBTT_OPTLOAD, MBTF_OPTLOAD, VARTYPE_NAME},
+    {(char *) 0, 0, 0}
+};
+
+sConf confclasstab[] =
+{
+    {SCONFT_NAME, SCONFF_NAME, VARTYPE_NAME},
+    {SCONFT_PINGFREQ, SCONFF_PINGFREQ, VARTYPE_INT},
+    {SCONFT_CONNFREQ, SCONFF_CONNFREQ, VARTYPE_INT},
+    {SCONFT_MAXCLONE, SCONFF_CONNFREQ, VARTYPE_NAME},
+    {SCONFT_MAXUSERS, SCONFF_MAXUSERS, VARTYPE_INT},
+    {SCONFT_MAXLINKS, SCONFF_MAXUSERS, VARTYPE_INT},
+    {SCONFT_MAXSENDQ, SCONFF_MAXSENDQ, VARTYPE_INT},
+    {(char *) 0, 0, 0}
+};
+
+sConf confallowtab[] =
+{
+    {SCONFT_HOST, SCONFF_HOST, VARTYPE_NAME},
+    {SCONFT_IPMASK, SCONFF_IPMASK, VARTYPE_NAME},
+    {SCONFT_PORT, SCONFF_PORT, VARTYPE_INT},
+    {SCONFT_PASSWD, SCONFF_PASSWD, VARTYPE_NAME},
+    {SCONFT_CLASS, SCONFF_CLASS, VARTYPE_NAME},
+    {SCONFT_FLAGS, SCONFF_FLAGS, VARTYPE_NAME},
+    {(char *) 0, 0, 0}
+};
+
+sConf confopertab[] =
+{
+    {SCONFT_NAME, SCONFF_NAME, VARTYPE_NAME},
+    {SCONFT_HOST, SCONFF_HOST, VARTYPE_NAME},
+    {SCONFT_PASSWD, SCONFF_PASSWD, VARTYPE_NAME},
+    {SCONFT_ACCESS, SCONFF_ACCESS, VARTYPE_NAME},
+    {SCONFT_CLASS, SCONFF_CLASS, VARTYPE_NAME},
+    {(char *) 0, 0, 0}
+};
+
+sConf confconnecttab[] =
+{
+    {SCONFT_NAME, SCONFF_NAME, VARTYPE_NAME},
+    {SCONFT_HOST, SCONFF_HOST, VARTYPE_NAME},
+    {SCONFT_BIND, SCONFF_BIND, VARTYPE_NAME},
+    {SCONFT_APASSWD, SCONFF_APASSWD, VARTYPE_NAME},
+    {SCONFT_CPASSWD, SCONFF_CPASSWD, VARTYPE_NAME},
+    {SCONFT_FLAGS, SCONFF_FLAGS, VARTYPE_NAME},
+    {SCONFT_PORT, SCONFF_PORT, VARTYPE_INT},
+    {SCONFT_CLASS, SCONFF_CLASS, VARTYPE_NAME},
+    {(char *) 0, 0, 0}
+};
+
+sConf confrestricttab[] =
+{
+    {SCONFT_REASON, SCONFF_REASON, VARTYPE_STRING},
+    {SCONFT_TYPE, SCONFF_TYPE, VARTYPE_NAME},
+    {SCONFT_MASK, SCONFF_MASK, VARTYPE_NAME},
+    {(char *) 0, 0, 0}
+};
+
+sConf confkilltab[] =
+{
+    {SCONFT_REASON, SCONFF_REASON, VARTYPE_STRING},
+    {SCONFT_MASK, SCONFF_MASK, VARTYPE_NAME},
+    {(char *) 0, 0, 0}
+};
+
+sConf confporttab[] =
+{
+    {SCONFT_PORT, SCONFF_PORT, VARTYPE_INT},
+    {SCONFT_BIND, SCONFF_BIND, VARTYPE_NAME},
+    {SCONFT_IPMASK, SCONFF_IPMASK, VARTYPE_NAME},
+    {(char *) 0, 0, 0}
+};
+
+
+sConf confopentab[] =
+{
+    {0,SCONFF_STRING,0},
+    {(char *) 0, 0, 0}
+};
+
+extern int confadd_global(cVar **, int);
+extern int confadd_options(cVar **, int);
+extern int confadd_class(cVar **, int);
+extern int confadd_allow(cVar **, int);
+extern int confadd_oper(cVar **, int);
+extern int confadd_connect(cVar **, int);
+extern int confadd_restrict(cVar **, int);
+extern int confadd_super(cVar **, int);
+extern int confadd_kill(cVar **, int);
+extern int confadd_admin(cVar **, int);
+extern int confadd_port(cVar **, int);
+extern int confadd_modules(cVar **, int);
+
+struct TopConf tconftab[] = 
+{
+    {CONFT_GLOBAL, CONFF_GLOBAL, CONFF_ADMIN, confglobtab, confadd_global},
+    {CONFT_OPTIONS, CONFF_OPTIONS, 0, confopttab, confadd_options},
+    {CONFT_CLASS, CONFF_CLASS, 0, confclasstab, confadd_class},
+    {CONFT_ALLOW, CONFF_ALLOW, 0, confallowtab, confadd_allow},
+    {CONFT_OPER, CONFF_OPER, 0, confopertab, confadd_oper},
+    {CONFT_CONNECT, CONFF_CONNECT, 0, confconnecttab, confadd_connect},
+    {CONFT_RESTRICT, CONFF_RESTRICT, 0, confrestricttab, confadd_restrict},
+    {CONFT_KILL, CONFF_KILL, 0, confkilltab, confadd_kill},
+    {CONFT_ADMIN, CONFF_ADMIN, 0, confopentab, confadd_admin},
+    {CONFT_SUPER, CONFF_SUPER, 0, confopentab, confadd_super},
+    {CONFT_PORT, CONFF_PORT, 0, confporttab, confadd_port},
+    {CONFT_MODULES, CONFF_MODULES, 0, confmoduletab, confadd_modules},
+    {(char *) 0, 0, 0, 0}
+};
+
+
+#endif
diff --git a/include/dbuf.h b/include/dbuf.h
new file mode 100644 (file)
index 0000000..bd0c5c9
--- /dev/null
@@ -0,0 +1,135 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/dbuf.h
+ *   Copyright (C) 1990 Markku Savela
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/* $Id: dbuf.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+#ifndef INCLUDED_dbuf_h
+#define INCLUDED_dbuf_h
+#ifndef INCLUDED_config_h
+#include "config.h"
+#endif
+#ifndef INCLUDED_sys_types_h
+#include <sys/types.h>
+#define INCLUDED_sys_types_h
+#endif
+
+#define IsEol(c) (c==10||c==13)
+/*
+** dbuf is a collection of functions which can be used to
+** maintain a dynamic buffering of a byte stream.
+** Functions allocate and release memory dynamically as
+** required [Actually, there is nothing that prevents
+** this package maintaining the buffer on disk, either]
+*/
+struct DBufBuffer;
+
+/*
+** These structure definitions are only here to be used
+** as a whole, *DO NOT EVER REFER TO THESE FIELDS INSIDE
+** THE STRUCTURES*! It must be possible to change the internal
+** implementation of this package without changing the
+** interface.
+*/
+struct DBuf 
+{
+    struct DBufBuffer* head;   /* First data buffer, if length > 0 */
+    struct DBufBuffer* tail;   /* last data buffer, if length > 0 */
+    size_t             length; /* Current number of bytes stored */
+};
+
+extern int DBufCount;
+extern int DBufUsedCount;
+
+/*
+** dbuf_put
+**      Append the number of bytes to the buffer, allocating more
+**      memory as needed. Bytes are copied into internal buffers
+**      from users buffer.
+**
+**      returns > 0, if operation successfull
+**              < 0, if failed (due memory allocation problem)
+*/
+extern int dbuf_put(struct DBuf* dyn, const char* buf, size_t len);
+
+/*
+** dbuf_get
+**      Remove number of bytes from the buffer, releasing dynamic
+**      memory, if applicaple. Bytes are copied from internal buffers
+**      to users buffer.
+**
+**      returns the number of bytes actually copied to users buffer,
+**              if >= 0, any value less than the size of the users
+**              buffer indicates the dbuf became empty by this operation.
+**
+**              Return 0 indicates that buffer was already empty.
+**
+**              Negative return values indicate some unspecified
+**              error condition, rather fatal...
+*/
+extern size_t dbuf_get(struct DBuf* dbuf, char* buf, size_t len);
+
+/*
+** dbuf_map, dbuf_delete
+**      These functions are meant to be used in pairs and offer
+**      a more efficient way of emptying the buffer than the
+**      normal 'dbuf_get' would allow--less copying needed.
+**
+**      map     returns a pointer to a largest contiguous section
+**              of bytes in front of the buffer, the length of the
+**              section is placed into the indicated "long int"
+**              variable. Returns NULL *and* zero length, if the
+**              buffer is empty.
+**
+**      delete  removes the specified number of bytes from the
+**              front of the buffer releasing any memory used for them.
+**
+**      Example use (ignoring empty condition here ;)
+**
+**              buf = dbuf_map(&dyn, &count);
+**              <process N bytes (N <= count) of data pointed by 'buf'>
+**              dbuf_delete(&dyn, N);
+**
+**      Note:   delete can be used alone, there is no real binding
+**              between map and delete functions...
+*/
+/*
+ * dyn - Dynamic buffer header
+ * len - Return number of bytes accessible 
+ */
+extern char* dbuf_map(struct DBuf* dyn, size_t* len);
+extern void        dbuf_delete(struct DBuf* dyn, size_t len);
+
+/*
+** DBufLength
+**      Return the current number of bytes stored into the buffer.
+**      (One should use this instead of referencing the internal
+**      length field explicitly...)
+*/
+#define DBufLength(dyn) ((dyn)->length)
+
+/*
+** DBufClear
+**      Scratch the current content of the buffer. Release all
+**      allocated buffers and make it empty.
+*/
+#define DBufClear(dyn)  dbuf_delete((dyn), DBufLength(dyn))
+
+extern int  dbuf_getmsg(struct DBuf* dyn, char* buf, size_t len);
+extern void dbuf_init(void);
+extern void count_dbuf_memory(size_t* allocated, size_t* used);
+
+#endif /* INCLUDED_dbuf_h */
diff --git a/include/defs.h b/include/defs.h
new file mode 100644 (file)
index 0000000..da67a6c
--- /dev/null
@@ -0,0 +1,87 @@
+#if !defined( DEFS_H_INCLUDED )
+# define DEFS_H_INCLUDED 1
+
+/*
+ * None of the following should need to be changed by hand.  
+ */
+
+/* $Id: defs.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+# if !defined( HAVE_BCOPY )
+#      define bcopy(a,b,c) memcpy(b,a,c)
+# endif
+# if !defined( HAVE_BCMP )
+#      define bcmp memcmp
+# endif
+# if !defined( HAVE_BZERO )
+#      define bzero(a,b) memset(a,0,b)
+# endif
+
+# if defined( __osf__ )
+#      define OSF
+#      undef BSD
+#      include <sys/param.h>
+#      if !defined( BSD )
+#       define BSD
+#      endif
+# endif
+
+# if !defined(HAVE_DN_SKIPNAME)
+#      if defined(HAVE___DN_SKIPNAME)
+#       define dn_skipname __dn_skipname
+#      else
+#      error Could not find dn_skipname() or __dn_skipname()
+# endif
+#endif
+/*
+ * The following OS specific stuff is a compatibility kludge it would
+ * be nice to get rid of all of this eventually.
+ */
+#if defined(OS_SOLARIS2) && !defined( SOL20 )
+# define SOL20 1
+#endif
+
+#if defined( aix ) || defined( OS_AIX )
+# include <sys/machine.h>
+# if BYTE_ORDER == BIG_ENDIAN
+#      define BIT_ZERO_ON_LEFT
+# elif BYTE_ORDER == LOTTLE_ENDIAN
+#      define BIT_ZERO_ON_RIGHT
+# endif
+# define BSD_INCLUDES
+# if !defined( AIX )
+#      define AIX 1
+# endif
+# define USE_POLL 1            /* KLUGE - only define on AIX 4.x!! -cab */
+#endif
+
+#if defined( OS_MIPS )
+# undef SYSV
+# undef BSD
+# define BSD 1                 /* mips only works in a bsd43 environment */
+# if !defined( MIPS )
+#      define MIPS 1
+# endif
+#endif
+
+/* This code contributed by Rossi 'vejeta' Marcello <vjt@users.sourceforge.net>
+ * Originally in va_copy.h, however there wasnt much there, so i stuck it in
+ * here.  Thanks Rossi!  -epi
+ */
+
+/* va_copy hooks for IRCd */
+
+#if defined(__powerpc__)
+# if defined(__NetBSD__)
+#  define VA_COPY va_copy
+# elif defined(__FreeBSD__) || defined(__linux__)
+#  define VA_COPY __va_copy
+# endif
+#elif defined (__x86_64)
+# define VA_COPY __va_copy
+#else
+# define VA_COPY(x, y) x = y
+#endif
+
+
+#endif                         /* DEFS_H_INCLUDED */
diff --git a/include/dh.h b/include/dh.h
new file mode 100644 (file)
index 0000000..cacc26c
--- /dev/null
@@ -0,0 +1,146 @@
+#ifndef DH_HEADER
+extern int dh_init();
+extern void dh_end_session(void *);
+extern void *dh_start_session();
+extern char *dh_get_s_public(char *, int, void *);
+extern int dh_get_s_shared(char *, int *, void *);
+extern int dh_generate_shared(void *, char *);
+
+extern int dh_hexstr_to_raw(char *string, unsigned char *hexout, int *hexlen);
+
+extern void rc4_process_stream_to_buf(void *rc4_context, const unsigned char *istring,
+                               unsigned char *ostring, unsigned int stringlen);
+extern void rc4_process_stream(void *rc4_context, unsigned char *istring, unsigned int stringlen);
+extern void *rc4_initstate(unsigned char *key, int keylen);
+extern void rc4_destroystate(void *a);
+
+#else
+
+/* this stuff is only included for dh.c .. this is a kludge,
+ * but our header files are fucking disgusting anyway.
+ */
+
+struct session_info
+{
+    DH *dh;
+    char *session_shared;
+    int session_shared_length;
+};
+
+/*
+ * Do not change these unless
+ * you also change the prime below
+ */
+
+#define KEY_BITS 512
+
+#define RAND_BITS KEY_BITS
+#define RAND_BYTES (RAND_BITS / 8)
+#define RAND_BYTES_HEX ((RAND_BYTES * 2) + 1)
+
+#define PRIME_BITS 1024
+#define PRIME_BYTES (PRIME_BITS / 8)
+#define PRIME_BYTES_HEX ((PRIME_BYTES * 2) + 1)
+
+static BIGNUM *ircd_prime;
+static BIGNUM *ircd_generator;
+
+static char *hex_to_string[256] =
+{
+    "00", "01", "02", "03", "04", "05", "06", "07",
+    "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
+    "10", "11", "12", "13", "14", "15", "16", "17",
+    "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
+    "20", "21", "22", "23", "24", "25", "26", "27",
+    "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
+    "30", "31", "32", "33", "34", "35", "36", "37",
+    "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
+    "40", "41", "42", "43", "44", "45", "46", "47",
+    "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
+    "50", "51", "52", "53", "54", "55", "56", "57",
+    "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
+    "60", "61", "62", "63", "64", "65", "66", "67",
+    "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
+    "70", "71", "72", "73", "74", "75", "76", "77",
+    "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
+    "80", "81", "82", "83", "84", "85", "86", "87",
+    "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
+    "90", "91", "92", "93", "94", "95", "96", "97",
+    "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
+    "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
+    "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
+    "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7",
+    "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
+    "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7",
+    "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
+    "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+    "d8", "d9", "da", "db", "dc", "dd", "de", "df",
+    "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7",
+    "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
+    "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+    "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff"
+};
+
+/* This prime is taken from IPsec */
+
+static unsigned int dh_gen_1024 = 2;
+static unsigned char dh_prime_1024[] =
+{
+        0xF4, 0x88, 0xFD, 0x58, 0x4E, 0x49, 0xDB, 0xCD,
+        0x20, 0xB4, 0x9D, 0xE4, 0x91, 0x07, 0x36, 0x6B,
+        0x33, 0x6C, 0x38, 0x0D, 0x45, 0x1D, 0x0F, 0x7C,
+        0x88, 0xB3, 0x1C, 0x7C, 0x5B, 0x2D, 0x8E, 0xF6,
+        0xF3, 0xC9, 0x23, 0xC0, 0x43, 0xF0, 0xA5, 0x5B,
+        0x18, 0x8D, 0x8E, 0xBB, 0x55, 0x8C, 0xB8, 0x5D,
+        0x38, 0xD3, 0x34, 0xFD, 0x7C, 0x17, 0x57, 0x43,
+        0xA3, 0x1D, 0x18, 0x6C, 0xDE, 0x33, 0x21, 0x2C,
+        0xB5, 0x2A, 0xFF, 0x3C, 0xE1, 0xB1, 0x29, 0x40,
+        0x18, 0x11, 0x8D, 0x7C, 0x84, 0xA7, 0x0A, 0x72,
+        0xD6, 0x86, 0xC4, 0x03, 0x19, 0xC8, 0x07, 0x29,
+        0x7A, 0xCA, 0x95, 0x0C, 0xD9, 0x96, 0x9F, 0xAB,
+        0xD0, 0x0A, 0x50, 0x9B, 0x02, 0x46, 0xD3, 0x08,
+        0x3D, 0x66, 0xA4, 0x5D, 0x41, 0x9F, 0x9C, 0x7C,
+        0xBD, 0x89, 0x4B, 0x22, 0x19, 0x26, 0xBA, 0xAB,
+        0xA2, 0x5E, 0xC3, 0x55, 0xE9, 0x2F, 0x78, 0xC7
+};
+
+/*
+ * These are too big for ircd. :\
+static unsigned int dh_gen_2048 = 2;
+static unsigned char dh_prime_2048[] = {
+        0xF6, 0x42, 0x57, 0xB7, 0x08, 0x7F, 0x08, 0x17,
+        0x72, 0xA2, 0xBA, 0xD6, 0xA9, 0x42, 0xF3, 0x05,
+        0xE8, 0xF9, 0x53, 0x11, 0x39, 0x4F, 0xB6, 0xF1,
+        0x6E, 0xB9, 0x4B, 0x38, 0x20, 0xDA, 0x01, 0xA7,
+        0x56, 0xA3, 0x14, 0xE9, 0x8F, 0x40, 0x55, 0xF3,
+        0xD0, 0x07, 0xC6, 0xCB, 0x43, 0xA9, 0x94, 0xAD,
+        0xF7, 0x4C, 0x64, 0x86, 0x49, 0xF8, 0x0C, 0x83,
+        0xBD, 0x65, 0xE9, 0x17, 0xD4, 0xA1, 0xD3, 0x50,
+        0xF8, 0xF5, 0x59, 0x5F, 0xDC, 0x76, 0x52, 0x4F,
+        0x3D, 0x3D, 0x8D, 0xDB, 0xCE, 0x99, 0xE1, 0x57,
+        0x92, 0x59, 0xCD, 0xFD, 0xB8, 0xAE, 0x74, 0x4F,
+        0xC5, 0xFC, 0x76, 0xBC, 0x83, 0xC5, 0x47, 0x30,
+        0x61, 0xCE, 0x7C, 0xC9, 0x66, 0xFF, 0x15, 0xF9,
+        0xBB, 0xFD, 0x91, 0x5E, 0xC7, 0x01, 0xAA, 0xD3,
+        0x5B, 0x9E, 0x8D, 0xA0, 0xA5, 0x72, 0x3A, 0xD4,
+        0x1A, 0xF0, 0xBF, 0x46, 0x00, 0x58, 0x2B, 0xE5,
+        0xF4, 0x88, 0xFD, 0x58, 0x4E, 0x49, 0xDB, 0xCD,
+        0x20, 0xB4, 0x9D, 0xE4, 0x91, 0x07, 0x36, 0x6B,
+        0x33, 0x6C, 0x38, 0x0D, 0x45, 0x1D, 0x0F, 0x7C,
+        0x88, 0xB3, 0x1C, 0x7C, 0x5B, 0x2D, 0x8E, 0xF6,
+        0xF3, 0xC9, 0x23, 0xC0, 0x43, 0xF0, 0xA5, 0x5B,
+        0x18, 0x8D, 0x8E, 0xBB, 0x55, 0x8C, 0xB8, 0x5D,
+        0x38, 0xD3, 0x34, 0xFD, 0x7C, 0x17, 0x57, 0x43,
+        0xA3, 0x1D, 0x18, 0x6C, 0xDE, 0x33, 0x21, 0x2C,
+        0xB5, 0x2A, 0xFF, 0x3C, 0xE1, 0xB1, 0x29, 0x40,
+        0x18, 0x11, 0x8D, 0x7C, 0x84, 0xA7, 0x0A, 0x72,
+        0xD6, 0x86, 0xC4, 0x03, 0x19, 0xC8, 0x07, 0x29,
+        0x7A, 0xCA, 0x95, 0x0C, 0xD9, 0x96, 0x9F, 0xAB,
+        0xD0, 0x0A, 0x50, 0x9B, 0x02, 0x46, 0xD3, 0x08,
+        0x3D, 0x66, 0xA4, 0x5D, 0x41, 0x9F, 0x9C, 0x7C,
+        0xBD, 0x89, 0x4B, 0x22, 0x19, 0x26, 0xBA, 0xAB,
+        0xA2, 0x5E, 0xC3, 0x55, 0xE9, 0x32, 0x0B, 0x3B
+};
+ */
+
+#endif
diff --git a/include/fdlist.h b/include/fdlist.h
new file mode 100644 (file)
index 0000000..c0f2b37
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef _IRCD_DOG3_FDLIST
+#define _IRCD_DOG3_FDLIST
+
+/* $Id: fdlist.h,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+typedef struct fdstruct 
+{
+    int         entry[MAXCONNECTIONS + 2];
+    int         last_entry;
+#ifdef USE_KQUEUE
+    int                kqueue_fd;
+#endif
+} fdlist;
+
+void        addto_fdlist(int a, fdlist * b);
+void        delfrom_fdlist(int a, fdlist * b);
+void        init_fdlist(fdlist * b);
+
+#endif /* _IRCD_DOG3_FDLIST */
diff --git a/include/fds.h b/include/fds.h
new file mode 100644 (file)
index 0000000..7c84d08
--- /dev/null
@@ -0,0 +1,46 @@
+/* we recommend to not print,
+ * but if you want to, just switch undef and define
+#define FDSDEBUG
+ */
+#undef FDSDEBUG
+#ifdef FDSDEBUG
+#define fdfprintf(x, y, ...) if(isatty(2)) fprintf(x, y);
+#else
+#define fdfprintf(x, y, ...)
+#endif
+
+struct fd_callbackp {
+   void (*callback)(struct fd_callbackp *);
+   void *param;
+   int fd;  /* fd number                */
+   int rdf; /* fd is set for reading    */
+   int wrf; /* fd is set for writing    */
+};
+
+#define FDT_NONE      0
+#define FDT_AUTH      1
+#define FDT_RESOLVER  2
+#define FDT_CLIENT    3
+#define FDT_LISTENER  4
+#define FDT_CALLBACKP 5
+
+#define FDF_WANTREAD  0x01
+#define FDF_WANTWRITE 0x02
+
+void init_fds();
+
+void add_fd(int fd, int type, void *value);
+void del_fd(int fd);
+#define add_callback_fd(fds) add_fd((fds)->fd, FDT_CALLBACKP, (fds))
+#define del_callback_fd(fds) del_fd((fds)->fd)
+
+void get_fd_info(int fd, int *type, unsigned int *flags, void **value);
+void set_fd_flags(int fd, unsigned int flags);
+void unset_fd_flags(int fd, unsigned int flags);
+
+void set_fd_internal(int fd, void *ptr);
+void *get_fd_internal(int fd);
+
+void check_client_fd(aClient *cptr);
+
+void report_fds(aClient *cptr);
diff --git a/include/find.h b/include/find.h
new file mode 100644 (file)
index 0000000..60d7fbe
--- /dev/null
@@ -0,0 +1,37 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/find.h
+ *   Copyright (C) 2000 
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: find.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef        __find_include__
+#define __find_include__
+
+#define find_nickserver(a, b)   hash_find_nickserver(a, b)
+#define find_server(a, b)       hash_find_server(a, b)
+#define find_name(a, b)         hash_find_server(a, b)
+#define find_client(a, b)       hash_find_client(a, b)
+static inline aClient *find_person(char *name, aClient *cptr)
+{
+    aClient *acptr = find_client(name, cptr);
+    
+    return acptr ? (IsClient(acptr) ? acptr : cptr) : cptr;
+} 
+
+#endif /* __find_include__ */
diff --git a/include/h.h b/include/h.h
new file mode 100644 (file)
index 0000000..a14c10f
--- /dev/null
@@ -0,0 +1,338 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/h.h
+ *   Copyright (C) 1992 Darren Reed
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+ * "h.h". - Headers file.
+ * 
+ * Most of the externs and prototypes thrown in here to 'cleanup' things.
+ * -avalon
+ * 
+ * 
+ */
+
+/* $Id: h.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#include "send.h"
+#include "ircsprintf.h"
+
+extern int  R_do_dns, R_fin_dns, R_fin_dnsc, R_fail_dns, R_do_id,
+    R_fin_id, R_fail_id;
+
+extern aMotd *motd;
+extern struct tm *motd_tm;
+
+extern aMotd *shortmotd;
+extern aMotd *helpfile;                /* oper helpfile is a link list of aMotd */
+
+extern DLink *server_list;
+extern DLink *oper_list;
+extern DLink *listing_clients;
+extern DLink *recvq_clients;
+
+extern aConnect *connects;
+extern aAllow *allows;
+extern aPort *ports;
+extern Conf_Me *MeLine;
+extern aOper *opers;
+extern char  *uservers[];
+extern aClass *classes;
+
+extern char ProxyMonURL[TOPICLEN+1];
+extern char ProxyMonHost[HOSTLEN+1];
+extern char Network_Name[HOSTLEN+1];
+extern char Services_Name[HOSTLEN+1];
+extern char NS_Services_Name[HOSTLEN+9];
+extern char CS_Services_Name[HOSTLEN+9];
+extern char MS_Services_Name[HOSTLEN+9];
+extern char RS_Services_Name[HOSTLEN+9];
+extern char Stats_Name[HOSTLEN+1];
+extern char OS_Stats_Name[HOSTLEN+9];
+extern char SS_Stats_Name[HOSTLEN+9];
+extern char HS_Stats_Name[HOSTLEN+9];
+extern char Staff_Address[HOSTLEN+1];
+extern char NS_Register_URL[TOPICLEN+1];
+extern char Network_Kline_Address[HOSTLEN+1];
+extern char Local_Kline_Address[HOSTLEN+1];
+extern int  maxchannelsperuser, tsmaxdelta, tswarndelta;
+extern int  confopts, new_confopts;
+extern int  local_ip_limit, local_ip24_limit, global_ip_limit,
+            global_ip24_limit;
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+extern char dpath[PATH_MAX], spath[PATH_MAX];
+
+#include "fdlist.h"
+extern fdlist   serv_fdlist;
+extern fdlist   busycli_fdlist;
+extern fdlist   default_fdlist;
+extern fdlist   oper_fdlist;
+extern int      MAXCLIENTS;
+extern struct Counter Count;
+
+extern time_t   NOW;
+extern time_t   last_stat_save;
+extern time_t   nextconnect, nextdnscheck, nextping, timeofday;
+extern aClient  *client, me, *local[];
+extern aChannel *channel;
+extern struct    stats *ircstp;
+extern int      bootopt;
+
+extern char    *canonize(char *);
+extern void     check_fdlists();
+extern aChannel *find_channel(char *, aChannel *);
+extern aBan    *nick_is_banned(aChannel *, char *, aClient *);
+extern void     remove_matching_bans(aChannel *, aClient *, aClient *);
+#ifdef EXEMPT_LISTS
+extern void     remove_matching_exempts(aChannel *, aClient *, aClient *);
+#endif
+#ifdef INVITE_LISTS
+extern void     remove_matching_invites(aChannel *, aClient *, aClient *);
+#endif
+extern void     remove_user_from_channel(aClient *, aChannel *);
+extern void     del_invite(aClient *, aChannel *);
+extern void     send_user_joins(aClient *, aClient *);
+extern int      can_send(aClient *, aChannel *, char *);
+extern int      is_chan_op(aClient *, aChannel *);
+extern int      has_voice(aClient *, aChannel *);
+extern int      count_channels(aClient *);
+extern char    *pretty_mask(char *);
+
+
+extern aClient         *find_chasing(aClient *, char *, int *);
+
+extern int       find_restrict(aClient *);
+extern int       rehash(aClient *, aClient *, int);
+extern int       initconf(char *);
+extern inline char *finishconf(void);
+extern void       merge_confs();
+extern int       lock_kline_file();
+
+extern void      clear_scache_hash_table(void);
+extern char     *find_or_add(char *);
+extern void      count_scache(int *, u_long *);
+extern void      list_scache(aClient *, aClient *, int, char **);
+
+extern void     *MyMalloc(size_t);
+extern void     *MyRealloc(void *, size_t);
+
+/* MyFree is defined as a macro in sys.h 
+ * extern  void     MyFree (void *); 
+ */
+
+extern char     *debugmode, configfile[], *sbrk0;
+extern char     *klinefile;
+extern char     *zlinefile;
+extern char     *getfield(char *);
+extern void      get_sockhost(aClient *, char *);
+extern char     *rpl_str(int);
+extern char     *err_str(int);
+extern char     *getreply(int);
+extern char     *strerror(int);
+extern int       dgets(int, char *, int);
+extern char     *inetntoa(char *);
+extern int       dbufalloc, dbufblocks, debuglevel, errno;
+extern int       highest_fd, debuglevel, portnum,
+    debugtty, maxusersperchannel;
+extern int       readcalls, udpfd, resfd;
+extern aClient          *add_connection(aListener *, int);
+extern int       add_listener(aPort *);
+extern void      add_local_domain(char *, int);
+extern int       check_client(aClient *);
+extern int       check_server_init(aClient *);
+extern void      close_connection(aClient *);
+extern void      close_listeners();
+extern void       open_listeners();
+extern int       connect_server(aConnect *, aClient *, struct hostent *);
+extern void      get_my_name(aClient *, char *, int);
+extern int       get_sockerr(aClient *);
+extern int       inetport(aClient *, char *, int, u_long);
+extern void      init_sys();
+extern int       read_message(time_t, fdlist *);
+extern void      report_error(char *, aClient *);
+extern void      set_non_blocking(int, aClient *);
+extern int       setup_ping(void);
+extern void      summon(aClient *, char *, char *, char *);
+extern int       unixport(aClient *, char *, int);
+extern int       utmp_open(void);
+extern int       utmp_read(int, char *, char *, char *, int);
+extern int       utmp_close(int);
+
+extern void    do_dns_async(void);
+extern int     completed_connection(aClient *);
+extern void    accept_connection(aListener *);
+extern char *  irc_get_sockerr(aClient *);
+extern int     read_packet(aClient *);
+extern int     do_client_queue(aClient *);
+extern void    read_error_exit(aClient *, int, int);
+extern int     readwrite_client(aClient *, int, int);
+
+extern inline char *get_listener_name(aListener *);
+extern int        attach_Iline(aClient *, struct hostent *, char *);
+extern aConnect  *find_aConnect(char *);
+extern aOper     *find_oper(char *, char *, char *, char *);
+extern aConnect  *find_aConnect_match(char *, char *, char *);
+extern int        find_aUserver(char *);
+extern void       clear_conflinks(aClient *);
+
+extern void      start_auth(aClient *);
+extern void      read_authports(aClient *);
+extern void      send_authports(aClient *);
+
+extern void      restart(char *);
+extern void      send_channel_modes(aClient *, aChannel *);
+extern void      server_reboot(void);
+extern void      terminate(void), write_pidfile(void);
+
+extern int       match(char *, char *);
+extern char     *collapse(char *);
+
+extern int       writecalls, writeb[];
+#ifdef WRITEV_IOV
+extern int        deliver_it(aClient *, struct iovec *, int);
+#else
+extern int       deliver_it(aClient *, char *, int);
+#endif
+
+extern inline int check_registered(aClient *);
+extern inline int check_registered_user(aClient *);
+extern char     *get_client_name(aClient *, int);
+extern char     *get_client_host(aClient *);
+extern char     *my_name_for_link(char *, aConnect *);
+extern char     *myctime(time_t), *date(time_t);
+extern int       exit_client(aClient *, aClient *, aClient *, char *);
+extern void      initstats(void);
+extern char      *make_parv_copy(char *, int, char **);
+
+extern int       parse(aClient *, char *, char *);
+extern void      init_tree_parse(struct Message *);
+
+extern int       do_numeric(int, aClient *, aClient *, int, char **);
+extern int       hunt_server(aClient *, aClient *, char *, int, int, char **);
+extern aClient          *next_client(aClient *, char *);
+extern aClient          *next_client_double(aClient *, char *);
+
+extern int       m_umode(aClient *, aClient *, int, char **);
+extern int       m_names(aClient *, aClient *, int, char **);
+extern void      send_umode(aClient *, aClient *, int, int, char *);
+extern int       del_silence(aClient *, char *);
+
+
+extern void      free_client(aClient *);
+extern void      free_link(Link *);
+extern void      free_dlink(DLink *);
+extern void      free_chanmember(chanMember *);
+extern void      free_class(aClass *);
+extern void      free_user(anUser *, aClient *);
+extern void      free_channel(aChannel *);
+extern aChannel  *make_channel();
+extern Link     *make_link(void);
+extern DLink    *make_dlink(void);
+extern chanMember *make_chanmember(void);
+extern anUser   *make_user(aClient *);
+extern aClass   *make_class(void);
+extern aServer   *make_server(aClient *);
+extern aClient   *make_client(aClient *, aClient *);
+extern chanMember *find_user_member(chanMember *, aClient *);
+extern Link     *find_str_link(Link *, char *);
+extern void      add_client_to_list(aClient *);
+extern void      checklist(void);
+extern void      remove_client_from_list(aClient *);
+extern void      initlists(void);
+extern void      block_garbage_collect(void);  /* list.c */
+extern void       block_destroy(void);         /* list.c */
+
+extern void     set_effective_class(aClient *);
+extern void     initclass();
+
+extern struct hostent *get_res(char *);
+extern struct hostent *gethost_byaddr(char *, Link *);
+extern struct hostent *gethost_byname(char *, Link *);
+extern void      flush_cache(void);
+extern int       init_resolver(int);
+extern time_t    timeout_query_list(time_t);
+extern time_t    expire_cache(time_t);
+extern void      del_queries(char *);
+
+extern void      clear_channel_hash_table(void);
+extern void      clear_client_hash_table(void);
+extern int       add_to_client_hash_table(char *, aClient *);
+extern int       del_from_client_hash_table(char *, aClient *);
+extern int       add_to_channel_hash_table(char *, aChannel *);
+extern int       del_from_channel_hash_table(char *, aChannel *);
+extern aChannel  *hash_find_channel(char *, aChannel *);
+extern aClient   *hash_find_client(char *, aClient *);
+extern aClient   *hash_find_nickserver(char *, aClient *);
+extern aClient   *hash_find_server(char *, aClient *);
+
+extern void      add_history(aClient *, int);
+extern aClient   *get_history(char *, time_t);
+extern void      initwhowas(void);
+extern void      off_history(aClient *);
+
+extern int       dopacket(aClient *, char *, int);
+extern int       client_dopacket(aClient *, char *, int);
+
+/* VARARGS2 */
+
+extern void       send_rplversion(aClient *);
+extern void       send_rplisupport(aClient *);
+extern void       build_rplcache(void);
+
+extern void      debug(int level, char *pattern, ...);
+
+#ifdef DEBUGMODE
+extern void      send_listinfo(aClient *, char *);
+#endif
+
+extern void      send_list(aClient *, int);
+extern aChannel  *hash_get_chan_bucket(int);
+
+#ifdef DUMP_DEBUG
+extern FILE     *dumpfp;
+#endif
+
+#ifdef FLUD
+int              check_for_flood();
+void             free_fluders();
+void             free_fludees();
+#define MyFludConnect(x)       (((x)->fd >= 0) || ((x)->fd == -2))
+
+#endif /* FLUD */
+
+#ifdef ANTI_SPAMBOT
+#define MIN_SPAM_NUM 5
+#define MIN_SPAM_TIME 60
+#endif
+
+#define MAXKILLS 20
+extern void      count_watch_memory(int *, u_long *);
+extern void              clear_watch_hash_table(void);
+extern int               add_to_watch_hash_table(char *, aClient *);
+extern int               del_from_watch_hash_table(char *, aClient *);
+extern int               hash_check_watch(aClient *, int);
+extern int               hash_del_watch_list(aClient  *);
+extern aWatch   *hash_get_watch(char *);
+#define MAXWATCH       128
+
+DLink *add_to_list(DLink **, void *);
+void remove_from_list(DLink **, void *, DLink *);
+void print_list_memory(aClient *);
+
+#include "find.h"
diff --git a/include/hash.h b/include/hash.h
new file mode 100644 (file)
index 0000000..6af59e4
--- /dev/null
@@ -0,0 +1,67 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/hash.h
+ *   Copyright (C) 1991 Darren Reed
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+ * This is all DOUGH_HASH now - Dianora 
+ */
+
+/* $Id: hash.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef        __hash_include__
+#define __hash_include__
+
+typedef struct hashentry
+{
+    int         hits;
+    int         links;
+    void       *list;
+} aHashEntry;
+
+/* Taner had BITS_PER_COL 4 BITS_PER_COL_MASK 0xF - Dianora */
+
+#define BITS_PER_COL 3
+#define BITS_PER_COL_MASK 0x7
+#define MAX_SUB     (1<<BITS_PER_COL)
+
+/* Client hash table 
+ * used in hash.c 
+ */
+
+#define U_MAX_INITIAL  8192
+#define U_MAX_INITIAL_MASK (U_MAX_INITIAL-1)
+#define U_MAX (U_MAX_INITIAL*MAX_SUB)
+
+/* Channel hash table 
+ * used in hash.c 
+ */
+
+#define CH_MAX_INITIAL  2048
+#define CH_MAX_INITIAL_MASK (CH_MAX_INITIAL-1)
+#define CH_MAX (CH_MAX_INITIAL*MAX_SUB)
+
+/* Who was hash table 
+ * used in whowas.c 
+ */
+
+#define WW_MAX_INITIAL  16
+#define WW_MAX_INITIAL_MASK (WW_MAX_INITIAL-1)
+#define WW_MAX (WW_MAX_INITIAL*MAX_SUB)
+
+#define WATCHHASHSIZE   10007
+
+#endif /* __hash_include__ */
diff --git a/include/hooks.h b/include/hooks.h
new file mode 100644 (file)
index 0000000..99ce288
--- /dev/null
@@ -0,0 +1,64 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/hooks.h
+ *   Copyright (C) 2003 Lucas Madar
+ *
+ */
+
+enum c_hooktype {
+   CHOOK_10SEC,       /* Called every 10 seconds or so -- 
+                       * not guaranteed to be 10 seconds *
+                       * Params: None
+                       * Returns void 
+                       */
+   CHOOK_PREACCESS,   /* Called before any access checks (dns, ident) 
+                       * are done, acptr->ip is valid, 
+                       * acptr->hostip is not "*"
+                       * Params: 1: (aClient *) 
+                       * Returns int
+                       */
+   CHOOK_POSTACCESS,  /* called after access checks are done 
+                       * (right before client is put on network)
+                       * Params: 1: (aClient *) 
+                       * Returns int 
+                       */
+   CHOOK_POSTMOTD,    /* called after MOTD is shown to the client 
+                       * Params: 1: (aClient *)
+                       * Returns int 
+                       */
+
+   CHOOK_MSG,         /* called for every privmsg or notice
+                       * Params: 3: (aClient *, int isnotice, char *msgtext), 
+                       * Returns int 
+                       */
+   CHOOK_CHANMSG,     /* called for every privmsg or notice to a channel
+                       * Params: 4: (aClient *source, aChannel *destination, 
+\                      *             int isnotice, char *msgtxt)
+                       * Returns int
+                       */
+   CHOOK_USERMSG,     /* called for every privmsg or notice to a user
+                       * Params: 4: (aClient *source, aClient *destination,
+                       *             int isnotice, char *msgtxt)
+                       * Returns int
+                       */
+   CHOOK_MYMSG,       /* called for every privmsg or notice to 'me.name' 
+                       * Params: 3: (aClient *, int isnotice, char *msgtext)
+                       * Returns int 
+                       */
+   CHOOK_SIGNOFF,     /* called on client exit (exit_client)
+                       * Params: 1: (aClient *)
+                       * Returns void */
+   MHOOK_LOAD,        /* Called for modules loading and unloading */
+   MHOOK_UNLOAD       /* Params: 2: (char *modulename, void *moduleopaque) */
+};
+
+extern int call_hooks(enum c_hooktype hooktype, ...);
+extern int init_modules();
+
+#define MODULE_INTERFACE_VERSION 1006 /* the interface version (hooks, modules.c commands, etc) */
+
+#ifdef BIRCMODULE
+extern void *bircmodule_add_hook(enum c_hooktype, void *, void *);
+extern void bircmodule_del_hook();
+extern int bircmodule_malloc(int);
+extern int bircmodule_free(void *);
+#endif
diff --git a/include/inet.h b/include/inet.h
new file mode 100644 (file)
index 0000000..0c94f63
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California. All
+ * rights reserved.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its
+ * contributors'' in the documentation or other materials provided with
+ * the distribution and in all advertising materials mentioning
+ * features or use of this software. Neither the name of the University
+ * nor the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ * 
+ * @(#)inet.h   5.4 (Berkeley) 6/1/90
+ */
+
+/* $Id: inet.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+/* External definitions for functions in inet(3) */
+#include "config.h"            /* for system definitions */
+
+#ifdef __alpha
+#define        __u_l   unsigned int
+#else
+#define        __u_l   unsigned long
+#endif
+
+#ifdef __STDC__
+extern __u_l inet_addr(char *);
+extern char *inet_ntoa(char *);
+extern __u_l inet_makeaddr(int, int);
+extern __u_l inet_network(char *);
+extern __u_l inet_lnaof(struct in_addr);
+extern __u_l inet_netof(struct in_addr);
+
+#else
+extern __u_l inet_addr();
+extern char *inet_ntoa();
+
+extern __u_l inet_makeaddr();
+
+#endif
+extern __u_l inet_network();
+extern __u_l inet_lnaof();
+extern __u_l inet_netof();
+
+#undef __u_l
diff --git a/include/ircsprintf.h b/include/ircsprintf.h
new file mode 100644 (file)
index 0000000..cd92002
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef IRCSPRINTF_H
+#define IRCSPRINTF_H
+#include <stdarg.h>
+#include <stdio.h>
+#include "setup.h"
+
+/* define this if you intend to use ircsnprintf or ircvsnprintf */
+/* It's not used, and sNprintf functions are not in all libraries */
+#define WANT_SNPRINTF
+
+int ircsprintf(char *str, const char *format, ...);
+int ircvsprintf(char *str, const char *format, va_list ap);
+#ifdef WANT_SNPRINTF
+int ircvsnprintf(char *str, size_t size, const char *format, va_list ap);
+int ircsnprintf(char *str, size_t size, const char *format, ...);
+#endif
+/* This code contributed by Rossi 'vejeta' Marcello <vjt@users.sourceforge.net>
+ * Originally in va_copy.h, however there wasnt much there, so i stuck it in
+ * here.  Thanks Rossi!  -epi
+ */
+
+/* va_copy hooks for IRCd */
+
+#if defined(__powerpc__)
+# if defined(__NetBSD__)
+#  define VA_COPY va_copy
+# elif defined(__FreeBSD__) || defined(__linux__)
+#  define VA_COPY __va_copy
+# endif
+#elif defined (__x86_64)
+# define VA_COPY __va_copy
+#else
+# define VA_COPY(x, y) x = y
+#endif
+
+#endif
+
+
diff --git a/include/msg.h b/include/msg.h
new file mode 100644 (file)
index 0000000..08910c7
--- /dev/null
@@ -0,0 +1,335 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/msg.h
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* $Id: msg.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef        __msg_include__
+#define __msg_include__
+
+#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_STATS    "STATS"           /* STAT */
+#define MSG_USERS    "USERS"           /* USER -> USRS */
+#define MSG_HELP     "HELP"            /* 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_TRACE    "TRACE"           /* TRAC */
+#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_USERHOST "USERHOST"                /* USER -> USRH */
+#define MSG_USERIP   "USERIP"          /* USER -> USRH */
+#define MSG_ISON     "ISON"            /* ISON */
+#define MSG_REHASH   "REHASH"          /* REHA */
+#define MSG_RESTART  "RESTART"         /* REST */
+#define MSG_CLOSE    "CLOSE"           /* CLOS */
+#define MSG_SVINFO   "SVINFO"          /* SVINFO */
+#define MSG_SJOIN    "SJOIN"           /* SJOIN */
+#define MSG_DIE             "DIE"              /* DIE */
+#define MSG_HASH     "HASH"            /* HASH */
+#define MSG_DNS      "DNS"             /* DNS  -> DNSS */
+#define MSG_OPERWALL "OPERWALL"                /* OPERWALL */
+#define MSG_GLOBOPS  "GLOBOPS"         /* GLOBOPS */
+#define MSG_CHATOPS  "CHATOPS"         /* CHATOPS */
+#define MSG_GOPER    "GOPER"           /* GOPER */
+#define MSG_GNOTICE  "GNOTICE"         /* GNOTICE */
+#define MSG_KLINE    "KLINE"           /* KLINE */
+#ifdef UNKLINE
+#define MSG_UNKLINE  "UNKLINE"         /* UNKLINE */
+#endif
+#define MSG_SET      "SET"             /* SET */
+#define MSG_SAMODE   "SAMODE"          /* SAMODE */
+#define MSG_SAJOIN   "SAJOIN"          /* SAJOIN */
+#define MSG_CHANSERV "CHANSERV"                /* CHANSERV */
+#define MSG_NICKSERV "NICKSERV"                /* NICKSERV */
+#define MSG_MEMOSERV "MEMOSERV"                /* MEMOSERV */
+#define MSG_ROOTSERV "ROOTSERV"                /* MEMOSERV */
+#define MSG_OPERSERV "OPERSERV"                /* OPERSERV */
+#define MSG_STATSERV "STATSERV"        /* STATSERV */
+#define MSG_HELPSERV "HELPSERV"        /* HELPSERV */
+#define MSG_SERVICES "SERVICES"                /* SERVICES */
+#define MSG_IDENTIFY "IDENTIFY"                /* IDENTIFY */
+#define MSG_CAPAB    "CAPAB"           /* CAPAB */ 
+#define MSG_LOCOPS   "LOCOPS"          /* LOCOPS */
+#define MSG_SVSNICK  "SVSNICK"         /* SVSNICK */
+#define MSG_SVSNOOP  "SVSNOOP"         /* SVSNOOP */
+#define MSG_SVSKILL  "SVSKILL"         /* SVSKILL */
+#define MSG_SVSMODE  "SVSMODE"         /* SVSMODE */
+#define MSG_SVSHOLD  "SVSHOLD"         /* SVSHOLD */
+#define MSG_AKILL    "AKILL"           /* AKILL */
+#define MSG_RAKILL   "RAKILL"          /* RAKILL */
+#define MSG_SILENCE  "SILENCE"         /* SILENCE */
+#define MSG_WATCH    "WATCH"           /* WATCH */
+#define MSG_SQLINE   "SQLINE"          /* SQLINE */
+#define MSG_UNSQLINE "UNSQLINE"        /* UNSQLINE */
+#define MSG_BURST    "BURST"           /* BURST */
+#define MSG_DCCALLOW "DCCALLOW"                /* dccallow */
+#define MSG_SGLINE   "SGLINE"           /* sgline */
+#define MSG_UNSGLINE "UNSGLINE"         /* unsgline */
+#define MSG_DKEY     "DKEY"            /* diffie-hellman negotiation */
+#define MSG_NS      "NS"               /* NickServ commands */
+#define MSG_CS      "CS"               /* ChanServ commands */
+#define MSG_MS      "MS"               /* MemoServ commands */
+#define MSG_RS      "RS"               /* RootServ commands */
+#define MSG_OS      "OS"               /* OperServ commands */
+#define MSG_SS      "SS"               /* StatServ commands */
+#define MSG_HS      "HS"               /* StatServ commands */
+#define MSG_RESYNCH  "RESYNCH"         /* RESYNCH */
+#define MSG_LUSERSLOCK "LUSERSLOCK"     /* Lusers LOCK */
+#define MSG_LINKSCONTROL "LINKSCONTROL" /* LINKSCONTROL */
+#define MSG_MODULE   "MODULE"          /* MODULE */
+#define MSG_RWHO     "RWHO"         /* RWHO */
+#define MSG_SVSCLONE "SVSCLONE"     /* SVSCLONE */
+
+#define MAXPARA      15
+
+extern int  m_kline(aClient *, aClient *, int, char **);
+extern int  m_unkline(aClient *, aClient *, int, char **);
+extern int  m_akill(aClient *, aClient *, int, char **);
+extern int  m_rakill(aClient *, aClient *, int, char **);
+extern int  m_locops(aClient *, aClient *, int, char **);
+extern int  m_private(aClient *, aClient *, int, char **);
+extern int  m_topic(aClient *, aClient *, int, char **);
+extern int  m_join(aClient *, aClient *, int, char **);
+extern int  m_part(aClient *, aClient *, int, char **);
+extern int  m_mode(aClient *, aClient *, int, char **);
+extern int  m_ping(aClient *, aClient *, int, char **);
+extern int  m_pong(aClient *, aClient *, int, char **);
+extern int  m_wallops(aClient *, aClient *, int, char **);
+extern int  m_kick(aClient *, aClient *, int, char **);
+extern int  m_nick(aClient *, aClient *, int, char **);
+extern int  m_error(aClient *, aClient *, int, char **);
+extern int  m_notice(aClient *, aClient *, int, char **);
+extern int  m_invite(aClient *, aClient *, int, char **);
+extern int  m_quit(aClient *, aClient *, int, char **);
+extern int  m_kill(aClient *, aClient *, int, char **);
+extern int  m_motd(aClient *, aClient *, int, char **);
+extern int  m_who(aClient *, aClient *, int, char **);
+extern int  m_whois(aClient *, aClient *, int, char **);
+extern int  m_user(aClient *, aClient *, int, char **);
+extern int  m_list(aClient *, aClient *, int, char **);
+extern int  m_server(aClient *, aClient *, int, char **);
+extern int  m_info(aClient *, aClient *, int, char **);
+extern int  m_links(aClient *, aClient *, int, char **);
+extern int  m_summon(aClient *, aClient *, int, char **);
+extern int  m_stats(aClient *, aClient *, int, char **);
+extern int  m_users(aClient *, aClient *, int, char **);
+extern int  m_services(aClient *, aClient *, int, char **);
+extern int  m_identify(aClient *, aClient *, int, char **);
+extern int  m_ns(aClient *, aClient *, int, char **);
+extern int  m_cs(aClient *, aClient *, int, char **);
+extern int  m_ms(aClient *, aClient *, int, char **);
+extern int  m_rs(aClient *, aClient *, int, char **);
+extern int  m_os(aClient *, aClient *, int, char **);
+extern int  m_ss(aClient *, aClient *, int, char **);
+extern int  m_hs(aClient *, aClient *, int, char **);
+extern int  m_svsnick(aClient *, aClient *, int, char **);
+extern int  m_svskill(aClient *, aClient *, int, char **);
+extern int  m_svsmode(aClient *, aClient *, int, char **);
+extern int  m_svshold(aClient *, aClient *, int, char **);
+extern int  m_version(aClient *, aClient *, int, char **);
+extern int  m_help(aClient *, aClient *, int, char **);
+extern int  m_squit(aClient *, aClient *, int, char **);
+extern int  m_away(aClient *, aClient *, int, char **);
+extern int  m_connect(aClient *, aClient *, int, char **);
+extern int  m_oper(aClient *, aClient *, int, char **);
+extern int  m_pass(aClient *, aClient *, int, char **);
+extern int  m_trace(aClient *, aClient *, int, char **);
+extern int  m_time(aClient *, aClient *, int, char **);
+extern int  m_names(aClient *, aClient *, int, char **);
+extern int  m_admin(aClient *, aClient *, int, char **);
+extern int  m_lusers(aClient *, aClient *, int, char **);
+extern int  m_umode(aClient *, aClient *, int, char **);
+extern int  m_close(aClient *, aClient *, int, char **);
+extern int  m_motd(aClient *, aClient *, int, char **);
+extern int  m_whowas(aClient *, aClient *, int, char **);
+extern int  m_userhost(aClient *, aClient *, int, char **);
+extern int  m_userip(aClient *, aClient *, int, char **);
+extern int  m_ison(aClient *, aClient *, int, char **);
+extern int  m_svinfo(aClient *, aClient *, int, char **);
+extern int  m_sjoin(aClient *, aClient *, int, char **);
+extern int  m_samode(aClient *, aClient *, int, char **);
+extern int  m_sajoin(aClient *, aClient *, int, char **);
+extern int  m_globops(aClient *, aClient *, int, char **);
+extern int  m_chatops(aClient *, aClient *, int, char **);
+extern int  m_goper(aClient *, aClient *, int, char **);
+extern int  m_gnotice(aClient *, aClient *, int, char **);
+extern int  m_rehash(aClient *, aClient *, int, char **);
+extern int  m_restart(aClient *, aClient *, int, char **);
+extern int  m_die(aClient *, aClient *, int, char **);
+extern int  m_hash(aClient *, aClient *, int, char **);
+extern int  m_dns(aClient *, aClient *, int, char **);
+extern int  m_set(aClient *, aClient *, int, char **);
+extern int  m_capab(aClient *, aClient *, int, char **);
+extern int  m_silence(aClient *, aClient *, int, char **);
+extern int  m_watch(aClient *, aClient *, int, char **);
+extern int  m_sqline(aClient *, aClient *, int, char **);
+extern int  m_unsqline(aClient *, aClient *, int, char **);
+extern int  m_burst(aClient *, aClient *, int, char **);
+extern int  m_dccallow(aClient *, aClient *, int, char **);
+extern int  m_sgline(aClient *, aClient *, int, char **);
+extern int  m_unsgline(aClient *, aClient *, int, char **);
+extern int  m_dkey(aClient *, aClient *, int, char **);
+extern int  m_resynch(aClient *, aClient *, int, char **);
+extern int  m_luserslock(aClient *, aClient *, int, char **);
+extern int  m_linkscontrol(aClient *, aClient *, int, char **);
+extern int  m_module(aClient *, aClient *, int, char **);
+extern int  m_rwho(aClient *, aClient *, int, char **);
+extern int  m_svsclone(aClient *, aClient *, int, char **);
+
+#ifdef MSGTAB
+struct Message msgtab[] = 
+{
+    {MSG_PRIVATE, m_private, 0, MAXPARA, 1, 0, 1, 0L},
+    {MSG_NICK, m_nick, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_NOTICE, m_notice, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_JOIN, m_join, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_MODE, m_mode, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_SAMODE, m_samode, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_SAJOIN, m_sajoin, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_QUIT, m_quit, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_PART, m_part, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_TOPIC, m_topic, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_INVITE, m_invite, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_KICK, m_kick, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_WALLOPS, m_wallops, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_LOCOPS, m_locops, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_PONG, m_pong, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_PING, m_ping, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_ERROR, m_error, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_KILL, m_kill, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_USER, m_user, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_AWAY, m_away, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_ISON, m_ison, 0, 1, 1, 0, 0, 0L},
+    {MSG_SERVER, m_server, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_SQUIT, m_squit, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_WHOIS, m_whois, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_WHO, m_who, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_WHOWAS, m_whowas, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_LIST, m_list, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_NAMES, m_names, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_USERHOST, m_userhost, 0, 1, 1, 0, 0, 0L},
+    {MSG_USERIP, m_userip, 0, 1, 1, 0, 0, 0L},
+    {MSG_TRACE, m_trace, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_PASS, m_pass, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_LUSERS, m_lusers, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_TIME, m_time, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_OPER, m_oper, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_CONNECT, m_connect, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_VERSION, m_version, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_STATS, m_stats, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_LINKS, m_links, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_ADMIN, m_admin, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_USERS, m_users, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_HELP, m_help, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_INFO, m_info, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_MOTD, m_motd, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_SVINFO, m_svinfo, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_SJOIN, m_sjoin, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_GLOBOPS, m_globops, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_CHATOPS, m_chatops, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_GOPER, m_goper, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_GNOTICE, m_gnotice, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_CLOSE, m_close, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_KLINE, m_kline, 0, MAXPARA, 1, 0, 0, 0L},
+#ifdef UNKLINE
+    {MSG_UNKLINE, m_unkline, 0, MAXPARA, 1, 0, 0, 0L},
+#endif
+    {MSG_HASH, m_hash, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_DNS, m_dns, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_REHASH, m_rehash, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_RESTART, m_restart, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_DIE, m_die, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_SET, m_set, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_CHANSERV, m_cs, 0, 1, 1, 0, 0, 0L},
+    {MSG_NICKSERV, m_ns, 0, 1, 1, 0, 0, 0L},
+    {MSG_MEMOSERV, m_ms, 0, 1, 1, 0, 0, 0L},
+    {MSG_ROOTSERV, m_rs, 0, 1, 1, 0, 0, 0L},
+    {MSG_OPERSERV, m_os, 0, 1, 1, 0, 0, 0L},
+    {MSG_STATSERV, m_ss, 0, 1, 1, 0, 0, 0L},
+    {MSG_HELPSERV, m_hs, 0, 1, 1, 0, 0, 0L},
+    {MSG_SERVICES, m_services, 0, 1, 1, 0, 0, 0L},
+    {MSG_IDENTIFY, m_identify, 0, 1, 1, 0, 0, 0L},
+    {MSG_SVSNICK,  m_svsnick,  0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_SVSKILL,  m_svskill,  0, MAXPARA, 1, 0, 0, 0L},        
+    {MSG_SVSMODE,  m_svsmode,  0, MAXPARA, 1, 0, 0, 0L},        
+    {MSG_SVSHOLD,  m_svshold,  0, MAXPARA, 1, 0, 0, 0L},        
+    {MSG_AKILL,    m_akill,    0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_RAKILL,   m_rakill,   0, MAXPARA, 1, 0, 0, 0L}, 
+    {MSG_SILENCE,  m_silence,  0, MAXPARA, 1, 0, 0, 0L },
+    {MSG_WATCH, m_watch, 0, 1, 1, 0, 0, 0L },
+    {MSG_DCCALLOW, m_dccallow, 0, 1, 1, 0, 0, 0L },
+    {MSG_SQLINE, m_sqline, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_UNSQLINE, m_unsqline, 0, MAXPARA, 1, 0, 0, 0L },
+    {MSG_CAPAB, m_capab, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_BURST, m_burst, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_SGLINE, m_sgline, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_UNSGLINE, m_unsgline, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_DKEY, m_dkey, 0, MAXPARA, 1, 1, 0, 0L},
+    {MSG_NS, m_ns, 0, 1, 1, 1, 0, 0L},
+    {MSG_CS, m_cs, 0, 1, 1, 1, 0, 0L},
+    {MSG_MS, m_ms, 0, 1, 1, 1, 0, 0L},
+    {MSG_RS, m_rs, 0, 1, 1, 1, 0, 0L},
+    {MSG_OS, m_os, 0, 1, 1, 1, 0, 0L},
+    {MSG_SS, m_ss, 0, 1, 1, 1, 0, 0L},
+    {MSG_HS, m_hs, 0, 1, 1, 1, 0, 0L},
+    {MSG_RESYNCH, m_resynch, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_LUSERSLOCK, m_luserslock, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_LINKSCONTROL, m_linkscontrol, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_MODULE, m_module, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_RWHO, m_rwho, 0, MAXPARA, 1, 0, 0, 0L},
+    {MSG_SVSCLONE, m_svsclone, 0, MAXPARA, 1, 0 ,0, 0L},
+    {(char *) 0, (int (*)()) 0, 0, 0, 0, 0, 0, 0L}
+};
+
+MESSAGE_TREE *msg_tree_root;
+#else
+extern struct Message msgtab[];
+extern MESSAGE_TREE *msg_tree_root;
+#endif
+#endif /* __msg_include__  */
diff --git a/include/nameser.h b/include/nameser.h
new file mode 100644 (file)
index 0000000..b399278
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 1983, 1989 Regents of the University of California.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its
+ * contributors'' in the documentation or other materials provided with
+ * the distribution and in all advertising materials mentioning
+ * features or use of this software. Neither the name of the University
+ * nor the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ * 
+ * @(#)nameser.h        5.24 (Berkeley) 6/1/90
+ */
+
+/* $Id: nameser.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+/* Define constants based on rfc883 */
+
+#define PACKETSZ       512     /* maximum packet size */
+#define MAXDNAME       256     /* maximum domain name */
+#define MAXCDNAME      255     /* maximum compressed domain name */
+#define MAXLABEL       63      /* maximum length of domain label */
+
+#define QFIXEDSZ       4       /* Number of bytes of fixed size 
+                                * data in query structure */
+#define RRFIXEDSZ      10      /* number of bytes of fixed size data
+                                * in resource record */
+#define NAMESERVER_PORT        53      /* Internet nameserver port number */
+
+/* Currently defined opcodes */
+
+#define QUERY          0x0     /* standard query */
+#define IQUERY         0x1     /* inverse query */
+#define STATUS         0x2     /* nameserver status query */
+
+/* non standard */
+
+#define UPDATEA                0x9     /* add resource record */
+#define UPDATED                0xa     /* delete a specific resource record */
+#define UPDATEDA       0xb     /* delete all nemed resource record */
+#define UPDATEM                0xc     /* modify a specific resource record */
+#define UPDATEMA       0xd     /* modify all named resource record */
+#define ZONEINIT       0xe     /* initial zone transfer */
+#define ZONEREF                0xf     /* incremental zone referesh */
+
+/* Currently defined response codes */
+
+#ifdef NOERROR                 /* defined by solaris2 in 
+#undef NOERROR                  * <sys/stream.h> to be -1 */
+#endif
+
+#define NOERROR                0       /* no error */
+#define FORMERR                1       /* format error */
+#define SERVFAIL       2       /* server failure */
+#define NXDOMAIN       3       /* non existent domain */
+#define NOTIMP         4       /* not implemented */
+#define REFUSED                5       /* query refused */
+
+/* non standard  */
+
+#define NOCHANGE       0xf     /* update failed to change db */
+
+/* Type values for resources and queries */
+
+#define T_A            1       /* host address */
+#define T_NS           2       /* authoritative server */
+#define T_MD           3       /* mail destination */
+#define T_MF           4       /* mail forwarder */
+#define T_CNAME                5       /* connonical name */
+#define T_SOA          6       /* start of authority zone */
+#define T_MB           7       /* mailbox domain name */
+#define T_MG           8       /* mail group member */
+#define T_MR           9       /* mail rename name */
+#define T_NULL         10      /* null resource record */
+#define T_WKS          11      /* well known service */
+#define T_PTR          12      /* domain name pointer */
+#define T_HINFO                13      /* host information */
+#define T_MINFO                14      /* mailbox information */
+#define T_MX           15      /* mail routing information */
+#define T_TXT          16      /* text strings */
+
+/* non standard */
+
+#define T_UINFO                100     /* user (finger) information */
+#define T_UID          101     /* user ID */
+#define T_GID          102     /* group ID */
+#define T_UNSPEC       103     /* Unspecified format (binary data) */
+
+/* Query type values which do not appear in resource records */
+
+#define T_AXFR         252     /* transfer zone of authority */
+#define T_MAILB                253     /* transfer mailbox records */
+#define T_MAILA                254     /* transfer mail agent records */
+#define T_ANY          255     /* wildcard match */
+
+/* Values for class field */
+
+#define C_IN           1       /* the arpa internet */
+#define C_CHAOS                3       /* for chaos net at MIT */
+#define C_HS           4       /* for Hesiod name server at MIT */
+
+/* Query class values which do not appear in resource records */
+
+#define C_ANY          255     /* wildcard match */
+
+/* Status return codes for T_UNSPEC conversion routines */
+
+#define CONV_SUCCESS 0
+#define CONV_OVERFLOW -1
+#define CONV_BADFMT -2
+#define CONV_BADCKSUM -3
+#define CONV_BADBUFLEN -4
+
+#ifndef BYTE_ORDER
+#define        LITTLE_ENDIAN   1234   /* least-significant byte first (vax) */
+#define        BIG_ENDIAN      4321   /* most-significant byte first (IBM, net) */
+#define        PDP_ENDIAN      3412   /* LSB first in word, MSW first in long (pdp) */
+
+#if defined(vax) || defined(ns32000) || defined(sun386) || defined(MIPSEL) || \
+    defined(BIT_ZERO_ON_RIGHT) || defined(i386) ||\
+    defined(___vax__) || defined(__ns32000__) || defined(__sun386__) ||\
+    defined(__alpha)
+
+#define BYTE_ORDER     LITTLE_ENDIAN
+
+#endif
+
+#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
+    defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \
+    defined(MIPSEB) || defined(__hpux) || defined(__convex__) || \
+    defined(__pyr__) || defined(__mc68000__) || defined(__sparc__) ||\
+    defined(_IBMR2) || defined (BIT_ZERO_ON_LEFT)
+
+#define BYTE_ORDER     BIG_ENDIAN
+
+#endif
+
+#endif /* BYTE_ORDER */
+
+#ifndef BYTE_ORDER
+/* you must determine what the correct bit order is for your compiler */
+
+UNDEFINED_BIT_ORDER;
+
+#endif
+
+/*
+ * Structure for query header, the order of the fields is machine and
+ * compiler dependent, in our case, the bits within a byte are assignd
+ * least significant first, while the order of transmition is most
+ * significant first.  This requires a somewhat confusing
+ * rearrangement.
+ */
+
+typedef struct
+{
+    u_short     id;            /* query identification number */
+#if BYTE_ORDER == BIG_ENDIAN    
+    
+    /* fields in third byte */
+    
+    u_char      qr:1;          /* response flag */
+    u_char      opcode:4;      /* purpose of message */
+    u_char      aa:1;          /* authoritive answer */
+    u_char      tc:1;          /* truncated message */
+    u_char      rd:1;          /* recursion desired */
+    
+    /* fields in fourth byte */
+    
+    u_char      ra:1;          /* recursion available */
+    u_char      pr:1;          /* primary server required (non standard) */
+    u_char      unused:2;      /* unused bits */
+    u_char      rcode:4;               /* response code */
+    
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
+    
+    /* fields in third byte */
+
+    u_char      rd:1;          /* recursion desired */
+    u_char      tc:1;          /* truncated message */
+    u_char      aa:1;          /* authoritive answer */
+    u_char      opcode:4;      /* purpose of message */
+    u_char      qr:1;          /* response flag */
+    
+    /* fields in fourth byte */
+    
+    u_char      rcode:4;               /* response code */
+    u_char      unused:2;      /* unused bits */
+    u_char      pr:1;          /* primary server required (non standard) */
+    u_char      ra:1;          /* recursion available */
+    
+#endif
+
+    /* remaining bytes */
+    
+    u_short     qdcount;               /* number of question entries */
+    u_short     ancount;               /* number of answer entries */
+    u_short     nscount;               /* number of authority entries */
+    u_short     arcount;               /* number of resource entries */
+} HEADER;
+
+/* Defines for handling compressed domain names */
+
+#define INDIR_MASK     0xc0
+
+/* Structure for passing resource records around. */
+
+struct rrec 
+{
+    short       r_zone;                /* zone number */
+    short       r_class;               /* class number */
+    short       r_type;                /* type number */
+#ifdef __alpha
+    u_int       r_ttl;         /* time to live */
+#else
+    u_long      r_ttl;         /* time to live */
+#endif
+    int         r_size;                /* size of data area */
+    char       *r_data;                /* pointer to data */
+};
+
+extern u_short _getshort();
+
+#ifdef __alpha
+extern u_int _getlong();
+#else
+extern u_long _getlong();
+#endif
+
+/*
+ * Inline versions of get/put short/long. Pointer is advanced; we
+ * assume that both arguments are lvalues and will already be in
+ * registers. cp MUST be u_char *.
+ */
+
+#define GETSHORT(s, cp) { \
+       (s) = *(cp)++ << 8; \
+       (s) |= *(cp)++; \
+}
+
+#define GETLONG(l, cp) { \
+       (l) = *(cp)++ << 8; \
+       (l) |= *(cp)++; (l) <<= 8; \
+       (l) |= *(cp)++; (l) <<= 8; \
+       (l) |= *(cp)++; \
+}
+
+#define PUTSHORT(s, cp) { \
+       *(cp)++ = (s) >> 8; \
+       *(cp)++ = (s); \
+}
+
+/* Warning: PUTLONG destroys its first argument.*/
+
+#define PUTLONG(l, cp) { \
+       (cp)[3] = l; \
+       (cp)[2] = (l >>= 8); \
+       (cp)[1] = (l >>= 8); \
+       (cp)[0] = l >> 8; \
+       (cp) += sizeof(u_long); \
+}
diff --git a/include/numeric.h b/include/numeric.h
new file mode 100644 (file)
index 0000000..b21a88e
--- /dev/null
@@ -0,0 +1,269 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/numeric.h
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* $Id: numeric.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+
+#define        RPL_WELCOME          001
+#define        RPL_YOURHOST         002
+#define        RPL_CREATED          003
+#define        RPL_MYINFO           004
+#define RPL_ISUPPORT         005
+
+#define RPL_TRACELINK        200
+#define RPL_TRACECONNECTING  201
+#define RPL_TRACEHANDSHAKE   202
+#define RPL_TRACEUNKNOWN     203
+#define RPL_TRACEOPERATOR    204
+#define RPL_TRACEUSER        205
+#define RPL_TRACESERVER      206
+
+#define RPL_TRACENEWTYPE     208
+#define RPL_TRACECLASS       209
+
+#define RPL_STATSLINKINFO    211
+#define RPL_STATSCOMMANDS    212
+#define RPL_STATSCLINE       213
+#define RPL_STATSNLINE       214
+#define RPL_STATSILINE       215
+#define RPL_STATSKLINE       216
+#define RPL_STATSQLINE       217
+#define RPL_STATSYLINE       218
+#define RPL_ENDOFSTATS       219
+
+#define RPL_UMODEIS          221
+#define RPL_STATSBLINE      222
+#define RPL_STATSELINE      223
+
+#define RPL_STATSCLONE      225
+#define RPL_STATSCOUNT      226
+#define RPL_STATSGLINE       227
+
+#define RPL_SERVLIST         234
+#define RPL_SERVLISTEND      235
+
+#define        RPL_STATSLLINE       241
+#define        RPL_STATSUPTIME      242
+#define        RPL_STATSOLINE       243
+#define        RPL_STATSHLINE       244
+#define        RPL_STATSSLINE       245
+#define RPL_STATSULINE      246
+
+#define        RPL_STATSDEBUG       249
+
+#define        RPL_LUSERCLIENT      251
+#define RPL_LUSEROP          252
+#define        RPL_LUSERUNKNOWN     253
+#define        RPL_LUSERCHANNELS    254
+#define        RPL_LUSERME          255
+#define        RPL_ADMINME          256
+#define        RPL_ADMINLOC1        257
+#define        RPL_ADMINLOC2        258
+#define        RPL_ADMINEMAIL       259
+
+#define        RPL_TRACELOG         261
+#define RPL_ENDOFTRACE       262
+#define RPL_LOAD2HI          263
+
+#define RPL_LOCALUSERS       265
+#define RPL_GLOBALUSERS      266
+
+#define RPL_SILELIST         271
+#define RPL_ENDOFSILELIST    272
+
+#define        RPL_NONE             300
+#define RPL_AWAY             301
+#define RPL_USERHOST         302
+#define RPL_ISON             303
+#define RPL_TEXT             304
+#define        RPL_UNAWAY           305
+#define        RPL_NOWAWAY          306
+#define RPL_WHOISREGNICK     307
+#define RPL_WHOISADMIN       308
+#define RPL_WHOISSADMIN      309
+#define RPL_WHOISSVCMSG      310
+#define RPL_WHOISUSER        311
+#define RPL_WHOISSERVER      312
+#define RPL_WHOISOPERATOR    313
+#define RPL_WHOWASUSER       314
+#define RPL_ENDOFWHO         315
+#define RPL_WHOISCHANOP      316
+#define RPL_WHOISIDLE        317
+#define RPL_ENDOFWHOIS       318
+#define RPL_WHOISCHANNELS    319
+
+#define RPL_LISTSTART        321
+#define RPL_LIST             322
+#define RPL_LISTEND          323
+#define RPL_CHANNELMODEIS    324
+
+#define RPL_CREATIONTIME     329
+
+#define RPL_NOTOPIC          331
+#define RPL_TOPIC            332
+#define RPL_TOPICWHOTIME     333
+#define RPL_COMMANDSYNTAX    334
+
+#define RPL_WHOISTEXT        337
+#define RPL_WHOISACTUALLY    338
+
+#define RPL_INVITING         341
+#define        RPL_SUMMONING        342
+
+#define RPL_INVITELIST       346
+#define RPL_ENDOFINVITELIST  347
+#define RPL_EXEMPTLIST       348
+#define RPL_ENDOFEXEMPTLIST  349
+
+#define RPL_VERSION          351
+#define RPL_WHOREPLY         352
+#define RPL_NAMREPLY         353
+#define RPL_RWHOREPLY        354
+
+#define RPL_KILLDONE         361
+#define        RPL_CLOSING          362
+#define RPL_CLOSEEND         363
+#define RPL_LINKS            364
+#define RPL_ENDOFLINKS       365
+#define RPL_ENDOFNAMES       366
+#define RPL_BANLIST          367
+#define RPL_ENDOFBANLIST     368
+#define        RPL_ENDOFWHOWAS      369
+
+#define        RPL_INFO             371
+#define        RPL_MOTD             372
+#define        RPL_INFOSTART        373
+#define        RPL_ENDOFINFO        374
+#define        RPL_MOTDSTART        375
+#define        RPL_ENDOFMOTD        376
+
+#define RPL_YOUREOPER        381
+#define RPL_REHASHING        382
+
+#define RPL_MYPORTIS         384
+#define RPL_NOTOPERANYMORE   385
+
+#define RPL_TIME             391
+#define        RPL_USERSSTART       392
+#define        RPL_USERS            393
+#define        RPL_ENDOFUSERS       394
+#define        RPL_NOUSERS          395
+
+#define ERR_NOSUCHNICK       401
+#define ERR_NOSUCHSERVER     402
+#define ERR_NOSUCHCHANNEL    403
+#define ERR_CANNOTSENDTOCHAN 404
+#define ERR_TOOMANYCHANNELS  405
+#define ERR_WASNOSUCHNICK    406
+#define ERR_TOOMANYTARGETS   407
+#define ERR_NOCOLORSONCHAN   408
+#define        ERR_NOORIGIN         409
+
+#define ERR_NORECIPIENT      411
+#define ERR_NOTEXTTOSEND     412
+#define ERR_NOTOPLEVEL       413
+#define ERR_WILDTOPLEVEL     414
+
+#define ERR_UNKNOWNCOMMAND   421
+#define        ERR_NOMOTD           422
+#define        ERR_NOADMININFO      423
+#define        ERR_FILEERROR        424
+
+#define ERR_TOOMANYAWAY      429
+
+#define ERR_NONICKNAMEGIVEN  431
+#define ERR_ERRONEUSNICKNAME 432
+#define ERR_NICKNAMEINUSE    433
+
+#define ERR_BANONCHAN        435
+#define        ERR_NICKCOLLISION    436
+#define ERR_BANNICKCHANGE    437
+
+#define ERR_TARGETTOFAST     439
+
+#define ERR_SERVICESDOWN     440
+#define ERR_USERNOTINCHANNEL 441
+#define ERR_NOTONCHANNEL     442
+#define        ERR_USERONCHANNEL    443
+#define ERR_NOLOGIN          444
+#define        ERR_SUMMONDISABLED   445
+#define ERR_USERSDISABLED    446
+
+#define ERR_NOTREGISTERED    451
+
+#define ERR_NEEDMOREPARAMS   461
+#define ERR_ALREADYREGISTRED 462
+#define ERR_NOPERMFORHOST    463
+#define ERR_PASSWDMISMATCH   464
+#define ERR_YOUREBANNEDCREEP 465
+#define ERR_YOUWILLBEBANNED  466
+#define        ERR_KEYSET           467
+#define ERR_ONLYSERVERSCANCHANGE 468
+
+#define ERR_CHANNELISFULL    471
+#define ERR_UNKNOWNMODE      472
+#define ERR_INVITEONLYCHAN   473
+#define ERR_BANNEDFROMCHAN   474
+#define        ERR_BADCHANNELKEY    475
+#define        ERR_BADCHANMASK      476
+#define ERR_NEEDREGGEDNICK   477
+#define ERR_BANLISTFULL      478
+#define ERR_BADCHANNAME      479
+
+#define ERR_NOPRIVILEGES     481
+#define ERR_CHANOPRIVSNEEDED 482
+#define        ERR_CANTKILLSERVER   483
+#define ERR_DESYNC           484
+#define ERR_CHANBANREASON    485
+#define ERR_NONONREG         486
+#define ERR_MSGSERVICES      487
+
+#define ERR_NOOPERHOST       491
+
+#define ERR_UMODEUNKNOWNFLAG 501
+#define ERR_USERSDONTMATCH   502
+
+#define ERR_GHOSTEDCLIENT    503
+#define ERR_LAST_ERR_MSG     504
+
+#define ERR_SILELISTFULL     511
+#define ERR_TOOMANYWATCH     512
+
+#define ERR_TOOMANYDCC       514
+
+#define ERR_LISTSYNTAX       521
+#define ERR_WHOSYNTAX        522
+#define ERR_WHOLIMEXCEED     523
+
+#define RPL_LOGON            600
+#define RPL_LOGOFF           601
+#define RPL_WATCHOFF         602
+#define RPL_WATCHSTAT        603
+#define RPL_NOWON            604
+#define RPL_NOWOFF           605
+#define RPL_WATCHLIST        606
+#define RPL_ENDOFWATCHLIST   607
+
+#define RPL_DCCSTATUS        617
+#define RPL_DCCLIST          618
+#define RPL_ENDOFDCCLIST     619
+#define RPL_DCCINFO          620
+
+#define ERR_NUMERIC_ERR      999
diff --git a/include/patchlevel.h b/include/patchlevel.h
new file mode 100644 (file)
index 0000000..aa6c0d0
--- /dev/null
@@ -0,0 +1,41 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/patchlevel.h
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: patchlevel.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef __patchlevel_header__
+#define __patchlevel_header__
+
+/* these don't go in the version itself, but they're importany anyways */
+#define RELEASE 1
+#define BETA    2
+#define CURRENT 3
+
+#define BRANCHSTATUS RELEASE
+
+#define BASENAME "bahamut"
+#define MAJOR 1
+#define MINOR 8
+#define PATCH 3
+
+#define PATCHES ""
+
+void build_version(void);
+
+#endif
+
diff --git a/include/pcre.h b/include/pcre.h
new file mode 100644 (file)
index 0000000..c8a0275
--- /dev/null
@@ -0,0 +1,193 @@
+/*************************************************
+*       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          4
+#define PCRE_MINOR          5
+#define PCRE_DATE           01-December-2003
+
+/* 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 extern __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 <stdlib.h>
+
+/* 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
+#define PCRE_NO_UTF8_CHECK      0x2000
+
+/* 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 */
+#define PCRE_ERROR_BADUTF8        (-10)
+#define PCRE_ERROR_BADUTF8_OFFSET (-11)
+
+/* 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
+#define PCRE_CONFIG_STACKRECURSE            5
+
+/* 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. Special ones are used in the
+non-recursive case for "frames". 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 void *(*pcre_stack_malloc)(size_t);
+PCRE_DATA_SCOPE void  (*pcre_stack_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 void *pcre_stack_malloc(size_t);
+extern void  pcre_stack_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/include/pcre_config.h b/include/pcre_config.h
new file mode 100644 (file)
index 0000000..3572fb7
--- /dev/null
@@ -0,0 +1,11 @@
+/* pcre_config.h, created to embed PCRE in bahamut */
+
+#include "setup.h"
+
+#define EBCDIC 0
+#define EXPORT
+#define NEWLINE '\n'
+#define LINK_SIZE   2
+#define POSIX_MALLOC_THRESHOLD 10
+#define MATCH_LIMIT 1000
+
diff --git a/include/pcre_internal.h b/include/pcre_internal.h
new file mode 100644 (file)
index 0000000..226d73d
--- /dev/null
@@ -0,0 +1,677 @@
+/*************************************************
+*      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 <ph10@cam.ac.uk>
+
+           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 "pcre_config.h"
+
+/* Standard C headers plus the external interface definition. The only time
+setjmp and stdarg are used is when NO_RECURSE is set. */
+
+#include <ctype.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef PCRE_SPY
+#define PCRE_DEFINITION       /* Win32 __declspec(export) trigger for .dll */
+#endif
+
+#include "pcre.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
+
+
+/* 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|PCRE_NO_UTF8_CHECK)
+
+#define PUBLIC_EXEC_OPTIONS \
+  (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK)
+
+#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,         /* 57 Extended class for handling UTF-8 chars within the
+                           class. This does both positive and negative. */
+
+  OP_REF,            /* 58 Match a back reference */
+  OP_RECURSE,        /* 59 Match a numbered subpattern (possibly recursive) */
+  OP_CALLOUT,        /* 60 Call out to external function if provided */
+
+  OP_ALT,            /* 61 Start of alternation */
+  OP_KET,            /* 62 End of group that doesn't have an unbounded repeat */
+  OP_KETRMAX,        /* 63 These two must remain together and in this */
+  OP_KETRMIN,        /* 64 order. They are for groups the repeat for ever. */
+
+  /* The assertions must come before ONCE and COND */
+
+  OP_ASSERT,         /* 65 Positive lookahead */
+  OP_ASSERT_NOT,     /* 66 Negative lookahead */
+  OP_ASSERTBACK,     /* 67 Positive lookbehind */
+  OP_ASSERTBACK_NOT, /* 68 Negative lookbehind */
+  OP_REVERSE,        /* 69 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,           /* 70 Once matched, don't back up into the subpattern */
+  OP_COND,           /* 71 Conditional group */
+  OP_CREF,           /* 72 Used to hold an extraction string number (cond ref) */
+
+  OP_BRAZERO,        /* 73 These two must remain together and in this */
+  OP_BRAMINZERO,     /* 74 order. */
+
+  OP_BRANUMBER,      /* 75 Used for extracting brackets whose number is greater
+                           than can fit into an opcode. */
+
+  OP_BRA             /* 76 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                            ** These are  */ \
+  2, 2, 2, 2, 2, 2,              /* *, *?, +, +?, ?, ??      ** minima in  */ \
+  4, 4, 4,                       /* upto, minupto, exact     ** UTF-8 mode */ \
+  /* Negative single-char repeats - only for chars < 256                   */ \
+  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"
+#define ERR44 "invalid UTF-8 string"
+
+/* 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 *prevrec; /* 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;
+
+/* When compiling in a mode that doesn't use recursive calls to match(),
+a structure is used to remember local variables on the heap. It is defined in
+pcre.c, close to the match() function, so that it is easy to keep it in step
+with any changes of local variable. However, the pointer to the current frame
+must be saved in some "static" place over a longjmp(). We declare the
+structure here so that we can put a pointer in the match_data structure.
+NOTE: This isn't used for a "normal" compilation of pcre. */
+
+struct heapframe;
+
+/* 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 */
+  struct heapframe *thisframe;  /* Used only when compiling for no recursion */
+} 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/include/queue.h b/include/queue.h
new file mode 100644 (file)
index 0000000..0de8211
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)queue.h     8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.40 2000/08/03 17:31:56 hsu Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ *                     SLIST   LIST    STAILQ  TAILQ   CIRCLEQ
+ * _HEAD               +       +       +       +       +
+ * _HEAD_INITIALIZER   +       +       +       +       +
+ * _ENTRY              +       +       +       +       +
+ * _INIT               +       +       +       +       +
+ * _EMPTY              +       +       +       +       +
+ * _FIRST              +       +       +       +       +
+ * _NEXT               +       +       +       +       +
+ * _PREV               -       -       -       +       +
+ * _LAST               -       -       +       +       +
+ * _FOREACH            +       +       +       +       +
+ * _FOREACH_REVERSE    -       -       -       +       +
+ * _INSERT_HEAD                +       +       +       +       +
+ * _INSERT_BEFORE      -       +       -       +       +
+ * _INSERT_AFTER       +       +       +       +       +
+ * _INSERT_TAIL                -       -       +       +       +
+ * _REMOVE_HEAD                +       -       +       -       -
+ * _REMOVE             +       +       +       +       +
+ *
+ */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define        SLIST_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *slh_first; /* first element */                     \
+}
+
+#define        SLIST_HEAD_INITIALIZER(head)                                    \
+       { NULL }
+#define        SLIST_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *sle_next;  /* next element */                      \
+}
+/*
+ * Singly-linked List functions.
+ */
+#define        SLIST_EMPTY(head)       ((head)->slh_first == NULL)
+
+#define        SLIST_FIRST(head)       ((head)->slh_first)
+
+#define        SLIST_FOREACH(var, head, field)                                 \
+       for ((var) = SLIST_FIRST((head));                               \
+           (var);                                                      \
+           (var) = SLIST_NEXT((var), field))
+
+#define        SLIST_INIT(head) do {                                           \
+       SLIST_FIRST((head)) = NULL;                                     \
+} while (0)
+
+#define        SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \
+       SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);       \
+       SLIST_NEXT((slistelm), field) = (elm);                          \
+} while (0)
+
+#define        SLIST_INSERT_HEAD(head, elm, field) do {                        \
+       SLIST_NEXT((elm), field) = SLIST_FIRST((head));                 \
+       SLIST_FIRST((head)) = (elm);                                    \
+} while (0)
+
+#define        SLIST_NEXT(elm, field)  ((elm)->field.sle_next)
+
+#define        SLIST_REMOVE(head, elm, type, field) do {                       \
+       if (SLIST_FIRST((head)) == (elm)) {                             \
+               SLIST_REMOVE_HEAD((head), field);                       \
+       }                                                               \
+       else {                                                          \
+               struct type *curelm = SLIST_FIRST((head));              \
+               while (SLIST_NEXT(curelm, field) != (elm))              \
+                       curelm = SLIST_NEXT(curelm, field);             \
+               SLIST_NEXT(curelm, field) =                             \
+                   SLIST_NEXT(SLIST_NEXT(curelm, field), field);       \
+       }                                                               \
+} while (0)
+
+#define        SLIST_REMOVE_HEAD(head, field) do {                             \
+       SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);   \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define        STAILQ_HEAD(name, type)                                         \
+struct name {                                                          \
+       struct type *stqh_first;/* first element */                     \
+       struct type **stqh_last;/* addr of last next element */         \
+}
+
+#define        STAILQ_HEAD_INITIALIZER(head)                                   \
+       { NULL, &(head).stqh_first }
+
+#define        STAILQ_ENTRY(type)                                              \
+struct {                                                               \
+       struct type *stqe_next; /* next element */                      \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define        STAILQ_EMPTY(head)      ((head)->stqh_first == NULL)
+
+#define        STAILQ_FIRST(head)      ((head)->stqh_first)
+
+#define        STAILQ_FOREACH(var, head, field)                                \
+       for((var) = STAILQ_FIRST((head));                               \
+          (var);                                                       \
+          (var) = STAILQ_NEXT((var), field))
+
+#define        STAILQ_INIT(head) do {                                          \
+       STAILQ_FIRST((head)) = NULL;                                    \
+       (head)->stqh_last = &STAILQ_FIRST((head));                      \
+} while (0)
+
+#define        STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {               \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_NEXT((tqelm), field) = (elm);                            \
+} while (0)
+
+#define        STAILQ_INSERT_HEAD(head, elm, field) do {                       \
+       if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+               (head)->stqh_last = &STAILQ_NEXT((elm), field);         \
+       STAILQ_FIRST((head)) = (elm);                                   \
+} while (0)
+
+#define        STAILQ_INSERT_TAIL(head, elm, field) do {                       \
+       STAILQ_NEXT((elm), field) = NULL;                               \
+       *(head)->stqh_last = (elm);                                     \
+       (head)->stqh_last = &STAILQ_NEXT((elm), field);                 \
+} while (0)
+
+#define        STAILQ_LAST(head, type, field)                                  \
+       (STAILQ_EMPTY(head) ?                                           \
+               NULL :                                                  \
+               strbase(type, (head)->stqh_last, field))
+
+#define        STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define        STAILQ_REMOVE(head, elm, type, field) do {                      \
+       if (STAILQ_FIRST((head)) == (elm)) {                            \
+               STAILQ_REMOVE_HEAD(head, field);                        \
+       }                                                               \
+       else {                                                          \
+               struct type *curelm = STAILQ_FIRST((head));             \
+               while (STAILQ_NEXT(curelm, field) != (elm))             \
+                       curelm = STAILQ_NEXT(curelm, field);            \
+               if ((STAILQ_NEXT(curelm, field) =                       \
+                    STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+                       (head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+       }                                                               \
+} while (0)
+
+#define        STAILQ_REMOVE_HEAD(head, field) do {                            \
+       if ((STAILQ_FIRST((head)) =                                     \
+            STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)         \
+               (head)->stqh_last = &STAILQ_FIRST((head));              \
+} while (0)
+
+#define        STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do {                 \
+       if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
+               (head)->stqh_last = &STAILQ_FIRST((head));              \
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define        LIST_HEAD(name, type)                                           \
+struct name {                                                          \
+       struct type *lh_first;  /* first element */                     \
+}
+
+#define        LIST_HEAD_INITIALIZER(head)                                     \
+       { NULL }
+
+#define        LIST_ENTRY(type)                                                \
+struct {                                                               \
+       struct type *le_next;   /* next element */                      \
+       struct type **le_prev;  /* address of previous next element */  \
+}
+
+/*
+ * List functions.
+ */
+
+#define        LIST_EMPTY(head)        ((head)->lh_first == NULL)
+
+#define        LIST_FIRST(head)        ((head)->lh_first)
+
+#define        LIST_FOREACH(var, head, field)                                  \
+       for ((var) = LIST_FIRST((head));                                \
+           (var);                                                      \
+           (var) = LIST_NEXT((var), field))
+
+#define        LIST_INIT(head) do {                                            \
+       LIST_FIRST((head)) = NULL;                                      \
+} while (0)
+
+#define        LIST_INSERT_AFTER(listelm, elm, field) do {                     \
+       if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+               LIST_NEXT((listelm), field)->field.le_prev =            \
+                   &LIST_NEXT((elm), field);                           \
+       LIST_NEXT((listelm), field) = (elm);                            \
+       (elm)->field.le_prev = &LIST_NEXT((listelm), field);            \
+} while (0)
+
+#define        LIST_INSERT_BEFORE(listelm, elm, field) do {                    \
+       (elm)->field.le_prev = (listelm)->field.le_prev;                \
+       LIST_NEXT((elm), field) = (listelm);                            \
+       *(listelm)->field.le_prev = (elm);                              \
+       (listelm)->field.le_prev = &LIST_NEXT((elm), field);            \
+} while (0)
+
+#define        LIST_INSERT_HEAD(head, elm, field) do {                         \
+       if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)     \
+               LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+       LIST_FIRST((head)) = (elm);                                     \
+       (elm)->field.le_prev = &LIST_FIRST((head));                     \
+} while (0)
+
+#define        LIST_NEXT(elm, field)   ((elm)->field.le_next)
+
+#define        LIST_REMOVE(elm, field) do {                                    \
+       if (LIST_NEXT((elm), field) != NULL)                            \
+               LIST_NEXT((elm), field)->field.le_prev =                \
+                   (elm)->field.le_prev;                               \
+       *(elm)->field.le_prev = LIST_NEXT((elm), field);                \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define        TAILQ_HEAD(name, type)                                          \
+struct name {                                                          \
+       struct type *tqh_first; /* first element */                     \
+       struct type **tqh_last; /* addr of last next element */         \
+}
+
+#define        TAILQ_HEAD_INITIALIZER(head)                                    \
+       { NULL, &(head).tqh_first }
+
+#define        TAILQ_ENTRY(type)                                               \
+struct {                                                               \
+       struct type *tqe_next;  /* next element */                      \
+       struct type **tqe_prev; /* address of previous next element */  \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define        TAILQ_EMPTY(head)       ((head)->tqh_first == NULL)
+
+#define        TAILQ_FIRST(head)       ((head)->tqh_first)
+
+#define        TAILQ_FOREACH(var, head, field)                                 \
+       for ((var) = TAILQ_FIRST((head));                               \
+           (var);                                                      \
+           (var) = TAILQ_NEXT((var), field))
+
+#define        TAILQ_FOREACH_REVERSE(var, head, headname, field)               \
+       for ((var) = TAILQ_LAST((head), headname);                      \
+           (var);                                                      \
+           (var) = TAILQ_PREV((var), headname, field))
+
+#define        TAILQ_INIT(head) do {                                           \
+       TAILQ_FIRST((head)) = NULL;                                     \
+       (head)->tqh_last = &TAILQ_FIRST((head));                        \
+} while (0)
+
+#define        TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   &TAILQ_NEXT((elm), field);                          \
+       else                                                            \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+       TAILQ_NEXT((listelm), field) = (elm);                           \
+       (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);          \
+} while (0)
+
+#define        TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \
+       (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \
+       TAILQ_NEXT((elm), field) = (listelm);                           \
+       *(listelm)->field.tqe_prev = (elm);                             \
+       (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);          \
+} while (0)
+
+#define        TAILQ_INSERT_HEAD(head, elm, field) do {                        \
+       if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)   \
+               TAILQ_FIRST((head))->field.tqe_prev =                   \
+                   &TAILQ_NEXT((elm), field);                          \
+       else                                                            \
+               (head)->tqh_last = &TAILQ_NEXT((elm), field);           \
+       TAILQ_FIRST((head)) = (elm);                                    \
+       (elm)->field.tqe_prev = &TAILQ_FIRST((head));                   \
+} while (0)
+
+#define        TAILQ_INSERT_TAIL(head, elm, field) do {                        \
+       TAILQ_NEXT((elm), field) = NULL;                                \
+       (elm)->field.tqe_prev = (head)->tqh_last;                       \
+       *(head)->tqh_last = (elm);                                      \
+       (head)->tqh_last = &TAILQ_NEXT((elm), field);                   \
+} while (0)
+
+#define        TAILQ_LAST(head, headname)                                      \
+       (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define        TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define        TAILQ_PREV(elm, headname, field)                                \
+       (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define        TAILQ_REMOVE(head, elm, field) do {                             \
+       if ((TAILQ_NEXT((elm), field)) != NULL)                         \
+               TAILQ_NEXT((elm), field)->field.tqe_prev =              \
+                   (elm)->field.tqe_prev;                              \
+       else                                                            \
+               (head)->tqh_last = (elm)->field.tqe_prev;               \
+       *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);              \
+} while (0)
+
+/*
+ * Circular queue declarations.
+ */
+#define        CIRCLEQ_HEAD(name, type)                                        \
+struct name {                                                          \
+       struct type *cqh_first;         /* first element */             \
+       struct type *cqh_last;          /* last element */              \
+}
+
+#define        CIRCLEQ_HEAD_INITIALIZER(head)                                  \
+       { (void *)&(head), (void *)&(head) }
+
+#define        CIRCLEQ_ENTRY(type)                                             \
+struct {                                                               \
+       struct type *cqe_next;          /* next element */              \
+       struct type *cqe_prev;          /* previous element */          \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define        CIRCLEQ_EMPTY(head)     ((head)->cqh_first == (void *)(head))
+
+#define        CIRCLEQ_FIRST(head)     ((head)->cqh_first)
+
+#define        CIRCLEQ_FOREACH(var, head, field)                               \
+       for ((var) = CIRCLEQ_FIRST((head));                             \
+           (var) != (void *)(head);                                    \
+           (var) = CIRCLEQ_NEXT((var), field))
+
+#define        CIRCLEQ_FOREACH_REVERSE(var, head, field)                       \
+       for ((var) = CIRCLEQ_LAST((head));                              \
+           (var) != (void *)(head);                                    \
+           (var) = CIRCLEQ_PREV((var), field))
+
+#define        CIRCLEQ_INIT(head) do {                                         \
+       CIRCLEQ_FIRST((head)) = (void *)(head);                         \
+       CIRCLEQ_LAST((head)) = (void *)(head);                          \
+} while (0)
+
+#define        CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {            \
+       CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field);    \
+       CIRCLEQ_PREV((elm), field) = (listelm);                         \
+       if (CIRCLEQ_NEXT((listelm), field) == (void *)(head))           \
+               CIRCLEQ_LAST((head)) = (elm);                           \
+       else                                                            \
+               CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\
+       CIRCLEQ_NEXT((listelm), field) = (elm);                         \
+} while (0)
+
+#define        CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {           \
+       CIRCLEQ_NEXT((elm), field) = (listelm);                         \
+       CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field);    \
+       if (CIRCLEQ_PREV((listelm), field) == (void *)(head))           \
+               CIRCLEQ_FIRST((head)) = (elm);                          \
+       else                                                            \
+               CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\
+       CIRCLEQ_PREV((listelm), field) = (elm);                         \
+} while (0)
+
+#define        CIRCLEQ_INSERT_HEAD(head, elm, field) do {                      \
+       CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head));             \
+       CIRCLEQ_PREV((elm), field) = (void *)(head);                    \
+       if (CIRCLEQ_LAST((head)) == (void *)(head))                     \
+               CIRCLEQ_LAST((head)) = (elm);                           \
+       else                                                            \
+               CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm);     \
+       CIRCLEQ_FIRST((head)) = (elm);                                  \
+} while (0)
+
+#define        CIRCLEQ_INSERT_TAIL(head, elm, field) do {                      \
+       CIRCLEQ_NEXT((elm), field) = (void *)(head);                    \
+       CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head));              \
+       if (CIRCLEQ_FIRST((head)) == (void *)(head))                    \
+               CIRCLEQ_FIRST((head)) = (elm);                          \
+       else                                                            \
+               CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm);      \
+       CIRCLEQ_LAST((head)) = (elm);                                   \
+} while (0)
+
+#define        CIRCLEQ_LAST(head)      ((head)->cqh_last)
+
+#define        CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
+
+#define        CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
+
+#define        CIRCLEQ_REMOVE(head, elm, field) do {                           \
+       if (CIRCLEQ_NEXT((elm), field) == (void *)(head))               \
+               CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field);      \
+       else                                                            \
+               CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) =       \
+                   CIRCLEQ_PREV((elm), field);                         \
+       if (CIRCLEQ_PREV((elm), field) == (void *)(head))               \
+               CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field);     \
+       else                                                            \
+               CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) =       \
+                   CIRCLEQ_NEXT((elm), field);                         \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H */
+/* vi:set ts=8 sts=4 sw=4 tw=79: */
diff --git a/include/res.h b/include/res.h
new file mode 100644 (file)
index 0000000..a324615
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * irc2.7.2/ircd/res.h (C)opyright 1992 Darren Reed.
+ */
+
+/* $Id: res.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef        __res_include__
+#define        __res_include__
+
+#define        RES_INITLIST    1
+#define        RES_CALLINIT    2
+#define RES_INITSOCK   4
+#define RES_INITDEBG   8
+#define RES_INITCACH    16
+
+#define MAXPACKET      1024
+#define IRC_MAXALIASES 10
+#define IRC_MAXADDRS   10
+
+#define        AR_TTL          600      /* TTL in seconds for dns cache entries */
+
+struct hent 
+{
+    char       *h_name;                    /* official name of host */
+    char       *h_aliases[IRC_MAXALIASES];     /* alias list */
+    int         h_addrtype;        /* host address type */
+    int         h_length;          /* length of address */
+    
+    /* list of addresses from name server */
+    struct in_addr h_addr_list[IRC_MAXADDRS];
+    
+#define        h_addr  h_addr_list[0]      /* address, for backward compatiblity */
+};
+
+typedef struct reslist 
+{
+    int         id;
+    int         sent;                  /* number of requests sent */
+    int         srch;
+    time_t      ttl;
+    char        type;
+    char        retries;               /* retry counter */
+    char        sends;                 /* number of sends (>1 means resent) */
+    char        resend;                        /* send flag. 0 == dont resend */
+    time_t      sentat;
+    time_t      timeout;
+    struct in_addr addr;
+    char       *name;
+    Link        cinfo;
+    struct hent he;
+    int         has_rev;                /* is he_rev valid? */
+    struct hent he_rev;
+
+    struct reslist *next;
+    struct reslist *id_hashnext;
+    struct reslist *cp_hashnext;
+} ResRQ;
+
+typedef struct cache 
+{
+    time_t      expireat;
+    time_t      ttl;
+    struct hostent he;
+    struct cache *hname_next, *hnum_next, *list_next;
+} aCache;
+
+typedef struct cachetable 
+{
+    aCache     *num_list;
+    aCache     *name_list;
+} CacheTable;
+
+typedef struct reshash
+{
+    ResRQ *id_list;
+    ResRQ *cp_list;
+} ResHash;
+
+#define ARES_CACSIZE   307
+#define ARES_IDCACSIZE  2099
+
+#define        IRC_MAXCACHED   281
+
+#endif /* __res_include__ */
diff --git a/include/resolv.h b/include/resolv.h
new file mode 100644 (file)
index 0000000..e8f6ad5
--- /dev/null
@@ -0,0 +1,120 @@
+#ifndef LOCAL_RESOLV_H
+#define LOCAL_RESOLV_H 1
+
+/*
+ * Copyright (c) 1983, 1987, 1989 The Regents of the University of
+ * California. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its
+ * contributors'' in the documentation or other materials provided with
+ * the distribution and in all advertising materials mentioning
+ * features or use of this software. Neither the name of the University
+ * nor the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ * 
+ * @(#)resolv.h 5.10.1 (Berkeley) 6/1/90
+ */
+/*
+ * Resolver configuration file. Normally not present, but may contain
+ * the address of the inital name server(s) to query and the domain
+ * search list.
+ */
+
+/* $Id: resolv.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef        _PATH_RESCONF
+#define _PATH_RESCONF        "/etc/resolv.conf"
+#endif
+/* Global defines and variables for resolver stub. */
+#define        MAXNS           3       /* max # name servers we'll track */
+#define        MAXDFLSRCH      3       /* # default domain levels to try */
+#define        MAXDNSRCH       6       /* max # domains in search path */
+#define        LOCALDOMAINPARTS 2      /* min levels in name that is "local" */
+#define MAXSERVICES    2       /* max # of services to search */
+
+#define        RES_TIMEOUT     5       /* min. seconds between retries */
+
+#define RES_SERVICE_NONE       0
+#define RES_SERVICE_BIND       1
+#define RES_SERVICE_LOCAL      2
+/* Resolver options */
+#define RES_INIT       0x0001  /* address initialized */
+#define RES_DEBUG      0x0002  /* print debug messages */
+#define RES_AAONLY     0x0004  /* authoritative answers only */
+#define RES_USEVC      0x0008  /* use virtual circuit */
+#define RES_PRIMARY    0x0010  /* query primary server only */
+#define RES_IGNTC      0x0020  /* ignore trucation errors */
+#define RES_RECURSE    0x0040  /* recursion desired */
+#define RES_DEFNAMES   0x0080  /* use default domain name */
+#define RES_STAYOPEN   0x0100  /* Keep TCP socket open */
+#define RES_DNSRCH     0x0200  /* search up local domain tree */
+
+#define RES_DEFAULT    (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH)
+
+#if ((__GNU_LIBRARY__ == 6) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 3))
+# define MAXRESOLVSORT         10      /* number of net to sort on */
+
+struct __res_state {
+       int     retrans;                /* retransmition time interval */
+       int     retry;                  /* number of times to retransmit */
+       u_long  options;                /* option flags - see below. */
+       int     nscount;                /* number of name servers */
+       struct sockaddr_in
+               nsaddr_list[MAXNS];     /* address of name server */
+# define nsaddr        nsaddr_list[0]          /* for backward compatibility */
+       u_short id;                     /* current message id */
+       char    *dnsrch[MAXDNSRCH+1];   /* components of domain to search */
+       char    defdname[256];          /* default domain (deprecated) */
+       u_long  pfcode;                 /* RES_PRF_ flags - see below. */
+       unsigned ndots:4;               /* threshold for initial abs. query */
+       unsigned nsort:4;               /* number of elements in sort_list[] */
+       char    unused[3];
+       struct {
+               struct in_addr  addr;
+               u_int32_t       mask;
+       } sort_list[MAXRESOLVSORT];
+};
+
+typedef struct __res_state *res_state;
+
+extern struct __res_state *__res_state(void) __attribute__ ((__const__));
+#define _res (*__res_state())
+
+#else
+
+struct state {
+    int         retrans;               /* retransmition time interval */
+    int         retry;                 /* number of times to retransmit */
+    long        options;               /* option flags - see below. */
+    int         nscount;               /* number of name servers */
+    struct sockaddr_in nsaddr_list[MAXNS];     /* address of name server */
+#define        nsaddr  nsaddr_list[0]          /* for backward compatibility */
+    unsigned short id;                 /* current packet id */
+    char        defdname[MAXDNAME];    /* default domain */
+    char       *dnsrch[MAXDNSRCH + 1]; /* components of domain to search */
+    unsigned short order[MAXSERVICES + 1];     /* search service order */
+};
+
+extern struct state _res;
+#endif
+
+extern char *p_cdname(), *p_rr(), *p_type(), *p_class(), *p_time();
+
+#if ((__GNU_LIBRARY__ == 6) && (__GLIBC__ >=2) && (__GLIBC_MINOR__ >= 2))
+#define res_init __res_init
+#define res_mkquery __res_mkquery
+#define dn_expand __dn_expand
+#endif
+
+extern int  res_mkquery ();
+extern int  dn_expand ();
+extern int  res_init();
+#endif
diff --git a/include/sbuf.h b/include/sbuf.h
new file mode 100644 (file)
index 0000000..34914ef
--- /dev/null
@@ -0,0 +1,65 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/sbuf.h
+ *   Copyright (C) 2004 David Parton
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/* $Id: sbuf.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef SBUF_H
+#define SBUF_H
+
+/* Definitions */
+#define SBUF_LARGE_BUFFER                      512
+#define SBUF_SMALL_BUFFER                      256
+
+/* Macros */
+#define IsEol(c)                    ((c) == '\r' || (c) == '\n')
+#define SBufLength(s)                          ((s)->length)
+#define SBufClear(s)                           sbuf_delete((s), (s)->length)
+
+/* forward declaration */
+struct _SBufUser;
+struct _SBuf;
+
+
+typedef struct _SBuf
+{
+    int       length;
+    struct _SBufUser *head, *tail;
+} SBuf;
+
+/* function protoypes */
+
+extern int                     sbuf_init();
+extern int                     sbuf_begin_share(const char* theData, int theLength, void** thePtr);
+extern int          sbuf_end_share(void** thePtr, int theNum);
+extern int          sbuf_put_share(SBuf* theBuf, void *theSBuffer);
+extern int                     sbuf_put(SBuf* theBuf, const char* theData, int theLength);
+extern int                     sbuf_delete(SBuf* theBuf, int theLength);
+extern char*           sbuf_map(SBuf* theBuf, int* theLength);
+extern int          sbuf_flush(SBuf* theBuf);
+extern int          sbuf_getmsg(SBuf* theBuf, char* theData, int theLength);
+extern int          sbuf_get(SBuf* theBuf, char* theData, int theLength);
+extern void         sbuf_count(int* userUsed, int* userTotal, int* userSize,
+                               int* smallUsed, int* smallTotal, int* smallSize,
+                               int* largeUsed, int* largeTotal, int* largeSize,
+                               int* blockUsed, int* blockSize, int* userblockUsed, int* userblockSize);
+
+#ifdef WRITEV_IOV
+extern int          sbuf_mapiov(SBuf *, struct iovec *);
+#endif
+
+#endif /* #ifndef SBUF_H */
diff --git a/include/send.h b/include/send.h
new file mode 100644 (file)
index 0000000..c089fd3
--- /dev/null
@@ -0,0 +1,91 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/h.h
+ *   Copyright (C) 1992 Darren Reed
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+ * "send.h". - Headers file.
+ * 
+ * all the send* functions are declared here.
+ */
+
+/* $Id: send.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef SEND_H
+#define SEND_H
+
+/* send all queued crap to aClient */
+extern int  send_queued(aClient *);
+
+#include <stdarg.h>
+#include "fdlist.h"
+
+extern void init_send();
+
+extern void send_chatops(char *pattern, ...);
+extern void send_globops(char *pattern, ...);
+extern void send_operwall(aClient *, char *, char *);
+
+extern void sendto_all_butone(aClient *one, aClient *from, char *pattern, ...);
+extern void sendto_channel_butone(aClient *one, aClient *from, 
+                                 aChannel *chptr, char *pattern, ...);
+extern void sendto_channel_remote_butone(aClient *one, aClient *from, 
+                                        aChannel *chptr, char *pattern, ...);
+extern void sendto_channel_butserv(aChannel *chptr, aClient *from,
+                                  char *pattern, ...);
+extern void sendto_channel_butserv_me(aChannel *chptr, aClient *from,
+                                     char *pattern, ...);
+extern void sendto_channelops_butserv(aChannel *chptr, aClient *from,
+                                     char *pattern, ...);
+extern void sendto_channelops_butone(aClient *one, aClient *from,
+                                    aChannel *chptr,char *pattern, ...);
+extern void sendto_channelvoice_butone(aClient *one, aClient *from,
+                                      aChannel *chptr, char *pattern, ...);
+extern void sendto_channelvoiceops_butone(aClient *one, aClient *from,
+                                         aChannel *chptr, char *patern, ...);
+extern void sendto_common_channels(aClient *user, char *pattern, ...);
+extern void send_quit_to_common_channels(aClient *from, char *reason);
+extern void send_part_to_common_channels(aClient *from, char *reason);
+extern void sendto_fdlist(fdlist *listp, char *pattern, ...);
+extern void sendto_locops(char *pattern, ...);
+extern void sendto_match_butone(aClient *one, aClient *from, char *mask,
+                               int what, char *pattern, ...);
+extern void sendto_one(aClient *to, char *pattern, ...);
+extern void sendto_one_services(aClient *to, char *pattern, ...);
+extern void sendto_ops(char *pattern, ...);
+extern void sendto_ops_butone(aClient *one, aClient *from, char *pattern, ...);
+extern void sendto_ops_lev(int lev, char *pattern, ...);
+extern void sendto_prefix_one(aClient *to, aClient *from, char *pattern, ...);
+
+extern void sendto_realops_lev(int lev, char *pattern, ...);
+extern void sendto_realops(char *pattern, ...);
+extern void sendto_serv_butone(aClient *one, char *pattern, ...);
+extern void sendto_serv_butone_services(aClient *one, char *pattern, ...);
+extern void sendto_wallops_butone(aClient *one, aClient *from,
+                                 char *pattern, ...);
+extern void sendto_gnotice(char *pattern, ...);
+
+extern void ts_warn(char *pattern, ...);
+
+extern void vsendto_fdlist(fdlist *listp, char *pattern, va_list vl);
+extern void vsendto_one(aClient *to, char *pattern, va_list vl);
+extern void vsendto_prefix_one(aClient *to, aClient *from,
+                              char *pattern, va_list vl);
+extern void vsendto_realops(char *pattern, va_list vl);
+
+extern void flush_connections();
+extern void dump_connections();
+#endif
diff --git a/include/setup.h.in b/include/setup.h.in
new file mode 100644 (file)
index 0000000..6220569
--- /dev/null
@@ -0,0 +1,266 @@
+/* include/setup.h.in.  Generated from configure.in by autoheader.  */
+
+/* AIX support */
+#undef AIX
+
+/* If on a system with a broken FD_SETSIZE */
+#undef BROKEN_FD_SETSIZE
+
+/* Define to 1 if you have the `alarm' function. */
+#undef HAVE_ALARM
+
+/* Define to 1 if you have the `bcmp' function. */
+#undef HAVE_BCMP
+
+/* Define to 1 if you have the `bcopy' function. */
+#undef HAVE_BCOPY
+
+/* Define to 1 if you have the `break' function. */
+#undef HAVE_BREAK
+
+/* Define to 1 if you have the `bzero' function. */
+#undef HAVE_BZERO
+
+/* Define to 1 if you have the `dlopen' function. */
+#undef HAVE_DLOPEN
+
+/* Define to 1 if you have the `dlsym' function. */
+#undef HAVE_DLSYM
+
+/* Define to 1 if you have the `dn_skipname' function. */
+#undef HAVE_DN_SKIPNAME
+
+/* Define to 1 if you have the `dup2' function. */
+#undef HAVE_DUP2
+
+/* If we support encryption */
+#undef HAVE_ENCRYPTION_ON
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `gethostbyname' function. */
+#undef HAVE_GETHOSTBYNAME
+
+/* Define to 1 if you have the `gethostname' function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#undef HAVE_GETOPT_H
+
+/* Define to 1 if you have the `getpass' function. */
+#undef HAVE_GETPASS
+
+/* Define to 1 if you have the `getrusage' function. */
+#undef HAVE_GETRUSAGE
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the `index' function. */
+#undef HAVE_INDEX
+
+/* Define to 1 if you have the `inet_addr' function. */
+#undef HAVE_INET_ADDR
+
+/* Define to 1 if you have the `inet_aton' function. */
+#undef HAVE_INET_ATON
+
+/* Define to 1 if you have the `inet_netof' function. */
+#undef HAVE_INET_NETOF
+
+/* Define to 1 if you have the `inet_ntoa' function. */
+#undef HAVE_INET_NTOA
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `isascii' function. */
+#undef HAVE_ISASCII
+
+/* Define to 1 if you have the `crypt' library (-lcrypt). */
+#undef HAVE_LIBCRYPT
+
+/* Define to 1 if you have the `descrypt' library (-ldescrypt). */
+#undef HAVE_LIBDESCRYPT
+
+/* Define to 1 if you have the `dl' library (-ldl). */
+#undef HAVE_LIBDL
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+#undef HAVE_LIBRESOLV
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if you have the `lrand48' function. */
+#undef HAVE_LRAND48
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* If we have the MIN and MAX macros */
+#undef HAVE_MINMAX
+
+/* Define to 1 if you have the `munmap' function. */
+#undef HAVE_MUNMAP
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the `setenv' function. */
+#undef HAVE_SETENV
+
+/* Define to 1 if you have the `sigaction' function. */
+#undef HAVE_SIGACTION
+
+/* Define to 1 if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#undef HAVE_STDDEF_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#undef HAVE_STRNCASECMP
+
+/* Define to 1 if you have the `strrchr' function. */
+#undef HAVE_STRRCHR
+
+/* Define to 1 if you have the `strtok' function. */
+#undef HAVE_STRTOK
+
+/* Define to 1 if you have the `strtoken' function. */
+#undef HAVE_STRTOKEN
+
+/* Define to 1 if you have the `strtol' function. */
+#undef HAVE_STRTOL
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#undef HAVE_SYS_RESOURCE_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the `times' function. */
+#undef HAVE_TIMES
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <utmp.h> header file. */
+#undef HAVE_UTMP_H
+
+/* Define to 1 if you have the `__dn_skipname' function. */
+#undef HAVE___DN_SKIPNAME
+
+/* Maximum Connections we allow */
+#undef MAXCONNECTIONS
+
+/* epoll behavior */
+#undef NEED_EPOLL_DEFS
+
+/* Not quite sure why we have two of these */
+#undef OS_SOLARIS
+
+/* If we're running on Solaris */
+#undef OS_SOLARIS2
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* If the sys_errlist array is defined */
+#undef SYS_ERRLIST_DECLARED
+
+/* Do we support loadable modules */
+#undef USE_HOOKMODULES
+
+/* Maxmimum number of iovecs supported by writev() */
+#undef WRITEV_IOV
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+   if it is not supported. */
+#undef inline
diff --git a/include/sock.h b/include/sock.h
new file mode 100644 (file)
index 0000000..70222d4
--- /dev/null
@@ -0,0 +1,28 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/sock.h
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: sock.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef FD_ZERO
+#define FD_ZERO(set)      (((set)->fds_bits[0]) = 0)
+#define FD_SET(s1, set)   (((set)->fds_bits[0]) |= 1 << (s1))
+#define FD_ISSET(s1, set) (((set)->fds_bits[0]) & (1 << (s1)))
+#define FD_SETSIZE        30
+#endif
diff --git a/include/struct.h b/include/struct.h
new file mode 100644 (file)
index 0000000..6740202
--- /dev/null
@@ -0,0 +1,1429 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/struct.h
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+/* $Id: struct.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef        __struct_include__
+#define __struct_include__
+
+#include "config.h"
+#if !defined(CONFIG_H_LEVEL_183)
+#error Incorrect config.h for this revision of ircd.
+#endif
+
+#include "sys.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#if defined( HAVE_STDDEF_H )
+#include <stddef.h>
+#endif
+#ifdef ORATIMING
+#include <sys/time.h>
+#endif
+
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#if defined( HAVE_SYS_SYSLOG_H )
+#include <sys/syslog.h>
+#endif
+#endif
+
+#define REPORT_DO_DNS_    ":%s NOTICE AUTH :*** Looking up your hostname..."
+#define REPORT_FIN_DNS_           ":%s NOTICE AUTH :*** Found your hostname"
+#define REPORT_FIN_DNSC_   ":%s NOTICE AUTH :*** Found your hostname, cached"
+#define REPORT_FAIL_DNS_   ":%s NOTICE AUTH :*** Couldn't look up your "\
+                           "hostname"
+#define REPORT_DO_ID_     ":%s NOTICE AUTH :*** Checking Ident"
+#define REPORT_FIN_ID_    ":%s NOTICE AUTH :*** Got Ident response"
+#define REPORT_FAIL_ID_           ":%s NOTICE AUTH :*** No Ident response"
+
+extern char REPORT_DO_DNS[256], REPORT_FIN_DNS[256], REPORT_FIN_DNSC[256], 
+    REPORT_FAIL_DNS[256], REPORT_DO_ID[256], REPORT_FIN_ID[256], 
+    REPORT_FAIL_ID[256];
+
+#include "hash.h"
+
+#include "sbuf.h"
+
+typedef struct Client aClient;
+typedef struct Channel aChannel;
+typedef struct User anUser;
+typedef struct Server aServer;
+typedef struct SLink Link;
+typedef struct SLinkD DLink;
+typedef struct ChanLink chanMember;
+typedef struct SMode Mode;
+typedef struct Watch aWatch;
+typedef struct Ban aBan;
+#ifdef INVITE_LISTS
+typedef struct LInvite anInvite;
+#endif
+#ifdef EXEMPT_LISTS
+typedef struct Exempt aBanExempt;
+#endif
+typedef struct ListOptions LOpts;
+typedef struct Listener aListener;
+typedef struct Conf_Connect aConnect;
+typedef struct Conf_Allow aAllow;
+typedef struct Conf_Oper aOper;
+typedef struct Conf_Me Conf_Me;
+typedef struct Conf_Port aPort;
+typedef struct Conf_Class aClass;
+typedef struct Conf_Modules Conf_Modules;
+typedef long ts_val;
+
+typedef struct MotdItem aMotd;
+
+
+
+#define        HOSTLEN         63      /* Length of hostname.  Updated to */
+
+/* comply with RFC1123 */
+
+#define HOSTIPLEN      15      /* Length of dotted quad form of IP */
+
+#define        NICKLEN         30      
+
+/* Necessary to put 9 here instead of 10  if  
+ * s_msg.c/m_nick has been corrected.  This 
+ * preserves compatibility with old * servers --msa 
+ */
+
+#define MAX_DATE_STRING 32     /* maximum string length for a date string */
+
+#define        USERLEN             10
+#define        REALLEN             50
+#define        TOPICLEN            307
+#define        KILLLEN             400
+#define        CHANNELLEN          32
+#define        PASSWDLEN           63
+#define        KEYLEN              23
+#define        BUFSIZE             512 /* WARNING: *DONT* CHANGE THIS!!!! */
+#define        MAXRECIPIENTS       20
+#define        MAXBANS             100
+#define MAXINVITELIST       45
+#define MAXEXEMPTLIST       45
+
+#define MOTDLINELEN        90
+
+#define        MAXSILES        10
+#define        MAXSILELENGTH   128
+
+#define MAXDCCALLOW 5
+#define DCC_LINK_ME    0x01    /* This is my dcc allow */
+#define DCC_LINK_REMOTE 0x02    /* I need to remove these dcc allows from
+                                * these clients when I die
+                                */
+
+#define        USERHOST_REPLYLEN       (NICKLEN+HOSTLEN+USERLEN+5)
+
+/*
+ * 'offsetof' is defined in ANSI-C. The following definition * is not
+ * absolutely portable (I have been told), but so far * it has worked
+ * on all machines I have needed it. The type * should be size_t but...
+ * --msa
+ */
+
+#ifndef offsetof
+#define        offsetof(t,m) (int)((&((t *)0L)->m))
+#endif
+
+#define        elementsof(x) (sizeof(x)/sizeof(x[0]))
+
+/* flags for bootup options (command line flags) */
+
+#define        BOOT_CONSOLE     1
+#define        BOOT_QUICK       2
+#define        BOOT_DEBUG       4
+#define        BOOT_TTY         8
+#define        BOOT_OPER        16
+#define BOOT_STDERR     64
+#define        STAT_LOG         -6     /* logfile for -x */
+#define        STAT_MASTER      -5     /* Local ircd master before identification */
+#define        STAT_CONNECTING  -4
+#define        STAT_HANDSHAKE   -3
+#define        STAT_ME          -2
+#define        STAT_UNKNOWN     -1
+/* the line of truth lies here (truth == registeredness) */
+#define        STAT_SERVER      0
+#define        STAT_CLIENT      1
+
+/* status macros. */
+
+#define        IsRegisteredUser(x)     ((x)->status == STAT_CLIENT)
+#define        IsRegistered(x)         ((x)->status >= STAT_SERVER)
+#define        IsConnecting(x)         ((x)->status == STAT_CONNECTING)
+#define        IsHandshake(x)          ((x)->status == STAT_HANDSHAKE)
+#define        IsMe(x)                 ((x)->status == STAT_ME)
+#define        IsUnknown(x)            ((x)->status == STAT_UNKNOWN || \
+                                (x)->status == STAT_MASTER)
+#define        IsServer(x)             ((x)->status == STAT_SERVER)
+#define        IsClient(x)             ((x)->status == STAT_CLIENT)
+#define        IsLog(x)                ((x)->status == STAT_LOG)
+
+#define        SetMaster(x)            ((x)->status = STAT_MASTER)
+#define        SetConnecting(x)        ((x)->status = STAT_CONNECTING)
+#define        SetHandshake(x)         ((x)->status = STAT_HANDSHAKE)
+#define        SetMe(x)                ((x)->status = STAT_ME)
+#define        SetUnknown(x)           ((x)->status = STAT_UNKNOWN)
+#define        SetServer(x)            ((x)->status = STAT_SERVER)
+#define        SetClient(x)            ((x)->status = STAT_CLIENT)
+#define        SetLog(x)               ((x)->status = STAT_LOG)
+
+#define        FLAGS_PINGSENT     0x000001   /* Unreplied ping sent */
+#define        FLAGS_DEADSOCKET   0x000002   /* Local socket is dead--Exiting soon */
+#define        FLAGS_KILLED       0x000004   /* Prevents "QUIT" from being sent for
+                                      * this */
+#define        FLAGS_BLOCKED      0x000008   /* socket is in a blocked condition */
+#define        FLAGS_CLOSING      0x000010   /* set when closing to suppress errors */
+#define        FLAGS_LISTEN       0x000020   /* used to mark clients which we listen()
+                                      * on */
+#define FLAGS_HAVERECVQ           0x000040   /* Client has full commands in their recvq */
+#define        FLAGS_DOINGDNS     0x000080   /* client is waiting for a DNS 
+                                      * response */
+#define        FLAGS_AUTH         0x000100   /* client is waiting on rfc931 
+                                      * response */
+#define        FLAGS_WRAUTH       0x000200   /* set if we havent writen to ident 
+                                      * server */
+#define        FLAGS_LOCAL        0x000400   /* set for local clients */
+#define        FLAGS_GOTID        0x000800   /* successful ident lookup achieved */
+#define        FLAGS_DOID         0x001000   /* I-lines say must use ident return */
+#define        FLAGS_NONL         0x002000   /* No \n in buffer */
+#define FLAGS_NORMALEX     0x004000   /* Client exited normally */
+#define FLAGS_SENDQEX      0x008000   /* Sendq exceeded */
+#define FLAGS_ULINE       0x020000   /* client is U-lined */
+#define FLAGS_USERBURST           0x040000   /* server in nick/channel netburst */
+#define FLAGS_TOPICBURST   0x080000   /* server in topic netburst */
+#define FLAGS_BURST       (FLAGS_USERBURST | FLAGS_TOPICBURST)
+#define FLAGS_SOBSENT      0x100000   /* we've sent an SOB, just have to 
+                                      * send an EOB */
+#define FLAGS_EOBRECV      0x200000   /* we're waiting on an EOB */
+#define FLAGS_BAD_DNS     0x400000   /* spoofer-guy */
+#define FLAGS_SERV_NEGO           0x800000  /* This is a server that has passed
+                                      * connection tests, but is a stat < 0
+                                      * for handshake purposes */
+#define FLAGS_RC4IN        0x1000000  /* This link is rc4 encrypted. */
+#define FLAGS_RC4OUT       0x2000000  /* This link is rc4 encrypted. */
+#define FLAGS_ZIPPED_IN           0x4000000  /* This link is gzipped. */
+#define FLAGS_ZIPPED_OUT   0x8000000 /* This link is gzipped. */
+
+/* Capabilities of the ircd or clients */
+
+#define CAPAB_DKEY    0x0001 /* server supports dh-key exchange */
+#define CAPAB_ZIP     0x0002 /* server supports gz'd links */
+#define CAPAB_DOZIP   0x0004 /* output to this link shall be gzipped */
+#define CAPAB_DODKEY  0x0008 /* do I do dkey with this link? */
+#define CAPAB_BURST   0x0010 /* server supports BURST command */
+#define CAPAB_UNCONN  0x0020 /* server supports UNCONNECT */
+#ifdef NOQUIT
+#define CAPAB_NOQUIT  0x0040 /* noquit support */
+#endif
+
+
+#define SetDKEY(x)     ((x)->capabilities |= CAPAB_DKEY)
+#define CanDoDKEY(x)    ((x)->capabilities & CAPAB_DKEY)
+/* N: line, flag E */
+#define SetWantDKEY(x) ((x)->capabilities |= CAPAB_DODKEY)
+#define WantDKEY(x)    ((x)->capabilities & CAPAB_DODKEY) 
+
+#define SetZipCapable(x) ((x)->capabilities |= CAPAB_ZIP)
+#define IsZipCapable(x)        ((x)->capabilities & CAPAB_ZIP)
+/* this is set in N: line, flag Z */
+#define DoZipThis(x)   ((x)->capabilities & CAPAB_DOZIP) 
+
+#define SetBurst(x)     ((x)->capabilities |= CAPAB_BURST)
+#define IsBurst(x)      ((x)->capabilities & CAPAB_BURST)
+
+#define SetUnconnect(x) ((x)->capabilities |= CAPAB_UNCONN)
+#define IsUnconnect(x)  ((x)->capabilities & CAPAB_UNCONN)
+
+#ifdef NOQUIT
+#define SetNoquit(x)    ((x)->capabilities |= CAPAB_NOQUIT)
+#define IsNoquit(x)     ((x)->capabilities & CAPAB_NOQUIT)
+#endif
+
+
+/* flag macros. */
+#define IsULine(x) ((x)->flags & FLAGS_ULINE)
+
+
+/* User Modes */
+#define UMODE_o     0x00001    /* umode +o - Oper */
+#define UMODE_O     0x00002    /* umode +O - Local Oper */
+#define UMODE_i     0x00004    /* umode +i - Invisible */
+#define UMODE_w     0x00008    /* umode +w - Get wallops */
+#define UMODE_s     0x00010    /* umode +s - Server notices */
+#define UMODE_c     0x00020    /* umode +c - Client connections/exits */
+#define UMODE_r     0x00040    /* umode +r - registered nick */
+#define UMODE_k     0x00080    /* umode +k - Server kill messages */
+#define UMODE_f     0x00100    /* umode +f - Server flood messages */
+#define UMODE_y     0x00200    /* umode +y - Stats/links */
+#define UMODE_d     0x00400    /* umode +d - Debug info */
+#define UMODE_g     0x01000    /* umode +g - Globops */
+#define UMODE_b     0x02000    /* umode +b - Chatops */
+#define UMODE_a     0x04000    /* umode +a - Services Admin */
+#define UMODE_A     0x08000     /* umode +A - Server Admin */
+#define UMODE_n     0x10000    /* umode +n - Routing Notices */
+#define UMODE_h     0x20000     /* umode +h - Helper */
+#define UMODE_m     0x40000     /* umode +m - spambot notices */
+#define UMODE_R     0x80000     /* unmode +R - No non registered msgs */
+#define UMODE_e     0x100000    /* umode +e - oper notices for the above +D */
+#define UMODE_x     0x200000    /* umode +x - Squelch with notice */
+#define UMODE_X     0x400000    /* umode +X - Squelch without notice */
+#define UMODE_D     0x800000    /* umode +D - Hidden dccallow umode */
+#define UMODE_F     0x1000000   /* umode +F - no cptr->since message rate throttle */
+#define UMODE_j            0x2000000   /* umode +j - client rejection notices */
+#define UMODE_K     0x4000000   /* umode +K - U: lined server kill messages */
+#define UMODE_I     0x8000000   /* umode +I - invisible oper (masked) */
+
+/* for sendto_ops_lev */
+
+#define CCONN_LEV      1
+#define REJ_LEV                2
+#define SKILL_LEV      3
+#define SPY_LEV                4
+#define DEBUG_LEV      5
+#define FLOOD_LEV      6
+#define SPAM_LEV       7
+#define DCCSEND_LEV    8
+#define USKILL_LEV     9
+
+/* SEND_UMODES:
+ *  we send these to remote servers.
+ * ALL_UMODES
+ *  we send these to our clients.
+ *  if you don't put something in ALL_UMODES, 
+ *  that mode will be 'silent.'
+ */
+
+#define SEND_UMODES (UMODE_a|UMODE_i|UMODE_o|UMODE_r|UMODE_A|UMODE_I|UMODE_R)
+#define ALL_UMODES (SEND_UMODES|UMODE_b|UMODE_c|UMODE_d|UMODE_e|UMODE_f|\
+                    UMODE_g|UMODE_h|UMODE_j|UMODE_k|UMODE_m|UMODE_n|UMODE_s|\
+                    UMODE_w|UMODE_y|UMODE_F|UMODE_K|UMODE_O)
+
+/* modes users can set themselves */
+#define USER_UMODES (UMODE_i|UMODE_k|UMODE_w|UMODE_s|UMODE_R)
+
+/* modes only opers can have */
+#define OPER_UMODES (UMODE_a|UMODE_b|UMODE_c|UMODE_d|UMODE_e|UMODE_f|UMODE_g|\
+                     UMODE_h|UMODE_j|UMODE_m|UMODE_n|UMODE_y|UMODE_A|UMODE_F|\
+                     UMODE_K)
+
+#define        FLAGS_ID (FLAGS_DOID|FLAGS_GOTID)
+
+#define        IsOper(x)               ((x)->umode & UMODE_o)
+#define        IsLocOp(x)              ((x)->umode & UMODE_O)
+#define        IsInvisible(x)          ((x)->umode & UMODE_i)
+#define        IsAnOper(x)             ((x)->umode & (UMODE_o|UMODE_O))
+#define        CanBeOper(x)            ((x)->umode & (UMODE_o|UMODE_O|UMODE_I))
+#define IsARegNick(x)           ((x)->umode & (UMODE_r))
+#define IsRegNick(x)            ((x)->umode & UMODE_r)
+#define IsSAdmin(x)             ((x)->umode & UMODE_a)
+#define IsAdmin(x)              ((x)->umode & UMODE_A)
+#define IsUmodef(x)             ((x)->umode & UMODE_f)
+#define IsUmodec(x)             ((x)->umode & UMODE_c)
+#define IsUmodey(x)             ((x)->umode & UMODE_y)
+#define IsUmoded(x)             ((x)->umode & UMODE_d)
+#define IsUmodeb(x)             ((x)->umode & UMODE_b)
+#define IsUmoden(x)             ((x)->umode & UMODE_n)
+#define IsUmodem(x)             ((x)->umode & UMODE_m)
+#define IsUmodeh(x)             ((x)->umode & UMODE_h)
+#define IsUmodee(x)             ((x)->umode & UMODE_e)
+#define IsUmodej(x)            ((x)->umode & UMODE_j)
+#define        IsUmodeK(x)             ((x)->umode & UMODE_K)
+#define        IsUmodek(x)             ((x)->umode & UMODE_k)
+#define        IsUmodes(x)             ((x)->umode & UMODE_s)
+#define        IsUmodeI(x)             ((x)->umode & UMODE_I)
+#define IsNoNonReg(x)           ((x)->umode & UMODE_R)
+#define IsWSquelch(x)           ((x)->umode & UMODE_x)
+#define IsSSquelch(x)           ((x)->umode & UMODE_X)
+#define IsSquelch(x)            (IsSSquelch(x) || IsWSquelch(x))
+#define        IsPerson(x)             ((x)->user && IsClient(x))
+#define        IsPrivileged(x)         (IsAnOper(x) || IsServer(x))
+#define        SendWallops(x)          ((x)->umode & UMODE_w)
+#define        SendServNotice(x)       ((x)->umode & UMODE_s)
+#define SendCConnNotice(x)     ((x)->umode & UMODE_c)
+#define SendRejNotice(x)       ((x)->umode & UMODE_j)
+#define SendSkillNotice(x)     ((x)->umode & UMODE_k)
+#define SendSUkillNotice(x)    ((x)->umode & UMODE_K)
+#define SendSpyNotice(x)       ((x)->umode & UMODE_y)
+#define SendDCCNotice(x)       ((x)->umode & UMODE_e)
+#define SendFloodNotice(x)      ((x)->umode & UMODE_f)
+#define SendSpamNotice(x)       ((x)->umode & UMODE_m)
+#define SendDebugNotice(x)     ((x)->umode & UMODE_d)
+#define SendGlobops(x)          ((x)->umode & UMODE_g)
+#define SendChatops(x)          ((x)->umode & UMODE_b)
+#define SendRnotice(x)          ((x)->umode & UMODE_n)
+#define NoMsgThrottle(x)       ((x)->umode & UMODE_F)
+#define        IsListening(x)          ((x)->flags & FLAGS_LISTEN)
+#define        IsLocal(x)              ((x)->flags & FLAGS_LOCAL)
+#define        IsDead(x)               ((x)->flags & FLAGS_DEADSOCKET)
+#define        SetOper(x)              ((x)->umode |= UMODE_o)
+#define SetRegNick(x)           ((x)->umode |= UMODE_r)
+#define SetNoNonReg(x)          ((x)->umode |= UMODE_R)
+#define SetSAdmin(x)            ((x)->umode |= UMODE_a)
+#define        SetLocOp(x)             ((x)->umode |= UMODE_O)
+#define        SetInvisible(x)         ((x)->umode |= UMODE_i)
+#define        SetWallops(x)           ((x)->umode |= UMODE_w)
+#define SetWSquelch(x)          ((x)->umode |= UMODE_x)
+#define SetSSquelch(x)          ((x)->umode |= UMODE_X)
+#define SetNoMsgThrottle(x)    ((x)->umode |= UMODE_F)
+#define        SetDNS(x)               ((x)->flags |= FLAGS_DOINGDNS)
+#define        DoingDNS(x)             ((x)->flags & FLAGS_DOINGDNS)
+#define        DoingAuth(x)            ((x)->flags & FLAGS_AUTH)
+#define        NoNewLine(x)            ((x)->flags & FLAGS_NONL)
+#define SeenDCCNotice(x)        ((x)->umode & UMODE_D)
+#define SetDCCNotice(x)         ((x)->umode |= UMODE_D)
+
+#define SetNegoServer(x)       ((x)->flags |= FLAGS_SERV_NEGO)
+#define IsNegoServer(x)                ((x)->flags & FLAGS_SERV_NEGO)
+#define ClearNegoServer(x)     ((x)->flags &= ~FLAGS_SERV_NEGO)
+#define IsRC4OUT(x)            ((x)->flags & FLAGS_RC4OUT)
+#define SetRC4OUT(x)           ((x)->flags |= FLAGS_RC4OUT)
+#define IsRC4IN(x)             ((x)->flags & FLAGS_RC4IN)
+#define SetRC4IN(x)            ((x)->flags |= FLAGS_RC4IN)
+#define RC4EncLink(x)          (((x)->flags & (FLAGS_RC4IN|FLAGS_RC4OUT)) ==\
+                                 (FLAGS_RC4IN|FLAGS_RC4OUT))
+
+#define ZipIn(x)               ((x)->flags & FLAGS_ZIPPED_IN)
+#define SetZipIn(x)            ((x)->flags |= FLAGS_ZIPPED_IN)
+#define ZipOut(x)              ((x)->flags & FLAGS_ZIPPED_OUT)
+#define SetZipOut(x)           ((x)->flags |= FLAGS_ZIPPED_OUT)
+
+#define ClearSAdmin(x)          ((x)->umode &= ~UMODE_a)
+#define ClearAdmin(x)           ((x)->umode &= ~UMODE_A)
+#define ClearUmodef(x)          ((x)->umode &= ~UMODE_f)
+#define ClearUmodem(x)          ((x)->umode &= ~UMODE_m)
+#define ClearUmodec(x)          ((x)->umode &= ~UMODE_c)
+#define ClearUmodey(x)          ((x)->umode &= ~UMODE_y)
+#define ClearUmoded(x)          ((x)->umode &= ~UMODE_d)
+#define ClearUmodeb(x)          ((x)->umode &= ~UMODE_b)
+#define ClearUmoden(x)          ((x)->umode &= ~UMODE_n)
+#define ClearUmodeh(x)          ((x)->umode &= ~UMODE_h)
+#define ClearUmodee(x)          ((x)->umode &= ~UMODE_e)
+#define ClearUmodej(x)         ((x)->umode &= ~UMODE_j)
+#define ClearUmodeK(x)         ((x)->umode &= ~UMODE_K)
+#define ClearUmodek(x)         ((x)->umode &= ~UMODE_k)
+#define ClearUmodes(x)         ((x)->umode &= ~UMODE_s)
+#define ClearUmodeI(x)         ((x)->umode &= ~UMODE_I)
+#define ClearNoNonReg(x)        ((x)->umode &= ~UMODE_R)
+#define ClearWSquelch(x)        ((x)->umode &= ~UMODE_x)
+#define ClearSSquelch(x)        ((x)->umode &= ~UMODE_X)
+#define        ClearOper(x)            ((x)->umode &= ~UMODE_o)
+#define ClearLocOp(x)          ((x)->umode &= ~UMODE_O)
+#define        ClearInvisible(x)       ((x)->umode &= ~UMODE_i)
+#define        ClearWallops(x)         ((x)->umode &= ~UMODE_w)
+#define ClearNoMsgThrottle(x)  ((x)->umode &= ~UMODE_F)
+#define        ClearDNS(x)             ((x)->flags &= ~FLAGS_DOINGDNS)
+#define        ClearAuth(x)            ((x)->flags &= ~FLAGS_AUTH)
+
+/* Oper flags */
+
+/* defined operator access levels */ 
+#define OFLAG_REHASH   0x00000001  /* Oper can /rehash server */
+#define OFLAG_DIE      0x00000002  /* Oper can /die the server */
+#define OFLAG_RESTART  0x00000004  /* Oper can /restart the server */
+#define OFLAG_HELPOP   0x00000010  /* Oper can send /HelpOps */
+#define OFLAG_GLOBOP   0x00000020  /* Oper can send /GlobOps */
+#define OFLAG_WALLOP   0x00000040  /* Oper can send /WallOps */
+#define OFLAG_LOCOP    0x00000080  /* Oper can send /LocOps */
+#define OFLAG_LROUTE   0x00000100  /* Oper can do local routing */
+#define OFLAG_GROUTE   0x00000200  /* Oper can do global routing */
+#define OFLAG_LKILL    0x00000400  /* Oper can do local kills */
+#define OFLAG_GKILL    0x00000800  /* Oper can do global kills */
+#define OFLAG_KLINE    0x00001000  /* Oper can /kline users */
+#define OFLAG_UNKLINE  0x00002000  /* Oper can /unkline users */
+#define OFLAG_LNOTICE  0x00004000  /* Oper can send local serv notices */
+#define OFLAG_GNOTICE  0x00008000  /* Oper can send global notices */
+#define OFLAG_ADMIN    0x00010000  /* Admin */
+#define OFLAG_UMODEc   0x00020000  /* Oper can set umode +c : client connect */
+#define OFLAG_UMODEf   0x00040000  /* Oper can set umode +f : flood notices */
+#define OFLAG_SADMIN    0x00080000  /* Oper can be a services admin */
+#define OFLAG_ZLINE    0x00100000  /* Oper can use /zline and /unzline */
+#define OFLAG_UMODEy    0x00200000  /* Oper can set umode +y : spy */
+#define OFLAG_UMODEd    0x00400000  /* Oper can set umode +d : debug */
+#define OFLAG_UMODEb    0x00800000  /* Oper can set umode +b : chatops */
+#define OFLAG_UMODEF   0x01000000  /* Oper can set umode +F : no flood throttling */
+#define OFLAG_LOCAL    (OFLAG_REHASH|OFLAG_HELPOP|OFLAG_GLOBOP|OFLAG_WALLOP|\
+                         OFLAG_LOCOP|OFLAG_LROUTE|OFLAG_LKILL|OFLAG_KLINE|\
+                         OFLAG_UNKLINE|OFLAG_LNOTICE|OFLAG_UMODEc|OFLAG_UMODEf|OFLAG_UMODEd|\
+                         OFLAG_UMODEb|OFLAG_UMODEy)
+#define OFLAG_GLOBAL   (OFLAG_LOCAL|OFLAG_GROUTE|OFLAG_GKILL|OFLAG_GNOTICE)
+#define OFLAG_ISGLOBAL (OFLAG_GROUTE|OFLAG_GKILL|OFLAG_GNOTICE)
+#define OPCanZline(x)          ((x)->oflag & OFLAG_SADMIN)
+#define OPCanRehash(x)         ((x)->oflag & OFLAG_REHASH)
+#define OPCanDie(x)            ((x)->oflag & OFLAG_DIE)
+#define OPCanRestart(x)                ((x)->oflag & OFLAG_RESTART)
+#define OPCanHelpOp(x)         ((x)->oflag & OFLAG_HELPOP)
+#define OPCanGlobOps(x)                ((x)->oflag & OFLAG_GLOBOP)
+#define OPCanWallOps(x)                ((x)->oflag & OFLAG_WALLOP)
+#define OPCanLocOps(x)         ((x)->oflag & OFLAG_LOCOP)
+#define OPCanLRoute(x)         ((x)->oflag & OFLAG_LROUTE)
+#define OPCanGRoute(x)         ((x)->oflag & OFLAG_GROUTE)
+#define OPCanLKill(x)          ((x)->oflag & OFLAG_LKILL)
+#define OPCanGKill(x)          ((x)->oflag & OFLAG_GKILL)
+#define OPCanKline(x)          ((x)->oflag & OFLAG_KLINE)
+#define OPCanUnKline(x)                ((x)->oflag & OFLAG_UNKLINE)
+#define OPCanLNotice(x)                ((x)->oflag & OFLAG_LNOTICE)
+#define OPCanGNotice(x)                ((x)->oflag & OFLAG_GNOTICE)
+#define OPIsAdmin(x)           ((x)->oflag & OFLAG_ADMIN)
+#define OPIsSAdmin(x)          ((x)->oflag & OFLAG_SADMIN)
+#define OPCanUModec(x)         ((x)->oflag & OFLAG_UMODEc)
+#define OPCanUModef(x)         ((x)->oflag & OFLAG_UMODEf)
+#define OPCanUModey(x)          ((x)->oflag & OFLAG_UMODEy)     
+#define OPCanUModed(x)          ((x)->oflag & OFLAG_UMODEd)     
+#define OPCanUModeb(x)          ((x)->oflag & OFLAG_UMODEb)
+#define OPCanUModeF(x)         ((x)->oflag & OFLAG_UMODEF)
+#define OPClearRehash(x)       ((x)->oflag &= ~OFLAG_REHASH)
+#define OPClearDie(x)          ((x)->oflag &= ~OFLAG_DIE)  
+#define OPClearRestart(x)      ((x)->oflag &= ~OFLAG_RESTART)
+#define OPClearHelpOp(x)       ((x)->oflag &= ~OFLAG_HELPOP)
+#define OPClearGlobOps(x)      ((x)->oflag &= ~OFLAG_GLOBOP)
+#define OPClearWallOps(x)      ((x)->oflag &= ~OFLAG_WALLOP)
+#define OPClearLocOps(x)       ((x)->oflag &= ~OFLAG_LOCOP)
+#define OPClearLRoute(x)       ((x)->oflag &= ~OFLAG_LROUTE)
+#define OPClearGRoute(x)       ((x)->oflag &= ~OFLAG_GROUTE)
+#define OPClearLKill(x)                ((x)->oflag &= ~OFLAG_LKILL)
+#define OPClearGKill(x)                ((x)->oflag &= ~OFLAG_GKILL)
+#define OPClearKline(x)                ((x)->oflag &= ~OFLAG_KLINE)
+#define OPClearUnKline(x)      ((x)->oflag &= ~OFLAG_UNKLINE)
+#define OPClearLNotice(x)      ((x)->oflag &= ~OFLAG_LNOTICE)
+#define OPClearGNotice(x)      ((x)->oflag &= ~OFLAG_GNOTICE)
+#define OPClearAdmin(x)                ((x)->oflag &= ~OFLAG_ADMIN)
+#define OPClearSAdmin(x)       ((x)->oflag &= ~OFLAG_SADMIN)
+#define OPClearUModec(x)       ((x)->oflag &= ~OFLAG_UMODEc)
+#define OPClearUModef(x)       ((x)->oflag &= ~OFLAG_UMODEf)
+#define OPClearUModey(x)        ((x)->oflag &= ~OFLAG_UMODEy) 
+#define OPClearUModed(x)        ((x)->oflag &= ~OFLAG_UMODEd) 
+#define OPClearUModeb(x)        ((x)->oflag &= ~OFLAG_UMODEb)
+#define OPClearZLine(x)                ((x)->oflag &= ~OFLAG_ZLINE)
+#define OPClearUModeF(x)       ((x)->oflag &= ~OFLAG_UMODEF)
+
+/* defined debugging levels */
+#define        DEBUG_FATAL  0
+#define        DEBUG_ERROR  1 /* report_error() and other errors that are found */
+#define        DEBUG_NOTICE 3
+#define        DEBUG_DNS    4 /* used by all DNS related routines - a *lot* */
+#define        DEBUG_INFO   5 /* general usful info */
+#define        DEBUG_NUM    6 /* numerics */
+#define        DEBUG_SEND   7 /* everything that is sent out */
+#define        DEBUG_DEBUG  8 /* anything to do with debugging, ie unimportant :) */
+#define        DEBUG_MALLOC 9 /* malloc/free calls */
+#define        DEBUG_LIST  10 /* debug list use */
+/* defines for curses in client */
+#define        DUMMY_TERM      0
+#define        CURSES_TERM     1
+#define        TERMCAP_TERM    2
+
+struct Counter 
+{
+    int         server;      /* servers */
+    int         myserver;    /* my servers */
+    int         myulined;    /* my ulined servers */
+    int         oper;        /* Opers */
+    int         chan;        /* Channels */
+    int         local;       /* Local Clients */
+    int         total;       /* total clients */
+    int         invisi;      /* invisible clients */
+    int         unknown;     /* unknown connections */
+    int         max_loc;     /* MAX local clients */
+    int         max_tot;     /* MAX global clients */
+    ts_val      start;       /* when we started collecting info */
+    u_long      today;      /* Client Connections today */
+    ts_val      day;        /* when today started */
+    u_long     weekly;      /* connections this week */
+    ts_val     week;        /* when this week started */
+    u_long     monthly;     /* connections this month */
+    ts_val     month;       /* when this month started */
+    u_long     yearly;      /* this is gonna be big */
+    ts_val     year;        /* when this year started (HEH!) */
+};
+
+struct MotdItem 
+{
+    char        line[MOTDLINELEN];
+    struct MotdItem *next;
+};
+
+/* lets speed this up... also removed away information. *tough* Dianora */
+typedef struct Whowas 
+{
+    int         hashv;
+    char        name[NICKLEN + 1];
+    char        username[USERLEN + 1];
+    char        hostname[HOSTLEN + 1];
+    char       *servername;
+    char        realname[REALLEN + 1];
+    time_t      logoff;
+    unsigned int umode;
+    struct Client *online;  /* Pointer to new nickname for chasing or NULL */
+    
+    struct Whowas *next;    /* for hash table... */
+    
+    struct Whowas *prev;    /* for hash table... */
+    struct Whowas *cnext;   /* for client struct linked list */
+    struct Whowas *cprev;   /* for client struct linked list */
+} aWhowas;
+
+/* configuration structures */
+
+#define CONF_ILLEGAL            0x80000000
+#define CONF_MATCH              0x40000000
+
+#define CONF_CLIENT             0x0002
+#define CONF_CONNECT_SERVER     0x0004
+#define CONF_NOCONNECT_SERVER   0x0008
+#define CONF_LOCOP              0x0010
+#define CONF_OPERATOR           0x0020
+#define CONF_ME                 0x0040
+#define CONF_KILL               0x0080
+#define CONF_ADMIN              0x0100
+#define CONF_CLASS              0x0200
+#define CONF_SERVICE            0x0400
+#define CONF_LISTEN_PORT        0x1000
+#define CONF_HUB                0x2000
+#define CONF_ELINE              0x4000
+#define CONF_FLINE              0x8000
+#define CONF_QUARANTINE         0x40000
+#define CONF_ULINE              0x80000
+#define CONF_DRPASS             0x100000    /* die/restart pass, from df465 */
+#define CONF_GCOS               0x200000
+#define CONF_SQLINE             0x400000
+#define CONF_MONINFO            0x800000    /* proxy monitor info */
+#define CONF_OPS                (CONF_OPERATOR | CONF_LOCOP)
+#define CONF_SERVER_MASK        (CONF_CONNECT_SERVER | CONF_NOCONNECT_SERVER)
+#define CONF_CLIENT_MASK        (CONF_CLIENT | CONF_SERVICE | CONF_OPS | \
+                                 CONF_SERVER_MASK)
+#define IsIllegal(x)            ((x)->status & CONF_ILLEGAL)
+
+/* did the password field specify OPER? */
+#define CONF_FLAGS_I_OPERPORT      0x0002
+/* does NAME in I:HOST::NAME have an @? */
+#define CONF_FLAGS_I_NAME_HAS_AT   0x0004
+/* does HOST in I:HOST::NAME have an @? */
+#define CONF_FLAGS_I_HOST_HAS_AT   0x0008
+/* do we match allow:hostmask? */
+#define CONF_FLAGS_I_MATCH_NAME    0x0010
+/* do we match allow:ipmask? */
+#define CONF_FLAGS_I_MATCH_HOST    0x0020
+/* remove throttle on match? */
+#define CONF_FLAGS_NOTHROTTLE      0x0040
+/* force umode +F? */
+#define CONF_FLAGS_FORCEFLOOD      0x0080
+/* skip clone checks? */
+#define CONF_FLAGS_SKIPCLONES      0x0100
+
+/* global configuration flags */
+
+#define FLAGS_HUB       0x0001
+#define FLAGS_SERVHUB   0x0002
+#define FLAGS_SMOTD     0x0004
+#define FLAGS_CRYPTPASS 0x0008
+#define FLAGS_WGMONURL  0x0010
+#define FLAGS_WGMONHOST 0x0020
+#define FLAGS_WGMON     (FLAGS_WGMONURL|FLAGS_WGMONHOST)
+#define FLAGS_SHOWLINKS 0x0040
+#define FLAGS_SPLITOPOK 0x0080
+
+/* flags for connects */
+
+#define CONN_ZIP       0x001   /* zippable    */
+#define CONN_DKEY      0x010   /* cryptable   */
+#define CONN_HUB       0x100   /* hubbable!   */
+
+struct Conf_Connect
+{
+       /* this is currently BOTH C:lines and N:lines */
+       char *host;
+       char *apasswd;  /* nline password - to accept connection */
+       char *cpasswd;  /* cline password - to connect out with  */
+       char *name;
+       char *source;   /* when connecting, use this IP address  */
+       struct in_addr ipnum;   /* ip address host field */
+       int   port;
+       int   flags;
+       int   legal;
+       aClient *acpt;  /* whos using this! */
+       time_t   hold;  /* used for autoconnections */
+    char *class_name;   /* the accual name of our class */
+       aClass   *class;    /* pointer to the class */
+       aConnect *next;
+};
+
+struct Conf_Allow
+{
+       /* this is an I:line */
+       char *ipmask;
+       char *passwd;
+       char *hostmask;
+       int   port;
+       int   flags;
+       int   clients;
+       int   legal;
+    char *class_name;
+       aClass *class;
+       aAllow *next;
+};
+
+struct Conf_Me
+{
+       /* combined A, X and M lines */
+       char *servername;
+       char *info;
+       char *diepass;
+       char *restartpass;
+    char *admin[3];     /* our three admin lines */
+};
+
+#define MAXHOSTS 32
+
+struct Conf_Oper
+{
+       /* this is an O:line */
+       char *hosts[MAXHOSTS+1];
+       char *passwd;
+       char *nick;
+       int   flags;
+       int   legal;
+    int   opers;    /* number of opers currently using this */
+    char *class_name;
+       aClass *class;
+       aOper *next;
+};
+
+struct Conf_Modules
+{
+    char *module_path;
+    char *autoload[128];
+    char *optload[128];
+};
+
+struct Conf_Port
+{
+       char *allow;
+       char *address;
+       int   port;
+    aListener *lstn;
+       int   legal;
+       aPort *next;
+};
+
+struct Conf_Class
+{
+    char        *name;
+    int          connfreq;
+    int          pingfreq;
+    int          maxlinks;
+    long         maxsendq;
+    int          ip24clones;
+    int          links;
+    int          refs;
+    aClass      *next;
+};
+
+struct Listener {
+        struct Listener *next;              /* next listener */
+        int             fd;                 /* fd of this listener */
+        u_short         port; 
+        char            allow_string[32];   /* allow from */
+        char            vhost_string[32];   /* bind to */
+        struct in_addr  allow_ip;           /* allow from */
+        struct in_addr  vhost_ip;           /* bind to */
+        time_t          lasttime;           /* last time I accepted */
+        long            sendK;              /* counters, see below */
+        u_short         sendB;           
+        long            sendM;          
+        long            receiveK;      
+        u_short         receiveB;     
+        long            receiveM;    
+        u_long          ccount;   /* total number of clients to connect here */
+        int             clients;  /* number of clients currently on this */
+        aPort           *aport;   /* link to the P: line I came from */
+};
+
+
+/* Client structures */
+struct User
+{
+    Link       *channel;       /* chain of channel pointer blocks */
+    Link       *invited;       /* chain of invite pointer blocks */
+    char       *away;          /* pointer to away message */
+    time_t      last;
+    int         joined;        /* number of channels joined */
+    char        username[USERLEN + 1];
+    char        host[HOSTLEN + 1];
+    char       *server;        /* pointer to scached server name */
+    unsigned int servicetype;  /* set by SVSMODE +T */
+    unsigned int servicestamp; /* set by SVSMODE +d */
+    /*
+     * In a perfect world the 'server' name should not be needed, a
+     * pointer to the client describing the server is enough. 
+     * Unfortunately, in reality, server may not yet be in links while
+     * USER is introduced... --msa
+     */
+    Link       *silence;      /* chain of silenced users */
+    LOpts         *lopt;     /* Saved /list options */
+    Link       *dccallow;     /* chain of dcc send allowed users */
+#if (RIDICULOUS_PARANOIA_LEVEL>=1)
+    char       *real_oper_host;
+    char       *real_oper_username;
+    char       *real_oper_ip;
+#endif
+    aOper      *oper;
+    aAllow     *allow;
+};
+
+struct Server
+{
+    char       *up;              /* Pointer to scache name */
+    char        bynick[NICKLEN + 1];
+    char        byuser[USERLEN + 1];
+    char        byhost[HOSTLEN + 1];
+    aConnect   *aconn;                 /* N-line pointer for this server */
+    int         dkey_flags;        /* dkey flags */
+#ifdef HAVE_ENCRYPTION_ON
+    void       *sessioninfo_in;   /* pointer to opaque sessioninfo structure */
+    void       *sessioninfo_out;  /* pointer to opaque sessioninfo structure */
+    void       *rc4_in;           /* etc */
+    void       *rc4_out;          /* etc */
+#endif
+    void       *zip_out;
+    void       *zip_in;
+};
+
+struct Client 
+{
+    struct Client *next, *prev, *hnext;
+    anUser     *user;       /* ...defined, if this is a User */
+    aServer    *serv;       /* ...defined, if this is a server */
+    aWhowas    *whowas;     /* Pointers to whowas structs */
+    time_t      lasttime;   /* ...should be only LOCAL clients? --msa */
+    time_t      firsttime;  /* time client was created */
+    time_t      since;      /* last time we parsed something */
+    ts_val      tsinfo;     /* TS on the nick, SVINFO on servers */
+    long        flags;      /* client flags */
+    long        umode;      /* We can illeviate overflow this way */
+    aClient    *from;       /* == self, if Local Client, *NEVER* NULL! */
+    aClient    *uplink;     /* this client's uplink to the network */
+    int         fd;         /* >= 0, for local clients */
+    int         hopcount;   /* number of servers to this 0 = local */
+    short       status;     /* Client type */
+    char        nicksent;
+    char        name[HOSTLEN + 1];  /* Unique name of the client, nick or
+                                    * host */
+    char        info[REALLEN + 1];  /* Free form additional client 
+                                    * information */
+#ifdef FLUD
+    Link       *fludees;
+#endif
+    
+    struct in_addr ip;      /* keep real ip# too */
+    char        hostip[HOSTIPLEN + 1]; /* Keep real ip as string 
+                                       * too - Dianora */
+    
+    Link *watch; /* user's watch list */
+    int watches; /* how many watches this user has set */
+
+#ifdef THROTTLE_ENABLE
+    struct {                /* clone tracking */
+        aClient     *prev;
+        aClient     *next;
+    } clone;
+#endif
+    
+/*
+####### #     # ### #     #  #####   #####
+   #    #     #  #  ##    # #     # #     #
+   #    #     #  #  # #   # #       #
+   #    #######  #  #  #  # #  ####  #####
+   #    #     #  #  #   # # #     #       #
+   #    #     #  #  #    ## #     # #     #
+   #    #     # ### #     #  #####   #####
+
+######  ####### #       ####### #     #
+#     # #       #       #     # #  #  #
+#     # #       #       #     # #  #  #
+######  #####   #       #     # #  #  #
+#     # #       #       #     # #  #  #
+#     # #       #       #     # #  #  #
+######  ####### ####### #######  ## ##
+
+ #####  ####### #     # #     # #######
+#     # #     # #     # ##    #    #
+#       #     # #     # # #   #    #
+#       #     # #     # #  #  #    #
+#       #     # #     # #   # #    #
+#     # #     # #     # #    ##    #
+ #####  #######  #####  #     #    #
+
+   #    ######  #######    #       #######  #####     #    #
+  # #   #     # #          #       #     # #     #   # #   #
+ #   #  #     # #          #       #     # #        #   #  #
+#     # ######  #####      #       #     # #       #     # #
+####### #   #   #          #       #     # #       ####### #
+#     # #    #  #          #       #     # #     # #     # #
+#     # #     # #######    ####### #######  #####  #     # #######
+
+####### #     # #       #     # ### ### ###
+#     # ##    # #        #   #  ### ### ###
+#     # # #   # #         # #   ### ### ###
+#     # #  #  # #          #     #   #   #
+#     # #   # # #          #
+#     # #    ## #          #    ### ### ###
+####### #     # #######    #    ### ### ###
+
+*/
+    /*
+     * The following fields are allocated only for local clients 
+     * (directly connected to this server with a socket.  The first
+     * of them MUST be the "count"--it is the field to which the
+     * allocation is tied to! Never refer to  these fields, if (from != self).
+     */
+    
+    int         count;         /* Amount of data in buffer */
+#ifdef FLUD
+    time_t      fludblock;
+    struct fludbot *fluders;
+#endif
+#ifdef ANTI_SPAMBOT
+    time_t      last_join_time;         /* when this client last joined a channel */
+    time_t      last_leave_time; /* when this client last left a channel */
+    int         join_leave_count;      /* count of JOIN/LEAVE in less 
+                                        * than MIN_JOIN_LEAVE_TIME seconds */
+    int         oper_warn_count_down;  /* warn opers of this possible spambot 
+                                        * every time this gets to 0 */
+#endif
+    char        buffer[BUFSIZE];        /* Incoming message buffer */
+    short       lastsq;                 /* # of 2k blocks when sendqueued called 
+                                 * last */
+    SBuf        sendQ;      /* Outgoing message queue--if socket full */
+    SBuf        recvQ;      /* Hold for data incoming yet to be parsed */
+    long        sendM;          /* Statistics: protocol messages send */
+    long        sendK;          /* Statistics: total k-bytes send */
+    long        receiveM;       /* Statistics: protocol messages received */
+    long        receiveK;       /* Statistics: total k-bytes received */
+    u_short     sendB;          /* counters to count upto 1-k lots of bytes */
+    u_short     receiveB;       /* sent and received. */
+    long        lastrecvM;       /* to check for activity --Mika */
+    int         priority;
+    aListener  *lstn;           /* listener which we accepted from */
+    int         authfd;                 /* fd for rfc931 authentication */
+    char        username[USERLEN + 1]; /* username here now for auth stuff */
+    unsigned short port;        /* and the remote port# too :-) */
+    struct hostent *hostp;
+#ifdef ANTI_NICK_FLOOD
+    time_t      last_nick_change;
+    int         number_of_nick_changes;
+#endif
+#ifdef NO_AWAY_FLUD
+    time_t         alas;       /* last time of away set */
+    int            acount;             /* count of away settings */
+#endif
+    
+    char        sockhost[HOSTLEN + 1]; /* This is the host name from
+                                        * the socket and after which the 
+                                        * connection was accepted. */
+    char        passwd[PASSWDLEN + 1];
+    /* try moving this down here to prevent weird problems... ? */
+    int         oflag;          /* Operator Flags */
+    int sockerr;                /* what was the last error returned for
+                                * this socket? */
+    int capabilities;           /* what this server/client supports */
+    aClass  *class;             /* our current effective class */
+
+#ifdef MSG_TARGET_LIMIT
+    struct {
+       struct Client *cli;
+       time_t sent;
+    } targets[MSG_TARGET_MAX];              /* structure for target rate limiting */
+    time_t last_target_complain;
+    unsigned int num_target_errors;
+#endif
+
+};
+
+#define        CLIENT_LOCAL_SIZE sizeof(aClient)
+#define        CLIENT_REMOTE_SIZE offsetof(aClient,count)
+/* statistics structures */
+struct stats
+{
+    unsigned int is_cl;     /* number of client connections */
+    unsigned int is_sv;     /* number of server connections */
+    unsigned int is_ni;     /* connection but no idea who it was */
+    unsigned short is_cbs;  /* bytes sent to clients */
+    unsigned short is_cbr;  /* bytes received to clients */
+    unsigned short is_sbs;  /* bytes sent to servers */
+    unsigned short is_sbr;  /* bytes received to servers */
+    unsigned long is_cks;   /* k-bytes sent to clients */
+    unsigned long is_ckr;   /* k-bytes received to clients */
+    unsigned long is_sks;   /* k-bytes sent to servers */
+    unsigned long is_skr;   /* k-bytes received to servers */
+    time_t      is_cti;     /* time spent connected by clients */
+    time_t      is_sti;     /* time spent connected by servers */
+    unsigned int is_ac;     /* connections accepted */
+    unsigned int is_ref;    /* accepts refused */
+    unsigned int is_throt;  /* accepts throttled */
+    unsigned int is_drone;  /* refused drones */
+    unsigned int is_unco;   /* unknown commands */
+    unsigned int is_wrdi;   /* command going in wrong direction */
+    unsigned int is_unpf;   /* unknown prefix */
+    unsigned int is_empt;   /* empty message */
+    unsigned int is_num;    /* numeric message */
+    unsigned int is_kill;   /* number of kills generated on collisions */
+    unsigned int is_fake;   /* MODE 'fakes' */
+    unsigned int is_asuc;   /* successful auth requests */
+    unsigned int is_abad;   /* bad auth requests */
+    unsigned int is_udp;    /* packets recv'd on udp port */
+    unsigned int is_loc;    /* local connections made */
+    unsigned int is_ref_1;  /* refused at kline stage 1 */
+    unsigned int is_ref_2;  /* refused at kline stage 2 */
+#ifdef FLUD
+    unsigned int is_flud;   /* users/channels flood protected */
+#endif                     /* FLUD */
+};
+
+/* mode structure for channels */
+
+struct SMode 
+{
+    unsigned int mode;
+    int         limit;
+    char        key[KEYLEN + 1];
+    int                join_num;
+    int                join_time;
+};
+
+/* Message table structure */
+
+struct Message 
+{
+    char       *cmd;
+    int         (*func) ();
+    unsigned int count;         /* number of times command used */
+    int         parameters;
+    char        flags;
+    
+    /* bit 0 set means that this command is allowed to be used only on
+     * the average of once per 2 seconds -SRB */
+    
+    /* I could have defined other bit maps to above instead of the next
+     * two flags that I added. so sue me. -Dianora */
+    
+    char        allow_unregistered_use;        /* flag if this command can be used 
+                                        * if unregistered */
+    
+    char        reset_idle;    /* flag if this command causes idle time to be 
+                                * reset */
+    unsigned long bytes;
+};
+
+typedef struct msg_tree 
+{
+    char       *final;
+    struct Message *msg;
+    struct msg_tree *pointers[26];
+} MESSAGE_TREE;
+
+/*
+ * Move BAN_INFO information out of the SLink struct its _only_ used
+ * for bans, no use wasting the memory for it in any other type of
+ * link. Keep in mind, doing this that it makes it slower as more
+ * Malloc's/Free's have to be done, on the plus side bans are a smaller
+ * percentage of SLink usage. Over all, the th+hybrid coding team came
+ * to the conclusion it was worth the effort.
+ * 
+ * - Dianora
+ */
+
+struct Ban 
+{
+    char       *banstr;
+    char       *who;
+    time_t      when;
+    u_char         type;
+    aBan          *next;
+};
+
+#ifdef INVITE_LISTS
+/* channel invite list structure */
+
+struct LInvite
+{
+    char*        invstr;
+    char*        who;
+    time_t       when;
+    anInvite*    next;
+};
+#endif
+
+#ifdef EXEMPT_LISTS
+/* channel ban exempt list structure */
+
+struct Exempt
+{
+    char*        banstr;
+    char*        who;
+    time_t       when;
+    u_char       type;
+    aBanExempt*  next;
+};
+#endif
+
+/* channel member link structure, used for chanmember chains */
+struct ChanLink 
+{
+    struct ChanLink *next;
+    aClient *cptr;
+    int flags;
+    unsigned int banserial;     /* used for bquiet cache */
+};
+
+/* general link structure used for chains */
+
+struct SLink 
+{
+    struct SLink *next;
+    union
+    {
+       aClient    *cptr;
+       aChannel   *chptr;
+       aConnect   *aconn;      /* here for resolver purposes */
+       aBan       *banptr;
+       aWatch *wptr;
+       char       *cp;
+    } value;
+    int         flags;
+};
+
+struct SLinkD
+{
+    struct SLinkD *next;
+    struct SLinkD *prev;
+    union
+    {
+       aClient    *cptr;
+       aChannel   *chptr;
+       aBan       *banptr;
+       aWatch *wptr;
+       char       *cp;
+    } value;
+    int         flags;
+};
+
+/* channel structure */
+
+struct Channel 
+{
+    struct Channel *nextch, *prevch, *hnextch;
+    int         hashv;         /* raw hash value */
+    Mode        mode;
+    char        topic[TOPICLEN + 1];
+    char        topic_nick[NICKLEN + 1];
+    time_t      topic_time;
+    int         users;
+    chanMember* members;
+    Link*       invites; /* users invited to the channel */
+    aBan*       banlist;
+#ifdef INVITE_LISTS
+    anInvite*   invite_list; /* +I list */
+#endif
+#ifdef EXEMPT_LISTS
+    aBanExempt* banexempt_list;
+#endif
+    ts_val      channelts;
+#ifdef FLUD
+    time_t      fludblock;
+    struct fludbot *fluders;
+#endif
+    char        chname[CHANNELLEN+1];
+    int                join_start;         /* these two are for +j watching */
+    int                join_count;
+    int     default_join_start; /* these two handle the default joinrate handling */
+    int     default_join_count;
+    unsigned int banserial;     /* used for bquiet cache */
+};
+
+#define        TS_CURRENT      5       /* current TS protocol version */
+#define        TS_MIN          3       /* minimum supported TS protocol version */
+#define        TS_DOESTS       0x20000000
+#define        DoesTS(x)       ((x)->tsinfo == TS_DOESTS)
+/* Channel Related macros follow */
+
+/* Channel related flags */
+
+#define        CHFL_CHANOP     0x0001  /* Channel operator */
+#define        CHFL_VOICE      0x0002  /* the power to speak */
+#define        CHFL_DEOPPED    0x0004  /* deopped by us, modes need to be bounced */
+#define        CHFL_BANNED     0x0008  /* is banned */
+
+/* ban mask types */
+
+#define MTYP_FULL      0x01    /* mask is nick!user@host */
+#define MTYP_USERHOST  0x02    /* mask is user@host */
+#define MTYP_HOST      0x04    /* mask is host only */
+
+/* Channel Visibility macros */
+
+#define        MODE_CHANOP     CHFL_CHANOP
+#define        MODE_VOICE      CHFL_VOICE
+#define        MODE_DEOPPED    CHFL_DEOPPED
+#define        MODE_PRIVATE    0x00008
+#define        MODE_SECRET     0x00010
+#define        MODE_MODERATED  0x00020
+#define        MODE_TOPICLIMIT 0x00040
+#define        MODE_INVITEONLY 0x00080
+#define        MODE_NOPRIVMSGS 0x00100
+#define        MODE_KEY        0x00200
+#define        MODE_BAN        0x00400
+#define        MODE_LIMIT      0x00800
+#define MODE_REGISTERED        0x01000
+#define MODE_REGONLY   0x02000
+#define MODE_NOCOLOR   0x04000
+#define MODE_OPERONLY   0x08000
+#define MODE_MODREG     0x10000
+#define MODE_LISTED    0x20000
+#define MODE_JOINRATE  0x40000
+
+/* mode flags which take another parameter (With PARAmeterS) */
+
+#define        MODE_WPARAS     (MODE_CHANOP|MODE_VOICE|MODE_BAN|MODE_KEY|MODE_LIMIT)
+
+/*
+ * Undefined here, these are used in conjunction with the above modes
+ * in the source. #define       MODE_DEL       0x40000000 #define
+ * MODE_ADD       0x80000000
+ */
+
+#define        HoldChannel(x)          (!(x))
+
+/*name invisible */
+
+#define        SecretChannel(x)        ((x) && ((x)->mode.mode & MODE_SECRET))
+
+/* channel not shown but names are */
+
+#define        HiddenChannel(x)        ((x) && ((x)->mode.mode & MODE_PRIVATE))
+
+/* channel visible */
+
+#define        ShowChannel(v,c)        (PubChannel(c) || IsMember((v),(c)))
+#define        PubChannel(x)           ((!x) || ((x)->mode.mode &\
+                                 (MODE_PRIVATE | MODE_SECRET)) == 0)
+
+#define IsMember(blah,chan) ((blah && blah->user && \
+               find_channel_link((blah->user)->channel, chan)) ? 1 : 0)
+
+#define        IsChannelName(name) ((name) && (*(name) == '#'))
+
+/* Misc macros */
+
+#define        BadPtr(x) (!(x) || (*(x) == '\0'))
+
+#define        isvalid(c) (((c) >= 'A' && (c) < '~') || IsDigit(c) || (c) == '-')
+
+#define        MyConnect(x)                    ((x)->fd >= 0)
+#define        MyClient(x)                     (MyConnect(x) && IsClient(x))
+#define        MyOper(x)                       (MyConnect(x) && IsOper(x))
+
+/* String manipulation macros */
+
+/* strncopynt --> strncpyzt to avoid confusion, sematics changed N must
+ * be now the number of bytes in the array --msa */
+
+#define        strncpyzt(x, y, N) do{(void)strncpy(x,y,N);x[N-1]='\0';}while(0)
+#define        StrEq(x,y)      (!strcmp((x),(y)))
+
+/* used in SetMode() in channel.c and m_umode() in s_msg.c */
+
+#define        MODE_NULL      0
+#define        MODE_ADD       0x40000000
+#define        MODE_DEL       0x20000000
+
+/* return values for hunt_server() */
+
+#define        HUNTED_NOSUCH   (-1)    /* if the hunted server is not found */
+#define        HUNTED_ISME     0       /* if this server should execute the command */
+#define        HUNTED_PASS     1       /* if message passed onwards successfully */
+
+/* used when sending to #mask or $mask */
+
+#define        MATCH_SERVER  1
+#define        MATCH_HOST    2
+
+/* used for async dns values */
+
+#define        ASYNC_NONE      (-1)
+#define        ASYNC_CLIENT    0
+#define        ASYNC_CONNECT   1
+#define        ASYNC_CONF      2
+#define        ASYNC_SERVER    3
+
+/* misc variable externs */
+
+extern char version[128], *infotext[];
+extern char *generation, *creation;
+
+/* misc defines */
+
+#define ZIP_NEXT_BUFFER -4
+#define RC4_NEXT_BUFFER -3
+#define        FLUSH_BUFFER    -2
+#define        UTMP            "/etc/utmp"
+#define        COMMA           ","
+
+#ifdef ORATIMING
+/*
+ * Timing stuff (for performance measurements): compile with
+ * -DORATIMING and put a TMRESET where you want the counter of time
+ * spent set to 0, a TMPRINT where you want the accumulated results,
+ * and TMYES/TMNO pairs around the parts you want timed -orabidoo
+ */
+
+extern struct timeval tsdnow, tsdthen;
+extern unsigned long tsdms;
+
+#define TMRESET tsdms=0;
+#define TMYES gettimeofday(&tsdthen, NULL);
+#define TMNO gettimeofday(&tsdnow, NULL);\
+             if (tsdnow.tv_sec!=tsdthen.tv_sec) \
+                 tsdms+=1000000*(tsdnow.tv_sec-tsdthen.tv_sec);\
+                 tsdms+=tsdnow.tv_usec; tsdms-=tsdthen.tv_usec;
+#define TMPRINT sendto_ops("Time spent: %ld ms", tsdms);
+#else
+#define TMRESET
+#define TMYES
+#define TMNO
+#define TMPRINT
+#endif
+
+/* allow 5 minutes after server rejoins the network before allowing
+ * chanops new channel */
+
+#ifdef FLUD
+struct fludbot 
+{
+    struct Client *fluder;
+    int         count;
+    time_t      first_msg, last_msg;
+    struct fludbot *next;
+};
+
+#endif /* FLUD */
+
+struct Watch 
+{
+    aWatch  *hnext;
+    time_t   lasttime;
+    Link  *watch;
+    char  nick[1];
+};
+
+struct ListOptions 
+{
+    LOpts *next;
+    Link  *yeslist, *nolist;
+    int   starthash;
+    short int   showall, only_listed;
+    unsigned short usermin;
+    int   usermax;
+    time_t   currenttime;
+    time_t   chantimemin;
+    time_t   chantimemax;
+    time_t   topictimemin;
+    time_t   topictimemax;
+};
+
+typedef struct SearchOptions 
+{
+    int umodes;
+    char *nick;
+    char *user;
+    char *host;
+    char *gcos;
+    char *ip;
+    int class;
+    int class_value;
+    unsigned int cidr4_ip;
+    unsigned int cidr4_mask;
+    int ts;
+    int ts_value;
+    aChannel *channel;
+    aClient *server;
+    unsigned int channelflags;
+    unsigned int client_type;
+#ifdef AIX
+    unsigned umode_plus:1;
+    unsigned nick_plus:1;
+    unsigned user_plus:1;
+    unsigned host_plus:1;
+    unsigned gcos_plus:1;
+    unsigned ip_plus:1;
+    unsigned cidr4_plus:1;
+    unsigned chan_plus:1;
+    unsigned serv_plus:1;
+    unsigned away_plus:1;
+    unsigned check_away:1;
+    unsigned check_umode:1;
+    unsigned show_chan:1;
+    unsigned search_chan:1;
+    unsigned ip_show:1;
+    unsigned client_type_plus:1;
+    /* unsigned spare:0; spare space for more stuff(?) */
+#else
+    char umode_plus:1;
+    char nick_plus:1;
+    char user_plus:1;
+    char host_plus:1;
+    char gcos_plus:1;
+    char ip_plus:1;
+    char cidr4_plus:1;
+    char chan_plus:1;
+    char serv_plus:1;
+    char away_plus:1;
+    char check_away:1;
+    char check_umode:1;
+    char show_chan:1;
+    char search_chan:1;
+    char ip_show:1;
+    char client_type_plus:1;
+    /* char spare:0; spare space for more stuff(?) */
+#endif
+} SOpts;
+
+/*
+ * Send /LIST as long as their output isn't blocking
+ * and we haven't used 2/3rds of their sendQ
+ */
+#define IsSendable(x)      (!((x)->flags & FLAGS_BLOCKED) && \
+                            SBufLength(&(x)->sendQ) < (int) \
+                            ((float) (x)->class->maxsendq / 1.5))
+#define DoList(x)          (((x)->user) && ((x)->user->lopt))
+
+/* internal defines for cptr->sockerr */
+#define IRCERR_BUFALLOC           -11
+#define IRCERR_ZIP        -12
+
+#endif /* __struct_include__ */
+
diff --git a/include/structfunc.h b/include/structfunc.h
new file mode 100644 (file)
index 0000000..2fb5912
--- /dev/null
@@ -0,0 +1,124 @@
+/* include/structfunc.h
+ * Copyright(c) 2003, Aaron Wiebe
+ * Bahamut Development Team
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: structfunc.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+/* This file includes external function defines for aClient (and possibly
+ * other) structure references. */
+
+extern aClient *ac_next(aClient *);
+extern aClient *ac_prev(aClient *);
+extern anUser *ac_user(aClient *);
+extern aServer *ac_server(aClient *);
+extern aWhowas *ac_whowas(aClient *);
+extern aClient *ac_from(aClient *);
+extern aClient *ac_uplink(aClient *);
+extern time_t ac_lasttime(aClient *);
+extern time_t ac_firsttime(aClient *);
+extern time_t ac_since(aClient *);
+extern ts_val ac_tsinfo(aClient *);
+extern int ac_fd(aClient *);
+extern int ac_hopcount(aClient *);
+extern short ac_status(aClient *);
+extern char ac_nicksent(aClient *);
+extern char *ac_name(aClient *);
+extern char *ac_info(aClient *);
+#ifdef FLUD
+extern Link *ac_fludees(aClient *);
+#endif
+extern struct in_addr ac_ip(aClient *);
+extern char *ac_hostip(aClient *);
+extern Link *ac_watch(aClient *);
+extern int ac_watches(aClient *);
+/******************************************
+ * These are the local functions...
+ ******************************************/
+extern int ac_count(aClient *);
+#ifdef FLUD
+extern time_t ac_fludblock(aClient *);
+extern struct fludbot *ac_fluders(aClient *);
+#endif
+#ifdef ANTI_SPAMBOT
+extern time_t ac_last_join_time(aClient *);
+extern time_t ac_last_leave_time(aClient *);
+extern int ac_join_leave_count(aClient *);
+extern int ac_oper_warn_count_down(aClient *);
+#endif
+extern char *ac_buffer(aClient *);
+extern short ac_lastsq(aClient *);
+extern SBuf *ac_sendQ(aClient *);
+extern SBuf *ac_recvQ(aClient *);
+extern long ac_sendM(aClient *);
+extern long ac_sendK(aClient *);
+extern long ac_recieveM(aClient *);
+extern long ac_recieveK(aClient *);
+extern u_short ac_sendB(aClient *);
+extern u_short ac_recieveB(aClient *);
+extern long ac_lastrecvM(aClient *);
+extern int ac_priority(aClient *);
+extern aListener *ac_lstn(aClient *);
+extern Link *ac_confs(aClient *);
+extern int ac_authfd(aClient *);
+extern char *ac_username(aClient *);
+extern unsigned short ac_port(aClient *);
+extern struct hostent *ac_hostp(aClient *);
+#ifdef ANTI_NICK_FLOOD
+extern time_t ac_last_nick_change(aClient *);
+extern int ac_number_of_nick_changes(aClient *);
+#endif
+#ifdef NO_AWAY_FLUD
+extern time_t ac_alas(aClient *);
+extern int ac_acount(aClient *);
+#endif
+extern char *ac_sockhost(aClient *);
+extern char *ac_passwd(aClient *);
+extern int ac_oflag(aClient *);
+extern int ac_sockerr(aClient *);
+extern int ac_capabilities(aClient *);
+extern int ac_pingval(aClient *);
+extern int ac_sendqlen(aClient *);
+/********************************
+ * These are channel access functions
+ ********************************/
+extern aChannel *ch_next(aChannel *);
+extern aChannel *ch_prev(aChannel *);
+extern aChannel *ch_hnext(aChannel *);
+extern int ch_hashv(aChannel *);
+extern Mode ch_mode(aChannel *);
+extern char *ch_topic(aChannel *);
+extern char *ch_topic_nick(aChannel *);
+extern time_t ch_topic_time(aChannel *);
+extern int ch_users(aChannel *);
+extern chanMember *ch_members(aChannel *);
+extern Link *ch_invites(aChannel *);
+extern aBan *ch_banlist(aChannel *);
+#ifdef INVITE_LISTS
+extern anInvite *ch_invite_list(aChannel *);
+#endif
+#ifdef EXEMPT_LISTS
+extern aBanExempt *ch_banexempt_list(aChannel *);
+#endif
+extern ts_val ch_channelts(aChannel *);
+#ifdef FLUD
+extern time_t ch_fludblock(aChannel *);
+extern struct fludbot *ch_fluders(aChannel *);
+#endif
+extern char *ch_chname(aChannel *);
+extern int ch_join_start(aChannel *);
+extern int ch_join_count(aChannel *);
diff --git a/include/sys.h b/include/sys.h
new file mode 100644 (file)
index 0000000..b3471bb
--- /dev/null
@@ -0,0 +1,91 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/sys.h
+ *   Copyright (C) 1990 University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* $Id: sys.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef        __sys_include__
+#define __sys_include__
+#ifdef ISC202
+#include <net/errno.h>
+#else
+#include <sys/errno.h>
+#endif
+#include "setup.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#if defined( HAVE_UNISTD_H )
+#include <unistd.h>
+#endif
+
+#if defined( HAVE_STDLIB_H )
+#include <stdlib.h>
+#endif
+
+#if defined( HAVE_STRINGS_H )
+#include <strings.h>
+#endif
+
+#if defined( HAVE_STRING_H )
+#include <string.h>
+#endif
+
+#if defined( HAVE_LIMITS_H )
+#include <limits.h>
+#endif
+
+#define        strcasecmp      mycmp
+#define        strncasecmp     myncmp
+
+#if !defined( HAVE_INDEX )
+#define   index   strchr
+#define   rindex  strrchr
+#endif
+
+#ifdef AIX
+#include <sys/select.h>
+#include <time.h>
+#endif
+
+#include <sys/time.h>
+
+#if ((__GNU_LIBRARY__ == 6) && (__GLIBC__ >=2) && (__GLIBC_MINOR__ >= 2))
+#include <time.h>
+#endif
+
+#ifdef WRITEV_IOV
+#include <sys/uio.h>
+#endif
+
+#define MyFree(x)       do { if (x) free(x); (x) = NULL; } while(0)
+
+extern void dummy();
+
+#ifdef NO_U_TYPES
+
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned long u_long;
+typedef unsigned int u_int;
+
+#endif
+
+#endif /* __sys_include__ */
diff --git a/include/throttle.h b/include/throttle.h
new file mode 100644 (file)
index 0000000..36cf633
--- /dev/null
@@ -0,0 +1,138 @@
+#ifndef THROTTLE_H
+#define THROTTLE_H
+/*
+ * Copyright 2000, 2001 Chip Norkus
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 
+ * 2a. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ * 2b. Redistribution in binary form requires specific prior written
+ *     authorization of the maintainer.
+ * 
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *    This product includes software developed by Chip Norkus.
+ * 
+ * 4. The names of the maintainer, developers and contributors may not be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE MAINTAINER, DEVELOPERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE DEVELOPERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* define functions with throttling enabled, then add other definitions later
+ * in case throttling was removed at compile time to speed along the system ;)
+ * the functions are pretty simple, externally.  throttle_check is given
+ * an IP in string dotted quad form, and returns 1 if it should be allowed,
+ * or 0 if it is to be throttled and dropped.  this should be done at the same
+ * time as the z:line check.  throttle_timer() should be called once per i/o
+ * loop to expire throttles and Z:lines.  All other structures and functions
+ * can be found in src/throttle.c as they should not be accessed outside of it.
+ *
+ * additionally, throttle_init() should be called once at initialization stage
+ * to setup hash tables and what-have-you
+ */
+
+/* setting bits */
+extern int throttle_enable, throttle_tcount, throttle_ttime, throttle_rtime;
+void throttle_force(char *host);
+
+
+#ifdef THROTTLE_ENABLE
+int throttle_check(char *ip, int fd, time_t sotime);
+void throttle_remove(char *host);
+void throttle_timer(time_t now);
+
+void throttle_init(void);
+void throttle_rehash(void);
+void throttle_resize(int size);
+void throttle_stats(aClient *cptr, char *name);
+
+#else
+
+#define throttle_check(x,y,z) ((int)1)
+#define throttle_remove(x) ((void)0)
+#define throttle_timer(x) ((void)0)
+
+#define throttle_init() ((void)0)
+#define throttle_rehash() ((void)0)
+#define throttle_resize(x) ((void)0)
+#define throttle_stats(x,y) ((void)0)
+#endif
+
+
+#include "queue.h"
+
+SLIST_HEAD(hashent_list_t, hashent_t);
+typedef struct hashent_list_t hashent_list;
+
+typedef struct hashent_t
+{
+    void    *ent;
+    SLIST_ENTRY(hashent_t) lp;
+} hashent;
+
+typedef struct hash_table_t
+{
+    int     size;           /* this should probably always be prime :) */
+    hashent_list *table;    /* our table */
+    size_t  keyoffset;      /* this stores the offset of the key from the
+        given structure */
+    size_t  keylen;         /* the length of the key. if 0, assume key
+        is a NULL terminated string */
+
+#define HASH_FL_NOCASE 0x1      /* ignore case (ToLower before hash) */
+#define HASH_FL_STRING 0x2      /* key is a nul-terminated string, treat len
+    as a maximum length to hash */
+    int     flags;
+    /* our comparison function, used in hash_find_ent().  this behaves much
+        * like the the compare function is used in qsort().  This means that a
+        * return of 0 (ZERO) means success! (this lets you use stuff like
+                                             * strncmp easily) */
+    int     (*cmpfunc)(void *, void *);
+} hash_table;
+
+/* this function creates a hashtable with 'elems' buckets (elems should be
+* prime for best efficiency).  'offset' is the offset of the key from
+* structures being added (this should be obtained with the 'offsetof()'
+                          * function).  len is the length of the key, and flags are any flags for the
+* table (see above).  cmpfunc is the function which should be used for
+* comparison when calling 'hash_find' */
+hash_table *create_hash_table(int elems, size_t offset, size_t len, int flags,
+                              int (*cmpfunc)(void *, void *));
+/* this function destroys a previously created hashtable */
+void destroy_hash_table(hash_table *table);
+/* this function resizes a hash-table to the new size given with 'elems'.
+* this is not in any way inexpensive, and should really not be done very
+* often.  */
+void resize_hash_table(hash_table *table, int elems);
+/* this function gets the hash value of a given key, relative to the size of
+* the hashtable */
+unsigned int hash_get_key_hash(hash_table *table, void *key, size_t offset);
+/* these functions do what you would expect, adding/deleting/finding items
+* in a hash table */
+int hash_insert(hash_table *table, void *ent);
+int hash_delete(hash_table *table, void *ent);
+void *hash_find(hash_table *table, void *key);
+
+#endif /* THROTTLE_H */
+/* vi:set ts=8 sts=4 sw=4 tw=79: */
diff --git a/include/userban.h b/include/userban.h
new file mode 100644 (file)
index 0000000..f1a7ea7
--- /dev/null
@@ -0,0 +1,113 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/userban.h
+ *   Copyright (C) 2002 Lucas Madar
+ *                      and the DALnet coding team
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: userban.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#define UBAN_LOCAL     0x001   /* formerly known as a K: or Z: line */
+#define UBAN_NETWORK   0x002   /* formerly known as an autokill or an SZline */
+
+#define UBAN_GECOS     0x004   /* formerly known as an SGLINE */
+#define UBAN_NICK      0x008   /* formerly known as an SQLINE */
+
+#define UBAN_HOST      0x010   /* this ban matches against the user's resolved host */
+#define UBAN_IP        0x020   /* this ban matches against the user's IP address */
+
+#define UBAN_WILD      0x040   /* this ban has wildcards */
+
+#define UBAN_CIDR4     0x080   /* this ban is an IPv4 CIDR ban */
+#define UBAN_CIDR4BIG  0x100   /* this ban is an IPv4 CIDR ban for something greater than a /16 */
+
+#define UBAN_WILDUSER  0x200   /* Username is just '*' */
+#define UBAN_WILDHOST  0x400   /* Hostname is just '*.*' or '*' -- this ban is a user@* ban */
+
+#define UBAN_TEMPORARY 0x800   /* userban is temporary */
+
+#define SBAN_LOCAL     0x001   
+#define SBAN_NETWORK   0x002   
+#define SBAN_NICK      0x004   /* sban on the nick field */
+#define SBAN_GCOS      0x008   /* sban on the gcos field */
+#define SBAN_CHAN      0x010   /* sban on the chname field */
+#define SBAN_WILD      0x020   /* sban mask contains wildcards */
+#define SBAN_TEMPORARY 0x040   /* sban is temporary */
+
+struct userBan {
+   unsigned int flags;
+   char *u;                    /* username */
+   char *h;                    /* host or IP or GECOS or NICK */
+
+   unsigned int cidr4ip;       /* cidr4 IP */   
+   unsigned int cidr4mask;     /* cidr4 mask */   
+
+   char *reason;
+   time_t timeset;             /* time this ban was set */
+   time_t duration;            /* length of this ban, in seconds, or 0xFFFFFFFF for permanent */   
+
+   void *internal_ent;         /* internal -- pointer to banlist entry tag */
+};
+
+struct simBan {
+   unsigned int flags;
+   char *mask;
+
+   char *reason;
+   time_t timeset;
+   time_t duration;
+
+   void *internal_ent;         /* internal -- pointer to banlist entry tag */
+};
+
+
+void init_userban();
+
+struct userBan *make_hostbased_ban(char *, char *);
+
+void add_hostbased_userban(struct userBan *);
+void remove_userban(struct userBan *);
+void userban_free(struct userBan *);
+
+struct userBan *check_userbanned(aClient *, unsigned int, unsigned int);
+struct userBan *find_userban_exact(struct userBan *, unsigned int);
+
+void expire_userbans();
+void remove_userbans_match_flags(unsigned int, unsigned int);
+void report_userbans_match_flags(aClient *cptr, unsigned int, unsigned int);
+
+int user_match_ban(aClient *, struct userBan *);
+char *get_userban_host(struct userBan *, char *, int);
+
+int count_userbans(aClient *cptr);
+
+/* Simban Calls */
+
+struct simBan *make_simpleban(unsigned int, char *);
+void add_simban(struct simBan *);
+void remove_simban(struct simBan *);
+struct simBan *find_simban_exact(struct simBan *);
+int user_match_simban(aClient *, struct simBan *);
+struct simBan *check_mask_simbanned(char *, unsigned int);
+void simban_free(struct simBan *);
+void remove_simban(struct simBan *);
+void remove_simbans_match_flags(unsigned int, unsigned int);
+void remove_simbans_match_mask(unsigned int, char *, int);
+void report_simbans_match_flags(aClient *, unsigned int, unsigned int);
+void expire_simbans();
+void send_simbans(aClient *, unsigned int);
+int count_simbans(aClient *);
+void remove_simban(struct simBan *);
diff --git a/include/whowas.h b/include/whowas.h
new file mode 100644 (file)
index 0000000..ab1c9b7
--- /dev/null
@@ -0,0 +1,64 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/whowas.h
+ *   Copyright (C) 1990  Markku Savela
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: whowas.h,v 1.1 2005/01/12 07:44:58 mmondor Exp $ */
+
+#ifndef        __whowas_include__
+#define __whowas_include__
+
+/* WHOWAS structure moved here from whowas.c */
+
+typedef struct aname 
+{
+    anUser     *ww_user;
+    aClient    *ww_online;
+    time_t      ww_logout;
+    char        ww_nick[NICKLEN + 1];
+    char        ww_info[REALLEN + 1];
+} aName;
+
+/*
+ * * add_history 
+ *    Add the currently defined name of the client to  history. 
+ *    usually called before changing to a new name (nick). 
+ *    Client must be a fully registered user (specifically, 
+ *    the user structure must have been allocated).
+ */
+void        add_history(aClient *, int);
+
+/*
+ * * off_history 
+ *    This must be called when the client structure is about to 
+ *    be released. History mechanism keeps pointers to client 
+ *    structures and it must know when they cease to exist. This 
+ *    also implicitly calls AddHistory.
+ */
+void        off_history(aClient *);
+
+/*
+ * * get_history 
+ *    Return the current client that was using the given 
+ *    nickname within the timelimit. Returns NULL, if no 
+ *    one found...
+ */
+aClient    *get_history(char *, time_t);
+
+int         m_whowas(aClient *, aClient *, int, char *[]);
+
+#endif /* __whowas_include__ */
diff --git a/include/zlink.h b/include/zlink.h
new file mode 100644 (file)
index 0000000..8e0ef38
--- /dev/null
@@ -0,0 +1,15 @@
+extern void *zip_create_input_session();
+extern void *zip_create_output_session();
+extern char *zip_input(void *session, char *buffer, int *len, int *err,
+                      char **nbuf, int *nlen);
+/* largedata is err return */
+extern char *zip_output(void *session, char *buffer, int *len,
+                       int forceflush, int *largedata);
+extern int zip_is_data_out(void *session);
+extern void zip_out_get_stats(void *session, unsigned long *insiz,
+                             unsigned long *outsiz, double *ratio);
+extern void zip_in_get_stats(void *session, unsigned long *insiz,
+                            unsigned long *outsiz, double *ratio);
+extern void zip_destroy_input_session(void *session);
+extern void zip_destroy_output_session(void *session);
+
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..2c212cc
--- /dev/null
@@ -0,0 +1,250 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644 (file)
index 0000000..3abbff2
--- /dev/null
@@ -0,0 +1,321 @@
+CC=@CC@
+RM=@RM@
+MV=@MV@
+IRCDLIBS=@LIBS@ ../zlib/libz.a
+INCLUDEDIR=-I../include
+OPENSSLINC=@SSL_INCLUDES@
+ENGINE=@SENGINE@
+CRYPTO=@ENCRYPT_SRC@
+INSTALL=@INSTALL@
+INSTALL_BIN=@INSTALL_PROGRAM@
+INSTALL_DIR=@INSTALL_DIR@
+CFLAGS=@CFLAGS@
+
+RES_SRC =
+
+#This is only for very old systems that NEED this
+#RES_SRC = res_mkquery.c res_init.c res_comp.c
+
+SOURCES = blalloc.c bsd.c channel.c clientlist.c clones.c confparse.c \
+          fdlist.c fds.c hash.c hide.c inet_addr.c ircd.c ircsprintf.c list.c \
+          m_nick.c m_rwho.c m_server.c m_services.c m_stats.c m_who.c match.c \
+          modules.c packet.c parse.c pcre.c res.c s_auth.c s_bsd.c s_conf.c \
+          s_debug.c s_err.c s_misc.c s_numeric.c s_serv.c s_user.c sbuf.c \
+          scache.c send.c struct.c support.c throttle.c userban.c whowas.c \
+          zlink.c \
+          $(ENGINE) $(CRYPTO)
+
+OBJECTS = $(SOURCES:.c=.o) version.o
+
+all:
+       @echo ""
+       @echo "You're in the wrong directory. Make in ..!"
+       @echo ""
+
+build: ircd
+
+# Yuck - don't use this.
+depend:
+       makedepend $(INCLUDE) $(SOURCES)
+
+clean:
+       $(RM) -f $(OBJECTS) *~ ircd.core core ircd 
+
+distclean: clean
+       $(RM) -f Makefile version.c.last
+
+.c.o:
+       $(CC) $(CFLAGS) $(INCLUDEDIR) -c $<
+
+ircd: $(OBJECTS)
+       $(CC) ${LDFLAGS} -o ircd $(OBJECTS) $(IRCDLIBS)
+       mv version.c version.c.last
+
+install:
+       @if test -f $(INSTALL_DIR)/ircd; then \
+               echo $(MV) $(INSTALL_DIR)/ircd $(INSTALL_DIR)/ircd.old; \
+               $(MV) $(INSTALL_DIR)/ircd $(INSTALL_DIR)/ircd.old; \
+       fi
+       @echo $(INSTALL_BIN) ircd $(INSTALL_DIR);
+       $(INSTALL_BIN) ircd $(INSTALL_DIR)
+
+version.c: version.c.SH
+       /bin/sh ./version.c.SH
+
+blalloc.o: blalloc.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h ../include/numeric.h ../include/blalloc.h
+bsd.o: bsd.c ../include/struct.h ../include/config.h ../include/setup.h \
+  ../include/defs.h ../include/hash.h ../include/sbuf.h \
+  ../include/common.h ../include/sys.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/fds.h
+channel.o: channel.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/channel.h ../include/msg.h \
+  ../include/h.h ../include/send.h ../include/fdlist.h \
+  ../include/ircsprintf.h ../include/find.h ../include/userban.h
+clientlist.o: clientlist.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h ../include/numeric.h ../include/blalloc.h
+clones.o: clones.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h ../include/blalloc.h ../include/numeric.h \
+  ../include/channel.h ../include/msg.h ../include/throttle.h \
+  ../include/queue.h ../include/clones.h
+confparse.o: confparse.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h ../include/userban.h ../include/confparse.h
+fdlist.o: fdlist.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h
+fds.o: fds.c ../include/struct.h ../include/config.h ../include/setup.h \
+  ../include/defs.h ../include/hash.h ../include/sbuf.h \
+  ../include/common.h ../include/sys.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/fds.h ../include/numeric.h
+hash.o: hash.c ../include/struct.h ../include/config.h ../include/setup.h \
+  ../include/defs.h ../include/hash.h ../include/sbuf.h \
+  ../include/common.h ../include/sys.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h
+hide.o: hide.c ../include/struct.h ../include/config.h ../include/setup.h \
+  ../include/defs.h ../include/hash.h ../include/sbuf.h \
+  ../include/common.h ../include/sys.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/fds.h ../include/numeric.h
+inet_addr.o: inet_addr.c ../include/setup.h ../include/struct.h \
+  ../include/config.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/nameser.h ../include/resolv.h
+ircd.o: ircd.c ../include/struct.h ../include/config.h ../include/setup.h \
+  ../include/defs.h ../include/hash.h ../include/sbuf.h \
+  ../include/common.h ../include/sys.h ../include/numeric.h \
+  ../include/msg.h ../include/inet.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/patchlevel.h ../include/dh.h ../include/throttle.h \
+  ../include/queue.h ../include/userban.h ../include/clones.h \
+  ../include/hooks.h ../include/fds.h
+ircsprintf.o: ircsprintf.c ../include/ircsprintf.h ../include/setup.h
+list.o: list.c ../include/struct.h ../include/config.h ../include/setup.h \
+  ../include/defs.h ../include/hash.h ../include/sbuf.h \
+  ../include/common.h ../include/sys.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/numeric.h ../include/blalloc.h ../include/dh.h \
+  ../include/zlink.h
+m_nick.o: m_nick.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/msg.h ../include/channel.h \
+  ../include/h.h ../include/send.h ../include/fdlist.h \
+  ../include/ircsprintf.h ../include/find.h ../include/userban.h
+m_rwho.o: m_rwho.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h ../include/numeric.h ../include/channel.h \
+  ../include/msg.h ../include/inet.h ../include/pcre.h
+m_server.o: m_server.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/dh.h ../include/userban.h ../include/zlink.h \
+  ../include/throttle.h ../include/queue.h ../include/clones.h
+m_services.o: m_services.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/msg.h ../include/channel.h \
+  ../include/h.h ../include/send.h ../include/fdlist.h \
+  ../include/ircsprintf.h ../include/find.h ../include/userban.h \
+  ../include/clones.h
+m_stats.o: m_stats.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/msg.h ../include/channel.h \
+  ../include/h.h ../include/send.h ../include/fdlist.h \
+  ../include/ircsprintf.h ../include/find.h ../include/zlink.h \
+  ../include/userban.h ../include/blalloc.h ../include/throttle.h \
+  ../include/queue.h ../include/whowas.h ../include/res.h
+m_who.o: m_who.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/inet.h ../include/msg.h \
+  ../include/channel.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h
+match.o: match.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h
+modules.o: modules.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/msg.h ../include/channel.h \
+  ../include/throttle.h ../include/queue.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h ../include/hooks.h
+packet.o: packet.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/msg.h \
+  ../include/h.h ../include/send.h ../include/fdlist.h \
+  ../include/ircsprintf.h ../include/find.h ../include/dh.h \
+  ../include/zlink.h
+parse.o: parse.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/msg.h ../include/sys.h \
+  ../include/numeric.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h
+pcre.o: pcre.c ../include/pcre_internal.h ../include/pcre_config.h \
+  ../include/setup.h ../include/pcre.h pcre_chartables.c
+res.o: res.c ../include/struct.h ../include/config.h ../include/setup.h \
+  ../include/defs.h ../include/hash.h ../include/sbuf.h \
+  ../include/common.h ../include/sys.h ../include/res.h \
+  ../include/numeric.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/fds.h ../include/nameser.h ../include/resolv.h \
+  ../include/inet.h
+s_auth.o: s_auth.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/res.h \
+  ../include/numeric.h ../include/patchlevel.h ../include/sock.h \
+  ../include/h.h ../include/send.h ../include/fdlist.h \
+  ../include/ircsprintf.h ../include/find.h ../include/fds.h
+s_bsd.o: s_bsd.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/res.h \
+  ../include/numeric.h ../include/patchlevel.h ../include/zlink.h \
+  ../include/throttle.h ../include/queue.h ../include/userban.h \
+  ../include/clones.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/inet.h ../include/hooks.h ../include/nameser.h \
+  ../include/resolv.h ../include/fds.h
+s_conf.o: s_conf.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/inet.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/userban.h ../include/confparse.h
+s_debug.o: s_debug.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h ../include/patchlevel.h ../include/numeric.h \
+  ../include/channel.h ../include/msg.h
+s_err.o: s_err.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/numeric.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h
+s_misc.o: s_misc.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/zlink.h ../include/hooks.h \
+  ../include/clones.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h
+s_numeric.o: s_numeric.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/channel.h ../include/msg.h \
+  ../include/h.h ../include/send.h ../include/fdlist.h \
+  ../include/ircsprintf.h ../include/find.h
+s_serv.o: s_serv.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/msg.h ../include/channel.h \
+  ../include/nameser.h ../include/resolv.h ../include/dh.h \
+  ../include/zlink.h ../include/userban.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h ../include/throttle.h ../include/queue.h
+s_user.o: s_user.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/msg.h ../include/channel.h \
+  ../include/throttle.h ../include/queue.h ../include/clones.h \
+  ../include/h.h ../include/send.h ../include/fdlist.h \
+  ../include/ircsprintf.h ../include/find.h ../include/blalloc.h \
+  ../include/userban.h ../include/hooks.h
+sbuf.o: sbuf.c ../include/sbuf.h ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/common.h ../include/sys.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h
+scache.o: scache.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h
+send.o: send.c ../include/struct.h ../include/config.h ../include/setup.h \
+  ../include/defs.h ../include/hash.h ../include/sbuf.h \
+  ../include/common.h ../include/sys.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/numeric.h ../include/dh.h ../include/zlink.h \
+  ../include/fds.h
+struct.o: struct.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/msg.h ../include/channel.h \
+  ../include/throttle.h ../include/queue.h ../include/structfunc.h
+support.o: support.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/h.h \
+  ../include/send.h ../include/fdlist.h ../include/ircsprintf.h \
+  ../include/find.h
+throttle.o: throttle.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h ../include/res.h \
+  ../include/h.h ../include/send.h ../include/fdlist.h \
+  ../include/ircsprintf.h ../include/find.h ../include/numeric.h \
+  ../include/blalloc.h ../include/queue.h ../include/throttle.h
+userban.o: userban.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/inet.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h \
+  ../include/userban.h ../include/queue.h
+version.o: version.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/patchlevel.h
+whowas.o: whowas.c ../include/struct.h ../include/config.h \
+  ../include/setup.h ../include/defs.h ../include/hash.h \
+  ../include/sbuf.h ../include/common.h ../include/sys.h \
+  ../include/numeric.h ../include/h.h ../include/send.h \
+  ../include/fdlist.h ../include/ircsprintf.h ../include/find.h
+
+dh.o: dh.c \
+ ../include/patchlevel.h
+       $(CC) $(CFLAGS) $(OPENSSLINC) $(INCLUDEDIR) -c $<
+rc4.o: rc4.c \
+ ../include/patchlevel.h
+zlink.o: zlink.c ../zlib/zlib.h ../zlib/zconf.h
+       $(CC) $(CFLAGS) $(INCLUDEDIR) -I../zlib -c $<
+socketengine_poll.o: ../include/config.h ../include/h.h
+socketengine_select.o: ../include/config.h ../include/h.h
+socketengine_kqueue.o: ../include/config.h ../include/h.h
+socketengine_epoll.o: ../include/config.h ../include/h.h
diff --git a/src/blalloc.c b/src/blalloc.c
new file mode 100644 (file)
index 0000000..f69c940
--- /dev/null
@@ -0,0 +1,364 @@
+/************************************************************************
+ *
+ * File:   blalloc.c
+ * Owner:  Wohali (Joan Touzet)
+ * Hacked up for use in ircd by Dianora                             
+ *************************************************************************/
+
+/* $Id: blalloc.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+/* INCLUDES */
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "numeric.h"
+#include "blalloc.h"
+
+/* FUNCTION PROTOTYPES (LOCAL FUNCTIONS ONLY) */
+static int  newblock(BlockHeap *bh);
+
+/* FUNCTION PROTOTYPES (EXTERN FUNCTIONS ONLY) */
+extern void outofmemory();     /* defined in list.c */
+
+/* FUNCTION DOCUMENTATION:
+ *
+ * newblock
+ *  Description:
+ *   mallocs a new block for addition to a blockheap
+ *  Parameters:
+ *   bh (IN): Pointer to parent blockheap.
+ *  Returns:
+ *   0 if successful, 1 if not
+ */
+static int newblock(BlockHeap *bh)
+{
+    Block      *b;
+    int         i;
+    /* Setup the initial data structure.  */
+    b = (Block *) MyMalloc(sizeof(Block));
+    if (b == NULL)
+       return 1;
+    b->freeElems = bh->elemsPerBlock;
+    b->next = bh->base;
+    b->allocMap = (unsigned long *) MyMalloc(sizeof(unsigned long) * (bh->numlongs + 1));
+    memset((void *) b->allocMap, '\0', (bh->numlongs + 1) * sizeof(unsigned long));
+    if (b->allocMap == NULL)
+    {
+       MyFree(b);
+       return 1;
+    }
+    /* Now allocate the memory for the elems themselves. */
+    b->elems = (void *) MyMalloc((bh->elemsPerBlock + 1) * bh->elemSize);
+    if (b->elems == NULL)
+    {
+       MyFree(b->allocMap);
+       MyFree(b);
+       return 1;
+    }
+    b->endElem = (void *) ((unsigned long) b->elems +
+                          (unsigned long) ((bh->elemsPerBlock - 1) * 
+                                           bh->elemSize));
+    /* Mark all blocks as free */
+    for (i = 0; i < bh->numlongs; i++)
+       b->allocMap[i] = 0L;
+    /* Finally, link it in to the heap. */
+    ++bh->blocksAllocated;
+    bh->freeElems += bh->elemsPerBlock;
+    bh->base = b;
+    return 0;
+}
+
+/* FUNCTION DOCUMENTATION:
+ *
+ * BlockHeapCreate
+ *
+ * Description:
+ *  Creates a new blockheap from which smaller blocks can be allocated.
+ *   Intended to be used instead of multiple calls to malloc() when
+ *   performance is an issue.
+ * Parameters:
+ *  elemsize (IN):  Size of the basic element to be stored
+ *  elemsperblock (IN):  Number of elements to be stored in a single
+ *   block of memory.  When the blockheap runs out of free memory, it will  
+ *   allocate elemsize * elemsperblock more.                          
+ * Returns:
+ *  Pointer to new BlockHeap, or NULL if unsuccessful
+ */
+BlockHeap *BlockHeapCreate(size_t elemsize, int elemsperblock)
+{
+    BlockHeap  *bh;
+    /* Catch idiotic requests up front */
+    if ((elemsize <= 0) || (elemsperblock <= 0))
+    {
+       outofmemory();          /* die.. out of memory */
+    }
+    /* Allocate our new BlockHeap */
+    bh = (BlockHeap *) MyMalloc(sizeof(BlockHeap));
+    if (bh == NULL)
+    {
+       outofmemory();          /* die.. out of memory */
+    }
+    elemsize = elemsize + (elemsize & (sizeof(void *) - 1));
+    bh->elemSize = elemsize;
+    bh->elemsPerBlock = elemsperblock;
+    bh->blocksAllocated = 0;
+    bh->freeElems = 0;
+    bh->numlongs = (bh->elemsPerBlock / (sizeof(unsigned long) * 8)) + 1;
+    if ((bh->elemsPerBlock % (sizeof(unsigned long) * 8)) == 0)
+       bh->numlongs--;
+    bh->base = NULL;
+  
+    /* Be sure our malloc was successful */
+    if (newblock(bh))
+    {
+       MyFree(bh);
+       outofmemory();          /* die.. out of memory */
+    }
+    /* DEBUG */
+    if (bh == NULL)
+    {
+       outofmemory();          /* die.. out of memory */
+    }
+    return bh;
+}
+
+/* FUNCTION DOCUMENTATION:
+ *
+ * BlockHeapAlloc
+ *
+ * Description:
+ *  Returns a pointer to a struct within our BlockHeap that's free for
+ *  the taking.
+ * Parameters:
+ *  bh (IN):  Pointer to the Blockheap.
+ * Returns:
+ *  Pointer to a structure (void *), or NULL if unsuccessful.
+ */
+
+void *BlockHeapAlloc(BlockHeap *bh)
+{
+    Block      *walker;
+    int         unit;
+    unsigned long mask;
+    unsigned long ctr;
+    if (bh == NULL) return ((void *) NULL);
+    if (bh->freeElems == 0)    /* Allocate new block and assign */
+    {
+       /* newblock returns 1 if unsuccessful, 0 if not */
+       if (newblock(bh))
+       {
+           return ((void *) NULL);
+       }
+       else
+       {
+           walker = bh->base;
+           walker->allocMap[0] = 0x1L;
+           walker->freeElems--;
+           bh->freeElems--;
+           if (bh->base->elems == NULL)
+               return ((void *) NULL);
+       }
+       return ((bh->base)->elems);     /* ...and take the first elem. */
+    }
+    for (walker = bh->base; walker != NULL; walker = walker->next)
+    {
+       if (walker->freeElems > 0)
+       {
+           mask = 0x1L;
+           ctr = 0;
+           unit = 0;
+           while (unit < bh->numlongs)
+           {
+               if ((mask == 0x1L) && (walker->allocMap[unit] == ~0))
+               {
+                   /* Entire subunit is used, skip to next one. */
+                   unit++;
+                   ctr = 0;
+                   continue;
+               }
+               /* Check the current element, if free allocate block */
+               if (!(mask & walker->allocMap[unit]))
+               {
+                   walker->allocMap[unit] |= mask;   /* Mark block as used */
+                   walker->freeElems--;
+                   bh->freeElems--;
+                   /* And return the pointer
+                    * 
+                    * Address arithemtic is always ca-ca have to make sure
+                    * the the bit pattern for the base address is converted
+                    * into the same number of bits in an integer type, that
+                    * has at least sizeof(unsigned long) at least ==
+                    * sizeof(void *) -Dianora
+                    */
+         
+                   return ((void *) ((unsigned long) walker->elems +
+                                     ((unit * sizeof(unsigned long) *
+                                       8 + ctr) *
+                                      (unsigned long) bh->elemSize)));
+               }
+               /* Step up to the next unit */
+               mask <<= 1;
+               ctr++;
+               if (!mask)
+               {
+                   mask = 0x1L;
+                   unit++;
+                   ctr = 0;
+               }
+           }                   /* while */
+       }                               /* if */
+    }                          /* for */
+    
+    return ((void *) NULL);    /* If you get here, something bad happened ! */
+}
+
+/* FUNCTION DOCUMENTATION:
+ *
+ * BlockHeapFree
+ *
+ * Description: 
+ *  Returns an element to the free pool, does not free()
+ * Parameters:
+ *  bh (IN): Pointer to BlockHeap containing element
+ *  ptr (in):  Pointer to element to be "freed"
+ * Returns:
+ *  0 if successful, 1 if element not contained within BlockHeap.
+ */
+
+int BlockHeapFree(BlockHeap *bh, void *ptr)
+{
+    Block      *walker;
+    unsigned long ctr;
+    unsigned long bitmask;
+    if (bh == NULL)
+    {
+#if defined(USE_SYSLOG) && defined(SYSLOG_BLOCK_ALLOCATOR)
+       syslog(LOG_DEBUG, "blalloc.c bh == NULL");
+#endif
+       return 1;
+    }
+    for (walker = bh->base; walker != NULL; walker = walker->next)
+    {
+       if ((ptr >= walker->elems) && (ptr <= walker->endElem))
+       {
+           ctr = ((unsigned long) ptr - (unsigned long) (walker->elems)) /
+               (unsigned long) bh->elemSize;
+           bitmask = 1L << (ctr % (sizeof(unsigned long) * 8));
+           ctr = ctr / (sizeof(unsigned long) * 8);
+           /* Flip the right allocation bit
+            *
+            * Complain if the bit is already clear, something is wrong
+            * (typically, someone freed the same block twice)
+            */
+           if ((walker->allocMap[ctr] & bitmask) == 0)
+           {
+               abort(); /* This is more useful than some random complaining */
+#if defined(USE_SYSLOG) && defined(SYSLOG_BLOCK_ALLOCATOR)
+               syslog(LOG_DEBUG, "blalloc.c bit already clear in map!");
+#endif
+               sendto_ops("blalloc.c bit already clear in map!");
+               sendto_ops("Please report to the bahamut team!"
+                          "bahamut-bugs@bahamut.net");
+           }
+           else
+           {
+               walker->allocMap[ctr] = walker->allocMap[ctr] & ~bitmask;
+               walker->freeElems++;
+               bh->freeElems++;
+           }
+           return 0;
+       }
+    }
+    return 1;
+}
+/* FUNCTION DOCUMENTATION:
+ *
+ * BlockHeapGarbageCollect
+ *
+ * Description:
+ *  Performs garbage colletion on the block heap.  Any blocks that are
+ *  completely unallocated are removed from the heap.  Garbage
+ *  collection will never remove the root node of the heap.
+ * Parameters:
+ *  bh (IN):  Pointer to the BlockHeap to be cleaned up
+ * Returns:
+ *  0 if successful, 1 if bh == NULL
+ */
+int BlockHeapGarbageCollect(BlockHeap *bh)
+{
+    Block      *walker, *last;
+  
+    if (bh == NULL) return 1;
+  
+    if (bh->freeElems < bh->elemsPerBlock)
+    {
+       /* There couldn't possibly be an entire free block.  Return. */
+       return 0;
+    }
+  
+    last = NULL;
+    walker = bh->base;
+    while (walker)
+    {
+       /* This section rewritten Dec 10 1998 - Dianora */
+       int i;
+       for (i = 0; i < bh->numlongs; i++)
+       {
+           if (walker->allocMap[i])
+               break;
+       }
+       if (i == bh->numlongs)
+       {
+           /* This entire block is free.  Remove it. */
+           MyFree(walker->elems);
+           MyFree(walker->allocMap);
+           if (last)
+           {
+               last->next = walker->next;
+               MyFree(walker);
+               walker = last->next;
+           }
+           else
+           {
+               bh->base = walker->next;
+               MyFree(walker);
+               walker = bh->base;
+           }
+           bh->blocksAllocated--;
+           bh->freeElems -= bh->elemsPerBlock;
+       }
+       else
+       {
+           last = walker;
+           walker = walker->next;
+       }
+    }
+    return 0;
+}
+
+/* FUNCTION DOCUMENTATION:
+ *
+ * BlockHeapDestroy
+ *
+ * Description:
+ *  Completely free()s a BlockHeap.  Use for cleanup.
+ * Parameters:
+ *  bh (IN):  Pointer to the BlockHeap to be destroyed.
+ * Returns:
+ *  0 if successful, 1 if bh == NULL
+ */
+int BlockHeapDestroy(BlockHeap *bh)
+{
+    Block      *walker, *next;
+    if (bh == NULL) return 1;
+    for (walker = bh->base; walker != NULL; walker = next)
+    {
+       next = walker->next;
+       MyFree(walker->elems);
+       MyFree(walker->allocMap);
+       MyFree(walker);
+    }
+    MyFree(bh);        
+    return 0;
+}
diff --git a/src/bsd.c b/src/bsd.c
new file mode 100644 (file)
index 0000000..08cc2b0
--- /dev/null
+++ b/src/bsd.c
@@ -0,0 +1,166 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/bsd.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: bsd.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "fds.h"
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+extern int  errno;  /* ...seems that errno.h doesn't define this everywhere */
+#ifndef SYS_ERRLIST_DECLARED
+extern char *sys_errlist[];
+#endif
+
+#if defined(DEBUGMODE) || defined (DNS_DEBUG)
+int writecalls = 0, writeb[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+int readcalls = 0;
+#endif
+void dummy()
+{
+    struct sigaction act;
+    
+    act.sa_handler = dummy;
+    act.sa_flags = 0;
+    (void) sigemptyset(&act.sa_mask);
+    (void) sigaddset(&act.sa_mask, SIGALRM);
+    (void) sigaddset(&act.sa_mask, SIGPIPE);
+#ifdef SIGWINCH
+    (void) sigaddset(&act.sa_mask, SIGWINCH);
+#endif
+    (void) sigaction(SIGALRM, &act, (struct sigaction *) NULL);
+    (void) sigaction(SIGPIPE, &act, (struct sigaction *) NULL);
+#ifdef SIGWINCH
+    (void) sigaction(SIGWINCH, &act, (struct sigaction *) NULL);
+#endif
+}
+
+/*
+ * deliver_it 
+ *
+ * Attempt to send a sequence of bytes to the connection. 
+ * Returns 
+ *  < 0     Some fatal error occurred, (but not EWOULDBLOCK). 
+ *    his return is a request to close the socket and clean up the link.
+ *  >= 0    No real error occurred, returns the number of bytes actually
+ *    transferred. EWOULDBLOCK and other similar conditions should be mapped
+ *    to zero return.
+ *    Upper level routine will have to decide what to do with
+ *    those unwritten bytes... 
+ * *NOTE*  alarm calls have been preserved, so this should work equally 
+ *  well whether blocking or non-blocking mode is used...
+ */
+#ifdef WRITEV_IOV
+int deliver_it(aClient *cptr, struct iovec *iov, int len)
+#else
+int deliver_it(aClient *cptr, char *str, int len)
+#endif
+{
+    int         retval;
+    aListener    *lptr = cptr->lstn;    
+#ifdef  DEBUGMODE
+    writecalls++;
+#endif
+#ifdef WRITEV_IOV
+    retval = writev(cptr->fd, iov, len);
+#else
+    retval = send(cptr->fd, str, len, 0);
+#endif
+    /*
+     * Convert WOULDBLOCK to a return of "0 bytes moved". This 
+     * should occur only if socket was non-blocking. Note, that all is
+     * Ok, if the 'write' just returns '0' instead of an error and
+     * errno=EWOULDBLOCK. 
+     */
+    if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN ||
+               errno == ENOBUFS))
+    {
+        retval = 0;
+        cptr->flags |= FLAGS_BLOCKED;
+        set_fd_flags(cptr->fd, FDF_WANTWRITE);
+        return (retval);        /* Just get out now... */
+    }
+    else if (retval > 0)
+    {
+        if(cptr->flags & FLAGS_BLOCKED)
+        {
+            cptr->flags &= ~FLAGS_BLOCKED;
+            unset_fd_flags(cptr->fd, FDF_WANTWRITE);
+        }
+    }
+    
+#ifdef DEBUGMODE
+    if (retval < 0)
+    {
+        writeb[0]++;
+        Debug((DEBUG_ERROR, "write error (%s) to %s",
+           sys_errlist[errno], cptr->name));
+    }
+    else if (retval == 0)
+        writeb[1]++;
+    else if (retval < 16)
+        writeb[2]++;
+    else if (retval < 32)
+        writeb[3]++;
+    else if (retval < 64)
+        writeb[4]++;
+    else if (retval < 128)
+        writeb[5]++;
+    else if (retval < 256)
+        writeb[6]++;
+    else if (retval < 512)
+        writeb[7]++;
+    else if (retval < 1024)
+        writeb[8]++;
+    else
+        writeb[9]++;
+#endif
+    if (retval > 0)
+    {
+        cptr->sendB += retval;
+        if (cptr->sendB & 0x0400)
+        {
+            cptr->sendK += (cptr->sendB >> 10);
+            cptr->sendB &= 0x03ff;  /* 2^10 = 1024, 3ff = 1023 */
+        }
+        me.sendB += retval;
+        if (me.sendB & 0x0400)
+        {
+            me.sendK += (me.sendB >> 10);
+            me.sendB &= 0x03ff;
+        }
+
+        if (lptr)
+        {
+            lptr->sendB += retval;
+            if (lptr->sendB & 0x0400) 
+            {
+                lptr->sendK += (lptr->sendB >> 10);
+                lptr->sendB &= 0x03ff;
+            }
+        }
+    }
+    return (retval);
+}
diff --git a/src/channel.c b/src/channel.c
new file mode 100644 (file)
index 0000000..ae8182a
--- /dev/null
@@ -0,0 +1,4686 @@
+/*
+ *   IRC - Internet Relay Chat, src/channel.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Co Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: channel.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "channel.h"
+#include "h.h"
+#include "userban.h"
+
+int         server_was_split = YES;
+
+aChannel   *channel = NullChn;
+
+#ifdef INVITE_LISTS
+/* +I list functions */
+int       add_invite_id(aClient*, aChannel*, char*);
+int       del_invite_id(aChannel*, char*);
+anInvite* is_invited(aClient*, aChannel*);
+#endif
+
+#ifdef EXEMPT_LISTS
+/* +e list functions */
+int       add_exempt_id(aClient*, aChannel*, char*);
+int       del_exempt_id(aChannel*, char*);
+#endif
+
+static int  add_banid(aClient *, aChannel *, char *);
+static int  can_join(aClient *, aChannel *, char *);
+static void channel_modes(aClient *, char *, char *, aChannel *);
+static int  del_banid(aChannel *, char *);
+static int  is_banned(aClient *, aChannel *, chanMember *);
+static int  set_mode(aClient *, aClient *, aChannel *, int, 
+                     int, char **, char *, char *);
+static void sub1_from_channel(aChannel *);
+
+int         check_channelname(aClient *, unsigned char *);
+void        clean_channelname(unsigned char *);
+static void add_invite(aClient *, aChannel *);
+void        del_invite(aClient *, aChannel *);
+
+#ifdef ORATIMING
+struct timeval tsdnow, tsdthen;
+unsigned long tsdms;
+#endif
+
+/* number of seconds to add to all readings of time() when making TS's */
+
+static char *PartFmt = ":%s PART %s";
+static char *PartFmt2 = ":%s PART %s :%s";
+
+/* server <-> server SJOIN format  */
+static char *SJOINFmt = ":%s SJOIN %ld %s %s %s :%s";
+/* NP means no paramaters, don't send the extra space there */
+static char *SJOINFmtNP = ":%s SJOIN %ld %s %s :%s";
+/* client SJOIN format, for no channel creation */
+static char *CliSJOINFmt = ":%s SJOIN %ld %s";
+
+/* some buffers for rebuilding channel/nick lists with ,'s */
+static char nickbuf[BUFSIZE], buf[BUFSIZE];
+static char modebuf[REALMODEBUFLEN], parabuf[REALMODEBUFLEN];
+
+/* externally defined function */
+extern Link *find_channel_link(Link *, aChannel *);     /* defined in list.c */
+#ifdef ANTI_SPAMBOT
+extern int  spam_num;           /* defined in s_serv.c */
+extern int  spam_time;          /* defined in s_serv.c */
+#endif
+
+/* return the length (>=0) of a chain of links. */
+static int list_length(Link *lp)
+{
+    int     count = 0;
+    
+    for (; lp; lp = lp->next)
+        count++;
+    return count;
+}
+
+/* check to see if the message has any color chars in it. */
+static int
+msg_has_colors(char *msg)
+{
+
+    char *c;
+    if (msg == NULL) 
+        return 0;
+    c = msg;
+    while(*c)
+    {
+        if(*c == '\003' || *c == '\033')
+            break;
+        else
+            c++;
+    }
+    if(*c)
+        return 1;
+    return 0;
+}
+
+/*
+ * find_chasing 
+ *   Find the client structure for a nick name (user) using history 
+ *   mechanism if necessary. If the client is not found, an error message 
+ *   (NO SUCH NICK) is generated. If the client was found through the 
+ *   history, chasing will be 1 and otherwise 0.
+ */
+aClient *find_chasing(aClient *sptr, char *user, int *chasing)
+{
+    aClient *who = find_client(user, (aClient *) NULL);
+    
+    if (chasing)
+        *chasing = 0;
+    if (who)
+        return who;
+    if (!(who = get_history(user, (long) KILLCHASETIMELIMIT)))
+    {
+        sendto_one(sptr, err_str(ERR_NOSUCHNICK),
+                   me.name, sptr->name, user);
+        return ((aClient *) NULL);
+    }
+    if (chasing)
+        *chasing = 1;
+    return who;
+}
+
+/*
+ * Fixes a string so that the first white space found becomes an end of
+ * string marker (`\-`).  returns the 'fixed' string or "*" if the
+ * string was NULL length or a NULL pointer.
+ */
+static char * check_string(char *s)
+{
+    static char star[2] = "*";
+    char       *str = s;
+    
+    if (BadPtr(s))
+        return star;
+    
+    for (; *s; s++)
+        if (IsSpace(*s))
+        {
+            *s = '\0';
+            break;
+        }
+    
+    return (BadPtr(str)) ? star : str;
+}
+/*
+ * create a string of form "foo!bar@fubar" given foo, bar and fubar as
+ * the parameters.  If NULL, they become "*".
+ */
+static char *make_nick_user_host(char *nick, char *name, char *host)
+{
+    static char namebuf[NICKLEN + USERLEN + HOSTLEN + 6];
+    int         n;
+    char   *ptr1, *ptr2;
+    
+    ptr1 = namebuf;
+    for (ptr2 = check_string(nick), n = NICKLEN; *ptr2 && n--;)
+        *ptr1++ = *ptr2++;
+    *ptr1++ = '!';
+    for (ptr2 = check_string(name), n = USERLEN; *ptr2 && n--;)
+        *ptr1++ = *ptr2++;
+    *ptr1++ = '@';
+    for (ptr2 = check_string(host), n = HOSTLEN; *ptr2 && n--;)
+        *ptr1++ = *ptr2++;
+    *ptr1 = '\0';
+    return (namebuf);
+}
+
+#ifdef EXEMPT_LISTS
+/* Exempt list functions (+e) */
+
+int add_exempt_id(aClient* cptr, aChannel* chptr, char* exempt_id)
+{
+    aBanExempt*   exempt = NULL;
+    int           cnt = 0;
+
+    for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
+    {
+        if (MyClient(cptr))
+        {
+            if (++cnt >= MAXEXEMPTLIST)
+            {
+                sendto_one(cptr, getreply(ERR_BANLISTFULL), me.name, cptr->name,
+                    chptr->chname, exempt_id, "exempt");
+                return -1;
+            }
+            if (!match(exempt->banstr, exempt_id))
+                return -1;
+        }
+        else if (!mycmp(exempt->banstr, exempt_id))
+            return -1;
+    }
+    exempt = (aBanExempt*)MyMalloc(sizeof(aBanExempt));
+    exempt->banstr = (char*)MyMalloc(strlen(exempt_id)+1);
+    strcpy(exempt->banstr, exempt_id);
+    exempt->when = timeofday;
+    exempt->next = chptr->banexempt_list;
+    chptr->banexempt_list = exempt;
+    chptr->banserial++;
+
+    if (IsPerson(cptr))
+    {
+        exempt->who = (char *) MyMalloc(strlen(cptr->name) +
+                                     strlen(cptr->user->username) +
+                                     strlen(cptr->user->host) + 3);
+        (void) ircsprintf(exempt->who, "%s!%s@%s",
+                          cptr->name, cptr->user->username, cptr->user->host);
+    }
+    else
+    {
+        exempt->who = (char *) MyMalloc(strlen(cptr->name) + 1);
+        (void) strcpy(exempt->who, cptr->name);
+    }
+
+    /* determine type for less matching later */
+    if(exempt_id[0] == '*' && exempt_id[1] == '!')
+    {
+        if(exempt_id[2] == '*' && exempt_id[3] == '@')
+            exempt->type = MTYP_HOST;
+        else
+            exempt->type = MTYP_USERHOST;
+    }
+    else
+        exempt->type = MTYP_FULL;
+
+    return 0;
+}
+
+int del_exempt_id(aChannel* chptr, char* exempt_id)
+{
+   aBanExempt**  exempt;
+   aBanExempt*   tmp;
+
+   if (!exempt_id)
+       return -1;
+   for (exempt = &chptr->banexempt_list; *exempt; exempt = &((*exempt)->next))
+   {
+       if (mycmp(exempt_id, (*exempt)->banstr) == 0)
+       {
+           tmp = *exempt;
+           *exempt = tmp->next;
+
+           chptr->banserial++;
+
+           MyFree(tmp->banstr);
+           MyFree(tmp->who);
+           MyFree(tmp);
+           
+           break;
+       }
+   }
+   return 0;
+}
+
+#endif
+
+#ifdef INVITE_LISTS
+/* Invite list functions (+I) */
+
+int add_invite_id(aClient* cptr, aChannel* chptr, char* invite_id)
+{
+    anInvite*     invite;
+    int           cnt = 0;
+    
+    for (invite = chptr->invite_list; invite; invite = invite->next)
+    {
+        if (MyClient(cptr))
+        {
+            if (++cnt >= MAXINVITELIST)
+            {
+                sendto_one(cptr, getreply(ERR_BANLISTFULL), me.name, cptr->name,
+                    chptr->chname, invite_id, "invite");
+                return -1;
+            }
+            if (!match(invite->invstr, invite_id))
+                return -1;
+        }
+        else if (!mycmp(invite->invstr, invite_id))
+            return -1;
+    }
+
+    invite = (anInvite*)MyMalloc(sizeof(anInvite));
+    invite->invstr = (char*)MyMalloc(strlen(invite_id)+1);
+    strcpy(invite->invstr, invite_id);
+    invite->when = timeofday;
+    invite->next = chptr->invite_list;
+    chptr->invite_list = invite;
+    
+    if (IsPerson(cptr))
+    {
+        invite->who = (char *) MyMalloc(strlen(cptr->name) +
+                                     strlen(cptr->user->username) +
+                                     strlen(cptr->user->host) + 3);
+        (void) ircsprintf(invite->who, "%s!%s@%s",
+                          cptr->name, cptr->user->username, cptr->user->host);
+    }
+    else
+    {
+        invite->who = (char *) MyMalloc(strlen(cptr->name) + 1);
+        (void) strcpy(invite->who, cptr->name);
+    }
+    return 0;
+}
+
+int del_invite_id(aChannel* chptr, char* invite_id)
+{
+   anInvite**    invite;
+   anInvite*     tmp;
+
+   if (!invite_id)
+       return -1;
+   for (invite = &chptr->invite_list; *invite; invite = &((*invite)->next))
+   {
+       if (mycmp(invite_id, (*invite)->invstr) == 0)
+       {
+           tmp = *invite;
+           *invite = tmp->next;
+           
+           MyFree(tmp->invstr);
+           MyFree(tmp->who);
+           MyFree(tmp);
+           
+           break;
+       }
+   }
+   return 0;
+}
+
+anInvite* is_invited(aClient* cptr, aChannel* chptr)
+{
+    char         s[NICKLEN + USERLEN + HOSTLEN + 6];
+    char        *s2;
+    anInvite*    invite;
+
+    strcpy(s, make_nick_user_host(cptr->name, cptr->user->username,
+                                  cptr->user->host));
+    s2 = make_nick_user_host(cptr->name, cptr->user->username,
+                             cptr->hostip);
+
+    for (invite = chptr->invite_list; invite; invite = invite->next)
+    {
+        if (!match(invite->invstr, s) || !match(invite->invstr, s2))
+            break;
+    }
+    return invite;
+}
+
+#endif
+
+/* Ban functions to work with mode +b */
+/* add_banid - add an id to be banned to the channel  (belongs to cptr) */
+
+static int add_banid(aClient *cptr, aChannel *chptr, char *banid)
+{
+    aBan        *ban;
+    int          cnt = 0;
+    
+    for (ban = chptr->banlist; ban; ban = ban->next)
+    {
+        /* Begin unbreaking redundant ban checking.  First step is to allow
+         * ALL non-duplicates from remote servers.  Local clients are still
+         * subject to the flawed redundancy check for compatibility with
+         * older servers.  This check can be corrected later.  -Quension */
+        if (MyClient(cptr))
+        {
+            if (++cnt >= MAXBANS)
+            {
+                sendto_one(cptr, getreply(ERR_BANLISTFULL), me.name, cptr->name,
+                        chptr->chname, banid, "ban");
+                return -1;
+            }
+            if (!match(ban->banstr, banid))
+                return -1;
+        }
+        else if (!mycmp(ban->banstr, banid))
+            return -1;
+    }
+
+    ban = (aBan *) MyMalloc(sizeof(aBan));
+    ban->banstr = (char *) MyMalloc(strlen(banid) + 1);
+    (void) strcpy(ban->banstr, banid);
+    ban->next = chptr->banlist;
+    
+    if (IsPerson(cptr))
+    {
+        ban->who = (char *) MyMalloc(strlen(cptr->name) +
+                                     strlen(cptr->user->username) +
+                                     strlen(cptr->user->host) + 3);
+        (void) ircsprintf(ban->who, "%s!%s@%s",
+                          cptr->name, cptr->user->username, cptr->user->host);
+    }
+    else
+    {
+        ban->who = (char *) MyMalloc(strlen(cptr->name) + 1);
+        (void) strcpy(ban->who, cptr->name);
+    }
+    
+    /* determine what 'type' of mask this is, for less matching later */
+    
+    if(banid[0] == '*' && banid[1] == '!')
+    {
+        if(banid[2] == '*' && banid[3] == '@')
+            ban->type = MTYP_HOST;
+        else
+            ban->type = MTYP_USERHOST;
+    }
+    else
+        ban->type = MTYP_FULL;
+
+    ban->when = timeofday;
+    chptr->banlist = ban;
+    chptr->banserial++;
+    
+    return 0;
+}
+
+/*
+ * del_banid - delete an id belonging to cptr if banid is null,
+ * deleteall banids belonging to cptr.
+ */
+static int del_banid(aChannel *chptr, char *banid)
+{
+   aBan        **ban;
+   aBan         *tmp;
+
+   if (!banid)
+       return -1;
+   for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next))
+       if (mycmp(banid, (*ban)->banstr) == 0)
+       {
+           tmp = *ban;
+           *ban = tmp->next;
+
+           chptr->banserial++;
+
+           MyFree(tmp->banstr);
+           MyFree(tmp->who);
+           MyFree(tmp);
+           
+           break;
+       }
+   return 0;
+}
+
+/*
+ * is_banned - returns CHFL_BANNED if banned else 0
+ * 
+ * caches banned status in chanMember for can_send()
+ *   -Quension [Jun 2004]
+ */
+
+static int is_banned(aClient *cptr, aChannel *chptr, chanMember *cm)
+{
+    aBan       *ban;
+#ifdef EXEMPT_LISTS
+    aBanExempt *exempt;
+#endif
+    char        s[NICKLEN + USERLEN + HOSTLEN + 6];
+    char       *s2;
+    
+    if (!IsPerson(cptr))
+        return 0;
+
+    /* if cache is valid, use it */
+    if (cm)
+    {
+        if (cm->banserial == chptr->banserial)
+            return (cm->flags & CHFL_BANNED);
+        cm->banserial = chptr->banserial;
+        cm->flags &= ~CHFL_BANNED;
+    }
+
+    strcpy(s, make_nick_user_host(cptr->name, cptr->user->username,
+                                  cptr->user->host));
+    s2 = make_nick_user_host(cptr->name, cptr->user->username,
+                             cptr->hostip);
+
+#ifdef EXEMPT_LISTS
+    for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
+        if (!match(exempt->banstr, s) || !match(exempt->banstr, s2))
+            return 0;
+#endif
+
+    for (ban = chptr->banlist; ban; ban = ban->next)
+        if ((match(ban->banstr, s) == 0) ||
+            (match(ban->banstr, s2) == 0))
+            break;
+
+    if (ban)
+    {
+        if (cm)
+            cm->flags |= CHFL_BANNED;
+        return CHFL_BANNED;
+    }
+
+    return 0;
+}
+
+aBan *nick_is_banned(aChannel *chptr, char *nick, aClient *cptr)
+{
+    aBan *ban;
+#ifdef EXEMPT_LISTS
+    aBanExempt *exempt;
+#endif
+    char *s, s2[NICKLEN+USERLEN+HOSTLEN+6];
+    
+    if (!IsPerson(cptr)) return NULL;
+    
+    strcpy(s2, make_nick_user_host(nick, cptr->user->username,
+                                   cptr->user->host));
+    s = make_nick_user_host(nick, cptr->user->username, cptr->hostip);
+
+#ifdef EXEMPT_LISTS
+    for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
+        if (exempt->type == MTYP_FULL &&
+            ((match(exempt->banstr, s2) == 0) ||
+             (match(exempt->banstr, s) == 0)))
+            return NULL;
+#endif
+
+    for (ban = chptr->banlist; ban; ban = ban->next)
+        if (ban->type == MTYP_FULL &&        /* only check applicable bans */
+            ((match(ban->banstr, s2) == 0) ||    /* check host before IP */
+             (match(ban->banstr, s) == 0)))
+            break;
+    return (ban);
+}
+
+void remove_matching_bans(aChannel *chptr, aClient *cptr, aClient *from) 
+{
+    aBan *ban, *bnext;
+    char targhost[NICKLEN+USERLEN+HOSTLEN+6];
+    char targip[NICKLEN+USERLEN+HOSTLEN+6];
+    char *m;
+    int count = 0, send = 0;
+    
+    if (!IsPerson(cptr)) return;
+    
+    strcpy(targhost, make_nick_user_host(cptr->name, cptr->user->username,
+                                         cptr->user->host));
+  strcpy(targip, make_nick_user_host(cptr->name, cptr->user->username,
+                                     cptr->hostip));
+  
+  m = modebuf;  
+  *m++ = '-';
+  *m = '\0'; 
+  
+  *parabuf = '\0';
+  
+  ban = chptr->banlist;
+  
+  while(ban)
+  {
+      bnext = ban->next;
+      if((match(ban->banstr, targhost) == 0) ||
+         (match(ban->banstr, targip) == 0))
+      {
+          if (strlen(parabuf) + strlen(ban->banstr) + 10 < (size_t) MODEBUFLEN)
+          {
+              if(*parabuf)
+                  strcat(parabuf, " ");
+              strcat(parabuf, ban->banstr);
+              count++;
+              *m++ = 'b';
+              *m = '\0';
+          }
+          else 
+              if(*parabuf)
+                  send = 1;
+          
+          if(count == MAXTSMODEPARAMS)
+              send = 1;
+          
+          if(send)
+          {
+              sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", 
+                                        from->name, chptr->chname, modebuf,
+                                        parabuf);
+              sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
+                                 chptr->chname, chptr->channelts, modebuf,
+                                 parabuf);
+              send = 0;
+              *parabuf = '\0';
+              m = modebuf;
+              *m++ = '-';
+              if(count != MAXTSMODEPARAMS)
+              {
+                  strcpy(parabuf, ban->banstr);
+                  *m++ = 'b';
+                  count = 1;
+              }
+              else
+                  count = 0;
+              *m = '\0';
+          }
+          
+          del_banid(chptr, ban->banstr);
+      }
+      ban = bnext;
+  }
+  
+  if(*parabuf)
+  {
+      sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name,
+                                chptr->chname, modebuf, parabuf);
+      sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
+                         chptr->chname, chptr->channelts, modebuf, parabuf);
+  }
+  
+  return;
+}
+
+#ifdef EXEMPT_LISTS
+void remove_matching_exempts(aChannel *chptr, aClient *cptr, aClient *from)
+{
+    aBanExempt *ex, *enext;
+    char targhost[NICKLEN+USERLEN+HOSTLEN+6];
+    char targip[NICKLEN+USERLEN+HOSTLEN+6];
+    char *m;
+    int count = 0, send = 0;
+
+    if (!IsPerson(cptr)) return;
+
+    strcpy(targhost, make_nick_user_host(cptr->name, cptr->user->username,
+                                         cptr->user->host));
+    strcpy(targip, make_nick_user_host(cptr->name, cptr->user->username,
+                                       cptr->hostip));
+
+    m = modebuf;
+    *m++ = '-';
+    *m = '\0';
+
+    *parabuf = '\0';
+
+    ex = chptr->banexempt_list;
+
+    while(ex)
+    {
+        enext = ex->next;
+        if((match(ex->banstr, targhost) == 0) ||
+           (match(ex->banstr, targip) == 0))
+        {
+            if (strlen(parabuf) + strlen(ex->banstr) + 10 < (size_t) MODEBUFLEN)
+            {
+                if(*parabuf)
+                    strcat(parabuf, " ");
+                strcat(parabuf, ex->banstr);
+                count++;
+                *m++ = 'e';
+                *m = '\0';
+            }
+            else
+                if(*parabuf)
+                    send = 1;
+
+            if(count == MAXTSMODEPARAMS)
+                send = 1;
+
+            if(send)
+            {
+                sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s",
+                                          from->name, chptr->chname, modebuf,
+                                          parabuf);
+                sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
+                                   chptr->chname, chptr->channelts, modebuf,
+                                   parabuf);
+                send = 0;
+                *parabuf = '\0';
+                m = modebuf;
+                *m++ = '-';
+                if(count != MAXTSMODEPARAMS)
+                {
+                    strcpy(parabuf, ex->banstr);
+                    *m++ = 'e';
+                    count = 1;
+                }
+                else
+                    count = 0;
+                *m = '\0';
+            }
+
+            del_exempt_id(chptr, ex->banstr);
+        }
+        ex = enext;
+    }
+
+    if(*parabuf)
+    {
+        sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name,
+                                  chptr->chname, modebuf, parabuf);
+        sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
+                           chptr->chname, chptr->channelts, modebuf, parabuf);
+    }
+
+    return;
+}
+#endif
+
+#ifdef INVITE_LISTS
+void remove_matching_invites(aChannel *chptr, aClient *cptr, aClient *from)
+{
+    anInvite *inv, *inext;
+    char targhost[NICKLEN+USERLEN+HOSTLEN+6];
+    char targip[NICKLEN+USERLEN+HOSTLEN+6];
+    char *m;
+    int count = 0, send = 0;
+
+    if (!IsPerson(cptr)) return;
+
+    strcpy(targhost, make_nick_user_host(cptr->name, cptr->user->username,
+                                         cptr->user->host));
+    strcpy(targip, make_nick_user_host(cptr->name, cptr->user->username,
+                                       cptr->hostip));
+
+    m = modebuf;
+    *m++ = '-';
+    *m = '\0';
+
+    *parabuf = '\0';
+
+    inv = chptr->invite_list;
+
+    while(inv)
+    {
+        inext = inv->next;
+        if((match(inv->invstr, targhost) == 0) ||
+           (match(inv->invstr, targip) == 0))
+        {
+            if (strlen(parabuf) + strlen(inv->invstr) + 10 < (size_t) MODEBUFLEN)
+            {
+                if(*parabuf)
+                    strcat(parabuf, " ");
+                strcat(parabuf, inv->invstr);
+                count++;
+                *m++ = 'I';
+                *m = '\0';
+            }
+            else
+                if(*parabuf)
+                    send = 1;
+
+            if(count == MAXTSMODEPARAMS)
+                send = 1;
+
+            if(send)
+            {
+                sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s",
+                                          from->name, chptr->chname, modebuf,
+                                          parabuf);
+                sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
+                                   chptr->chname, chptr->channelts, modebuf,
+                                   parabuf);
+                send = 0;
+                *parabuf = '\0';
+                m = modebuf;
+                *m++ = '-';
+                if(count != MAXTSMODEPARAMS)
+                {
+                    strcpy(parabuf, inv->invstr);
+                    *m++ = 'I';
+                    count = 1;
+                }
+                else
+                    count = 0;
+                *m = '\0';
+            }
+
+            del_invite_id(chptr, inv->invstr);
+        }
+        inv = inext;
+    }
+
+    if(*parabuf)
+    {
+        sendto_channel_butserv_me(chptr, from, ":%s MODE %s %s %s", from->name,
+                                  chptr->chname, modebuf, parabuf);
+        sendto_serv_butone(from, ":%s MODE %s %ld %s %s", from->name,
+                           chptr->chname, chptr->channelts, modebuf, parabuf);
+    }
+
+    return;
+}
+#endif
+
+int check_joinrate(aChannel *chptr, time_t ts, int local, aClient *cptr)
+{
+    int join_num = DEFAULT_JOIN_NUM;
+    int join_time = DEFAULT_JOIN_TIME;
+
+    if(!local && (NOW - ts) > 60)
+        return 1; /* attempt to compensate for lag */
+
+    /* This first section checks the defaults, the second
+     * checks the channels +j settings */
+    /* Has the join_time period elapsed? */
+    if((NOW - chptr->default_join_start) > join_time)
+    {
+        chptr->default_join_start = NOW;
+        chptr->default_join_count = 0;
+    }
+    /* If it's local and we've filled the join count, complain
+     * to ops so they can take the appropriate measures */
+    if(local && chptr->default_join_count >= join_num)
+        sendto_realops_lev(DEBUG_LEV, "Join rate warning on %s"
+                                      " for %s!%s@%s (%d/%d in %d) (No action taken)",
+                           chptr->chname, cptr->name, cptr->user->username,
+                           cptr->user->host, chptr->default_join_count, join_num,
+                           NOW - chptr->default_join_start);
+    /* update the count here for attempts, in case a lower throttle blocks */
+    chptr->default_join_count++;
+
+    /* Has the channel set their own custom settings? */
+    if(chptr->mode.mode & MODE_JOINRATE)
+    {
+        if(chptr->mode.join_num == 0 || chptr->mode.join_time == 0)
+            return 1; /* channel has turned this off */
+
+        join_time = chptr->mode.join_time;
+        join_num = chptr->mode.join_num;
+    }
+    /* Has the join_time period elapsed? */
+    if((NOW - chptr->join_start) > join_time)
+    {
+        chptr->join_start = NOW;
+        chptr->join_count = 0;
+        return 1;
+    }
+
+    /* If it's local and we've filled the join count, say no */
+    if(local && chptr->join_count >= join_num)
+    {
+        sendto_realops_lev(DEBUG_LEV, "Join rate throttling on %s"
+                                      " for %s!%s@%s (%d/%d in %d)",
+                           chptr->chname, cptr->name, cptr->user->username, 
+                           cptr->user->host, chptr->join_count, join_num, 
+                           NOW - chptr->join_start);
+        return 0;
+    }
+    return 1;
+}
+
+/*
+ * adds a user to a channel by adding another link to the channels
+ * member chain.
+ */
+static void add_user_to_channel(aChannel *chptr, aClient *who, int flags)
+{
+    Link   *ptr;
+    chanMember *cm;
+    
+#ifdef DUMP_DEBUG
+    fprintf(dumpfp,"Add to channel %s: %p:%s\n",chptr->chname,who,who->name);
+#endif
+    
+    if (who->user)
+    {
+        cm = make_chanmember();
+        cm->flags = flags;
+        cm->cptr = who;
+        cm->next = chptr->members;
+        cm->banserial = chptr->banserial;
+
+        chptr->members = cm;
+        chptr->users++;
+        
+        ptr = make_link();
+        ptr->value.chptr = chptr;
+        ptr->next = who->user->channel;
+        who->user->channel = ptr;
+        who->user->joined++;
+    }
+}
+
+void remove_user_from_channel(aClient *sptr, aChannel *chptr)
+{
+    chanMember  **curr, *tmp;
+    Link           **lcurr, *ltmp;
+    
+    for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next)
+        if (tmp->cptr == sptr)
+        {
+            *curr = tmp->next;
+            free_chanmember(tmp);
+            break;
+        }
+
+    for (lcurr = &sptr->user->channel; (ltmp = *lcurr); lcurr = &ltmp->next)
+        if (ltmp->value.chptr == chptr)
+        {
+            *lcurr = ltmp->next;
+            free_link(ltmp);
+            break;
+        }
+    sptr->user->joined--;
+    sub1_from_channel(chptr);
+}
+
+int is_chan_op(aClient *cptr, aChannel *chptr)
+{
+    chanMember   *cm;
+    
+    if (chptr)
+        if ((cm = find_user_member(chptr->members, cptr)))
+            return (cm->flags & CHFL_CHANOP);
+    
+    return 0;
+}
+
+int is_deopped(aClient *cptr, aChannel *chptr)
+{
+    chanMember   *cm;
+    
+    if (chptr)
+        if ((cm = find_user_member(chptr->members, cptr)))
+            return (cm->flags & CHFL_DEOPPED);
+    
+    return 0;
+}
+
+int has_voice(aClient *cptr, aChannel *chptr)
+{
+    chanMember   *cm;
+    
+    if (chptr)
+        if ((cm = find_user_member(chptr->members, cptr)))
+            return (cm->flags & CHFL_VOICE);
+    
+    return 0;
+}
+
+int can_send(aClient *cptr, aChannel *chptr, char *msg)
+{
+    chanMember   *cm;
+    
+    if (IsServer(cptr) || IsULine(cptr))
+        return 0;
+    
+    cm = find_user_member(chptr->members, cptr);
+    
+    if(!cm)
+    {
+        if (chptr->mode.mode & MODE_MODERATED)
+            return (MODE_MODERATED);
+        if(chptr->mode.mode & MODE_NOPRIVMSGS)
+            return (MODE_NOPRIVMSGS);
+        if ((chptr->mode.mode & MODE_MODREG) && !IsRegNick(cptr))
+            return (ERR_NEEDREGGEDNICK);
+        if ((chptr->mode.mode & MODE_NOCOLOR) && msg_has_colors(msg))
+            return (ERR_NOCOLORSONCHAN);
+        if (MyClient(cptr) && is_banned(cptr, chptr, NULL))
+            return (MODE_BAN); /*
+                                * channel is -n and user is not there;
+                                * we need to bquiet them if we can
+                                */
+    }
+    else
+    {
+        /* ops and voices can talk through everything except NOCOLOR */
+        if (!(cm->flags & (CHFL_CHANOP | CHFL_VOICE)))
+        {
+            if (chptr->mode.mode & MODE_MODERATED)
+                return (MODE_MODERATED);
+            if (is_banned(cptr, chptr, cm))
+                return (MODE_BAN);
+            if ((chptr->mode.mode & MODE_MODREG) && !IsRegNick(cptr))
+                return (ERR_NEEDREGGEDNICK);
+        }
+        if ((chptr->mode.mode & MODE_NOCOLOR) && msg_has_colors(msg))
+            return (ERR_NOCOLORSONCHAN);
+    }
+    
+    return 0;
+}
+
+/*
+ * write the "simple" list of channel modes for channel chptr onto
+ * buffer mbuf with the parameters in pbuf.
+ */
+static void channel_modes(aClient *cptr, char *mbuf, char *pbuf,
+                          aChannel *chptr)
+{
+    pbuf[0] = '\0';
+    *mbuf++ = '+';
+    if (chptr->mode.mode & MODE_SECRET)
+        *mbuf++ = 's';
+    else if (chptr->mode.mode & MODE_PRIVATE)
+        *mbuf++ = 'p';
+    if (chptr->mode.mode & MODE_MODERATED)
+        *mbuf++ = 'm';
+    if (chptr->mode.mode & MODE_TOPICLIMIT)
+        *mbuf++ = 't';
+    if (chptr->mode.mode & MODE_INVITEONLY)
+        *mbuf++ = 'i';
+    if (chptr->mode.mode & MODE_NOPRIVMSGS)
+        *mbuf++ = 'n';
+    if (chptr->mode.mode & MODE_REGISTERED)
+        *mbuf++ = 'r';
+    if (chptr->mode.mode & MODE_REGONLY)
+        *mbuf++ = 'R';
+    if (chptr->mode.mode & MODE_NOCOLOR)
+        *mbuf++ = 'c';
+    if (chptr->mode.mode & MODE_OPERONLY)
+        *mbuf++ = 'O';
+    if (chptr->mode.mode & MODE_MODREG)
+        *mbuf++ = 'M';
+#ifdef USE_CHANMODE_L
+    if (chptr->mode.mode & MODE_LISTED)
+        *mbuf++ = 'L';
+#endif
+    if (chptr->mode.limit) 
+    {
+        *mbuf++ = 'l';
+        if (IsMember(cptr, chptr) || IsServer(cptr) || IsULine(cptr))
+            ircsprintf(pbuf, "%d", chptr->mode.limit);      
+    }
+    if (*chptr->mode.key)
+    {
+        *mbuf++ = 'k';
+        if (IsMember(cptr, chptr) || IsServer(cptr) || IsULine(cptr))
+        {
+            if(pbuf[0] != '\0')
+                strcat(pbuf, " ");
+            strcat(pbuf, chptr->mode.key);
+        }
+    }
+    if (chptr->mode.mode & MODE_JOINRATE)
+    {
+        *mbuf++ = 'j';
+
+        if (IsMember(cptr, chptr) || IsServer(cptr) || IsULine(cptr))
+        {
+            char tmp[128];
+            if(pbuf[0] != '\0')
+                strcat(pbuf, " ");
+
+            if(chptr->mode.join_num == 0 || chptr->mode.join_time == 0)
+                ircsprintf(tmp, "0");
+            else
+                ircsprintf(tmp, "%d:%d", chptr->mode.join_num, 
+                            chptr->mode.join_time);
+
+            strcat(pbuf, tmp);
+        }
+    }
+    *mbuf++ = '\0';
+    return;
+}
+
+static void send_channel_lists(aClient *cptr, aChannel *chptr)
+{
+    aBan   *bp;
+#ifdef EXEMPT_LISTS
+    aBanExempt *exempt;
+#endif
+#ifdef INVITE_LISTS
+    anInvite *inv;
+#endif            
+    char   *cp;
+    int         count = 0, send = 0;
+
+    cp = modebuf + strlen(modebuf);
+
+    if (*parabuf) /* mode +l or +k xx */
+        count = 1;
+
+    for (bp = chptr->banlist; bp; bp = bp->next) 
+    {
+        if (strlen(parabuf) + strlen(bp->banstr) + 20 < (size_t) MODEBUFLEN) 
+        {
+            if(*parabuf)
+                strcat(parabuf, " ");
+            strcat(parabuf, bp->banstr);
+            count++;
+            *cp++ = 'b';
+            *cp = '\0';
+        }
+        else if (*parabuf)
+            send = 1;
+
+        if (count == MAXTSMODEPARAMS)
+            send = 1;
+
+        if (send) 
+        {
+            sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname,
+                           chptr->channelts, modebuf, parabuf);
+            send = 0;
+            *parabuf = '\0';
+            cp = modebuf;
+            *cp++ = '+';
+            if (count != MAXTSMODEPARAMS) 
+            {
+                strcpy(parabuf, bp->banstr);
+                *cp++ = 'b';
+                count = 1;
+            }
+            else
+                count = 0;
+            *cp = '\0';
+        }
+    }
+#ifdef EXEMPT_LISTS
+    for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
+    {
+        if (strlen(parabuf) + strlen(exempt->banstr) + 20 < (size_t)MODEBUFLEN)
+        {
+            if (*parabuf) strcat(parabuf, " ");
+            strcat(parabuf, exempt->banstr);
+            count++;
+            *cp++ = 'e';
+            *cp = 0;
+        }
+        else if (*parabuf)
+            send = 1;
+        
+        
+        if (count == MAXTSMODEPARAMS)
+            send = 1;
+        
+        if (send)
+        {
+            sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname,
+                           chptr->channelts, modebuf, parabuf);
+            send = 0;
+            *parabuf = 0;
+            cp = modebuf;
+            *cp++ = '+';
+            if (count != MAXTSMODEPARAMS)
+            {
+                strcpy(parabuf, exempt->banstr);
+                *cp++ = 'e';
+                count = 1;
+            }
+            else count = 0;
+            *cp = 0;
+        }
+    }
+#endif    
+#ifdef INVITE_LISTS
+    for (inv = chptr->invite_list; inv; inv = inv->next)
+    {
+        if (strlen(parabuf) + strlen(inv->invstr) + 20 < (size_t)MODEBUFLEN)
+        {
+            if (*parabuf) strcat(parabuf, " ");
+            strcat(parabuf, inv->invstr);
+            count++;
+            *cp++ = 'I';
+            *cp = 0;
+        }
+        else if (*parabuf)
+            send = 1;
+        
+        
+        if (count == MAXTSMODEPARAMS)
+            send = 1;
+        
+        if (send)
+        {
+            sendto_one(cptr, ":%s MODE %s %ld %s %s", me.name, chptr->chname,
+                           chptr->channelts, modebuf, parabuf);
+            send = 0;
+            *parabuf = 0;
+            cp = modebuf;
+            *cp++ = '+';
+            if (count != MAXTSMODEPARAMS)
+            {
+                strcpy(parabuf, inv->invstr);
+                *cp++ = 'I';
+                count = 1;
+            }
+            else count = 0;
+            *cp = 0;
+        }
+    }
+#endif    
+    
+}
+
+/* send "cptr" a full list of the modes for channel chptr. */
+void send_channel_modes(aClient *cptr, aChannel *chptr)
+{
+    chanMember       *l, *anop = NULL, *skip = NULL;
+    int         n = 0;
+    char       *t;
+
+    if (*chptr->chname != '#')
+        return;
+
+    *modebuf = *parabuf = '\0';
+    channel_modes(cptr, modebuf, parabuf, chptr);
+
+    ircsprintf(buf, ":%s SJOIN %ld %s %s %s :", me.name,
+               chptr->channelts, chptr->chname, modebuf, parabuf);
+    t = buf + strlen(buf);
+    for (l = chptr->members; l; l = l->next)
+        if (l->flags & MODE_CHANOP)
+        {
+            anop = l;
+            break;
+        }
+    /*
+     * follow the channel, but doing anop first if it's defined *
+     * -orabidoo
+     */
+    l = NULL;
+    for (;;)
+    {
+        if (anop)
+        {
+            l = skip = anop;
+            anop = NULL;
+        }
+        else
+        {
+            if (l == NULL || l == skip)
+                l = chptr->members;
+            else
+                l = l->next;
+            if (l && l == skip)
+                l = l->next;
+            if (l == NULL)
+                break;
+        }
+        if (l->flags & MODE_CHANOP)
+            *t++ = '@';
+        if (l->flags & MODE_VOICE)
+            *t++ = '+';
+        strcpy(t, l->cptr->name);
+        t += strlen(t);
+        *t++ = ' ';
+        n++;
+        if (t - buf > BUFSIZE - 80)
+        {
+            *t++ = '\0';
+            if (t[-1] == ' ')
+                t[-1] = '\0';
+            sendto_one(cptr, "%s", buf);
+            sprintf(buf, ":%s SJOIN %ld %s 0 :", me.name,
+                    chptr->channelts, chptr->chname);
+            t = buf + strlen(buf);
+            n = 0;
+        }
+    }
+
+    if (n)
+    {
+        *t++ = '\0';
+        if (t[-1] == ' ')
+            t[-1] = '\0';
+        sendto_one(cptr, "%s", buf);
+    }
+    *parabuf = '\0';
+    *modebuf = '+';
+    modebuf[1] = '\0';
+    send_channel_lists(cptr, chptr);
+    if (modebuf[1] || *parabuf)
+        sendto_one(cptr, ":%s MODE %s %ld %s %s",
+                me.name, chptr->chname, chptr->channelts, modebuf, parabuf);
+}
+
+/* m_mode parv[0] - sender parv[1] - channel */
+
+int m_mode(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int         mcount = 0, chanop=0;
+    aChannel   *chptr;
+    int subparc = 2;
+    
+    /* Now, try to find the channel in question */
+    if (parc > 1)
+    {
+        chptr = find_channel(parv[1], NullChn);
+        if (chptr == NullChn)
+            return m_umode(cptr, sptr, parc, parv);
+    }
+    else
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "MODE");
+        return 0;
+    }
+    
+    if (!check_channelname(sptr, (unsigned char *) parv[1]))
+        return 0;
+
+    if (MyClient(sptr))
+    {
+        if (is_chan_op(sptr, chptr))
+            chanop = 1;
+    }
+    else
+        chanop = 2;
+        
+    if (parc < 3)
+    {
+        *modebuf = *parabuf = '\0';
+        modebuf[1] = '\0';
+        channel_modes(sptr, modebuf, parabuf, chptr);
+        sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS), me.name, parv[0],
+                   chptr->chname, modebuf, parabuf);
+        sendto_one(sptr, rpl_str(RPL_CREATIONTIME), me.name, parv[0],
+                   chptr->chname, chptr->channelts);
+        return 0;
+    }
+
+    if(IsServer(cptr) && IsDigit(parv[2][0]))
+    {
+        ts_val modets = atol(parv[2]);
+        if(modets != 0 && (modets > chptr->channelts))
+            return 0;
+        subparc++;
+    }
+
+    mcount = set_mode(cptr, sptr, chptr, chanop, parc - subparc, parv + subparc,
+                      modebuf, parabuf);
+
+    if (strlen(modebuf) > (size_t) 1)
+        switch (mcount)
+        {
+            case 0:
+                break;
+            case -1:
+                if (MyClient(sptr))
+                    sendto_one(sptr,
+                           err_str(ERR_CHANOPRIVSNEEDED),
+                           me.name, parv[0], chptr->chname);
+                else
+                    ircstp->is_fake++;
+                break;
+            default:
+                sendto_channel_butserv_me(chptr, sptr,
+                                      ":%s MODE %s %s %s", parv[0],
+                                      chptr->chname, modebuf,
+                                      parabuf);
+                sendto_serv_butone(cptr, ":%s MODE %s %ld %s %s", parv[0],
+                                   chptr->chname, chptr->channelts, modebuf,
+                                   parabuf);
+        }
+    return 0;
+}
+
+/* the old set_mode was pissing me off with it's disgusting
+ * hackery, so I rewrote it.  Hope this works. }:> --wd
+ * Corrected a 4-year-old mistake: the max modes limit applies to
+ * the number of parameters, not mode changes. -Quension [Apr 2004]
+ */
+static int set_mode(aClient *cptr, aClient *sptr, aChannel *chptr,
+                    int level, int parc, char *parv[], char *mbuf, char *pbuf) 
+{
+#define SM_ERR_NOPRIVS 0x0001 /* is not an op */
+#define SM_ERR_MOREPARMS 0x0002 /* needs more parameters */     
+#define SM_ERR_RESTRICTED 0x0004 /* not allowed to op others or be op'd */      
+#define SM_ERR_NOTOPER    0x0008 /* not an irc op */
+#define SM_MAXMODES MAXMODEPARAMSUSER
+
+/* this macro appends to pbuf */
+#define ADD_PARA(p) pptr = p; if(pidx) pbuf[pidx++] = ' '; while(*pptr) \
+                    pbuf[pidx++] = *pptr++;
+    
+    static int flags[] = 
+    {
+        MODE_PRIVATE, 'p', MODE_SECRET, 's',
+        MODE_MODERATED, 'm', MODE_NOPRIVMSGS, 'n',
+        MODE_TOPICLIMIT, 't', MODE_REGONLY, 'R',
+        MODE_INVITEONLY, 'i', MODE_NOCOLOR, 'c', MODE_OPERONLY, 'O',
+        MODE_MODREG, 'M', 
+#ifdef USE_CHANMODE_L
+        MODE_LISTED, 'L',
+#endif
+        0x0, 0x0
+    };
+    
+    Link *lp; /* for walking lists */
+    chanMember *cm; /* for walking channel member lists */
+    aBan *bp; /* for walking banlists */
+    char *modes=parv[0]; /* user's idea of mode changes */
+    int args; /* counter for what argument we're on */
+    int anylistsent = IsServer(sptr) ? 1 : 0; /* Only send 1 list and not to servers */
+    char change='+'; /* by default we + things... */
+    int errors=0; /*
+                   * errors returned, set with bitflags
+                   * so we only return them once
+                   */
+    /* from remote servers, ungodly numbers of modes can be sent, but
+     * from local users only SM_MAXMODES are allowed */
+    int maxparams=((IsServer(sptr) || IsULine(sptr)) ? 512 : SM_MAXMODES);
+    int nmodes=0; /* how many modes we've set so far */
+    int nparams=0; /* how many modes with parameters we've set so far */
+    aClient *who = NULL; /* who we're doing a mode for */
+    int chasing = 0;
+    int i=0;
+    char moreparmsstr[]="MODE   ";
+    char nuhbuf[NICKLEN + USERLEN + HOSTLEN + 6]; /* for bans */
+    char tmp[128]; /* temporary buffer */
+    int pidx = 0; /* index into pbuf */
+    char *pptr; /* temporary paramater pointer */
+    char *morig = mbuf; /* beginning of mbuf */
+    /* :cptr-name MODE chptr->chname [MBUF] [PBUF] (buflen - 3 max and NULL) */
+    /* added another 11 bytes to this, for TSMODE -epi */
+    int prelen = strlen(cptr->name) + strlen(chptr->chname) + 27;
+    /* drop duplicates in the same mode change -- yeah, this is cheap, but real
+       duplicate checking will have to wait for a protocol change to kill
+       desyncs */
+    int seenalready = 0;
+
+
+    args=1;
+        
+    if(parc<1)
+        return 0;
+
+    *mbuf++='+'; /* add the plus, even if they don't */
+    /* go through once to clean the user's mode string so we can
+     * have a simple parser run through it...*/
+
+    while(*modes) 
+    {
+        switch(*modes) 
+        {
+        case '+':
+            if(*(mbuf-1)=='-') 
+            {
+                *(mbuf-1)='+'; /* change it around now */
+                change='+';
+                break;
+            }
+            else if(change=='+') /* we're still doing a +, we don't care */
+                break;
+            change=*modes;
+            *mbuf++='+';
+            break;
+
+        case '-':
+            if(*(mbuf-1)=='+') 
+            {
+                *(mbuf-1)='-'; /* change it around now */
+                change='-';
+                break;
+            }
+            else if(change=='-')
+                break; /* we're still doing a -, we don't care */
+            change=*modes;
+            *mbuf++='-';
+            break;
+
+        case 'O':
+            if (level<1 || (MyClient(sptr) && !IsOper(sptr)))
+            {
+                errors |= SM_ERR_NOTOPER;
+                break;
+            } 
+            else 
+            {
+                if (change=='+')
+                    chptr->mode.mode|=MODE_OPERONLY;
+                else
+                    chptr->mode.mode&=~MODE_OPERONLY;
+                *mbuf++ = *modes;
+                nmodes++;
+            }
+            break;
+        case 'o':
+        case 'v':
+            if(level<1) 
+            {
+                errors |= SM_ERR_NOPRIVS;
+                break;
+            }
+            if(parv[args]==NULL)
+            {
+                /* silently drop the spare +o/v's */
+                break;
+            }
+            if(++nparams > maxparams)
+            {
+                /* too many modes with params, eat this one */
+                args++;
+                break;
+            }
+                        
+            who = find_chasing(sptr, parv[args], &chasing);
+            cm = find_user_member(chptr->members, who);
+            if(cm == NULL) 
+            {
+                sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
+                           me.name, cptr->name, parv[args], chptr->chname);
+                /* swallow the arg */
+                args++;
+                break;
+            }
+            /* if we're going to overflow our mode buffer,
+             * drop the change instead */
+            if((prelen + (mbuf - morig) + pidx + NICKLEN + 1) > 
+               REALMODEBUFLEN) 
+            {
+                args++;
+                break;
+            }
+            
+            /* if we have the user, set them +/-[vo] */
+            if(change=='+')
+                cm->flags|=(*modes=='o' ? CHFL_CHANOP : CHFL_VOICE);
+            else
+                cm->flags&=~((*modes=='o' ? CHFL_CHANOP : CHFL_VOICE));
+            
+            /* we've decided their mode was okay, cool */
+            *mbuf++ = *modes;
+            ADD_PARA(cm->cptr->name)
+                args++;
+            nmodes++;
+            if (IsServer(sptr) && *modes == 'o' && change=='+') 
+            {
+                chptr->channelts = 0;
+                sendto_ops("Server %s setting +o and blasting TS on %s",
+                           sptr->name, chptr->chname);
+            }
+            break;
+
+#ifdef INVITE_LISTS
+        case 'I':
+            if (level < 1 && parv[args] != NULL)
+            {
+                errors |= SM_ERR_NOPRIVS;
+                break;
+            }
+            else if (parv[args] == NULL)
+            {
+                anInvite    *invite;
+
+                if (anylistsent) /* don't send the list if they have received one */
+                    break;
+
+                for (invite = chptr->invite_list; invite; invite = invite->next)
+                    sendto_one(sptr, rpl_str(RPL_INVITELIST), me.name, cptr->name,
+                               chptr->chname, invite->invstr, invite->who, invite->when);
+                sendto_one(cptr, rpl_str(RPL_ENDOFINVITELIST), me.name,
+                           cptr->name, chptr->chname);
+                anylistsent = 1;
+                break;
+            }
+            if(++nparams > maxparams)
+            {
+                /* too many modes with params, eat this one */
+                args++;
+                break;
+            }
+            
+            if (*parv[args] == ':' || *parv[args] == '\0')
+            {
+                args++; 
+                break;
+            }
+
+            strcpy(nuhbuf, collapse(pretty_mask(parv[args])));
+            parv[args] = nuhbuf;
+            /* if we're going to overflow our mode buffer,
+             * drop the change instead */
+            if((prelen + (mbuf - morig) + pidx + strlen(nuhbuf) + 1) > 
+               REALMODEBUFLEN) 
+            {
+                args++;
+                break;
+            }
+            /* if we can't add or delete (depending) the ban, change is
+             * worthless anyhow */
+            
+            if(!(change=='+' && !add_invite_id(sptr, chptr, parv[args])) && 
+               !(change=='-' && !del_invite_id(chptr, parv[args])))
+            {
+                args++;
+                break;
+            }
+            
+            *mbuf++ = 'I';
+            ADD_PARA(parv[args])
+                args++;
+            nmodes++;
+            break;
+#endif
+
+#ifdef EXEMPT_LISTS
+        case 'e':
+            if (level < 1 && parv[args] != NULL)
+            {
+                errors |= SM_ERR_NOPRIVS;
+                break;
+            }
+            else if (parv[args] == NULL)
+            {
+                aBanExempt*    exempt;
+                
+                if (anylistsent) /* don't send the list if they have received one */
+                    break;
+                for (exempt = chptr->banexempt_list; exempt; exempt = exempt->next)
+                    sendto_one(sptr, rpl_str(RPL_EXEMPTLIST), me.name, cptr->name,
+                               chptr->chname, exempt->banstr, exempt->who, exempt->when);
+                sendto_one(cptr, rpl_str(RPL_ENDOFEXEMPTLIST), me.name,
+                           cptr->name, chptr->chname);
+                anylistsent = 1;
+                break;
+            }
+            if(++nparams > maxparams)
+            {
+                /* too many modes with params, eat this one */
+                args++;
+                break;
+            }
+            
+            if (*parv[args] == ':' || *parv[args] == '\0')
+            {
+                args++; 
+                break;
+            }
+
+            strcpy(nuhbuf, collapse(pretty_mask(parv[args])));
+            parv[args] = nuhbuf;
+            /* if we're going to overflow our mode buffer,
+             * drop the change instead */
+            if((prelen + (mbuf - morig) + pidx + strlen(nuhbuf) + 1) > 
+               REALMODEBUFLEN) 
+            {
+                args++;
+                break;
+            }
+            /* if we can't add or delete (depending) the exempt, change is
+             * worthless anyhow */
+            
+            if(!(change=='+' && !add_exempt_id(sptr, chptr, parv[args])) && 
+               !(change=='-' && !del_exempt_id(chptr, parv[args])))
+            {
+                args++;
+                break;
+            }
+
+            *mbuf++ = 'e';
+            ADD_PARA(parv[args])
+                args++;
+            nmodes++;
+            break;
+#endif
+    
+        case 'b':
+            /* if the user has no more arguments, then they just want
+             * to see the bans, okay, cool. */
+            if(level < 1 && parv[args] != NULL)
+            {
+                errors |= SM_ERR_NOPRIVS;
+                break;
+            }
+            /* show them the bans, woowoo */
+            if(parv[args]==NULL)
+            {
+                if (anylistsent)
+                    break;
+                for(bp=chptr->banlist;bp;bp=bp->next)
+                    sendto_one(sptr, rpl_str(RPL_BANLIST), me.name, cptr->name,
+                               chptr->chname, bp->banstr, bp->who, bp->when);
+                sendto_one(cptr, rpl_str(RPL_ENDOFBANLIST), me.name,
+                           cptr->name, chptr->chname);
+                anylistsent = 1;
+                break; /* we don't pass this along, either.. */
+            }
+            if(++nparams > maxparams)
+            {
+                /* too many modes with params, eat this one */
+                args++;
+                break;
+            }
+            
+            /* do not allow : in bans, or a null ban */
+            if(*parv[args]==':' || *parv[args] == '\0') 
+            {
+                args++;
+                break;
+            }
+
+            /* make a 'pretty' ban mask here, then try and set it */
+            /* okay kids, let's do this again.
+             * the buffer returned by pretty_mask is from 
+             * make_nick_user_host. This buffer is eaten by add/del banid.
+             * Thus, some poor schmuck gets himself on the banlist.
+             * Fixed. - lucas */
+            strcpy(nuhbuf, collapse(pretty_mask(parv[args])));
+            parv[args] = nuhbuf;
+            /* if we're going to overflow our mode buffer,
+             * drop the change instead */
+            if((prelen + (mbuf - morig) + pidx + strlen(nuhbuf) + 1) > 
+               REALMODEBUFLEN) 
+            {
+                args++;
+                break;
+            }
+            /* if we can't add or delete (depending) the ban, change is
+             * worthless anyhow */
+            
+            if(!(change=='+' && !add_banid(sptr, chptr, parv[args])) && 
+               !(change=='-' && !del_banid(chptr, parv[args])))
+            {
+                args++;
+                break;
+            }
+            
+            *mbuf++ = 'b';
+            ADD_PARA(parv[args])
+                args++;
+            nmodes++;
+            break;
+
+        case 'j':
+#ifdef JOINRATE_SERVER_ONLY
+            if (MyClient(sptr)) 
+            {
+                sendto_one(sptr, err_str(ERR_ONLYSERVERSCANCHANGE),
+                           me.name, cptr->name, chptr->chname);
+                break;
+            }
+#endif
+
+            if(level<1) 
+            {
+                errors |= SM_ERR_NOPRIVS;
+                break;
+            }
+
+            /* if it's a -, just change the flag, we have no arguments */
+            if(change=='-')
+            {
+                if (MyClient(sptr) && (seenalready & MODE_JOINRATE))
+                    break;
+                seenalready |= MODE_JOINRATE;
+
+                if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN) 
+                    break;
+                *mbuf++ = 'j';
+                chptr->mode.mode &= ~MODE_JOINRATE;
+                chptr->mode.join_num = 0;
+                chptr->mode.join_time = 0;
+                chptr->join_start = 0;
+                chptr->join_count = 0;
+                nmodes++;
+                break;
+            }
+            else 
+            {
+                char *tmpa, *tmperr;
+                int j_num, j_time, tval;
+
+                if(parv[args] == NULL) 
+                {
+                    errors|=SM_ERR_MOREPARMS;
+                    break;
+                }
+                if(++nparams > maxparams)
+                {
+                    /* too many modes with params, eat this one */
+                    args++;
+                    break;
+                }
+                if (MyClient(sptr) && (seenalready & MODE_JOINRATE))
+                {
+                    args++;
+                    break;
+                }
+                seenalready |= MODE_JOINRATE;
+
+                tmpa = strchr(parv[args], ':');
+                if(tmpa)
+                {
+                    *tmpa = '\0';
+                    tmpa++;
+                    j_time = strtol(tmpa, &tmperr, 10);
+                    if(*tmperr != '\0' || j_time < 0)
+                    {
+                        /* error, user specified something 
+                         * invalid, just bail. */
+                        args++;
+                        break;
+                    }
+                }
+                else
+                    j_time = 0;
+
+                j_num = strtol(parv[args], &tmperr, 10);
+                if(*tmperr != '\0' || j_num < 0)
+                {
+                    args++;
+                    break;
+                }
+
+                /* range limit for local non-samodes */
+                if (MyClient(sptr) && level < 2)
+                {
+                    /* static limits: time <= 60, 4 <= num <= 60 */
+                    if (j_time > 60)
+                        j_time = 60;
+                    if (j_num > 60)
+                        j_num = 60;
+                    if (j_num < 4)
+                        j_num = 4;
+
+                    /* adjust number to time using min rate 1/8 */
+                    tval = (j_time-1)/8+1;
+                    if (j_num < tval)
+                        j_num = tval;
+
+                    /* adjust time to number using max rate 2/1 */
+                    tval = j_num/2;
+                    if (j_time < tval)
+                        j_time = tval;
+                }
+
+                if(j_num == 0 || j_time == 0)
+                {
+                    j_num = j_time = 0;
+                    ircsprintf(tmp, "0");
+                }
+                else
+                    ircsprintf(tmp, "%d:%d", j_num, j_time);
+
+                /* if we're going to overflow our mode buffer,
+                 * drop the change instead */
+                if((prelen + (mbuf - morig) + pidx + strlen(tmp)) > REALMODEBUFLEN) 
+                {
+                    args++;
+                    break;
+                }
+
+                chptr->mode.mode |= MODE_JOINRATE;
+                chptr->mode.join_num = j_num;
+                chptr->mode.join_time = j_time;
+                chptr->join_start = 0;
+                chptr->join_count = 0;
+                *mbuf++ = 'j';
+                ADD_PARA(tmp);
+                args++;
+                nmodes++;
+                break;
+            }
+
+        case 'l':
+            if(level<1) 
+            {
+                errors |= SM_ERR_NOPRIVS;
+                break;
+            }
+
+            /* if it's a -, just change the flag, we have no arguments */
+            if(change=='-')
+            {
+                if (MyClient(sptr) && (seenalready & MODE_LIMIT))
+                    break;
+                seenalready |= MODE_LIMIT;
+
+                if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN) 
+                    break;
+                *mbuf++ = 'l';
+                chptr->mode.mode &= ~MODE_LIMIT;
+                chptr->mode.limit = 0;
+                nmodes++;
+                break;
+            }
+            else 
+            {
+                if(parv[args] == NULL) 
+                {
+                    errors|=SM_ERR_MOREPARMS;
+                    break;
+                }
+                if(++nparams > maxparams)
+                {
+                    /* too many modes with params, eat this one */
+                    args++;
+                    break;
+                }
+                if (MyClient(sptr) && (seenalready & MODE_LIMIT))
+                {
+                    args++;
+                    break;
+                }
+                seenalready |= MODE_LIMIT;
+
+                /* if we're going to overflow our mode buffer,
+                 * drop the change instead */
+                if((prelen + (mbuf - morig) + pidx + 16) > REALMODEBUFLEN) 
+                {
+                    args++;
+                    break;
+                }
+               
+                i = atoi(parv[args]);
+
+                /* toss out invalid modes */
+                if(i < 1)
+                {
+                    args++;
+                    break;
+                }
+                ircsprintf(tmp, "%d", i);
+                chptr->mode.limit = i;
+                chptr->mode.mode |= MODE_LIMIT;
+                *mbuf++ = 'l';
+                ADD_PARA(tmp);
+                args++;
+                nmodes++;
+                break;
+            }
+
+        case 'k':
+            if(level<1) 
+            {
+                errors |= SM_ERR_NOPRIVS;
+                break;
+            }
+            if(parv[args]==NULL)
+                break;
+            if(++nparams > maxparams)
+            {
+                /* too many modes with params, eat this one */
+                args++;
+                break;
+            }
+            if (MyClient(sptr) && (seenalready & MODE_KEY))
+            {
+                args++;
+                break;
+            }
+            seenalready |= MODE_KEY;
+
+            /* do not allow keys to start with :! ack! - lucas */
+            /* another ack: don't let people set null keys! */
+            /* and yet a third ack: no spaces in keys -epi  */
+            if(*parv[args]==':' || *parv[args] == '\0' ||
+               strchr(parv[args], ' '))
+            {
+                args++;
+                break;
+            }
+            
+            /* Do not let *'s in keys in preperation for key hiding - Raist
+             * Also take out ",", which makes a channel unjoinable - lucas
+             */
+            
+            if (strchr(parv[args], '*') != NULL || 
+                strchr(parv[args], ',') != NULL) 
+            {
+                args++;
+                break;
+            }
+            
+            /* if we're going to overflow our mode buffer,
+             * drop the change instead */
+            if((prelen + (mbuf - morig) + pidx + KEYLEN+2) > REALMODEBUFLEN) 
+            {
+                args++;
+                break;
+            }
+            
+            /* if they're an op, they can futz with the key in
+             * any manner they like, we're not picky */
+            if(change=='+') 
+            {
+                strncpy(chptr->mode.key,parv[args],KEYLEN);
+                ADD_PARA(chptr->mode.key)
+            }
+            else 
+            {
+                char *sendkey = chptr->mode.key;
+                if (!*sendkey)
+                    sendkey = parv[args];
+                ADD_PARA(sendkey)
+                *chptr->mode.key = '\0';
+            }
+            *mbuf++='k';
+            args++;
+            nmodes++;
+            break;
+
+        case 'r':
+            if (MyClient(sptr) && (seenalready & MODE_REGISTERED))
+                break;
+            seenalready |= MODE_REGISTERED;
+            if (!IsServer(sptr) && !IsULine(sptr)) 
+            {
+                sendto_one(sptr, err_str(ERR_ONLYSERVERSCANCHANGE),
+                           me.name, cptr->name, chptr->chname);
+                break;
+            }
+            else 
+            {
+                if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN) 
+                    break;
+                
+                if(change=='+')
+                    chptr->mode.mode|=MODE_REGISTERED;
+                else
+                    chptr->mode.mode&=~MODE_REGISTERED;
+            }
+            *mbuf++='r';
+            nmodes++;
+            break;
+
+        case 'L':
+            if (MyClient(sptr) && (seenalready & MODE_LISTED))
+                break;
+            seenalready |= MODE_LISTED;
+            if (MyClient(sptr))
+            {
+                sendto_one(sptr, err_str(ERR_ONLYSERVERSCANCHANGE),
+                           me.name, cptr->name, chptr->chname);
+                break;
+            }
+            else
+            {       
+                if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN)
+                    break;
+             
+                if(change=='+')
+                    chptr->mode.mode|=MODE_LISTED;
+                else
+                    chptr->mode.mode&=~MODE_LISTED;
+            }
+            *mbuf++='L';
+            nmodes++;
+            break;
+            
+        case 'i':
+            if(level < 1) 
+            {
+                errors |= SM_ERR_NOPRIVS;
+                break;
+            }
+            if(change=='-')
+                while ((lp=chptr->invites))
+                    del_invite(lp->value.cptr, chptr);
+            /* fall through to default case */
+
+        default:
+            /* phew, no more tough modes. }:>, the rest are all
+             * covered in one step 
+             * with the above array */
+            if(level<1) 
+            {
+                errors |= SM_ERR_NOPRIVS;
+                break;
+            }
+            for(i=1;flags[i]!=0x0;i+=2) 
+            {
+                if((prelen + (mbuf - morig) + pidx + 1) > REALMODEBUFLEN) 
+                    break;
+                
+                if(*modes==(char)flags[i]) 
+                {
+                    if (MyClient(sptr) && (seenalready & flags[i-1]))
+                        break;
+                    seenalready |= flags[i-1];
+                    
+                    if(change=='+')
+                        chptr->mode.mode |= flags[i-1];
+                    else
+                        chptr->mode.mode &= ~flags[i-1];
+                    *mbuf++=*modes;
+                    nmodes++;
+                    break;
+                }
+            }
+            /* unknown mode.. */
+            if(flags[i]==0x0) 
+            {
+                /* we still spew lots of unknown mode bits...*/
+                /* but only to our own clients, silently ignore bogosity
+                 * from other servers... */
+                if(MyClient(sptr))
+                    sendto_one(sptr, err_str(ERR_UNKNOWNMODE), me.name,
+                               sptr->name, *modes);
+                        
+            }
+            break;
+        }
+        
+        /* spit out more parameters error here */
+        if(errors & SM_ERR_MOREPARMS && MyClient(sptr)) 
+        {
+            moreparmsstr[5]=change;
+            moreparmsstr[6]=*modes;
+            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name,
+                       sptr->name, moreparmsstr);
+            errors &= ~SM_ERR_MOREPARMS; /* oops, kill it in this case */
+        }
+        modes++;
+    }
+    /* clean up the end of the string... */
+    if(*(mbuf-1) == '+' || *(mbuf-1) == '-')
+        *(mbuf-1) = '\0';
+    else
+        *mbuf = '\0';
+    pbuf[pidx] = '\0';
+    if(MyClient(sptr)) 
+    {
+        if(errors & SM_ERR_NOPRIVS)
+            sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name,
+                       sptr->name, chptr->chname);        
+        if(errors & SM_ERR_NOTOPER)
+            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, sptr->name);
+        if(errors & SM_ERR_RESTRICTED)
+            sendto_one(sptr,":%s NOTICE %s :*** Notice -- You are "
+                       "restricted and cannot chanop others",
+                       me.name, sptr->name);
+    }
+    /* all done! */
+    return nmodes;
+#undef ADD_PARA
+}
+
+static int can_join(aClient *sptr, aChannel *chptr, char *key)
+{
+    Link   *lp;
+    int invited = 0;
+    int error = 0;
+    char *r = NULL;
+
+    for(lp = sptr->user->invited; lp; lp = lp->next)
+    {
+        if(lp->value.chptr == chptr)
+        {
+            invited = 1;
+            break;
+        }
+    }
+
+    if (invited || IsULine(sptr))
+        return 1;
+
+    if (check_joinrate(chptr, NOW, 1, sptr) == 0)
+    {
+        r = "+j";
+        error = ERR_CHANNELISFULL;
+    }
+    else if (chptr->mode.mode & MODE_INVITEONLY)
+    {
+        r = "+i";
+        error = ERR_INVITEONLYCHAN;
+    }
+    else if (chptr->mode.mode & MODE_OPERONLY && !IsOper(sptr))
+    {
+        r = "+O";
+        error = ERR_INVITEONLYCHAN;
+    }
+    else if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
+    {
+        r = "+l";
+        error = ERR_CHANNELISFULL;
+    }
+    else if (chptr->mode.mode & MODE_REGONLY && !IsRegNick(sptr))
+        error = ERR_NEEDREGGEDNICK;
+    else if (*chptr->mode.key && (BadPtr(key) || mycmp(chptr->mode.key, key)))
+        error = ERR_BADCHANNELKEY;
+
+#ifdef INVITE_LISTS
+    if (error && is_invited(sptr, chptr))
+        error = 0;
+#endif
+
+    if (!error && is_banned(sptr, chptr, NULL))
+        error = ERR_BANNEDFROMCHAN;
+
+    if (error)
+    {
+        if (error==ERR_NEEDREGGEDNICK)
+            sendto_one(sptr, getreply(ERR_NEEDREGGEDNICK), me.name, sptr->name,
+                       chptr->chname, "join", NS_Services_Name,
+                       NS_Register_URL);
+        else
+            sendto_one(sptr, getreply(error), me.name, sptr->name,
+                       chptr->chname, r);
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * can_join_whynot:
+ * puts a list of the modes preventing us from joining in reasonbuf
+ * ret is number of matched modes
+ */
+static int 
+can_join_whynot(aClient *sptr, aChannel *chptr, char *key, char *reasonbuf)
+{
+    Link   *lp;
+    int invited = 0;
+    int rbufpos = 0;
+
+    for(lp = sptr->user->invited; lp; lp = lp->next) 
+    {
+        if(lp->value.chptr == chptr) 
+        {
+            invited = 1;
+            break;
+        }
+    }
+
+    if (invited || IsULine(sptr))
+        return 0;
+
+    if (chptr->mode.mode & MODE_INVITEONLY)
+        reasonbuf[rbufpos++] = 'i';
+    if (chptr->mode.mode & MODE_REGONLY && !IsRegNick(sptr))
+        reasonbuf[rbufpos++] = 'R';
+    if (chptr->mode.mode & MODE_OPERONLY && !IsOper(sptr))
+        reasonbuf[rbufpos++] = 'O';
+    if (*chptr->mode.key && (BadPtr(key) || mycmp(chptr->mode.key, key)))
+        reasonbuf[rbufpos++] = 'k';
+    if (chptr->mode.limit && chptr->users >= chptr->mode.limit) 
+        reasonbuf[rbufpos++] = 'l';
+    if (check_joinrate(chptr, NOW, 1, sptr) == 0)
+        reasonbuf[rbufpos++] = 'j';
+
+#ifdef INVITE_LISTS
+    if (rbufpos && is_invited(sptr, chptr))
+        rbufpos = 0;
+#endif
+
+    if (is_banned(sptr, chptr, NULL))
+        reasonbuf[rbufpos++] = 'b';
+
+    reasonbuf[rbufpos] = '\0';
+    return rbufpos;
+}
+
+/*
+ * Remove bells and commas from channel name
+ */
+void clean_channelname(unsigned char *cn)
+{
+    for (; *cn; cn++)
+        /*
+         * All characters >33 are allowed, except commas, and the weird
+         * fake-space character mIRCers whine about -wd
+         */
+        if (*cn < 33 || *cn == ',' || (*cn == 160))
+        {
+            *cn = '\0';
+            return;
+        }
+    return;
+}
+
+/* we also tell the client if the channel is invalid. */
+int check_channelname(aClient *cptr, unsigned char *cn)
+{
+    if(!MyClient(cptr))
+        return 1;
+    for(;*cn;cn++) 
+    {
+        if(*cn<33 || *cn == ',' || *cn==160) 
+        {
+            sendto_one(cptr, getreply(ERR_BADCHANNAME), me.name, cptr->name,
+                       cn);
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/*
+ * *  Get Channel block for chname (and allocate a new channel *
+ * block, if it didn't exist before).
+ */
+static aChannel *
+get_channel(aClient *cptr, char *chname, int flag, int *created)
+{
+    aChannel *chptr;
+    int         len;
+
+    if(created)
+        *created = 0;
+
+    if (BadPtr(chname))
+        return NULL;
+
+    len = strlen(chname);
+    if (MyClient(cptr) && len > CHANNELLEN)
+    {
+        len = CHANNELLEN;
+        *(chname + CHANNELLEN) = '\0';
+    }
+    if ((chptr = find_channel(chname, (aChannel *) NULL)))
+        return (chptr);
+    if (flag == CREATE)
+    {
+        chptr = make_channel();
+
+        if(created)
+            *created = 1;
+        
+        strncpyzt(chptr->chname, chname, len + 1);
+        if (channel)
+            channel->prevch = chptr;
+        chptr->prevch = NULL;
+        chptr->nextch = channel;
+        channel = chptr;
+        chptr->channelts = timeofday;
+        (void) add_to_channel_hash_table(chname, chptr);
+        Count.chan++;
+    }
+    return chptr;
+}
+
+static void add_invite(aClient *cptr, aChannel *chptr)
+{
+    Link   *inv, **tmp;
+    
+    del_invite(cptr, chptr);
+    /*
+     * delete last link in chain if the list is max length
+     */
+    if (list_length(cptr->user->invited) >= maxchannelsperuser)
+    {
+        /*
+         * This forgets the channel side of invitation     -Vesa inv =
+         * cptr->user->invited; cptr->user->invited = inv->next;
+         * free_link(inv);
+         */
+        del_invite(cptr, cptr->user->invited->value.chptr);
+        
+    }
+    
+    /*
+     * add client to channel invite list
+     */
+    inv = make_link();
+    inv->value.cptr = cptr;
+    inv->next = chptr->invites;
+    chptr->invites = inv;
+    /*
+     * add channel to the end of the client invite list
+     */
+    for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next));
+    inv = make_link();
+    inv->value.chptr = chptr;
+    inv->next = NULL;
+    (*tmp) = inv;
+}
+
+/*
+ * Delete Invite block from channel invite list and client invite list
+ */
+void del_invite(aClient *cptr, aChannel *chptr)
+{
+    Link  **inv, *tmp;
+
+    for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
+        if (tmp->value.cptr == cptr)
+        {
+            *inv = tmp->next;
+            free_link(tmp);
+            break;
+        }
+    
+    for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next)
+        if (tmp->value.chptr == chptr)
+        {
+            *inv = tmp->next;
+            free_link(tmp);
+            break;
+        }
+}
+
+/*
+ * *  Subtract one user from channel i (and free channel *  block, if
+ * channel became empty).
+ */
+static void sub1_from_channel(aChannel *chptr)
+{
+    Link   *tmp;
+    aBan              *bp, *bprem;
+#ifdef INVITE_LISTS
+    anInvite          *invite, *invrem;
+#endif
+#ifdef EXEMPT_LISTS
+    aBanExempt        *exempt, *exrem;
+#endif
+    
+    if (--chptr->users <= 0) 
+    {
+        /*
+         * Now, find all invite links from channel structure
+         */
+        while ((tmp = chptr->invites))
+            del_invite(tmp->value.cptr, chptr);
+
+        bp = chptr->banlist;
+        while (bp)
+        {
+            bprem = bp;
+            bp = bp->next;
+            MyFree(bprem->banstr);
+            MyFree(bprem->who);
+            MyFree(bprem);
+        }
+#ifdef INVITE_LISTS
+        invite = chptr->invite_list;
+        while (invite)
+        {
+            invrem = invite;
+            invite = invite->next;
+            MyFree(invrem->invstr);
+            MyFree(invrem->who);
+            MyFree(invrem);
+        }
+#endif
+#ifdef EXEMPT_LISTS
+        exempt = chptr->banexempt_list;
+        while (exempt)
+        {
+            exrem = exempt;
+            exempt = exempt->next;
+            MyFree(exrem->banstr);
+            MyFree(exrem->who);
+            MyFree(exrem);
+        }
+#endif
+
+        if (chptr->prevch)
+            chptr->prevch->nextch = chptr->nextch;
+        else
+            channel = chptr->nextch;
+        if (chptr->nextch)
+            chptr->nextch->prevch = chptr->prevch;
+        (void) del_from_channel_hash_table(chptr->chname, chptr);
+#ifdef FLUD
+        free_fluders(NULL, chptr);
+#endif
+        free_channel(chptr);
+        Count.chan--;
+    }
+}
+
+/*
+ * m_join 
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ * parv[2] = channel password (key)
+ */
+int m_join(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    static char jbuf[BUFSIZE];
+    Link   *lp;
+    struct simBan *ban;
+    aChannel *chptr;
+    char   *name, *key = NULL;
+    int         i, flags = 0, chanlen=0;        
+    int         allow_op = YES;
+    char       *p = NULL, *p2 = NULL;
+        
+#ifdef ANTI_SPAMBOT
+    int         successful_join_count = 0;      
+    /* Number of channels successfully joined */
+#endif
+        
+    if (!(sptr->user))
+    {
+        /* something is *fucked* - bail */
+        return 0;
+    }
+        
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "JOIN");
+        return 0;
+    }
+
+    if (MyClient(sptr))
+        parv[1] = canonize(parv[1]);
+        
+    *jbuf = '\0';
+    /*
+     * * Rebuild list of channels joined to be the actual result of the *
+     * JOIN.  Note that "JOIN 0" is the destructive problem.
+     */
+    for (i = 0, name = strtoken(&p, parv[1], ","); name;
+         name = strtoken(&p, (char *) NULL, ","))
+    {
+        /*
+         * pathological case only on longest channel name. * If not dealt
+         * with here, causes desynced channel ops * since ChannelExists()
+         * doesn't see the same channel * as one being joined. cute bug.
+         * Oct 11 1997, Dianora/comstud
+         */
+        if(!check_channelname(sptr, (unsigned char *) name))
+            continue;
+        
+        chanlen=strlen(name);
+        
+        if (chanlen > CHANNELLEN) /* same thing is done in get_channel() */
+        {
+            name[CHANNELLEN] = '\0';
+            chanlen=CHANNELLEN;
+        }
+        if (*name == '0' && !atoi(name))
+            *jbuf = '\0';
+        else if (!IsChannelName(name))
+        {
+            if (MyClient(sptr))
+                sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
+                           me.name, parv[0], name);
+            continue;
+        }
+        if (*jbuf)
+            (void) strcat(jbuf, ",");
+        (void) strncat(jbuf, name, sizeof(jbuf) - i - 1);
+        i += chanlen + 1;
+    }
+    
+    p = NULL;
+    if (parv[2])
+        key = strtoken(&p2, parv[2], ",");
+    parv[2] = NULL;             /*
+                                 * for m_names call later, parv[parc]
+                                 * * must == NULL 
+                                 */
+    for (name = strtoken(&p, jbuf, ","); name;
+         key = (key) ? strtoken(&p2, NULL, ",") : NULL,
+             name = strtoken(&p, NULL, ","))
+    {
+        /*
+         * JOIN 0 sends out a part for all channels a user * has
+         * joined.
+         */
+        if (*name == '0' && !atoi(name))
+        {
+            if (sptr->user->channel == NULL)
+                continue;
+            while ((lp = sptr->user->channel))
+            {
+                chptr = lp->value.chptr;
+                sendto_channel_butserv(chptr, sptr, PartFmt,
+                                       parv[0], chptr->chname);
+                remove_user_from_channel(sptr, chptr);
+            }
+            /*
+             * Added /quote set for SPAMBOT
+             * 
+             * int spam_time = MIN_JOIN_LEAVE_TIME; int spam_num =
+             * MAX_JOIN_LEAVE_COUNT;
+             */
+#ifdef ANTI_SPAMBOT             /* Dianora */
+                        
+            if (MyConnect(sptr) && !IsAnOper(sptr))
+            {
+                if (sptr->join_leave_count >= spam_num)
+                {
+                    sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) is a "
+                                   "possible spambot", sptr->name,
+                                   sptr->user->username, sptr->user->host);
+                    sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
+                }
+                else
+                {
+                    int         t_delta;
+                    
+                    if ((t_delta = (NOW - sptr->last_leave_time)) >
+                        JOIN_LEAVE_COUNT_EXPIRE_TIME)
+                    {
+                        int         decrement_count;
+                        
+                        decrement_count = (t_delta /
+                                           JOIN_LEAVE_COUNT_EXPIRE_TIME);
+                        
+                        if (decrement_count > sptr->join_leave_count)
+                            sptr->join_leave_count = 0;
+                        else
+                            sptr->join_leave_count -= decrement_count;
+                    }
+                    else
+                    {
+                        if ((NOW - (sptr->last_join_time)) < spam_time)
+                        {
+                            /* oh, its a possible spambot */
+                            sptr->join_leave_count++;
+                        }
+                    }
+                    sptr->last_leave_time = NOW;
+                }
+            }
+#endif
+            sendto_serv_butone(cptr, ":%s JOIN 0", parv[0]);
+            continue;
+        }
+        
+        if (MyConnect(sptr))
+        {
+            /* have we quarantined this channel? */
+            if(!IsOper(sptr) && (ban = check_mask_simbanned(name, SBAN_CHAN)))
+            {
+                sendto_one(sptr, getreply(ERR_CHANBANREASON), me.name, parv[0], name,
+                        BadPtr(ban->reason) ? "Reserved channel" :      
+                        ban->reason);
+                continue;
+            }
+
+            /*
+             * local client is first to enter previously nonexistent *
+             * channel so make them (rightfully) the Channel * Operator.
+             */
+            flags = (ChannelExists(name)) ? 0 : CHFL_CHANOP;
+
+            if (!IsAnOper(sptr) && server_was_split
+                && !(confopts & FLAGS_SPLITOPOK))
+                    allow_op = NO;
+            
+            if ((sptr->user->joined >= maxchannelsperuser) &&
+                (!IsAnOper(sptr) || (sptr->user->joined >= 
+                                     maxchannelsperuser * 3)))
+            {
+                sendto_one(sptr, err_str(ERR_TOOMANYCHANNELS),
+                           me.name, parv[0], name);
+#ifdef ANTI_SPAMBOT
+                if (successful_join_count)
+                    sptr->last_join_time = NOW;
+#endif
+                return 0;
+            }
+#ifdef ANTI_SPAMBOT             /*
+                                 * Dianora 
+                                 */
+            if (flags == 0)     /* if channel doesn't exist, don't penalize */
+                successful_join_count++;
+            if (sptr->join_leave_count >= spam_num)
+            {
+                                /* Its already known as a possible spambot */
+                
+                if (sptr->oper_warn_count_down > 0)  /* my general paranoia */
+                    sptr->oper_warn_count_down--;
+                else
+                    sptr->oper_warn_count_down = 0;
+                
+                if (sptr->oper_warn_count_down == 0)
+                {
+                    sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) trying to "
+                                   "join %s is a possible spambot",
+                                   sptr->name,
+                                   sptr->user->username,
+                                   sptr->user->host,
+                                   name);
+                    sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
+                }
+# ifndef ANTI_SPAMBOT_WARN_ONLY
+                return 0;               /* Don't actually JOIN anything, but
+                                         * don't let spambot know that */
+# endif
+            }
+#endif
+        }
+        else
+        {
+            /*
+             * complain for remote JOINs to existing channels * (they
+             * should be SJOINs) -orabidoo
+             */
+            if (!ChannelExists(name))
+                ts_warn("User on %s remotely JOINing new channel",
+                        sptr->user->server);
+        }
+
+        chptr = get_channel(sptr, name, CREATE, NULL);
+
+        if (chptr && IsMember(sptr, chptr))
+            continue;
+        
+        if (!chptr || (MyConnect(sptr) && !can_join(sptr, chptr, key)))
+        {
+#ifdef ANTI_SPAMBOT
+            if (successful_join_count > 0)
+                successful_join_count--;
+#endif
+            continue;
+        }
+        
+/* only complain when the user can join the channel, the channel is
+ * being created by this user, and this user is not allowed to be an op.
+ * - lucas 
+ */
+
+        if (flags && !allow_op)
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- Due to a network "
+                       "split, you can not obtain channel operator status in "
+                       "a new channel at this time.", me.name, sptr->name);
+        
+        /* Complete user entry to the new channel (if any) */
+        if (allow_op)
+            add_user_to_channel(chptr, sptr, flags);
+        else
+            add_user_to_channel(chptr, sptr, 0);
+        chptr->join_count++;
+        /* Set timestamp if appropriate, and propagate */
+        if (MyClient(sptr) && flags == CHFL_CHANOP) 
+        {
+            chptr->channelts = timeofday;
+            
+            /* we keep channel "creations" to the server sjoin format,
+               so we can bounce modes and stuff if our ts is older. */
+            
+            if (allow_op)
+                sendto_serv_butone(cptr, ":%s SJOIN %ld %s + :@%s", me.name,
+                                   chptr->channelts, name, parv[0]);
+            else
+                sendto_serv_butone(cptr, ":%s SJOIN %ld %s + :%s", me.name,
+                                   chptr->channelts, name, parv[0]);
+        }
+        else if (MyClient(sptr)) 
+            sendto_serv_butone(cptr, CliSJOINFmt, parv[0], chptr->channelts,
+                               name);
+        else 
+            sendto_serv_butone(cptr, ":%s JOIN :%s", parv[0], name);
+
+        /* notify all other users on the new channel */
+        sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name);
+                
+        if (MyClient(sptr)) 
+        {
+            del_invite(sptr, chptr);
+            if (chptr->topic[0] != '\0') 
+            {
+                sendto_one(sptr, rpl_str(RPL_TOPIC), me.name,
+                           parv[0], name, chptr->topic);
+                sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME),
+                           me.name, parv[0], name,
+                           chptr->topic_nick,
+                           chptr->topic_time);
+            }
+            parv[1] = name;
+            (void) m_names(cptr, sptr, 2, parv);
+        }
+    }
+        
+#ifdef ANTI_SPAMBOT
+    if (MyConnect(sptr) && successful_join_count)
+        sptr->last_join_time = NOW;
+#endif
+    return 0;
+}
+
+/* m_sajoin
+ * join a channel regardless of modes.
+ */
+
+int m_sajoin(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+        aChannel        *chptr;
+        char            *name;
+        int              i;
+        char            errmodebuf[128];
+
+        /* Remote sajoin? nope. */
+        if(!MyClient(sptr))
+                return 0;
+
+        if(!IsSAdmin(sptr))
+        {
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+                return 0;
+        }
+
+        if (parc < 2 || *parv[1] == '\0')
+        {
+                sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                           me.name, parv[0], "SAJOIN");
+                return 0;
+        }
+
+        name = parv[1];
+
+        chptr = find_channel(name, NULL);
+        if(!chptr)
+        {
+                sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
+                           me.name, parv[0], name);
+                return 0;
+        }
+
+        /* bail if they're already in the channel */
+        if(IsMember(sptr, chptr))
+                return 0;
+
+        if((i = can_join_whynot(sptr, chptr, NULL, errmodebuf)))
+        {
+            send_globops("from %s: %s used SAJOIN (%s +%s)",
+                         me.name, sptr->name, chptr->chname, errmodebuf);
+            sendto_serv_butone(NULL, ":%s GLOBOPS :%s used SAJOIN (%s +%s)",
+                               me.name, sptr->name, chptr->chname, errmodebuf);
+        }
+        else
+            sendto_one(sptr, ":%s NOTICE %s :You didn't need to use"
+                       " /SAJOIN for %s", me.name, parv[0], chptr->chname);
+
+        add_user_to_channel(chptr, sptr, 0);
+        sendto_serv_butone(cptr, CliSJOINFmt, parv[0], chptr->channelts, name);
+        sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name);
+        if(MyClient(sptr))
+        {
+            if(chptr->topic[0] != '\0')
+            {
+                sendto_one(sptr, rpl_str(RPL_TOPIC), me.name, parv[0], 
+                            name, chptr->topic);
+                sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME), me.name, parv[0], 
+                            name, chptr->topic_nick, chptr->topic_time);
+            }
+            parv[1] = name;
+            parv[2] = NULL;
+            m_names(cptr, sptr, 2, parv);
+        }
+        return 0;
+}
+
+/*
+ * m_part 
+ * parv[0] = sender prefix 
+ * parv[1] = channel
+ * parv[2] = Optional part reason
+ */
+int m_part(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aChannel *chptr;
+    char       *p, *name;
+    char *reason = (parc > 2 && parv[2]) ? parv[2] : NULL;
+
+    if (parc < 2 || parv[1][0] == '\0')
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "PART");
+        return 0;
+    }
+    
+    name = strtoken(&p, parv[1], ",");
+    
+#ifdef ANTI_SPAMBOT             /* Dianora */
+    /* if its my client, and isn't an oper */
+    
+    if (name && MyConnect(sptr) && !IsAnOper(sptr))
+    {
+        if (sptr->join_leave_count >= spam_num)
+        {
+            sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) is a possible"
+                        " spambot", sptr->name, sptr->user->username, 
+                        sptr->user->host);
+            sptr->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
+        }
+        else
+        {
+            int         t_delta;
+
+            if ((t_delta = (NOW - sptr->last_leave_time)) >
+                JOIN_LEAVE_COUNT_EXPIRE_TIME)
+            {
+                int         decrement_count;
+
+                decrement_count = (t_delta / JOIN_LEAVE_COUNT_EXPIRE_TIME);
+
+                if (decrement_count > sptr->join_leave_count)
+                    sptr->join_leave_count = 0;
+                else
+                    sptr->join_leave_count -= decrement_count;
+            }
+            else
+            {
+                if ((NOW - (sptr->last_join_time)) < spam_time)
+                {
+                    /* oh, its a possible spambot */
+                    sptr->join_leave_count++;
+                }
+            }
+            sptr->last_leave_time = NOW;
+        }
+    }
+#endif
+
+    while (name)
+    {
+        chptr = get_channel(sptr, name, 0, NULL);
+        if (!chptr)
+        {
+            sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
+                       me.name, parv[0], name);
+            name = strtoken(&p, (char *) NULL, ",");
+            continue;
+        }
+
+        if (!IsMember(sptr, chptr))
+        {
+            sendto_one(sptr, err_str(ERR_NOTONCHANNEL),
+                       me.name, parv[0], name);
+            name = strtoken(&p, (char *) NULL, ",");
+            continue;
+        }
+        /* Remove user from the old channel (if any) */
+
+        if (parc < 3 || can_send(sptr,chptr,reason))
+            sendto_serv_butone(cptr, PartFmt, parv[0], name);
+        else
+            sendto_serv_butone(cptr, PartFmt2, parv[0], name, reason);
+        if (parc < 3 || can_send(sptr,chptr,reason))
+            sendto_channel_butserv(chptr, sptr, PartFmt, parv[0], name);
+        else
+            sendto_channel_butserv(chptr, sptr, PartFmt2, parv[0], name,
+                                   reason);
+        remove_user_from_channel(sptr, chptr);
+        name = strtoken(&p, (char *) NULL, ",");
+    }
+    return 0;
+}
+
+/*
+ * m_kick
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ * parv[2] = client to kick
+ * parv[3] = kick comment
+ */
+int m_kick(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient    *who;
+    aChannel   *chptr;
+    int         chasing = 0;
+    int         user_count;     /* count nicks being kicked, only allow 4 */
+    char       *comment, *name, *p = NULL, *user, *p2 = NULL;
+
+    if (parc < 3 || *parv[1] == '\0')
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "KICK");
+        return 0;
+    }
+    if (IsServer(sptr) && !IsULine(sptr))
+        sendto_ops("KICK from %s for %s %s",
+                   parv[0], parv[1], parv[2]);
+    comment = (BadPtr(parv[3])) ? parv[0] : parv[3];
+    if (strlen(comment) > (size_t) TOPICLEN)
+        comment[TOPICLEN] = '\0';
+    
+    *nickbuf = *buf = '\0';
+    name = strtoken(&p, parv[1], ",");
+    
+    while (name)
+    {
+        chptr = get_channel(sptr, name, !CREATE, NULL);
+        if (!chptr)
+        {
+            sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL),
+                       me.name, parv[0], name);
+            name = strtoken(&p, (char *) NULL, ",");
+            continue;
+        }
+
+        /*
+         * You either have chan op privs, or you don't -Dianora 
+         *
+         * orabidoo and I discussed this one for a while... I hope he
+         * approves of this code, users can get quite confused...
+         * -Dianora
+         */
+
+        if (!IsServer(sptr) && !is_chan_op(sptr, chptr) && !IsULine(sptr))
+        {
+            /* was a user, not a server and user isn't seen as a chanop here */
+
+            if (MyConnect(sptr))
+            {
+                /* user on _my_ server, with no chanops.. so go away */
+
+                sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
+                           me.name, parv[0], chptr->chname);
+                name = strtoken(&p, (char *) NULL, ",");
+                continue;
+            }
+            
+            if (chptr->channelts == 0)
+            {
+                /* If its a TS 0 channel, do it the old way */
+
+                sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
+                           me.name, parv[0], chptr->chname);
+                name = strtoken(&p, (char *) NULL, ",");
+                continue;
+            }
+            /*
+             * Its a user doing a kick, but is not showing as chanop
+             * locally its also not a user ON -my- server, and the channel
+             * has a TS. There are two cases we can get to this point
+             * then...
+             * 
+             * 1) connect burst is happening, and for some reason a legit op
+             * has sent a KICK, but the SJOIN hasn't happened yet or been
+             * seen. (who knows.. due to lag...)
+             * 
+             * 2) The channel is desynced. That can STILL happen with TS
+             * 
+             * Now, the old code roger wrote, would allow the KICK to go
+             * through. Thats quite legit, but lets weird things like
+             * KICKS by users who appear not to be chanopped happen, or
+             * even neater, they appear not to be on the channel. This
+             * fits every definition of a desync, doesn't it? ;-) So I
+             * will allow the KICK, otherwise, things are MUCH worse. But
+             * I will warn it as a possible desync.
+             * 
+             * -Dianora
+             *
+             * sendto_one(sptr, err_str(ERR_DESYNC), me.name, parv[0],
+             * chptr->chname);
+             *
+             * After more discussion with orabidoo...
+             * 
+             * The code was sound, however, what happens if we have +h (TS4)
+             * and some servers don't understand it yet? we will be seeing
+             * servers with users who appear to have no chanops at all,
+             * merrily kicking users.... -Dianora
+             * 
+             */
+        }
+
+        user = strtoken(&p2, parv[2], ",");
+        user_count = 4;
+        while (user && user_count)
+        {
+            user_count--;
+            if (!(who = find_chasing(sptr, user, &chasing)))
+            {
+                user = strtoken(&p2, (char *) NULL, ",");
+                continue;               /* No such user left! */
+            }
+
+            if (IsMember(who, chptr))
+            {
+                sendto_channel_butserv(chptr, sptr,
+                                       ":%s KICK %s %s :%s", parv[0],
+                                       name, who->name, comment);
+                sendto_serv_butone(cptr, ":%s KICK %s %s :%s", parv[0], name,
+                                   who->name, comment);
+                remove_user_from_channel(who, chptr);
+            }
+            else
+                sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
+                           me.name, parv[0], user, name);
+            user = strtoken(&p2, (char *) NULL, ",");
+        }                               /* loop on parv[2] */
+
+        name = strtoken(&p, (char *) NULL, ",");
+    }                           /* loop on parv[1] */
+
+    return (0);
+}
+
+int count_channels(aClient *sptr)
+{
+    aChannel *chptr;
+    int     count = 0;
+
+    for (chptr = channel; chptr; chptr = chptr->nextch)
+        count++;
+    return (count);
+}
+
+void send_topic_burst(aClient *cptr)
+{
+    aChannel *chptr;
+    aClient *acptr;
+    for (chptr = channel; chptr; chptr = chptr->nextch)
+    {
+        if(chptr->topic[0] != '\0')
+            sendto_one(cptr, ":%s TOPIC %s %s %ld :%s", me.name, chptr->chname,
+                       chptr->topic_nick, chptr->topic_time, chptr->topic);
+    }
+    for (acptr = client; acptr; acptr = acptr->next)
+    {
+        if(!IsPerson(acptr) || acptr->from == cptr)
+            continue;
+        if(acptr->user->away)
+            sendto_one(cptr, ":%s AWAY :%s", acptr->name, acptr->user->away);
+    }
+}
+
+/*
+ * m_topic 
+ * parv[0] = sender prefix 
+ * parv[1] = topic text
+ */
+int m_topic(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aChannel   *chptr = NullChn;
+    char       *topic = NULL, *name, *tnick = sptr->name;
+    time_t     ts = timeofday;
+    int        member;  
+
+    if (parc < 2) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "TOPIC");
+        return 0;
+    }
+        
+    name = parv[1];
+    chptr = find_channel(name, NullChn);
+    if(!chptr) 
+    {
+        sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name);
+        return 0;
+    }
+    
+    member = IsMember(sptr, chptr);
+    
+    if (parc == 2) /* user is requesting a topic */ 
+    {   
+        char *namep = chptr->chname;
+        char tempchname[CHANNELLEN + 2];
+
+        if(!member && !(ShowChannel(sptr, chptr)))
+        {
+            if(IsAdmin(sptr))
+            {
+                tempchname[0] = '%';
+                strcpy(&tempchname[1], chptr->chname);
+                namep = tempchname;
+            }
+            else
+            {
+                sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],
+                           name);
+                return 0;
+            }
+        }
+
+        if (chptr->topic[0] == '\0')
+            sendto_one(sptr, rpl_str(RPL_NOTOPIC), me.name, parv[0], namep);
+        else 
+        {
+            sendto_one(sptr, rpl_str(RPL_TOPIC), me.name, parv[0], namep,
+                       chptr->topic);
+            sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME), me.name, parv[0],
+                       namep, chptr->topic_nick, chptr->topic_time);
+        }
+        return 0;
+    }
+
+    topic = parv[2];
+
+    if (MyClient(sptr))
+    {
+        if (!member)
+        {
+            sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],name);
+            return 0;
+        }
+
+        if ((chptr->mode.mode & MODE_TOPICLIMIT) && !is_chan_op(sptr, chptr))
+        {
+            sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
+                       chptr->chname);
+            return 0;
+        }
+    }
+    else
+    {
+        /* extended info */
+        if (parc > 3)
+        {
+            topic = (parc > 4 ? parv[4] : "");
+            tnick = parv[2];
+            ts = atoi(parv[3]);
+        }
+
+        /* ignore old topics during burst/race */
+        if (!IsULine(sptr) && chptr->topic[0] && chptr->topic_time >= ts)
+            return 0;
+    }
+
+    strncpyzt(chptr->topic, topic, TOPICLEN + 1);
+    strcpy(chptr->topic_nick, tnick);
+    chptr->topic_time = ts;
+
+    /* in this case I think it's better that we send all the info that df
+     * sends with the topic, so I changed everything to work like that.
+     * -wd */
+
+    sendto_serv_butone(cptr, ":%s TOPIC %s %s %lu :%s", parv[0],
+                       chptr->chname, chptr->topic_nick, chptr->topic_time,
+                       chptr->topic);
+    sendto_channel_butserv_me(chptr, sptr, ":%s TOPIC %s :%s", parv[0],
+                              chptr->chname, chptr->topic);
+        
+    return 0;
+}
+
+/*
+ * m_invite 
+ * parv[0] - sender prefix 
+ * parv[1] - user to invite 
+ * parv[2] - channel name
+ */
+int m_invite(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient    *acptr;
+    aChannel   *chptr = NULL;
+    
+    if (parc < 3 || *parv[1] == 0)
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "INVITE");
+        return -1;
+    }
+
+    if (!(acptr = find_person(parv[1], NULL)))
+    {
+        sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
+        return 0;
+    }
+
+    if (MyClient(sptr))
+    {
+        if (!(chptr = find_channel(parv[2], NULL)))
+        {
+            sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0],
+                       parv[2]);
+            return 0;
+        }
+
+        if (!IsMember(sptr, chptr))
+        {
+            sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],
+                       parv[2]);
+            return 0;
+        }
+
+        if (IsMember(acptr, chptr))
+        {
+            sendto_one(sptr, err_str(ERR_USERONCHANNEL), me.name, parv[0],
+                       parv[1], chptr->chname);
+            return 0;
+        }
+
+        if (!is_chan_op(sptr, chptr))
+        {
+            sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
+                       chptr->chname);
+            return 0;
+        }
+
+        sendto_one(sptr, rpl_str(RPL_INVITING), me.name, parv[0], acptr->name,
+                   chptr->chname);
+
+        if (acptr->user->away)
+            sendto_one(sptr, rpl_str(RPL_AWAY), me.name, parv[0], acptr->name,
+                       acptr->user->away);
+    }
+
+    if (MyClient(acptr))
+    {
+        /* stuff already done above */
+        if (!MyClient(sptr))
+        {
+            if (!(chptr = find_channel(parv[2], NullChn)))
+                return 0;
+
+            if (IsMember(acptr, chptr))
+                return 0;
+        }
+
+        add_invite(acptr, chptr);
+
+        sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s", parv[0],
+                          acptr->name, chptr->chname);
+        sendto_channelops_butone(NULL, &me, chptr, ":%s NOTICE @%s :%s invited"
+                                 " %s into channel %s", me.name, chptr->chname,
+                                 parv[0], acptr->name, chptr->chname);
+
+        return 0;
+    }
+
+    sendto_one(acptr, ":%s INVITE %s :%s", parv[0], parv[1], parv[2]);
+
+    return 0;
+}
+
+
+/*
+ * The function which sends the actual channel list back to the user.
+ * Operates by stepping through the hashtable, sending the entries back if
+ * they match the criteria.
+ * cptr = Local client to send the output back to.
+ * numsend = Number (roughly) of lines to send back. Once this number has
+ * been exceeded, send_list will finish with the current hash bucket,
+ * and record that number as the number to start next time send_list
+ * is called for this user. So, this function will almost always send
+ * back more lines than specified by numsend (though not by much,
+ * assuming CH_MAX is was well picked). So be conservative in your choice
+ * of numsend. -Rak
+ */
+
+void send_list(aClient *cptr, int numsend)
+{
+    aChannel    *chptr;
+    LOpts       *lopt = cptr->user->lopt;
+    int         hashnum;
+    
+    for (hashnum = lopt->starthash; hashnum < CH_MAX; hashnum++)
+    {
+        if (numsend > 0)
+        {
+            for (chptr = (aChannel *)hash_get_chan_bucket(hashnum); 
+                 chptr; chptr = chptr->hnextch)
+            {
+                if (SecretChannel(chptr) && !IsAdmin(cptr)
+                    && !IsMember(cptr, chptr))
+                    continue;
+#ifdef USE_CHANMODE_L
+                if (lopt->only_listed && !(chptr->mode.mode & MODE_LISTED))
+                    continue;
+#endif
+                if ((!lopt->showall) && ((chptr->users < lopt->usermin) ||
+                                         ((lopt->usermax >= 0) && 
+                                          (chptr->users > lopt->usermax)) ||
+                                         ((chptr->channelts||1) < 
+                                          lopt->chantimemin) ||
+                                         (chptr->topic_time < 
+                                          lopt->topictimemin) ||
+                                         (chptr->channelts > 
+                                          lopt->chantimemax) ||
+                                         (chptr->topic_time > 
+                                          lopt->topictimemax) ||
+                                         (lopt->nolist && 
+                                          find_str_link(lopt->nolist, 
+                                                        chptr->chname)) ||
+                                         (lopt->yeslist && 
+                                          !find_str_link(lopt->yeslist, 
+                                                         chptr->chname))))
+                    continue;
+
+                /* Seem'd more efficent to seperate into two commands 
+                 * then adding an or to the inline. -- Doc.
+                 */
+                if (IsAdmin(cptr))
+                {
+                    char tempchname[CHANNELLEN + 2], *altchname;
+
+                    if (SecretChannel(chptr))
+                    {
+                        tempchname[0] = '%';
+                        strcpy(&tempchname[1], chptr->chname);
+                        altchname = &tempchname[0];
+                    } 
+                    else 
+                        altchname = chptr->chname;
+
+                    sendto_one(cptr, rpl_str(RPL_LIST), me.name, cptr->name,
+                               altchname, chptr->users, chptr->topic);
+                } 
+                else 
+                {
+                    sendto_one(cptr, rpl_str(RPL_LIST), me.name, cptr->name,
+                               ShowChannel(cptr, chptr) ? chptr->chname : "*",
+                               chptr->users,
+                               ShowChannel(cptr, chptr) ? chptr->topic : "");
+                }
+                numsend--;
+            }
+        }
+        else
+            break;
+    }
+    
+    /* All done */
+    if (hashnum == CH_MAX)
+    {
+        Link *lp, *next;
+        sendto_one(cptr, rpl_str(RPL_LISTEND), me.name, cptr->name);
+        for (lp = lopt->yeslist; lp; lp = next)
+        {
+            next = lp->next;
+            free_link(lp);
+        }
+        for (lp = lopt->nolist; lp; lp = next)
+        {
+            next = lp->next;
+            free_link(lp);
+        }
+        
+        MyFree(cptr->user->lopt);
+        cptr->user->lopt = NULL;
+        remove_from_list(&listing_clients, cptr, NULL);
+        return;
+    }
+    
+    /* 
+     * We've exceeded the limit on the number of channels to send back
+     * at once.
+     */
+    lopt->starthash = hashnum;
+    return;
+}
+
+
+/*
+ * m_list 
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ */
+int m_list(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aChannel    *chptr;
+    time_t      currenttime = time(NULL);
+    char        *name, *p = NULL;
+    LOpts       *lopt = NULL;
+    Link        *lp, *next;
+    int         usermax, usermin, error = 0, doall = 0, only_listed = 1;
+    int         x;
+    time_t      chantimemin, chantimemax;
+    ts_val      topictimemin, topictimemax;
+    Link        *yeslist = NULL, *nolist = NULL;
+    
+    static char *usage[] = {
+        "   Usage: /raw LIST options (on mirc) or /quote LIST options (ircII)",
+        "",
+        "If you don't include any options, the default is to send you the",
+        "entire unfiltered list of channels. Below are the options you can",
+        "use, and what channels LIST will return when you use them.",
+        ">number  List channels with more than <number> people.",
+        "<number  List channels with less than <number> people.",
+        "C>number List channels created between now and <number> minutes ago.",
+        "C<number List channels created earlier than <number> minutes ago.",
+        "T>number List channels whose topics are older than <number> minutes",
+        "         (Ie, they have not changed in the last <number> minutes.",
+        "T<number List channels whose topics are not older than <number> "
+        "minutes.",
+        "*mask*   List channels that match *mask*",
+        "!*mask*  List channels that do not match *mask*",
+        NULL
+    };
+
+    /* Some starting san checks -- No interserver lists allowed. */
+    if (cptr != sptr || !sptr->user) return 0;
+
+    if (IsSquelch(sptr)) 
+    {
+        sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
+        return 0;
+    }
+
+    /* If a /list is in progress, then another one will cancel it */
+    if ((lopt = sptr->user->lopt)!=NULL)
+    {
+        sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
+        for (lp = lopt->yeslist; lp; lp = next)
+        {
+            next = lp->next;
+            free_link(lp);
+        }
+        for (lp = lopt->nolist; lp; lp = next)
+        {
+            next = lp->next;
+            free_link(lp);
+        }
+        MyFree(sptr->user->lopt);
+        sptr->user->lopt = NULL;
+        remove_from_list(&listing_clients, sptr, NULL);
+        return 0;
+    }
+
+    if (parc < 2 || BadPtr(parv[1]))
+    {
+
+        sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]);
+        lopt = sptr->user->lopt = (LOpts *) MyMalloc(sizeof(LOpts));
+        memset(lopt, '\0', sizeof(LOpts));
+
+        lopt->showall = 1;
+#ifdef USE_CHANMODE_L
+        lopt->only_listed = 1;
+#endif
+
+        add_to_list(&listing_clients, sptr);
+
+        if (SBufLength(&cptr->sendQ) < 2048)
+            send_list(cptr, 64);
+
+        return 0;
+    }
+
+    if ((parc == 2) && (parv[1][0] == '?') && (parv[1][1] == '\0'))
+    {
+        char **ptr = usage;
+        for (; *ptr; ptr++)
+            sendto_one(sptr, rpl_str(RPL_COMMANDSYNTAX), me.name,
+                       cptr->name, *ptr);
+        return 0;
+    }
+
+    sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]);
+
+    chantimemax = topictimemax = currenttime + 86400;
+    chantimemin = topictimemin = 0;
+    usermin = 2;  /* By default, set the minimum to 2 users */
+    usermax = -1; /* No maximum */
+
+    for (name = strtoken(&p, parv[1], ","); name && !error;
+         name = strtoken(&p, (char *) NULL, ","))
+    {
+
+        switch (*name)
+        {
+            case '<':
+                usermax = atoi(name+1) - 1;
+                doall = 1;
+                break;
+            case '>':
+                usermin = atoi(name+1) + 1;
+                doall = 1;
+                break;
+#ifdef USE_CHANMODE_L
+            case '-':
+                if(!strcasecmp(++name,"all")) 
+                {
+                    only_listed = 0;
+                    doall = 1;
+                }
+                break;                          
+#endif
+            case 'C':
+            case 'c': /* Channel TS time -- creation time? */
+                ++name;
+                switch (*name++)
+                {
+                    case '<':
+                        chantimemax = currenttime - 60 * atoi(name);
+                        doall = 1;
+                        break;
+                    case '>':
+                        chantimemin = currenttime - 60 * atoi(name);
+                        doall = 1;
+                        break;
+                    default:
+                        sendto_one(sptr, err_str(ERR_LISTSYNTAX), me.name, 
+                                cptr->name);
+                        error = 1;
+                }
+                break;
+            case 'T':
+            case 't':
+                ++name;
+                switch (*name++)
+                {
+                    case '<':
+                        topictimemax = currenttime - 60 * atoi(name);
+                        doall = 1;
+                        break;
+                    case '>':
+                        topictimemin = currenttime - 60 * atoi(name);
+                        doall = 1;
+                        break;
+                    default:
+                        sendto_one(sptr, err_str(ERR_LISTSYNTAX), me.name, 
+                                cptr->name);
+                        error = 1;
+                }
+                break;
+            default: /* A channel, possibly with wildcards.
+                      * Thought for the future: Consider turning wildcard
+                      * processing on the fly.
+                      * new syntax: !channelmask will tell ircd to ignore
+                      * any channels matching that mask, and then
+                      * channelmask will tell ircd to send us a list of
+                      * channels only masking channelmask. Note: Specifying
+                      * a channel without wildcards will return that
+                      * channel even if any of the !channelmask masks
+                      * matches it.
+                      */
+                if (*name == '!')
+                {
+                    doall = 1;
+                    lp = make_link();
+                    lp->next = nolist;
+                    nolist = lp;
+                    DupString(lp->value.cp, name+1);
+                }
+                else if (strchr(name, '*') || strchr(name, '*'))
+                {
+                    doall = 1;
+                    lp = make_link();
+                    lp->next = yeslist;
+                    yeslist = lp;
+                    DupString(lp->value.cp, name);
+                }
+                else /* Just a normal channel */
+                {
+                    chptr = find_channel(name, NullChn);
+                    if (chptr && ((x = ShowChannel(sptr, chptr)) || 
+                                    IsAdmin(sptr)))
+                    {
+                        char *nameptr = name;
+                        char channame[CHANNELLEN + 2];
+
+                        if(!x && IsAdmin(sptr))
+                        {
+                            channame[0] = '%';
+                            strcpy(&channame[1], chptr->chname);
+                            nameptr = channame;
+                        }
+
+                        sendto_one(sptr, rpl_str(RPL_LIST), me.name, parv[0],
+                               nameptr, chptr->users, chptr->topic);
+                    }
+                }
+        } /* switch */
+    } /* while */
+
+    if (doall)
+    {
+        lopt = sptr->user->lopt = (LOpts *) MyMalloc(sizeof(LOpts));
+        memset(lopt, '\0', sizeof(LOpts));
+        lopt->usermin = usermin;
+        lopt->usermax = usermax;
+        lopt->topictimemax = topictimemax;
+        lopt->topictimemin = topictimemin;
+        lopt->chantimemax = chantimemax;
+        lopt->chantimemin = chantimemin;
+        lopt->nolist = nolist;
+        lopt->yeslist = yeslist;
+        lopt->only_listed = only_listed;
+
+        add_to_list(&listing_clients, sptr);
+
+        if (SBufLength(&cptr->sendQ) < 2048)
+            send_list(cptr, 64);
+        return 0;
+    }
+
+    sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
+
+    return 0;
+}
+
+
+
+/************************************************************************
+ * m_names() - Added by Jto 27 Apr 1989
+ * 12 Feb 2000 - geesh, time for a rewrite -lucas
+ ************************************************************************/
+/*
+ * m_names 
+ * parv[0] = sender prefix 
+ * parv[1] = channel
+ */
+
+/* maximum names para to show to opers when abuse occurs */
+#define TRUNCATED_NAMES 64
+
+int m_names(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int mlen = strlen(me.name) + NICKLEN + 7;
+    aChannel *chptr;
+    aClient *acptr;
+    int member;
+    chanMember *cm;
+    int idx, flag = 1, spos;
+    char *s, *para = parv[1];
+
+    if (parc < 2 || !MyConnect(sptr)) 
+    {
+        sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], "*");
+        return 0;
+    }
+
+    for(s = para; *s; s++) 
+    {
+        if(*s == ',') 
+        {
+            if(strlen(para) > TRUNCATED_NAMES)
+                para[TRUNCATED_NAMES] = '\0';
+            sendto_realops("names abuser %s %s", get_client_name(sptr, FALSE),
+                           para);
+            sendto_one(sptr, err_str(ERR_TOOMANYTARGETS), me.name, sptr->name,
+                       "NAMES");
+            return 0;
+        }
+    }
+
+    if(!check_channelname(sptr, (unsigned char *)para))
+        return 0;
+     
+    chptr = find_channel(para, (aChannel *) NULL);
+
+    if (!chptr || !ShowChannel(sptr, chptr))
+    {
+        sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], para);
+        return 0;
+    }
+
+    /* cache whether this user is a member of this channel or not */
+    member = IsMember(sptr, chptr);
+    
+    if(PubChannel(chptr))
+        buf[0] = '=';
+    else if(SecretChannel(chptr))
+        buf[0] = '@';
+    else
+        buf[0] = '*';
+
+    idx = 1;
+    buf[idx++] = ' ';
+    for(s = chptr->chname; *s; s++)
+        buf[idx++] = *s;
+    buf[idx++] = ' ';
+    buf[idx++] = ':';
+
+    /* If we go through the following loop and never add anything,
+       we need this to be empty, otherwise spurious things from the
+       LAST /names call get stuck in there.. - lucas */
+    buf[idx] = '\0';
+
+    spos = idx; /* starting point in buffer for names!*/
+
+    for (cm = chptr->members; cm; cm = cm->next) 
+    {
+        acptr = cm->cptr;
+        if(IsInvisible(acptr) && !member)
+            continue;
+        if(cm->flags & CHFL_CHANOP)
+            buf[idx++] = '@';
+        else if(cm->flags & CHFL_VOICE)
+            buf[idx++] = '+';
+        for(s = acptr->name; *s; s++)
+            buf[idx++] = *s;
+        buf[idx++] = ' ';
+        buf[idx] = '\0';
+        flag = 1;
+        if(mlen + idx + NICKLEN > BUFSIZE - 3)
+        {
+            sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf);
+            idx = spos;
+            flag = 0;
+        }
+    }
+
+    if (flag) 
+        sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf);
+    
+    sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], para);
+    
+    return 0;
+}
+void send_user_joins(aClient *cptr, aClient *user)
+{
+    Link   *lp;
+    aChannel *chptr;
+    int     cnt = 0, len = 0, clen;
+    char       *mask;
+
+    *buf = ':';
+    (void) strcpy(buf + 1, user->name);
+    (void) strcat(buf, " JOIN ");
+    len = strlen(user->name) + 7;
+
+    for (lp = user->user->channel; lp; lp = lp->next)
+    {
+        chptr = lp->value.chptr;
+        if (*chptr->chname == '&')
+            continue;
+        if ((mask = strchr(chptr->chname, ':')))
+            if (match(++mask, cptr->name))
+                continue;
+        clen = strlen(chptr->chname);
+        if (clen > (size_t) BUFSIZE - 7 - len)
+        {
+            if (cnt)
+                sendto_one(cptr, "%s", buf);
+            *buf = ':';
+            (void) strcpy(buf + 1, user->name);
+            (void) strcat(buf, " JOIN ");
+            len = strlen(user->name) + 7;
+            cnt = 0;
+        }
+        (void) strcpy(buf + len, chptr->chname);
+        cnt++;
+        len += clen;
+        if (lp->next)
+        {
+            len++;
+            (void) strcat(buf, ",");
+        }
+    }
+    if (*buf && cnt)
+        sendto_one(cptr, "%s", buf);
+
+    return;
+}
+
+void kill_ban_list(aClient *cptr, aChannel *chptr)
+{  
+    void        *pnx;
+    aBan        *bp;
+#ifdef EXEMPT_LISTS
+    aBanExempt  *ep;
+#endif
+#ifdef INVITE_LISTS
+    anInvite   *ip;
+#endif
+    char       *cp;
+    int         count = 0, send = 0;
+
+    cp = modebuf;  
+    *cp++ = '-';
+    *cp = '\0';      
+
+    *parabuf = '\0';
+
+    for (bp = chptr->banlist; bp; bp = bp->next)
+    {  
+        if (strlen(parabuf) + strlen(bp->banstr) + 10 < (size_t) MODEBUFLEN)
+        {  
+            if(*parabuf)
+                strcat(parabuf, " ");
+            strcat(parabuf, bp->banstr);
+            count++;   
+            *cp++ = 'b';
+            *cp = '\0';
+        }
+        else if (*parabuf)
+            send = 1;
+   
+        if (count == MAXMODEPARAMS)
+            send = 1;
+    
+        if (send) {
+            sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s",
+                         cptr->name, chptr->chname, modebuf, parabuf);
+            send = 0;
+            *parabuf = '\0';
+            cp = modebuf;
+            *cp++ = '-';
+            if (count != MAXMODEPARAMS)
+            {
+                strcpy(parabuf, bp->banstr);
+                *cp++ = 'b';
+                count = 1;
+            }
+            else
+                count = 0; 
+            *cp = '\0';
+        }
+    }
+
+#ifdef EXEMPT_LISTS
+    for (ep = chptr->banexempt_list; ep; ep = ep->next)
+    {
+        if (strlen(parabuf) + strlen(ep->banstr) + 10 < (size_t) MODEBUFLEN)
+        {
+            if(*parabuf)
+                strcat(parabuf, " ");
+            strcat(parabuf, ep->banstr);
+            count++;
+            *cp++ = 'e';
+            *cp = '\0';
+        }
+        else if (*parabuf)
+            send = 1;
+
+        if (count == MAXMODEPARAMS)
+            send = 1;
+
+        if (send) {
+            sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s",
+                                      cptr->name, chptr->chname, modebuf, parabuf);
+            send = 0;
+            *parabuf = '\0';
+            cp = modebuf;
+            *cp++ = '-';
+            if (count != MAXMODEPARAMS)
+            {
+                strcpy(parabuf, ep->banstr);
+                *cp++ = 'e';
+                count = 1;
+            }
+            else
+                count = 0;
+            *cp = '\0';
+        }
+    }
+#endif
+
+#ifdef INVITE_LISTS
+    for (ip = chptr->invite_list; ip; ip = ip->next)
+    {
+        if (strlen(parabuf) + strlen(ip->invstr) + 10 < (size_t) MODEBUFLEN)
+        {
+            if(*parabuf)
+                strcat(parabuf, " ");
+            strcat(parabuf, ip->invstr);
+            count++;
+            *cp++ = 'I';
+            *cp = '\0';
+        }
+        else if (*parabuf)
+            send = 1;
+
+        if (count == MAXMODEPARAMS)
+            send = 1;
+
+        if (send) {
+            sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s",
+                                      cptr->name, chptr->chname, modebuf, parabuf);
+            send = 0;
+            *parabuf = '\0';
+            cp = modebuf;
+            *cp++ = '-';
+            if (count != MAXMODEPARAMS)
+            {
+                strcpy(parabuf, ip->invstr);
+                *cp++ = 'I';
+                count = 1;
+            }
+            else
+                count = 0;
+            *cp = '\0';
+        }
+    }
+#endif
+
+    if(*parabuf)
+    {
+        sendto_channel_butserv_me(chptr, cptr, ":%s MODE %s %s %s", cptr->name,
+                                  chptr->chname, modebuf, parabuf);
+    }
+
+    /* physically destroy channel ban list */   
+
+    bp = chptr->banlist;
+    while(bp)
+    {
+        pnx = bp->next;
+        MyFree(bp->banstr);
+        MyFree(bp->who);
+        MyFree(bp);
+        bp = pnx;
+    }
+    chptr->banlist = NULL;
+
+#ifdef EXEMPT_LISTS
+    ep = chptr->banexempt_list;
+    while(ep)
+    {
+        pnx = ep->next;
+        MyFree(ep->banstr);
+        MyFree(ep->who);
+        MyFree(ep);
+        ep = pnx;
+    }
+    chptr->banexempt_list = NULL;
+#endif
+
+#ifdef INVITE_LISTS
+    ip = chptr->invite_list;
+    while(ip)
+    {
+        pnx = ip->next;
+        MyFree(ip->invstr);
+        MyFree(ip->who);
+        MyFree(ip);
+        ip = pnx;
+    }
+    chptr->invite_list = NULL;
+#endif
+
+    /* reset bquiet cache */
+    chptr->banserial++;
+}
+
+static inline void sjoin_sendit(aClient *cptr, aClient *sptr,
+                                aChannel *chptr, char *from)
+{
+    sendto_channel_butserv_me(chptr, sptr, ":%s MODE %s %s %s", from,
+                              chptr->chname, modebuf, parabuf);
+}
+
+/* m_resynch
+ *
+ * parv[0] = sender
+ * parv[1] = #channel
+ *
+ * Sent from a server I am directly connected to that is requesting I resend
+ * EVERYTHING I know about #channel.
+ */
+int m_resynch(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aChannel *chptr;
+
+    if(!MyConnect(sptr) || !IsServer(sptr) || parc < 2)
+        return 0;
+
+    chptr = find_channel(parv[1], NullChn);
+
+    sendto_realops_lev(DEBUG_LEV, "%s is requesting a resynch of %s%s", 
+                       parv[0], parv[1], (chptr == NullChn) ? " [failed]" : "");
+
+    if (chptr != NullChn)
+        send_channel_modes(sptr, chptr);
+    return 0;
+}
+
+/*
+ * m_sjoin 
+ * parv[0] - sender 
+ * parv[1] - TS 
+ * parv[2] - channel 
+ * parv[3] - modes + n arguments (key and/or limit) 
+ * parv[4+n] - flags+nick list (all in one parameter)
+ * 
+ * process a SJOIN, taking the TS's into account to either ignore the
+ * incoming modes or undo the existing ones or merge them, and JOIN all
+ * the specified users while sending JOIN/MODEs to non-TS servers and
+ * to clients
+ */
+
+#define INSERTSIGN(x,y) \
+if (what != x) { \
+*mbuf++=y; \
+what = x; \
+}
+
+#define SJ_MODEPLUS(x, y) \
+   if(((y) & mode.mode) && !((y) & oldmode->mode)) \
+   { \
+      INSERTSIGN(1, '+') \
+      *mbuf++ = (x); \
+   }
+
+#define SJ_MODEMINUS(x, y) \
+   if(((y) & oldmode->mode) && !((y) & mode.mode)) \
+   { \
+      INSERTSIGN(-1, '-') \
+      *mbuf++ = (x); \
+   }
+
+#define SJ_MODEADD(x, y) case (x): mode.mode |= (y); break
+
+#define ADD_PARA(p) para = p; if(pbpos) parabuf[pbpos++] = ' '; \
+                     while(*para) parabuf[pbpos++] = *para++; 
+#define ADD_SJBUF(p) para = p; if(sjbufpos) sjbuf[sjbufpos++] = ' '; \
+                     while(*para) sjbuf[sjbufpos++] = *para++; 
+        
+int m_sjoin(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aChannel    *chptr;
+    aClient     *acptr;
+    ts_val      newts, oldts, tstosend;
+    static Mode mode, *oldmode;
+    chanMember  *cm;
+    int         args = 0, haveops = 0, keepourmodes = 1, keepnewmodes = 1,
+                doesop = 0, what = 0, pargs = 0, fl, people = 0,
+                isnew, clientjoin = 0, pbpos, sjbufpos, created = 0;
+    char        *s, *s0, *para;
+    static char numeric[16], sjbuf[BUFSIZE];
+    char        keep_modebuf[REALMODEBUFLEN], keep_parabuf[REALMODEBUFLEN];
+    char        *mbuf = modebuf, *p;
+
+    /* if my client is SJOINing, it's just a local user being a dufus. 
+     *  Ignore him.
+     * parc >= 5 (new serv<->serv SJOIN format)
+     * parc >= 6 (old serv<->serv SJOIN format)
+     * parc == 3 (new serv<->serv cliSJOIN format)
+     */
+
+    if (MyClient(sptr) || (parc < 5 && IsServer(sptr)) ||
+        (parc < 3 && IsPerson(sptr)))
+        return 0;
+    
+    if(parc == 3 && IsPerson(sptr))
+        clientjoin = 1;
+    else 
+        if(IsDigit(parv[2][0]))
+        {
+            int i;
+            
+            if(parc < 6) 
+                return 0;
+            
+            for(i = 2; i < (parc - 1); i++)
+                parv[i] = parv[i+1];
+
+            parc--;
+        }
+
+    if (!IsChannelName(parv[2]))
+        return 0;
+
+    newts = atol(parv[1]);
+        
+    isnew = ChannelExists(parv[2]) ? 0 : 1;
+    chptr = get_channel(sptr, parv[2], CREATE, &created);
+    oldts = chptr->channelts;
+
+    for (cm = chptr->members; cm; cm = cm->next)
+        if (cm->flags & MODE_CHANOP)
+        {
+            haveops++;
+            break;
+        }
+
+    if(clientjoin) /* we have a good old client sjoin, with timestamp */
+    {
+        if (isnew)
+            chptr->channelts = tstosend = newts;
+        else if (newts == 0 || oldts == 0)
+            chptr->channelts = tstosend = 0;
+        else if (newts == oldts)
+            tstosend = oldts;
+        else if (newts < oldts) 
+        {
+#ifdef OLD_WEIRD_CHANOP_NEGOTIATION
+            if (haveops)
+                tstosend = oldts;
+            else
+                chptr->channelts = tstosend = newts;
+#else
+            chptr->channelts = tstosend = newts;
+            sendto_realops_lev(DEBUG_LEV, "Changing TS for %s from %d to %d on"
+                               " client SJOIN", chptr->chname, oldts, newts);
+#endif
+        }
+        else 
+            tstosend = oldts;
+
+        /* parv[0] is the client that is joining. parv[0] == sptr->name */
+
+        if (!IsMember(sptr, chptr)) 
+        {
+            add_user_to_channel(chptr, sptr, 0);
+            chptr->join_count++;
+            chptr->default_join_count++;
+            sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0],
+                                   parv[2]);
+        }
+
+        sendto_serv_butone(cptr, CliSJOINFmt, parv[0], tstosend, parv[2]);
+
+        /* if the channel is created in client sjoin, 
+         * we lost some channel modes. */
+        if(created)
+        {
+            sendto_realops_lev(DEBUG_LEV, "Requesting resynch of %s from "
+                                "%s (%s!%s@%s[%s] created)", chptr->chname, 
+                                cptr->name, sptr->name, sptr->user->username,
+                                sptr->user->host, sptr->hostip);
+            sendto_one(cptr, "RESYNCH %s", chptr->chname);
+        }
+
+        return 0;
+    }
+
+    memset((char *) &mode, '\0', sizeof(mode));
+
+    s = parv[3];
+    while (*s)
+    {
+        switch (*(s++))
+        {
+            SJ_MODEADD('i', MODE_INVITEONLY);
+            SJ_MODEADD('n', MODE_NOPRIVMSGS);
+            SJ_MODEADD('p', MODE_PRIVATE);
+            SJ_MODEADD('s', MODE_SECRET);
+            SJ_MODEADD('m', MODE_MODERATED);
+            SJ_MODEADD('t', MODE_TOPICLIMIT);
+            SJ_MODEADD('r', MODE_REGISTERED);
+            SJ_MODEADD('R', MODE_REGONLY);
+            SJ_MODEADD('M', MODE_MODREG);
+            SJ_MODEADD('c', MODE_NOCOLOR);
+            SJ_MODEADD('O', MODE_OPERONLY);
+#ifdef USE_CHANMODE_L
+            SJ_MODEADD('L', MODE_LISTED);
+#endif
+            case 'k':
+                strncpyzt(mode.key, parv[4 + args], KEYLEN + 1);
+                args++;
+                if (parc < 5 + args)
+                    return 0;
+                break;
+
+            case 'j':
+                {
+                    char *tmpa, *tmpb;
+
+                    mode.mode |= MODE_JOINRATE;
+                    tmpa = parv[4 + args];
+
+                    tmpb = strchr(tmpa, ':');
+                    if(tmpb)
+                    {
+                        *tmpb = '\0';
+                        tmpb++;
+                        mode.join_time = atoi(tmpb);
+                    }
+                    else
+                        mode.join_time = 0;
+
+                    mode.join_num = atoi(tmpa);
+
+                    args++;
+                    if (parc < 5 + args)
+                        return 0;
+                }
+                break;
+
+            case 'l':
+                mode.limit = atoi(parv[4 + args]);
+                args++;
+                if (parc < 5 + args)
+                    return 0;
+                break;
+        }
+    }
+
+    doesop = (parv[4 + args][0] == '@' || parv[4 + args][1] == '@');
+
+    oldmode = &chptr->mode;
+
+    /* newts is the ts the remote server is providing */ 
+    /* oldts is our channel TS */ 
+    /* whichever TS is smaller wins. */ 
+        
+    if (isnew)
+        chptr->channelts = tstosend = newts;
+    else if (newts == 0 || oldts == 0)
+        chptr->channelts = tstosend = 0;
+    else if (newts == oldts)
+        tstosend = oldts;
+#ifdef OLD_WEIRD_CHANOP_NEGOTIATION
+    else if (newts < oldts)
+    {
+        /* if remote ts is older, and they have ops, don't keep our modes. */
+        if (doesop)   
+        {
+            kill_ban_list(sptr, chptr);
+            keepourmodes = 0;
+        }
+        if (haveops && !doesop)
+            tstosend = oldts;
+        else
+            chptr->channelts = tstosend = newts;
+    }
+    else /* if our TS is older, and we have ops, don't keep their modes */
+    {
+        if (haveops)
+            keepnewmodes = 0;
+        if (doesop && !haveops)
+        {
+            chptr->channelts = tstosend = newts;
+            if (MyConnect(sptr) && !IsULine(sptr))
+                ts_warn("Hacked ops on opless channel: %s", chptr->chname);
+        }
+        else
+            tstosend = oldts;
+    }
+#else 
+   else if (newts < oldts) 
+   { 
+      /* if remote ts is older, don't keep our modes. */ 
+      kill_ban_list(sptr, chptr);
+      keepourmodes = 0; 
+      chptr->channelts = tstosend = newts; 
+   } 
+   else /* if our TS is older, don't keep their modes */ 
+   { 
+      keepnewmodes = 0; 
+      tstosend = oldts; 
+   } 
+#endif
+        
+    if (!keepnewmodes)
+        mode = *oldmode;
+    else if (keepourmodes)
+    {
+        mode.mode |= oldmode->mode;
+        if (oldmode->limit > mode.limit)
+            mode.limit = oldmode->limit;
+        if(*oldmode->key && *mode.key && strcmp(mode.key, oldmode->key) > 0)
+            strcpy(mode.key, oldmode->key);
+        else if(*oldmode->key && *mode.key == '\0')
+            strcpy(mode.key, oldmode->key);
+        if ((oldmode->mode & MODE_JOINRATE) && mode.join_num)
+        {
+            /* 0 wins */
+            if (!oldmode->join_num)
+            {
+                mode.join_num = oldmode->join_num;
+                mode.join_time = oldmode->join_time;
+            }
+            /* more joins or same joins in less time wins */
+            else if (oldmode->join_num > mode.join_num ||
+                     (oldmode->join_num == mode.join_num &&
+                      oldmode->join_time < mode.join_time))
+            {
+                mode.join_num = oldmode->join_num;
+                mode.join_time = oldmode->join_time;
+            }
+        }
+    }
+    
+    pbpos = 0;
+    
+    /*
+     * since the most common case is that the modes are exactly the same,
+     *  this if will skip over the most common case... :)
+     * 
+     * this would look prettier in a for loop, but it's unrolled here
+     *  so it's a bit faster.   - lucas
+     * 
+     * pass +: go through and add new modes that are in mode and not oldmode
+     * pass -: go through and delete old modes that are in oldmode and not mode
+     */
+    
+    if(mode.mode != oldmode->mode)
+    {
+        SJ_MODEPLUS('p', MODE_PRIVATE);
+        SJ_MODEPLUS('s', MODE_SECRET);
+        SJ_MODEPLUS('m', MODE_MODERATED);
+        SJ_MODEPLUS('n', MODE_NOPRIVMSGS);
+        SJ_MODEPLUS('t', MODE_TOPICLIMIT);
+        SJ_MODEPLUS('i', MODE_INVITEONLY);
+        SJ_MODEPLUS('r', MODE_REGISTERED);
+        SJ_MODEPLUS('R', MODE_REGONLY);
+        SJ_MODEPLUS('M', MODE_MODREG);
+        SJ_MODEPLUS('c', MODE_NOCOLOR);
+        SJ_MODEPLUS('O', MODE_OPERONLY);
+#ifdef USE_CHANMODE_L
+        SJ_MODEPLUS('L', MODE_LISTED);
+#endif
+
+        SJ_MODEMINUS('p', MODE_PRIVATE);
+        SJ_MODEMINUS('s', MODE_SECRET);
+        SJ_MODEMINUS('m', MODE_MODERATED);
+        SJ_MODEMINUS('n', MODE_NOPRIVMSGS);
+        SJ_MODEMINUS('t', MODE_TOPICLIMIT);
+        SJ_MODEMINUS('i', MODE_INVITEONLY);
+        SJ_MODEMINUS('r', MODE_REGISTERED);
+        SJ_MODEMINUS('R', MODE_REGONLY);
+        SJ_MODEMINUS('M', MODE_MODREG);
+        SJ_MODEMINUS('c', MODE_NOCOLOR);
+        SJ_MODEMINUS('O', MODE_OPERONLY);
+#ifdef USE_CHANMODE_L
+        SJ_MODEMINUS('L', MODE_LISTED);
+#endif
+
+    }
+
+    if ((oldmode->mode & MODE_JOINRATE) && !(mode.mode & MODE_JOINRATE))
+    {
+        INSERTSIGN(-1,'-')
+        *mbuf++ = 'j';
+    }
+
+    if ((mode.mode & MODE_JOINRATE) && (!(oldmode->mode & MODE_JOINRATE) ||
+            (oldmode->join_num != mode.join_num || 
+            oldmode->join_time != mode.join_time)))
+    {
+        char tmp[128];
+
+        INSERTSIGN(1,'+')
+        *mbuf++ = 'j';
+        
+        if(mode.join_num == 0 || mode.join_time == 0)
+            ircsprintf(tmp, "0");
+        else
+            ircsprintf(tmp, "%d:%d", mode.join_num, mode.join_time);
+        ADD_PARA(tmp)
+        pargs++;
+    }
+
+    if (oldmode->limit && !mode.limit)
+    {
+        INSERTSIGN(-1,'-')
+        *mbuf++ = 'l';
+    }
+
+    if (mode.limit && oldmode->limit != mode.limit)
+    {
+        INSERTSIGN(1,'+')
+        *mbuf++ = 'l';
+        sprintf(numeric, "%-15d", mode.limit);
+        if ((s = strchr(numeric, ' ')))
+        *s = '\0';
+        ADD_PARA(numeric);
+        pargs++;
+    }
+
+    if (oldmode->key[0] && !mode.key[0])
+    {
+        INSERTSIGN(-1,'-')
+        *mbuf++ = 'k';
+        ADD_PARA(oldmode->key)
+        pargs++;
+    }
+
+    if (mode.key[0] && strcmp(oldmode->key, mode.key))
+    {
+        INSERTSIGN(1,'+')
+        *mbuf++ = 'k';
+        ADD_PARA(mode.key)
+        pargs++;
+    }
+        
+    chptr->mode = mode;
+        
+    if (!keepourmodes) /* deop and devoice everyone! */
+    {
+        what = 0;
+        for (cm = chptr->members; cm; cm = cm->next) 
+        {
+            if (cm->flags & MODE_CHANOP) 
+            {
+                INSERTSIGN(-1,'-')
+                    *mbuf++ = 'o';
+                ADD_PARA(cm->cptr->name)
+                    pargs++;
+                if (pargs >= MAXMODEPARAMS) 
+                {
+                    *mbuf = '\0';
+                    parabuf[pbpos] = '\0';
+                    sjoin_sendit(cptr, sptr, chptr, parv[0]);
+                    mbuf = modebuf;
+                    *mbuf = '\0';
+                    pargs = pbpos = what = 0;
+                }
+                cm->flags &= ~MODE_CHANOP;
+            }
+
+            if (cm->flags & MODE_VOICE) 
+            {
+                INSERTSIGN(-1,'-')
+                    *mbuf++ = 'v';
+                ADD_PARA(cm->cptr->name)
+                    pargs++;
+                if (pargs >= MAXMODEPARAMS) 
+                {
+                    *mbuf = '\0';
+                    parabuf[pbpos] = '\0';
+                    sjoin_sendit(cptr, sptr, chptr, parv[0]);
+                    mbuf = modebuf;
+                    *mbuf = '\0';
+                    pargs = pbpos = what = 0;
+                }
+                cm->flags &= ~MODE_VOICE;
+            }
+        }
+        /* 
+         * We know if we get here, and we're in a sync, we haven't sent our topic 
+         * to sptr yet. (since topic burst is sent after sjoin burst finishes) 
+         */ 
+        if(chptr->topic[0]) 
+        { 
+            chptr->topic[0] = '\0'; 
+            sendto_channel_butserv_me(chptr, sptr, ":%s TOPIC %s :%s", 
+                                sptr->name, chptr->chname, chptr->topic);
+        }
+        sendto_channel_butserv(chptr, &me,
+                               ":%s NOTICE %s :*** Notice -- TS for %s "
+                               "changed from %ld to %ld",
+                               me.name, chptr->chname, chptr->chname,
+                               oldts, newts);
+    }
+    
+    if (mbuf != modebuf) 
+    {
+        *mbuf = '\0';
+        parabuf[pbpos] = '\0';
+        sjoin_sendit(cptr, sptr, chptr, parv[0]);
+    }
+    
+    *modebuf = '\0';
+    parabuf[0] = '\0';
+    if (parv[3][0] != '0' && keepnewmodes)
+        channel_modes(sptr, modebuf, parabuf, chptr);
+    else 
+    {
+        modebuf[0] = '0';
+        modebuf[1] = '\0';
+    }
+    
+    /* We do this down below now, so we can send out for two sjoin formats.
+     * sprintf(t, ":%s SJOIN %ld %ld %s %s %s :", parv[0], tstosend, tstosend,
+     *                    parv[2], modebuf, parabuf);
+     * t += strlen(t);
+     * the pointer "t" has been removed and is now replaced with an 
+     * index into sjbuf for faster appending
+     */
+    
+    strcpy(keep_modebuf, modebuf);
+    strcpy(keep_parabuf, parabuf);
+    
+    sjbufpos = 0;
+    mbuf = modebuf;
+    pbpos = 0;
+    pargs = 0;
+    *mbuf++ = '+';
+    
+    for (s = s0 = strtoken(&p, parv[args + 4], " "); s;
+         s = s0 = strtoken(&p, (char *) NULL, " ")) 
+    {
+        fl = 0;
+        if (*s == '@' || s[1] == '@')
+            fl |= MODE_CHANOP;
+        if (*s == '+' || s[1] == '+')
+            fl |= MODE_VOICE;
+        if (!keepnewmodes) 
+        {
+            if (fl & MODE_CHANOP)
+                fl = MODE_DEOPPED;
+            else
+                fl = 0;
+        }
+        while (*s == '@' || *s == '+')
+            s++;
+        if (!(acptr = find_chasing(sptr, s, NULL)))
+            continue;
+        if (acptr->from != cptr)
+            continue;
+        people++;
+        if (!IsMember(acptr, chptr)) 
+        {
+            add_user_to_channel(chptr, acptr, fl);
+            sendto_channel_butserv(chptr, acptr, ":%s JOIN :%s", s, parv[2]);
+        }
+        if (keepnewmodes)
+        {
+            ADD_SJBUF(s0)
+        }
+        else
+        {
+            ADD_SJBUF(s)
+        }
+        if (fl & MODE_CHANOP) 
+        {
+            *mbuf++ = 'o';
+            ADD_PARA(s)
+            pargs++;
+            if (pargs >= MAXMODEPARAMS) 
+            {
+                *mbuf = '\0';
+                parabuf[pbpos] = '\0';
+                sjoin_sendit(cptr, sptr, chptr, parv[0]);
+                mbuf = modebuf;
+                *mbuf++ = '+';
+                pargs = pbpos = 0;
+            }
+        }
+        if (fl & MODE_VOICE) 
+        {
+            *mbuf++ = 'v';
+            ADD_PARA(s)
+            pargs++;
+            if (pargs >= MAXMODEPARAMS) 
+            {
+                *mbuf = '\0';
+                parabuf[pbpos] = '\0';
+                sjoin_sendit(cptr, sptr, chptr, parv[0]);
+                mbuf = modebuf;
+                *mbuf++ = '+';
+                pargs = pbpos = 0;
+            }
+        }
+    }
+
+    parabuf[pbpos] = '\0';
+
+    *mbuf = '\0';
+    if (pargs)
+        sjoin_sendit(cptr, sptr, chptr, parv[0]);
+    if (people) 
+    {
+        sjbuf[sjbufpos] = '\0';
+
+        if(keep_parabuf[0] != '\0')
+            sendto_serv_butone(cptr, SJOINFmt, parv[0], tstosend, parv[2],
+                               keep_modebuf, keep_parabuf, sjbuf);
+        else
+            sendto_serv_butone(cptr, SJOINFmtNP, parv[0], tstosend, parv[2],
+                               keep_modebuf, sjbuf);
+    }
+    else if(created && chptr->users == 0) 
+       sub1_from_channel(chptr);
+    return 0;
+}
+#undef INSERTSIGN
+#undef ADD_PARA
+#undef ADD_SJBUF
+
+/* m_samode - Just bout the same as df
+ *  - Raistlin 
+ * parv[0] = sender
+ * parv[1] = channel
+ * parv[2] = modes
+ */
+int m_samode(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aChannel *chptr;
+
+    if (!MyClient(sptr))
+        return 0;
+
+    if (!IsAnOper(sptr) || !IsSAdmin(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    if(parc < 3)
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "SAMODE");
+        return 0;
+    }
+
+    if((chptr = find_channel(parv[1], NullChn)) == NullChn)
+        return 0;
+
+    if(!check_channelname(sptr, (unsigned char *)parv[1]))
+        return 0;
+
+    set_mode(cptr, sptr, chptr, 2, parc - 2, parv + 2, modebuf, parabuf);
+        
+    if (strlen(modebuf) > (size_t)1)
+    {
+        sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s",
+                               parv[0], chptr->chname, modebuf, parabuf);
+        sendto_serv_butone(cptr, ":%s MODE %s 0 %s %s", parv[0], chptr->chname,
+                           modebuf, parabuf);
+        if(MyClient(sptr))
+        {
+            sendto_serv_butone(NULL, ":%s GLOBOPS :%s used SAMODE (%s %s%s%s)",
+                               me.name, sptr->name, chptr->chname, modebuf,
+                               (*parabuf!=0 ? " " : ""), parabuf);
+            send_globops("from %s: %s used SAMODE (%s %s%s%s)",
+                         me.name, sptr->name, chptr->chname, modebuf, 
+                         (*parabuf!=0 ? " " : ""), parabuf);
+        }
+    }
+    return 0;
+}
+
+char  *pretty_mask(char *mask)
+{
+    char  *cp, *user, *host;
+    
+    if ((user = strchr((cp = mask), '!')))
+        *user++ = '\0';
+    if ((host = strrchr(user ? user : cp, '@')))
+    {
+        *host++ = '\0';
+        if (!user)
+            return make_nick_user_host(NULL, cp, host);
+    }
+    else if (!user && strchr(cp, '.'))
+        return make_nick_user_host(NULL, NULL, cp);
+    return make_nick_user_host(cp, user, host);
+}
diff --git a/src/clientlist.c b/src/clientlist.c
new file mode 100644 (file)
index 0000000..3368775
--- /dev/null
@@ -0,0 +1,105 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/clientlist.c
+ *   Copyright (C) 2003 Lucas Madar
+ */
+
+/* $Id: clientlist.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "numeric.h"
+#include "blalloc.h"
+
+DLink *server_list = NULL;
+DLink *oper_list = NULL;
+
+/* Clients currently doing a /list */
+DLink *listing_clients = NULL;
+DLink *recvq_clients = NULL;
+
+int get_list_memory(DLink *list)
+{
+   DLink *lp;
+   int count = 0;
+
+   for(lp = list; lp; lp = lp->next)
+      count++;
+
+   return count;
+}
+
+void print_list_memory(aClient *cptr)
+{
+   int lc;
+
+   lc = get_list_memory(server_list);
+   sendto_one(cptr, ":%s %d %s :   server_list %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, lc, lc * sizeof(DLink));
+
+   lc = get_list_memory(oper_list);
+   sendto_one(cptr, ":%s %d %s :   oper_list %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, lc, lc * sizeof(DLink));
+
+   lc = get_list_memory(listing_clients);
+   sendto_one(cptr, ":%s %d %s :   listing_clients %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, lc, lc * sizeof(DLink));
+
+   lc = get_list_memory(recvq_clients);
+   sendto_one(cptr, ":%s %d %s :   recvq_clients %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, lc, lc * sizeof(DLink));
+}
+
+DLink *add_to_list(DLink **list, void *ptr) 
+{
+   DLink *lp = make_dlink();
+  
+   lp->value.cp = (char *) ptr;
+   lp->next = *list;
+   lp->prev = NULL;
+   if(lp->next)
+      lp->next->prev = lp;
+   *list = lp;
+
+   return lp;
+}
+
+static inline void remove_dlink_list(DLink **list, DLink *link)
+{
+   if(link->next)
+     link->next->prev = link->prev;
+
+   if(link->prev)
+      link->prev->next = link->next;
+   else
+   {
+      *list = link->next;
+      if(*list)
+         (*list)->prev = NULL;
+   }
+
+   free_dlink(link);
+}
+
+void remove_from_list(DLink **list, void *ptr, DLink *link)
+{
+   DLink *lp;
+
+   if(link)
+   {
+      remove_dlink_list(list, link);
+      return;
+   }
+
+   for(lp = *list; lp; lp = lp->next)
+   {
+      if(lp->value.cp == (char *) ptr)
+      {
+         remove_dlink_list(list, lp);
+         return;
+      }
+   }
+
+   sendto_realops("remove_from_list(%x, %x) failed!!", (int) list, (int) ptr);
+}
diff --git a/src/clones.c b/src/clones.c
new file mode 100644 (file)
index 0000000..0b1760b
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ *   clones.c - Clone detection and limiting
+ *   Copyright (C) 2004 Trevor Talbot and
+ *                      the DALnet coding team
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: clones.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+/*
+ * WARNING: code is chummy with throttle.c
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "blalloc.h"
+#include "numeric.h"
+#include "channel.h"
+
+#include "throttle.h"
+#include "clones.h"
+
+
+#ifndef THROTTLE_ENABLE
+extern BlockHeap *hashent_freelist;
+#endif
+
+
+static void *clones_hashtable;
+
+BlockHeap *free_cloneents;
+CloneEnt  *clones_list;
+CloneStat  clones_stat;
+
+
+static CloneEnt *
+get_clone(char *key, int create)
+{
+    CloneEnt *ce;
+
+    if (!(ce = hash_find(clones_hashtable, key)) && create)
+    {
+        ce = BlockHeapALLOC(free_cloneents, CloneEnt);
+        memset(ce, 0, sizeof(*ce));
+        strcpy(ce->ent, key);
+        hash_insert(clones_hashtable, ce);
+        ce->next = clones_list;
+        if (clones_list)
+            clones_list->prev = ce;
+        clones_list = ce;
+    }
+
+    return ce;
+}
+
+static void
+expire_clone(CloneEnt *ce)
+{
+    if (ce->gcount || ce->limit || ce->sllimit || ce->sglimit)
+        return;
+
+    if (ce->next)
+        ce->next->prev = ce->prev;
+    if (ce->prev)
+        ce->prev->next = ce->next;
+    else
+        clones_list = ce->next;
+    hash_delete(clones_hashtable, ce);
+    BlockHeapFree(free_cloneents, ce);
+}
+
+
+#ifdef THROTTLE_ENABLE
+static void
+get_clones(aClient *cptr, CloneEnt **ceip, CloneEnt **ce24, int create)
+{
+    char ip24[HOSTIPLEN+1];
+    char *s;
+
+    strcpy(ip24, cptr->hostip);
+    /* deliberate core if strrchr fails -- we need a valid IP string */
+    s = strrchr(ip24, '.');
+    *++s = '*';
+    *++s = 0;
+
+    *ceip = get_clone(cptr->hostip, create);
+    *ce24 = get_clone(ip24, create);
+}
+
+static int
+report_lclone(aClient *cptr, CloneEnt *ce, int l, int is24, char *t, char *n)
+{
+    if (n)
+        sendto_realops_lev(REJ_LEV, "clone %s!%s@%s (%s %d/%d local %s %s)",
+                           cptr->name, cptr->user->username, cptr->user->host,
+                           ce->ent, ce->lcount, l, t, n);
+    else
+        sendto_realops_lev(REJ_LEV, "clone %s!%s@%s (%s %d/%d local %s)",
+                           cptr->name, cptr->user->username, cptr->user->host,
+                           ce->ent, ce->lcount, l, t);
+
+    if (is24)
+        clones_stat.rls++;
+    else
+        clones_stat.rlh++;
+
+    throttle_force(cptr->hostip);
+
+    return (is24 ? 2 : 1);
+}
+
+static int
+report_gclone(aClient *cptr, CloneEnt *ce, int l, int is24, char *t)
+{
+    sendto_realops_lev(REJ_LEV, "clone %s!%s@%s (%s %d/%d global %s)",
+                       cptr->name, cptr->user->username, cptr->user->host,
+                       ce->ent, ce->gcount, l, t);
+    
+    if (is24)
+        clones_stat.rgs++;
+    else
+        clones_stat.rgh++;
+
+    throttle_force(cptr->hostip);
+
+    return (is24 ? 2 : 1);
+}
+
+/*
+ * Checks a local client against the clone limits.
+ * Returns 1 if IP/32 limit hit, 2 if IP/24 limit hit, 0 otherwise.
+ */
+int
+clones_check(aClient *cptr)
+{
+    CloneEnt *ceip;
+    CloneEnt *ce24;
+    int       limit;
+    int       lpri = 0;
+    int       gpri = 0;
+
+    get_clones(cptr, &ceip, &ce24, 0);
+
+    if (ceip)
+    {
+        /* local limit priority stack: soft set, class, default */
+        if ((limit = ceip->sllimit))
+        {
+            lpri = 3;
+            if (ceip->lcount >= limit)
+                return report_lclone(cptr, ceip, limit, 0, "soft", NULL);
+        }
+        else if ((limit = cptr->user->allow->class->connfreq))
+        {
+            lpri = 2;
+            if (ceip->lcount >= limit)
+                return report_lclone(cptr, ceip, limit, 0, "class",
+                                     cptr->user->allow->class->name);
+        }
+        else
+        {
+            lpri = 1;
+            limit = local_ip_limit;
+            if (ceip->lcount >= limit)
+                return report_lclone(cptr, ceip, limit, 0, "default", NULL);
+        }
+
+        /* global limit priority stack: soft set, services, default */
+        if ((limit = ceip->sglimit))
+        {
+            gpri = 3;
+            if (ceip->gcount >= limit)
+                return report_gclone(cptr, ceip, limit, 0, "soft");
+        }
+        else if ((limit = ceip->limit))
+        {
+            gpri = 2;
+            if (ceip->gcount >= limit)
+                return report_gclone(cptr, ceip, limit, 0, "hard");
+        }
+        else
+        {
+            gpri = 1;
+            limit = global_ip_limit;
+            if (ceip->gcount >= limit)
+                return report_gclone(cptr, ceip, limit, 0, "default");
+        }
+    }
+
+    if (ce24)
+    {
+        /* For local limits, a specific host limit provides an implicit
+         * exemption from site limits of a lower priority. */
+        if ((limit = ce24->sllimit))
+        {
+            if (ce24->lcount >= limit)
+                return report_lclone(cptr, ce24, limit, 1, "soft", NULL);
+        }
+        else if (lpri <= 2 && (limit = cptr->user->allow->class->ip24clones))
+        {
+            if (ce24->lcount >= limit)
+                return report_lclone(cptr, ce24, limit, 1, "class",
+                                     cptr->user->allow->class->name);
+        }
+        else if (lpri <= 1)
+        {
+            limit = local_ip24_limit;
+            if (ce24->lcount >= limit)
+                return report_lclone(cptr, ce24, limit, 1, "default", NULL);
+        }
+
+        /* For global limits, the implicit exemption is for the default only;
+         * the soft limit can only be lower, not higher, so the service-set
+         * hard limit wins if it's not present. */
+        if ((limit = ce24->sglimit))
+        {
+            if (ce24->gcount >= limit)
+                return report_gclone(cptr, ce24, limit, 1, "soft");
+        }
+        else if ((limit = ce24->limit))
+        {
+            if (ce24->gcount >= limit)
+                return report_gclone(cptr, ce24, limit, 1, "hard");
+        }
+        else if (gpri <= 1)
+        {
+            limit = global_ip24_limit;
+            if (ce24->gcount >= limit)
+                return report_gclone(cptr, ce24, limit, 1, "default");
+        }
+    }
+    
+    return 0;
+}
+
+/*
+ * Adds a client to the clone list.
+ */
+void
+clones_add(aClient *cptr)
+{
+    CloneEnt *ceip;
+    CloneEnt *ce24;
+
+    get_clones(cptr, &ceip, &ce24, 1);
+
+    cptr->clone.prev = NULL;
+    cptr->clone.next = ceip->clients;
+    if (ceip->clients)
+        ceip->clients->clone.prev = cptr;
+    ceip->clients = cptr;
+
+    ceip->gcount++;
+    ce24->gcount++;
+
+    if (MyConnect(cptr))
+    {
+        ceip->lcount++;
+        ce24->lcount++;
+    }
+}
+
+/*
+ * Removes a client from the clone list.
+ */
+void
+clones_remove(aClient *cptr)
+{
+    CloneEnt *ceip;
+    CloneEnt *ce24;
+
+    get_clones(cptr, &ceip, &ce24, 0);
+
+    if (cptr->clone.next)
+        cptr->clone.next->clone.prev = cptr->clone.prev;
+    if (cptr->clone.prev)
+        cptr->clone.prev->clone.next = cptr->clone.next;
+    else
+        ceip->clients = cptr->clone.next;
+
+    ceip->gcount--;
+    ce24->gcount--;
+
+    /* !$%#&*%@ user state handling! */
+    if (cptr->uplink == &me)
+    {
+        ceip->lcount--;
+        ce24->lcount--;
+    }
+
+    expire_clone(ceip);
+    expire_clone(ce24);
+}
+#endif  /* THROTTLE_ENABLE */
+
+/*
+ * Sets a global clone limit.  A limit of 0 reverts to default settings.
+ * Returns -1 on invalid parameters, old value otherwise.
+ */
+int
+clones_set(char *ent, int type, int limit)
+{
+    CloneEnt *ce;
+    int       rval = 0;
+
+    if (strlen(ent) > HOSTIPLEN)
+        return -1;
+
+    if (limit < 0)
+        return -1;
+
+    ce = get_clone(ent, 1);
+
+    switch (type)
+    {
+        case CLIM_HARD_GLOBAL:
+            rval = ce->limit;
+            ce->limit = limit;
+            if (limit && ce->sglimit > limit)
+                ce->sglimit = 0;
+            break;
+
+        case CLIM_SOFT_GLOBAL:
+            rval = ce->sglimit;
+            ce->sglimit = limit;
+            break;
+
+        case CLIM_SOFT_LOCAL:
+            rval = ce->sllimit;
+            ce->sllimit = limit;
+            break;
+    }
+
+    expire_clone(ce);
+
+    return rval;
+}
+
+/*
+ * Gets the current clone limits.  0 means using default.
+ */
+void clones_get(char *ent, int *hglimit, int *sglimit, int *sllimit)
+{
+    CloneEnt *ce;
+
+    ce = get_clone(ent, 0);
+
+    if (ce)
+    {
+        *hglimit = ce->limit;
+        *sglimit = ce->sglimit;
+        *sllimit = ce->sllimit;
+    }
+    else
+    {
+        *hglimit = 0;
+        *sglimit = 0;
+        *sllimit = 0;
+    }
+}
+
+/*
+ * Propagate global clone limits.
+ */
+void
+clones_send(aClient *cptr)
+{
+    CloneEnt *ce;
+
+    for (ce = clones_list; ce; ce = ce->next)
+    {
+        if (!ce->limit)
+            continue;
+        sendto_one(cptr, ":%s SVSCLONE %s %d", me.name, ce->ent, ce->limit);
+    }
+}
+
+/*
+ * Must be called AFTER throttle_init()
+ */
+void
+clones_init(void)
+{
+#ifndef THROTTLE_ENABLE
+    hashent_freelist = BlockHeapCreate(sizeof(hashent), 1024);
+#endif
+    free_cloneents = BlockHeapCreate(sizeof(CloneEnt), 1024);
+    clones_hashtable = create_hash_table(THROTTLE_HASHSIZE,
+                                         offsetof(CloneEnt, ent), HOSTIPLEN,
+                                         2, (void *)strcmp);
+}
+
diff --git a/src/confparse.c b/src/confparse.c
new file mode 100644 (file)
index 0000000..7705832
--- /dev/null
@@ -0,0 +1,748 @@
+/************************************************************************
+ *   Bahamut IRCd, src/confparse.c
+ *   Copyright (C) 2004, Aaron Wiebe
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: confparse.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "userban.h"
+#define CONF_TABS
+#include "confparse.h"
+
+#ifndef LINE_MAX
+#define LINE_MAX 256
+#endif
+
+/* notes on confparse.c
+ * While this initial revision requires a fair bit of trimming down,
+ * my primary goal right now was to build an extendable system that will
+ * allow for fairly easy changes to the config file.
+ * Heres a few notes on how to go about that.
+ *
+ * The parser works on two primary depths - blocks and tokens:
+ *
+ * block { 
+ *      token value;
+ *      token "string value";
+ *      token 123;      # int value
+ *      token;          # nonvar token
+ * };
+ *
+ * It can also parse non-token blocks:
+ *
+ * block (
+ *      "string string string";
+ *      "string blah";
+ * };
+ *
+ * Blocks are defined by tconftab (in confparse.h)
+ * Tokens are defined by sconftab (^^^^^^^^^^^^^^)
+ *
+ * Each block must have a function that takes the values collected
+ * and checks them against the requirements.  These functions are also
+ * handy for getting variables out of the array that they are stored in.
+ *
+ * The array variables are placed in (an array of cVar structs) contains
+ * all values for the the block, and the corrisponding sconftab item.
+ *
+ * I feel the need to rewrite large sections of this still, but I'll just
+ * be happy to have it working for now.
+ *
+ * Feb 24/04
+ * -epi
+ */
+
+extern int forked;
+extern char *set_classes(void);
+extern aPort *new_ports;
+extern Conf_Me *new_MeLine;
+
+/* free_vars()
+ * clear our temp variable array used by parse_block and children
+ */
+
+static void
+free_vars(cVar *vars[])
+{
+    int i = 0;
+
+    while(vars[i])
+    {
+        MyFree(vars[i]->value);
+        MyFree(vars[i]);
+        i++;
+    }
+}
+
+/* error handler */
+
+static char *current_file = "unknown";
+
+void
+confparse_error(char *problem, int line)
+{
+    if(!forked)
+        printf("ERROR:  %s near line %d of %s\n", problem, line, current_file);
+    else
+        sendto_realops("Conf Error:  %s near line %d of %s", problem, line,
+                        current_file);
+    return;
+}
+
+/* check_quote
+ * this routine skips over any ignored items inside our file
+ */
+
+static int quote = 0;
+
+static char *
+check_quote(char *cur)
+{
+    if(quote)
+    {
+        while((cur = strchr(cur, '*')))
+            if((*(++cur) == '/'))
+            {
+                cur++;
+                quote = 0;
+                break;
+            }
+        if(!cur)
+            return cur;
+    }
+    while((*cur == ' ') || (*cur == '\t'))
+        cur++;
+    /* now we've hit something .. check for single line quotes */
+    if (!*cur || *cur == '#' || *cur == '\n' ||
+            (*cur == '/' && *(cur+1) == '/'))
+        return NULL;
+    /* check for multiple line quotes */
+    if((*cur == '/') && (*(cur+1) == '*'))
+    {
+        cur += 2;
+        quote = 1;
+        while((cur = strchr(cur, '*')))
+            if((*(++cur) == '/'))
+            {
+                cur++;
+                quote = 0;
+                break;
+            }
+        if(!cur)
+            return cur;
+        else
+            return check_quote(cur);
+    }
+    return cur;
+}
+
+#define MAX_VALUES 128  /* maximum values per block */
+
+static char *
+parse_block(tConf *block, char *cur, FILE *file, int *lnum)
+{
+    char *tok, *var, *var2;
+    char line[LINE_MAX];
+    tConf *b2 = NULL;
+    sConf *item = NULL;
+    sConf *sconftab = block->subtok;
+    cVar  *vars[MAX_VALUES] = { 0 };
+    int   vnum = 0, tlnum = 0, clear = 0, done = 0, skip = 0;
+
+    if((sconftab) && (sconftab->flag == SCONFF_STRING))
+    {
+        /* this subtype only takes freeform variables
+         * dont bother looking for tokens
+         */
+        int i = 0;
+        while(!BadPtr(cur) || ((fgets(line, LINE_MAX, file) != NULL) && 
+                (*lnum)++ && (cur = line)))
+        {
+            cur = check_quote(cur);
+            if(BadPtr(cur))
+                continue;
+            if(clear)
+            {
+                if(*cur != ';')
+                {
+                    confparse_error("Missing semicolon", *lnum);
+                    free_vars(vars);
+                    return NULL;
+                }
+                else
+                    cur++;
+                clear = 0;
+                cur = check_quote(cur);
+                if(BadPtr(cur))
+                    continue;
+            }
+            if(done)
+            {
+                if(*cur != ';')
+                {
+                    confparse_error("Missing block end semicolon", *lnum);
+                    free_vars(vars);
+                    return NULL;
+                }
+                else
+                    cur++;
+                if(((*block->func) (vars, *lnum)) == -1)
+                {
+                    free_vars(vars);
+                    return NULL;
+                }
+                if(BadPtr(cur))
+                    *cur = '#';     /* we cant return a bad pointer because
+                                     * that will pull us out of the conf read
+                                     * so this will just get ignored
+                                     * kludgy, but effective */
+                free_vars(vars);
+                return cur;
+            }
+            cur = check_quote(cur);
+            if(BadPtr(cur))
+                continue;
+            if(*cur == '}')
+            {
+                done = 1;
+                cur++;
+                cur = check_quote(cur);
+                if(BadPtr(cur))
+                    continue;
+                if(*cur != ';')
+                {
+                    confparse_error("Missing block end semicolon", *lnum);
+                    free_vars(vars);
+                    return NULL;
+                }
+                else
+                    cur++;
+                if(((*block->func) (vars, *lnum)) == -1)
+                {
+                    free_vars(vars);
+                    return NULL;
+                }
+                if(BadPtr(cur))
+                    *cur = '#';     /* we cant return a bad pointer because
+                                     * that will pull us out of the conf read
+                                     * so this will just get ignored
+                                     * kludgy, but effective */
+                free_vars(vars);
+                return cur;
+            }
+            vars[vnum] = (cVar *) MyMalloc(sizeof(cVar));
+            memset((char *) vars[vnum], '\0', sizeof(cVar));
+            vars[vnum]->loaded = 1;
+            vars[vnum]->type = NULL;
+            tok = cur;
+            if(*cur == '"')
+            {
+                i = 1;
+                cur++;
+            }
+            var = cur;
+            if(i == 1)
+            {
+                while(!BadPtr(cur) && (*cur != '"'))
+                    cur++;
+                if(BadPtr(cur))
+                {
+                    confparse_error("Cant find closequote", *lnum);
+                    free_vars(vars);
+                    return NULL;
+                }
+                *cur = '\0';
+                cur++;
+                while(!BadPtr(cur) && (*cur != ';'))
+                    cur++;
+            }
+            else
+            {
+                while(!BadPtr(cur) && (*cur != ';'))
+                {
+                    if((*cur == ' '))
+                    {
+                        *cur = '\0';
+                        if(vars[vnum]->loaded == 1)
+                        {
+                            DupString(vars[vnum]->value, var);
+                            vars[vnum]->loaded = 2;
+                        }
+                    }
+                    else if(vars[vnum]->loaded == 2)
+                    {
+                        confparse_error("Junk after value", *lnum);
+                        free_vars(vars);
+                        return NULL;
+                    }
+                    cur++;
+                }
+            }
+            tlnum = *lnum;
+            if(BadPtr(cur))
+            {
+                clear = 1;
+                continue;
+            }
+            *cur = '\0';
+            cur++;
+            if(vars[vnum]->loaded == 1)
+                DupString(vars[vnum]->value, var);
+            vars[vnum]->loaded = 3;
+            vnum++;
+        }
+        confparse_error("Unexpected EOF: Syntax Error", tlnum);
+        free_vars(vars);
+        return NULL;
+    }
+
+    while(!BadPtr(cur) || ((fgets(line, LINE_MAX, file) != NULL) && (*lnum)++
+             && (cur = line)))
+    {
+        cur = check_quote(cur);
+        if(BadPtr(cur))
+            continue;
+        if(clear)
+        {
+            /* if we're looking for a closing semicolon, check for it first
+             * if we cant find it, ignore it and hope for the best
+             */
+            if(*cur != ';')
+            {
+                confparse_error("Missing semicolon ", *lnum);
+                free_vars(vars);
+                return NULL;
+            }
+            else
+                cur++;
+            clear = 0;
+            if(vars[vnum])
+            {
+                vars[vnum]->loaded = 3;
+                vnum++;
+            }
+            item = NULL;
+            cur = check_quote(cur);
+            if(BadPtr(cur))
+                continue;
+        }
+        if(done)
+        {
+            /* we've found the end of our block, now we're looking for the
+             * closing semicolon.  if we cant find it, ignore it and
+             * hope for the best
+             */
+            if(*cur != ';')
+            {
+                confparse_error("Missing block end semicolon", *lnum);
+                free_vars(vars);
+                return NULL;
+            }
+            else
+                cur++;
+            if(((*block->func) (vars, *lnum)) == -1)
+            {
+                free_vars(vars);
+                return NULL;
+            }
+            if(BadPtr(cur))
+                *cur = '#';     /* we cant return a bad pointer because
+                                 * that will pull us out of the conf read
+                                 * so this will just get ignored
+                                 * kludgy, but effective */
+            free_vars(vars);
+            return cur;
+        }
+        if(b2 && b2->tok)
+        {
+            /* we've identified a nested block in a previous loop.
+             * we didnt get an openquote yet, so look for that.
+             * we must find this.  keep looking til we do.
+             */
+            if(*cur != '{')
+            {
+                confparse_error("Junk after nested block token", *lnum);
+                free_vars(vars);
+                return NULL;
+            }
+            cur++;
+            cur = check_quote(cur);
+            cur = parse_block(b2, cur, file, lnum);
+            b2 = NULL;
+            continue;
+        }
+        if(!item || !item->tok)
+        {
+            /* if we dont already have a specific token we're working on
+             * find one here.
+             */
+            cur = check_quote(cur);
+            if(BadPtr(cur))
+                continue;
+            tok = cur;
+            tlnum = *lnum;
+            if(*cur == '}')
+            {
+                /* if we've got a closebracket, then we've hit the end
+                 * of our block.
+                 */
+                done = 1;
+                cur++;
+                cur = check_quote(cur);
+                if(BadPtr(cur))
+                    continue;
+                if(*cur != ';')
+                {
+                    confparse_error("Missing block end semicolon", *lnum);
+                    free_vars(vars);
+                    return NULL;
+                }
+                else
+                    cur++;
+                if(((*block->func) (vars, *lnum)) == -1)
+                {
+                    free_vars(vars);
+                    return NULL;
+                }
+                if(BadPtr(cur))
+                    *cur = '#';     /* we cant return a bad pointer because
+                                     * that will pull us out of the conf read
+                                     * so this will just get ignored
+                                     * kludgy, but effective */
+                free_vars(vars);
+                return cur;
+
+            }
+            /* our token ends where whitespace or a semicolon begins */
+            while(!BadPtr(cur) && ((*cur != ' ') && (*cur != ';') &&
+                   (*cur != '\t') && (*cur != '\n')))
+                cur++;
+            if(BadPtr(cur))
+            {
+                confparse_error("Unterminated token", *lnum);
+                free_vars(vars);
+                return NULL;
+            }
+            else 
+            {
+                if(*cur == ';')
+                    skip = 1;
+                *cur = '\0';
+            }
+            cur++;
+            if(block->nest)
+            {
+                /* we allow nested stuff inside here, so check for it. */
+                for(b2 = tconftab; b2->tok; b2++)
+                    if(!mycmp(b2->tok, tok))
+                        break;
+                if(b2 && b2->tok)
+                    if(!(block->nest & b2->flag))
+                        b2 = NULL;
+                if(b2 && b2->tok)
+                {
+                    /* recurse through the block we found */
+                    tlnum = *lnum;
+                    if((cur = strchr(cur, '{')))
+                    {
+                        cur++;
+                        cur = check_quote(cur);
+                        cur = parse_block(b2, cur, file, lnum);
+                        b2 = NULL;
+                        continue;
+                    }
+                    if(BadPtr(cur))
+                        continue;
+                }
+            }
+            /* find our token */
+            for(item = sconftab; item && item->tok; item++)
+                if(!mycmp(item->tok, tok))
+                    break;
+            if(!item->tok)
+            {
+                confparse_error("Unknown token", *lnum);
+                free_vars(vars);
+                return NULL;
+            }
+            /* create our variable */
+            vars[vnum] = (cVar *) MyMalloc(sizeof(cVar));
+            memset((char *) vars[vnum], '\0', sizeof(cVar));
+            vars[vnum]->type = item;
+            vars[vnum]->loaded = 1;
+        }
+        if(item->var & VARTYPE_NONE)
+        {
+            /* we dont need to grab a variable for this type 
+             * just look for the closing semicolon, and move on */
+            vars[vnum]->loaded = 2;
+            if(!skip)   
+            {
+                /* we've already gotten our semicolon back
+                 * at the end of our token.  dont look for it. */
+                cur = check_quote(cur);
+                while(!BadPtr(cur) && (*cur != ';'))
+                    cur++;
+                if(BadPtr(cur))
+                {
+                    clear = 1;
+                    continue;
+                }
+                cur++;
+            }
+            skip = 0;
+            vars[vnum]->loaded = 3;
+            vnum++;
+            item = NULL;
+            continue;
+        }
+        if(item->var & VARTYPE_STRING)
+        {
+            /* we're looking for a string here, so we require
+             * quotes around the string...
+             */
+            cur = check_quote(cur);
+            while(!BadPtr(cur) && (*cur != '"'))
+                cur++;
+            if(BadPtr(cur))
+                continue;
+            cur++;
+            var = cur;
+            while(!BadPtr(cur) && (*cur != '"'))
+                cur++;
+            if(BadPtr(cur))
+            {
+                confparse_error("Unterminated quote", *lnum);
+                free_vars(vars);
+                return NULL;
+            }
+            *cur = '\0';
+            cur++;
+            DupString(vars[vnum]->value, var);
+            vars[vnum]->loaded = 2;
+            while(!BadPtr(cur) && (*cur != ';'))
+                cur++;
+            if(BadPtr(cur))
+            {
+                clear = 1;
+                continue;
+            }
+            cur++;
+            vars[vnum]->loaded = 3;
+            vnum++;
+            item = NULL;
+            continue;
+        }
+        if(item->var & VARTYPE_INT)
+        {
+            cur = check_quote(cur);
+            var = cur;
+            while(!BadPtr(cur) && ((*cur != ';') && (*cur != '\t') &&
+                    (*cur != '\n') && (*cur != ' ')))
+                cur++;
+            if(BadPtr(cur))
+            {
+                clear = 1;
+                continue;
+            }
+            if(*cur != ';')
+                clear = 1;
+            *cur = '\0';
+            cur++;
+            var2 = var;
+            while(*var) 
+            {
+                if(IsDigit(*var))
+                    var++;
+                else
+                {
+                    confparse_error("Expecting integer value", *lnum);
+                    free_vars(vars);
+                    return NULL;
+                }
+            }
+            if(!item)
+                continue;
+            var = var2;
+            DupString(vars[vnum]->value, var);
+            vars[vnum]->loaded = 3;
+            vnum++;
+            item = NULL;
+            continue;
+        }
+        if(item->var & VARTYPE_NAME)
+        {
+            cur = check_quote(cur);
+            if(!BadPtr(cur) && (*cur == '"'))
+                cur++;
+            var = cur;
+            while(!BadPtr(cur) && (*cur != ';'))
+            {
+                if((*cur == ' ') || (*cur == '"') || (*cur == '\t'))
+                {
+                    *cur = '\0';
+                    if(vars[vnum]->loaded == 1)
+                    {
+                        DupString(vars[vnum]->value, var);
+                        vars[vnum]->loaded = 2;
+                    }
+                }
+                cur++;
+            }
+            if(BadPtr(cur))
+            {
+                clear = 1;
+                continue;
+            }
+            *cur = '\0';
+            cur++;
+            if(vars[vnum]->loaded == 1)
+                DupString(vars[vnum]->value, var);
+            vars[vnum]->loaded = 3;
+            vnum++;
+            item = NULL;
+            continue;
+        }
+        confparse_error("Unexpected EOF:  Syntax Error", tlnum);
+        free_vars(vars);
+        return NULL;
+    }
+    confparse_error("Unexpected EOF:  Syntax Error", tlnum);
+    free_vars(vars);
+    return NULL;
+}
+            
+int
+initconf(char *filename)
+{
+    int lnum = 0, blnum = 0, clear = 0;
+    char line[LINE_MAX];
+    char *cur = NULL;
+    char *tok;
+    tConf *block = NULL;
+    FILE *file;
+
+    current_file = filename;
+
+    if(!(file = fopen(filename, "r")))
+    {
+        if(forked)
+            sendto_realops("Unable to open config file %s%s", file, "\n");
+        else
+            printf("Unable to open config file %s\n", filename);
+        return -1;
+    }
+
+    while(!BadPtr(cur) || ((fgets(line, LINE_MAX, file) != NULL) && ++lnum
+             && (cur = line)))
+    {
+        cur = check_quote(cur);
+        if(BadPtr(cur))
+            continue;
+        /* now, we should be ok to get that token.. */
+        if(!block)
+        {
+            tok = cur;
+            while((*cur != ' ') && (*cur != '\n') && (*cur != '{'))
+                cur++;      /* find the whitespace following the token */
+            if(*cur == '{')
+                clear = 1;
+            *cur = '\0';
+            cur++;
+            if(!mycmp("INCLUDE", tok))
+            {
+                /* this is an include - find pull out the file name
+                 * and parse this file now
+                 */
+                char *var;
+                cur = check_quote(cur);
+                if((*cur == '"') || *cur == '<')
+                    cur++;
+                var = cur;
+                while((*cur != ' ') && (*cur != '"') && (*cur != '>') &&
+                        (*cur != '\n') && (*cur != '\t'))
+                    cur++;
+                if(BadPtr(cur))
+                {
+                    confparse_error("Bad include line", lnum);
+                    return -1;
+                }
+                *cur = '\0';
+                cur++;
+                if(initconf(var) == -1)
+                    return -1;
+                current_file = filename;    /* reset */
+                continue;
+            }    
+            for(block = tconftab; block->tok; block++)
+                if(!mycmp(block->tok, tok))
+                    break;
+            if(!block->tok)
+            {
+                confparse_error("Unknown block type", lnum);
+                return -1;
+            }
+            blnum = lnum;
+        }
+        cur = check_quote(cur);
+        if(BadPtr(cur))
+            continue;
+        if((*cur ==  '{') || clear)
+            cur++;
+        else
+        {
+            confparse_error("Junk after block name", lnum);
+            return -1;
+        }
+        if((cur = parse_block(block, cur, file, &lnum)) == NULL)
+        {
+            return -1;
+        }
+        clear = 0;
+        block = NULL;
+        continue;
+    }
+    if(clear)
+    {
+        confparse_error("Unexpected EOF:  Syntax error", blnum);
+        return -1;
+    }
+    return 1;
+}
+
+inline char *
+finishconf(void)
+{
+    static char buf[256];
+    char *ret;
+
+    if (!new_MeLine || !new_MeLine->servername)
+        return "Missing global block";
+    if ((ret = set_classes()))
+    {
+        ircsnprintf(buf, sizeof(buf), "Missing class block for referenced "
+                    "class '%s'", ret);
+        return buf;
+    }
+    if (!new_ports)
+        return "No ports defined";
+    return NULL;
+}
diff --git a/src/dh.c b/src/dh.c
new file mode 100644 (file)
index 0000000..8d613d8
--- /dev/null
+++ b/src/dh.c
@@ -0,0 +1,349 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/dh.c
+ *   Copyright (C) 2000 Lucas Madar
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: dh.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+/*
+ * Diffie-Hellman key exchange for bahamut ircd.
+ * Lucas Madar <lucas@dal.net> -- 2000
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+
+#define DH_HEADER
+#include "dh.h"
+
+
+#ifdef __OpenBSD__
+#define RAND_SRC "/dev/arandom"
+#else
+#define RAND_SRC "/dev/random"
+#endif
+
+
+static int verify_is_hex(char *string)
+{
+    int l = strlen(string);
+    char tmp[4] = {'\0', '\0', '\0', '\0'};
+    int tmpidx = 0;
+
+    if(l & 0x01) /* check if it's odd length */
+    {  
+        l++;
+        tmp[tmpidx++] = '0'; /* pad with zero */
+    }
+   
+    while(*string)
+    {
+        tmp[tmpidx++] = *string++;
+        if(tmpidx == 2)
+        {
+            char *eptr;
+            unsigned char x;
+   
+            tmpidx = 0;
+   
+            x = strtol(tmp, &eptr, 16);
+            if(*eptr != '\0')
+                return 0;
+        }
+    }
+    return 1;
+}
+
+int dh_hexstr_to_raw(char *string, unsigned char *hexout, int *hexlen)
+{
+    int l = strlen(string);
+    char tmp[3] = {'\0', '\0', '\0'};
+    int tmpidx = 0;
+    int hexidx = 0;
+
+    if(l & 0x01) /* check if it's odd length */
+    {  
+        l++;
+        tmp[tmpidx++] = '0'; /* pad with zero */
+    }
+   
+    while(*string)
+    {
+        tmp[tmpidx++] = *string++;
+        if(tmpidx == 2)
+        {
+            char *eptr;
+            unsigned char x;
+   
+            tmpidx = 0;
+   
+            x = strtol(tmp, &eptr, 16);
+            if(*eptr != '\0')
+                return 0;
+            hexout[hexidx++] = (unsigned char) x;
+        }
+    }
+    *hexlen = hexidx;
+    return 1;
+}
+
+static inline void entropy_error(void)
+{
+    printf("\nCould not generate entropy from %s:\n%s\n\n",
+           RAND_SRC, strerror(errno));
+    printf("ircd needs a %d byte random seed.\n", RAND_BYTES);
+    printf("You can place a file containing random data called"
+           " .ircd.entropy\nin the directory with your ircd.conf."
+           " This file must be at least %d bytes\n", RAND_BYTES);
+    printf("long and should be suitably random.\n");
+}
+
+static int make_entropy()
+{
+    char randbuf[RAND_BYTES * 4];
+    FILE *fp;
+    int i;
+
+    printf("\nNo random state found, generating entropy from %s...\n",
+           RAND_SRC);
+    printf("On some systems this may take a while, and can be helped by"
+           " keeping the\nsystem busy, such as pounding on the keyboard.\n");
+
+    fp = fopen(RAND_SRC, "r");
+    if(!fp)
+    {
+        entropy_error();
+        return 0;
+    }
+
+    for(i = 0; i < (RAND_BYTES * 4); i++)
+    {
+        int cv;
+
+        cv = fgetc(fp);
+
+        if(cv == EOF)
+        {
+            if(ferror(fp))
+            {
+                entropy_error();
+                fclose(fp);
+                return 0;
+            }
+            clearerr(fp);
+            usleep(100);
+            i--;
+            continue;
+        }
+
+        randbuf[i] = cv;
+        if(i && (i % 64 == 0))
+        {
+            printf(" %d%% .. ", (int)(((float) i / (float) (RAND_BYTES * 4)) 
+                    * 100.0));
+            fflush(stdout);
+        }
+    }
+    printf("Done.\n");
+    fclose(fp);
+
+    fp = fopen(".ircd.entropy", "w");
+    if(!fp)
+    {
+        printf("Could not open .ircd.entropy for writing: %s\n", 
+                strerror(errno));
+        return 0;
+    }
+
+    fwrite(randbuf, RAND_BYTES * 4, 1, fp);
+    fclose(fp);
+
+    RAND_load_file(".ircd.entropy", -1);
+
+    return 1;
+}
+
+static int init_random()
+{
+    int ret;
+    time_t now;
+
+    ret = RAND_load_file(".ircd.entropy", -1);
+    if(ret <= 0)
+    {
+        if(!make_entropy())
+            return -1;
+    }
+    else
+        printf("%d bytes of entropy loaded.\n", ret);
+
+    now = time(NULL);   
+
+    /* this is probably not too good, but it saves just writing
+       the whole state back to disk with no changes. */
+    RAND_seed(&now, 4); 
+    RAND_write_file(".ircd.entropy");
+
+    return 0;
+}
+
+static void create_prime()
+{
+    char buf[PRIME_BYTES_HEX];
+    int i;
+    int bufpos = 0;
+
+    for(i = 0; i < PRIME_BYTES; i++)
+    {
+        char *x = hex_to_string[dh_prime_1024[i]];
+        while(*x)
+            buf[bufpos++] = *x++;
+    }
+    buf[bufpos] = '\0';
+
+    ircd_prime = NULL;
+    BN_hex2bn(&ircd_prime, buf);
+    ircd_generator = BN_new();
+    BN_set_word(ircd_generator, dh_gen_1024);
+}
+
+int dh_init()
+{
+    ERR_load_crypto_strings();
+
+    create_prime();
+    if(init_random() == -1)
+        return -1;
+    return 0; 
+}
+
+int dh_generate_shared(void *session, char *public_key)
+{
+    BIGNUM *tmp;
+    int len;
+    struct session_info *si = (struct session_info *) session;
+
+    if(verify_is_hex(public_key) == 0 || !si || si->session_shared)
+        return 0;
+
+    tmp = NULL;
+    BN_hex2bn(&tmp, public_key);
+    if(!tmp)
+        return 0;
+
+    si->session_shared_length = DH_size(si->dh);
+    si->session_shared = (char *) malloc(DH_size(si->dh));
+    len = DH_compute_key(si->session_shared, tmp, si->dh);
+    BN_free(tmp);
+
+    if(len < 0)
+        return 0;
+
+    si->session_shared_length = len;
+
+    return 1;
+}
+
+void *dh_start_session()
+{
+    struct session_info *si;
+
+    si = (struct session_info *) malloc(sizeof(struct session_info));
+    if(!si) 
+        abort();
+
+    memset(si, 0, sizeof(struct session_info));
+
+    si->dh = DH_new();
+    si->dh->p = BN_dup(ircd_prime);
+    si->dh->g = BN_dup(ircd_generator);
+
+    if(!DH_generate_key(si->dh))
+    {
+        DH_free(si->dh);
+        free(si);
+        return NULL;
+    }
+
+    return (void *) si;
+}
+
+void dh_end_session(void *session)
+{
+    struct session_info *si = (struct session_info *) session;
+
+    if(si->dh)
+    {
+        DH_free(si->dh);
+        si->dh = NULL;
+    }
+
+    if(si->session_shared)
+    {
+        memset(si->session_shared, 0, si->session_shared_length);
+        free(si->session_shared);
+        si->session_shared = NULL;
+    }
+
+    free(si);
+}
+
+char *dh_get_s_public(char *buf, int maxlen, void *session)
+{
+    struct session_info *si = (struct session_info *) session;
+    char *tmp;
+
+    if(!si || !si->dh || !si->dh->pub_key)
+        return NULL;   
+
+    tmp = BN_bn2hex(si->dh->pub_key);
+    if(!tmp) 
+        return NULL;
+
+    if(strlen(tmp) + 1 > maxlen)
+    {
+        OPENSSL_free(tmp);
+        return NULL;
+    }
+    strcpy(buf, tmp);
+    OPENSSL_free(tmp);
+
+    return buf;
+}
+
+int dh_get_s_shared(char *buf, int *maxlen, void *session)
+{
+    struct session_info *si = (struct session_info *) session;
+
+    if(!si || !si->session_shared || *maxlen < si->session_shared_length)
+        return 0;
+
+    *maxlen = si->session_shared_length;
+    memcpy(buf, si->session_shared, *maxlen);
+
+    return 1;
+}
diff --git a/src/fdlist.c b/src/fdlist.c
new file mode 100644 (file)
index 0000000..11d0408
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * fdlist.c   maintain lists of certain important fds 
+ */
+
+/* $Id: fdlist.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "config.h"
+#include "fdlist.h"
+
+#ifdef USE_KQUEUE
+#include <sys/event.h>
+#endif
+
+void addto_fdlist(int fd, fdlist * listp)
+{
+    int index;
+    
+    if ((index = ++listp->last_entry) >= MAXCONNECTIONS) {
+       /* list too big.. must exit */
+       --listp->last_entry;
+       
+#ifdef USE_SYSLOG
+       (void) syslog(LOG_CRIT, "fdlist.c list too big.. must exit");
+#endif
+       abort();
+    }
+    else
+       listp->entry[index] = fd;
+    return;
+}
+
+void delfrom_fdlist(int fd, fdlist * listp)
+{
+    int i;
+    
+    for (i = listp->last_entry; i; i--)
+    {
+       if (listp->entry[i] == fd)
+           break;
+    }
+    if (!i)
+       return;                 /* could not find it! */
+    /* swap with last_entry */
+    if (i == listp->last_entry)
+    {
+       listp->entry[i] = 0;
+       listp->last_entry--;
+       return;
+    }
+    else
+    {
+       listp->entry[i] = listp->entry[listp->last_entry];
+       listp->entry[listp->last_entry] = 0;
+       listp->last_entry--;
+       return;
+    }
+}
+
+void init_fdlist(fdlist * listp)
+{
+    listp->last_entry = 0;
+    memset((char *) listp->entry, '\0', sizeof(listp->entry));
+#ifdef USE_KQUEUE
+    listp->kqueue_fd = kqueue();
+#endif
+    return;
+}
diff --git a/src/fds.c b/src/fds.c
new file mode 100644 (file)
index 0000000..9f7b6d2
--- /dev/null
+++ b/src/fds.c
@@ -0,0 +1,253 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/fds.c
+ *   Copyright (C) 2003 Lucas Madar
+ *
+ *   fds.c -- file descriptor list management routines
+ *
+ */
+
+/* $Id: fds.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "fds.h"
+#include "numeric.h"
+
+void engine_add_fd(int);
+void engine_del_fd(int);
+void engine_change_fd_state(int, unsigned int);
+
+struct afd_entry {
+   int type;
+   void *value;
+   unsigned int flags;
+   void *internal;
+};
+
+struct afd_entry fd_list[MAXCONNECTIONS];
+
+#ifdef DEBUGMODE
+static char *type_string(int type)
+{
+   switch(type)
+   {
+      case FDT_NONE:
+         return "NONE";
+
+      case FDT_CLIENT:
+         return "client";
+
+      case FDT_AUTH:
+         return "auth";
+
+      case FDT_RESOLVER:
+         return "resolver";
+
+      case FDT_LISTENER:
+         return "listener";
+
+      case FDT_CALLBACKP:
+         return "callback";
+   }
+
+   return "???";
+}
+
+static char *flags_string(unsigned int flags)
+{
+   static char fbuf[512];
+
+   fbuf[0] = '\0';
+
+   if(flags & FDF_WANTREAD)
+   {
+      if(fbuf[0])
+         strcat(fbuf, ", ");
+      strcat(fbuf, "read");
+   }
+
+   if(flags & FDF_WANTWRITE)
+   {
+      if(fbuf[0])
+         strcat(fbuf, ", ");
+      strcat(fbuf, "write");
+   }
+
+   return fbuf;
+}
+
+void report_fds(aClient *cptr)
+{
+   int i;
+   char *name, *blocking;
+
+   for(i = 0; i < MAXCONNECTIONS; i++)
+   {
+      if(fd_list[i].type == FDT_NONE)
+         continue;
+
+      if(fd_list[i].type == FDT_CLIENT ||
+         fd_list[i].type == FDT_AUTH)
+      {
+         aClient *cptr = (aClient *) fd_list[i].value;
+         int hide = (IsConnecting(cptr) || IsHandshake(cptr) || IsServer(cptr)) ? HIDEME : 0;
+
+         name = get_client_name(cptr, hide);
+         blocking = (cptr->flags & FLAGS_BLOCKED) ?
+                    "BLOCKED" : "_";
+      }
+      else
+      {
+         name = "-";
+         blocking = "-";
+      }
+
+      sendto_one(cptr, ":%s %d %s :%d - %s [%s] %s %s",
+                 me.name, RPL_STATSDEBUG, cptr->name,
+                 i, type_string(fd_list[i].type),
+                 flags_string(fd_list[i].flags),
+                 name, blocking);
+
+   }
+}
+#endif
+
+static inline void fd_range_assert(int fd)
+{
+   if(fd < 0 || fd >= MAXCONNECTIONS)
+      abort();
+}
+
+static inline void fd_notused_assert(int fd)
+{
+   if(fd_list[fd].type != FDT_NONE)
+      abort();
+}
+
+static inline void fd_used_assert(int fd)
+{
+   if(fd_list[fd].type == FDT_NONE)
+      abort();
+}
+
+void add_fd(int fd, int type, void *value)
+{
+   fdfprintf(stderr, "add_fd: %d %d %x\n", fd, type, (int) value);
+
+   fd_range_assert(fd);
+   fd_notused_assert(fd);
+
+   fd_list[fd].type = type;
+   fd_list[fd].value = value;
+   fd_list[fd].flags = 0;
+   engine_add_fd(fd);
+}
+
+void del_fd(int fd)
+{
+   fdfprintf(stderr, "del_fd: %d\n", fd);
+
+   fd_range_assert(fd);
+   fd_used_assert(fd);
+
+   engine_del_fd(fd);
+
+   fd_list[fd].type = FDT_NONE;
+   fd_list[fd].value = NULL;
+   fd_list[fd].internal = NULL;
+}
+
+void set_fd_flags(int fd, unsigned int flags)
+{
+   int oldflags;
+   fd_range_assert(fd);
+   fd_used_assert(fd);
+
+   oldflags = fd_list[fd].flags;
+
+   fd_list[fd].flags |= flags;
+
+   fdfprintf(stderr, "set_fd_flags: %d %x [%x -> %x]\n", fd, flags, oldflags, fd_list[fd].flags);
+
+   if(oldflags != fd_list[fd].flags)
+      engine_change_fd_state(fd, fd_list[fd].flags);
+}
+
+void unset_fd_flags(int fd, unsigned int flags)
+{
+   int oldflags;
+   fd_range_assert(fd);
+   fd_used_assert(fd);
+
+   oldflags = fd_list[fd].flags;
+
+   fd_list[fd].flags &= ~(flags);
+
+   fdfprintf(stderr, "unset_fd_flags: %d %x [%x -> %x]\n", fd, flags, oldflags, fd_list[fd].flags);
+   if(oldflags != fd_list[fd].flags)
+      engine_change_fd_state(fd, fd_list[fd].flags);
+}
+
+void get_fd_info(int fd, int *type, unsigned int *flags, void **value)
+{
+   fd_range_assert(fd);
+
+   *type = fd_list[fd].type;
+   *flags = fd_list[fd].flags;
+   *value = fd_list[fd].value;
+}
+
+unsigned int get_fd_flags(int fd)
+{
+   fd_range_assert(fd);
+   fd_used_assert(fd);
+
+   return fd_list[fd].flags;
+}
+
+void init_fds()
+{
+   memset(fd_list, 0, sizeof(struct afd_entry) * MAXCONNECTIONS);
+}
+
+void set_fd_internal(int fd, void *ptr)
+{
+   fd_list[fd].internal = ptr;
+}
+
+void *get_fd_internal(int fd)
+{
+   return fd_list[fd].internal;
+}
+
+/*
+ * check_client_fd
+ * 
+ * called whenever a state change is necessary on a client
+ * ie, when ident and dns are finished
+ */
+
+void check_client_fd(aClient *cptr)
+{
+   if (DoingAuth(cptr))
+   {
+      unsigned int fdflags = get_fd_flags(cptr->authfd);
+
+      if(!(fdflags & FDF_WANTREAD))
+         set_fd_flags(cptr->authfd, FDF_WANTREAD);
+
+      if((cptr->flags & FLAGS_WRAUTH) && !(fdflags & FDF_WANTWRITE))
+         set_fd_flags(cptr->authfd, FDF_WANTWRITE);
+      else if(!(cptr->flags & FLAGS_WRAUTH) && (fdflags & FDF_WANTWRITE))
+         unset_fd_flags(cptr->authfd, FDF_WANTWRITE);
+
+      return;
+   }
+
+   if (DoingDNS(cptr))
+      return;
+
+   set_fd_flags(cptr->fd, FDF_WANTREAD);
+}
diff --git a/src/hash.c b/src/hash.c
new file mode 100644 (file)
index 0000000..1dae1d1
--- /dev/null
@@ -0,0 +1,658 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/hash.c
+ *   Copyright (C) 1991 Darren Reed
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: hash.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "hash.h"
+#include "h.h"
+
+static aHashEntry clientTable[U_MAX];
+static aHashEntry channelTable[CH_MAX];
+
+/*
+ * look in whowas.c for the missing ...[WW_MAX]; entry - Dianora
+ *
+ * Hashing.
+ * 
+ * The server uses a chained hash table to provide quick and efficient
+ * hash table mantainence (providing the hash function works evenly
+ * over the input range).  The hash table is thus not susceptible to
+ * problems of filling all the buckets or the need to rehash. It is
+ * expected that the hash table would look somehting like this during
+ * use: +-----+    +-----+    +-----+   +-----+ 
+ *   ---| 224 |----| 225 |----| 226 |---| 227 |--- 
+ *      +-----+    +-----+    +-----+   +-----+ 
+ *         |          |          | 
+ *      +-----+    +-----+    +-----+ 
+ *      |  A  |    |  C  |    |  D  | 
+ *      +-----+    +-----+    +-----+ 
+ *         | 
+ *      +-----+ 
+ *      |  B  | 
+ *      +-----+
+ * 
+ * A - GOPbot, B - chang, C - hanuaway, D - *.mu.OZ.AU
+ * 
+ * The order shown above is just one instant of the server.  Each time a
+ * lookup is made on an entry in the hash table and it is found, the
+ * entry is moved to the top of the chain.
+ * 
+ * ^^^^^^^^^^^^^^^^ **** Not anymore - Dianora
+ * 
+ */
+
+unsigned hash_nick_name(char *nname)
+{
+    unsigned hash = 0;
+    int     hash2 = 0;
+    int     ret;
+    char    lower;
+
+    while (*nname)
+    {
+       lower = ToLower(*nname);
+       hash = (hash << 1) + lower;
+       hash2 = (hash2 >> 1) + lower;
+       nname++;
+    }
+    ret = ((hash & U_MAX_INITIAL_MASK) << BITS_PER_COL) +
+       (hash2 & BITS_PER_COL_MASK);
+    return ret;
+}
+
+/*
+ * hash_channel_name
+ * 
+ * calculate a hash value on at most the first 30 characters of the
+ * channel name. Most names are short than this or dissimilar in this
+ * range. There is little or no point hashing on a full channel name
+ * which maybe 255 chars long.
+ */
+int hash_channel_name(char *name)
+{
+    unsigned char *hname = (unsigned char *) name;
+    unsigned int hash = 0;
+    int hash2 = 0;
+    char lower;
+    int i = 30;
+
+    while (*hname && --i)
+    {
+       lower = ToLower(*hname);
+       hash = (hash << 1) + lower;
+       hash2 = (hash2 >> 1) + lower;
+       hname++;
+    }
+    return ((hash & CH_MAX_INITIAL_MASK) << BITS_PER_COL) +
+       (hash2 & BITS_PER_COL_MASK);
+}
+
+unsigned int hash_whowas_name(char *name)
+{
+    unsigned char *nname = (unsigned char *) name;
+    unsigned int hash = 0;
+    int hash2 = 0;
+    int ret;
+    char lower;
+
+    while (*nname)
+    {
+       lower = ToLower(*nname);
+       hash = (hash << 1) + lower;
+       hash2 = (hash2 >> 1) + lower;
+       nname++;
+    }
+    ret = ((hash & WW_MAX_INITIAL_MASK) << BITS_PER_COL) +
+       (hash2 & BITS_PER_COL_MASK);
+    return ret;
+}
+
+/*
+ * clear_*_hash_table
+ * 
+ * Nullify the hashtable and its contents so it is completely empty.
+ */
+
+void clear_client_hash_table()
+{
+    memset((char *) clientTable, '\0', sizeof(aHashEntry) * U_MAX);
+}
+
+void clear_channel_hash_table()
+{
+    memset((char *) channelTable, '\0', sizeof(aHashEntry) * CH_MAX);
+}
+
+/* add_to_client_hash_table */
+int add_to_client_hash_table(char *name, aClient *cptr)
+{
+    int     hashv;
+    
+    hashv = hash_nick_name(name);
+    cptr->hnext = (aClient *) clientTable[hashv].list;
+    clientTable[hashv].list = (void *) cptr;
+    clientTable[hashv].links++;
+    clientTable[hashv].hits++;
+    return 0;
+}
+
+/* add_to_channel_hash_table */
+int add_to_channel_hash_table(char *name, aChannel *chptr)
+{
+    int     hashv;
+
+    hashv = hash_channel_name(name);
+    chptr->hnextch = (aChannel *) channelTable[hashv].list;
+    channelTable[hashv].list = (void *) chptr;
+    channelTable[hashv].links++;
+    channelTable[hashv].hits++;
+    return 0;
+}
+
+/* del_from_client_hash_table */
+int
+del_from_client_hash_table(char *name, aClient *cptr)
+{
+    aClient *tmp, *prev = NULL;
+    int     hashv;
+
+    hashv = hash_nick_name(name);
+    for (tmp = (aClient *) clientTable[hashv].list; tmp; tmp = tmp->hnext)
+    {
+       if (tmp == cptr)
+       {
+           if (prev)
+               prev->hnext = tmp->hnext;
+           else
+               clientTable[hashv].list = (void *) tmp->hnext;
+           tmp->hnext = NULL;
+           if (clientTable[hashv].links > 0)
+           {
+               clientTable[hashv].links--;
+               return 1;
+           }
+           else
+               /*
+                * Should never actually return from here and if we do it
+                * is an error/inconsistency in the hash table.
+                */
+               return -1;
+       }
+       prev = tmp;
+    }
+    return 0;
+}
+
+/* del_from_channel_hash_table */
+int del_from_channel_hash_table(char *name, aChannel *chptr)
+{
+    aChannel *tmp, *prev = NULL;
+    int     hashv;
+    
+    hashv = hash_channel_name(name);
+    for (tmp = (aChannel *) channelTable[hashv].list; tmp;
+        tmp = tmp->hnextch)
+    {
+       if (tmp == chptr)
+       {
+           if (prev)
+               prev->hnextch = tmp->hnextch;
+           else
+               channelTable[hashv].list = (void *) tmp->hnextch;
+           tmp->hnextch = NULL;
+           if (channelTable[hashv].links > 0)
+           {
+               channelTable[hashv].links--;
+               return 1;
+           }
+           else
+               return -1;
+       }
+       prev = tmp;
+    }
+    return 0;
+}
+
+/* hash_find_client */
+aClient *hash_find_client(char *name, aClient *cptr)
+{
+    aClient *tmp;
+    aHashEntry *tmp3;
+    int hashv;
+    
+    hashv = hash_nick_name(name);
+    tmp3 = &clientTable[hashv];
+    /* Got the bucket, now search the chain. */
+    for (tmp = (aClient *) tmp3->list; tmp; tmp = tmp->hnext)
+       if (mycmp(name, tmp->name) == 0) 
+       {
+           return (tmp);
+       }
+    return (cptr);
+    /*
+     * If the member of the hashtable we found isnt at the top of its
+     * chain, put it there.  This builds a most-frequently used order
+     * into the chains of the hash table, giving speedier lookups on
+     * those nicks which are being used currently.  This same block of
+     * code is also used for channels and servers for the same
+     * performance reasons.
+     * 
+     * I don't believe it does.. it only wastes CPU, lets try it and
+     * see....
+     * 
+     * - Dianora
+     */
+}
+
+/*
+ * hash_find_nickserver
+ */
+aClient *hash_find_nickserver(char *name, aClient *cptr)
+{
+    aClient *tmp;
+    aHashEntry *tmp3;
+    int hashv;
+    char *serv;
+
+    serv = strchr(name, '@');
+    *serv++ = '\0';
+    hashv = hash_nick_name(name);
+    tmp3 = &clientTable[hashv];
+    /* Got the bucket, now search the chain. */
+    for (tmp = (aClient *) tmp3->list; tmp; tmp = tmp->hnext)
+       if (mycmp(name, tmp->name) == 0 && tmp->user &&
+           mycmp(serv, tmp->user->server) == 0)
+       {
+           *--serv = '\0';
+           return (tmp);
+       }
+    
+    *--serv = '\0';
+    return (cptr);
+}
+
+/* hash_find_server*/
+aClient *hash_find_server(char *server, aClient *cptr)
+{
+    aClient *tmp;
+    aHashEntry *tmp3;
+    
+    int         hashv;
+    
+    hashv = hash_nick_name(server);
+    tmp3 = &clientTable[hashv];
+
+    for (tmp = (aClient *) tmp3->list; tmp; tmp = tmp->hnext)
+    {
+       if (!IsServer(tmp) && !IsMe(tmp))
+           continue;
+       if (mycmp(server, tmp->name) == 0)
+       {
+           return (tmp);
+       }
+    }
+    
+    /*
+     * Whats happening in this next loop ? Well, it takes a name like
+     * foo.bar.edu and proceeds to earch for *.edu and then *.bar.edu.
+     * This is for checking full server names against masks although it
+     * isnt often done this way in lieu of using matches().
+     */
+    return (cptr);
+}
+
+/* hash_find_channel */
+aChannel *hash_find_channel(char *name, aChannel *chptr)
+{
+    int         hashv;
+    aChannel *tmp;
+    aHashEntry *tmp3;
+    
+    hashv = hash_channel_name(name);
+    tmp3 = &channelTable[hashv];
+    
+    for (tmp = (aChannel *) tmp3->list; tmp; tmp = tmp->hnextch)
+       if (mycmp(name, tmp->chname) == 0)
+       {
+           return (tmp);
+       }
+    return chptr;
+}
+
+/*
+ * NOTE: this command is not supposed to be an offical part of the ircd
+ * protocol.  It is simply here to help debug and to monitor the
+ * performance of the hash functions and table, enabling a better
+ * algorithm to be sought if this one becomes troublesome. -avalon
+ * 
+ * Needs rewriting for DOUGH_HASH, consider this a place holder until
+ * thats done. Hopefully for hybrid-5, if not. tough. - Dianora
+ * 
+ */
+
+int m_hash(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    return 0;
+}
+
+/*
+ * Rough figure of the datastructures for notify:
+ *
+ * NOTIFY HASH      cptr1
+ *   |                |- nick1
+ * nick1-|- cptr1     |- nick2
+ *   |   |- cptr2                cptr3
+ *   |   |- cptr3   cptr2          |- nick1
+ *   |                |- nick1
+ * nick2-|- cptr2     |- nick2
+ *       |- cptr1
+ *
+ * add-to-notify-hash-table:
+ * del-from-notify-hash-table:
+ * hash-del-notify-list:
+ * hash-check-notify:
+ * hash-get-notify:
+ */
+
+static   aWatch  *watchTable[WATCHHASHSIZE];
+
+void  count_watch_memory(int *count, u_long *memory)
+{
+    int   i = WATCHHASHSIZE;
+    aWatch  *anptr;
+    
+    
+    while (i--)
+    {
+       anptr = watchTable[i];
+       while (anptr)
+       {
+           (*count)++;
+           (*memory) += sizeof(aWatch)+strlen(anptr->nick);
+           anptr = anptr->hnext;
+       }
+    }
+}
+
+void clear_watch_hash_table(void)
+{
+    memset((char *)watchTable, '\0', sizeof(watchTable));
+}
+
+
+/* add_to_watch_hash_table */
+int   add_to_watch_hash_table(char *nick, aClient *cptr)
+{
+    int   hashv;
+    aWatch  *anptr;
+    Link  *lp;
+    
+    
+    /* Get the right bucket... */
+    hashv = hash_nick_name(nick)%WATCHHASHSIZE;
+    
+    /* Find the right nick (header) in the bucket, or NULL... */
+    if ((anptr = (aWatch *)watchTable[hashv]))
+       while (anptr && mycmp(anptr->nick, nick))
+           anptr = anptr->hnext;
+    
+    /* If found NULL (no header for this nick), make one... */
+    if (!anptr)
+    {
+       anptr = (aWatch *)MyMalloc(sizeof(aWatch)+strlen(nick));
+       anptr->lasttime = timeofday;
+       strcpy(anptr->nick, nick);
+       
+       anptr->watch = NULL;
+       
+       anptr->hnext = watchTable[hashv];
+       watchTable[hashv] = anptr;
+    }
+    /* Is this client already on the watch-list? */
+    if ((lp = anptr->watch))
+       while (lp && (lp->value.cptr != cptr))
+           lp = lp->next;
+    
+    /* No it isn't, so add it in the bucket and client addint it */
+    if (!lp)
+    {
+       lp = anptr->watch;
+       anptr->watch = make_link();
+       anptr->watch->value.cptr = cptr;
+       anptr->watch->next = lp;
+       
+       lp = make_link();
+       lp->next = cptr->watch;
+       lp->value.wptr = anptr;
+       cptr->watch = lp;
+       cptr->watches++;
+    }
+    
+    return 0;
+}
+
+/* hash_check_watch */
+int hash_check_watch(aClient *cptr, int reply)
+{
+    int   hashv;
+    aWatch  *anptr;
+    Link  *lp;
+    
+    
+    /* Get us the right bucket */
+    hashv = hash_nick_name(cptr->name)%WATCHHASHSIZE;
+       
+    /* Find the right header in this bucket */
+    if ((anptr = (aWatch *)watchTable[hashv]))
+       while (anptr && mycmp(anptr->nick, cptr->name))
+           anptr = anptr->hnext;
+    if (!anptr)
+       return 0;   /* This nick isn't on watch */
+    
+    /* Update the time of last change to item */
+    anptr->lasttime = NOW;
+    
+    /* Send notifies out to everybody on the list in header */
+    for (lp = anptr->watch; lp; lp = lp->next)
+       sendto_one(lp->value.cptr, rpl_str(reply), me.name,
+                  lp->value.cptr->name, cptr->name,
+                  (IsPerson(cptr)?cptr->user->username:"<N/A>"),
+                  (IsPerson(cptr)?cptr->user->host:"<N/A>"),
+                  anptr->lasttime, cptr->info);
+    
+    return 0;
+}
+
+/* hash_get_watch */
+aWatch  *hash_get_watch(char *name)
+{
+    int   hashv;
+    aWatch  *anptr;
+    
+    
+    hashv = hash_nick_name(name)%WATCHHASHSIZE;
+    
+    if ((anptr = (aWatch *)watchTable[hashv]))
+       while (anptr && mycmp(anptr->nick, name))
+           anptr = anptr->hnext;
+    
+    return anptr;
+}
+
+/* del_from_watch_hash_table */
+int   del_from_watch_hash_table(char *nick, aClient *cptr)
+{
+    int   hashv;
+    aWatch  *anptr, *nlast = NULL;
+    Link  *lp, *last = NULL;
+    
+    
+    /* Get the bucket for this nick... */
+    hashv = hash_nick_name(nick)%WATCHHASHSIZE;
+    
+    /* Find the right header, maintaining last-link pointer... */
+    if ((anptr = (aWatch *)watchTable[hashv]))
+       while (anptr && mycmp(anptr->nick, nick))
+       {
+           nlast = anptr;
+           anptr = anptr->hnext;
+       }
+    if (!anptr)
+       return 0;   /* No such watch */
+    
+    /* Find this client from the list of notifies... with last-ptr. */
+    if ((lp = anptr->watch))
+       while (lp && (lp->value.cptr != cptr))
+       {
+           last = lp;
+           lp = lp->next;
+       }
+    if (!lp)
+       return 0;   /* No such client to watch */
+    
+    /* Fix the linked list under header, then remove the watch entry */
+    if (!last)
+       anptr->watch = lp->next;
+    else
+       last->next = lp->next;
+    free_link(lp);
+    
+    /* Do the same regarding the links in client-record... */
+    last = NULL;
+    if ((lp = cptr->watch))
+       while (lp && (lp->value.wptr != anptr))
+       {
+           last = lp;
+           lp = lp->next;
+       }
+    
+    /*
+     * Give error on the odd case... probobly not even neccessary
+     * No error checking in ircd is unneccessary ;) -Cabal95
+     */
+    if (!lp)
+       sendto_ops("WATCH debug error: del_from_watch_hash_table "
+                  "found a watch entry with no client "
+                  "counterpoint processing nick %s on client %s!",
+                  nick, cptr->user);
+    else
+    {
+       if (!last) /* First one matched */
+           cptr->watch = lp->next;
+       else
+           last->next = lp->next;
+       free_link(lp);
+    }
+    /* In case this header is now empty of notices, remove it */
+    if (!anptr->watch)
+    {
+       if (!nlast)
+           watchTable[hashv] = anptr->hnext;
+       else
+           nlast->hnext = anptr->hnext;
+       MyFree(anptr);
+    }
+    
+    /* Update count of notifies on nick */
+    cptr->watches--;
+    
+    return 0;
+}
+
+/* hash_del_watch_list */
+int   hash_del_watch_list(aClient *cptr)
+{
+    int   hashv;
+    aWatch  *anptr;
+    Link  *np, *lp, *last;
+    
+    
+    if (!(np = cptr->watch))
+       return 0;   /* Nothing to do */
+    
+    cptr->watch = NULL; /* Break the watch-list for client */
+    while (np)
+    {
+       /* Find the watch-record from hash-table... */
+       anptr = np->value.wptr;
+       last = NULL;
+       for (lp = anptr->watch; lp && (lp->value.cptr != cptr);
+            lp = lp->next)
+           last = lp;
+       
+       /* Not found, another "worst case" debug error */
+       if (!lp)
+           sendto_ops("WATCH Debug error: hash_del_watch_list "
+                      "found a WATCH entry with no table "
+                      "counterpoint processing client %s!",
+                      cptr->name);
+       else
+       {
+           /* Fix the watch-list and remove entry */
+           if (!last)
+               anptr->watch = lp->next;
+           else
+               last->next = lp->next;
+           free_link(lp);
+           
+           /*
+            * If this leaves a header without notifies,
+            * remove it. Need to find the last-pointer!
+            */
+           if (!anptr->watch)
+           {
+               aWatch  *np2, *nl;
+               
+               hashv = hash_nick_name(anptr->nick)%WATCHHASHSIZE;
+               
+               nl = NULL;
+               np2 = watchTable[hashv];
+               while (np2 != anptr)
+               {
+                   nl = np2;
+                   np2 = np2->hnext;
+               }
+               
+               if (nl)
+                   nl->hnext = anptr->hnext;
+               else
+                   watchTable[hashv] = anptr->hnext;
+               MyFree(anptr);
+           }
+       }
+       
+       lp = np; /* Save last pointer processed */
+       np = np->next; /* Jump to the next pointer */
+       free_link(lp); /* Free the previous */
+    }
+    
+    cptr->watches = 0;
+    
+    return 0;
+}
+
+aChannel *hash_get_chan_bucket(int hashv)
+{
+    if (hashv > CH_MAX)
+       return NULL;
+    return (aChannel *)channelTable[hashv].list;
+}
diff --git a/src/hide.c b/src/hide.c
new file mode 100644 (file)
index 0000000..5c4aab1
--- /dev/null
@@ -0,0 +1,344 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/hide.c
+ *   Copyright (C) 2003 Lucas Madar
+ *
+ *   hide.c - code for hiding information
+ *
+ */
+
+/* $Id: hide.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "fds.h"
+#include "numeric.h"
+
+/* This is how we maintain a 'fake' list of servers */
+
+struct fakelinkserver {
+   char *name;
+   char *description;
+};
+
+/* lserver_list is a linked list of Link structures,
+   each with the 'cp' member pointing to a struct fakelinkserver */
+
+static Link *lserver_list = NULL;
+
+static struct fakelinkserver *fakelinkserver_find(char *name)
+{
+   Link *lp;
+   struct fakelinkserver *ls;
+
+   for(lp = lserver_list; lp; lp = lp->next)
+   {
+      ls = (struct fakelinkserver *) lp->value.cp;
+      if(mycmp(name, ls->name) == 0)
+         return ls;
+   }
+   return NULL;
+}
+
+/*
+ * Delete the entire list
+ */
+void fakelinkserver_reset()
+{
+   Link *lp;
+   struct fakelinkserver *ls;
+
+   while((lp = lserver_list))
+   {
+      lserver_list = lp->next;
+
+      ls = (struct fakelinkserver *) lp->value.cp;
+      MyFree(ls->name);
+      MyFree(ls->description);
+      MyFree(ls);
+      free_link(lp);
+   }
+}
+
+static void fakelinkserver_delete(char *name)
+{
+   Link *lp, *lpprev, *lpn;
+   struct fakelinkserver *ls;
+
+   for(lp = lserver_list, lpprev = NULL; lp; lpprev = lp, lp = lpn)
+   {
+      lpn = lp->next;
+      ls = (struct fakelinkserver *) lp->value.cp;
+      if(mycmp(name, ls->name) == 0)
+      {
+         if(lpprev)
+            lpprev->next = lp->next;
+         else
+            lserver_list = lp->next;
+
+         MyFree(ls->name);
+         MyFree(ls->description);
+         MyFree(ls);
+         free_link(lp);
+         return;
+      }
+   }
+}
+
+static void fakelinkserver_add(char *name, char *desc)
+{
+   struct fakelinkserver *ls;
+   Link *lp;
+
+   if(fakelinkserver_find(name))
+      return;
+
+   ls = (struct fakelinkserver *) MyMalloc(sizeof(struct fakelinkserver));
+   ls->name = (char *) MyMalloc(strlen(name) + 1);
+   strcpy(ls->name, name);
+   ls->description = (char *) MyMalloc(strlen(desc) + 1);
+   strcpy(ls->description, desc);
+
+   lp = make_link();
+   lp->value.cp = (char *) ls;
+   lp->next = lserver_list;
+   lserver_list = lp;
+}
+
+/*
+ * update the server's description 
+ */
+void fakelinkserver_update(char *name, char *desc)
+{
+   struct fakelinkserver *ls;
+
+   if(!(ls = fakelinkserver_find(name)))
+      return;
+
+   MyFree(ls->description);
+   ls->description = (char *) MyMalloc(strlen(desc) + 1);
+   strcpy(ls->description, desc);
+}
+
+int m_linkscontrol(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+   if (!(parc > 1 && (IsServer(sptr) || IsULine(sptr))))
+      return 0;
+   else
+   {
+      char pbuf[512];
+
+      make_parv_copy(pbuf, parc, parv);
+      sendto_serv_butone(cptr, ":%s LINKSCONTROL %s", parv[0], pbuf);
+   }
+
+   if(mycmp(parv[1], "RESET") == 0)
+   {
+      fakelinkserver_reset();
+      return 0;
+   }
+   else if(parc > 2 && mycmp(parv[1], "+") == 0)
+   {
+      char *servername = parv[2];
+      aClient *acptr = find_server(servername, NULL);
+      char *desc = (parc > 3) ? parv[3] : HIDDEN_SERVER_DESC;
+
+      if(strchr(servername, '.') == NULL)
+         return 0;
+
+      if(strchr(servername, ' ') != NULL)
+         return 0;
+
+      fakelinkserver_add(servername, acptr ? acptr->info : desc);
+   }
+   else if(parc > 2 && mycmp(parv[1], "-") == 0)
+   {
+      char *servername = parv[2];
+
+      fakelinkserver_delete(servername);
+   }
+
+   return 0;
+}
+
+/* send the list to a client doing /links */
+void fakeserver_list(aClient *sptr)
+{
+   Link *lp;
+
+   for (lp = lserver_list; lp; lp = lp->next)
+   {
+      struct fakelinkserver *ls = (struct fakelinkserver *) lp->value.cp;
+
+      sendto_one(sptr, rpl_str(RPL_LINKS), me.name, sptr->name,
+                 ls->name, ls->name, 0, ls->description);
+   }
+}
+
+/* send the list to a client server (we are a hub server) */
+void fakeserver_sendserver(aClient *sptr)
+{
+   Link *lp;
+
+#if 0 /* Don't do this. */
+   /* tell them to reset their list */
+   sendto_one(sptr, ":%s LINKSCONTROL RESET", me.name);
+#endif
+
+   for (lp = lserver_list; lp; lp = lp->next)
+   {
+      struct fakelinkserver *ls = (struct fakelinkserver *) lp->value.cp;
+
+      sendto_one(sptr, ":%s LINKSCONTROL + %s :%s",
+                 me.name, ls->name, ls->description);
+   }
+}
+
+/* -1 if lusers isn't locked */
+static time_t luserslock_expiretime = -1;
+
+int is_luserslocked() 
+{
+   if(luserslock_expiretime == -1)
+      return 0;
+
+   if(luserslock_expiretime <= NOW)
+   {
+      luserslock_expiretime = -1;
+      sendto_realops("LUSERS lock has expired");
+      return 0;
+   }
+
+   return 1;
+}
+
+static struct fakelusers_struct {
+   int m_server;
+   int m_ulined;
+   int m_client;
+   int m_clientmax;
+   int i_count;
+   int c_count;
+   int s_count;
+   int o_count;
+   int chan_count;
+   int m_total;
+   int m_totalmax;
+} fakelusers = {0};
+
+static void dolocklusers()
+{
+   fakelusers.m_server = Count.myserver;
+   fakelusers.m_ulined = Count.myulined;
+   fakelusers.m_client = Count.local;
+   fakelusers.m_clientmax = Count.max_loc;
+   fakelusers.i_count = Count.invisi;
+   fakelusers.c_count = Count.total - Count.invisi;
+   fakelusers.s_count = Count.server;
+   fakelusers.o_count = Count.oper;
+   fakelusers.chan_count = Count.chan;
+   fakelusers.m_total = Count.total;
+   fakelusers.m_totalmax = Count.max_tot;
+}
+
+void send_fake_users(aClient *sptr)
+{
+    sendto_one(sptr, rpl_str(RPL_LOCALUSERS), me.name, sptr->name,
+                   fakelusers.m_client, fakelusers.m_clientmax);
+    sendto_one(sptr, rpl_str(RPL_GLOBALUSERS), me.name, sptr->name,
+               fakelusers.m_total, fakelusers.m_totalmax);
+}
+
+void send_fake_lusers(aClient *sptr) 
+{
+#ifdef SHOW_INVISIBLE_LUSERS
+   sendto_one(sptr, rpl_str(RPL_LUSERCLIENT), me.name, sptr->name,
+              fakelusers.c_count, fakelusers.i_count, fakelusers.s_count);
+#else
+   sendto_one(sptr,
+              ":%s %d %s :There are %d users on %d servers", me.name,
+              RPL_LUSERCLIENT, sptr->name, fakelusers.c_count,
+              fakelusers.s_count);
+#endif
+
+   if (fakelusers.o_count)
+      sendto_one(sptr, rpl_str(RPL_LUSEROP),
+                 me.name, sptr->name, fakelusers.o_count);
+
+   if(fakelusers.chan_count)
+      sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS),
+                 me.name, sptr->name, fakelusers.chan_count);
+
+    sendto_one(sptr, rpl_str(RPL_LUSERME), me.name, sptr->name,
+#ifdef HIDEULINEDSERVS
+               fakelusers.m_server - fakelusers.m_ulined);
+#else
+               fakelusers.m_server);
+#endif
+
+    sendto_one(sptr, rpl_str(RPL_LOCALUSERS), me.name, sptr->name,
+                   fakelusers.m_client, fakelusers.m_clientmax);
+    sendto_one(sptr, rpl_str(RPL_GLOBALUSERS), me.name, sptr->name,
+               fakelusers.m_total, fakelusers.m_totalmax);
+}
+
+int m_luserslock(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+   char pbuf[512];
+
+   if(!(IsULine(sptr) || IsServer(sptr)))
+      return 0;
+
+   /* LUSERSLOCK UNTIL <time> */
+   if(parc > 2 && mycmp(parv[1], "UNTIL") == 0) 
+   {
+      time_t until = strtol(parv[2], NULL, 0);
+
+      if(until < NOW)
+         return 0;
+
+      if(luserslock_expiretime != -1 && luserslock_expiretime < until)
+      {
+         sendto_realops("LUSERS lock extended by %s (%d minute duration from now)", 
+                        sptr->name, 1 + (until - NOW) / 60);
+
+         luserslock_expiretime = until;
+      }
+      else if(luserslock_expiretime == -1)
+      {
+         sendto_realops("LUSERS lock activated by %s (%d minute duration)", 
+                        sptr->name, 1 + (until - NOW) / 60);
+    
+         luserslock_expiretime = until;
+         dolocklusers();
+      }
+   }
+   /* LUSERSLOCK CANCEL */
+   else if(parc > 1 && mycmp(parv[1], "CANCEL") == 0)
+   {
+       if(confopts & FLAGS_HUB)
+         /* If I'm a hub, toss out anyone but services telling me to cancel. */
+        if(!IsULine(sptr))
+           return 0;
+
+      if(luserslock_expiretime != -1)
+      {
+         luserslock_expiretime = -1;
+         sendto_realops("LUSERS lock cancelled by %s", sptr->name);
+      }
+   }
+
+   make_parv_copy(pbuf, parc, parv);
+   sendto_serv_butone(cptr, ":%s LUSERSLOCK %s", parv[0], pbuf);
+
+   return 0;
+}
+
+void fakelusers_sendlock(aClient *sptr)
+{
+   if(luserslock_expiretime == -1)
+      sendto_one(sptr, ":%s LUSERSLOCK CANCEL", me.name);
+   else
+      sendto_one(sptr, ":%s LUSERSLOCK UNTIL %d", (int) luserslock_expiretime);
+}
diff --git a/src/inet_addr.c b/src/inet_addr.c
new file mode 100644 (file)
index 0000000..6df6344
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * ++Copyright++ 1983, 1990, 1993 - Copyright (c) 1983, 1990, 1993 The
+ * Regents of the University of California.  All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met: 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer. 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with
+ * the distribution. 3. All advertising materials mentioning features
+ * or use of this software must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors. 4. Neither the name of
+ * the University nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. - Portions Copyright (c) 1993 by Digital Equipment
+ * Corporation.
+ * 
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies, and that the name of Digital Equipment Corporation not be
+ * used in advertising or publicity pertaining to distribution of the
+ * document or software without specific, written prior permission.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL
+ * DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -
+ * --Copyright--
+ */
+
+/* $Id: inet_addr.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "setup.h"
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include <netinet/in.h>
+#include "nameser.h"
+#include "resolv.h"
+
+#ifndef        INADDR_NONE
+#define        INADDR_NONE     ((u_long)0xffffffff)
+#endif
+
+/* this is a compatibility routine, not needed on recent BSD releases */
+#if !defined( HAVE_INET_ADDR )
+/*
+ * Ascii internet address interpretation routine. The value returned is
+ * in network order.
+ */
+u_long inet_addr(const char *cp)
+{
+    struct in_addr val;
+
+    if (inet_aton(cp, &val))
+       return (val.s_addr);
+    return (INADDR_NONE);
+}
+#endif
+
+/*
+ * Check whether "cp" is a valid ascii representation of an Internet
+ * address and convert to a binary address. Returns 1 if the address is
+ * valid, 0 if not. This replaces inet_addr, the return value from
+ * which cannot distinguish between failure and a local broadcast
+ * address.
+ */
+#if !defined( HAVE_INET_ATON )
+int inet_aton(const u_char *cp, struct in_addr *addr)
+{
+    u_long val;
+    int base, n;
+    u_char c;
+    u_int       parts[4];
+    u_int *pp = parts;
+
+    for (;;)
+    {
+       /*
+        * Collect number up to ``.''. Values are specified as for C:
+        * 0x=hex, 0=octal, other=decimal.
+        */
+       val = 0;
+       base = 10;
+       if (*cp == '0')
+       {
+           if (*++cp == 'x' || *cp == 'X')
+               base = 16, cp++;
+           else
+               base = 8;
+       }
+       while ((c = *cp) != '\0')
+       {
+           if (IsAscii(c) && IsDigit(c))
+           {
+               val = (val * base) + (c - '0');
+               cp++;
+               continue;
+           }
+           if (base == 16 && IsAscii(c) && IsXDigit(c))
+           {
+               val = (val << 4) +
+                   (c + 10 - (IsLower(c) ? 'a' : 'A'));
+               cp++;
+               continue;
+           }
+           break;
+       }
+       if (*cp == '.')
+       {
+           /*
+            * Internet format: a.b.c.d a.b.c    (with c treated as
+            * 16-bits) a.b        (with b treated as 24 bits)
+            */
+           if (pp >= parts + 3 || val > 0xff)
+               return (0);
+           *pp++ = val, cp++;
+       }
+       else
+           break;
+    }
+    /* Check for trailing characters. */
+    if (*cp && (!IsAscii(*cp) || !IsSpace(*cp)))
+       return (0);
+    /* Concoct the address according to the number of parts specified. */
+    n = pp - parts + 1;
+    switch (n)
+    {
+    case 1:                    /* a -- 32 bits */
+       break;
+
+    case 2:                    /* a.b -- 8.24 bits */
+       if (val > 0xffffff)
+           return (0);
+       val |= parts[0] << 24;
+       break;
+
+    case 3:                    /* a.b.c -- 8.8.16 bits */
+       if (val > 0xffff)
+           return (0);
+       val |= (parts[0] << 24) | (parts[1] << 16);
+       break;
+
+    case 4:                    /* a.b.c.d -- 8.8.8.8 bits */
+       if (val > 0xff)
+           return (0);
+       val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+       break;
+    }
+    if (addr)
+       addr->s_addr = htonl(val);
+    return (1);
+}
+#endif
diff --git a/src/ircd.c b/src/ircd.c
new file mode 100644 (file)
index 0000000..d7d7076
--- /dev/null
@@ -0,0 +1,1290 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/ircd.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: ircd.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "sbuf.h"
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <pwd.h>
+#include <signal.h>
+#include <fcntl.h>
+#if defined PROFILING && defined __GLIBC__ && (__GLIBC__ >= 2)
+#include <sys/gmon.h>
+#define monstartup __monstartup
+#endif
+#include "inet.h"
+#include "h.h"
+#include "patchlevel.h"
+#include "dh.h"
+
+#include "throttle.h"
+#include "userban.h"
+#include "clones.h"
+#include "hooks.h"
+#include "fds.h"
+
+aMotd      *motd;
+aMotd      *helpfile;           /* misnomer, aMotd could be generalized */
+aMotd      *shortmotd;          /* short motd */
+
+struct tm  *motd_tm;
+
+/* global conf options (from option block) */
+char ProxyMonURL[TOPICLEN+1];
+char ProxyMonHost[HOSTLEN+1];
+char Network_Name[HOSTLEN+1];
+char Services_Name[HOSTLEN+1];
+char NS_Services_Name[HOSTLEN+9];
+char CS_Services_Name[HOSTLEN+9];
+char MS_Services_Name[HOSTLEN+9];
+char RS_Services_Name[HOSTLEN+9];
+char Stats_Name[HOSTLEN+1];
+char OS_Stats_Name[HOSTLEN+9];
+char SS_Stats_Name[HOSTLEN+9];
+char HS_Stats_Name[HOSTLEN+9];
+char NS_Register_URL[TOPICLEN+1];
+char Network_Kline_Address[HOSTLEN+1];
+char Local_Kline_Address[HOSTLEN+1];
+char Staff_Address[HOSTLEN+1];
+int  maxchannelsperuser, tsmaxdelta, tswarndelta;
+int  confopts, new_confopts;
+int  local_ip_limit, local_ip24_limit, global_ip_limit, global_ip24_limit;
+
+/* this stuff by mnystrom@mit.edu */
+#include "fdlist.h"
+
+fdlist      default_fdlist;     /* just the number of the entry */
+
+int         MAXCLIENTS = MAX_ACTIVECONN;   /* runtime configurable by m_set */
+
+struct Counter Count;
+int         R_do_dns, R_fin_dns, R_fin_dnsc, R_fail_dns, R_do_id,
+            R_fin_id, R_fail_id;
+
+time_t           NOW;
+time_t           last_stat_save;
+aClient          me;            /* That's me */
+aClient         *client = &me;  /* Pointer to beginning of Client list */
+
+int     forked = 0;
+
+float curSendK = 0, curRecvK = 0;
+
+#ifdef  LOCKFILE
+extern time_t    pending_kline_time;
+extern struct pkl *pending_klines;
+extern void      do_pending_klines(void);
+#endif
+extern void      engine_read_message(int);
+
+void            server_reboot();
+void            restart(char *);
+static void     open_debugfile(), setup_signals();
+static void     io_loop();
+
+/* externally needed functions */
+
+extern void     init_fdlist(fdlist *);      /* defined in fdlist.c */
+extern void     read_motd(char *);          /* defined in s_serv.c */
+extern void     read_shortmotd(char *);     /* defined in s_serv.c */
+extern void     read_help(char *);          /* defined in s_serv.c */
+extern void     init_globals();
+
+char        **myargv;
+char        configfile[PATH_MAX] = {0};     /* Server configuration file */
+
+int         debuglevel = -1;       /* Server debug level */
+int         bootopt = 0;           /* Server boot option flags */
+char        *debugmode = "";        /* -"-    -"-   -"-  */
+char        *sbrk0;                 /* initial sbrk(0) */
+static int  dorehash = 0;
+char        dpath[PATH_MAX] = {0};  /* our configure files live in here */
+char        spath[PATH_MAX] = {0};  /* the path to our binary */
+int         rehashed = 1;
+int         zline_in_progress = 0; /* killing off matching D lines */
+time_t      nextconnect = 1;       /* time for next try_connections call */
+time_t      nextping = 1;          /* same as above for check_pings() */
+time_t      nextdnscheck = 0;      /* next time to poll dns to force timeout */
+time_t      nextexpire = 1;        /* next expire run on the dns cache */
+time_t      nextbanexpire = 1;     /* next time to expire the throttles/userbans */
+
+#ifdef PROFILING
+extern void _start, etext;
+
+static int profiling_state = 1;
+static int profiling_newmsg = 0;
+static char profiling_msg[512];
+
+void s_dumpprof()
+{
+    char buf[32];
+
+    sprintf(buf, "gmon.%d", (int)time(NULL));
+    setenv("GMON_OUT_PREFIX", buf, 1);
+    _mcleanup();
+    monstartup ((u_long) &_start, (u_long) &etext);
+    setenv("GMON_OUT_PREFIX", "gmon.auto", 1);
+    sprintf(profiling_msg, "Reset profile, saved past profile data to %s", buf);
+    profiling_newmsg = 1;
+}
+
+void s_toggleprof()
+{
+    char buf[32];
+
+    if(profiling_state == 1)
+    {
+       sprintf(buf, "gmon.%d", (int)time(NULL));
+       setenv("GMON_OUT_PREFIX", buf, 1);
+       _mcleanup();
+       sprintf(profiling_msg, "Turned profiling OFF, saved profile data to %s", buf);
+       profiling_state = 0;
+    }
+    else
+    {
+       monstartup ((u_long) &_start, (u_long) &etext);
+       setenv("GMON_OUT_PREFIX", "gmon.auto", 1);
+       profiling_state = 1;
+       sprintf(profiling_msg, "Turned profiling ON");
+    } 
+    profiling_newmsg = 1;
+}
+
+#endif
+
+void s_die() 
+{
+    FILE *fp;
+    char tmp[PATH_MAX];
+    dump_connections(me.fd);
+#ifdef  USE_SYSLOG
+    (void) syslog(LOG_CRIT, "Server killed By SIGTERM");
+#endif
+    ircsprintf(tmp, "%s/.maxclients", dpath);
+    fp=fopen(tmp, "w");
+    if(fp!=NULL) 
+    {
+        fprintf(fp, "%d %d %li %li %li %ld %ld %ld %ld", Count.max_loc, 
+                Count.max_tot, Count.weekly, Count.monthly, 
+                Count.yearly, Count.start, Count.week, Count.month, 
+                Count.year);
+        fclose(fp);
+    }
+    exit(0);
+}
+
+static  void s_rehash() 
+{
+    struct sigaction act;
+    dorehash = 1;
+    act.sa_handler = s_rehash;
+    act.sa_flags = 0;
+    (void) sigemptyset(&act.sa_mask);
+    (void) sigaddset(&act.sa_mask, SIGHUP);
+    (void) sigaction(SIGHUP, &act, NULL);
+}
+
+void restart(char *mesg) 
+{
+    static int  was_here = NO;  /* redundant due to restarting flag below */
+    if (was_here)
+        abort();
+    was_here = YES;
+        
+#ifdef  USE_SYSLOG
+    (void) syslog(LOG_WARNING, "Restarting Server because: %s, sbrk(0)-etext: %d",
+                  mesg, (u_int) sbrk((size_t) 0) - (u_int) sbrk0);
+#endif
+    server_reboot();
+}
+
+void s_restart() 
+{
+    static int  restarting = 0;
+        
+#ifdef  USE_SYSLOG
+    (void) syslog(LOG_WARNING, "Server Restarting on SIGINT");
+#endif
+    if (restarting == 0) 
+    {
+        /* Send (or attempt to) a dying scream to oper if present */
+        restarting = 1;
+        server_reboot();
+    }
+}
+
+void server_reboot() 
+{
+    int     i;
+    sendto_ops("Aieeeee!!!  Restarting server... sbrk(0)-etext: %d",
+               (u_int) sbrk((size_t) 0) - (u_int) sbrk0);
+        
+    Debug((DEBUG_NOTICE, "Restarting server..."));
+    dump_connections(me.fd);
+    /*
+     * fd 0 must be 'preserved' if either the -d or -i options have
+     * been passed to us before restarting.
+     */
+#ifdef USE_SYSLOG
+    (void) closelog();
+#endif
+    for (i = 3; i < MAXCONNECTIONS; i++)
+        (void) close(i);
+
+    if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
+        (void) close(2);
+
+    (void) close(1);
+
+    if (!(bootopt & BOOT_OPER))
+        (void) execv(spath, myargv);
+
+#ifdef USE_SYSLOG
+    /* Have to reopen since it has been closed above */
+    openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
+    syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", spath, myargv[0]);
+    closelog();
+#endif
+
+    Debug((DEBUG_FATAL, "Couldn't restart server: %s", strerror(errno)));
+    exit(-1);
+}
+
+/*
+ * try_connections 
+ * 
+ *      Scan through configuration and try new connections. 
+ *   Returns  the calendar time when the next call to this 
+ *      function should be made latest. (No harm done if this 
+ *      is called earlier or later...)
+ */
+static time_t try_connections(time_t currenttime)
+{
+    aConnect  *aconn, **pconn, *con_conn = (aConnect *) NULL;
+    aClient   *cptr;
+    aClass    *cltmp;
+    int        connecting, confrq;
+    time_t      next = 0;
+
+    connecting = FALSE;
+
+    Debug((DEBUG_NOTICE, "Connection check at   : %s",
+           myctime(currenttime)));
+
+    for (aconn = connects; aconn; aconn = aconn->next) 
+    {
+        /* Also when already connecting! (update holdtimes) --SRB */
+        if (aconn->port <= 0 || aconn->class->connfreq == 0)
+            continue;
+        cltmp = aconn->class;
+
+        /*
+         * * Skip this entry if the use of it is still on hold until 
+         * future. Otherwise handle this entry (and set it on hold 
+         * until next time). Will reset only hold times, if already 
+         * made one successfull connection... [this algorithm is a bit
+         * fuzzy... -- msa >;) ]
+         */
+
+        if ((aconn->hold > currenttime)) 
+        {
+            if ((next > aconn->hold) || (next == 0))
+                next = aconn->hold;
+            continue;
+        }
+
+        confrq = cltmp->connfreq;
+        aconn->hold = currenttime + confrq;
+
+        /* Found a CONNECT config with port specified, scan clients 
+         * and see if this server is already connected?
+         */
+
+        cptr = find_name(aconn->name, (aClient *) NULL);
+
+        if (!cptr && (cltmp->links < cltmp->maxlinks) && !connecting) 
+        {
+            con_conn = aconn;
+            /* We connect only one at time... */
+            connecting = TRUE;
+        }
+
+        if ((next > aconn->hold) || (next == 0))
+            next = aconn->hold;
+    }
+
+    if (connecting) 
+    {
+        if (con_conn->next)     /* are we already last? */
+        {
+            for (pconn = &connects; (aconn = *pconn);
+                 pconn = &(aconn->next))
+                /*
+                 * put the current one at the end and make sure we try all
+                 * connections
+                 */
+                if (aconn == con_conn)
+                    *pconn = aconn->next;
+            (*pconn = con_conn)->next = 0;
+        }
+        if (connect_server(con_conn, (aClient *) NULL,
+                           (struct hostent *) NULL) == 0)
+            sendto_gnotice("from %s: Connection to %s activated.", me.name,
+                           con_conn->name);
+    }
+    Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next)));
+    return (next);
+}
+
+/* dianora's code in the new checkpings is slightly wasteful.
+ * however, upon inspection (thanks seddy), when we close a connection,
+ * the ordering of local[i] is NOT reordered; simply local[highest_fd] becomes
+ * local[i], so we can just i--;  - lucas
+ */
+
+static time_t check_pings(time_t currenttime)
+{
+    aClient     *cptr;
+    int          ping = 0, i;
+    time_t       oldest = 0; /* timeout removed, see EXPLANATION below */
+    char         fbuf[512], *errtxt = "No response from %s, closing link";
+
+
+    for (i = 0; i <= highest_fd; i++) 
+    {
+        if (!(cptr = local[i]) || IsMe(cptr) || IsLog(cptr))
+            continue;
+
+        /* Note: No need to notify opers here. It's 
+         * already done when "FLAGS_DEADSOCKET" is set.
+         */
+
+        if (cptr->flags & FLAGS_DEADSOCKET) 
+        {
+            exit_client(cptr, cptr, &me, (cptr->flags & FLAGS_SENDQEX) ?
+                        "SendQ exceeded" : "Dead socket");
+            continue;
+        }
+
+        if (IsRegistered(cptr))
+            ping = cptr->class->pingfreq;
+        else
+            ping = CONNECTTIMEOUT;
+
+        /*
+         * Ok, so goto's are ugly and can be avoided here but this code
+         * is already indented enough so I think its justified. -avalon
+         *
+         * justified by what? laziness? <g>
+         * If the client pingtime is fine (ie, not larger than the client ping) 
+         * skip over all the checks below. - lucas
+         */
+        
+        if (ping < (currenttime - cptr->lasttime))
+        {
+            /*
+             * If the server hasnt talked to us in 2*ping seconds and it has
+             * a ping time, then close its connection. If the client is a
+             * user and a KILL line was found to be active, close this
+             * connection too.
+             */
+            if (((cptr->flags & FLAGS_PINGSENT) &&
+                 ((currenttime - cptr->lasttime) >= (2 * ping))) ||
+                ((!IsRegistered(cptr) && 
+                  (currenttime - cptr->since) >= ping))) 
+            {
+                if (!IsRegistered(cptr) && (DoingDNS(cptr) || 
+                                            DoingAuth(cptr))) 
+                {
+                    if (cptr->authfd >= 0) 
+                    {
+                        del_fd(cptr->authfd);
+                        close(cptr->authfd);
+                        cptr->authfd = -1;
+                        cptr->count = 0;
+                        *cptr->buffer = '\0';
+                    }
+#ifdef SHOW_HEADERS
+                    if (DoingDNS(cptr))
+                        sendto_one(cptr, REPORT_FAIL_DNS);
+                    if (DoingAuth(cptr))
+                        sendto_one(cptr, REPORT_FAIL_ID);
+#endif
+                    Debug((DEBUG_NOTICE, "DNS/AUTH timeout %s",
+                           get_client_name(cptr, TRUE)));
+                    del_queries((char *) cptr);
+                    ClearAuth(cptr);
+                    ClearDNS(cptr);
+                    cptr->since = currenttime;
+                    check_client_fd(cptr);
+                    continue;
+                }
+                
+                if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) 
+                {
+                    ircsprintf(fbuf, "from %s: %s", me.name, errtxt);
+                    sendto_gnotice(fbuf, get_client_name(cptr, HIDEME));
+                    ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt);
+                    sendto_serv_butone(cptr, fbuf, 
+                                       get_client_name(cptr, HIDEME));
+                }
+                
+                exit_client(cptr, cptr, &me, "Ping timeout");
+                continue;
+            } /* don't send pings during a burst, as we send them already. */
+            else if (!(cptr->flags & (FLAGS_PINGSENT|FLAGS_BURST)) && 
+                     !(IsConnecting(cptr) || IsHandshake(cptr))) 
+            {
+                /*
+                 * if we havent PINGed the connection and we havent heard from
+                 * it in a while, PING it to make sure it is still alive.
+                 */
+                cptr->flags |= FLAGS_PINGSENT;
+                /* not nice but does the job */
+                cptr->lasttime = currenttime - ping;
+                sendto_one(cptr, "PING :%s", me.name);
+            }
+        }
+        
+        /* see EXPLANATION below
+         *
+         * timeout = cptr->lasttime + ping;
+         * while (timeout <= currenttime)
+         *  timeout += ping;
+         * if (timeout < oldest || !oldest)
+         *   oldest = timeout;
+         */
+
+        /*
+         * Check UNKNOWN connections - if they have been in this state
+         * for > 100s, close them.
+         */
+        if (IsUnknown(cptr))
+            if (cptr->firsttime ? ((timeofday - cptr->firsttime) > 100) : 0) 
+                (void) exit_client(cptr, cptr, &me, "Connection Timed Out");
+    }
+    
+    rehashed = 0;
+    zline_in_progress = 0;
+    
+    /* EXPLANATION
+     * on a server with a large volume of clients, at any given point
+     * there may be a client which needs to be pinged the next second,
+     * or even right away (a second may have passed while running
+     * check_pings). Preserving CPU time is more important than
+     * pinging clients out at exact times, IMO. Therefore, I am going to make
+     * check_pings always return currenttime + 9. This means that it may take
+     * a user up to 9 seconds more than pingfreq to timeout. Oh well.
+     * Plus, the number is 9 to 'stagger' our check_pings calls out over
+     * time, to avoid doing it and the other tasks ircd does at the same time
+     * all the time (which are usually done on intervals of 5 seconds or so). 
+     * - lucas
+     *
+     *  if (!oldest || oldest < currenttime)
+     *     oldest = currenttime + PINGFREQUENCY;
+     */
+
+    oldest = currenttime + 9;
+
+    Debug((DEBUG_NOTICE, "Next check_ping() call at: %s, %d %d %d",
+           myctime(oldest), ping, oldest, currenttime));
+
+    return oldest;
+}
+
+/* get_paths()
+ * setup our file paths
+ */
+
+void get_paths(char *argv)
+{
+    char        t_dpath[PATH_MAX], t_d2path[PATH_MAX], tmp[PATH_MAX],
+                tmp2[PATH_MAX];
+    int len, fd;
+    
+    *t_dpath = 0;
+    *t_d2path = 0;
+    *tmp = 0;
+    *tmp2 = 0;
+
+    if(!*configfile)
+    {
+        getcwd(t_dpath, PATH_MAX);  /* directory we're called from */
+        if(argv[0] == '/')       /* absolute filename used to call */
+            strcat(spath, argv);
+        else
+        {
+            strcat(spath, t_dpath);
+            strcat(spath, "/");
+            strcat(spath, argv);
+        }
+        strcat(tmp, t_dpath);
+        strcat(tmp, "/ircd.conf");
+        if((fd = open(tmp, O_RDONLY)) > 0)
+        {
+            /* found our ircd.conf in the directory
+             * where we were called from */
+            strcpy(configfile, tmp);
+            close(fd);
+            strcpy(dpath, t_dpath);
+            return;
+        }
+        len = strlen(spath);
+        while(spath[len] != '/')
+            len--;
+        strncat(t_d2path, spath, len);
+        strcat(tmp2, t_d2path);
+        strcat(tmp2, "/ircd.conf");
+        if((fd = open(tmp2, O_RDONLY)) > 0)
+        {
+            /* found the ircd.conf in the directory local
+             * to our binary itself */
+            strcpy(configfile, tmp);
+            close(fd);
+            strcpy(dpath, t_d2path);
+            return;
+        }
+    }
+    else
+    {
+        getcwd(t_dpath, PATH_MAX);  /* directory we're called from */
+        if(argv[0] == '/')       /* absolute filename used to call */
+            strcat(spath, argv);
+        else
+        {
+            strcat(spath, t_dpath);
+            strcat(spath, "/");
+            strcat(spath, argv);
+        }
+        if(configfile[0] == '/')     /* absolute filename in configfile */
+        {
+            len = strlen(configfile);
+            while(configfile[len] != '/')
+                len--;
+            strncat(dpath, configfile, len);
+        }
+        else
+        {
+            strcat(dpath, t_dpath);
+            strcat(dpath, "/");
+            if(strchr(configfile, '/'))
+            {
+                len = strlen(configfile);
+                while(configfile[len] != '/')
+                    len--;
+                strncat(dpath, configfile, len);
+            }
+        }
+    }
+    printf("CONFIGFILE: %s\n", configfile);
+}
+
+
+/*
+ * bad_command 
+ *    This is called when the commandline is not acceptable. 
+ *    Give error message and exit without starting anything.
+ */
+static int bad_command()
+{
+    printf("Usage: ircd ");
+#ifdef CMDLINE_CONFIG
+    printf("[-f configfile] ");
+#endif
+    printf("[-t] [-v]\n");
+    printf("-t will cause ircd not to fork (mostly for debugging)\n");
+    printf("-v will cause ircd to print its version and quit\n");
+    printf("Server Not Started\n");
+    return (-1);
+}
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/* ripped this out of hybrid7 out of lazyness. */
+static void
+setup_corefile()
+{
+#ifdef HAVE_SYS_RESOURCE_H
+  struct rlimit rlim; /* resource limits */
+
+  /* Set corefilesize to maximum */
+  if (!getrlimit(RLIMIT_CORE, &rlim))
+    {
+      rlim.rlim_cur = rlim.rlim_max;
+      setrlimit(RLIMIT_CORE, &rlim);
+    }
+#endif
+}
+
+char REPORT_DO_DNS[256], REPORT_FIN_DNS[256], REPORT_FIN_DNSC[256], 
+    REPORT_FAIL_DNS[256], REPORT_DO_ID[256], REPORT_FIN_ID[256], 
+    REPORT_FAIL_ID[256];
+
+FILE *dumpfp=NULL;
+
+int
+main(int argc, char *argv[])
+{
+    uid_t         uid, euid;
+    char        tmp[PATH_MAX];
+    FILE        *mcsfp;
+    char        *conferr;
+        
+    if ((timeofday = time(NULL)) == -1) 
+    {
+        (void) fprintf(stderr, "ERROR: Clock Failure (%d)\n", errno);
+        exit(errno);
+    }
+        
+    build_version();
+
+    printf("\n%s booting...\n", version);
+    printf("Security related issues should be sent to coders@dal.net\n");
+    printf("All other issues should be sent to dalnet-src@dal.net\n\n");
+
+    setup_corefile();
+
+    Count.server = 1;           /* us */
+    Count.oper = 0;
+    Count.chan = 0;
+    Count.local = 0;
+    Count.total = 0;
+    Count.invisi = 0;
+    Count.unknown = 0;
+    Count.max_loc = 0;
+    Count.max_tot = 0;
+    Count.today = 0;
+    Count.weekly = 0;
+    Count.monthly = 0;
+    Count.yearly = 0;
+    Count.start = NOW;
+    Count.day = NOW;
+    Count.week = NOW;
+    Count.month = NOW;
+    Count.year = NOW;
+
+    /*
+     * this code by mika@cs.caltech.edu 
+     * it is intended to keep the ircd from being swapped out. BSD
+     * swapping criteria do not match the requirements of ircd
+     */
+
+#if defined(INITIAL_SBUFS_LARGE) && defined(INITIAL_SBUFS_SMALL)        
+    sbuf_init();        
+#endif
+    
+    sbrk0 = (char *) sbrk((size_t) 0);
+    uid = getuid();
+    euid = geteuid();
+
+#ifdef PROFILING
+    setenv("GMON_OUT_PREFIX", "gmon.out", 1);
+    (void) signal(SIGUSR1, s_dumpprof);
+    (void) signal(SIGUSR2, s_toggleprof);
+#endif
+        
+    myargv = argv;
+    (void) umask(077);          /* better safe than sorry --SRB  */
+    memset((char *) &me, '\0', sizeof(me));
+    
+    setup_signals();
+    /*
+     * * All command line parameters have the syntax "-fstring"  or "-f
+     * string" (e.g. the space is optional). String may  be empty. Flag
+     * characters cannot be concatenated (like "-fxyz"), it would
+     * conflict with the form "-fstring".
+     */
+    while (--argc > 0 && (*++argv)[0] == '-') 
+    {
+        char       *p = argv[0] + 1;
+        int         flag = *p++;
+        
+        if (flag == '\0' || *p == '\0') 
+        {
+            if (argc > 1 && argv[1][0] != '-') 
+            {
+                p = *++argv;
+                argc -= 1;
+            }
+            else
+                p = "";
+        }
+                
+        switch (flag) 
+        {
+#ifdef CMDLINE_CONFIG
+        case 'f':
+            (void) setuid((uid_t) uid);
+            strcpy(configfile, p);
+            break;
+#endif
+        case 's':
+            bootopt |= BOOT_STDERR;
+            break;
+        case 't':
+            (void) setuid((uid_t) uid);
+            bootopt |= BOOT_TTY;
+            break;
+        case 'v':
+            (void) printf("%s\n", version);
+            exit(0);
+        case 'x':
+#ifdef  DEBUGMODE
+            (void) setuid((uid_t) uid);
+            debuglevel = atoi(p);
+            debugmode = *p ? p : "0";
+            bootopt |= BOOT_DEBUG;
+            break;
+#else
+            bad_command();
+            break;
+#endif
+        default:
+            bad_command();
+            break;
+        }
+    }
+
+    get_paths(myargv[0]);
+
+    if(chdir(dpath))
+    {
+        printf("Error changing directory to ircd.conf location\n");
+        printf("Server not started\n");
+        exit(0);
+    }
+
+    ircsprintf(tmp, "%s/.maxclients", dpath);
+    mcsfp = fopen(tmp, "r");
+    if(mcsfp != NULL)
+    {
+        fscanf(mcsfp, "%d %d %li %li %li %ld %ld %ld %ld", &Count.max_loc,
+               &Count.max_tot, &Count.weekly, &Count.monthly, &Count.yearly,
+               &Count.start, &Count.week, &Count.month, &Count.year);
+        fclose(mcsfp);
+    }
+
+    if ((uid != euid) && !euid) 
+    {
+        printf("Do not run ircd as root.\nAborting...\n");
+        exit(-1);
+    }
+        
+    if (argc > 0)
+        return bad_command();   /* This should exit out  */
+
+    init_globals();
+
+#ifdef HAVE_ENCRYPTION_ON
+    printf("Initializing Encryption...");
+    if(dh_init() == -1)
+    {
+        printf("\n\nEncryption Init failed!\n\n");
+        return 0;
+    }
+#endif
+    
+    motd = (aMotd *) NULL;
+    helpfile = (aMotd *) NULL;
+    motd_tm = NULL;
+    shortmotd = NULL;
+        
+    clear_client_hash_table();
+    clear_channel_hash_table();
+    clear_scache_hash_table();  /* server cache name table */
+
+    /* init the throttle system -wd */
+    throttle_init();
+
+    /* clone tracking and limiting */
+    clones_init();
+
+    /* init the file descriptor tracking system */
+    init_fds();
+
+    /* init the kline/akill system */
+    init_userban();
+
+    initlists();
+    initwhowas();
+    initstats();
+    init_tree_parse(msgtab);
+    init_send();
+    open_debugfile();
+    NOW = time(NULL);
+
+    initclass();
+
+    if(initconf(configfile) == -1)
+    {
+        printf("Server not started\n");
+        exit(-1);
+    }
+    conferr = finishconf();
+    if (conferr)
+    {
+        printf("ERROR: %s in config file\nServer not started\n", conferr);
+        exit(-1);
+    }
+    merge_confs();
+    build_rplcache();
+    read_motd(MOTD);
+    read_help(HELPFILE);
+    if(confopts & FLAGS_SMOTD)
+        read_shortmotd(SHORTMOTD);
+    printf("Configuration Loaded.\n");
+
+    init_fdlist(&default_fdlist);
+    {
+        int i;
+                  
+        for (i = MAXCONNECTIONS + 1; i > 0; i--) 
+        {
+            default_fdlist.entry[i] = i - 1;
+        }
+    }
+
+    /* init the modules, load default modules! */
+    init_modules();
+
+    me.flags = FLAGS_LISTEN;
+    me.fd = -1;
+        
+    /* We don't want to calculate these every time they are used :) */
+        
+    sprintf(REPORT_DO_DNS, REPORT_DO_DNS_, me.name);
+    sprintf(REPORT_FIN_DNS, REPORT_FIN_DNS_, me.name);
+    sprintf(REPORT_FIN_DNSC, REPORT_FIN_DNSC_, me.name);
+    sprintf(REPORT_FAIL_DNS, REPORT_FAIL_DNS_, me.name);
+    sprintf(REPORT_DO_ID, REPORT_DO_ID_, me.name);
+    sprintf(REPORT_FIN_ID, REPORT_FIN_ID_, me.name);
+    sprintf(REPORT_FAIL_ID, REPORT_FAIL_ID_, me.name);
+    R_do_dns = strlen(REPORT_DO_DNS);
+    R_fin_dns = strlen(REPORT_FIN_DNS);
+    R_fin_dnsc = strlen(REPORT_FIN_DNSC);
+    R_fail_dns = strlen(REPORT_FAIL_DNS);
+    R_do_id = strlen(REPORT_DO_ID);
+    R_fin_id = strlen(REPORT_FIN_ID);
+    R_fail_id = strlen(REPORT_FAIL_ID);
+        
+    NOW = time(NULL);
+        
+    init_sys();
+    forked = 1;
+
+#ifdef USE_SYSLOG
+# define SYSLOG_ME     "ircd"
+    openlog(SYSLOG_ME, LOG_PID | LOG_NDELAY, LOG_FACILITY);
+#endif
+
+    /* the pid file must be written *AFTER* the fork */
+    write_pidfile();
+        
+
+    /* moved this to here such that we allow more verbose error
+     * checking on startup.  -epi
+     */
+    open_listeners();
+
+    get_my_name(&me, me.sockhost, sizeof(me.sockhost) - 1);
+    if (me.name[0] == '\0')
+        strncpyzt(me.name, me.sockhost, sizeof(me.name));
+    me.hopcount = 0;
+    me.authfd = -1;
+    me.next = NULL;
+    me.user = NULL;
+    me.from = &me;
+    SetMe(&me);
+    make_server(&me);
+    me.serv->up = me.name;
+    me.lasttime = me.since = me.firsttime = NOW;
+    (void) add_to_client_hash_table(me.name, &me);
+
+
+#ifdef DUMP_DEBUG
+    dumpfp=fopen("dump.log", "w");
+#endif
+#ifdef USE_SYSLOG
+    syslog(LOG_NOTICE, "Server Ready");
+#endif
+    
+    io_loop();
+    return 0;
+}
+
+void do_recvqs()
+{
+   DLink *lp, *lpn;
+   aClient *cptr;
+
+   for(lp = recvq_clients; lp; lp = lpn)
+   {
+      lpn = lp->next;
+      cptr = lp->value.cptr;
+
+      if(SBufLength(&cptr->recvQ) && !NoNewLine(cptr))
+      {
+         if(do_client_queue(cptr) == FLUSH_BUFFER)
+            continue;
+      }
+
+      if(!(SBufLength(&cptr->recvQ) && !NoNewLine(cptr)))
+      {
+         remove_from_list(&recvq_clients, cptr, lp);
+         cptr->flags &= ~(FLAGS_HAVERECVQ);
+      }
+   }
+}
+
+void send_safelists()
+{
+   DLink *lp, *lpn;
+   aClient *cptr;
+
+   for(lp = listing_clients; lp; lp = lpn)
+   {
+      lpn = lp->next;
+
+      cptr = lp->value.cptr;
+      while(DoList(cptr) && IsSendable(cptr))
+         send_list(cptr, 64);
+   }
+}
+
+void io_loop()
+{
+    char to_send[200];
+    int lastexp=0;
+
+    time_t      next10sec = 0; /* For events we do every 10 seconds */
+
+    time_t      lastbwcalc = 0;
+    long        lastbwSK = 0, lastbwRK = 0;
+    time_t      lasttimeofday;
+    int delay = 0;
+
+    while(1)
+    {
+        lasttimeofday = timeofday;
+
+        if ((timeofday = time(NULL)) == -1) 
+        {
+#ifdef USE_SYSLOG
+            syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", 
+                   errno);
+#endif
+            sendto_ops("Clock Failure (%d), TS can be corrupted", errno);
+        }
+
+        if (timeofday < lasttimeofday) 
+        {
+            ircsprintf(to_send, "System clock running backwards - (%d < %d)",
+                       timeofday, lasttimeofday);
+            report_error(to_send, &me);
+        }
+
+        NOW = timeofday;
+
+        /*
+         * Calculate a moving average of our total traffic.     
+         * Traffic is a 4 second average, 'sampled' every 2 seconds.
+         */
+
+        if((timeofday - lastbwcalc) >= 2)
+        {
+            long ilength = timeofday - lastbwcalc;
+
+            curSendK += (float) (me.sendK - lastbwSK) / (float) ilength;
+            curRecvK += (float) (me.receiveK - lastbwRK) / (float) ilength;
+            curSendK /= 2;
+            curRecvK /= 2;
+
+            lastbwSK = me.sendK;
+            lastbwRK = me.receiveK;
+            lastbwcalc = timeofday;
+        }
+
+        /*
+         * We only want to connect if a connection is due, not every
+         * time through.  Note, if there are no active C lines, this call
+         * to Tryconnections is made once only; it will return 0. - avalon
+         */
+
+        if (nextconnect && timeofday >= nextconnect)
+            nextconnect = try_connections(timeofday);
+
+        /* DNS checks. One to timeout queries, one for cache expiries.*/
+
+        if (timeofday >= nextdnscheck)
+            nextdnscheck = timeout_query_list(timeofday);
+        if (timeofday >= nextexpire)
+            nextexpire = expire_cache(timeofday);
+
+        if (timeofday >= nextbanexpire)
+        {
+            /*
+             * magic number: 13 seconds
+             * space out these heavy tasks at semi-random intervals, so as not to coincide
+             * with anything else ircd does regularly 
+             */
+            nextbanexpire = NOW + 13;
+            
+            if(lastexp == 0)
+            {
+                expire_userbans();
+                lastexp++;
+            }
+            else if(lastexp == 1)
+            {
+                expire_simbans();
+                lastexp++;
+            }
+            else
+            {
+                throttle_timer(NOW);
+                lastexp = 0;
+            }
+        }
+
+        if (timeofday >= next10sec)
+        {
+            next10sec = timeofday + 10;
+            call_hooks(CHOOK_10SEC);
+        }
+
+        /*
+         * take the smaller of the two 'timed' event times as the time
+         * of next event (stops us being late :) - avalon WARNING -
+         * nextconnect can return 0!
+         */
+
+        if (nextconnect)
+            delay = MIN(nextping, nextconnect);
+        else
+            delay = nextping;
+        delay = MIN(nextdnscheck, delay);
+        delay = MIN(nextexpire, delay);
+        delay -= timeofday;
+
+        /*
+         * Parse people who have blocked recvqs
+         */
+        do_recvqs();
+
+        /*
+         * Send people their /list replies, being careful
+         * not to fill their sendQ
+         */
+        send_safelists();
+
+        /*
+         * Adjust delay to something reasonable [ad hoc values] (one
+         * might think something more clever here... --msa) 
+         * We don't really need to check that often and as long 
+         * as we don't delay too long, everything should be ok. 
+         * waiting too long can cause things to timeout... 
+         * i.e. PINGS -> a disconnection :( 
+         * - avalon
+         */
+        if (delay < 1)
+            delay = 1;
+        else
+        {
+            /* We need to get back here to do that recvq thing */
+            if(recvq_clients != NULL)
+                delay = 1;
+            else
+                delay = MIN(delay, TIMESEC);
+        }
+
+        engine_read_message(delay);     /* check everything! */
+
+        /*
+         * * ...perhaps should not do these loops every time, but only if
+         * there is some chance of something happening (but, note that
+         * conf->hold times may be changed elsewhere--so precomputed next
+         * event time might be too far away... (similarly with ping
+         * times) --msa
+         */
+        
+        if ((timeofday >= nextping))
+            nextping = check_pings(timeofday);
+
+#ifdef PROFILING
+        if (profiling_newmsg)
+        {
+            sendto_realops("PROFILING: %s", profiling_msg);
+            profiling_newmsg = 0;
+        }
+#endif
+        
+        if (dorehash) 
+        {
+            (void) rehash(&me, &me, 1);
+        (void) read_motd(MOTD);
+            dorehash = 0;
+        }
+        /*
+         * 
+         * Flush output buffers on all connections now if they 
+         * have data in them (or at least try to flush)  -avalon
+         *
+         * flush_connections(me.fd);
+         *
+         * avalon, what kind of crack have you been smoking? why
+         * on earth would we flush_connections blindly when
+         * we already check to see if we can write (and do)
+         * in read_message? There is no point, as this causes
+         * lots and lots of unnecessary sendto's which 
+         * 99% of the time will fail because if we couldn't
+         * empty them in read_message we can't empty them here.
+         * one effect: during htm, output to normal lusers
+         * will lag.
+     * htm doesnt exist anymore, but this comment was funny, so i
+     * left it in. -epi
+         */
+        
+        /* Now we've made this call a bit smarter. */
+        /* Only flush non-blocked sockets. */
+        
+        flush_connections(me.fd);
+        
+#ifdef  LOCKFILE
+        /*
+         * * If we have pending klines and CHECK_PENDING_KLINES minutes
+         * have passed, try writing them out.  -ThemBones
+         */
+        
+        if ((pending_klines) && ((timeofday - pending_kline_time)
+                                 >= (CHECK_PENDING_KLINES * 60)))
+            do_pending_klines();
+#endif
+    }
+}
+
+/*
+ * open_debugfile
+ * 
+ * If the -t option is not given on the command line when the server is
+ * started, all debugging output is sent to the file set by LPATH in
+ * config.h Here we just open that file and make sure it is opened to
+ * fd 2 so that any fprintf's to stderr also goto the logfile.  If the
+ * debuglevel is not set from the command line by -x, use /dev/null as
+ * the dummy logfile as long as DEBUGMODE has been defined, else dont
+ * waste the fd.
+ */
+static void open_debugfile()
+{
+#ifdef  DEBUGMODE
+    int         fd;
+    aClient    *cptr;
+
+    if (debuglevel >= 0) 
+    {
+        cptr = make_client(NULL, NULL);
+        cptr->fd = 2;
+        SetLog(cptr);
+        cptr->port = debuglevel;
+        cptr->flags = 0;
+        /*XXX cptr->acpt = cptr; */
+        local[2] = cptr;
+        (void) strcpy(cptr->sockhost, me.sockhost);
+
+        (void) printf("isatty = %d ttyname = %#x\n",
+                      isatty(2), (u_int) ttyname(2));
+        if (!(bootopt & BOOT_TTY))      /* leave debugging output on fd */ 
+        {
+            (void) truncate(LOGFILE, 0);
+            if ((fd = open(LOGFILE, O_WRONLY | O_CREAT, 0600)) < 0)
+                if ((fd = open("/dev/null", O_WRONLY)) < 0)
+                    exit(-1);
+            if (fd != 2) 
+            {
+                (void) dup2(fd, 2);
+                (void) close(fd);
+            }
+            strncpyzt(cptr->name, LOGFILE, sizeof(cptr->name));
+        }
+        else if (isatty(2) && ttyname(2))
+            strncpyzt(cptr->name, ttyname(2), sizeof(cptr->name));
+        else
+            (void) strcpy(cptr->name, "FD2-Pipe");
+        Debug((DEBUG_FATAL, "Debug: File <%s> Level: %d at %s",
+               cptr->name, cptr->port, myctime(time(NULL))));
+    }
+    else
+        local[2] = NULL;
+#endif
+    return;
+}
+
+static void setup_signals()
+{
+    struct sigaction act;
+
+    act.sa_handler = SIG_IGN;
+    act.sa_flags = 0;
+    (void) sigemptyset(&act.sa_mask);
+    (void) sigaddset(&act.sa_mask, SIGPIPE);
+    (void) sigaddset(&act.sa_mask, SIGALRM);
+# ifdef SIGWINCH
+    (void) sigaddset(&act.sa_mask, SIGWINCH);
+    (void) sigaction(SIGWINCH, &act, NULL);
+# endif
+    (void) sigaction(SIGPIPE, &act, NULL);
+    act.sa_handler = dummy;
+    (void) sigaction(SIGALRM, &act, NULL);
+    act.sa_handler = s_rehash;
+    (void) sigemptyset(&act.sa_mask);
+    (void) sigaddset(&act.sa_mask, SIGHUP);
+    (void) sigaction(SIGHUP, &act, NULL);
+    act.sa_handler = s_restart;
+    (void) sigaddset(&act.sa_mask, SIGINT);
+    (void) sigaction(SIGINT, &act, NULL);
+    act.sa_handler = s_die;
+    (void) sigaddset(&act.sa_mask, SIGTERM);
+    (void) sigaction(SIGTERM, &act, NULL);
+
+#ifdef RESTARTING_SYSTEMCALLS
+    /*
+     * * At least on Apollo sr10.1 it seems continuing system calls 
+     * after signal is the default. The following 'siginterrupt' 
+     * should change that default to interrupting calls.
+     */
+    (void) siginterrupt(SIGALRM, 1);
+#endif
+}
+
+void build_version(void) 
+{
+    char *s=PATCHES;
+    ircsprintf(version, "%s-%.1d.%.1d(%.2d)%s", BASENAME,
+               MAJOR, MINOR, PATCH, (*s != 0 ? PATCHES : ""));  
+}
diff --git a/src/ircsprintf.c b/src/ircsprintf.c
new file mode 100644 (file)
index 0000000..4841047
--- /dev/null
@@ -0,0 +1,254 @@
+#include "ircsprintf.h"
+
+char num[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+char itoa_tab[10] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'  };
+char xtoa_tab[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
+                     'a', 'b', 'c', 'd', 'e', 'f'  };
+char nullstring[]="(null)";
+
+#ifdef WANT_SNPRINTF
+inline int irc_printf(char *str, size_t size, const char *pattern, va_list vl) 
+#else
+inline int irc_printf(char *str, const char *pattern, va_list vl) 
+#endif
+{
+    char *s;
+    char *buf=str;
+    const char *format=pattern;
+    unsigned long i, u;
+    int len=0;
+    va_list ap;
+    VA_COPY(ap, vl);
+
+#ifdef WANT_SNPRINTF
+    if(!size) 
+    {
+#endif
+       while(*format)
+       {
+           u = 0;
+           switch(*format)
+           {
+           case '%':
+               format++;
+               switch(*format)
+               {
+               case 's': /* most popular ;) */
+                   s=va_arg(ap, char *);
+                   while(*s)
+                       buf[len++]=*s++;
+                   format++;
+                   break;
+               case 'u':
+                   format--; /* falls through and is caught below */
+               case 'l':
+                   if (*(format+1) == 'u')
+                   {
+                       u=1;
+                       format++;
+                   }
+                   else if (*(format+1) == 'd')
+                   {
+                       u=0;
+                       format++;
+                   }
+                   else
+                       u=0;
+                   /* fallthrough */
+               case 'd':
+               case 'i':
+                   i=va_arg(ap, unsigned long);
+                   if(!u)
+                       if(i&0x80000000)
+                       {
+                           buf[len++]='-'; /* it's negative.. */
+                           i = 0x80000000 - (i & ~0x80000000);
+                       }
+                   s=&num[11];
+                   do
+                   {
+                       *--s=itoa_tab[i%10];
+                       i/=10;
+                   } while(i!=0);
+                   while(*s)
+                       buf[len++]=*s++;
+                   format++;
+                   break;
+               case 'n':
+                   /* oo, sneaky...it really is just a long, though! */
+               case 'x':
+               case 'X':
+                   i=va_arg(ap, long);
+                   buf[len++]='0';
+                   buf[len++]='x';
+                   s=&num[11];
+                   do
+                   {
+                       *--s=xtoa_tab[i%16];
+                       i/=16;
+                   } while(i!=0);
+                   while(*s)
+                       buf[len++]=*s++;
+                   format++;
+                   break;
+               case 'c':
+                   buf[len++]= (char) va_arg(ap, int);
+                   format++;
+                   break;
+               default:
+                   /* yick, unknown type...default to returning what our 
+                      s[n]printf friend would */
+                   return vsprintf(str, pattern, vl);
+                   break;
+               }
+               break;
+           default:
+               buf[len++]=*format++;
+               break;
+           }
+       }
+       buf[len]=0;
+       return len;
+#ifdef WANT_SNPRINTF
+    }
+    else
+    {
+       while(*format && len<size)
+       {
+           u = 0;
+           switch(*format)
+           {
+           case '%':
+               format++;
+               switch(*format)
+               {
+               case 's': /* most popular ;) */
+                   s=va_arg(ap, char *);
+                   if(s==NULL)
+                       s=nullstring;
+                   while(*s && len<size)
+                       buf[len++]=*s++;
+                   format++;
+                   break;
+               case 'u':
+                   format--; /* now fall through and it's caught, cool */
+               case 'l':
+                   if (*(format+1) == 'u')
+                   {
+                       u=1;
+                       format++;
+                   }
+                   else if (*(format+1) == 'd')
+                   {
+                       u=0;
+                       format++;
+                   }
+                   else
+                       u=0;
+                   /* fallthrough */
+               case 'd':
+               case 'i':
+                   i=va_arg(ap, unsigned long);
+                   if(!u)
+                       if(i&0x80000000)
+                       {
+                           buf[len++]='-'; /* it's negative.. */
+                           i = 0x80000000 - (i & ~0x80000000);
+                       }
+                   s=&num[11];
+                   do
+                   {
+                       *--s=itoa_tab[i%10];
+                       i/=10;
+                   } while(i!=0);
+                   while(*s && len<size)
+                       buf[len++]=*s++;
+                   format++;
+                   break;
+               case 'n':
+                   /* oo, sneaky...it really is just a long, though! */
+               case 'x':
+               case 'X':
+                   i=va_arg(ap, long);
+                   buf[len++]='0';
+                   if(len<size)
+                       buf[len++]='x';
+                   else 
+                       break;
+                   s=&num[11];
+                   do
+                   {
+                       *--s=xtoa_tab[i%16];
+                       i/=16;
+                   } while(i!=0);
+                   while(*s && len<size)
+                       buf[len++]=*s++;
+                   format++;
+                   break;
+               case 'c':
+                   buf[len++]= (char) va_arg(ap, int);
+                   format++;
+                   break;
+               default:
+                   /* yick, unknown type...default to returning what our 
+                      s[n]printf friend would */
+                   return vsnprintf(str, size, pattern, vl);
+                   break;
+               }
+               break;
+           default:
+               buf[len++]=*format++;
+               break;
+           }
+       }
+       buf[len]=0;
+       return len;
+    }
+#endif /* WANT_SNPRINTF */
+}
+
+int ircsprintf(char *str, const char *format, ...)
+{
+    int ret;
+    va_list vl;
+    va_start(vl, format);
+#ifdef WANT_SNPRINTF
+    ret=irc_printf(str, 0, format, vl);
+#else
+    ret=irc_printf(str, format, vl);
+#endif
+    va_end(vl);
+    return ret;
+}
+
+#ifdef WANT_SNPRINTF
+int ircsnprintf(char *str, size_t size, const char *format, ...)
+{
+    int ret;
+    va_list vl;
+    va_start(vl, format);
+    ret=irc_printf(str, size, format, vl);
+    va_end(vl);
+    return ret;
+}
+#endif
+
+int ircvsprintf(char *str, const char *format, va_list ap)
+{
+    int ret;
+#ifdef WANT_SNPRINTF
+    ret=irc_printf(str, 0, format, ap);
+#else
+    ret=irc_printf(str, format, ap);
+#endif
+    return ret;
+}
+
+#ifdef WANT_SNPRINTF
+int ircvsnprintf(char *str, size_t size, const char *format, va_list ap)
+{
+    int ret;
+    ret=irc_printf(str, size, format, ap);
+    return ret;
+}
+#endif
diff --git a/src/list.c b/src/list.c
new file mode 100644 (file)
index 0000000..9ad1f17
--- /dev/null
@@ -0,0 +1,589 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/list.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Finland
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: list.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "numeric.h"
+#include "blalloc.h"
+#include "dh.h"
+#include "zlink.h"
+
+extern int  BlockHeapGarbageCollect(BlockHeap *);
+
+/* locally defined functions */
+
+/*
+ * re-written to use Wohali (joant@cadence.com) block allocator
+ * routines. very nicely done Wohali
+ * 
+ * -Dianora
+ * 
+ * Number of Link's to pre-allocate at a time for Efnet 1000 seems
+ * reasonable, for smaller nets who knows? -Dianora
+ */
+
+#define LINK_PREALLOCATE 1024
+#define DLINK_PREALLOCATE 128
+#define CHANMEMBER_PREALLOCATE 1024
+/*
+ * Number of aClient structures to preallocate at a time for Efnet 1024
+ * is reasonable for smaller nets who knows? -Dianora
+ * This means you call MyMalloc 30 some odd times, rather than 30k
+ * times -Dianora
+ */
+
+#define CLIENTS_PREALLOCATE 1024
+#if CLIENTS_PREALLOCATE > MAXCONNECTIONS
+#undef CLIENTS_PREALLOCATE
+#define CLIENTS_PREALLOCATE MAXCONNECTIONS
+#endif
+
+/* Number of channels to allocate per block, 1024 sounds nice. */
+
+#define CHANNELS_PREALLOCATE 1024
+
+void        outofmemory();
+
+int         numclients = 0;
+
+/* for jolo's block allocator */
+BlockHeap  *free_local_aClients;
+BlockHeap  *free_Links;
+BlockHeap  *free_DLinks;
+BlockHeap  *free_chanMembers;
+BlockHeap  *free_remote_aClients;
+BlockHeap  *free_anUsers;
+BlockHeap  *free_channels;
+
+#ifdef FLUD
+BlockHeap  *free_fludbots;
+
+#endif /* FLUD */
+
+void initlists()
+{
+    /* Might want to bump up LINK_PREALLOCATE if FLUD is defined */
+    free_Links = BlockHeapCreate((size_t) sizeof(Link), LINK_PREALLOCATE);
+    free_DLinks = BlockHeapCreate((size_t) sizeof(DLink), DLINK_PREALLOCATE);
+    free_chanMembers = BlockHeapCreate((size_t) sizeof(chanMember),
+                                      CHANMEMBER_PREALLOCATE);
+    
+    /*
+     * start off with CLIENTS_PREALLOCATE for now... on typical efnet
+     * these days, it can get up to 35k allocated
+     */
+
+    free_remote_aClients =
+       BlockHeapCreate((size_t) CLIENT_REMOTE_SIZE, CLIENTS_PREALLOCATE);
+
+    /* Can't EVER have more than MAXCONNECTIONS number of local aClients 
+     * And that was stupid, because an idle server built for 32k would use
+     * 50 megs of ram.
+     */
+
+    free_local_aClients = BlockHeapCreate((size_t) CLIENT_LOCAL_SIZE,
+                                         CLIENTS_PREALLOCATE);
+    /* anUser structs are used by both local aClients, and remote aClients */
+
+    free_anUsers = BlockHeapCreate((size_t) sizeof(anUser),
+                                  CLIENTS_PREALLOCATE * 2);
+    
+    /* channels are a very frequent thing in ircd. :) */
+
+    free_channels = BlockHeapCreate((size_t) sizeof(aChannel),
+                                   CHANNELS_PREALLOCATE);
+
+
+#ifdef FLUD
+    /* fludbot structs are used to track CTCP Flooders */
+    free_fludbots = BlockHeapCreate((size_t) sizeof(struct fludbot),
+                                   CLIENTS_PREALLOCATE);
+    
+#endif /* FLUD */
+}
+
+/*
+ * outofmemory()
+ * 
+ * input                - NONE output           - NONE side effects     -
+ * simply try to report there is a problem I free all the memory in the
+ * kline lists hoping to free enough memory so that a proper report can
+ * be made. If I was already here (was_here) then I got called twice,
+ * and more drastic measures are in order. I'll try to just abort() at
+ * least. -Dianora
+ */
+
+void outofmemory()
+{
+    static int  was_here = 0;
+    
+    if (was_here)
+       abort();
+    
+    was_here = YES;
+    
+    Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
+    restart("Out of Memory");
+}
+
+/*
+ * Create a new aClient structure and set it to initial state. *
+ * 
+ *      from == NULL,   create local client (a client connected *
+ * o a socket). *
+ * 
+ *      from,   create remote client (behind a socket *
+ * ssociated with the client defined by *
+ *  ('from' is a local client!!).
+ *
+ * uplink is this client's uplink connection to the network. - lucas
+ */
+aClient    *make_client(aClient *from, aClient *uplink)
+{
+    aClient *cptr = NULL;
+
+    if (!from) /* from is NULL */
+    {                 
+       
+       cptr = BlockHeapALLOC(free_local_aClients, aClient);
+
+       if (cptr == (aClient *) NULL)
+           outofmemory();
+       
+       memset((char *) cptr, '\0' ,CLIENT_LOCAL_SIZE);
+
+       /* Note:  structure is zero (calloc) */
+       cptr->from = cptr;      /* 'from' of local client is self! */
+
+       cptr->status = STAT_UNKNOWN;
+       cptr->fd = -2;
+       cptr->uplink = uplink;
+       strcpy(cptr->username, "unknown");
+       cptr->since = cptr->lasttime = cptr->firsttime = timeofday;
+       cptr->sockerr = -1;
+       cptr->authfd = -1;
+       return (cptr);
+    }
+    else /* from is not NULL */
+    {          
+       cptr = BlockHeapALLOC(free_remote_aClients, aClient);
+       
+       if (cptr == (aClient *) NULL)
+           outofmemory();
+       
+       memset((char *) cptr, '\0', CLIENT_REMOTE_SIZE);
+
+       /* Note:  structure is zero (calloc) */
+       cptr->from = from;
+       cptr->status = STAT_UNKNOWN;
+       cptr->fd = -1;
+       cptr->uplink = uplink;
+       return (cptr);
+    }
+}
+
+void free_client(aClient *cptr)
+{
+    int         retval = 0;
+    
+    if (cptr->fd == -2)
+    {
+       retval = BlockHeapFree(free_local_aClients, cptr);
+    }
+    else
+    {
+       retval = BlockHeapFree(free_remote_aClients, cptr);
+    }
+    if (retval)
+    {
+       /*
+        * Looks unprofessional maybe, but I am going to leave this
+        * sendto_ops in it should never happen, and if it does, the
+        * hybrid team wants to hear about it
+        */
+       sendto_ops("list.c couldn't BlockHeapFree(free_remote_aClients,cptr) "
+                  "cptr = %lX", cptr);
+       sendto_ops("Please report to the bahamut team! "
+                  "coders@dal.net");
+       abort();
+#if defined(USE_SYSLOG) && defined(SYSLOG_BLOCK_ALLOCATOR)
+       syslog(LOG_DEBUG, "list.c couldn't "
+              "BlockHeapFree(free_remote_aClients,cptr) cptr = %lX", cptr);
+#endif
+    }
+}
+
+/*
+ * make_channel() free_channel()
+ * functions to maintain blockheap of channels.
+ */
+
+aChannel *make_channel()
+{
+    aChannel *chan;
+    
+    chan = BlockHeapALLOC(free_channels, aChannel);
+    
+    if(chan == NULL)
+       outofmemory();
+    
+    memset((char *)chan, '\0', sizeof(aChannel));
+    
+    return chan;
+}
+
+void free_channel(aChannel *chan)
+{
+    if (BlockHeapFree(free_channels, chan)) {
+       sendto_ops("list.c couldn't BlockHeapFree(free_channels,chan) "
+                  "chan = %lX", chan);
+       sendto_ops("Please report to the bahamut team!");
+    }
+}
+
+/*
+ * * 'make_user' add's an User information block to a client * if it
+ * was not previously allocated.
+ */
+anUser *make_user(aClient *cptr)
+{
+    anUser *user;
+    
+    user = cptr->user;
+    if (!user)
+    {
+       user = BlockHeapALLOC(free_anUsers, anUser);
+       
+       if (user == (anUser *) NULL)
+           outofmemory();
+       
+       memset(user, 0, sizeof(anUser));
+       cptr->user = user;
+    }
+    return user;
+}
+
+aServer *make_server(aClient *cptr)
+{
+    aServer *serv = cptr->serv;
+    
+    if (!serv)
+    {
+       serv = (aServer *) MyMalloc(sizeof(aServer));
+       
+       memset(serv, 0, sizeof(aServer));
+       cptr->serv = serv;
+    }
+    return cptr->serv;
+}
+
+/*
+ * free_user 
+ * Decrease user reference count by one and release block, 
+ * if count reaches 0
+ */
+void free_user(anUser *user, aClient *cptr)
+{
+    if (user->away)
+       MyFree(user->away);
+#if (RIDICULOUS_PARANOIA_LEVEL>=1)
+    if (user->real_oper_host)
+       MyFree(user->real_oper_host);
+    if (user->real_oper_username)
+       MyFree(user->real_oper_username);
+    if (user->real_oper_ip)
+       MyFree(user->real_oper_ip);
+#endif
+    /* sanity check */
+    if (user->joined || user->invited || user->channel)
+       sendto_ops("* %#x user (%s!%s@%s) %#x %#x %#x %d *",
+                  cptr, cptr ? cptr->name : "<noname>",
+                  user->username, user->host, user,
+                  user->invited, user->channel, user->joined);
+    
+    if (BlockHeapFree(free_anUsers, user)) 
+    {
+       sendto_ops("list.c couldn't BlockHeapFree(free_anUsers,user) "
+                  "user = %lX", user);
+       sendto_ops("Please report to the bahamut team! "
+                  "bahamut-bugs@bahamut.net");
+#if defined(USE_SYSLOG) && defined(SYSLOG_BLOCK_ALLOCATOR)
+       syslog(LOG_DEBUG, "list.c couldn't BlockHeapFree(free_anUsers,user) "
+              "user = %lX", user);
+#endif
+    }
+}
+
+/* taken the code from ExitOneClient() for this and placed it here. - avalon */
+void remove_client_from_list(aClient *cptr)
+{
+    if (IsServer(cptr))
+       Count.server--;
+    else if (IsClient(cptr))
+    {
+       Count.total--;
+       if (IsAnOper(cptr))
+           Count.oper--;
+       if (IsInvisible(cptr))
+           Count.invisi--;
+    }
+    if (cptr->prev)
+       cptr->prev->next = cptr->next;
+    else
+    {
+       client = cptr->next;
+       client->prev = NULL;
+    }
+    if (cptr->next)
+       cptr->next->prev = cptr->prev;
+
+    if (IsPerson(cptr) && cptr->user)
+    {
+       add_history(cptr, 0);
+       off_history(cptr);
+    }
+
+#ifdef FLUD
+    if (MyFludConnect(cptr))
+       free_fluders(cptr, NULL);
+    free_fludees(cptr);
+#endif
+
+    if (cptr->user)
+       free_user(cptr->user, cptr);    /* try this here */
+    if (cptr->serv) 
+    {
+#ifdef HAVE_ENCRYPTION_ON
+       if(cptr->serv->sessioninfo_in)
+           dh_end_session(cptr->serv->sessioninfo_in);
+       if(cptr->serv->sessioninfo_out)
+           dh_end_session(cptr->serv->sessioninfo_out);
+       if(cptr->serv->rc4_in)
+           rc4_destroystate(cptr->serv->rc4_in);
+       if(cptr->serv->rc4_out)
+           rc4_destroystate(cptr->serv->rc4_out);
+#endif
+       if(cptr->serv->zip_in)
+           zip_destroy_input_session(cptr->serv->zip_in);
+       if(cptr->serv->zip_out)
+           zip_destroy_output_session(cptr->serv->zip_out);
+       MyFree(cptr->serv);
+    }
+
+    free_client(cptr);
+    return;
+}
+
+/*
+ * although only a small routine, it appears in a number of places as a
+ * collection of a few lines...functions like this *should* be in this
+ * file, shouldnt they ?  after all, this is list.c, isnt it ? -avalon
+ */
+void add_client_to_list(aClient *cptr)
+{
+    /*
+     * since we always insert new clients to the top of the list, this
+     * should mean the "me" is the bottom most item in the list.
+     */
+    cptr->next = client;
+    client = cptr;
+    if (cptr->next)
+       cptr->next->prev = cptr;
+    return;
+}
+
+/* Look for ptr in the linked listed pointed to by link. */
+chanMember *find_user_member(chanMember *cm, aClient *ptr)
+{
+    if (ptr)
+       while (cm)
+       {
+           if (cm->cptr == ptr)
+               return (cm);
+           cm = cm->next;
+       }
+    return ((chanMember *) NULL);
+}
+
+Link *find_channel_link(Link *lp, aChannel *chptr)
+{
+    if (chptr)
+       for (; lp; lp = lp->next)
+           if (lp->value.chptr == chptr)
+               return lp;
+    return ((Link *) NULL);
+}
+
+/*
+ * Look for a match in a list of strings. Go through the list, and run
+ * match() on it. Side effect: if found, this link is moved to the top of
+ * the list.
+ */
+Link *find_str_link(Link *lp, char *charptr)
+{      
+    if (!charptr)
+       return ((Link *)NULL);
+    while(lp!=NULL)
+    {
+       if(!match(lp->value.cp, charptr))
+           return lp;
+       lp=lp->next;
+    }
+    return ((Link *)NULL);
+}
+
+Link *make_link()
+{
+    Link   *lp;
+    lp = BlockHeapALLOC(free_Links, Link);
+    
+    if (lp == (Link *) NULL)
+       outofmemory();
+    
+    lp->next = (Link *) NULL;  /* just to be paranoid... */
+
+    return lp;
+}
+
+void free_link(Link *lp)
+{
+    if (BlockHeapFree(free_Links, lp)) {
+       sendto_ops("list.c couldn't BlockHeapFree(free_Links,lp) lp = %lX", 
+                  lp);
+       sendto_ops("Please report to the bahamut team!");
+    }
+}
+
+DLink *make_dlink()
+{
+    DLink   *lp;
+    lp = BlockHeapALLOC(free_DLinks, DLink);
+    
+    if (lp == (DLink *) NULL)
+       outofmemory();
+    
+    lp->next = (DLink *) NULL; /* just to be paranoid... */
+    lp->prev = (DLink *) NULL; /* just to be paranoid... */
+
+    return lp;
+}
+
+void free_dlink(DLink *lp)
+{
+    if (BlockHeapFree(free_DLinks, lp)) {
+       sendto_ops("list.c couldn't BlockHeapFree(free_DLinks,lp) lp = %lX", 
+                  lp);
+       sendto_ops("Please report to the bahamut team!");
+    }
+}
+
+chanMember *make_chanmember() {
+    chanMember   *mp;
+    mp = BlockHeapALLOC(free_chanMembers, chanMember);
+
+    if (mp == (chanMember *) NULL)
+       outofmemory();
+
+    return mp;
+}
+
+void free_chanmember(chanMember *mp)
+{
+    if (BlockHeapFree(free_chanMembers, mp)) {
+       sendto_ops("list.c couldn't BlockHeapFree(free_chanMembers,mp) "
+                  "mp = %lX", mp);
+       sendto_ops("Please report to the bahamut team!");
+    }
+}
+
+aClass *make_class()
+{
+    aClass *tmp;
+    
+    tmp = (aClass *) MyMalloc(sizeof(aClass));
+    memset((char *) tmp, '\0', sizeof(aClass));
+    return tmp;
+}
+
+aOper *make_oper()
+{
+       aOper *i;
+       i = (struct Conf_Oper *) MyMalloc(sizeof(aOper));
+       memset((char *) i, '\0', sizeof(aOper));
+       return i;
+}
+
+aConnect *make_connect()
+{
+       aConnect *i;
+       i = (struct Conf_Connect *) MyMalloc(sizeof(aConnect));
+       memset((char *) i, '\0', sizeof(aConnect));
+       return i;
+}
+
+aAllow *make_allow()
+{
+       aAllow *i;
+       i = (struct Conf_Allow *) MyMalloc(sizeof(aAllow));
+       memset((char *) i, '\0', sizeof(aAllow));
+       return i;
+}
+
+aPort *make_port()
+{
+       aPort *i;
+       i = (struct Conf_Port *) MyMalloc(sizeof(aPort));
+       memset((char *) i, '\0', sizeof(aPort));
+       return i;
+}
+
+Conf_Me *make_me()
+{
+       Conf_Me *i;
+       i = (struct Conf_Me *) MyMalloc(sizeof(Conf_Me));
+       memset((char *) i, '\0', sizeof(Conf_Me));
+       return i;
+}
+
+/*
+ * Attempt to free up some block memory
+ * 
+ * list_garbage_collect
+ * 
+ * inputs               - NONE output           - NONE side effects     -
+ * memory is possibly freed up
+ */
+
+void block_garbage_collect()
+{
+    BlockHeapGarbageCollect(free_Links);
+    BlockHeapGarbageCollect(free_chanMembers);
+    BlockHeapGarbageCollect(free_local_aClients);
+    BlockHeapGarbageCollect(free_remote_aClients);
+    BlockHeapGarbageCollect(free_anUsers);
+    BlockHeapGarbageCollect(free_channels);
+#ifdef FLUD
+    BlockHeapGarbageCollect(free_fludbots);
+#endif
+}
diff --git a/src/m_nick.c b/src/m_nick.c
new file mode 100644 (file)
index 0000000..19366a2
--- /dev/null
@@ -0,0 +1,614 @@
+/* m_nick.c - Because s_user.c was just crazy.
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   This program is free softwmare; 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: m_nick.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "channel.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include "h.h"
+#include "userban.h"
+
+extern int do_user(char *, aClient *, aClient *, char *, char *, char *,
+                  unsigned long, unsigned int, char *);
+
+extern int register_user(aClient *, aClient *, char *, char *);
+
+
+extern int user_modes[];
+
+/*
+ * * 'do_nick_name' ensures that the given parameter (nick) is * really
+ * a proper string for a nickname (note, the 'nick' * may be modified
+ * in the process...) *
+ * 
+ *      RETURNS the length of the final NICKNAME (0, if *
+ * nickname is illegal) *
+ * 
+ *  Nickname characters are in range *  'A'..'}', '_', '-', '0'..'9' *
+ * anything outside the above set will terminate nickname. *  In
+ * addition, the first character cannot be '-' *  or a Digit. *
+ * 
+ *  Note: *     '~'-character should be allowed, but *  a change should
+ * be global, some confusion would *    result if only few servers
+ * allowed it...
+ */
+
+static int do_nick_name(char *nick) {
+  char   *ch;
+  
+  if (*nick == '-' || IsDigit(*nick))  /* first character is [0..9-] */
+      return 0;
+  
+  for (ch = nick; *ch && (ch - nick) < NICKLEN; ch++)
+      if (!isvalid(*ch) || IsSpace(*ch))
+         break;
+  
+  *ch = '\0';
+  
+  return (ch - nick);
+}
+
+/*
+ * m_nick 
+ * parv[0] = sender prefix 
+ * parv[1] = nickname 
+ * parv[2] = hopcount when new user; TS when nick change 
+ * parv[3] = TS
+ * ---- new user only below ---- 
+ * parv[4] = umode 
+ * parv[5] = username 
+ * parv[6] = hostname 
+ * parv[7] = server 
+ * parv[8] = serviceid
+ * parv[9] = IP
+ * parv[10] = ircname
+ * -- endif
+ */
+int m_nick(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    struct simBan *ban;
+    aClient    *acptr, *uplink;
+    Link       *lp;
+    char        nick[NICKLEN + 2];
+    ts_val      newts = 0;
+    int         sameuser = 0, samenick = 0;
+  
+    if (parc < 2)
+    {
+       sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN),
+                  me.name, parv[0]);
+       return 0;
+    }
+  
+    if (!IsServer(sptr) && IsServer(cptr) && parc > 2)
+       newts = atol(parv[2]);
+    else if (IsServer(sptr) && parc > 3)
+       newts = atol(parv[3]);
+    else
+       parc = 2;
+    /*
+     * parc == 2 on a normal client sign on (local) and a normal client 
+     * nick change 
+     * parc == 4 on a normal server-to-server client nick change
+     * parc == 11 on a normal TS style server-to-server NICK introduction
+     */
+    if ((IsServer(sptr) || (parc > 4)) && (parc < 11))
+    {
+       /*
+        * We got the wrong number of params. Someone is trying to trick
+        * us. Kill it. -ThemBones As discussed with ThemBones, not much
+        * point to this code now sending a whack of global kills would
+        * also be more annoying then its worth, just note the problem,
+        * and continue -Dianora
+        */
+       sendto_realops("IGNORING BAD NICK: %s[%s@%s] on %s (from %s)", parv[1],
+                      (parc >= 6) ? parv[5] : "-",
+                      (parc >= 7) ? parv[6] : "-",
+                      (parc >= 8) ? parv[7] : "-", parv[0]);
+       return 0;
+     
+    }
+   
+    strncpyzt(nick, parv[1], NICKLEN + 1);
+    /*
+     * if do_nick_name() returns a null name OR if the server sent a
+     * nick name and do_nick_name() changed it in some way (due to rules
+     * of nick creation) then reject it. If from a server and we reject
+     * it, and KILL it. -avalon 4/4/92
+     */
+    if (do_nick_name(nick) == 0 || (IsServer(cptr) && strcmp(nick, parv[1])))
+    {
+       sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME),
+                  me.name, parv[0], parv[1], "Erroneous Nickname");
+       
+       if (IsServer(cptr))
+       {
+           ircstp->is_kill++;
+           sendto_realops_lev(DEBUG_LEV, "Bad Nick: %s From: %s %s",
+                              parv[1], parv[0],
+                              get_client_name(cptr, FALSE));
+           sendto_one(cptr, ":%s KILL %s :%s (Bad Nick)",
+                      me.name, parv[1], me.name);
+           if (sptr != cptr) { /* bad nick change */     
+               sendto_serv_butone(cptr, ":%s KILL %s :%s (Bad Nick)", me.name,
+                                  parv[0], me.name);
+               sptr->flags |= FLAGS_KILLED;
+               return exit_client(cptr, sptr, &me, "BadNick");
+           }
+       }
+       return 0;
+    }
+    /*
+     * Check against nick name collisions.
+     * 
+     * Put this 'if' here so that the nesting goes nicely on the screen
+     * :) We check against server name list before determining if the
+     * nickname is present in the nicklist (due to the way the below
+     * for loop is constructed). -avalon
+     */
+    do
+    {
+       if ((acptr = find_server(nick, NULL)))
+           if (MyConnect(sptr))
+           {
+               sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name,
+                          BadPtr(parv[0]) ? "*" : parv[0], nick);
+               return 0;
+           }
+       
+       /* 
+        * acptr already has result from find_server
+        * Well. unless we have a capricious server on the net, a nick can
+        * never be the same as a server name - Dianora
+        * That's not the only case; maybe someone broke do_nick_name
+        * or changed it so they could use "." in nicks on their network 
+        * - sedition
+        */
+     
+       if (acptr)
+       {
+           /*
+            * We have a nickname trying to use the same name as a
+            * server. Send out a nick collision KILL to remove the
+            * nickname. As long as only a KILL is sent out, there is no
+            * danger of the server being disconnected.  Ultimate way to
+            * jupiter a nick ? >;-). -avalon
+            */
+           sendto_realops_lev(SKILL_LEV, "Nick collision on %s", sptr->name);
+           ircstp->is_kill++;
+           sendto_one(cptr, ":%s KILL %s :%s (Nick Collision)", me.name, 
+                      sptr->name, me.name);
+           sptr->flags |= FLAGS_KILLED;
+           return exit_client(cptr, sptr, &me, "Nick/Server collision");
+       }
+       
+       if (!(acptr = find_client(nick, NULL)))
+           break;
+     
+       /*
+        * If acptr == sptr, then we have a client doing a nick change
+        * between *equivalent* nicknames as far as server is concerned
+        * (user is changing the case of his/her nickname or somesuch)
+        */
+       if (acptr == sptr)
+       {
+           if (strcmp(acptr->name, nick) == 0) 
+               return 0;
+           else
+               break;
+       } /* If user is changing nick to itself no point in propogating */
+     
+       /*
+        * Note: From this point forward it can be assumed that acptr !=
+        * sptr (point to different client structures).
+        *
+        * If the older one is "non-person", the new entry is just 
+        * allowed to overwrite it. Just silently drop non-person, and
+        * proceed with the nick. This should take care of the "dormant
+        * nick" way of generating collisions...
+        */
+       if (IsUnknown(acptr))
+       {
+           if (MyConnect(acptr))
+           {
+               exit_client(NULL, acptr, &me, "Overridden");
+               break;
+           }
+           else if (!(acptr->user))
+           {
+               sendto_realops_lev(SKILL_LEV, "Nick Collision on %s", parv[1]);
+               sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)",
+                                  me.name, acptr->name, me.name);
+               acptr->flags |= FLAGS_KILLED;
+               /* Having no USER struct should be ok... */
+               return exit_client(cptr, acptr, &me,
+                                  "Got TS NICK before Non-TS USER");
+           }
+       }
+     
+       if (!IsServer(cptr))
+       {
+           /*
+            * NICK is coming from local client connection. Just send
+            * error reply and ignore the command.
+            * parv[0] is empty on connecting clients
+            */
+           sendto_one(sptr, err_str(ERR_NICKNAMEINUSE),
+                      me.name, BadPtr(parv[0]) ? "*" : parv[0], nick);
+           return 0;
+       }
+       /*
+        * NICK was coming from a server connection. Means that the same
+        * nick is registered for different users by different server.
+        * This is either a race condition (two users coming online about
+        * same time, or net reconnecting) or just two net fragments
+        * becoming joined and having same nicks in use. We cannot have
+        * TWO users with same nick--purge this NICK from the system with
+        * a KILL... >;)
+        *
+        * Changed to something reasonable like IsServer(sptr) (true if
+        * "NICK new", false if ":old NICK new") -orabidoo
+        */
+     
+       if (IsServer(sptr))
+       {
+           /*
+            * A new NICK being introduced by a neighbouring server (e.g.
+            * message type "NICK new" received)
+            */
+           if (!newts || !acptr->tsinfo || (newts == acptr->tsinfo))
+           {
+               sendto_realops_lev(SKILL_LEV, "Nick collision on %s", parv[1]);
+               ircstp->is_kill++;
+               sendto_one(acptr, err_str(ERR_NICKCOLLISION),
+                          me.name, acptr->name, acptr->name);
+               sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)",
+                                  me.name, acptr->name, me.name);
+               acptr->flags |= FLAGS_KILLED;
+               return exit_client(cptr, acptr, &me, "Nick collision");
+           }
+           else
+           {
+               /* XXX This looks messed up to me XXX - Raist */
+               sameuser = (acptr->user) &&
+                   mycmp(acptr->user->username, parv[5]) == 0 &&
+                   mycmp(acptr->user->host, parv[6]) == 0;
+               if ((sameuser && newts < acptr->tsinfo) || 
+                   (!sameuser && newts > acptr->tsinfo))
+               {
+                   return 0;
+               }
+               else
+               {
+                   sendto_realops_lev(SKILL_LEV, "Nick collision on %s",parv[1]);
+                   ircstp->is_kill++;
+                   sendto_one(acptr, err_str(ERR_NICKCOLLISION),
+                              me.name, acptr->name, acptr->name);
+                   sendto_serv_butone(sptr, ":%s KILL %s :%s (Nick Collision)",
+                                      me.name, acptr->name, me.name);
+                   acptr->flags |= FLAGS_KILLED;
+                   (void) exit_client(cptr, acptr, &me, "Nick collision");
+                   break;
+               }
+           }
+       }
+       /*
+        * * A NICK change has collided (e.g. message type * ":old NICK
+        * new". This requires more complex cleanout. * Both clients must be
+        * purged from this server, the "new" * must be killed from the
+        * incoming connection, and "old" must * be purged from all outgoing
+        * connections.
+        */
+       if (!newts || !acptr->tsinfo || (newts == acptr->tsinfo) ||
+           !sptr->user)
+       {
+           sendto_realops_lev(SKILL_LEV, "Nick change collision: %s", parv[1]);
+           ircstp->is_kill++;
+           sendto_one(acptr, err_str(ERR_NICKCOLLISION),
+                      me.name, acptr->name, acptr->name);
+           sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)",me.name,
+                              sptr->name, me.name);
+           ircstp->is_kill++;
+           sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)",me.name,
+                              acptr->name, me.name);
+           acptr->flags |= FLAGS_KILLED;
+           (void) exit_client(NULL, acptr, &me, "Nick collision(new)");
+           sptr->flags |= FLAGS_KILLED;
+           return exit_client(cptr, sptr, &me, "Nick collision(old)");
+       }
+       else
+       {
+           /* XXX This looks messed up XXX */
+           sameuser = mycmp(acptr->user->username, sptr->user->username) == 0
+               && mycmp(acptr->user->host, sptr->user->host) == 0;
+           if ((sameuser && newts < acptr->tsinfo) ||
+               (!sameuser && newts > acptr->tsinfo)) {
+               if (sameuser)
+                   sendto_realops_lev(SKILL_LEV, 
+                                  "Nick change collision from %s to %s",
+                                  sptr->name, acptr->name);
+               ircstp->is_kill++;
+               sendto_serv_butone(cptr, ":%s KILL %s : %s (Nick Collision)",me.name,
+                                  sptr->name, me.name);
+               sptr->flags |= FLAGS_KILLED;
+               if (sameuser)
+                   return exit_client(cptr, sptr, &me, "Nick collision(old)");
+               else
+                   return exit_client(cptr, sptr, &me, "Nick collision(new)");
+           } 
+           else
+           {
+               sendto_realops_lev(SKILL_LEV, "Nick collision on %s", acptr->name);
+               
+               ircstp->is_kill++;
+               sendto_one(acptr, err_str(ERR_NICKCOLLISION),
+                          me.name, acptr->name, acptr->name);
+               sendto_serv_butone(sptr, ":%s KILL %s :%s (Nick Collision)",me.name,
+                                  acptr->name, me.name);
+               acptr->flags |= FLAGS_KILLED;
+               (void) exit_client(cptr, acptr, &me, "Nick collision");
+           }
+       }
+    } while (0);
+    
+    if (IsServer(sptr))
+    {
+       uplink = find_server(parv[7], NULL);
+       if(!uplink)
+       {
+           /* if we can't find the server this nick is on, 
+            * complain loudly and ignore it. - lucas */
+           sendto_realops("Remote nick %s on UNKNOWN server %s\n",
+                          nick, parv[7]);
+           return 0;
+       }
+       sptr = make_client(cptr, uplink);
+       
+       /* If this is on a U: lined server, it's a U: lined client. */
+       if(IsULine(uplink))
+           sptr->flags|=FLAGS_ULINE;
+     
+       add_client_to_list(sptr);
+       if (parc > 2)
+           sptr->hopcount = atoi(parv[2]);
+       if (newts)
+       {
+           sptr->tsinfo = newts;
+       }
+       else
+       {
+           newts = sptr->tsinfo = (ts_val) timeofday;
+           ts_warn("Remote nick %s introduced without a TS", nick);
+       }
+       /* copy the nick in place */
+       (void) strcpy(sptr->name, nick);
+       (void) add_to_client_hash_table(nick, sptr);
+       if (parc >= 10)
+       {
+           int *s, flag;
+           char *m;
+       
+           /* parse the usermodes -orabidoo */
+           m = &parv[4][1];
+           while (*m)
+           {
+               for (s = user_modes; (flag = *s); s += 2)
+                   if (*m == *(s + 1))
+                   {
+                       if ((flag == UMODE_o) || (flag == UMODE_O))
+                           Count.oper++;
+                       sptr->umode |= flag & SEND_UMODES;
+                       break;
+                   }
+               m++;
+           }
+           if (parc==10)
+           {
+               return do_user(nick, cptr, sptr, parv[5], parv[6],
+                              parv[7], strtoul(parv[8], NULL, 0), 0, parv[9]);
+           } else if (parc==11)
+           {
+               return do_user(nick, cptr, sptr, parv[5], parv[6], parv[7],
+                              strtoul(parv[8], NULL, 0), 
+                              strtoul(parv[9], NULL, 0), parv[10]);
+           }
+       }
+    }
+    else if (sptr->name[0])
+    {
+       /*
+        * Client just changing his/her nick. If he/she is on a
+        * channel, send note of change to all clients on that channel.
+        * Propagate notice to other servers.
+        */
+       /* if the nickname is different, set the TS */
+       if (mycmp(parv[0], nick))
+       {
+           sptr->tsinfo = newts ? newts : (ts_val) timeofday;
+       }
+#ifdef DONT_CHECK_QLINE_REMOTE
+       if (MyConnect(sptr))
+       {
+#endif
+           if ((ban = check_mask_simbanned(nick, SBAN_NICK)))
+           {
+#ifndef DONT_CHECK_QLINE_REMOTE
+               if (!MyConnect(sptr))
+                   sendto_realops("Restricted nick %s from %s on %s", nick,
+                                  (*sptr->name != 0 && !IsServer(sptr)) ?
+                                  sptr->name : "<unregistered>",
+                                  (sptr->user == NULL) ? ((IsServer(sptr)) ?
+                                                          parv[6] : me.name) :
+                                  sptr->user->server);
+#endif
+               
+               if (MyConnect(sptr) && (!IsServer(cptr)) && (!IsOper(cptr))
+                   && (!IsULine(sptr)))
+               {
+                   sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name,
+                              BadPtr(parv[0]) ? "*" : parv[0], nick,
+                              BadPtr(ban->reason) ? "Erroneous Nickname" :
+                              ban->reason);
+                   sendto_realops_lev(REJ_LEV,
+                                      "Forbidding restricted nick %s from %s.",
+                                      nick, get_client_name(cptr, FALSE));
+                   return 0;
+               }
+           }
+#ifdef DONT_CHECK_QLINE_REMOTE
+       }
+#endif
+       if (MyConnect(sptr))
+       {
+           if (IsRegisteredUser(sptr))
+           {
+        
+               /* before we change their nick, make sure they're not banned
+                * on any channels, and!! make sure they're not changing to
+                * a banned nick -sed */
+               /* a little cleaner - lucas */
+        
+               for (lp = sptr->user->channel; lp; lp = lp->next)
+               {
+                   if (can_send(sptr, lp->value.chptr, NULL))
+                   { 
+                       sendto_one(sptr, err_str(ERR_BANNICKCHANGE), me.name,
+                                  sptr->name, lp->value.chptr->chname);
+                       return 0;
+                   }
+                   if (nick_is_banned(lp->value.chptr, nick, sptr) != NULL)
+                   {
+                       sendto_one(sptr, err_str(ERR_BANONCHAN), me.name,
+                                  sptr->name, nick, lp->value.chptr->chname);
+                       return 0;
+                   }
+               }
+#ifdef ANTI_NICK_FLOOD
+               if ((sptr->last_nick_change + MAX_NICK_TIME) < NOW)
+                   sptr->number_of_nick_changes = 0;
+               sptr->last_nick_change = NOW;
+               sptr->number_of_nick_changes++;
+        
+               if (sptr->number_of_nick_changes > MAX_NICK_CHANGES && 
+                   !IsAnOper(sptr))
+               {
+                   sendto_one(sptr,
+                              ":%s NOTICE %s :*** Notice -- Too many nick "
+                              "changes. Wait %d seconds before trying again.",
+                              me.name, sptr->name, MAX_NICK_TIME);
+                   return 0;
+               }
+#endif
+               /* If it changed nicks, -r it */
+               if ((sptr->umode & UMODE_r) && (mycmp(parv[0], nick) != 0))
+               {
+                   unsigned int oldumode;
+                   char mbuf[BUFSIZE];
+
+                   oldumode = sptr->umode;
+                   sptr->umode &= ~UMODE_r;
+                   send_umode(sptr, sptr, oldumode, ALL_UMODES, mbuf);
+               }
+
+               sendto_common_channels(sptr, ":%s NICK :%s", parv[0], 
+                                      nick);
+               if (sptr->user)
+               {
+                   add_history(sptr, 1);
+                       
+                   sendto_serv_butone(cptr, ":%s NICK %s :%ld",
+                                      parv[0], nick, sptr->tsinfo);
+               }
+           }
+       }
+       else
+       {
+           sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick);
+           if (sptr->user)
+           {
+               add_history(sptr, 1);
+               
+               sendto_serv_butone(cptr, ":%s NICK %s :%ld",
+                                  parv[0], nick, sptr->tsinfo);
+           }
+
+           /* If it changed nicks, -r it */
+           if (mycmp(parv[0], nick))
+               sptr->umode &= ~UMODE_r;
+       }
+    } 
+    else
+    {
+       /* Client setting NICK the first time */
+       if (MyConnect(sptr))
+       {
+           if ((ban = check_mask_simbanned(nick, SBAN_NICK)))
+           {
+               if (MyConnect(sptr) && (!IsServer(cptr)) && (!IsOper(cptr))
+                   && (!IsULine(sptr)))
+               {
+                   sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name,
+                              BadPtr(parv[0]) ? "*" : parv[0], nick,
+                              BadPtr(ban->reason) ? "Erroneous Nickname" :
+                              ban->reason);
+                   sendto_realops_lev(REJ_LEV,
+                                      "Forbidding restricted nick %s from "
+                                      "<unregistered>%s.", nick,
+                                      get_client_name(cptr, FALSE));
+                   return 0;
+               }
+           }
+       }
+       
+       strcpy(sptr->name, nick);
+       sptr->tsinfo = timeofday;
+       if (sptr->user)
+       {
+           /* USER already received, now we have NICK */
+       
+           if (register_user(cptr, sptr, nick, sptr->user->username)
+               == FLUSH_BUFFER)
+               return FLUSH_BUFFER;
+       }
+    }
+    /* Finally set new nick name. */
+   
+    if (sptr->name[0])
+    {
+       del_from_client_hash_table(sptr->name, sptr);
+       samenick = mycmp(sptr->name, nick) ? 0 : 1;
+       if (IsPerson(sptr) && !samenick)
+           hash_check_watch(sptr, RPL_LOGOFF);
+    }
+    strcpy(sptr->name, nick);
+    add_to_client_hash_table(nick, sptr);
+    if (IsPerson(sptr) && !samenick)
+       hash_check_watch(sptr, RPL_LOGON);
+    return 0;
+}
diff --git a/src/m_rwho.c b/src/m_rwho.c
new file mode 100644 (file)
index 0000000..d272237
--- /dev/null
@@ -0,0 +1,1292 @@
+/*
+ *   m_rwho.c - Regular expression enabled WHO
+ *   Copyright (C) 2004 Trevor Talbot and
+ *                      the DALnet coding team
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: m_rwho.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "numeric.h"
+#include "channel.h"
+#include "inet.h"
+#include "clones.h"
+
+#include "pcre.h"
+
+extern int user_modes[];
+extern unsigned int cidr_to_netmask(unsigned int);
+extern Link *find_channel_link(Link *, aChannel *);
+
+/* max capturing submatches to allow in all fields combined */
+#define MAX_SUBMATCHES  9
+
+/* for pcre_exec(), don't touch */
+#define NVEC        ((MAX_SUBMATCHES+1)*3)
+
+/* PCRE matched fields */
+#define RWHO_NICK   1
+#define RWHO_USER   2
+#define RWHO_GCOS   3
+#define RWHO_AWAY   4
+#define RWHO_COUNT  5
+
+/* other matched fields */
+#define RWM_AWAY    0x0001
+#define RWM_HOST    0x0002
+#define RWM_IP      0x0004
+#define RWM_MODES   0x0008
+#define RWM_SERVER  0x0010
+#define RWM_TS      0x0020
+#define RWM_STYPE   0x0040
+#define RWM_JOINS   0x0080
+#define RWM_CLONES  0x0100
+#define RWM_MATCHES 0x0200
+#define RWM_CHANNEL 0x0400
+
+/* whois compatibility */
+#define RWC_SHOWIP  0x0001
+#define RWC_CHANNEL 0x0002
+
+/* output options */
+#define RWO_NICK    0x0001
+#define RWO_USER    0x0002
+#define RWO_HOST    0x0004
+#define RWO_IP      0x0008
+#define RWO_MODES   0x0010
+#define RWO_FLAGS   0x0020
+#define RWO_SERVER  0x0040
+#define RWO_TS      0x0080
+#define RWO_STYPE   0x0100
+#define RWO_GCOS    0x0200
+#define RWO_AWAY    0x0400
+#define RWO_JOINS   0x0800
+#define RWO_CLONES  0x1000
+#define RWO_MATCHES 0x2000
+#define RWO_CHANNEL 0x4000
+
+
+static const char *rwho_help[] = {
+    "RWHO <[+|-]matchflags>[/<outputflags>[:<cookie>]] <args>",
+    "Match flags are specified like channel modes,",
+    "'+' being a positive match and '-' being a negative one:",
+    "  a             - user is (not) away",
+    "  c <channel>   - user is on channel <channel> (+ only)",
+    "  d <clones>    - there are N or more (less) users per host",
+    "  D <matches>   - there are N or more (less) matching users per host",
+    "  h <host>      - user's host does (not) match wildcard mask",
+    "  i <ip>        - user's IP does (not) match CIDR <ip>",
+    "  j <channels>  - user is in N or more (less) channels",
+    "  m <usermodes> - user is (not) using modes <usermodes>",
+    "  s <server>    - user is (not) on server <server>",
+    "  t <seconds>   - user has been online N or more (less) seconds",
+    "  T <type>      - user is (not) type <type> as set by services",
+    "  C             - for compatibility with WHO, use discouraged",
+    "  I             - for compatibility with WHO, use discouraged",
+    "The following match flags are compiled into a single regular expression",
+    "in the order you specify, so later flags can use backreferences to",
+    "submatches in the flags prior:",
+    "  A <away>      - user's away reason matches regexp pattern (implies +a)",
+    "  g <gcos/name> - user's real name matches regexp pattern",
+    "  n <nick>      - user's nick matches regexp pattern",
+    "  u <username>  - user's username matches regexp pattern",
+    "The regular expression flags do not support negative matches.",
+    "The optional output flags cause replies to be sent using numeric 354 and",
+    "contain only the fields associated with the flags in the order below:",
+    "  :<cookie>     - supplied cookie (useful for scripts)",
+    "  n             - user's nick",
+    "  u             - user's username",
+    "  h             - user's host",
+    "  i             - user's IP",
+    "  s             - user's server",
+    "  f             - standard WHO flags (GH*%@+)",
+    "  c             - user's most recently joined channel",
+    "  j             - number of joined channels",
+    "  d             - number of clones on user's IP",
+    "  D             - number of matches on user's IP (see below)",
+    "  t             - user's signon timestamp",
+    "  T             - user's type (set by services)",
+    "  m             - user's modes",
+    "  g             - user's gcos/real name (mutually exclusive with 'a')",
+    "  a             - user's away reason (mutually exclusive with 'g')",
+    "There are three special output flags:",
+    "  L<count>      - limit to N results (no space between L and <count>)",
+    "  C             - no results, just supply match count in RPL_ENDOFWHO",
+    "  D             - returns only one matching result per host (summarize)",
+    NULL
+};
+
+static struct {
+    unsigned  check[2];         /* things to try match */
+    unsigned  rplfields;        /* fields to include in the response */
+    unsigned  compat;           /* WHO compatibility flags */
+    char     *rplcookie;        /* response cookie */
+    int       countonly;        /* counting only, no results */
+    int       limit;            /* max number of results */
+    int       spat[RWHO_COUNT]; /* match string build pattern */
+    pcre     *re;               /* regex pattern */
+    aClient  *server;           /* server */
+    aChannel *chptr;            /* search in channel */
+    char     *host_pat[2];      /* wildcard host pattern */
+    int      (*host_func[2])(); /* host match function */
+    int       umodes[2];        /* usermodes */
+    unsigned  stype;            /* services type */
+    unsigned  ip_mask[2];       /* IP netmask */
+    unsigned  ip_addr[2];       /* IP address */
+    char     *ip_str[2];        /* IP string if CIDR is invalid */
+    ts_val    ts[2];            /* signon timestamp */
+    int       joined[2];        /* min/max joined chans */
+    int       clones[2];        /* min/max clones */
+    int       matches[2];       /* min/max clone matches */
+    int       thisclones;       /* number of clones on this host */
+    int       thismatches;      /* number of matches on this host */
+} rwho_opts;
+
+static char rwhobuf[1024];
+static char scratch[1024];
+
+
+/*
+ * Send a syntax error message.
+ */
+static void rwho_synerr(aClient *sptr, char *msg)
+{
+    sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name, sptr->name, "RWHO",
+               "rwho");
+    if (msg)
+        sendto_one(sptr, getreply(RPL_COMMANDSYNTAX), me.name, sptr->name,msg);
+}
+
+/*
+ * Build the regular expression to use for nick/user/gcos/away matching.
+ * Returns 1 on success, 0 on failure.
+ */
+static int rwho_compile(aClient *cptr, char *remap[])
+{
+    const char *es;
+    int         ei;
+    char       *s;
+    int         i, j;
+    char        arg = 0;
+
+    s = rwhobuf;
+    for (i = 0; rwho_opts.spat[i]; i++)
+        s += ircsprintf(s, "(?>(?:%s)\\x00)", remap[rwho_opts.spat[i]]);
+
+    rwho_opts.re = pcre_compile(rwhobuf,
+                                PCRE_EXTRA|PCRE_ANCHORED|PCRE_UNGREEDY,
+                                &es, &ei, NULL);
+
+    if (!rwho_opts.re)
+    {
+        rwho_synerr(cptr, NULL);
+
+        /* the things we do for error messages... */
+        for (i = 0; rwho_opts.spat[i]; i++)
+        {
+            rwho_opts.re = pcre_compile(remap[rwho_opts.spat[i]],
+                                        PCRE_EXTRA|PCRE_ANCHORED|PCRE_UNGREEDY,
+                                        &es, &ei, NULL);
+            if (rwho_opts.re)
+            {
+                free(rwho_opts.re);
+                continue;
+            }
+
+            if (es)
+            {
+                j = 0;
+                s = remap[rwho_opts.spat[i]];
+
+                switch (rwho_opts.spat[i])
+                {
+                    case RWHO_AWAY: arg = 'A'; break;
+                    case RWHO_GCOS: arg = 'g'; break;
+                    case RWHO_NICK: arg = 'n'; break;
+                    case RWHO_USER: arg = 'u'; break;
+                }
+
+                while (*s)
+                {
+                    if (ei == j)
+                    {
+                        scratch[j++] = 037;
+                        scratch[j++] = *s++;
+                        scratch[j++] = 037;
+                    }
+                    else
+                        scratch[j++] = *s++;
+                }
+                scratch[j] = 0;
+
+                ircsprintf(rwhobuf, "Invalid flag %c expression %s", arg,
+                           scratch);
+                sendto_one(cptr, getreply(RPL_COMMANDSYNTAX), me.name,
+                           cptr->name, rwhobuf);
+                sendto_one(cptr, getreply(RPL_COMMANDSYNTAX), me.name,
+                           cptr->name, es);
+                break;
+            }
+        }
+
+        return 0;
+    }
+
+    pcre_fullinfo(rwho_opts.re, NULL, PCRE_INFO_CAPTURECOUNT, &ei);
+    if (ei > MAX_SUBMATCHES)
+    {
+        rwho_synerr(cptr, "too many capturing submatches, use (?:)");
+        free(rwho_opts.re);
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * Parse the options to the RWHO command.
+ * Returns 1 on success, 0 on failure.
+ */
+static int rwho_parseopts(aClient *sptr, int parc, char *parv[])
+{
+    char       *remap[RWHO_COUNT] = {0};
+    char       *sfl;
+    char       *s;
+    int         spatidx = 0;
+    int         neg = 0;
+    int         arg = 2;
+    int         i;
+    ts_val      ts;
+    unsigned    ui;
+
+    memset(&rwho_opts, 0, sizeof(rwho_opts));
+
+    if (parc < 2)
+    {
+        sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name, sptr->name, "RWHO",
+                   "rwho");
+        return 0;
+    }
+
+    if (*parv[1] == '?')
+    {
+        const char **ptr;
+        for (ptr = rwho_help; *ptr; ptr++)
+            sendto_one(sptr, getreply(RPL_COMMANDSYNTAX), me.name,
+                       parv[0], *ptr);
+        sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, parv[0], "?","RWHO");
+        return 0;
+    }
+
+    /* parse match options */
+    for (sfl = parv[1]; *sfl; sfl++)
+    {
+        if (*sfl == '/')
+        {
+            sfl++;
+            break;
+        }
+
+        switch (*sfl)
+        {
+            case '+':
+                neg = 0;
+                break;
+
+            case '-':
+                neg = 1;
+                break;
+
+            case 'a':
+                if (rwho_opts.check[!neg] & RWM_AWAY)
+                {
+                    rwho_synerr(sptr, "cannot use both +a and -a in match");
+                    return 0;
+                }
+                rwho_opts.check[neg] |= RWM_AWAY;
+                break;
+
+            case 'c':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag c");
+                    return 0;
+                }
+                if (neg)
+                {
+                    rwho_synerr(sptr, "negative match not supported for match"
+                                " flag c");
+                    return 0;
+                }
+                rwho_opts.chptr = find_channel(parv[arg], NULL);
+                if (!rwho_opts.chptr)
+                {
+                    sendto_one(sptr, getreply(ERR_NOSUCHCHANNEL), me.name,
+                               parv[0], parv[arg]);
+                    return 0;
+                }
+                rwho_opts.check[neg] |= RWM_CHANNEL;
+                arg++;
+                break;
+
+            case 'C':
+                rwho_opts.compat |= RWC_CHANNEL;
+                break;
+
+            case 'd':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag d");
+                    return 0;
+                }
+                i = strtol(parv[arg], &s, 0);
+                if (*s != 0 || i < 1)
+                {
+                    rwho_synerr(sptr, "invalid number of clones for match"
+                                " flag d");
+                    return 0;
+                }
+                rwho_opts.clones[neg] = i;
+                rwho_opts.check[neg] |= RWM_CLONES;
+                arg++;
+                break;
+
+            case 'D':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag D");
+                    return 0;
+                }
+                i = strtol(parv[arg], &s, 0);
+                if (*s != 0 || i < 1)
+                {
+                    rwho_synerr(sptr, "invalid number of matches for match"
+                                " flag D");
+                    return 0;
+                }
+                rwho_opts.matches[neg] = i;
+                rwho_opts.check[neg] |= RWM_MATCHES;
+                arg++;
+                break;
+
+            case 'h':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag h");
+                    return 0;
+                }
+                if (strchr(parv[arg], '*') || strchr(parv[arg], '?'))
+                    rwho_opts.host_func[neg] = match;
+                else
+                    rwho_opts.host_func[neg] = mycmp;
+                rwho_opts.host_pat[neg] = parv[arg];
+                rwho_opts.check[neg] |= RWM_HOST;
+                arg++;
+                break;
+
+            case 'i':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag i");
+                    return 0;
+                }
+                if ((s = strchr(parv[arg], '/')))
+                {
+                    *s++ = 0;
+                    i = strtol(s, &s, 10);
+                    if (*s == 0 && 1 < i && i < 32)
+                        rwho_opts.ip_mask[neg] = htonl(cidr_to_netmask(i));
+                }
+                else
+                    rwho_opts.ip_mask[neg] = ~0;
+                rwho_opts.ip_addr[neg] = inet_addr(parv[arg]);
+                if (!rwho_opts.ip_mask[neg] ||
+                    (rwho_opts.ip_mask[neg] != ~0 &&
+                     rwho_opts.ip_addr[neg] == 0xFFFFFFFF))
+                {
+                    rwho_synerr(sptr, "invalid CIDR IP for match flag i");
+                    return 0;
+                }
+                if (rwho_opts.ip_addr[neg] == 0xFFFFFFFF)
+                    rwho_opts.ip_str[neg] = parv[arg];
+                else
+                    rwho_opts.ip_addr[neg] &= rwho_opts.ip_mask[neg];
+                rwho_opts.check[neg] |= RWM_IP;
+                arg++;
+                break;
+
+            case 'I':
+                rwho_opts.compat |= RWC_SHOWIP;
+                break;
+
+            case 'j':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag s");
+                    return 0;
+                }
+                i = strtol(parv[arg], &s, 0);
+                if (*s != 0 || i < 0)
+                {
+                    rwho_synerr(sptr, "invalid number of channels for match"
+                                " flag j");
+                    return 0;
+                }
+                rwho_opts.joined[neg] = i;
+                rwho_opts.check[neg] |= RWM_JOINS;
+                arg++;
+                break;
+
+            case 'm':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag m");
+                    return 0;
+                }
+                for (s = parv[arg]; *s; s++)
+                    for (i = 1; user_modes[i]; i+=2)
+                        if (*s == user_modes[i])
+                        {
+                            rwho_opts.umodes[neg] |= user_modes[i-1];
+                            break;
+                        }
+                rwho_opts.check[neg] |= RWM_MODES;
+                arg++;
+                break;
+
+            case 's':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag s");
+                    return 0;
+                }
+                if (rwho_opts.check[!neg] & RWM_SERVER)
+                {
+                    rwho_synerr(sptr, "cannot use both +s and -s in match");
+                    return 0;
+                }
+                rwho_opts.server = find_server(parv[arg], NULL);
+                if (!rwho_opts.server)
+                {
+                    sendto_one(sptr, getreply(ERR_NOSUCHSERVER), me.name,
+                               sptr->name, parv[arg]);
+                    return 0;
+                }
+                rwho_opts.check[neg] |= RWM_SERVER;
+                arg++;
+                break;
+
+            case 't':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag t");
+                    return 0;
+                }
+                ts = strtol(parv[arg], &s, 0);
+                if (*s != 0 || ts <= 0)
+                {
+                    rwho_synerr(sptr, "invalid number of seconds for match"
+                                " flag t");
+                    return 0;
+                }
+                rwho_opts.ts[neg] = NOW - ts;
+                rwho_opts.check[neg] |= RWM_TS;
+                arg++;
+                break;
+
+            case 'T':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag T");
+                    return 0;
+                }
+                if (rwho_opts.check[!neg] & RWM_STYPE)
+                {
+                    rwho_synerr(sptr, "cannot use both +T and -T in match");
+                    return 0;
+                }
+                ui = strtoul(parv[arg], &s, 0);
+                if (*s != 0)
+                {
+                    rwho_synerr(sptr, "invalid type for match flag T");
+                    return 0;
+                }
+                rwho_opts.stype = ui;
+                rwho_opts.check[neg] |= RWM_STYPE;
+                arg++;
+                break;
+
+            case 'A':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag A");
+                    return 0;
+                }
+                if (remap[RWHO_AWAY])
+                {
+                    rwho_synerr(sptr, "flags may not be used more than once");
+                    return 0;
+                }
+                if (neg)
+                {
+                    rwho_synerr(sptr, "negative match not supported for match"
+                                " flag A");
+                    return 0;
+                }
+                if (rwho_opts.check[!neg] & RWM_AWAY)
+                {
+                    rwho_synerr(sptr, "cannot use both +A and -a in match");
+                    return 0;
+                }
+                remap[RWHO_AWAY] = parv[arg];
+                rwho_opts.spat[spatidx++] = RWHO_AWAY;
+                rwho_opts.check[neg] |= RWM_AWAY;    /* implicit +a */
+                arg++;
+                break;
+
+            case 'g':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag g");
+                    return 0;
+                }
+                if (remap[RWHO_GCOS])
+                {
+                    rwho_synerr(sptr, "flags may not be used more than once");
+                    return 0;
+                }
+                if (neg)
+                {
+                    rwho_synerr(sptr, "negative match not supported for match"
+                                " flag g");
+                    return 0;
+                }
+                remap[RWHO_GCOS] = parv[arg];
+                rwho_opts.spat[spatidx++] = RWHO_GCOS;
+                arg++;
+                break;
+
+            case 'n':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag n");
+                    return 0;
+                }
+                if (remap[RWHO_NICK])
+                {
+                    rwho_synerr(sptr, "flags may not be used more than once");
+                    return 0;
+                }
+                if (neg)
+                {
+                    rwho_synerr(sptr, "negative match not supported for match"
+                                " flag n");
+                    return 0;
+                }
+                remap[RWHO_NICK] = parv[arg];
+                rwho_opts.spat[spatidx++] = RWHO_NICK;
+                arg++;
+                break;
+
+            case 'u':
+                if (!parv[arg])
+                {
+                    rwho_synerr(sptr, "missing argument for match flag u");
+                    return 0;
+                }
+                if (remap[RWHO_USER])
+                {
+                    rwho_synerr(sptr, "flags may not be used more than once");
+                    return 0;
+                }
+                if (neg)
+                {
+                    rwho_synerr(sptr, "negative match not supported for match"
+                                " flag u");
+                    return 0;
+                }
+                remap[RWHO_USER] = parv[arg];
+                rwho_opts.spat[spatidx++] = RWHO_USER;
+                arg++;
+                break;
+                
+            default:
+                ircsprintf(scratch, "unknown match flag %c", *sfl);
+                rwho_synerr(sptr, scratch);
+                return 0;
+        }
+    }
+
+    /* parse output options */
+    while (*sfl)
+    {
+        if (*sfl == ':')
+        {
+            if (!*++sfl)
+            {
+                rwho_synerr(sptr, NULL);
+                return 0;
+            }
+            rwho_opts.rplcookie = sfl;
+            break;
+        }
+
+        switch (*sfl)
+        {
+            case 'n': rwho_opts.rplfields |= RWO_NICK; sfl++; break;
+            case 'u': rwho_opts.rplfields |= RWO_USER; sfl++; break;
+            case 'h': rwho_opts.rplfields |= RWO_HOST; sfl++; break;
+            case 'i': rwho_opts.rplfields |= RWO_IP; sfl++; break;
+            case 's': rwho_opts.rplfields |= RWO_SERVER; sfl++; break;
+            case 'f': rwho_opts.rplfields |= RWO_FLAGS; sfl++; break;
+            case 'c': rwho_opts.rplfields |= RWO_CHANNEL; sfl++; break;
+            case 'j': rwho_opts.rplfields |= RWO_JOINS; sfl++; break;
+            case 'd': rwho_opts.rplfields |= RWO_CLONES; sfl++; break;
+            case 'D': rwho_opts.rplfields |= RWO_MATCHES; sfl++; break;
+            case 't': rwho_opts.rplfields |= RWO_TS; sfl++; break;
+            case 'T': rwho_opts.rplfields |= RWO_STYPE; sfl++; break;
+            case 'm': rwho_opts.rplfields |= RWO_MODES; sfl++; break;
+            case 'g': rwho_opts.rplfields |= RWO_GCOS; sfl++; break;
+            case 'a': rwho_opts.rplfields |= RWO_AWAY; sfl++; break;
+
+            case 'C': rwho_opts.countonly = 1; sfl++; break;
+
+            case 'L':
+                rwho_opts.limit = strtol(sfl+1, &sfl, 10);
+                if (rwho_opts.limit < 1)
+                {
+                    rwho_synerr(sptr, "invalid limit for output flag L");
+                    return 0;
+                }
+                break;
+
+            default:
+                ircsprintf(scratch, "unknown output flag %c", *sfl);
+                rwho_synerr(sptr, scratch);
+                return 0;
+        }
+    }
+
+    if ((rwho_opts.rplfields & (RWO_GCOS|RWO_AWAY)) == (RWO_GCOS|RWO_AWAY))
+    {
+        rwho_synerr(sptr, "output flags g and a may not be used together");
+        return 0;
+    }
+
+    if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_CLONES)
+        && (rwho_opts.clones[0] > rwho_opts.clones[1]))
+    {
+        rwho_synerr(sptr, "values for match flags +d and -d will never match");
+        return 0;
+    }
+
+    if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_MATCHES)
+        && (rwho_opts.matches[0] > rwho_opts.matches[1]))
+    {
+        rwho_synerr(sptr, "values for match flags +D and -D will never match");
+        return 0;
+    }
+
+    if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_JOINS)
+        && (rwho_opts.joined[0] > rwho_opts.joined[1]))
+    {
+        rwho_synerr(sptr, "values for match flags +j and -j will never match");
+        return 0;
+    }
+
+    if ((rwho_opts.check[0] & rwho_opts.check[1] & RWM_TS)
+        && (rwho_opts.ts[0] < rwho_opts.ts[1]))
+    {
+        rwho_synerr(sptr, "values for match flags +t and -t will never match");
+        return 0;
+    }
+
+    if (spatidx && !rwho_compile(sptr, remap))
+        return 0;
+
+    return 1;
+}
+
+/*
+ * See if a client matches the search parameters.
+ * Returns 1 on match, 0 on no match.
+ * Fills in failcode and failclient upon unexpected PCRE error.
+ */
+static int rwho_match(aClient *cptr, int *failcode, aClient **failclient)
+{
+    char *s;
+    char *b;
+    int   i;
+    int   ovec[NVEC];
+
+    if ((rwho_opts.check[0] & RWM_SERVER) &&
+        (cptr->uplink != rwho_opts.server))
+        return 0;
+    else if ((rwho_opts.check[1] & RWM_SERVER) &&
+        (cptr->uplink == rwho_opts.server))
+        return 0;
+
+    if ((rwho_opts.check[0] & RWM_TS) && (cptr->tsinfo > rwho_opts.ts[0]))
+        return 0;
+
+    if ((rwho_opts.check[1] & RWM_TS) && (cptr->tsinfo < rwho_opts.ts[1]))
+        return 0;
+
+    if ((rwho_opts.check[0] & RWM_AWAY) && !cptr->user->away)
+        return 0;
+    else if ((rwho_opts.check[1] & RWM_AWAY) && cptr->user->away)
+        return 0;
+
+    if ((rwho_opts.check[0] & RWM_STYPE) &&
+        (cptr->user->servicetype != rwho_opts.stype))
+        return 0;
+    else if ((rwho_opts.check[1] & RWM_STYPE) &&
+        (cptr->user->servicetype == rwho_opts.stype))
+        return 0;
+
+    if ((rwho_opts.check[0] & RWM_JOINS) &&
+        (cptr->user->joined < rwho_opts.joined[0]))
+        return 0;
+
+    if ((rwho_opts.check[1] & RWM_JOINS) &&
+        (cptr->user->joined > rwho_opts.joined[1]))
+        return 0;
+
+    if ((rwho_opts.check[0] & RWM_MODES) &&
+        ((cptr->umode & rwho_opts.umodes[0]) != rwho_opts.umodes[0]))
+        return 0;
+
+    if ((rwho_opts.check[1] & RWM_MODES) &&
+        (cptr->umode & rwho_opts.umodes[1]))
+        return 0;
+
+    if ((rwho_opts.check[0] & RWM_CHANNEL) && !IsMember(cptr, rwho_opts.chptr))
+        return 0;
+
+    if (rwho_opts.check[0] & RWM_IP)
+    {
+        if (rwho_opts.ip_str[0])
+        {
+            if (match(rwho_opts.ip_str[0], cptr->hostip))
+                return 0;
+        }
+        else if ((cptr->ip.s_addr & rwho_opts.ip_mask[0])
+                 != rwho_opts.ip_addr[0])
+            return 0;
+    }
+
+    if (rwho_opts.check[1] & RWM_IP)
+    {
+        if (rwho_opts.ip_str[1])
+        {
+            if (!match(rwho_opts.ip_str[1], cptr->hostip))
+                return 0;
+        }
+        else if ((cptr->ip.s_addr & rwho_opts.ip_mask[1])
+                 == rwho_opts.ip_addr[1])
+            return 0;
+    }
+
+    if ((rwho_opts.check[0] & RWM_HOST) &&
+        rwho_opts.host_func[0](rwho_opts.host_pat[0], cptr->user->host))
+        return 0;
+
+    if ((rwho_opts.check[1] & RWM_HOST) &&
+        !rwho_opts.host_func[1](rwho_opts.host_pat[1], cptr->user->host))
+        return 0;
+    
+    if (rwho_opts.re)
+    {
+        s = scratch;
+        for (i = 0; rwho_opts.spat[i]; i++)
+        {
+            switch (rwho_opts.spat[i])
+            {
+                case RWHO_NICK:
+                    b = cptr->name;
+                    while ((*s++ = *b++));
+                    /* note: deliberately using zero terminator */
+                    break;
+
+                case RWHO_USER:
+                    b = cptr->user->username;
+                    while ((*s++ = *b++));
+                    break;
+
+                case RWHO_GCOS:
+                    b = cptr->info;
+                    while ((*s++ = *b++));
+                    break;
+
+                /* will core if RWM_AWAY wasn't implicitly set */
+                case RWHO_AWAY:
+                    b = cptr->user->away;
+                    while ((*s++ = *b++));
+                    break;
+            }
+        }
+
+        i = pcre_exec(rwho_opts.re, NULL, scratch, s - scratch, 0, 0, ovec,
+                      NVEC);
+
+        if (i < 0)
+        {
+            if (i == PCRE_ERROR_NOMATCH)
+                return 0;
+
+            *failcode = i;
+            *failclient = cptr;
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/*
+ * Prepare rwhobuf for response text.
+ * Returns a pointer to space for rwho_reply().
+ */
+static char *rwho_prepbuf(aClient *cptr)
+{
+    char *s = rwhobuf;
+
+    if (!rwho_opts.rplfields)
+        return s;
+
+    s += ircsprintf(s, getreply(RPL_RWHOREPLY), me.name, cptr->name);
+
+    if (rwho_opts.rplcookie)
+        s += ircsprintf(s, " %s", rwho_opts.rplcookie);
+
+    return s;
+}
+
+/*
+ * Build response text in supplied buffer.
+ */
+static void rwho_reply(aClient *cptr, aClient *ac, char *buf, chanMember *cm)
+{
+    char     *src;
+    char     *dst;
+    aChannel *chptr = NULL;
+
+    dst = buf;
+
+    if (ac->user->channel)
+        chptr = ac->user->channel->value.chptr;
+
+    /* use standard RPL_WHOREPLY if no output flags */
+    if (!rwho_opts.rplfields)
+    {
+        char status[5];
+        char chname[CHANNELLEN+2] = "*";
+
+        if (!cm && (rwho_opts.compat & RWC_CHANNEL) && chptr)
+        {
+            for (cm = chptr->members; cm; cm = cm->next)
+                if (cm->cptr == ac)
+                    break;
+        }
+
+        dst = status;
+        if (ac->user->away)
+            *dst++ = 'G';
+        else
+            *dst++ = 'H';
+        if (IsAnOper(ac))
+            *dst++ = '*';
+        else if (IsInvisible(ac))
+            *dst++ = '%';
+        if (cm)
+        {
+            if (cm->flags & CHFL_CHANOP)
+                *dst++ = '@';
+            else if (cm->flags & CHFL_VOICE)
+                *dst++ = '+';
+        }
+        *dst = 0;
+
+        if (!rwho_opts.chptr && (rwho_opts.compat & RWC_CHANNEL) && chptr)
+        {
+            dst = chname;
+            if (!PubChannel(chptr))
+                *dst++ = '%';
+            if (PubChannel(chptr) || IsAdmin(cptr))
+                strcpy(dst, chptr->chname);
+        }
+
+        if (rwho_opts.compat & RWC_SHOWIP)
+            src = ac->hostip;
+        else
+            src = ac->user->host;
+
+        ircsprintf(buf, getreply(RPL_WHOREPLY), me.name, cptr->name,
+                   rwho_opts.chptr ? rwho_opts.chptr->chname : chname,
+                   ac->user->username, src, ac->user->server,
+                   ac->name, status, ac->hopcount, ac->info);
+        return;
+    }
+
+    if (rwho_opts.rplfields & RWO_NICK)
+    {
+        src = ac->name;
+        *dst++ = ' ';
+        while (*src)
+            *dst++ = *src++;
+    }
+
+    if (rwho_opts.rplfields & RWO_USER)
+    {
+        src = ac->user->username;
+        *dst++ = ' ';
+        while (*src)
+            *dst++ = *src++;
+    }
+
+    if (rwho_opts.rplfields & RWO_HOST)
+    {
+        src = ac->user->host;
+        *dst++ = ' ';
+        while (*src)
+            *dst++ = *src++;
+    }
+
+    if (rwho_opts.rplfields & RWO_IP)
+    {
+        src = ac->hostip;
+        *dst++ = ' ';
+        while (*src)
+            *dst++ = *src++;
+    }
+
+    if (rwho_opts.rplfields & RWO_SERVER)
+    {
+        src = ac->user->server;
+        *dst++ = ' ';
+        while (*src)
+            *dst++ = *src++;
+    }
+
+    if (rwho_opts.rplfields & RWO_FLAGS)
+    {
+        *dst++ = ' ';
+        if (ac->user->away)
+            *dst++ = 'G';
+        else
+            *dst++ = 'H';
+        if (IsAnOper(ac))
+            *dst++ = '*';
+        if (IsInvisible(ac))
+            *dst++ = '%';
+
+        if (!cm && (rwho_opts.rplfields & RWO_CHANNEL) && chptr)
+        {
+            for (cm = chptr->members; cm; cm = cm->next)
+                if (cm->cptr == cptr)
+                    break;
+        }
+
+        if (cm)
+        {
+            if (cm->flags & CHFL_CHANOP)
+                *dst++ = '@';
+            if (cm->flags & CHFL_VOICE)
+                *dst++ = '+';
+        }
+    }
+
+    if (rwho_opts.rplfields & RWO_CHANNEL)
+    {
+        *dst++ = ' ';
+
+        if (!chptr)
+            *dst++ = '*';
+        else
+        {
+            if (!PubChannel(chptr))
+                *dst++ = '%';
+            if (PubChannel(chptr) || IsAdmin(cptr))
+            {
+                src = chptr->chname;
+                while (*src)
+                    *dst++ = *src++;
+            }
+        }
+    }
+
+    if (rwho_opts.rplfields & RWO_JOINS)
+        dst += ircsprintf(dst, " %d", ac->user->joined);
+
+    if (rwho_opts.rplfields & RWO_CLONES)
+        dst += ircsprintf(dst, " %d", rwho_opts.thisclones);
+
+    if (rwho_opts.rplfields & RWO_MATCHES)
+        dst += ircsprintf(dst, " %d", rwho_opts.thismatches);
+
+    if (rwho_opts.rplfields & RWO_TS)
+        dst += ircsprintf(dst, " %d", ac->tsinfo);
+
+    if (rwho_opts.rplfields & RWO_STYPE)
+        dst += ircsprintf(dst, " %d", ac->user->servicetype);
+
+    if (rwho_opts.rplfields & RWO_MODES)
+    {
+        int i;
+
+        *dst++ = ' ';
+        *dst++ = '+';
+        for (i = 0; user_modes[i]; i += 2)
+        {
+            if (ac->umode & user_modes[i])
+                *dst++ = user_modes[i+1];
+        }
+    }
+
+    if (rwho_opts.rplfields & RWO_GCOS)
+    {
+        src = ac->info;
+        *dst++ = ' ';
+        *dst++ = ':';
+        while (*src)
+            *dst++ = *src++;
+    }
+    else if (rwho_opts.rplfields & RWO_AWAY)
+    {
+        src = ac->user->away;
+        *dst++ = ' ';
+        *dst++ = ':';
+        if (src)
+            while (*src)
+                *dst++ = *src++;
+    }
+
+    *dst = 0;
+}
+
+/*
+ * m_rwho - flexible client search with regular expression support
+ * parv[0] - sender
+ * parv[1] - flags
+ * parv[2] - arguments
+ */
+int m_rwho(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    chanMember *cm;
+    aClient    *ac;
+    aClient    *failclient = NULL;
+    int         failcode = 0;
+    int         results = 0;
+    int         left;
+    char       *fill;
+
+    if (!IsAnOper(sptr))
+    {
+        sendto_one(sptr, getreply(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    if (!rwho_parseopts(sptr, parc, parv))
+        return 0;
+
+    left = rwho_opts.limit ? rwho_opts.limit : INT_MAX;
+
+    fill = rwho_prepbuf(sptr);
+
+    if (rwho_opts.chptr && !IsAdmin(sptr) && !ShowChannel(sptr, rwho_opts.chptr))
+        rwho_opts.countonly = 1;
+
+    if (((rwho_opts.check[0] | rwho_opts.check[1]) & (RWM_CLONES|RWM_MATCHES))
+        || (rwho_opts.rplfields & (RWO_CLONES|RWO_MATCHES)))
+    {
+        CloneEnt *ce;
+        aClient *fm;
+
+        for (ce = clones_list; ce; ce = ce->next)
+        {
+            if (!ce->clients)
+                continue;
+
+            if ((rwho_opts.check[0] & RWM_CLONES) &&
+                (ce->gcount < rwho_opts.clones[0]))
+                continue;
+
+            if ((rwho_opts.check[1] & RWM_CLONES) &&
+                (ce->gcount > rwho_opts.clones[1]))
+                continue;
+
+            fm = NULL;
+            rwho_opts.thismatches = 0;
+            rwho_opts.thisclones = ce->gcount;
+
+            /* if using match flag D or summarizing, we need the match count */
+            if (((rwho_opts.check[0] | rwho_opts.check[1]) & RWM_MATCHES)
+                || (rwho_opts.rplfields & RWO_MATCHES))
+            {
+                for (ac = ce->clients; ac; ac = ac->clone.next)
+                {
+                    if (!rwho_match(ac, &failcode, &failclient))
+                        continue;
+
+                    if (!fm)
+                        fm = ac;
+
+                    rwho_opts.thismatches++;
+                }
+
+                /* we know no matches, so no need to process further */
+                if (!rwho_opts.thismatches)
+                    continue;
+
+                if ((rwho_opts.check[0] & RWM_MATCHES) &&
+                    (rwho_opts.thismatches < rwho_opts.matches[0]))
+                    continue;
+
+                if ((rwho_opts.check[1] & RWM_MATCHES) &&
+                    (rwho_opts.thismatches > rwho_opts.matches[1]))
+                    continue;
+            }
+
+            /* if summarizing, we cached from the sweep above */
+            if (rwho_opts.rplfields & RWO_MATCHES)
+            {
+                if (!left)
+                {
+                    sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name,
+                               parv[0], rwho_opts.limit, "RWHO");
+                    break;
+                }
+
+                if (!rwho_opts.countonly)
+                {
+                    rwho_reply(sptr, fm, fill, NULL);
+                    sendto_one(sptr, "%s", rwhobuf);
+                }
+
+                results++;
+                left--;
+                continue;
+            }
+
+            /* not summarizing, so send each match */
+            for (ac = ce->clients; ac; ac = ac->clone.next)
+            {
+                if (!rwho_match(ac, &failcode, &failclient))
+                    continue;
+
+                if (!left)
+                {
+                    sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name,
+                               parv[0], rwho_opts.limit, "RWHO");
+                    break;
+                }
+
+                if (!rwho_opts.countonly)
+                {
+                    rwho_reply(sptr, ac, fill, NULL);
+                    sendto_one(sptr, "%s", rwhobuf);
+                }
+
+                results++;
+                left--;
+            }
+        }
+    }
+    else if (rwho_opts.chptr)
+    {
+        rwho_opts.check[0] &= ~RWM_CHANNEL;
+
+        for (cm = rwho_opts.chptr->members; cm; cm = cm->next)
+        {
+            ac = cm->cptr;
+
+            if (!rwho_match(ac, &failcode, &failclient))
+                continue;
+
+            if (!left)
+            {
+                sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name, parv[0],
+                           rwho_opts.limit, "RWHO");
+                break;
+            }
+
+            if (!rwho_opts.countonly)
+            {
+                rwho_reply(sptr, ac, fill, cm);
+                sendto_one(sptr, "%s", rwhobuf);
+            }
+
+            results++;
+            left--;
+        }
+    }
+    else
+    {
+        for (ac = client; ac; ac = ac->next)
+        {
+            if (!IsClient(ac))
+                continue;
+
+            if (!rwho_match(ac, &failcode, &failclient))
+                continue;
+
+            if (!left)
+            {
+                sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name, parv[0],
+                           rwho_opts.limit, "RWHO");
+                break;
+            }
+
+            if (!rwho_opts.countonly)
+            {
+                rwho_reply(sptr, ac, fill, NULL);
+                sendto_one(sptr, "%s", rwhobuf);
+            }
+
+            results++;
+            left--;
+        }
+    }
+
+    if (rwho_opts.compat)
+        sendto_one(sptr, getreply(RPL_COMMANDSYNTAX), me.name, sptr->name,
+                   "NOTE: match flags C and I are deprecated");
+
+    ircsprintf(rwhobuf, "%d", results);
+    sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, parv[0], rwhobuf,"RWHO");
+
+    if (failcode)
+    {
+        sendto_one(sptr, ":%s NOTICE %s :RWHO: Internal error %d during "
+                   "match, notify coders!", me.name, parv[0], failcode);
+        sendto_one(sptr, ":%s NOTICE %s :RWHO: Match target was: %s %s "
+                   "[%s] [%s]", me.name, parv[0], failclient->name,
+                   failclient->user->username, failclient->info,
+                   failclient->user->away ? failclient->user->away : "");
+    }
+
+    free(rwho_opts.re);
+
+    return 0;
+}
+
diff --git a/src/m_server.c b/src/m_server.c
new file mode 100644 (file)
index 0000000..9566e40
--- /dev/null
@@ -0,0 +1,908 @@
+/* Bahamut IRCd, src/m_server.c
+ * Copyright (c) 2004, Aaron Wiebe and the Bahamut Team
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: m_server.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "h.h"
+#include "dh.h"
+#include "userban.h"
+#include "zlink.h"
+#include "throttle.h"
+#include "clones.h"
+
+/* externally defined functions */
+
+extern void fakelinkserver_update(char *, char *);
+extern void fakeserver_sendserver(aClient *);
+extern void fakelusers_sendlock(aClient *);
+extern void reset_sock_opts(int, int);
+
+/* internal functions */
+
+static void sendnick_TS(aClient *cptr, aClient *acptr)
+{
+    static char ubuf[12];
+
+    if (IsPerson(acptr))
+    {
+        send_umode(NULL, acptr, 0, SEND_UMODES, ubuf);
+        if (!*ubuf)     /* trivial optimization - Dianora */
+        {
+            ubuf[0] = '+';
+            ubuf[1] = '\0';
+        }
+        sendto_one(cptr, "NICK %s %d %ld %s %s %s %s %lu %lu :%s",
+                       acptr->name, acptr->hopcount + 1, acptr->tsinfo, ubuf,
+                       acptr->user->username, acptr->user->host,
+                       acptr->user->server, acptr->user->servicestamp,
+                       htonl(acptr->ip.s_addr), acptr->info);
+    }
+}
+
+static int
+do_server_estab(aClient *cptr)
+{
+    aClient *acptr;
+    aConnect *aconn;
+    aChannel *chptr;
+    int i;
+    /* "refresh" inpath with host  */
+    char *inpath = get_client_name(cptr, HIDEME);
+
+    SetServer(cptr);
+
+    Count.server++;
+    Count.myserver++;
+
+    if(IsZipCapable(cptr) && DoZipThis(cptr))
+    {
+        sendto_one(cptr, "SVINFO ZIP");
+        SetZipOut(cptr);
+        cptr->serv->zip_out = zip_create_output_session();
+    }
+
+#ifdef MAXBUFFERS
+    /* let's try to bump up server sock_opts... -Taner */
+    reset_sock_opts(cptr->fd, 1);
+#endif
+
+    /* adds to server list */
+    add_to_list(&server_list, cptr);
+
+    set_effective_class(cptr);
+
+    /* Check one more time for good measure... is it there? */
+    if ((acptr = find_name(cptr->name, NULL)))
+    {
+        char        nbuf[HOSTLEN * 2 + USERLEN + 5];
+        aClient *bcptr;
+
+        /*
+         * While negotiating stuff, another copy of this server appeared.
+         *
+         * Rather than KILL the link which introduced it, KILL the
+         * youngest of the two links. -avalon
+         */
+
+        bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr :
+            acptr->from;
+        sendto_one(bcptr, "ERROR :Server %s already exists", cptr->name);
+        if (bcptr == cptr)
+        {
+            sendto_gnotice("from %s: Link %s cancelled, server %s already "
+                           "exists (final phase)", me.name,
+                           get_client_name(bcptr, HIDEME), cptr->name);
+            sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, "
+                                "server %s already exists (final phase)",
+                                me.name, get_client_name(bcptr, HIDEME),
+                                cptr->name);
+            return exit_client(bcptr, bcptr, &me,
+                               "Server Exists (final phase)");
+        }
+        /* inform all those who care (set +n) -epi */
+
+        strcpy(nbuf, get_client_name(bcptr, HIDEME));
+        sendto_gnotice("from %s: Link %s cancelled, server %s reintroduced "
+                       "by %s (final phase)", me.name, nbuf, cptr->name,
+                       get_client_name(cptr, HIDEME));
+        sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s "
+                           "reintroduced by %s (final phase)", me.name, nbuf,
+                           cptr->name, get_client_name(cptr, HIDEME));
+        exit_client(bcptr, bcptr, &me, "Server Exists (final phase)");
+    }
+
+    /* error, error, error! if a server is U:'d, and it connects to us,
+     * we need to figure that out! So, do it here. - lucas
+     */
+
+    if (find_aUserver(cptr->name))
+    {
+        Count.myulined++;
+        cptr->flags |= FLAGS_ULINE;
+    }
+
+    fakelinkserver_update(cptr->name, cptr->info);
+
+    sendto_gnotice("from %s: Link with %s established, states:%s%s%s%s",
+                   me.name, inpath, ZipOut(cptr) ? " Output-compressed" : "",
+                   RC4EncLink(cptr) ? " encrypted" : "",
+                   IsULine(cptr) ? " ULined" : "",
+                   DoesTS(cptr) ? " TS" : " Non-TS");
+
+    /*
+     * Notify everyone of the fact that this has just linked: the entire
+     * network should get two of these, one explaining the link between
+     * me->serv and the other between serv->me
+     */
+
+    sendto_serv_butone(NULL, ":%s GNOTICE :Link with %s established: %s",
+                       me.name, inpath,
+                       DoesTS(cptr) ? "TS link" : "Non-TS link!");
+
+    add_to_client_hash_table(cptr->name, cptr);
+
+    /* add it to scache */
+
+    find_or_add(cptr->name);
+
+    /*
+     * Old sendto_serv_but_one() call removed because we now need to
+     * send different names to different servers (domain name
+     * matching) Send new server to other servers.
+     */
+    for (i = 0; i <= highest_fd; i++)
+    {
+        if (!(acptr = local[i]) || !IsServer(acptr) || acptr == cptr ||
+            IsMe(acptr))
+            continue;
+        if ((aconn = acptr->serv->aconn) &&
+            !match(my_name_for_link(me.name, aconn), cptr->name))
+            continue;
+        sendto_one(acptr, ":%s SERVER %s 2 :%s", me.name, cptr->name,
+                   cptr->info);
+    }
+
+    /*
+     * Pass on my client information to the new server
+     *
+     * First, pass only servers (idea is that if the link gets
+     * cancelled beacause the server was already there, there are no
+     * NICK's to be cancelled...). Of course, if cancellation occurs,
+     * all this info is sent anyway, and I guess the link dies when a
+     * read is attempted...? --msa
+     *
+     * Note: Link cancellation to occur at this point means that at
+     * least two servers from my fragment are building up connection
+     * this other fragment at the same time, it's a race condition,
+     * not the normal way of operation...
+     *
+     * ALSO NOTE: using the get_client_name for server names-- see
+     * previous *WARNING*!!! (Also, original inpath is
+     * destroyed...)
+     */
+
+    aconn = cptr->serv->aconn;
+    for (acptr = &me; acptr; acptr = acptr->prev)
+    {
+        if (acptr->from == cptr)
+            continue;
+        if (IsServer(acptr))
+        {
+            if (match(my_name_for_link(me.name, aconn), acptr->name) == 0)
+                continue;
+            sendto_one(cptr, ":%s SERVER %s %d :%s",
+                       acptr->serv->up, acptr->name,
+                       acptr->hopcount + 1, acptr->info);
+        }
+    }
+
+    /* send out our SQLINES and SGLINES too */
+    send_simbans(cptr, SBAN_CHAN|SBAN_NETWORK);
+    send_simbans(cptr, SBAN_NICK|SBAN_NETWORK);
+    send_simbans(cptr, SBAN_GCOS|SBAN_NETWORK);
+
+    /* Send out fake server list and other 'fake' stuff */
+    fakeserver_sendserver(cptr);
+
+    /* send clone list */
+    clones_send(cptr);
+
+    /* Bursts are about to start.. send a BURST */
+    if (IsBurst(cptr))
+        sendto_one(cptr, "BURST");
+
+    /*
+     * * Send it in the shortened format with the TS, if it's a TS
+     * server; walk the list of channels, sending all the nicks that
+     * haven't been sent yet for each channel, then send the channel
+     * itself -- it's less obvious than sending all nicks first, but
+     * on the receiving side memory will be allocated more nicely
+     * saving a few seconds in the handling of a split -orabidoo
+     */
+    {
+        chanMember       *cm;
+        static char nickissent = 1;
+
+        nickissent = 3 - nickissent;
+        /*
+         * flag used for each nick to check if we've sent it yet - must
+         * be different each time and !=0, so we alternate between 1 and
+         * 2 -orabidoo
+         */
+        for (chptr = channel; chptr; chptr = chptr->nextch)
+        {
+            for (cm = chptr->members; cm; cm = cm->next)
+            {
+                acptr = cm->cptr;
+                if (acptr->nicksent != nickissent)
+                {
+                    acptr->nicksent = nickissent;
+                    if (acptr->from != cptr)
+                        sendnick_TS(cptr, acptr);
+                }
+            }
+            send_channel_modes(cptr, chptr);
+        }
+        /* also send out those that are not on any channel */
+        for (acptr = &me; acptr; acptr = acptr->prev)
+            if (acptr->nicksent != nickissent)
+            {
+                acptr->nicksent = nickissent;
+                if (acptr->from != cptr)
+                    sendnick_TS(cptr, acptr);
+            }
+    }
+
+    if(confopts & FLAGS_HUB)
+        fakelusers_sendlock(cptr);
+
+    if(ZipOut(cptr))
+    {
+        unsigned long inb, outb;
+        double rat;
+
+        zip_out_get_stats(cptr->serv->zip_out, &inb, &outb, &rat);
+
+        if(inb)
+        {
+            sendto_gnotice("from %s: Connect burst to %s: %lu bytes normal, "
+                           "%lu compressed (%3.2f%%)", me.name,
+                           get_client_name(cptr, HIDEME), inb, outb, rat);
+            sendto_serv_butone(cptr, ":%s GNOTICE :Connect burst to %s: %lu "
+                               "bytes normal, %lu compressed (%3.2f%%)",
+                               me.name, get_client_name(cptr, HIDEME), inb,
+                               outb, rat);
+        }
+    }
+
+    /* stuff a PING at the end of this burst so we can figure out when
+       the other side has finished processing it. */
+    cptr->flags |= FLAGS_BURST|FLAGS_PINGSENT;
+    if (IsBurst(cptr)) cptr->flags |= FLAGS_SOBSENT;
+    sendto_one(cptr, "PING :%s", me.name);
+
+    return 0;
+}
+
+static int
+m_server_estab(aClient *cptr)
+{
+    aConnect *aconn;
+
+    char       *inpath, *host, *s, *encr;
+    int         split;
+
+    inpath = get_client_name(cptr, HIDEME);  /* "refresh" inpath with host  */
+    split = mycmp(cptr->name, cptr->sockhost);
+    host = cptr->name;
+
+    if (!(aconn = cptr->serv->aconn))
+    {
+        ircstp->is_ref++;
+        sendto_one(cptr, "ERROR :Access denied. No Connect block for server %s",
+                   inpath);
+        sendto_ops("Access denied. No Connect block for server %s", inpath);
+        return exit_client(cptr, cptr, cptr, "No Connect block for server");
+    }
+
+    encr = cptr->passwd;
+    if (*aconn->apasswd && !StrEq(aconn->apasswd, encr))
+    {
+        ircstp->is_ref++;
+        sendto_one(cptr, "ERROR :No Access (passwd mismatch) %s", inpath);
+        sendto_ops("Access denied (passwd mismatch) %s", inpath);
+        return exit_client(cptr, cptr, cptr, "Bad Password");
+    }
+    memset(cptr->passwd, '\0', sizeof(cptr->passwd));
+
+    if(!(confopts & FLAGS_HUB))
+    {
+        int i;
+        for (i = 0; i <= highest_fd; i++)
+            if (local[i] && IsServer(local[i]))
+            {
+                ircstp->is_ref++;
+                sendto_one(cptr, "ERROR :I'm a leaf not a hub");
+                return exit_client(cptr, cptr, cptr, "I'm a leaf");
+            }
+    }
+
+    /* aconf->port is a CAPAB field, kind-of. kludge. mm, mm. */
+    /* no longer! this should still get better though */
+    if((aconn->flags & CONN_ZIP))
+        SetZipCapable(cptr);
+    if((aconn->flags & CONN_DKEY))
+        SetWantDKEY(cptr);
+    if (IsUnknown(cptr))
+    {
+        if (aconn->cpasswd[0])
+            sendto_one(cptr, "PASS %s :TS", aconn->cpasswd);
+
+        /* Pass my info to the new server */
+
+#ifdef HAVE_ENCRYPTION_ON
+        if(!WantDKEY(cptr))
+            sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP "
+                       "NICKIP TSMODE");
+        else
+            sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT DKEY "
+                       "ZIP NICKIP TSMODE");
+#else
+        sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP NICKIP TSMODE");
+#endif
+
+        sendto_one(cptr, "SERVER %s 1 :%s",
+                   my_name_for_link(me.name, aconn),
+                   (me.info[0]) ? (me.info) : "IRCers United");
+    }
+    else 
+    {
+        s = (char *) strchr(aconn->host, '@');
+        *s = '\0';      /* should never be NULL -- wanna bet? -Dianora */
+
+        Debug((DEBUG_INFO, "Check Usernames [%s]vs[%s]", aconn->host,
+               cptr->username));
+        if (match(aconn->host, cptr->username))
+        {
+            *s = '@';
+            ircstp->is_ref++;
+            sendto_ops("Username mismatch [%s]v[%s] : %s",
+                       aconn->host, cptr->username,
+                       get_client_name(cptr, HIDEME));
+            sendto_one(cptr, "ERROR :No Username Match");
+            return exit_client(cptr, cptr, cptr, "Bad User");
+        }
+        *s = '@';
+    }
+
+    /* send routing notice, this should never happen anymore */
+    if (!DoesTS(cptr))
+    {
+        sendto_gnotice("from %s: Warning: %s linked, non-TS server",
+                       me.name, get_client_name(cptr, TRUE));
+        sendto_serv_butone(cptr,
+                           ":%s GNOTICE :Warning: %s linked, non-TS server",
+                           me.name, get_client_name(cptr, TRUE));
+    }
+
+    sendto_one(cptr, "SVINFO %d %d 0 :%ld", TS_CURRENT, TS_MIN,
+               (ts_val) timeofday);
+
+    /* sendto one(cptr, "CAPAB ...."); moved to after PASS but before SERVER
+     * now in two places.. up above and in s_bsd.c. - lucas
+     * This is to make sure we pass on our capabilities before we establish
+     * a server connection
+     */
+
+    /*
+     * *WARNING*
+     *   In the following code in place of plain
+     * server's name we send what is returned by
+     * get_client_name which may add the "sockhost" after the name.
+     * It's *very* *important* that there is a SPACE between
+     * the name and sockhost (if present). The receiving server
+     * will start the information field from this first blank and
+     * thus puts the sockhost into info. ...a bit tricky, but
+     * you have been warned, besides code is more neat this way...
+     * --msa
+     */
+
+    cptr->serv->up = me.name;
+    cptr->serv->aconn = aconn;
+
+    throttle_remove(inetntoa((char *)&cptr->ip));
+
+    /* now fill out the servers info so nobody knows dink about it. */
+    memset((char *)&cptr->ip, '\0', sizeof(struct in_addr));
+    strcpy(cptr->hostip, "127.0.0.1");
+    strcpy(cptr->sockhost, "localhost");
+
+#ifdef HAVE_ENCRYPTION_ON
+    if(!CanDoDKEY(cptr) || !WantDKEY(cptr))
+        return do_server_estab(cptr);
+    else
+    {
+        SetNegoServer(cptr); /* VERY IMPORTANT THAT THIS IS HERE */
+        sendto_one(cptr, "DKEY START");
+    }
+#else
+    return do_server_estab(cptr);
+#endif
+
+    return 0;
+}
+
+/*
+ *  m_server
+ *       parv[0] = sender prefix
+ *       parv[1] = servername
+ *       parv[2] = serverinfo/hopcount
+ *       parv[3] = serverinfo
+ */
+int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int     i;
+    char        info[REALLEN + 1], *inpath, *host;
+    aClient    *acptr, *bcptr;
+    aConnect   *aconn;
+    int         hop;
+    char        nbuf[HOSTLEN * 2 + USERLEN + 5]; /* same size as in s_misc.c */
+
+    info[0] = '\0';
+    inpath = get_client_name(cptr, HIDEME);
+
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        sendto_one(cptr, "ERROR :No servername");
+        return 0;
+    }
+
+    hop = 0;
+    host = parv[1];
+    if (parc > 3 && atoi(parv[2]))
+    {
+        hop = atoi(parv[2]);
+        strncpyzt(info, parv[3], REALLEN);
+    }
+    else if (parc > 2)
+    {
+        strncpyzt(info, parv[2], REALLEN);
+        if ((parc > 3) && ((i = strlen(info)) < (REALLEN - 2)))
+        {
+            strcat(info, " ");
+            strncat(info, parv[3], REALLEN - i - 2);
+            info[REALLEN] = '\0';
+        }
+    }
+    /*
+     * July 5, 1997
+     * Rewritten to throw away server cruft from users,
+     * combined the hostname validity test with cleanup of host name,
+     * so a cleaned up hostname can be returned as an error if
+     * necessary. - Dianora
+     */
+
+    /* yes, the if(strlen) below is really needed!! */
+    if (strlen(host) > HOSTLEN)
+        host[HOSTLEN] = '\0';
+
+    if (IsPerson(cptr))
+    {
+        /* A local link that has been identified as a USER tries
+         * something fishy... ;-)
+         */
+        sendto_one(cptr, err_str(ERR_UNKNOWNCOMMAND),
+                   me.name, parv[0], "SERVER");
+
+        return 0;
+    }
+    else
+        /* hostile servername check */
+    {
+        /*
+         * Lets check for bogus names and clean them up we don't bother
+         * cleaning up ones from users, becasuse we will never see them
+         * any more - Dianora
+         */
+
+        int bogus_server = 0;
+        int found_dot = 0;
+        char clean_host[(2 * HOSTLEN) + 1];
+        char *s;
+        char *d;
+        int n;
+
+        s = host;
+        d = clean_host;
+        n = (2 * HOSTLEN) - 2;
+
+        while (*s && n > 0)
+        {
+            if ((unsigned char) *s < (unsigned char) ' ')
+                /* Is it a control character? */
+            {
+                bogus_server = 1;
+                *d++ = '^';
+                *d++ = (char) ((unsigned char) *s + 0x40);
+                /* turn it into a printable */
+                n -= 2;
+            }
+            else if ((unsigned char) *s > (unsigned char) '~')
+            {
+                bogus_server = 1;
+                *d++ = '.';
+                n--;
+            }
+            else
+            {
+                if (*s == '.')
+                    found_dot = 1;
+                *d++ = *s;
+                n--;
+            }
+            s++;
+        }
+        *d = '\0';
+
+        if ((!found_dot) || bogus_server)
+        {
+            sendto_one(sptr, "ERROR :Bogus server name (%s)",
+                       clean_host);
+            return exit_client(cptr, cptr, cptr, "Bogus server name");
+        }
+    }
+
+    /*
+     * check to see this host even has an N line before bothering
+     * anyone about it. Its only a quick sanity test to stop the
+     * conference room and win95 ircd dorks. Sure, it will be
+     * redundantly checked again in m_server_estab() *sigh* yes there
+     * will be wasted CPU as the conf list will be scanned twice. But
+     * how often will this happen? - Dianora
+     *
+     * This should (will be) be recoded to check the IP is valid as well,
+     * with a pointer to the valid N line conf kept for later, saving an
+     * extra lookup.. *sigh* - Dianora
+     */
+    if (!IsServer(cptr))
+    {
+        if (!find_aConnect(host))
+        {
+
+#ifdef WARN_NO_NLINE
+            sendto_realops("Link %s dropped, no Connect block",
+                           get_client_name(cptr, TRUE));
+#endif
+
+            return exit_client(cptr, cptr, cptr, "No Connect block");
+        }
+    }
+
+    if ((acptr = find_name(host, NULL)))
+    {
+        /*
+         * * This link is trying feed me a server that I already have
+         * access through another path -- multiple paths not accepted
+         * currently, kill this link immediately!!
+         *
+         * Rather than KILL the link which introduced it, KILL the
+         * youngest of the two links. -avalon
+         */
+
+        bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr :
+            acptr->from;
+        sendto_one(bcptr, "ERROR :Server %s already exists", host);
+        if (bcptr == cptr)
+        {
+            /* Don't complain for servers that are juped */
+            /* (don't complain if the server that already exists is U: lined,
+                unless I actually have a .conf U: line for it */
+            if(!IsULine(acptr) || !find_aUserver(acptr->name))
+            {
+                sendto_gnotice("from %s: Link %s cancelled, server %s already "
+                               "exists", me.name, get_client_name(bcptr, HIDEME),
+                               host);
+                sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, "
+                                   "server %s already exists", me.name,
+                                   get_client_name(bcptr, HIDEME), host);
+            }
+            return exit_client(bcptr, bcptr, &me, "Server Exists");
+        }
+        /* inform all those who care (set +n) -epi */
+        strcpy(nbuf, get_client_name(bcptr, HIDEME));
+        sendto_gnotice("from %s: Link %s cancelled, server %s reintroduced "
+                       "by %s", me.name, nbuf, host,
+                       get_client_name(cptr, HIDEME));
+        sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s "
+                           "reintroduced by %s", me.name, nbuf, host,
+                           get_client_name(cptr, HIDEME));
+        exit_client(bcptr, bcptr, &me, "Server Exists");
+    }
+    /*
+     * The following if statement would be nice to remove since user
+     * nicks never have '.' in them and servers must always have '.' in
+     * them. There should never be a server/nick name collision, but it
+     * is possible a capricious server admin could deliberately do
+     * something strange.
+     *
+     * -Dianora
+     */
+
+    if ((acptr = find_client(host, NULL)) && acptr != cptr)
+    {
+        /*
+         * * Server trying to use the same name as a person. Would
+         * cause a fair bit of confusion. Enough to make it hellish for
+         * a while and servers to send stuff to the wrong place.
+         */
+        sendto_one(cptr, "ERROR :Nickname %s already exists!", host);
+        strcpy(nbuf, get_client_name(cptr, HIDEME));
+        sendto_gnotice("from %s: Link %s cancelled, servername/nick collision",
+                       me.name, nbuf);
+        sendto_serv_butone(cptr, ":%s GNOTICE :Link %s cancelled, "
+                           "servername/nick collision", me.name, nbuf);
+        return exit_client(cptr, cptr, cptr, "Nick as Server");
+    }
+
+    if (IsServer(cptr))
+    {
+        /*
+         * * Server is informing about a new server behind this link.
+         * Create REMOTE server structure, add it to list and propagate
+         * word to my other server links...
+         */
+        if (parc == 1 || info[0] == '\0')
+        {
+            sendto_one(cptr, "ERROR :No server info specified for %s", host);
+            return 0;
+        }
+        /*
+         * * See if the newly found server is behind a guaranteed leaf
+         * (L-line). If so, close the link.
+         *
+         * Depreciated.  Kinda redundant with Hlines. -epi
+         */
+        if (!(cptr->serv->aconn->flags & CONN_HUB))
+        {
+            aconn = cptr->serv->aconn;
+            sendto_gnotice("from %s: Non-Hub link %s introduced %s(%s).",
+                           me.name, get_client_name(cptr, HIDEME), host,
+                           aconn ? (aconn->host ? aconn->host : "*") : "!");
+            sendto_serv_butone(cptr,":%s GNOTICE :Non-Hub link %s introduced "
+                               "%s(%s).", me.name,
+                               get_client_name(cptr, HIDEME), host,
+                               aconn ? (aconn->host ? aconn->host : "*") :
+                               "!");
+            sendto_one(cptr, "ERROR :You're not a hub (introducing %s)",
+                       host);
+            return exit_client(cptr, cptr, cptr, "Too many servers");
+        }
+
+        acptr = make_client(cptr, sptr);
+        make_server(acptr);
+        acptr->hopcount = hop;
+        strncpyzt(acptr->name, host, sizeof(acptr->name));
+        strncpyzt(acptr->info, info, REALLEN);
+        acptr->serv->up = find_or_add(parv[0]);
+
+        fakelinkserver_update(acptr->name, acptr->info);
+        SetServer(acptr);
+
+        /*
+         * if this server is behind a U-lined server, make it U-lined as
+         * well. - lucas
+         */
+
+        if (IsULine(sptr) || find_aUserver(acptr->name))
+        {
+            acptr->flags |= FLAGS_ULINE;
+            sendto_realops_lev(DEBUG_LEV, "%s introducing super server %s",
+                               cptr->name, acptr->name);
+        }
+
+        Count.server++;
+
+        add_client_to_list(acptr);
+        add_to_client_hash_table(acptr->name, acptr);
+        /*
+         * Old sendto_serv_but_one() call removed because we now need
+         * to send different names to different servers (domain name matching)
+         */
+        for (i = 0; i <= highest_fd; i++)
+        {
+            if (!(bcptr = local[i]) || !IsServer(bcptr) || bcptr == cptr ||
+                IsMe(bcptr))
+                continue;
+            if (!(aconn = bcptr->serv->aconn))
+            {
+                sendto_gnotice("from %s: Lost Connect block for %s on %s."
+                               " Closing", me.name,
+                               get_client_name(cptr, HIDEME), host);
+                sendto_serv_butone(cptr, ":%s GNOTICE :Lost Connect block for"
+                                   " %s on %s. Closing", me.name,
+                                   get_client_name(cptr, HIDEME), host);
+                return exit_client(cptr, cptr, cptr, "Lost Connect block");
+            }
+            if (match(my_name_for_link(me.name, aconn), acptr->name) == 0)
+                continue;
+            sendto_one(bcptr, ":%s SERVER %s %d :%s",
+                       parv[0], acptr->name, hop + 1, acptr->info);
+        }
+        return 0;
+    }
+
+    if (!IsUnknown(cptr) && !IsHandshake(cptr))
+        return 0;
+
+    /*
+     * * A local link that is still in undefined state wants to be a
+     * SERVER. Check if this is allowed and change status
+     * accordingly...
+     */
+    /*
+     * * Reject a direct nonTS server connection if we're TS_ONLY
+     * -orabidoo
+     */
+
+    strncpyzt(cptr->name, host, sizeof(cptr->name));
+    strncpyzt(cptr->info, info[0] ? info : me.name, REALLEN);
+    cptr->hopcount = hop;
+
+    switch (check_server_init(cptr))
+    {
+        case 0:
+            return m_server_estab(cptr);
+        case 1:
+            sendto_ops("Access check for %s in progress",
+                       get_client_name(cptr, HIDEME));
+            return 1;
+        default:
+            ircstp->is_ref++;
+            sendto_ops("Received unauthorized connection from %s.",
+                       get_client_host(cptr));
+            return exit_client(cptr, cptr, cptr, "No Connect block");
+    }
+}
+
+/* m_dkey
+ * lucas's code, i assume.
+ * moved here from s_serv.c due to its integration in the encrypted 
+ * server negotiation stuffs. -epi
+ */
+
+#define DKEY_GOTIN  0x01
+#define DKEY_GOTOUT 0x02
+
+#define DKEY_DONE(x) (((x) & (DKEY_GOTIN|DKEY_GOTOUT)) == \
+                      (DKEY_GOTIN|DKEY_GOTOUT))
+
+int m_dkey(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    if(!(IsNegoServer(sptr) && parc > 1))
+    {
+        if(IsPerson(sptr))
+            return 0;
+        return exit_client(sptr, sptr, sptr, "Not negotiating now");
+    }
+#ifdef HAVE_ENCRYPTION_ON
+    if(mycmp(parv[1], "START") == 0)
+    {
+        char keybuf[1024];
+
+        if(parc != 2)
+            return exit_client(sptr, sptr, sptr, "DKEY START failure");
+
+        if(sptr->serv->sessioninfo_in != NULL &&
+           sptr->serv->sessioninfo_out != NULL)
+            return exit_client(sptr, sptr, sptr, "DKEY START duplicate?!");
+
+        sptr->serv->sessioninfo_in = dh_start_session();
+        sptr->serv->sessioninfo_out = dh_start_session();
+
+        sendto_realops("Initiating diffie-hellman key exchange with %s",
+                       sptr->name);
+
+        dh_get_s_public(keybuf, 1024, sptr->serv->sessioninfo_in);
+        sendto_one(sptr, "DKEY PUB I %s", keybuf);
+
+        dh_get_s_public(keybuf, 1024, sptr->serv->sessioninfo_out);
+        sendto_one(sptr, "DKEY PUB O %s", keybuf);
+        return 0;
+    }
+
+    if(mycmp(parv[1], "PUB") == 0)
+    {
+        char keybuf[1024];
+        int keylen;
+
+        if(parc != 4 || !sptr->serv->sessioninfo_in ||
+           !sptr->serv->sessioninfo_out)
+            return exit_client(sptr, sptr, sptr, "DKEY PUB failure");
+
+        if(mycmp(parv[2], "O") == 0) /* their out is my in! */
+        {
+            if(!dh_generate_shared(sptr->serv->sessioninfo_in, parv[3]))
+                return exit_client(sptr, sptr, sptr, "DKEY PUB O invalid");
+            sptr->serv->dkey_flags |= DKEY_GOTOUT;
+        }
+        else if(mycmp(parv[2], "I") == 0) /* their out is my in! */
+        {
+            if(!dh_generate_shared(sptr->serv->sessioninfo_out, parv[3]))
+                return exit_client(sptr, sptr, sptr, "DKEY PUB I invalid");
+            sptr->serv->dkey_flags |= DKEY_GOTIN;
+        }
+        else
+            return exit_client(sptr, sptr, sptr, "DKEY PUB bad option");
+
+        if(DKEY_DONE(sptr->serv->dkey_flags))
+        {
+            sendto_one(sptr, "DKEY DONE");
+            SetRC4OUT(sptr);
+
+            keylen = 1024;
+            if(!dh_get_s_shared(keybuf, &keylen, sptr->serv->sessioninfo_in))
+                return exit_client(sptr, sptr, sptr,
+                                   "Could not setup encrypted session");
+            sptr->serv->rc4_in = rc4_initstate(keybuf, keylen);
+
+            keylen = 1024;
+            if(!dh_get_s_shared(keybuf, &keylen, sptr->serv->sessioninfo_out))
+                return exit_client(sptr, sptr, sptr,
+                                   "Could not setup encrypted session");
+            sptr->serv->rc4_out = rc4_initstate(keybuf, keylen);
+
+            dh_end_session(sptr->serv->sessioninfo_in);
+            dh_end_session(sptr->serv->sessioninfo_out);
+
+            sptr->serv->sessioninfo_in = sptr->serv->sessioninfo_out = NULL;
+            return 0;
+        }
+
+        return 0;
+    }
+
+
+    if(mycmp(parv[1], "DONE") == 0)
+    {
+        if(!((sptr->serv->sessioninfo_in == NULL &&
+              sptr->serv->sessioninfo_out == NULL) &&
+             (sptr->serv->rc4_in != NULL && sptr->serv->rc4_out != NULL)))
+            return exit_client(sptr, sptr, sptr, "DKEY DONE when not done!");
+        SetRC4IN(sptr);
+        sendto_realops("Diffie-Hellman exchange with %s complete, connection "
+                       "encrypted.", sptr->name);
+        sendto_one(sptr, "DKEY EXIT");
+        return RC4_NEXT_BUFFER;
+    }
+
+    if(mycmp(parv[1], "EXIT") == 0)
+    {
+        if(!(IsRC4IN(sptr) && IsRC4OUT(sptr)))
+            return exit_client(sptr, sptr, sptr, "DKEY EXIT when not in "
+                               "proper stage");
+        ClearNegoServer(sptr);
+        return do_server_estab(sptr);
+    }
+#endif
+    return 0;
+}
+
diff --git a/src/m_services.c b/src/m_services.c
new file mode 100644 (file)
index 0000000..67b6515
--- /dev/null
@@ -0,0 +1,699 @@
+/* m_services.c - Because s_user.c was just crazy.
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   This program is free softwmare; 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: m_services.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "channel.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include "h.h"
+#include "userban.h"
+#include "clones.h"
+
+/* Externally defined stuffs */
+extern int user_modes[];
+
+/*
+ * the services aliases. *
+ *
+ * NICKSERV     - /nickserv * CHANSERV  - /chanserv * OPERSERV  -
+ * /operserv * MEMOSERV         - /memoserv * SERVICES  - /services *
+ * IDENTIFY     - /identify * taz's code -mjs
+ */
+
+/* Code provided by orabidoo */
+/*
+ * a random number generator loosely based on RC5; assumes ints are at
+ * least 32 bit
+ */
+
+static unsigned long 
+my_rand()
+{
+    static unsigned long s = 0, t = 0, k = 12345678;
+    int         i;
+
+    if (s == 0 && t == 0)
+    {
+        s = (unsigned long) getpid();
+        t = (unsigned long) time(NULL);
+    }
+    for (i = 0; i < 12; i++)
+    {
+        s = (((s ^ t) << (t & 31)) | ((s ^ t) >> (31 - (t & 31)))) + k;
+        k += s + t;
+        t = (((t ^ s) << (s & 31)) | ((t ^ s) >> (31 - (s & 31)))) + k;
+        k += s + t;
+    }
+    return s;
+}
+
+/* m_ns */
+int m_ns(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient    *acptr;
+
+    if (check_registered_user(sptr))
+       return 0;
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        if(MyClient(sptr))
+         sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_server(Services_Name, NULL)))
+    {
+        if(confopts & FLAGS_SERVHUB)
+               sendto_one(acptr, ":%s NS :%s", parv[0], parv[1]);
+        else
+               sendto_one(acptr, ":%s PRIVMSG %s@%s :%s", 
+                       parv[0], NICKSERV, Services_Name, parv[1]);
+    }
+    else
+       sendto_one(sptr, err_str(ERR_SERVICESDOWN), me.name,
+                  parv[0], NICKSERV);
+    return 0;
+}
+
+/* m_cs */
+int m_cs(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient    *acptr;
+
+    if (check_registered_user(sptr))
+       return 0;
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        if(MyClient(sptr))
+         sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_server(Services_Name, NULL)))
+    {
+        if(confopts & FLAGS_SERVHUB)
+
+               sendto_one(acptr, ":%s CS :%s", parv[0], parv[1]);
+        else
+               sendto_one(acptr, ":%s PRIVMSG %s@%s :%s", 
+                       parv[0], CHANSERV, Services_Name, parv[1]);
+    }
+    else
+       sendto_one(sptr, err_str(ERR_SERVICESDOWN), me.name,
+                  parv[0], CHANSERV);
+    return 0;
+}
+
+/* m_ms */
+int m_ms(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient    *acptr;
+
+    if (check_registered_user(sptr))
+       return 0;
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        if(MyClient(sptr))
+         sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_server(Services_Name, NULL)))
+    {
+        if(confopts & FLAGS_SERVHUB)
+               sendto_one(acptr, ":%s MS :%s", parv[0], parv[1]);
+        else
+               sendto_one(acptr, ":%s PRIVMSG %s@%s :%s", 
+                       parv[0], MEMOSERV, Services_Name, parv[1]);
+    }
+    else
+       sendto_one(sptr, err_str(ERR_SERVICESDOWN), me.name,
+                  parv[0], MEMOSERV);
+    return 0;
+}
+
+/* m_rs */
+int m_rs(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient    *acptr;
+
+    if (check_registered_user(sptr))
+       return 0;
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        if(MyClient(sptr))
+         sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_server(Services_Name, NULL)))
+    {
+        if(confopts & FLAGS_SERVHUB)
+               sendto_one(acptr, ":%s RS :%s", parv[0], parv[1]);
+        else
+               sendto_one(acptr, ":%s PRIVMSG %s@%s :%s", 
+                       parv[0], ROOTSERV, Services_Name, parv[1]);
+    }
+    else
+       sendto_one(sptr, err_str(ERR_SERVICESDOWN), me.name,
+                  parv[0], ROOTSERV);
+    return 0;
+}
+
+/* m_os */
+int m_os(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient    *acptr;
+
+    if (check_registered_user(sptr))
+       return 0;
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        if(MyClient(sptr))
+         sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_server(Stats_Name, NULL)))
+    {
+        if(confopts & FLAGS_SERVHUB)
+               sendto_one(acptr, ":%s OS :%s", parv[0], parv[1]);
+        else
+               sendto_one(acptr, ":%s PRIVMSG %s@%s :%s", 
+                   parv[0], OPERSERV, Stats_Name, parv[1]);
+    }
+    else
+       sendto_one(sptr, err_str(ERR_SERVICESDOWN), me.name,
+                  parv[0], OPERSERV);
+    return 0;
+}
+
+/* m_ss */
+int m_ss(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient    *acptr;
+
+    if (check_registered_user(sptr))
+       return 0;
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        if(MyClient(sptr))
+         sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_server(Stats_Name, NULL)))
+    {
+        if(confopts & FLAGS_SERVHUB)
+               sendto_one(acptr, ":%s SS :%s", parv[0], parv[1]);
+        else
+               sendto_one(acptr, ":%s PRIVMSG %s@%s :%s", 
+                       parv[0], STATSERV, Stats_Name, parv[1]);
+    }
+    else
+       sendto_one(sptr, err_str(ERR_SERVICESDOWN), me.name,
+                  parv[0], STATSERV);
+    return 0;
+}
+
+/* m_hs */
+int m_hs(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient    *acptr;
+
+    if (check_registered_user(sptr))
+       return 0;
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        if(MyClient(sptr))
+         sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_server(Stats_Name, NULL)))
+    {
+        if(confopts & FLAGS_SERVHUB)
+               sendto_one(acptr, ":%s HS :%s", parv[0], parv[1]);
+        else
+               sendto_one(acptr, ":%s PRIVMSG %s@%s :%s", 
+                       parv[0], HELPSERV, Stats_Name, parv[1]);
+    }
+    else
+       sendto_one(sptr, err_str(ERR_SERVICESDOWN), me.name,
+                  parv[0], HELPSERV);
+    return 0;
+}
+
+/* m_services -- see df465+taz */
+int m_services(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       *tmps;
+
+    if (check_registered_user(sptr))
+       return 0;
+
+    if (parc < 2 || *parv[1] == '\0')
+    {
+       sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((strlen(parv[1]) >= 4) && (!myncmp(parv[1], "help", 4)))
+    {
+       sendto_one(sptr, ":services!service@%s NOTICE %s :For ChanServ "
+                  "help use: /chanserv help", Services_Name,
+                  sptr->name);
+       sendto_one(sptr, ":services!service@%s NOTICE %s :For NickServ "
+                  "help use: /nickserv help", Services_Name,
+                  sptr->name);
+       sendto_one(sptr, ":services!service@%s NOTICE %s :For MemoServ "
+                  "help use: /memoserv help", Services_Name,
+                  sptr->name);
+       return 0;
+    }
+    if ((tmps = (char *) strchr(parv[1], ' ')))
+    {
+       for(; *tmps == ' '; tmps++); /* er.. before this for loop, the next
+                                     * comparison would always compare '#' 
+                                     * with ' '.. oops. - lucas
+                                     */
+       if (*tmps == '#')
+           return m_cs(cptr, sptr, parc, parv);
+       else
+           return m_ns(cptr, sptr, parc, parv);
+    }
+    return m_ns(cptr, sptr, parc, parv);
+}
+
+/* m_identify  df465+taz */
+int m_identify(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       buf[BUFSIZE+1];
+    char       *myparv[MAXPARA + 1];
+
+    if (check_registered_user(sptr))
+       return 0;
+
+    if (parc < 2 || *parv[1] == '\0')
+    {
+       sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    (void) ircsnprintf(buf, BUFSIZE, "IDENTIFY %s", parv[1]);
+
+    myparv[0]=parv[0];
+    myparv[1]=buf;
+
+    if (*parv[1] == '#')
+      return m_cs(cptr, sptr, parc, myparv);
+    else
+      return m_ns(cptr, sptr, parc, myparv);
+
+    return 0;
+}
+
+/* s_svsnick - Pretty straight forward.  Mostly straight outta df
+ *  - Raistlin
+ * parv[0] = sender
+ * parv[1] = old nickname
+ * parv[2] = new nickname
+ * parv[3] = timestamp
+ */
+int m_svsnick(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient *acptr, *ocptr;
+    char newnick[NICKLEN + 1];
+
+    if (!IsULine(sptr)||parc < 4||(strlen(parv[2]) > NICKLEN)) 
+       return 0;
+
+    if(hunt_server(cptr, sptr, ":%s SVSNICK %s %s :%s", 1, parc, parv) != HUNTED_ISME)
+       return 0;
+
+    /* can't find them? oh well. */
+    if ((acptr = find_person(parv[1], NULL)) == NULL)
+       return 0;
+
+    strncpyzt(newnick, parv[2], NICKLEN+1);
+
+    /* does the nick we're changing them to already exist? */
+    /* Try to make a unique nickname */
+    if((ocptr = find_client(newnick, NULL)) != NULL)
+    {
+        int tries = 0, nprefix;
+
+        do 
+        {
+           nprefix = my_rand() % 99999;
+           ircsnprintf(newnick, NICKLEN, "%s-%d", parv[2], nprefix);
+            tries++;
+        } while (((ocptr = find_client(newnick, NULL)) != NULL) && (tries < 10));
+
+       /* well, we tried.. */
+        if(ocptr)
+        {
+           if(IsUnknown(ocptr))
+              return exit_client(ocptr, ocptr, &me, "SVSNICK Override");
+           else
+              return exit_client(acptr, acptr, &me, "SVSNICK Collide");
+        }
+    }
+
+    if(acptr->umode & UMODE_r)
+    {
+       unsigned int oldumode;
+       char mbuf[BUFSIZE];
+
+       oldumode = acptr->umode;
+       acptr->umode &= ~UMODE_r;
+
+        send_umode(acptr, acptr, oldumode, ALL_UMODES, mbuf);
+    }
+
+    acptr->tsinfo = atoi(parv[3]);
+#ifdef ANTI_NICK_FLOOD
+    acptr->last_nick_change = atoi(parv[3]);
+#endif
+    sendto_common_channels(acptr, ":%s NICK :%s", parv[1], newnick);
+    add_history(acptr, 1);
+    sendto_serv_butone(NULL, ":%s NICK %s :%d", parv[1], newnick,
+                      acptr->tsinfo);
+    if(acptr->name[0]) 
+       del_from_client_hash_table(acptr->name, acptr);
+    strcpy(acptr->name, newnick);
+    add_to_client_hash_table(acptr->name, acptr);
+
+    return 0;
+}
+
+/* channel_svsmode:
+ * parv[0] sender
+ * parv[1] channel
+ * parv[2] modes
+ * parv[3] nick
+ * parv[4] nickts
+ * currently, only a mode of -b is supported.
+ * services should use MODE for regular channel modes.
+ * 2/5/00 lucas
+ * preconditions: parc >= 3, sptr is ulined
+ */
+int channel_svsmode(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aChannel *chptr;
+    aClient *acptr = NULL;
+    char *m, *nick = NULL;
+    char change = '+';
+    ts_val nickts = 0;
+    int sendmsg = 1;
+
+    if(!(chptr = find_channel(parv[1], NULL)))
+       return 0;
+
+    if(parc >= 4)
+    {
+       nick = parv[3];
+       if(parc > 4)
+           nickts = atol(parv[4]);
+    }
+
+    if(nick)
+    {
+       acptr = find_person(nick, NULL);
+       if(!acptr || (nickts && acptr->tsinfo != nickts))
+           return 0;
+    }
+
+    for(m = parv[2]; *m; m++)
+       switch(*m)
+       {
+       case '+':
+       case '-':
+            change = *m;
+            break;
+
+       case 'b':
+            if(nick && MyClient(acptr) && change == '-')
+            {
+               remove_matching_bans(chptr, acptr, &me);
+               sendmsg--;
+            }
+            break;
+
+#ifdef EXEMPT_LISTS
+    case 'e':
+            if (nick && MyClient(acptr) && change == '-')
+            {
+                remove_matching_exempts(chptr, acptr, &me);
+                sendmsg--;
+            }
+            break;
+#endif
+
+#ifdef INVITE_LISTS
+    case 'I':
+            if (nick && MyClient(acptr) && change == '-')
+            {
+                remove_matching_invites(chptr, acptr, &me);
+                sendmsg--;
+            }
+            break;
+#endif
+
+       default:
+            sendmsg++;
+            break;
+       }
+
+    if(!sendmsg) return 0;
+
+    if(nick)
+       sendto_serv_butone(cptr, ":%s SVSMODE %s %s %s %ld", parv[0], parv[1],
+                          parv[2], nick, acptr->tsinfo);
+    else
+       sendto_serv_butone(cptr, ":%s SVSMODE %s %s", parv[0], parv[1], 
+                          parv[2]);
+
+    return 0;
+}
+
+/* m_svsmode - df function integrated
+ *  - Raistlin
+ * -- Behaviour changed - Epi (11/30/99)
+ * parv[0] - sender
+ * parv[1] - nick
+ * parv[2] - TS (or mode, depending on svs version)
+ * parv[3] - mode (or services id if old svs version)
+ * parv[4] - optional arguement (services id)
+ */
+int m_svsmode(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int            flag, *s, what, oldumode;
+    char          *m, *modes, *optarg;
+    aClient       *acptr;
+    ts_val         ts = 0;
+
+    if (!IsULine(sptr) || (parc < 3))
+       return 0;
+
+    if (parv[1][0] == '#')
+       return channel_svsmode(cptr, sptr, parc, parv);
+
+    if ((parc >= 4) && ((parv[3][0] == '+') || (parv[3][0] == '-')))
+    {
+       ts = atol(parv[2]);
+       modes = parv[3];
+       optarg = (parc > 4) ? parv[4] : NULL;
+    }
+    else
+    {
+       modes = parv[2];
+       optarg = (parc > 3) ? parv[3] : NULL;
+    }
+
+    if (!(acptr = find_person(parv[1], NULL)))
+       return 0;
+
+    if (ts && (ts != acptr->tsinfo))
+       return 0;
+
+    what = MODE_ADD;
+    oldumode = acptr->umode;
+    for (m = modes; *m; m++)
+       switch(*m)
+       {
+       case '+':
+           what = MODE_ADD;
+           break;
+       case '-':
+           what = MODE_DEL;
+           break;
+       case ' ':
+       case '\n':
+       case '\r':
+       case '\t':
+           break;
+       case 'd':
+           if (optarg && IsDigit(*optarg))
+               acptr->user->servicestamp = strtoul(optarg, NULL, 0);
+           break;
+       case 'T':
+           if (optarg && IsDigit(*optarg))
+               acptr->user->servicetype = strtoul(optarg, NULL, 0);
+           break;
+       default:
+           for (s = user_modes; (flag = *s); s += 2)
+           {
+               if (*m == (char)(*(s+1)))
+               {
+            if (what == MODE_ADD)
+            {
+                /* no opering this way */
+                if (flag & (UMODE_o|UMODE_O))
+                    break;
+                acptr->umode |= flag;
+            }
+            else if (acptr->umode & flag)
+            {
+                acptr->umode &= ~flag;
+
+                /* deopering ok */
+                if (MyConnect(acptr) && (flag & (UMODE_o|UMODE_O))
+                    && !IsAnOper(acptr))
+                {
+                    acptr->oflag = 0;
+                    remove_from_list(&oper_list, acptr, NULL);
+                }
+            }
+            
+                   break;
+               }
+           }
+           break;
+       }
+
+    if (optarg)
+       sendto_serv_butone(cptr, ":%s SVSMODE %s %ld %s %s",
+                          parv[0], parv[1], acptr->tsinfo, modes, optarg);
+    else
+       sendto_serv_butone(cptr, ":%s SVSMODE %s %ld %s",
+                          parv[0], parv[1], acptr->tsinfo, modes);
+
+    if (MyClient(acptr) && (oldumode != acptr->umode))
+    {
+        char buf[BUFSIZE];
+        send_umode(acptr, acptr, oldumode, ALL_UMODES, buf);
+    }
+
+    return 0;
+}
+
+/* m_svshold
+ *   Adds a temporary local nick ban.
+ * parv[0] - sender
+ * parv[1] - nick
+ * parv[2] - duration (0 to remove existing ban)
+ * parv[3] - optional reason
+ */
+int m_svshold(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    struct simBan *ban, *oban;
+    char *reason, *mask;
+    int length;
+
+    if(!IsULine(sptr) || parc < 3)
+        return 0;
+
+    mask = parv[1];
+    length = strtol(parv[2], NULL, 0);
+    reason = (parc < 4) ? "Nickname is reserved, try again later" : parv[3];
+
+    /* marked local so netbursts don't propagate it */
+    ban = make_simpleban(SBAN_LOCAL|SBAN_NICK|SBAN_TEMPORARY, parv[1]);
+    if(!ban)
+    {
+       sendto_realops_lev(DEBUG_LEV, "make_simpleban(%s) failed on svshold", mask);
+       return 0;
+    }
+    ban->reason = NULL;
+    
+    if((oban = find_simban_exact(ban)) != NULL)
+    {
+       simban_free(ban);
+       ban = NULL;
+
+       if(length <= 0)
+       {
+           remove_simban(oban);
+       }
+       else
+       {
+           if(oban->reason)
+               MyFree(oban->reason);
+           oban->reason = (char *) MyMalloc(strlen(reason) + 1);
+           strcpy(oban->reason, reason);
+           oban->timeset = NOW;
+           oban->duration = length;
+       }
+    }
+    else if(length > 0)
+    {
+       ban->reason = (char *) MyMalloc(strlen(reason) + 1);
+       strcpy(ban->reason, reason);
+       ban->timeset = NOW;
+       ban->duration = length;
+       add_simban(ban);
+    }
+    else
+       simban_free(ban);
+
+    if(parc < 4)
+       sendto_serv_butone(cptr, ":%s SVSHOLD %s %s", sptr->name, parv[1], parv[2]);
+    else
+       sendto_serv_butone(cptr, ":%s SVSHOLD %s %s :%s", sptr->name, parv[1], parv[2], parv[3]);
+
+    return 0;
+}
+
+
+/* m_svsclone
+*   Sets a clone limit for an IP mask (1.2.3.4 or 1.2.3.*).
+* parv[0] - sender
+* parv[1] - mask
+* parv[2] - duration (0 to revert to default limit)
+*/
+int
+m_svsclone(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int d;
+
+    if (parc != 3)
+        return 0;
+
+    if (!(IsServer(sptr) || IsULine(sptr)))
+        return 0;
+
+    d = atoi(parv[2]);
+    clones_set(parv[1], CLIM_HARD_GLOBAL, d);
+    sendto_serv_butone(cptr, ":%s SVSCLONE %s %s", parv[0], parv[1], parv[2]);
+
+    return 0;
+}
+
diff --git a/src/m_stats.c b/src/m_stats.c
new file mode 100644 (file)
index 0000000..079e4d3
--- /dev/null
@@ -0,0 +1,1470 @@
+/* m_stats.c
+ * Copyright (c) 2004, The Bahamut Development Team and Aaron Wiebe
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   This program is free softwmare; 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: m_stats.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "channel.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include "h.h"
+#include "zlink.h"
+#include "userban.h"
+#include "blalloc.h"
+#include "throttle.h"
+#include "whowas.h"
+#include "res.h"
+#include "sbuf.h"
+#include "clones.h"
+
+#if defined(DEBUGMODE) && defined(HAVE_GETRUSAGE)
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+
+extern float curSendK, curRecvK;
+extern aWhowas WHOWAS[];
+extern aCache *cachetop;
+#ifdef DEBUGMODE
+extern void report_fds(aClient *);
+#endif
+
+/* internal function defines */
+
+static void show_opers(aClient *, char *);
+static void show_servers(aClient *, char *);
+static void count_memory(aClient *, char *);
+#ifdef DEBUGMODE
+static void send_usage(aClient *, char *);
+#endif
+static void count_whowas_memory(int *, u_long *);
+static void serv_info(aClient *, char *);
+static void tstats(aClient *, char *);
+static u_long cres_mem(aClient *);
+static u_long count_conf_memory(aClient *);
+
+/* support functions */
+/* show_opers
+ * replies to stats p requests
+ */
+
+static u_long 
+cres_mem(aClient *sptr)
+{
+    aCache *c = cachetop;
+    struct hostent *h;
+    int i;
+    u_long      nm = 0, im = 0, sm = 0, ts = 0;
+
+    for (; c; c = c->list_next)
+    {
+    sm += sizeof(*c);
+    h = &c->he;
+    for (i = 0; h->h_addr_list[i]; i++)
+    {
+        im += sizeof(char *);
+        im += sizeof(struct in_addr);
+    }
+    im += sizeof(char *);
+
+    for (i = 0; h->h_aliases[i]; i++)
+    {
+        nm += sizeof(char *);
+
+        nm += strlen(h->h_aliases[i]);
+    }
+    nm += i - 1;
+    nm += sizeof(char *);
+
+    if (h->h_name)
+        nm += strlen(h->h_name);
+    }
+    ts = ARES_CACSIZE * sizeof(CacheTable);
+    sendto_one(sptr, ":%s %d %s :RES table sz %d",
+           me.name, RPL_STATSDEBUG, sptr->name, ts);
+    sendto_one(sptr, ":%s %d %s :RES Structs sz %d IP storage sz %d "
+           "Name storage sz %d", me.name, RPL_STATSDEBUG, sptr->name, sm,
+           im, nm);
+    return ts + sm + im + nm;
+}
+
+static void
+count_whowas_memory(int *wwu, u_long *wwum)
+{
+    aWhowas *tmp;
+    int i;
+    int         u = 0;
+    u_long      um = 0;
+
+    /* count the number of used whowas structs in 'u' */
+    /* count up the memory used of whowas structs in um */
+
+    for (i = 0, tmp = &WHOWAS[0]; i < NICKNAMEHISTORYLENGTH; i++, tmp++)
+    if (tmp->hashv != -1)
+    {
+        u++;
+        um += sizeof(aWhowas);
+    }
+    *wwu = u;
+    *wwum = um;
+    return;
+}
+
+#define MAXUSERVS 24
+
+/*
+ * Send conf memory usage to a client, and return the total bytes in use.
+ */
+static u_long 
+count_conf_memory(aClient *cptr)
+{
+    int cnc = 0; u_long cnm = 0;    /* aConnects */
+    int alc = 0; u_long alm = 0;    /* aAllows */
+    int opc = 0; u_long opm = 0;    /* aOpers */
+    int ptc = 0; u_long ptm = 0;    /* aPorts */
+    int clc = 0; u_long clm = 0;    /* aClasses */
+    int usc = 0; u_long usm = 0;    /* U:lined */
+    int tc  = 0; u_long tm  = 0;    /* totals */
+
+    int          i;
+    aConnect    *cnptr;
+    aAllow      *alptr;
+    aOper       *opptr;
+    aPort       *ptptr;
+    aClass      *clptr;
+
+    for (cnptr = connects; cnptr; cnptr = cnptr->next)
+    {
+        cnc++;
+        cnm += sizeof(*cnptr);
+        if (!BadPtr(cnptr->host))
+            cnm += strlen(cnptr->host) + 1;
+        if (!BadPtr(cnptr->apasswd))
+            cnm += strlen(cnptr->apasswd) + 1;
+        if (!BadPtr(cnptr->cpasswd))
+            cnm += strlen(cnptr->cpasswd) + 1;
+        if (!BadPtr(cnptr->name))
+            cnm += strlen(cnptr->name) + 1;
+        if (!BadPtr(cnptr->source))
+            cnm += strlen(cnptr->source) + 1;
+        if (!BadPtr(cnptr->class_name))
+            cnm += strlen(cnptr->class_name) + 1;
+    }
+
+    for (alptr = allows; alptr; alptr = alptr->next)
+    {
+        alc++;
+        alm += sizeof(*alptr);
+        if (!BadPtr(alptr->ipmask))
+            alm += strlen(alptr->ipmask) + 1;
+        if (!BadPtr(alptr->passwd))
+            alm += strlen(alptr->passwd) + 1;
+        if (!BadPtr(alptr->hostmask))
+            alm += strlen(alptr->hostmask) + 1;
+        if (!BadPtr(alptr->class_name))
+            alm += strlen(alptr->class_name) + 1;
+    }
+
+    for (opptr = opers; opptr; opptr = opptr->next)
+    {
+        opc++;
+        opm += sizeof(*opptr);
+        if (!BadPtr(opptr->passwd))
+            opm += strlen(opptr->passwd) + 1;
+        if (!BadPtr(opptr->nick))
+            opm += strlen(opptr->nick) + 1;
+        if (!BadPtr(opptr->class_name))
+            opm += strlen(opptr->class_name) + 1;
+        for (i = 0; i < sizeof(opptr->hosts)/sizeof(opptr->hosts[0]); i++)
+            if (!BadPtr(opptr->hosts[i]))
+                opm += strlen(opptr->hosts[i]) + 1;
+    }
+
+    for (ptptr = ports; ptptr; ptptr = ptptr->next)
+    {
+        ptc++;
+        ptm += sizeof(*ptptr);
+        if (!BadPtr(ptptr->allow))
+            ptm += strlen(ptptr->allow) + 1;
+        if (!BadPtr(ptptr->address))
+            ptm += strlen(ptptr->address) + 1;
+    }
+
+    for (clptr = classes; clptr; clptr = clptr->next)
+    {
+        clc++;
+        clm += sizeof(*clptr);
+        if (!BadPtr(clptr->name))
+            clm += strlen(clptr->name) + 1;
+    }
+
+    for (i = 0; i < MAXUSERVS; i++)
+    {
+        if (!BadPtr(uservers[i]))
+        {
+            usc++;
+            usm += strlen(uservers[i]) + 1;
+        }
+    }
+
+    tc = cnc + alc + opc + ptc + clc + usc;
+    tm = cnm + alm + opm + ptm + clm + usm;
+
+    sendto_one(cptr, ":%s %d %s :Conf entries %d(%lu):",
+               me.name, RPL_STATSDEBUG, cptr->name, tc, tm);
+    sendto_one(cptr, ":%s %d %s :    aConnect %d(%lu)",
+               me.name, RPL_STATSDEBUG, cptr->name, cnc, cnm);
+    sendto_one(cptr, ":%s %d %s :    aAllow %d(%lu)",
+               me.name, RPL_STATSDEBUG, cptr->name, alc, alm);
+    sendto_one(cptr, ":%s %d %s :    aOper %d(%lu)",
+               me.name, RPL_STATSDEBUG, cptr->name, opc, opm);
+    sendto_one(cptr, ":%s %d %s :    aPort %d(%lu)",
+               me.name, RPL_STATSDEBUG, cptr->name, ptc, ptm);
+    sendto_one(cptr, ":%s %d %s :    aClass %d(%lu)",
+               me.name, RPL_STATSDEBUG, cptr->name, clc, clm);
+    sendto_one(cptr, ":%s %d %s :    U-servers %d(%lu)",
+               me.name, RPL_STATSDEBUG, cptr->name, usc, usm);
+
+    return tm;
+}
+
+
+
+#ifdef DEBUGMODE
+static void
+send_usage(aClient *cptr, char *nick)
+{
+
+#if defined( HAVE_GETRUSAGE )
+    struct rusage rus;
+    time_t      secs, rup;
+
+#ifdef  hz
+#define hzz hz
+#else
+#ifdef HZ
+#define hzz HZ
+#else
+    int         hzz = 1;
+
+#endif
+#endif
+
+    if (getrusage(RUSAGE_SELF, &rus) == -1) {
+        sendto_one(cptr, ":%s NOTICE %s :Getruseage error: %s.",
+                   me.name, nick, sys_errlist[errno]);
+        return;
+    }
+    secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
+    rup = timeofday - me.since;
+    if (secs == 0)
+        secs = 1;
+
+    sendto_one(cptr,
+               ":%s %d %s :CPU Secs %d:%d User %d:%d System %d:%d",
+               me.name, RPL_STATSDEBUG, nick, secs / 60, secs % 60,
+               rus.ru_utime.tv_sec / 60, rus.ru_utime.tv_sec % 60,
+               rus.ru_stime.tv_sec / 60, rus.ru_stime.tv_sec % 60);
+    sendto_one(cptr, ":%s %d %s :RSS %d ShMem %d Data %d Stack %d",
+               me.name, RPL_STATSDEBUG, nick, rus.ru_maxrss,
+               rus.ru_ixrss / (rup * hzz), rus.ru_idrss / (rup * hzz),
+               rus.ru_isrss / (rup * hzz));
+    sendto_one(cptr, ":%s %d %s :Swaps %d Reclaims %d Faults %d",
+               me.name, RPL_STATSDEBUG, nick, rus.ru_nswap,
+               rus.ru_minflt, rus.ru_majflt);
+    sendto_one(cptr, ":%s %d %s :Block in %d out %d",
+               me.name, RPL_STATSDEBUG, nick, rus.ru_inblock,
+               rus.ru_oublock);
+    sendto_one(cptr, ":%s %d %s :Msg Rcv %d Send %d",
+               me.name, RPL_STATSDEBUG, nick, rus.ru_msgrcv, rus.ru_msgsnd);
+    sendto_one(cptr, ":%s %d %s :Signals %d Context Vol. %d Invol %d",
+               me.name, RPL_STATSDEBUG, nick, rus.ru_nsignals,
+               rus.ru_nvcsw, rus.ru_nivcsw);
+#else
+#if defined( HAVE_TIMES )
+    struct tms  tmsbuf;
+    time_t      secs, mins;
+    int         hzz = 1, ticpermin;
+    int         umin, smin, usec, ssec;
+
+    ticpermin = hzz * 60;
+
+    umin = tmsbuf.tms_utime / ticpermin;
+    usec = (tmsbuf.tms_utime % ticpermin) / (float) hzz;
+    smin = tmsbuf.tms_stime / ticpermin;
+    ssec = (tmsbuf.tms_stime % ticpermin) / (float) hzz;
+    secs = usec + ssec;
+    mins = (secs / 60) + umin + smin;
+    secs %= hzz;
+    if (times(&tmsbuf) == -1) {
+        sendto_one(cptr, ":%s %d %s :times(2) error: %s.",
+                   me.name, RPL_STATSDEBUG, nick, strerror(errno));
+        return;
+    }
+    secs = tmsbuf.tms_utime + tmsbuf.tms_stime;
+
+    sendto_one(cptr,
+               ":%s %d %s :CPU Secs %d:%d User %d:%d System %d:%d",
+               me.name, RPL_STATSDEBUG, nick, mins, secs, umin, usec,
+               smin, ssec);
+#endif /* HAVE_TIMES */
+#endif /* HAVE_GETRUSAGE */
+    sendto_one(cptr, ":%s %d %s :Reads %d Writes %d",
+               me.name, RPL_STATSDEBUG, nick, readcalls, writecalls);
+/*    sendto_one(cptr, ":%s %d %s :DBUF alloc %d used %d",
+               me.name, RPL_STATSDEBUG, nick, DBufCount, DBufUsedCount);
+               */
+    sendto_one(cptr,
+               ":%s %d %s :Writes:  <0 %d 0 %d <16 %d <32 %d <64 %d",
+               me.name, RPL_STATSDEBUG, nick,
+               writeb[0], writeb[1], writeb[2], writeb[3], writeb[4]);
+    sendto_one(cptr,
+               ":%s %d %s :<128 %d <256 %d <512 %d <1024 %d >1024 %d",
+               me.name, RPL_STATSDEBUG, nick,
+               writeb[5], writeb[6], writeb[7], writeb[8], writeb[9]);
+    return;
+}
+#endif  /* DEBUGMODE */
+
+
+static void 
+count_memory(aClient *cptr, char *nick)
+{
+    extern aChannel *channel;
+
+    extern BlockHeap *free_local_aClients;
+    extern BlockHeap *free_Links;
+    extern BlockHeap *free_DLinks;
+    extern BlockHeap *free_remote_aClients;
+    extern BlockHeap *free_anUsers;
+    extern BlockHeap *free_channels;
+    extern BlockHeap *free_chanMembers;
+#ifdef FLUD
+    extern BlockHeap *free_fludbots;
+#endif
+    extern BlockHeap *free_cloneents;
+
+    extern aMotd      *motd;
+    extern aMotd      *shortmotd;
+    extern aMotd      *helpfile;
+
+    extern int num_msg_trees;
+
+    aClient *acptr;
+    Link   *link;
+    chanMember *cm;
+    aBan   *bp;
+    aChannel *chptr;
+    aMotd *amo;
+    CloneEnt *ce;
+
+    int         lc = 0;         /* local clients */
+    int         lcc = 0;        /* local client conf links */
+    int         rc = 0;         /* remote clients */
+    int         us = 0;         /* user structs */
+    int         chu = 0;        /* channel users */
+    int         chi = 0;        /* channel invites */
+    int         chb = 0;        /* channel bans */
+    int         wwu = 0;        /* whowas users */
+    int         ch = 0;
+    int         usi = 0;        /* users invited */
+    int         usc = 0;        /* users in channels */
+    int         usdm = 0;       /* dccallow local */
+    int         usdr = 0;       /* dccallow remote */
+    int         uss = 0;        /* silenced users */
+    int         aw = 0;         /* aways set */
+    int         number_servers_cached;  /* number of servers cached by
+                                         * scache
+                                         */
+    u_long      chbm = 0;       /* memory used by channel bans */
+    u_long      lcm = 0;        /* memory used by local clients */
+    u_long      rcm = 0;        /* memory used by remote clients */
+    u_long      awm = 0;        /* memory used by aways */
+    u_long      wwm = 0;        /* whowas array memory used */
+    u_long      rm = 0;         /* res memory used */
+    u_long      mem_servers_cached;     /* memory used by scache */
+
+    u_long      totco = 0;
+    u_long      totcl = 0;
+    u_long      totch = 0;
+    u_long      totww = 0;
+    u_long      totmisc = 0;
+    u_long      tothash = 0;
+    u_long      totuban = 0;
+    u_long      tot = 0;
+
+    int wlh=0, wle=0; /* watch headers/entries */
+    u_long wlhm=0; /* memory used by watch */
+
+    int lcalloc = 0;    /* local clients allocated */
+    int rcalloc = 0;    /* remote clients allocated */
+    int useralloc = 0;  /* allocated users */
+    int linkalloc = 0;  /* allocated links */
+    int dlinkalloc = 0; /* allocated dlinks */
+    int totallinks = 0; /* total links used */
+    int chanalloc = 0; /* total channels alloc'd */
+    int cmemballoc = 0;
+    int clonealloc = 0;
+    u_long lcallocsz = 0, rcallocsz = 0; /* size for stuff above */
+    u_long userallocsz = 0, linkallocsz = 0, dlinkallocsz = 0, chanallocsz = 0;
+    u_long cmemballocsz = 0, cloneallocsz = 0;
+
+    int fludalloc = 0;
+    u_long fludallocsz = 0;
+    int fludlink = 0;
+
+    int cloneent = 0;
+    u_long cloneentsz = 0;
+
+    int motdlen = 0;
+
+    int servn = 0;
+    
+    /* sbuf counts -- 0 = used, 1 = total, 2 = size */
+    int sbuf_user[3], sbuf_small[3], sbuf_large[3];
+    /* block counts -- 0 = total, 1 = size */
+    int sbuf_blocks[2], sbuf_userblocks[2];
+    
+
+    count_whowas_memory(&wwu, &wwm);    /* no more away memory to count */
+
+    count_watch_memory(&wlh, &wlhm);
+
+    for(acptr = client; acptr; acptr = acptr->next)
+    {
+        if(MyConnect(acptr))
+        {
+            lc++;
+            wle += acptr->watches;
+        }
+        else
+            rc++;
+
+
+#ifdef FLUD
+        for (link = acptr->fludees; link;
+             link = link->next)
+            fludlink++;
+#endif
+        if (acptr->serv)
+            servn++;
+
+        if (acptr->user)
+        {
+            us++;
+            for (link = acptr->user->invited; link;
+                 link = link->next)
+                usi++;
+            for (link = acptr->user->channel; link;
+                 link = link->next)
+                usc++;
+            for (link = acptr->user->dccallow; link;
+                 link = link->next)
+            {
+                if(link->flags == DCC_LINK_ME)
+                    usdm++;
+                else
+                    usdr++;
+            }
+            for (link = acptr->user->silence; link;
+                 link = link->next)
+                uss++;
+            if (acptr->user->away)
+            {
+                aw++;
+                awm += (strlen(acptr->user->away) + 1);
+            }
+        }
+    }
+
+    lcm = lc * CLIENT_LOCAL_SIZE;
+    rcm = rc * CLIENT_REMOTE_SIZE;
+
+    for (chptr = channel; chptr; chptr = chptr->nextch)
+    {
+        ch++;
+
+        for (cm = chptr->members; cm; cm = cm->next)
+            chu++;
+        for (link = chptr->invites; link; link = link->next)
+            chi++;
+        for (bp = chptr->banlist; bp; bp = bp->next)
+        {
+            chb++;
+            chbm += (strlen(bp->who) + strlen(bp->banstr) + 2 + sizeof(aBan));
+    }
+    }
+
+    for (amo = motd; amo; amo = amo->next)
+        motdlen++;
+    for (amo = shortmotd; amo; amo = amo->next)
+        motdlen++;
+    for (amo = helpfile; amo; amo = amo->next)
+        motdlen++;
+
+    for (ce = clones_list; ce; ce = ce->next)
+        cloneent++;
+    cloneentsz = cloneent * sizeof(*ce);
+
+    lcalloc = free_local_aClients->blocksAllocated *
+        free_local_aClients->elemsPerBlock;
+    lcallocsz = lcalloc * free_local_aClients->elemSize;
+
+    rcalloc = free_remote_aClients->blocksAllocated *
+        free_remote_aClients->elemsPerBlock;
+    rcallocsz = rcalloc * free_remote_aClients->elemSize;
+
+    useralloc = free_anUsers->blocksAllocated * free_anUsers->elemsPerBlock;
+    userallocsz = useralloc * free_anUsers->elemSize;
+
+    linkalloc = free_Links->blocksAllocated * free_Links->elemsPerBlock;
+    linkallocsz = linkalloc * free_Links->elemSize;
+
+    dlinkalloc = free_DLinks->blocksAllocated * free_DLinks->elemsPerBlock;
+    dlinkallocsz = dlinkalloc * free_DLinks->elemSize;
+
+    chanalloc = free_channels->blocksAllocated * free_channels->elemsPerBlock;
+    chanallocsz = chanalloc * free_channels->elemSize;
+
+    cmemballoc = free_chanMembers->blocksAllocated *
+        free_chanMembers->elemsPerBlock;
+    cmemballocsz = cmemballoc * free_chanMembers->elemSize;
+
+#ifdef FLUD
+    fludalloc = free_fludbots->blocksAllocated * free_fludbots->elemsPerBlock;
+    fludallocsz = fludalloc * free_fludbots->elemSize;
+#endif
+
+    clonealloc = free_cloneents->blocksAllocated
+               * free_cloneents->elemsPerBlock;
+    cloneallocsz = clonealloc * free_cloneents->elemSize;
+
+    totallinks = lcc + usi +  uss + usc + chi + wle + fludlink + usdm + usdr;
+
+    sendto_one(cptr, ":%s %d %s :Memory Use Summary",
+               me.name, RPL_STATSDEBUG, nick);
+    sendto_one(cptr, ":%s %d %s :Client usage %d(%d) ALLOC %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, lc + rc, lcm + rcm,
+               lcalloc + rcalloc, lcallocsz + rcallocsz);
+    sendto_one(cptr, ":%s %d %s :   Local %d(%d) ALLOC %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, lc, lcm, lcalloc, lcallocsz);
+    sendto_one(cptr, ":%s %d %s :   Remote %d(%d) ALLOC %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, rc, rcm, rcalloc, rcallocsz);
+    sendto_one(cptr, ":%s %d %s :Users %d(%d) ALLOC %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, us, us * sizeof(anUser),
+               useralloc, userallocsz);
+
+    totcl = lcallocsz + rcallocsz + userallocsz;
+
+    sendto_one(cptr, ":%s %d %s :Links %d(%d) ALLOC %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, totallinks,
+               totallinks * sizeof(Link), linkalloc, linkallocsz);
+    sendto_one(cptr, ":%s %d %s :   UserInvites %d(%d) ChanInvites %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, usi, usi * sizeof(Link), chi,
+               chi * sizeof(Link));
+    sendto_one(cptr, ":%s %d %s :   UserChannels %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, usc, usc * sizeof(Link));
+    sendto_one(cptr, ":%s %d %s :   DCCAllow Local %d(%d) Remote %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, usdm, usdm * sizeof(Link),
+               usdr, usdr * sizeof(Link));
+    sendto_one(cptr, ":%s %d %s :   WATCH entries %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, wle, wle*sizeof(Link));
+    sendto_one(cptr, ":%s %d %s :   Fludees %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, fludlink, fludlink*sizeof(Link));
+
+    sendto_one(cptr, ":%s %d %s :DLinks ALLOC %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, dlinkalloc, dlinkallocsz);
+    /* Print summary of DLINKs used in clientlist.c */
+    print_list_memory(cptr);
+
+    /* Count (and print) conf memory used in s_conf.c */
+    totco = count_conf_memory(cptr);
+
+    sendto_one(cptr, ":%s %d %s :WATCH headers %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, wlh, wlhm);
+    sendto_one(cptr, ":%s %d %s :Away Messages %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, aw, awm);
+    sendto_one(cptr, ":%s %d %s :MOTD structs %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, motdlen,
+               motdlen * sizeof(aMotd));
+    sendto_one(cptr, ":%s %d %s :Servers %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, servn, servn * sizeof(aServer));
+    sendto_one(cptr, ":%s %d %s :Message Trees %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, num_msg_trees,
+               num_msg_trees * sizeof(MESSAGE_TREE));
+
+    totmisc = wlhm + awm + (motdlen * sizeof(aMotd)) + totco +
+        (servn * sizeof(aServer)) +
+        (num_msg_trees * sizeof(MESSAGE_TREE));
+
+    sendto_one(cptr, ":%s %d %s :Fludbots ALLOC %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, fludalloc, fludallocsz);
+
+    sendto_one(cptr, ":%s %d %s :Clones %d(%d) ALLOC %d(%d)", me.name,
+               RPL_STATSDEBUG, nick, cloneent, cloneentsz, clonealloc,
+               cloneallocsz);
+
+    sendto_one(cptr, ":%s %d %s :Channels %d(%d) ALLOC %d(%d) Bans %d(%d) "
+               "Members %d(%d) ALLOC %d(%d)", me.name, RPL_STATSDEBUG, nick,
+               ch, ch * sizeof(aChannel), chanalloc, chanallocsz, chb, chbm,
+               chu, chu * sizeof(chanMember), cmemballoc, cmemballocsz);
+
+    totch = chanallocsz + cmemballocsz + chbm;
+
+    /* print userban summary, get userban total usage */
+    totuban = count_userbans(cptr);
+    totuban += count_simbans(cptr);
+
+    sendto_one(cptr, ":%s %d %s :Whowas users %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, wwu, wwu * sizeof(anUser));
+    sendto_one(cptr, ":%s %d %s :Whowas array %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, NICKNAMEHISTORYLENGTH, wwm);
+
+    totww = wwu * sizeof(anUser) + wwm;
+
+    sendto_one(cptr, ":%s %d %s :Hash: client %d(%d) chan %d(%d) whowas "
+               "%d(%d) watch %d(%d)", me.name, RPL_STATSDEBUG, nick,
+               U_MAX, sizeof(aHashEntry) * U_MAX,
+               CH_MAX, sizeof(aHashEntry) * CH_MAX,
+               WW_MAX, sizeof(aWhowas *) * WW_MAX,
+               WATCHHASHSIZE, sizeof(aWatch *) * WATCHHASHSIZE);
+
+/*    count_dbuf_memory(&db, &db2);
+    sendto_one(cptr, ":%s %d %s :Dbuf blocks %d(%d) MAX %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, DBufUsedCount, db2,
+               DBufCount, db);
+*/
+    sbuf_count(&sbuf_user[0], &sbuf_user[1], &sbuf_user[2],
+               &sbuf_small[0], &sbuf_small[1], &sbuf_small[2],
+               &sbuf_large[0], &sbuf_large[1], &sbuf_large[2],
+               &sbuf_blocks[0], &sbuf_blocks[1], &sbuf_userblocks[0], &sbuf_userblocks[1]);
+    sendto_one(cptr, ":%s %d %s :SBUF ALLOC(%d)", me.name, RPL_STATSDEBUG, nick,
+               sbuf_user[1]*sbuf_user[2] + sbuf_small[1]*sbuf_small[2] + sbuf_large[1]*sbuf_large[2] +
+               sbuf_blocks[0]*sbuf_blocks[1] + sbuf_userblocks[0]*sbuf_userblocks[1]);
+    sendto_one(cptr, ":%s %d %s :   BLOCKS %d(%d) USERBLOCKS %d(%d)", me.name, RPL_STATSDEBUG, nick,
+               sbuf_blocks[0], sbuf_blocks[0]*sbuf_blocks[1], sbuf_userblocks[0], sbuf_userblocks[0]*sbuf_userblocks[1]);
+    sendto_one(cptr, ":%s %d %s :   USERS %d MAX(%d) ALLOC(%d)", me.name, RPL_STATSDEBUG, nick,
+               sbuf_user[0], sbuf_user[1], sbuf_user[1]*sbuf_user[2]);
+    sendto_one(cptr, ":%s %d %s :   SMALL %d MAX(%d) ALLOC(%d)", me.name, RPL_STATSDEBUG, nick,
+               sbuf_small[0], sbuf_small[1], sbuf_small[1]*sbuf_small[2]);
+    sendto_one(cptr, ":%s %d %s :   LARGE %d MAX(%d) ALLOC(%d)", me.name, RPL_STATSDEBUG, nick,
+               sbuf_large[0], sbuf_large[1], sbuf_large[1]*sbuf_large[2]);
+    
+
+
+    rm = cres_mem(cptr);
+
+    count_scache(&number_servers_cached, &mem_servers_cached);
+
+    sendto_one(cptr, ":%s %d %s :scache %d(%d)",
+               me.name, RPL_STATSDEBUG, nick,
+               number_servers_cached,
+               mem_servers_cached);
+
+    tothash = (sizeof(aHashEntry)*U_MAX)+(sizeof(aHashEntry)*CH_MAX) +
+        (sizeof(aWatch *)*WATCHHASHSIZE) + (sizeof(aWhowas *)*WW_MAX);
+
+    tot = totww + totch + totcl + totmisc + /*db +*/ rm + tothash + linkallocsz +
+          dlinkallocsz + fludallocsz + totuban;
+
+    sendto_one(cptr, ":%s %d %s :whowas %d chan %d client/user %d misc %d "
+               /*dbuf %d*/ "hash %d res %d link %d flud %d simuserban %d",
+               me.name, RPL_STATSDEBUG, nick, totww, totch, totcl, totmisc,
+               /*db,*/ tothash, rm, linkallocsz, fludallocsz, totuban);
+
+    sendto_one(cptr, ":%s %d %s :TOTAL: %d sbrk(0)-etext: %u",
+               me.name, RPL_STATSDEBUG, nick, tot,
+               (u_int) sbrk((size_t) 0) - (u_int) sbrk0);
+    return;
+}
+
+
+static void 
+show_opers(aClient *cptr, char *name) 
+{
+    aClient *cptr2;
+    DLink *lp;
+    int j = 0;
+
+    for (lp = oper_list; lp; lp = lp->next)
+    {
+    cptr2 = lp->value.cptr;
+
+    if (!IsAnOper(cptr))
+    {
+        if (cptr2->umode & UMODE_h)
+        {
+        sendto_one(cptr, ":%s %d %s :%s (%s@%s) Idle: %d",
+               me.name, RPL_STATSDEBUG, name, cptr2->name,
+               cptr2->user->username, cptr2->user->host,
+               timeofday - cptr2->user->last);
+        j++;
+        }
+    }
+    else
+    {
+        sendto_one(cptr, ":%s %d %s :%s (%s@%s) Idle: %d",
+               me.name, RPL_STATSDEBUG, name, cptr2->name,
+               cptr2->user->username, cptr2->user->host,
+               timeofday - cptr2->user->last);
+        j++;
+    }
+    }
+    sendto_one(cptr, ":%s %d %s :%d OPER%s", me.name, RPL_STATSDEBUG,
+           name, j, (j == 1) ? "" : "s");
+}
+
+/* show_servers
+ * replies to stats v requests
+ */
+static void 
+show_servers(aClient *cptr, char *name)
+{
+    aClient *cptr2;
+    DLink *lp;
+    int j = 0;
+
+    for (lp = server_list; lp; lp = lp->next)
+    {
+    cptr2 = lp->value.cptr;
+
+#ifdef HIDEULINEDSERVS
+    if(IsULine(cptr2) && !IsAnOper(cptr))
+        continue;
+#endif
+    j++;
+    sendto_one(cptr, ":%s %d %s :%s (%s!%s@%s) Idle: %d",
+           me.name, RPL_STATSDEBUG, name, cptr2->name,
+           (cptr2->serv->bynick[0] ? cptr2->serv->bynick : "Remote."),
+           (cptr2->serv->byuser[0] ? cptr2->serv->byuser : "*"),
+           (cptr2->serv->byhost[0] ? cptr2->serv->byhost : "*"),
+           timeofday - cptr2->lasttime);
+    }
+    sendto_one(cptr, ":%s %d %s :%d Server%s", me.name, RPL_STATSDEBUG,
+           name, j, (j == 1) ? "" : "s");
+}
+
+/* serv_info
+ * replies to stats ? requests
+ */
+
+#define _1MEG   (1024.0)
+#define _1GIG   (1024.0*1024.0)
+#define _1TER   (1024.0*1024.0*1024.0)
+#define _GMKs(x)    ((x > _1TER) ? "Terabytes" : ((x > _1GIG) ? \
+                        "Gigabytes" : \
+            ((x > _1MEG) ? "Megabytes" : "Kilobytes")))
+#define _GMKv(x)    ( (x > _1TER) ? (float)(x/_1TER) : ((x > _1GIG) ? \
+            (float)(x/_1GIG) : ((x > _1MEG) ? (float)(x/_1MEG) :\
+                        (float)x)))
+
+static void 
+serv_info(aClient *cptr, char *name)
+{
+    static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :%u %u %s";
+    long        sendK, receiveK, uptime;
+    aClient    *acptr;
+    DLink      *lp;
+    int         i = 0;
+
+    sendK = receiveK = 0;
+
+    for (lp = server_list; lp; lp = lp->next)
+    {
+        acptr = lp->value.cptr;
+
+#ifdef HIDEULINEDSERVS
+        if (IsULine(acptr) && !IsAnOper(cptr))
+            continue;
+#endif
+        sendK += acptr->sendK;
+        receiveK += acptr->receiveK;
+        sendto_one(cptr, Lformat, me.name, RPL_STATSLINKINFO,
+                    name, ( IsAnOper(cptr) ? get_client_name(acptr, HIDEME)
+                                           : acptr->name ),
+                    (int) SBufLength(&acptr->sendQ),
+                    (int) acptr->sendM, (int) acptr->sendK,
+                    (int) acptr->receiveM, (int) acptr->receiveK,
+                    timeofday - acptr->firsttime, timeofday - acptr->since,
+                    IsServer(acptr) ? (DoesTS(acptr) ? "TS" : "NoTS") : "-");
+
+
+        if(RC4EncLink(acptr))
+            sendto_one(cptr, ":%s %d %s : - RC4 encrypted", me.name, 
+                        RPL_STATSDEBUG, name);
+
+        if(ZipOut(acptr))
+        {
+            unsigned long ib, ob;
+            double rat;
+
+            zip_out_get_stats(acptr->serv->zip_out, &ib, &ob, &rat);
+            if(ib)
+            {
+                sendto_one(cptr, ":%s %d %s : - [O] Zip inbytes %d, "
+                            "outbytes %d (%3.2f%%)", me.name, RPL_STATSDEBUG,
+                             name, ib, ob, rat);
+            }
+        }
+
+        if(ZipIn(acptr))
+        {
+            unsigned long ib, ob;
+            double rat;
+
+            zip_in_get_stats(acptr->serv->zip_in, &ib, &ob, &rat);
+            if(ob)
+            {
+                sendto_one(cptr, ":%s %d %s : - [I] Zip inbytes %d, "
+                            "outbytes %d (%3.2f%%)", me.name, RPL_STATSDEBUG,
+                             name, ib, ob, rat);
+            }
+        }
+        i++;
+    }
+    sendto_one(cptr, ":%s %d %s :%u total server%s",
+           me.name, RPL_STATSDEBUG, name, i, (i == 1) ? "" : "s");
+    sendto_one(cptr, ":%s %d %s :Sent total : %7.2f %s",
+           me.name, RPL_STATSDEBUG, name, _GMKv(sendK), _GMKs(sendK));
+    sendto_one(cptr, ":%s %d %s :Recv total : %7.2f %s",
+           me.name, RPL_STATSDEBUG, name, _GMKv(receiveK),
+           _GMKs(receiveK));
+
+    uptime = (timeofday - me.since);
+    sendto_one(cptr, ":%s %d %s :Server send: %7.2f %s (%4.1f K/s total,"
+                     " %4.1f K/s current)", me.name, RPL_STATSDEBUG, name,
+                     _GMKv(me.sendK), _GMKs(me.sendK), 
+                    (float) ((float) me.sendK / (float) uptime), curSendK);
+    sendto_one(cptr, ":%s %d %s :Server recv: %7.2f %s (%4.1f K/s total,"
+                     " %4.1f K/s current)", me.name, RPL_STATSDEBUG, name, 
+                    _GMKv(me.receiveK), _GMKs(me.receiveK),
+                    (float) ((float) me.receiveK / (float) uptime), curRecvK);
+}
+
+/* tstats
+ * responced to stats t requests (oddly enough)
+ */
+
+static void 
+tstats(aClient *cptr, char *name)
+{
+    aClient *acptr;
+    int     i;
+    struct stats *sp;
+    struct stats tmp;
+
+    sp = &tmp;
+    memcpy((char *) sp, (char *) ircstp, sizeof(*sp));
+    for (i = 0; i < highest_fd; i++)
+    {
+        if (!(acptr = local[i]))
+            continue;
+        if (IsServer(acptr))
+        {
+            sp->is_sbs += acptr->sendB;
+            sp->is_sbr += acptr->receiveB;
+            sp->is_sks += acptr->sendK;
+            sp->is_skr += acptr->receiveK;
+            sp->is_sti += timeofday - acptr->firsttime;
+            sp->is_sv++;
+            if (sp->is_sbs > 1023)
+            {
+                sp->is_sks += (sp->is_sbs >> 10);
+                sp->is_sbs &= 0x3ff;
+            }
+            if (sp->is_sbr > 1023)
+            {
+                sp->is_skr += (sp->is_sbr >> 10);
+                sp->is_sbr &= 0x3ff;
+            }
+
+        }
+        else if (IsClient(acptr))
+        {
+            sp->is_cbs += acptr->sendB;
+            sp->is_cbr += acptr->receiveB;
+            sp->is_cks += acptr->sendK;
+            sp->is_ckr += acptr->receiveK;
+            sp->is_cti += timeofday - acptr->firsttime;
+            sp->is_cl++;
+            if (sp->is_cbs > 1023)
+            {
+                sp->is_cks += (sp->is_cbs >> 10);
+                sp->is_cbs &= 0x3ff;
+            }
+            if (sp->is_cbr > 1023)
+            {
+                sp->is_ckr += (sp->is_cbr >> 10);
+                sp->is_cbr &= 0x3ff;
+            }
+
+        }
+        else if (IsUnknown(acptr))
+            sp->is_ni++;
+    }
+
+    sendto_one(cptr, ":%s %d %s :accepts %u refused %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_ac, sp->is_ref);
+    sendto_one(cptr, ":%s %d %s :unknown commands %u prefixes %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_unco, sp->is_unpf);
+    sendto_one(cptr, ":%s %d %s :nick collisions %u unknown closes %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_kill, sp->is_ni);
+    sendto_one(cptr, ":%s %d %s :wrong direction %u empty %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_wrdi, sp->is_empt);
+    sendto_one(cptr, ":%s %d %s :numerics seen %u mode fakes %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_num, sp->is_fake);
+    sendto_one(cptr, ":%s %d %s :auth successes %u fails %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_asuc, sp->is_abad);
+    sendto_one(cptr, ":%s %d %s :local connections %u udp packets %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_loc, sp->is_udp);
+    sendto_one(cptr, ":%s %d %s :drones refused %u throttled rejections %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_drone, sp->is_throt);
+    sendto_one(cptr, ":%s %d %s :banned users refused before ident/dns"
+                     " %u after ident/dns %u", me.name, RPL_STATSDEBUG, 
+                     name, sp->is_ref_1, sp->is_ref_2);
+    sendto_one(cptr, ":%s %d %s :Client Server", 
+                     me.name, RPL_STATSDEBUG, name);
+    sendto_one(cptr, ":%s %d %s :connected %u %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_cl, sp->is_sv);
+    sendto_one(cptr, ":%s %d %s :bytes sent %u.%uK %u.%uK",
+               me.name, RPL_STATSDEBUG, name,
+               sp->is_cks, sp->is_cbs, sp->is_sks, sp->is_sbs);
+    sendto_one(cptr, ":%s %d %s :bytes recv %u.%uK %u.%uK",
+               me.name, RPL_STATSDEBUG, name,
+               sp->is_ckr, sp->is_cbr, sp->is_skr, sp->is_sbr);
+    sendto_one(cptr, ":%s %d %s :time connected %u %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_cti, sp->is_sti);
+#ifdef FLUD
+    sendto_one(cptr, ":%s %d %s :CTCP Floods Blocked %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_flud);
+#endif /* FLUD */
+}
+
+
+/*  m_stats and friends
+ *  Carved off from s_serv.c in Feb04 by epiphani
+ *  This mess of routines seemed to go better by themselves, seeing
+ *  as how s_serv.c is getting massive.
+ *
+ * m_stats
+ *      parv[0] = sender prefix
+ *      parv[1] = statistics selector (defaults to Message frequency)
+ *      parv[2] = server name (current server defaulted, if omitted)
+ */
+
+int m_stats(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :%u %u %s";
+    static char Sformat[] = ":%s %d %s Name SendQ SendM SendBytes RcveM "
+                            "RcveBytes :Open_since Idle TS";
+
+    struct Message  *mptr;
+    aClient         *acptr;
+    char             stat = parc > 1 ? parv[1][0] : '\0';
+    int              i, doall = 0, wilds = 0;
+    char            *name;
+    time_t           sincetime;
+    static time_t   last_used = 0L;
+
+#ifdef NO_USER_STATS
+    if (!IsAnOper(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+#else
+#ifdef NO_LOCAL_USER_STATS
+    if (!IsAnOper(sptr) && !MyConnect(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+#endif
+#endif
+
+    if (hunt_server(cptr, sptr, ":%s STATS %s :%s", 2, parc, parv) != 
+                    HUNTED_ISME)
+        return 0;
+
+    if (IsSquelch(sptr))
+        return 0;
+
+    if (!IsAnOper(sptr) && !IsULine(sptr))
+    {
+        /* allow remote stats p l ? u */
+        if (!((stat == 'p') || (stat == 'P') || (stat=='?') || (stat=='u') ||
+              (stat=='l') || (stat=='L')) && !MyConnect(sptr))
+            return 0;
+
+        /* if they're my user, penalize them. */
+        if (MyConnect(sptr))
+            sptr->since += 5;
+
+        if ((last_used + MOTD_WAIT) > NOW)
+            return 0;
+        else
+            last_used = NOW;
+    }
+
+    if (parc > 2)
+    {
+        name = parv[2];
+        if (!mycmp(name, me.name))
+            doall = 2;
+        else if (match(name, me.name) == 0)
+            doall = 1;
+        if (strchr(name, '*') || strchr(name, '?'))
+            wilds = 1;
+    }
+    else
+        name = me.name;
+
+    if (stat != (char) 0 && !IsULine(sptr) && !IsServer(sptr))
+        sendto_realops_lev(SPY_LEV, "STATS %c requested by %s (%s@%s) [%s]",
+               stat, sptr->name, sptr->user->username,
+               sptr->user->host, sptr->user->server);
+    switch (stat)
+    {
+        case 'L':
+        case 'l':
+        /* changed behavior totally.  This is what we do now:
+         * #1: if the user is not opered, never return ips for anything
+         * #2: we DON'T deny /stats l for opers.  Ever heard of /sping?
+         *     it's easy to see if you're lagging a server, why restrict
+         *     something used 99% of the time for good when you're not
+         *     doing any harm?
+         * #3: NEVER return all users on a server, UGH, just like
+         *     /trace, this was fiercely obnoxious.  If you don't
+         *     add an argument, you get all SERVER links.
+         */
+        sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
+        if ((parc > 2) && !(doall || wilds))
+        {         /* Single client lookup */
+            if (!(acptr = find_person(name, NULL)))
+            break;
+            /*
+             * sincetime might be greater than timeofday,
+             * store a new value here to avoid sending
+             * negative since-times. -Rak
+             */
+            sincetime = (acptr->since > timeofday) ? 0 : 
+                                timeofday - acptr->since;
+            sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
+                        ( (IsAnOper(sptr) || !IsAnOper(acptr))
+                          ? get_client_name(acptr, TRUE)
+                          : get_client_name(acptr, HIDEME) ),
+                        (int) SBufLength(&acptr->sendQ),
+                        (int) acptr->sendM, (int) acptr->sendK,
+                        (int) acptr->receiveM, (int) acptr->receiveK,
+                        timeofday - acptr->firsttime, sincetime,
+                        IsServer(acptr) ? (DoesTS(acptr) ?
+                        "TS" : "NoTS") : "-");
+        }
+        else
+        {
+            for (i = 0; i <= highest_fd; i++)
+            {
+                if (!(acptr = local[i]))
+                    continue;
+                if(!IsServer(acptr))
+                    continue; /* nothing but servers */
+#ifdef HIDEULINEDSERVS
+                if(IsULine(acptr) && !IsAnOper(sptr))
+                    continue;
+#endif
+                sincetime = (acptr->since > timeofday) ? 0 : 
+                             timeofday - acptr->since;
+                sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
+                        ( IsAnOper(sptr) ? get_client_name(acptr, HIDEME)
+                                         : acptr->name ),
+                        (int) SBufLength(&acptr->sendQ),
+                        (int) acptr->sendM, (int) acptr->sendK,
+                        (int) acptr->receiveM, (int) acptr->receiveK,
+                        timeofday - acptr->firsttime, sincetime,
+                        IsServer(acptr) ? (DoesTS(acptr) ?
+                        "TS" : "NoTS") : "-");
+            }
+        }
+        break;
+        case 'C':
+        case 'c':
+        /* this should be fixed and combined into a more reasonable
+         * single responce.  Will work on this later -epi
+         */
+#ifdef HIDEULINEDSERVS
+        if (!IsAnOper(sptr))
+            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+        else
+#endif
+        {
+            aConnect *tmp;
+            if(!connects)
+                break;
+            for(tmp = connects; tmp; tmp = tmp->next)
+            {
+                if (tmp->legal == -1)
+                    continue;
+
+                if(IsULine(sptr) || (MyClient(sptr) && IsAdmin(sptr)))
+                {
+                    sendto_one(sptr, rpl_str(RPL_STATSCLINE), me.name,
+                           sptr->name, "C", tmp->host, tmp->name, tmp->port,
+                           tmp->class->name);
+                    sendto_one(sptr, rpl_str(RPL_STATSNLINE), me.name,
+                           sptr->name, "N", tmp->host, tmp->name, tmp->flags,
+                           tmp->class->name);
+                }
+                else
+                {
+                    sendto_one(sptr, rpl_str(RPL_STATSCLINE), me.name,
+                               sptr->name, "C", "*", tmp->name, tmp->port,
+                               tmp->class->name);
+                    sendto_one(sptr, rpl_str(RPL_STATSNLINE), me.name,
+                               sptr->name, "N", "*", tmp->name, tmp->flags,
+                               tmp->class->name);
+                }
+            }
+        }
+        break;
+
+        case 'D':
+            if (!IsAnOper(sptr))
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            else
+            {
+                CloneEnt *ce;
+
+                for (ce = clones_list; ce; ce = ce->next)
+                    if (ce->limit || ce->sllimit || ce->sglimit)
+                        sendto_one(sptr, rpl_str(RPL_STATSCLONE), me.name,
+                                   parv[0], ce->ent, ce->sllimit, ce->sglimit,
+                                   ce->limit);
+            }
+            break;
+
+        case 'd':
+            if (!IsAnOper(sptr))
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            else
+            {
+                CloneEnt *ce;
+                int entries = 0;
+#ifdef THROTTLE_ENABLE
+                int sllimits = 0;
+                int sglimits = 0;
+                int hlimits = 0;
+                int active = 0;
+                int sites = 0;
+                unsigned long rtot;
+#endif
+
+                for (ce = clones_list; ce; ce = ce->next)
+                {
+                    entries++;
+#ifdef THROTTLE_ENABLE
+                    if (ce->sllimit)
+                        sllimits++;
+                    if (ce->sglimit)
+                        sglimits++;
+                    if (ce->limit)
+                        hlimits++;
+                    if (ce->gcount)
+                    {
+                        active++;
+                        /* blah, but not important enough for its own flag */
+                        if (!ce->clients)
+                            sites++;
+                    }
+#endif
+                }
+
+#ifdef THROTTLE_ENABLE
+                rtot = clones_stat.rlh + clones_stat.rls
+                     + clones_stat.rgh + clones_stat.rgs;
+
+                sendto_one(sptr, ":%s %d %s :Default local host limit: %d"
+                           "  site: %d", me.name, RPL_STATSDEBUG, parv[0],
+                           local_ip_limit, local_ip24_limit);
+                sendto_one(sptr, ":%s %d %s :Default global host limit: %d"
+                           "  site: %d", me.name, RPL_STATSDEBUG, parv[0],
+                           global_ip_limit, global_ip24_limit);
+#endif
+                sendto_one(sptr, ":%s %d %s :Clone entries: %d", me.name,
+                           RPL_STATSDEBUG, parv[0], entries);
+#ifdef THROTTLE_ENABLE
+                sendto_one(sptr, ":%s %d %s :    Active hosts: %d  sites: %d",
+                           me.name, RPL_STATSDEBUG, parv[0], active-sites,
+                           sites);
+                sendto_one(sptr, ":%s %d %s :    Soft local limits: %d"
+                           "  global: %d", me.name, RPL_STATSDEBUG, parv[0],
+                           sllimits, sglimits);
+                sendto_one(sptr, ":%s %d %s :    Hard global limits: %d",
+                           me.name, RPL_STATSDEBUG, parv[0], hlimits);
+                sendto_one(sptr, ":%s %d %s :Rejected connections: %lu",
+                           me.name, RPL_STATSDEBUG, parv[0], rtot);
+                sendto_one(sptr, ":%s %d %s :    Local hosts: %lu  sites: %lu",
+                           me.name, RPL_STATSDEBUG, parv[0],
+                           clones_stat.rlh, clones_stat.rls);
+                sendto_one(sptr, ":%s %d %s :    Global hosts: %lu  sites: %lu",
+                           me.name, RPL_STATSDEBUG, parv[0],
+                           clones_stat.rgh, clones_stat.rgs);
+#endif
+            }
+            break;
+
+        case 'G':
+            if(IsAnOper(sptr))
+                report_simbans_match_flags(sptr, SBAN_GCOS|SBAN_LOCAL, 0);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+        case 'g':
+            if(IsAnOper(sptr))
+                report_simbans_match_flags(sptr, SBAN_GCOS|SBAN_NETWORK, 0);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+
+        case 'I':
+        case 'i':
+        {
+            aAllow *tmp;
+            if(!allows)
+                break;
+            for(tmp = allows; tmp; tmp = tmp->next)
+            {
+                if (tmp->passwd && !(IsAnOper(sptr) || IsULine(sptr)))
+                    continue;
+                sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
+                           sptr->name, (tmp->legal == -1 ? "Ix" : "I"),
+                           tmp->ipmask, tmp->flags, tmp->hostmask, tmp->port,
+                           tmp->class->name);
+            }
+            break;
+        }
+        case 'k':
+            if(IsAnOper(sptr))
+                report_userbans_match_flags(sptr, UBAN_TEMPORARY|UBAN_LOCAL, 0);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+
+        case 'K':
+            if (IsAnOper(sptr))
+                report_userbans_match_flags(sptr, UBAN_LOCAL, UBAN_TEMPORARY);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+
+        case 'A':
+        case 'a':
+            if(IsAnOper(sptr))
+                report_userbans_match_flags(sptr, UBAN_NETWORK, 0);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+
+        case 'M':
+        case 'm':
+        /*
+         * original behaviour was not to report the command, if
+         * the command hadn't been used. I'm going to always
+         * report the command instead -Dianora
+         * Why would users want to see this?  Made Oper only.
+         */
+            if(IsAnOper(sptr))
+                for (mptr = msgtab; mptr->cmd; mptr++)
+                    sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS), me.name, 
+                            parv[0], mptr->cmd, mptr->count, mptr->bytes);
+            break;
+
+        case 'N':
+        case 'n':
+            sendto_one(sptr, rpl_str(RPL_STATSCOUNT), me.name, parv[0],
+                        "User Connects Today: ", Count.today);
+            sendto_one(sptr, rpl_str(RPL_STATSCOUNT), me.name, parv[0],
+                        "User Connects past week: ", Count.weekly);
+            sendto_one(sptr, rpl_str(RPL_STATSCOUNT), me.name, parv[0],
+                        "User Connects past month: ", Count.monthly);
+            sendto_one(sptr, rpl_str(RPL_STATSCOUNT), me.name, parv[0],
+                        "User Connects past year: ", Count.yearly);
+            break;
+        case 'o':
+        case 'O':
+        {
+            aOper *tmp;
+            int i = 0;
+            if(!opers)
+                break;
+            if (IsAnOper(sptr) || IsULine(sptr))
+            {
+                for(tmp = opers; tmp; tmp = tmp->next)
+                    for(i = 0; tmp->hosts[i]; i++)
+                        sendto_one(sptr, rpl_str(RPL_STATSOLINE), me.name,
+                                sptr->name, (tmp->legal == -1 ? "Ox" : "O"),
+                                tmp->hosts[i], tmp->nick, tmp->flags,
+                                tmp->class->name);
+            }
+            else
+            {
+                for(tmp = opers; tmp; tmp = tmp->next)
+                {
+                    if (tmp->legal == -1)
+                        continue;
+                    sendto_one(sptr, rpl_str(RPL_STATSOLINE), me.name,
+                            sptr->name, "O", "*", tmp->nick, tmp->flags,
+                            tmp->class->name);
+                }
+            }
+            break;
+        }
+
+        case 'p':
+        case 'P':
+            show_opers(sptr, parv[0]);
+            break;
+
+        case 'Q':
+            if(IsAnOper(sptr))
+            {
+                report_simbans_match_flags(sptr, SBAN_NICK|SBAN_LOCAL, 0);
+                report_simbans_match_flags(sptr, SBAN_CHAN|SBAN_LOCAL, 0);
+            }
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+        case 'q':
+            if(IsAnOper(sptr))
+            {
+                report_simbans_match_flags(sptr, SBAN_NICK|SBAN_NETWORK, 0);
+                report_simbans_match_flags(sptr, SBAN_CHAN|SBAN_NETWORK, 0);
+            }
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+
+        case 'R':
+        case 'r':
+#ifdef DEBUGMODE
+            send_usage(sptr, parv[0]);
+#endif
+            break;
+
+        case 'S':
+        case 's':
+            if (IsAnOper(sptr))
+                list_scache(cptr, sptr, parc, parv);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+
+        case 'T':
+            if (IsAnOper(sptr)) 
+                throttle_stats(sptr, parv[0]);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+
+        case 't':
+            if (IsAnOper(sptr))
+                tstats(sptr, parv[0]);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+
+        case 'U':
+#ifdef HIDEULINEDSERVS
+            if (!IsOper(sptr))
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            else
+#endif
+            {
+                int i;
+                for(i = 0; uservers[i]; i++)
+                    sendto_one(sptr, rpl_str(RPL_STATSULINE), me.name,
+                    sptr->name, "U", "*", uservers[i], 0, 0);
+            }
+            break;
+
+        case 'u':
+        {
+            time_t now;
+
+            now = timeofday - me.since;
+            sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
+                now / 86400, (now / 3600) % 24, (now / 60) % 60, now % 60);
+            break;
+        }
+
+        case 'v':
+        case 'V':
+            show_servers(sptr, parv[0]);
+            break;
+
+#ifdef DEBUGMODE
+        case 'w':
+        case 'W':
+            if(IsAnOper(sptr))
+                report_fds(sptr);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name,  parv[0]);
+            break;
+#endif
+
+        case 'Y':
+        case 'y':
+        {
+            aClass *tmp;
+            if(!classes)
+                break;
+            for(tmp = classes; tmp; tmp = tmp->next)
+                sendto_one(sptr, rpl_str(RPL_STATSYLINE), me.name,
+                           sptr->name, 'Y', tmp->name, tmp->pingfreq,
+                           tmp->connfreq, tmp->ip24clones, tmp->maxlinks,
+                           tmp->maxsendq);
+            break;
+        }
+
+        case 'Z':
+        case 'z':
+            if (IsAnOper(sptr))
+                count_memory(sptr, parv[0]);
+            else
+                sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            break;
+
+        case '?':
+            serv_info(sptr, parv[0]);
+            break;
+
+        default:
+            stat = '*';
+            break;
+    }
+    sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
+    return 0;
+}
+
diff --git a/src/m_who.c b/src/m_who.c
new file mode 100644 (file)
index 0000000..a556464
--- /dev/null
@@ -0,0 +1,814 @@
+/* m_who.c - Because s_user.c was just crazy.
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   This program is free softwmare; 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: m_who.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "inet.h"
+#include "msg.h"
+#include "channel.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include "h.h"
+
+/* Internally defined stuffs */
+SOpts wsopts;
+int build_searchopts(aClient *, int, char **);
+int chk_who(aClient *, int);
+
+/* Externally defined stuffs */
+extern int user_modes[];
+
+extern Link *find_channel_link(Link *, aChannel *);
+extern unsigned int cidr_to_netmask(unsigned int);
+
+int build_searchopts(aClient *sptr, int parc, char *parv[])
+{
+  static char *who_oper_help[] =
+  {
+      "/WHO [+|-][acghilmnstuCM] [args]",
+      "Flags are specified like channel modes,",
+      "The flags cghimnsu all have arguments",
+      "Flags are set to a positive check by +, a negative check by -",
+      "The flags work as follows:",
+      "Flag a: user is away",
+      "Flag c <channel>: user is on <channel>,",
+      "                  no wildcards accepted",
+      "Flag g <gcos/realname>: user has string <gcos> in their GCOS,",
+      "                        wildcards accepted, oper only",
+      "Flag h <host>: user has string <host> in their hostname,",
+      "               wildcards accepted",
+      "Flag i <ip>: user is from <ip>, wildcards and cidr accepted,",
+      "Flag m <usermodes>: user has <usermodes> set on them",
+      "Flag n <nick>: user has string <nick> in their nickname,",
+      "               wildcards accepted",
+      "Flag s <server>: user is on server <server>,",
+      "                 wildcards not accepted",
+      "Flag t <seconds>: (+t) show users on for more than or equal to <seconds> seconds",
+      "                  (-t) show users on for less than <seconds> seconds",
+      "Flag u <user>: user has string <user> in their username,",
+      "               wildcards accepted",
+      "Flag T <type>: user is of type <type>, where type is assigned",
+      "               by services.",
+      "Behavior flags:",
+      "Flag C: show first visible channel user is in",
+      "Flag M: check for user in channels I am a member of",
+      "Flag I: always show IPs instead of hosts",
+      NULL
+  };
+
+  static char *who_user_help[] =
+  {
+      "/WHO [+|-][achmnsuCM] [args]",
+      "Flags are specified like channel modes,",
+      "The flags cghimnsu all have arguments",
+      "Flags are set to a positive check by +, a negative check by -",
+      "The flags work as follows:",
+      "Flag a: user is away",
+      "Flag c <channel>: user is on <channel>,",
+      "                  no wildcards accepted",
+      "Flag h <host>: user has string <host> in their hostname,",
+      "               wildcards accepted",
+      "Flag m <usermodes>: user has <usermodes> set on them,",
+      "                    only usermodes o/O/a/A will return a result",
+      "Flag n <nick>: user has string <nick> in their nickname,",
+      "               wildcards accepted",
+      "Flag s <server>: user is on server <server>,",
+      "                 wildcards not accepted",
+      "Flag u <user>: user has string <user> in their username,",
+      "               wildcards accepted",
+      "Behavior flags:",
+      "Flag C: show first visible channel user is in",
+      "Flag M: check for user in channels I am a member of",
+      NULL
+  };
+
+  char *flags, change=1, *s, *err;
+  int args=1, i, rval;
+
+  memset((char *)&wsopts, '\0', sizeof(SOpts));
+  /* if we got no extra arguments, send them the help. yeech. */
+  /* if it's /who ?, send them the help */
+  if(parc < 1 || parv[0][0]=='?')
+  {
+      /* So we don't confuse users with flags they cannot use,
+         a different /who ? output will be given to users and
+         opers -srd */
+
+      char **ptr = NULL;
+
+      if (!IsAnOper(sptr))
+       ptr = who_user_help;
+      else
+       ptr = who_oper_help;
+
+      for (; *ptr; ptr++)
+         sendto_one(sptr, getreply(RPL_COMMANDSYNTAX), me.name,
+                    sptr->name, *ptr);
+      sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name, "?","WHO");
+      return 0;
+  }
+  /* backwards compatibility */
+  else if(parv[0][0]=='0' && parv[0][1]==0)
+  {
+      if(parc>1 && *parv[1]=='o')
+      {
+         wsopts.check_umode=1;
+         wsopts.umode_plus=1;
+         wsopts.umodes=UMODE_o;
+      }
+      wsopts.host_plus=1;
+      wsopts.host="*";
+      return 1;
+  }
+  /* if the first argument isn't a list of stuff */
+  else if(parv[0][0]!='+' && parv[0][0]!='-')
+  {
+      if(parv[0][0]=='#' || parv[0][0]=='&')
+      {
+         wsopts.channel=find_channel(parv[0],NullChn);
+         if(wsopts.channel==NULL)
+         {
+             sendto_one(sptr, getreply(ERR_NOSUCHCHANNEL), me.name,
+                        sptr->name, parv[0]);
+             return 0;
+         }
+      }
+      else
+      {
+         /* If the arguement has a . in it, treat it as an
+          * address. Otherwise treat it as a nick. -Rak */
+         if (strchr(parv[0], '.'))
+         {
+             wsopts.host_plus=1;
+             wsopts.host=parv[0];
+         }
+         else
+         {
+             wsopts.nick_plus=1;
+             wsopts.nick=parv[0];
+         }
+      }
+      return 1;
+  }
+  /* now walk the list (a lot like set_mode) and set arguments
+   * as appropriate. */
+  flags=parv[0];
+  while(*flags)
+  {
+      switch(*flags)
+      {
+      case '+':
+      case '-':
+         change=(*flags=='+' ? 1 : 0);
+         break;
+      case 'a':
+         if(change)
+             wsopts.away_plus=1; /* they want here people */
+         else
+             wsopts.away_plus=0;
+         wsopts.check_away=1;
+         break;
+      case 'C':
+         wsopts.show_chan = change;
+         break;
+      case 'M':
+         wsopts.search_chan = change;
+         break;
+      case 'c':
+         if(parv[args]==NULL || !change)
+         {
+             sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                        sptr->name, "WHO", "who");
+             return 0;
+         }
+         if(*parv[args] == '@' || *parv[args] == '+')
+         {
+             char *cname = parv[args] + 1;
+
+              if(*parv[args] == '@')
+             {
+                 wsopts.channelflags = CHFL_CHANOP;
+                 if(*cname == '+')
+                 {
+                     wsopts.channelflags |= CHFL_VOICE;
+                     cname++;
+                 }
+             }
+             else
+                 wsopts.channelflags = CHFL_VOICE;
+
+             wsopts.channel=find_channel(cname, NullChn);
+         }
+         else
+         {
+             wsopts.channelflags = 0;
+             wsopts.channel=find_channel(parv[args],NullChn);
+         }
+         if(wsopts.channel==NULL)
+         {
+             sendto_one(sptr, getreply(ERR_NOSUCHCHANNEL), me.name,
+                        sptr->name, parv[args]);
+             return 0;
+         }
+         wsopts.chan_plus=change;
+         args++;
+         break;
+      case 'g':
+          if(parv[args]==NULL)
+          {
+              sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                         sptr->name, "WHO", "who");
+              return 0;
+          }
+          else if(!IsAnOper(sptr))
+          {
+              sendto_one(sptr, getreply(ERR_NOPRIVILEGES), me.name, parv[0]);
+              return 0;
+          }
+          wsopts.gcos=parv[args];
+          wsopts.gcos_plus=change;
+          args++;
+          break;
+      case 'h':
+         if(parv[args]==NULL)
+         {
+             sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                        sptr->name, "WHO", "who");
+             return 0;
+         }
+         wsopts.host=parv[args];
+         wsopts.host_plus=change;
+         args++;
+         break;
+       case 't':
+          if(parv[args]==NULL || (rval = strtol(parv[args], &err, 0)) == 0 || *err != '\0')
+          {
+              sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                         sptr->name, "WHO", "who");
+              return 0;
+          }
+          else if(!IsAnOper(sptr))
+          {
+              sendto_one(sptr, getreply(ERR_NOPRIVILEGES), me.name, parv[0]);
+              return 0;
+          }
+          wsopts.ts = rval;
+          wsopts.ts_value = change ? 2 : 1;
+          args++;
+          break; 
+       case 'T':
+          if(parv[args]==NULL || (rval = strtol(parv[args], &err, 0)) == 0 || *err != '\0')
+          {
+              sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                         sptr->name, "WHO", "who");
+              return 0;
+          }
+          else if(!IsAnOper(sptr))
+          {
+              sendto_one(sptr, getreply(ERR_NOPRIVILEGES), me.name, parv[0]);
+              return 0;
+          }
+          wsopts.client_type = rval;
+          wsopts.client_type_plus = change ? 1 : 0;
+          args++;
+          break;
+      case 'I':
+          if(!IsAnOper(sptr))
+          {
+              sendto_one(sptr, getreply(ERR_NOPRIVILEGES), me.name, parv[0]);
+              return 0;
+          }
+          wsopts.ip_show = change;
+          break; 
+      case 'i':
+          if(parv[args]==NULL)
+          {
+              sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                         sptr->name, "WHO", "who");
+              return 0;
+          }
+          else if(!IsAnOper(sptr))
+          {
+              sendto_one(sptr, getreply(ERR_NOPRIVILEGES), me.name, parv[0]);
+              return 0;
+          }
+          else
+          {
+              char *cpos;
+          
+              if((cpos = strchr(parv[args], '/'))) 
+              {  
+                  char *err;
+                  unsigned int maskval, ipval;
+          
+                  *(cpos++) = '\0';
+              
+                  ipval = inet_addr(parv[args]);
+                  maskval = strtol(cpos, &err, 10);
+                  if(ipval == 0xFFFFFFFF || *err != '\0' ||
+                     maskval < 1 || maskval > 32)
+                  {
+                      sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name, sptr->name,
+                         "WHO", "who");
+                      return 0;
+                  }
+              
+                  maskval = htonl(cidr_to_netmask(maskval));
+                  ipval &= maskval;
+           
+                  wsopts.cidr4_plus = change;
+                  wsopts.cidr4_mask = maskval;
+                  wsopts.cidr4_ip = ipval;
+                  args++;
+              }
+              else
+              {
+                  wsopts.ip=parv[args];
+                  wsopts.ip_plus=change;
+                  args++;
+              }
+          }
+          break;
+      case 'm':
+         if(parv[args]==NULL)
+         {
+             sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                        sptr->name, "WHO", "who");
+             return 0;
+         }
+         s=parv[args];
+         while(*s)
+         {
+             for(i=1;user_modes[i]!=0x0;i+=2)
+             {
+                 if(*s==(char)user_modes[i])
+                 {
+                     wsopts.umodes|=user_modes[i-1];
+                     break;
+                 }
+             }
+             s++;
+         }
+         if(!IsAnOper(sptr)) /* only let users search for +/-oOaA */
+             wsopts.umodes=(wsopts.umodes&(UMODE_o|UMODE_O|UMODE_a|UMODE_A));
+         wsopts.umode_plus=change;
+         if(wsopts.umodes)
+             wsopts.check_umode=1;
+         args++;
+         break;
+      case 'n':
+         if(parv[args]==NULL)
+         {
+             sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                        sptr->name, "WHO", "who");
+             return 0;
+         }
+         wsopts.nick=parv[args];
+         wsopts.nick_plus=change;
+         args++;
+         break;
+      case 's':
+         if(parv[args]==NULL || !change)
+         {
+             sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                        sptr->name, "WHO", "who");
+             return 0;
+         }
+         wsopts.server=find_server(parv[args],NULL);
+         if(wsopts.server==NULL)
+         {
+             sendto_one(sptr, getreply(ERR_NOSUCHSERVER), me.name,
+                        sptr->name, parv[args]);
+             return 0;
+         }
+         wsopts.serv_plus=change;
+         args++;
+         break;
+      case 'u':
+         if(parv[args]==NULL)
+         {
+             sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                        sptr->name, "WHO", "who");
+             return 0;
+         }
+         wsopts.user=parv[args];
+         wsopts.user_plus=change;
+         args++;
+         break;
+      default:
+         sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name,
+                    sptr->name, "WHO", "who");
+         return 0;
+
+      }
+      flags++;
+  }
+  
+  /* if we specified search_chan, we _must_ specify something useful 
+   * to go with it. Specifying a channel makes no sense, and no params make no 
+   * sense either, as does specifying a nick.
+   */
+  
+  if(wsopts.search_chan && !(wsopts.check_away || wsopts.gcos_plus || 
+                            wsopts.host_plus || wsopts.check_umode || 
+                            wsopts.serv_plus || wsopts.nick_plus || 
+                            wsopts.user_plus || wsopts.ts_value || 
+                 wsopts.client_type_plus || wsopts.ip_plus))
+  {
+      if(parv[args]==NULL || wsopts.channel || wsopts.nick ||
+        parv[args][0] == '#' || parv[args][0] == '&')
+      {
+         sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name, sptr->name, "WHO",
+                 "who");
+         return 0;
+      }
+
+      if (strchr(parv[args], '.'))
+      {
+         wsopts.host_plus=1;
+         wsopts.host=parv[args];
+      }
+      else
+      {
+         sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name, sptr->name, "WHO",
+                 "who");
+         return 0;
+      }
+  } 
+  else /* can't show_chan if nothing else is set! */
+      if(wsopts.show_chan && !(wsopts.check_away || wsopts.gcos_plus || 
+                              wsopts.host_plus || wsopts.check_umode || 
+                              wsopts.serv_plus || wsopts.nick_plus || 
+                              wsopts.user_plus || wsopts.ts_value || 
+                   wsopts.client_type_plus || wsopts.ip_plus || 
+                   wsopts.chan_plus))
+      {
+         if(parv[args]==NULL)
+         {
+             sendto_one(sptr, getreply(ERR_WHOSYNTAX), me.name, sptr->name, "WHO",
+                     "who");
+             return 0;
+         }
+         
+         if (strchr(parv[args], '.'))
+         {
+             wsopts.host_plus=1;
+             wsopts.host=parv[args];
+         }
+         else
+         {
+             wsopts.nick_plus=1;
+             wsopts.nick=parv[args];
+         }
+      }
+
+  /* hey cool, it all worked! */
+  return 1;
+}
+
+/* these four are used by chk_who to check gcos/nick/user/host
+ * respectively 
+ * as well as ip -srd */
+
+int (*gchkfn)(char *, char *);
+int (*nchkfn)(char *, char *);
+int (*uchkfn)(char *, char *);
+int (*hchkfn)(char *, char *);
+int (*ichkfn)(char *, char *);
+
+int chk_who(aClient *ac, int showall)
+{
+    if(!IsClient(ac))
+       return 0;
+    if(IsInvisible(ac) && !showall)
+       return 0;
+
+    if(wsopts.client_type_plus &&
+       wsopts.client_type != ac->user->servicetype)
+       return 0;
+
+    if(wsopts.check_umode)
+       if((wsopts.umode_plus && 
+           !((ac->umode&wsopts.umodes)==wsopts.umodes)) ||
+          (!wsopts.umode_plus && ((ac->umode&wsopts.umodes)==wsopts.umodes)))
+           return 0;
+
+    if(wsopts.check_away)
+       if((wsopts.away_plus && ac->user->away==NULL) ||
+          (!wsopts.away_plus && ac->user->away!=NULL))
+           return 0;
+
+    /* while this is wasteful now, in the future
+     * when clients contain pointers to their servers
+     * of origin, this'll become a 4 byte check instead of a mycmp
+     * -wd */
+    /* welcome to the future... :) - lucas */
+    if(wsopts.serv_plus)
+    {
+       if(wsopts.server != ac->uplink)
+           return 0;
+       /* don't let people find hidden opers via /who +s server */
+       if(IsUmodeI(ac) && !showall)
+           return 0;
+    }
+    /* we only call match once, since if the first condition
+     * isn't true, most (all?) compilers will never try the
+     * second...phew :) */
+    if(wsopts.user!=NULL)
+       if((wsopts.user_plus && uchkfn(wsopts.user, ac->user->username)) ||
+          (!wsopts.user_plus && !uchkfn(wsopts.user, ac->user->username)))
+           return 0;
+
+    if(wsopts.nick!=NULL)
+       if((wsopts.nick_plus && nchkfn(wsopts.nick, ac->name)) ||
+          (!wsopts.nick_plus && !nchkfn(wsopts.nick, ac->name)))
+           return 0;
+    
+    if(wsopts.host!=NULL)
+       if((wsopts.host_plus && hchkfn(wsopts.host, ac->user->host)) ||
+          (!wsopts.host_plus && !hchkfn(wsopts.host, ac->user->host)))
+           return 0;
+
+    if(wsopts.cidr4_plus)
+       if((ac->ip.s_addr & wsopts.cidr4_mask) != wsopts.cidr4_ip)
+           return 0;
+    
+    if(wsopts.ip_plus)
+       if(ichkfn(wsopts.ip, ac->hostip))
+           return 0;
+    
+    if(wsopts.gcos!=NULL)
+       if((wsopts.gcos_plus && gchkfn(wsopts.gcos, ac->info)) ||
+          (!wsopts.gcos_plus && !gchkfn(wsopts.gcos, ac->info)))
+           return 0;
+
+    /*
+     * For the below options, a value of two means '+', 
+     * a value of 1 means '-', and a value of 0 means
+     * not speficied. 
+     */
+
+    if(wsopts.ts_value == 2 && /* +t */
+        NOW - ac->tsinfo < wsopts.ts)
+        return 0;
+    else if(wsopts.ts_value == 1 && /* -t */
+        NOW - ac->tsinfo >= wsopts.ts)
+        return 0;
+
+    return 1;
+}
+
+inline char *first_visible_channel(aClient *cptr, aClient *sptr)
+{
+    Link *lp;
+    int secret = 0;
+    aChannel *chptr = NULL;
+    static char chnbuf[CHANNELLEN + 2];
+
+    if(cptr->user->channel)
+    {
+       if(IsAdmin(sptr))
+       {
+           chptr = cptr->user->channel->value.chptr;
+           if(!(ShowChannel(sptr, chptr)))
+               secret = 1;
+       }
+       else
+       {
+           for(lp = cptr->user->channel; lp; lp = lp->next)
+           {
+               if(ShowChannel(sptr, lp->value.chptr))
+                   break;
+           }
+           if(lp)
+               chptr = lp->value.chptr;
+       }
+
+       if(chptr)
+       {
+           if(!secret)
+               return chptr->chname;
+           ircsprintf(chnbuf, "%%%s", chptr->chname);
+           return chnbuf;
+       }
+    }
+    return "*";
+}
+
+/* allow lusers only 200 replies from /who */
+#define MAXWHOREPLIES 200
+#define WHO_HOPCOUNT(s, a) ( ( (IsULine((a)) || IsUmodeI((a))) && !IsAnOper((s)) ) ? 0 : a->hopcount)
+#define WHO_SERVER(s ,a) ((IsUmodeI((a)) && !IsAnOper((s))) ? HIDDEN_SERVER_NAME : a->user->server)
+#define WHO_HOST(a) ((wsopts.ip_show) ? (a)->hostip : (a)->user->host)
+int m_who(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient *ac;
+    chanMember *cm;
+    Link *lp;
+    int shown=0, i=0, showall=IsAnOper(sptr);
+    char status[4];
+
+    /* drop nonlocal clients */
+    if(!MyClient(sptr))
+       return 0;
+    
+    if(!build_searchopts(sptr, parc-1, parv+1))
+       return 0; /* /who was no good */
+    
+    if(wsopts.gcos!=NULL && (strchr(wsopts.gcos, '?'))==NULL &&
+       (strchr(wsopts.gcos, '*'))==NULL)
+       gchkfn=mycmp;
+    else
+       gchkfn=match;
+    if(wsopts.nick!=NULL && (strchr(wsopts.nick, '?'))==NULL &&
+       (strchr(wsopts.nick, '*'))==NULL)
+       nchkfn=mycmp;
+    else
+       nchkfn=match;
+    if(wsopts.user!=NULL && (strchr(wsopts.user, '?'))==NULL &&
+       (strchr(wsopts.user, '*'))==NULL)
+       uchkfn=mycmp;
+    else
+       uchkfn=match;
+    if(wsopts.host!=NULL && (strchr(wsopts.host, '?'))==NULL &&
+       (strchr(wsopts.host, '*'))==NULL)
+       hchkfn=mycmp;
+    else
+       hchkfn=match;
+
+    if(wsopts.ip!=NULL && (strchr(wsopts.ip, '?'))==NULL &&
+       (strchr(wsopts.ip, '*'))==NULL)
+       ichkfn=mycmp;
+    else
+       ichkfn=match;
+
+    if(wsopts.channel!=NULL)
+    {
+       if(IsMember(sptr,wsopts.channel))
+           showall=1;
+       else if(SecretChannel(wsopts.channel) && IsAdmin(sptr))
+           showall=1;
+       else if(!SecretChannel(wsopts.channel) && IsAnOper(sptr))
+           showall=1;
+       else
+           showall=0;
+       if(showall || !SecretChannel(wsopts.channel))
+       {
+           for(cm=wsopts.channel->members; cm; cm=cm->next)
+           {
+               ac=cm->cptr;
+               i=0;
+               if(!chk_who(ac,showall))
+                   continue;
+               /* If we have channel flags set, verify they match */
+               if(wsopts.channelflags && ((cm->flags & wsopts.channelflags) == 0))
+                   continue;
+               /* get rid of the pidly stuff first */
+               /* wow, they passed it all, give them the reply...
+                * IF they haven't reached the max, or they're an oper */
+               status[i++]=(ac->user->away==NULL ? 'H' : 'G');
+               status[i]=(IsAnOper(ac) ? '*' : ((IsInvisible(ac) &&
+                                                 IsOper(sptr)) ? '%' : 0));
+               status[((status[i]) ? ++i : i)]=((cm->flags&CHFL_CHANOP) ? '@'
+                                                : ((cm->flags&CHFL_VOICE) ? 
+                                                   '+' : 0));
+               status[++i]=0;
+               sendto_one(sptr, getreply(RPL_WHOREPLY), me.name, sptr->name,
+                          wsopts.channel->chname, ac->user->username,
+                          WHO_HOST(ac), WHO_SERVER(sptr, ac), ac->name, status,
+                          WHO_HOPCOUNT(sptr, ac),
+                          ac->info);
+           }
+       }
+       sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name,
+                  wsopts.channel->chname, "WHO");
+       return 0;
+    }
+    /* if (for whatever reason) they gave us a nick with no
+     * wildcards, just do a find_person, bewm! */
+    else if(nchkfn==mycmp)
+    {
+       ac=find_person(wsopts.nick,NULL);
+       if(ac!=NULL)
+       {
+           if(!chk_who(ac,1))
+           {
+               sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name,
+                          wsopts.host!=NULL ? wsopts.host : wsopts.nick, "WHO");
+               return 0;
+           }
+           else
+           {
+               status[0]=(ac->user->away==NULL ? 'H' : 'G');
+               status[1]=(IsAnOper(ac) ? '*' : (IsInvisible(ac) &&
+                                                IsAnOper(sptr) ? '%' : 0));
+               status[2]=0;
+               sendto_one(sptr, getreply(RPL_WHOREPLY), me.name, sptr->name,
+                          wsopts.show_chan ? first_visible_channel(ac, sptr)
+                          : "*", ac->user->username, WHO_HOST(ac),
+                          WHO_SERVER(sptr, ac), ac->name, status,
+                          WHO_HOPCOUNT(sptr, ac),
+                          ac->info);
+               sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name,
+                          wsopts.host!=NULL ? wsopts.host : wsopts.nick, "WHO");
+               return 0;
+           }
+       }
+       sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name,
+                  wsopts.host!=NULL ? wsopts.host : wsopts.nick, "WHO");
+       return 0;
+    }
+    
+    if(wsopts.search_chan)
+    {
+       for(lp = sptr->user->channel; lp; lp = lp->next)
+       {
+           for(cm = lp->value.chptr->members; cm; cm = cm->next)
+           {
+               ac = cm->cptr;
+               if(!chk_who(ac, 1))
+                   continue;
+               
+               if(shown==MAXWHOREPLIES && !IsAnOper(sptr))
+               {
+                   sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name,
+                              sptr->name, MAXWHOREPLIES, "WHO");
+                   break;
+               }
+               
+               i = 0;
+               status[i++]=(ac->user->away==NULL ? 'H' : 'G');
+               status[i]=(IsAnOper(ac) ? '*' : ((IsInvisible(ac) &&
+                                                 IsOper(sptr)) ? '%' : 0));
+               status[((status[i]) ? ++i : i)]=((cm->flags&CHFL_CHANOP) ? 
+                                                '@' : ((cm->flags&CHFL_VOICE)
+                                                       ? '+' : 0));
+               status[++i]=0;
+               sendto_one(sptr, getreply(RPL_WHOREPLY), me.name, sptr->name,
+                          lp->value.chptr->chname, ac->user->username,
+                          WHO_HOST(ac),WHO_SERVER(sptr, ac), ac->name,
+                          status, WHO_HOPCOUNT(sptr, ac), ac->info);
+               shown++;
+           }
+       }
+    }
+    else
+    {
+       for(ac=client;ac;ac=ac->next)
+       {
+           if(!chk_who(ac,showall))
+               continue;
+           /* wow, they passed it all, give them the reply...
+            * IF they haven't reached the max, or they're an oper */
+           if(shown==MAXWHOREPLIES && !IsAnOper(sptr))
+           {
+               sendto_one(sptr, getreply(ERR_WHOLIMEXCEED), me.name, 
+                          sptr->name, MAXWHOREPLIES, "WHO");
+               break; /* break out of loop so we can send end of who */
+           }
+           status[0]=(ac->user->away==NULL ? 'H' : 'G');
+           status[1]=(IsAnOper(ac) ? '*' : (IsInvisible(ac) && 
+                                            IsAnOper(sptr) ? '%' : 0));
+           status[2]=0;
+           sendto_one(sptr, getreply(RPL_WHOREPLY), me.name, sptr->name,
+                      wsopts.show_chan ? first_visible_channel(ac, sptr) :
+                      "*", ac->user->username, WHO_HOST(ac),
+                      WHO_SERVER(sptr, ac), ac->name, status,
+                      WHO_HOPCOUNT(sptr, ac), ac->info);
+           shown++;
+       }
+    }
+    sendto_one(sptr, getreply(RPL_ENDOFWHO), me.name, sptr->name,
+              (wsopts.host!=NULL ? wsopts.host :
+               (wsopts.nick!=NULL ? wsopts.nick :
+                (wsopts.user!=NULL ? wsopts.user :
+                 (wsopts.gcos!=NULL ? wsopts.gcos :
+                  (wsopts.server!=NULL ? wsopts.server->name :
+                   "*"))))), "WHO");
+    return 0;
+}
diff --git a/src/match.c b/src/match.c
new file mode 100644 (file)
index 0000000..c64db76
--- /dev/null
@@ -0,0 +1,437 @@
+
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/match.c
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: match.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+
+#if 0 /* this code is depricated, however it may still prove useful
+         in the long run to 'show' it here */
+
+#define MAX_CALLS 512
+static int  calls = 0;
+
+/*
+ * *  Compare if a given string (name) matches the given *  mask (which
+ * can contain wild cards: '*' - match any *  number of chars, '?' -
+ * match any single character. *
+ * 
+ *      return  0, if match *           1, if no match
+ */
+/*
+ * * _match() * Iterative matching function, rather than recursive. *
+ * Written by Douglas A Lewis (dalewis@acsu.buffalo.edu)
+ */
+
+static int wdmatch(const char *string, const char *wild);
+
+static int _match(char *mask, char *name)
+{
+    u_char *m = (u_char *) mask, *n = (u_char *) name;
+    char       *ma = mask, *na = name;
+    int         wild = 0, q = 0;
+
+    while (1)
+    {
+       if (calls++ > MAX_CALLS)
+           return 1;
+       if (*m == '*')
+       {
+           while (*m == '*')
+               m++;
+           wild = 1;
+           ma = (char *) m;
+           na = (char *) n;
+       }
+
+       if (!*m)
+       {
+           if (!*n)
+               return 0;
+           for (m--; (m > (u_char *) mask) && (*m == '?'); m--);
+           if ((*m == '*') && (m > (u_char *) mask) &&
+               (m[-1] != '\\'))
+               return 0;
+           if (!wild)
+               return 1;
+           m = (u_char *) ma;
+           n = (u_char *) ++na;
+       }
+       else if (!*n)
+       {
+           while (*m == '*')
+               m++;
+           return (*m != 0);
+       }
+       if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
+       {
+           m++;
+           q = 1;
+       }
+       else
+           q = 0;
+
+       if ((ToLower(*m) != ToLower(*n)) && ((*m != '?') || q))
+       {
+           if (!wild)
+               return 1;
+           m = (u_char *) ma;
+           n = (u_char *) ++na;
+       }
+       else
+       {
+           if (*m)
+               m++;
+           if (*n)
+               n++;
+       }
+    }
+}
+
+int match(char *n, char *m)
+{
+    calls=0;
+    return wdmatch(n,m);
+}
+#endif
+
+/*
+ * * collapse a pattern string into minimal components. * This
+ * particular version is "in place", so that it changes the pattern *
+ * which is to be reduced to a "minimal" size.
+ */
+char *collapse(char *pattern)
+{
+    char   *s = pattern, *s1, *t;
+    
+    if (BadPtr(pattern))
+       return pattern;
+    /* Collapse all \** into \*, \*[?]+\** into \*[?]+ */
+    for (; *s; s++)
+       if (*s == '\\')
+       {
+           if (!*(s + 1))
+               break;
+           else
+               s++;
+       }
+       else if (*s == '*')
+       {
+           if (*(t = s1 = s + 1) == '*')
+               while (*t == '*')
+                   t++;
+           else if (*t == '?')
+               for (t++, s1++; *t == '*' || *t == '?'; t++)
+                   if (*t == '?')
+                       *s1++ = *t;
+           while ((*s1++ = *t++));
+       }
+    return pattern;
+}
+
+unsigned char tolowertab[] =
+{0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
+ 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f,
+ ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
+ '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ ':', ';', '<', '=', '>', '?',
+ '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+ 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', '\\', ']', '^', '_',
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+ 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
+ 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
+ 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+ 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+ 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+ 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
+ 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
+
+unsigned char touppertab[] =
+{0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
+ 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f,
+ ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
+ '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ ':', ';', '<', '=', '>', '?',
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
+ 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+ 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', 0x5f,
+ '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
+ 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+ 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~',
+ 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
+ 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+ 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+ 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+ 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
+ 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
+
+unsigned char char_atribs[] =
+{
+    /* 0-7 */
+    CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL,
+    /* 8-12*/
+    CNTRL, CNTRL | SPACE, CNTRL | SPACE, CNTRL | SPACE, CNTRL | SPACE,
+    /* 13-15 */
+    CNTRL | SPACE, CNTRL, CNTRL,
+    /* 16-23 */
+    CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL,
+    /* 24-31 */
+    CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL,
+    /* space */
+    PRINT | SPACE,
+    /* !"#$%&'( */
+    PRINT, PRINT, PRINT, PRINT, PRINT, PRINT, PRINT, PRINT,
+    /* )*+,-./ */
+    PRINT, PRINT, PRINT, PRINT, PRINT, PRINT, PRINT,
+    /* 0123 */
+    PRINT | DIGIT, PRINT | DIGIT, PRINT | DIGIT, PRINT | DIGIT,
+    /* 4567 */
+    PRINT | DIGIT, PRINT | DIGIT, PRINT | DIGIT, PRINT | DIGIT,
+    /* 89:; */
+    PRINT | DIGIT, PRINT | DIGIT, PRINT, PRINT,
+    /* <=>? */
+    PRINT, PRINT, PRINT, PRINT,
+    /* @ */
+    PRINT,
+    /* ABC */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* DEF */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* GHI */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* JKL */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* MNO */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* PQR */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* STU */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* VWX */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* YZ[ */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* \]^ */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* _` */
+    PRINT, PRINT,
+    /* abc */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* def */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* ghi */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* jkl */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* mno */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* pqr */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* stu */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* vwx */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* yz{ */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* \}~ */
+    PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+    /* del */
+    0,
+    /* 80-8f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* 90-9f */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* a0-af */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* b0-bf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* c0-cf */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* d0-df */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* e0-ef */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    /* f0-ff */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* screw that lexocographic crap, nothing in ircd ever cares */
+int mycmp(char *s1, char *s2)
+{
+    unsigned char *str1 = (unsigned char *) s1;
+    unsigned char *str2 = (unsigned char *) s2;
+    while (touppertab[*str1] == touppertab[*str2])
+    {
+       if (*str1 == '\0')
+           return 0;
+       str1++;str2++;
+    }
+    return (-1);
+}
+
+int mycmp_diff(char *s1, char *s2)
+{
+    int ret;
+    unsigned char *str1 = (unsigned char *) s1;
+    unsigned char *str2 = (unsigned char *) s2;
+    while ((ret = touppertab[*str1] - touppertab[*str2]) == 0)
+    {
+       if (*str1 == '\0')
+           return 0;
+       str1++;str2++;
+    }
+    return ret;
+}
+
+int myncmp(char *str1, char *str2, int n)
+{
+    unsigned char *s1 = (unsigned char *) str1;
+    unsigned char *s2 = (unsigned char *) str2;
+    while (touppertab[*s1] == touppertab[*s2])
+    {
+       s1++;
+       s2++;
+       n--;
+       if (n == 0 || (*s1 == '\0' && *s2 == '\0'))
+           return 0;
+    }
+    return (-1);
+}
+
+#define MAX_CALLS 512
+static int  calls = 0;
+
+static inline int _match(char *wild, char *string);
+
+int match(char *n, char *m)
+{
+    calls=0;
+    return _match(n,m);
+}
+
+static inline int _match(char *wild, char *string)
+{
+    if (calls++ > MAX_CALLS)
+       return 1;
+    
+    /* the *! in a match is such a common case that we optimize
+     * for it inherently */
+
+    if(wild[0]=='*' && wild[1]=='!')
+    {
+       wild+=2;
+       while(*string!='!' && *string)
+           string++;
+       if(!*string)
+           return 1;
+       string++;
+    }
+    /* we also optimize further for *!*@...which is also very
+     * common...*/
+    if(wild[0]=='*' && wild[1]=='@')
+    {
+       wild+=2;
+       while(*string!='@' && *string)
+           string++;
+       if(!*string)
+           return 1;
+       string++;
+    }
+    while(1)
+    {
+       /*
+        * Logic: Match * in a string, this is confusing, sort of
+        * if * is the last thing in the wildcard, the match is
+        * definite if we've gotten this far.
+        * otherwise we try and find every occurance of the
+        * the next character in the wildcard within the string
+        * and match from there, calling the function recursively
+        * until some level below us returns positive, in which
+        * case we too return positive.  For strings with lots
+        * of wildcards this gets disgustingly recursive.
+        */
+       if(!*wild)
+           return ((!*string) ? 0 : 1);
+       if(*wild=='*')
+       {
+           /* if ret==2 then we know it's a problem un-solvable
+            * by match, so we totally give up instead of trying
+            * a bunch of other recursions to check through */
+           wild++;
+           /* swallow all extraneous '*'s after.  */
+           while(*wild=='*')
+               wild++;
+           while(*wild=='?' && *string)
+           {
+               wild++;string++;
+           }
+           if(!*wild)
+               return 0;
+           if(*wild=='*')
+               continue;
+           while(*string)
+           {
+               if(touppertab[(u_char)*string]
+                  == touppertab[(u_char)*wild]
+                  && !_match((wild+1), (string+1)))
+                   return 0;
+               string++;
+           }
+       }
+       if(!*string)
+           return 1;
+       if(*wild!='?' && touppertab[(u_char)*string]!=
+          touppertab[(u_char)*wild])
+           return 1;
+       string++;
+       wild++;
+    }
+    return 1;
+}
diff --git a/src/modules.c b/src/modules.c
new file mode 100644 (file)
index 0000000..fdced87
--- /dev/null
@@ -0,0 +1,870 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/modules.c
+ *   Copyright (C) 2003, Lucas Madar
+ */
+
+/* $Id: modules.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "channel.h"
+#include "throttle.h"
+#include "h.h"
+#include "hooks.h"
+
+extern Conf_Modules *modules;
+
+#ifndef USE_HOOKMODULES
+int 
+m_module(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    if(MyClient(sptr) && !IsAnOper(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    if (parc > 2)
+    {
+        if (!mycmp(parv[1], "LIST") || !mycmp(parv[1], "HOOKS"))
+        {
+            if (hunt_server(cptr, sptr, "%s MODULE %s %s", 2, parc, parv)
+                != HUNTED_ISME)
+                return 0;
+        }
+        else if (!mycmp(parv[1], "CGLOBAL"))
+        {
+            char pbuf[512];
+
+            if(!(IsServer(sptr) || IsULine(sptr)))
+                return 0;
+
+            /* Pass this on to all servers! */
+            make_parv_copy(pbuf, parc, parv);
+            sendto_serv_butone(cptr, ":%s MODULE %s", parv[0], pbuf);
+
+            return 0;
+        }
+    }
+
+    if (IsPerson(sptr))
+        sendto_one(sptr, "%s NOTICE %s :I don't have module support.",
+                   me.name, sptr->name);
+
+    return 0;
+}
+
+int 
+call_hooks(enum c_hooktype hooktype, ...)
+{
+    return 0;
+}
+
+int 
+init_modules()
+{
+    return 0;
+}
+
+#else
+
+#include <dlfcn.h>
+
+/* XXX hack.  check on RTLD_NOW later. */
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+
+DLink *module_list = NULL;
+
+typedef struct loaded_module 
+{
+    char *name;
+
+    char *version;
+    char *description;
+
+    void *handle;
+
+    void (*module_check) (int *);
+    int  (*module_init) (void *);
+    void (*module_shutdown) (void);
+    void (*module_getinfo) (char **, char **);
+    int  (*module_command) (aClient *, int, char **);
+    int  (*module_globalcommand) (aClient *, aClient *, int, char **);
+} aModule;
+
+/* Forward decls */
+char *bircmodule_strdup(char *);
+void *bircmodule_malloc(int);
+void  bircmodule_free(void *);
+void  drop_all_hooks(aModule *owner);
+void  list_hooks(aClient *sptr);
+
+aModule *
+find_module(char *name) 
+{
+    DLink *lp;
+
+    for(lp = module_list; lp; lp = lp->next)
+    {
+        aModule *mod = (aModule *) lp->value.cp;
+
+        if(strcmp(mod->name, name) == 0)
+            return mod;
+    }
+    return NULL;
+}
+
+aModule *
+find_module_opaque(void *opaque) 
+{
+    DLink *lp;
+
+    for(lp = module_list; lp; lp = lp->next)
+    {
+        aModule *mod = (aModule *) lp->value.cp;
+
+        if(opaque == (void *) mod)
+            return mod;
+    }
+    return NULL;
+}
+
+int 
+modsym_load(aClient *sptr, char *modname, char *symbol, void *modulehandle, 
+            void **retfunc)
+{
+    void *ret;
+    const char *error;
+
+    ret = dlsym(modulehandle, symbol);
+
+    if((error = dlerror()) != NULL)
+    {
+        if(sptr)
+            sendto_one(sptr, ":%s NOTICE %s :Module symbol error for %s/%s: %s",
+                       me.name, sptr->name, modname, symbol, error);
+        else
+            fprintf(stderr, " - Module symbol error for %s/%s: %s\n",
+                    modname, symbol, error);
+        
+        dlclose(modulehandle);
+        return 0;
+    }
+
+    *retfunc = ret;
+    return 1;
+}
+
+void 
+list_modules(aClient *sptr)
+{
+    DLink *lp;
+
+    for(lp = module_list; lp; lp = lp->next)
+    {
+        aModule *mod = (aModule *) lp->value.cp;
+        sendto_one(sptr, ":%s NOTICE %s :Module: %s    Version: %s",
+                   me.name, sptr->name, mod->name, mod->version);
+
+        sendto_one(sptr, ":%s NOTICE %s :  - %s", me.name, sptr->name, 
+                   mod->description);
+    }
+}
+
+void 
+destroy_module(aModule *themod)
+{
+    (*themod->module_shutdown)();
+    dlclose(themod->handle);
+    bircmodule_free(themod->name);
+    bircmodule_free(themod->version);
+    bircmodule_free(themod->description);   
+    remove_from_list(&module_list, themod, NULL);
+    bircmodule_free(themod);
+}
+
+int 
+load_module(aClient *sptr, char *modname)
+{
+    aModule tmpmod, *themod;
+    char mnamebuf[512], *ver, *desc;
+    int acsz = -1, ret;
+
+    if((themod = find_module(modname)))
+    {
+        if(sptr)
+            sendto_one(sptr, ":%s NOTICE %s :Module %s is already loaded"
+                       " [version: %s]", me.name, sptr->name, modname, 
+                       themod->version);
+        else
+            fprintf(stderr, " - Module %s is already loaded [version: %s]\n",
+                    modname, themod->version);
+        return 0;
+    }
+
+    if(modules && modules->module_path)
+        ircsnprintf(mnamebuf, 512, "%s/%s.so", modules->module_path, modname);
+    else
+        ircsnprintf(mnamebuf, 512, "%s/modules/%s.so", dpath, modname);
+
+    tmpmod.handle = dlopen(mnamebuf, RTLD_NOW);
+    if(tmpmod.handle == NULL)
+    {
+        if(sptr)
+            sendto_one(sptr, ":%s NOTICE %s :Module load error for %s: %s",
+                       me.name, sptr->name, modname, dlerror());
+        else
+            fprintf(stderr, " - Module load error for %s: %s\n",
+                    modname, dlerror());
+        return -1;
+    }
+
+    if(!modsym_load(sptr, modname, "bircmodule_check", tmpmod.handle, 
+                    (void **) &tmpmod.module_check))
+        return -1;
+    if(!modsym_load(sptr, modname, "bircmodule_init", tmpmod.handle, 
+                    (void **) &tmpmod.module_init))
+        return -1;
+    if(!modsym_load(sptr, modname, "bircmodule_shutdown", tmpmod.handle, 
+                    (void **) &tmpmod.module_shutdown))
+        return -1;
+    if(!modsym_load(sptr, modname, "bircmodule_getinfo", tmpmod.handle, 
+                    (void **) &tmpmod.module_getinfo))
+        return -1;
+    if(!modsym_load(sptr, modname, "bircmodule_command", tmpmod.handle, 
+                    (void **) &tmpmod.module_command))
+        return -1;
+    if(!modsym_load(sptr, modname, "bircmodule_globalcommand", tmpmod.handle, 
+                    (void **) &tmpmod.module_globalcommand))
+        return -1;
+
+    (*tmpmod.module_check)(&acsz);
+    if(acsz != MODULE_INTERFACE_VERSION)
+    {
+        if(sptr)
+            sendto_one(sptr, ":%s NOTICE %s :Module load error for %s:"
+                    " Incompatible module (My interface version: %d Module"
+                    " version: %d)", me.name, sptr->name, modname, 
+                    MODULE_INTERFACE_VERSION, acsz);
+        else
+            fprintf(stderr, " - Module load error for %s: Incompatible module ("
+                            "My interface version: %d Module version: %d)\n",
+                    modname, MODULE_INTERFACE_VERSION, acsz);
+        dlclose(tmpmod.handle);
+        return -1;
+    }
+
+    tmpmod.name = bircmodule_strdup(modname);
+
+    ver = desc = NULL;
+    (*tmpmod.module_getinfo)(&ver, &desc);
+    tmpmod.version = bircmodule_strdup((ver != NULL) ? ver : "<no version>");
+    tmpmod.description = bircmodule_strdup((desc != NULL) ? desc : 
+                                                           "<no description>");
+    themod = (aModule *) bircmodule_malloc(sizeof(aModule));
+    memcpy(themod, &tmpmod, sizeof(aModule));
+    add_to_list(&module_list, themod);
+
+    ret = (*themod->module_init)((void *) themod);
+
+    if(ret == 0)
+    {
+        if(sptr)
+            sendto_one(sptr, ":%s NOTICE %s :Module %s successfully loaded"
+                       " [version: %s]", me.name, sptr->name, modname, 
+                       themod->version);
+        else
+            fprintf(stderr, " - Module %s successfully loaded [version: %s]\n",
+                    modname, themod->version);
+
+        call_hooks(MHOOK_LOAD, modname, (void *) themod);
+    }
+    else
+    {
+        drop_all_hooks(themod);
+        destroy_module(themod);
+
+        if(sptr)
+            sendto_one(sptr, ":%s NOTICE %s :Module %s load failed (module"
+                        " requested unload)", me.name, sptr->name, modname);
+        else
+            fprintf(stderr, " - Module %s load failed (module requested"
+                            " unload)\n", modname);
+    }
+    return 0;
+}
+
+int 
+unload_module(aClient *sptr, char *modname)
+{
+    aModule *themod = find_module(modname);
+
+    if(!themod)
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Module %s is not loaded",
+                   me.name, sptr->name, modname);
+        return 0;
+    }
+
+    drop_all_hooks(themod);
+    call_hooks(MHOOK_UNLOAD, themod->name, (void *) themod);
+    destroy_module(themod);
+
+    sendto_one(sptr, ":%s NOTICE %s :Module %s successfully unloaded",
+               me.name, sptr->name, modname);
+
+    return 0;
+}
+
+int 
+m_module(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    if(!IsAnOper(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    if(parc < 2)
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name,
+                        parv[0], "MODULE");
+        return 0;
+    }
+
+    /* this should technically never happen anyway, but.. */
+    if(!MyClient(sptr) && !(IsAnOper(sptr) || IsULine(sptr) || IsServer(sptr)))
+        return 0;
+
+    if(mycmp(parv[1], "LOAD") == 0)
+    {
+        if(!(MyClient(sptr) && IsAdmin(sptr)))
+        {
+            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            return 0;
+        }
+        if(!BadPtr(parv[2]))
+            load_module(sptr, parv[2]);
+        else
+        {
+            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name,
+                       parv[0], "MODULE");
+            return 0;
+        }
+
+    }
+    else if(mycmp(parv[1], "UNLOAD") == 0)
+    {
+        if(!(MyClient(sptr) && IsAdmin(sptr)))
+        {
+            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            return 0;
+        }
+        if(!BadPtr(parv[2]))
+            unload_module(sptr, parv[2]);
+        else
+        {
+            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name,
+                       parv[0], "MODULE");
+            return 0;
+        }
+    }
+    else if(mycmp(parv[1], "LIST") == 0)
+    {
+        if(parc > 2 && hunt_server(cptr, sptr, ":%s MODULE %s %s", 2,
+                       parc, parv) != HUNTED_ISME)
+            return 0;
+
+        list_modules(sptr);
+        sendto_one(sptr, ":%s NOTICE %s :--- End of module list ---",
+                   me.name, sptr->name);
+    }
+    else if(mycmp(parv[1], "HOOKS") == 0)
+    {
+        if(parc > 2 && hunt_server(cptr, sptr, ":%s MODULE %s %s", 2,
+                                   parc, parv) != HUNTED_ISME)
+            return 0;
+
+        list_hooks(sptr);
+        sendto_one(sptr, ":%s NOTICE %s :--- End of hook list ---",
+                   me.name, sptr->name);
+    }
+    else if(mycmp(parv[1], "CMD") == 0)
+    {
+        aModule *themod;
+        if(!(MyClient(sptr) && IsAdmin(sptr)))
+        {
+            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            return 0;
+        }
+        if(BadPtr(parv[2]))
+        {
+            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name,
+                       parv[0], "MODULE");
+            return 0;
+        }
+        themod = find_module(parv[2]);
+        if(!themod)
+        {
+            sendto_one(sptr, ":%s NOTICE %s :Module %s not found for cmd",
+                       me.name, sptr->name, parv[2]);
+            return 0;
+        }
+        return (*themod->module_command) (sptr, parc - 2, parv + 2);
+    }
+    else if(parc > 2 && mycmp(parv[1], "CGLOBAL") == 0)
+    {
+        char pbuf[512];
+        aModule *themod;
+
+        if(!(IsServer(sptr) || IsULine(sptr)))
+            return 0;
+
+        themod = find_module(parv[2]);
+
+        /* Pass this on to all servers! */
+        make_parv_copy(pbuf, parc, parv);
+        sendto_serv_butone(cptr, ":%s MODULE %s", parv[0], pbuf);
+
+        if(themod)
+            return (*themod->module_globalcommand) 
+                                            (cptr, sptr, parc - 2, parv + 2);
+    }
+    return 0;
+}
+
+
+/* module memory functions */
+
+char *
+bircmodule_strdup(char *string)
+{
+    char *ret = MyMalloc(strlen(string) + 1);
+    strcpy(ret, string);
+    return ret;
+}
+
+void *
+bircmodule_malloc(int size)
+{
+    return MyMalloc(size);
+}
+
+void 
+bircmodule_free(void *p)
+{
+    MyFree(p);
+}
+
+/* hook functions */
+
+typedef struct module_hook 
+{
+    aModule *owner;
+    void *funcptr;
+    int hooktype;
+} aHook;
+
+static DLink *preaccess_hooks = NULL;
+static DLink *postaccess_hooks = NULL;
+static DLink *postmotd_hooks = NULL;
+static DLink *msg_hooks = NULL;
+static DLink *chanmsg_hooks = NULL;
+static DLink *usermsg_hooks = NULL;
+static DLink *mymsg_hooks = NULL;
+static DLink *every10_hooks = NULL;
+static DLink *signoff_hooks = NULL;
+static DLink *mload_hooks = NULL;
+static DLink *munload_hooks = NULL;
+
+static DLink *all_hooks = NULL;
+
+char *
+get_texthooktype(enum c_hooktype hooktype)
+{
+    static char ubuf[32];
+
+    switch(hooktype)
+    {
+        case CHOOK_10SEC:
+            return "10 seconds";
+
+        case CHOOK_PREACCESS:
+            return "Pre-access";
+
+        case CHOOK_POSTACCESS:
+            return "Post-access";
+
+        case CHOOK_POSTMOTD:
+            return "Post-MOTD";
+
+        case CHOOK_MSG:
+            return "Message";
+
+        case CHOOK_CHANMSG:
+            return "Channel Message";
+
+        case CHOOK_USERMSG:
+            return "User targeted Message";
+
+        case CHOOK_MYMSG:
+            return "Message to me";
+
+        case CHOOK_SIGNOFF:
+            return "Signoff";
+
+        case MHOOK_LOAD:
+            return "Module load";
+
+        case MHOOK_UNLOAD:
+            return "Module unload";
+
+        default:
+            ircsnprintf(ubuf, 32, "Unknown (%d)", hooktype);
+            return ubuf;
+    }
+}
+
+DLink **
+get_hooklist(enum c_hooktype hooktype)
+{
+    DLink **hooklist;
+
+    switch(hooktype)
+    {
+        case CHOOK_10SEC:
+            hooklist = &every10_hooks;
+            break;
+
+        case CHOOK_PREACCESS:
+            hooklist = &preaccess_hooks;
+            break;
+
+        case CHOOK_POSTACCESS:
+            hooklist = &postaccess_hooks;
+            break;
+
+        case CHOOK_POSTMOTD:
+            hooklist = &postmotd_hooks;
+            break;
+
+        case CHOOK_MSG:
+            hooklist = &msg_hooks;
+            break;
+
+        case CHOOK_CHANMSG:
+            hooklist = &chanmsg_hooks;
+            break;
+
+        case CHOOK_USERMSG:
+            hooklist = &usermsg_hooks;
+            break;
+
+        case CHOOK_MYMSG:
+            hooklist = &mymsg_hooks;
+            break;
+
+        case CHOOK_SIGNOFF:
+            hooklist = &signoff_hooks;
+            break;
+
+        case MHOOK_LOAD:
+            hooklist = &mload_hooks;
+            break;
+
+        case MHOOK_UNLOAD:
+            hooklist = &munload_hooks;
+            break;
+
+        default:
+            return NULL;
+    }
+    return hooklist;
+}
+
+void 
+drop_all_hooks(aModule *owner)
+{
+    DLink *lp, *lpn, **hooklist;
+
+    for(lp = all_hooks; lp; lp = lpn)
+    {
+        aHook *hk = (aHook *) lp->value.cp;
+
+        lpn = lp->next;
+
+        if(hk->owner == owner)
+        {
+            sendto_realops_lev(DEBUG_LEV, "Module cleanup: removing hook [%s]"
+                            " for opaque %d", get_texthooktype(hk->hooktype), 
+                            (int) owner);
+
+            hooklist = get_hooklist((enum c_hooktype) hk->hooktype);
+
+            remove_from_list(hooklist, hk, NULL);
+            remove_from_list(&all_hooks, hk, NULL);
+            bircmodule_free(hk);
+        }
+    }
+}
+
+void *
+bircmodule_add_hook(enum c_hooktype hooktype, void *opaque, void *funcptr)
+{
+    DLink **hooklist;
+    aHook *hk;
+    aModule *owner;
+
+    if(!(owner = find_module_opaque(opaque)))
+    {
+        sendto_realops_lev(DEBUG_LEV, "Module tried to add hooktype %d with"
+                         " unknown opaque 0x%x", (int) hooktype, (int) opaque);
+        return NULL;
+    }
+
+    if((hooklist = get_hooklist(hooktype)) == NULL)
+        return NULL;
+
+    hk = (aHook *) bircmodule_malloc(sizeof(aHook));
+    hk->owner = owner;
+    hk->funcptr = funcptr;
+    hk->hooktype = (int) hooktype;
+
+    add_to_list(&all_hooks, hk);
+    add_to_list(hooklist, hk);
+
+    return (void *) hk;
+}
+
+void 
+bircmodule_del_hook(void *opaque)
+{
+    DLink *lp, *lpn, **hooklist;
+
+    for(lp = all_hooks; lp; lp = lpn)
+    {
+        aHook *hk = (aHook *) lp->value.cp;
+
+        lpn = lp->next;
+
+        if((void *) hk == opaque)
+        {
+            hooklist = get_hooklist((enum c_hooktype) hk->hooktype);
+
+            remove_from_list(hooklist, hk, NULL);
+            remove_from_list(&all_hooks, hk, NULL);
+            bircmodule_free(hk);
+        }
+    }
+}
+
+int 
+call_hooks(enum c_hooktype hooktype, ...)
+{
+    va_list vl;
+    int ret = 0;
+    DLink *lp;
+
+    va_start(vl, hooktype);
+
+    switch(hooktype)
+    {
+        case CHOOK_10SEC:
+            for(lp = every10_hooks; lp; lp = lp->next)
+            {
+                void (*rfunc) () = ((aHook *)lp->value.cp)->funcptr;
+                (*rfunc)();
+            }
+            break;
+
+        case CHOOK_PREACCESS:
+            {
+                aClient *acptr = va_arg(vl, aClient *);
+
+                for(lp = preaccess_hooks; lp; lp = lp->next)
+                {
+                    int (*rfunc) (aClient *) = ((aHook *)lp->value.cp)->funcptr;
+                    if((ret = (*rfunc)(acptr)) == FLUSH_BUFFER)
+                        break;
+                }
+                break;
+            }
+
+        case CHOOK_POSTACCESS:
+            {
+                aClient *acptr = va_arg(vl, aClient *);
+
+                for(lp = postaccess_hooks; lp; lp = lp->next)
+                {
+                    int (*rfunc) (aClient *) = ((aHook *)lp->value.cp)->funcptr;
+                    if((ret = (*rfunc)(acptr)) == FLUSH_BUFFER)
+                        break;
+                }
+                break;
+            }
+
+        case CHOOK_POSTMOTD:
+            {
+                aClient *acptr = va_arg(vl, aClient *);
+
+                for(lp = postmotd_hooks; lp; lp = lp->next)
+                {
+                    int (*rfunc) (aClient *) = ((aHook *)lp->value.cp)->funcptr;
+                    if((ret = (*rfunc)(acptr)) == FLUSH_BUFFER)
+                        break;
+                }
+                break;
+            }
+
+        case CHOOK_MSG:
+            {
+                aClient *acptr = va_arg(vl, aClient *);
+                int aint = va_arg(vl, int);
+                char *txtptr = va_arg(vl, char *);
+
+                for(lp = msg_hooks; lp; lp = lp->next)
+                {
+                    int (*rfunc) (aClient *, int, char *) = 
+                                    ((aHook *)lp->value.cp)->funcptr;
+                    if((ret = (*rfunc)(acptr, aint, txtptr)) == FLUSH_BUFFER)
+                        break;
+                }
+                break;
+            }
+
+        case CHOOK_CHANMSG:
+            {
+                aClient *acptr = va_arg(vl, aClient *);
+                aChannel *chptr = va_arg(vl, aChannel *);
+                int aint = va_arg(vl, int);
+                char *txtptr = va_arg(vl, char *);
+
+                for(lp = chanmsg_hooks; lp; lp = lp->next)
+                {
+                    int (*rfunc) (aClient *, aChannel *, int, char *) =
+                                  ((aHook *)lp->value.cp)->funcptr;
+                    if((ret = (*rfunc)(acptr, chptr, aint, txtptr)) 
+                                    == FLUSH_BUFFER)
+                        break;
+                }
+                break;
+            }
+
+        case CHOOK_USERMSG:
+            {
+                aClient *acptr = va_arg(vl, aClient *);
+                aClient *dcptr = va_arg(vl, aClient *);
+                int aint = va_arg(vl, int);
+                char *txtptr = va_arg(vl, char *);
+
+                for(lp = usermsg_hooks; lp; lp = lp->next)
+                {
+                    int (*rfunc) (aClient *, aClient *, int, char *) =
+                                 ((aHook *)lp->value.cp)->funcptr;
+                    if((ret = (*rfunc)(acptr, dcptr, aint, txtptr))
+                                    == FLUSH_BUFFER)
+                        break;
+                }
+                break;
+            }
+
+        case CHOOK_MYMSG:
+            {
+                aClient *acptr = va_arg(vl, aClient *);
+                int aint = va_arg(vl, int);
+                char *txtptr = va_arg(vl, char *);
+    
+                for(lp = mymsg_hooks; lp; lp = lp->next)
+                {  
+                    int (*rfunc) (aClient *, int, char *) = 
+                                 ((aHook *)lp->value.cp)->funcptr;
+                    if((ret = (*rfunc)(acptr, aint, txtptr)) == FLUSH_BUFFER)
+                        break;
+                }
+                break;
+            }
+
+        case CHOOK_SIGNOFF:
+            {
+                aClient *acptr = va_arg(vl, aClient *);
+                for(lp = signoff_hooks; lp; lp = lp->next)
+                {
+                    void (*rfunc) (aClient *) = 
+                                    ((aHook *)lp->value.cp)->funcptr;
+                    (*rfunc)(acptr);
+                }
+                break;
+            }
+
+        case MHOOK_LOAD:
+            {
+                char *txtptr = va_arg(vl, char *);
+                void *avoid = va_arg(vl, void *);
+
+                for(lp = mload_hooks; lp; lp = lp->next)
+                {
+                    int (*rfunc) (char *, void *) = 
+                                ((aHook *)lp->value.cp)->funcptr;
+                    (*rfunc)(txtptr, avoid);
+                }
+                break;
+            }
+
+        case MHOOK_UNLOAD:
+            {
+                char *txtptr = va_arg(vl, char *);
+                void *avoid = va_arg(vl, void *);
+                for(lp = munload_hooks; lp; lp = lp->next)
+                {
+                    int (*rfunc) (char *, void *) = 
+                                  ((aHook *)lp->value.cp)->funcptr;
+                    (*rfunc)(txtptr, avoid);
+                }
+                break;
+            }
+      
+        default:
+            sendto_realops_lev(DEBUG_LEV, "Call for unknown hook type %d", 
+                hooktype);
+            break;
+    }   
+    va_end(vl);
+    return ret;
+}
+
+void 
+list_hooks(aClient *sptr)
+{
+    DLink *lp;
+
+    for(lp = all_hooks; lp; lp = lp->next)
+    {
+        aHook *hook = (aHook *) lp->value.cp;
+        aModule *mod = hook->owner;
+
+        sendto_one(sptr, ":%s NOTICE %s :Module: %s  Type: %s",
+                   me.name, sptr->name, mod->name, 
+                   get_texthooktype(hook->hooktype));
+    }
+}
+
+int init_modules()
+{
+    int i;
+
+    if(!modules)
+        return 0;
+
+    for(i = 0; modules->autoload[i]; i++)
+    {
+        load_module(NULL, modules->autoload[i]);
+        printf("Module %s Loaded Successfully.\n", modules->autoload[i]);
+    }
+    return 0;
+}
+#endif
diff --git a/src/packet.c b/src/packet.c
new file mode 100644 (file)
index 0000000..4efc920
--- /dev/null
@@ -0,0 +1,255 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/packet.c
+ *   Copyright (C) 1990  Jarkko Oikarinen and
+ *                       University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: packet.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "msg.h"
+#include "h.h"
+#include "dh.h"
+#include "zlink.h"
+
+/*
+ * * dopacket 
+ * cptr - pointer to client structure for which the buffer
+ * data applies. 
+ * buffer - pointr to the buffer containing the newly read data 
+ * length - number of valid bytes of data in the buffer
+ * 
+ * Note: 
+ * It is implicitly assumed that dopacket is called only
+ * with cptr of "local" variation, which contains all the
+ * necessary fields (buffer etc..)
+ */
+int dopacket(aClient *cptr, char *buffer, int length)
+{
+    char   *ch1;
+    char   *ch2;
+    char *cptrbuf = cptr->buffer;
+    aListener    *lptr = cptr->lstn;
+    char *nbuf = NULL;
+    int nlen;
+    
+#ifdef HAVE_ENCRYPTION_ON
+    if(IsRC4IN(cptr))
+       rc4_process_stream(cptr->serv->rc4_in, buffer, length);
+#endif
+    
+    me.receiveB += length;        /* Update bytes received */
+    cptr->receiveB += length;
+    
+    if (cptr->receiveB & 0x0400) 
+    {
+       cptr->receiveK += (cptr->receiveB >> 10);
+       cptr->receiveB &= 0x03ff;  /* 2^10 = 1024, 3ff = 1023 */
+    }
+
+    if (lptr) 
+    {
+       lptr->receiveB += length;
+       if (lptr->receiveB & 0x0400)
+       {
+           lptr->receiveK += (lptr->receiveB >> 10);
+           lptr->receiveB &= 0x03ff;
+       }
+    }
+    else if (me.receiveB & 0x0400)
+    {
+       me.receiveK += (me.receiveB >> 10);
+       me.receiveB &= 0x03ff;
+    }
+    
+zcontinue:
+    ch1 = cptrbuf + cptr->count;
+    ch2 = buffer;   
+    
+    if(ZipIn(cptr))
+    {
+       int err;
+       ch2 = zip_input(cptr->serv->zip_in, ch2, &length, &err, &nbuf, &nlen);
+
+       if(length == -1)
+       {
+           sendto_realops("Zipin error for %s: (%d) %s\n", cptr->name,
+                          err, ch2);
+           return exit_client(cptr, cptr, &me, "fatal error in zip_input!");
+       }
+    }
+    
+    while (--length >= 0) 
+    {
+       char g;
+       
+       g = (*ch1 = *ch2++);
+       /*
+        * Yuck.  Stuck.  To make sure we stay backward compatible, we
+        * must assume that either CR or LF terminates the message and
+        * not CR-LF.  By allowing CR or LF (alone) into the body of
+        * messages, backward compatibility is lost and major problems
+        * will arise. - Avalon
+        */
+       if (g < '\16' && (g == '\n' || g == '\r')) {
+           if (ch1 == cptrbuf)
+               continue;               /* Skip extra LF/CR's */
+           *ch1 = '\0';
+           me.receiveM += 1;   /* Update messages received */
+           cptr->receiveM += 1;
+           if (lptr)
+               lptr->receiveM += 1;
+           cptr->count = 0;    /*
+                                * ...just in case parse returns with
+                                * FLUSH_BUFFER without removing the
+                                * structure pointed by cptr... --msa 
+                                */
+           switch (parse(cptr, cptr->buffer, ch1))
+           {
+           case FLUSH_BUFFER:
+               return FLUSH_BUFFER;
+               
+           case ZIP_NEXT_BUFFER:
+               if(length)
+               {
+                   int err;
+                   ch2 = zip_input(cptr->serv->zip_in, ch2, &length,
+                                   &err, &nbuf, &nlen);
+                   
+                   if(length == -1)
+                   {
+                       sendto_realops("Zipin error for %s: (%d) %s\n",
+                                      cptr->name, err, ch2);
+                       return exit_client(cptr, cptr, &me,
+                                          "fatal error in zip_input!");
+                   }
+               }
+               break;
+
+#ifdef HAVE_ENCRYPTION_ON
+           case RC4_NEXT_BUFFER:
+               if(length)
+                   rc4_process_stream(cptr->serv->rc4_in, ch2, length);
+               break;
+#endif
+
+           default:
+               break;
+           }
+           
+           /*
+            * Socket is dead so exit (which always returns with *
+            * FLUSH_BUFFER here).  - avalon
+            */
+           if (cptr->flags & FLAGS_DEADSOCKET)
+               return exit_client(cptr, cptr, &me,
+                                  (cptr->flags & FLAGS_SENDQEX) ?
+                                  "SendQ exceeded" : "Dead socket");
+           ch1 = cptrbuf;
+       }
+       else if (ch1 < cptrbuf + (sizeof(cptr->buffer) - 1))
+           ch1++;                      /* There is always room for the null */
+    }
+    cptr->count = ch1 - cptrbuf;
+    
+    if(nbuf)
+    {
+#if 0   /* this message is annoying and not quite that useful */
+       static time_t last_complain = 0;
+       static int numrepeat = 0;
+       
+       numrepeat++;
+       
+       if(NOW > (last_complain + 300)) /* if more than 5 mins have elapsed */
+       {
+           if(last_complain == 0)
+           {
+               sendto_realops("Overflowed zipInBuf! "
+                              "If you see this a lot, you should increase "
+                              "zipInBufSize in src/zlink.c.");
+           }
+           else
+           {
+               sendto_realops("Overflowed zipInBuf %d time%s in the "
+                              "last %d minutes. If you see this a lot, you "
+                              "should increase zipInBufSize in src/zlink.c.",
+                              numrepeat, numrepeat == 1 ? "" : "s",
+                              (NOW - last_complain) / 60);
+           }
+           last_complain = NOW;
+           numrepeat = 0;
+       }
+#endif
+
+       buffer = nbuf;
+       length = nlen;
+       nbuf = NULL;
+       goto zcontinue; /* gross, but it should work.. */
+    }   
+    
+    return 0;
+}
+
+int client_dopacket(aClient *cptr, char *buffer, int length)
+{
+    
+    strncpy(cptr->buffer, buffer, BUFSIZE);
+    length = strlen(cptr->buffer);
+    
+    /* Update messages received */
+    ++me.receiveM;
+    ++cptr->receiveM;
+   
+    /* Update bytes received */
+    cptr->receiveB += length;
+   
+    if (cptr->receiveB > 1023)
+    {
+       cptr->receiveK += (cptr->receiveB >> 10);
+       cptr->receiveB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */
+    }
+    me.receiveB += length;
+    
+    if (me.receiveB > 1023)
+    {
+       me.receiveK += (me.receiveB >> 10);
+       me.receiveB &= 0x03ff;
+    }
+    
+    cptr->count = 0;    /* ...just in case parse returns with */
+    if (FLUSH_BUFFER == parse(cptr, cptr->buffer, cptr->buffer + length))
+    {
+       /*
+        * CLIENT_EXITED means actually that cptr
+        * structure *does* not exist anymore!!! --msa
+        */
+       return FLUSH_BUFFER;
+    }
+    else if (cptr->flags & FLAGS_DEADSOCKET)
+    {
+       /*
+        * Socket is dead so exit (which always returns with
+        * CLIENT_EXITED here).  - avalon
+        */
+       return exit_client(cptr, cptr, &me,
+                          (cptr->flags & FLAGS_SENDQEX) ?
+                          "SendQ exceeded" : "Dead socket");
+    }
+    return 1;
+}
diff --git a/src/parse.c b/src/parse.c
new file mode 100644 (file)
index 0000000..225ab44
--- /dev/null
@@ -0,0 +1,611 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/parse.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: parse.c,v 1.1 2005/01/12 07:44:56 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#define MSGTAB
+#include "msg.h"
+#undef MSGTAB
+#include "sys.h"
+#include "numeric.h"
+#include "h.h"
+
+#if defined( HAVE_STRING_H )
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+/* NOTE: parse() should not be called recursively by other functions! */
+static char *para[MAXPARA + 1];
+static int  cancel_clients(aClient *, aClient *, char *);
+static void remove_unknown(aClient *, char *, char *);
+
+static char sender[HOSTLEN + 1];
+static int  cancel_clients(aClient *, aClient *, char *);
+static void remove_unknown(aClient *, char *, char *);
+
+static struct Message *do_msg_tree(MESSAGE_TREE *, char *, struct Message *);
+static struct Message *tree_parse(char *);
+
+int num_msg_trees = 0;
+
+/*
+ * parse a buffer.
+ * 
+ * NOTE: parse() should not be called recusively by any other functions!
+ */
+
+int parse(aClient *cptr, char *buffer, char *bufend)
+{
+    aClient *from = cptr;
+    char *ch, *s;
+    int i, numeric = 0, paramcount;
+    struct Message *mptr;
+
+#ifdef DUMP_DEBUG
+    if(dumpfp!=NULL) 
+    {
+       fprintf(dumpfp, "<- %s: %s\n", (cptr->name ? cptr->name : "*"),
+               buffer);
+       fflush(dumpfp);
+    }
+#endif
+    Debug((DEBUG_DEBUG, "Parsing %s: %s", get_client_name(cptr, TRUE),
+          buffer));
+    
+    if (IsDead(cptr))
+       return -1;
+    
+    s = sender;
+    *s = '\0';
+    
+    for (ch = buffer; *ch == ' '; ch++);       /* skip spaces */
+    
+    para[0] = from->name;
+    if (*ch == ':') 
+    {
+       /*
+        * Copy the prefix to 'sender' assuming it terminates with
+        * SPACE (or NULL, which is an error, though).
+        */
+       
+       for (++ch; *ch && *ch != ' '; ++ch)
+           if (s < (sender + HOSTLEN))
+               *s++ = *ch;             
+       *s = '\0';
+       
+       /*
+        * Actually, only messages coming from servers can have the
+        * prefix--prefix silently ignored, if coming from a user
+        * client...
+        * 
+        * ...sigh, the current release "v2.2PL1" generates also null
+        * prefixes, at least to NOTIFY messages (e.g. it puts
+        * "sptr->nickname" as prefix from server structures where it's
+        * null--the following will handle this case as "no prefix" at
+        * all --msa  (": NOTICE nick ...")
+        */
+       
+       if (*sender && IsServer(cptr)) 
+       {
+           from = find_client(sender, (aClient *) NULL);
+
+           /*
+            * okay, this doesn't seem to do much here.
+            * from->name _MUST_ be equal to sender. 
+            * That's what find_client does.
+            * find_client will find servers too, and since we don't use server
+            * masking, the find server call is useless (and very wasteful).
+            * now, there HAS to be a from and from->name and
+            * sender have to be the same
+            * for us to get to the next if. but the next if
+            * starts out with if(!from)
+            * so this is UNREACHABLE CODE! AGH! - lucas
+            *
+            *  if (!from || mycmp(from->name, sender))
+            *     from = find_server(sender, (aClient *) NULL);
+            *  else if (!from && strchr(sender, '@'))
+            *     from = find_nickserv(sender, (aClient *) NULL);
+            */
+
+           para[0] = sender;
+           /*
+            * Hmm! If the client corresponding to the prefix is not
+            * found--what is the correct action??? Now, I will ignore the
+            * message (old IRC just let it through as if the prefix just
+            * wasn't there...) --msa
+            */
+           if (!from) 
+           {
+               Debug((DEBUG_ERROR, "Unknown prefix (%s)(%s) from (%s)",
+                      sender, buffer, cptr->name));
+
+               ircstp->is_unpf++;
+               remove_unknown(cptr, sender, buffer);
+                
+               return -1;
+           }
+
+           if (from->from != cptr) 
+           {
+               ircstp->is_wrdi++;
+               Debug((DEBUG_ERROR, "Message (%s) coming from (%s)",
+                      buffer, cptr->name));
+               
+               return cancel_clients(cptr, from, buffer);
+           }
+       }
+       while (*ch == ' ')
+           ch++;
+    }
+
+    if (*ch == '\0') 
+    {
+       ircstp->is_empt++;
+       Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
+              cptr->name, from->name));
+       return (-1);
+    }
+    /*
+     * Extract the command code from the packet.  Point s to the end
+     * of the command code and calculate the length using pointer
+     * arithmetic.  Note: only need length for numerics and *all*
+     * numerics must have parameters and thus a space after the command
+     * code. -avalon
+     * 
+     * ummm???? - Dianora
+     */
+
+    /* check for numeric */
+    if (*(ch + 3) == ' ' && IsDigit(*ch) && IsDigit(*(ch + 1)) &&
+       IsDigit(*(ch + 2))) 
+    {
+       mptr = (struct Message *) NULL;
+       numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') *
+           10 + (*(ch + 2) - '0');
+       paramcount = MAXPARA;
+       ircstp->is_num++;
+       s = ch + 3;
+       *s++ = '\0';
+    }
+    else 
+    {
+       s = strchr(ch, ' ');
+       
+       if (s)
+           *s++ = '\0';
+       
+       mptr = tree_parse(ch);
+       
+       if (!mptr || !mptr->cmd) 
+       {
+           /*
+            * only send error messages to things that actually sent
+            * buffers to us and only people, too.
+            */
+           if (buffer[0] != '\0') 
+           {
+               if (IsPerson(from))
+                   sendto_one(from, ":%s %d %s %s :Unknown command",
+                              me.name, ERR_UNKNOWNCOMMAND, from->name, ch);
+               Debug((DEBUG_ERROR, "Unknown (%s) from %s",
+                      ch, get_client_name(cptr, TRUE)));
+           }
+           ircstp->is_unco++;
+           return -1;
+       }
+       
+       paramcount = mptr->parameters;
+       i = bufend - ((s) ? s : ch);
+       mptr->bytes += i;
+       /*
+        * Allow only 1 msg per 2 seconds (on average) to prevent
+        * dumping. to keep the response rate up, bursts of up to 5 msgs
+        * are allowed -SRB Opers can send 1 msg per second, burst of ~20
+        * -Taner
+        */
+       if ((mptr->flags & 1) && !(IsServer(cptr))) 
+       {
+        if (!NoMsgThrottle(cptr))
+        {
+#ifdef NO_OPER_FLOOD
+            if (IsAnOper(cptr))
+                /* "randomly" (weighted) increase the since */
+                cptr->since += (cptr->receiveM % 10) ? 1 : 0;
+            else
+#endif
+                cptr->since += (2 + i / 120);
+        }
+    }
+    }
+    /*
+     * Must the following loop really be so devious? On surface it
+     * splits the message to parameters from blank spaces. But, if
+     * paramcount has been reached, the rest of the message goes into
+     * this last parameter (about same effect as ":" has...) --msa
+     */
+
+    /* Note initially true: s==NULL || *(s-1) == '\0' !! */
+    
+    i = 1;
+    if (s) 
+    {
+       if (paramcount > MAXPARA)
+           paramcount = MAXPARA;
+       for (;;) 
+       {
+           while (*s == ' ')
+               *s++ = '\0';
+           
+           if (*s == '\0')
+               break;
+           if (*s == ':') 
+           {
+               /* The rest is a single parameter */
+               para[i++] = s + 1;
+               break;
+           }
+           para[i++] = s;
+           if (i >= paramcount)
+            {
+                if(paramcount == MAXPARA && strchr(s, ' '))
+                {
+                   sendto_realops_lev(DEBUG_LEV, "Overflowed MAXPARA on %s from %s",
+                          mptr ? mptr->cmd : "numeric",
+                          get_client_name(cptr, (IsServer(cptr) ? HIDEME : TRUE)));
+                }
+               break;
+            }
+           
+           while(*s && *s != ' ')
+               s++;
+       }
+    }
+    
+    para[i] = NULL;
+    if (mptr == (struct Message *) NULL)
+       return (do_numeric(numeric, cptr, from, i, para));
+    
+    mptr->count++;
+    
+    /* patch to avoid server flooding from unregistered connects */
+    /*
+     * check allow_unregistered_use flag I've set up instead of function
+     * comparing *yech* - Dianora
+     */
+    
+    if (!IsRegistered(cptr) && !mptr->allow_unregistered_use) {
+       sendto_one(from, ":%s %d %s %s :Register first.",
+                  me.name, ERR_NOTREGISTERED, from->name, ch);
+       return -1;
+    }
+    
+    if (IsRegisteredUser(cptr) && mptr->reset_idle)
+       from->user->last = timeofday;
+    
+    return (*mptr->func) (cptr, from, i, para);
+}
+
+/*
+ * init_tree_parse()
+ * 
+ * inputs               - pointer to msg_table defined in msg.h output
+ *  NONE side effects   - MUST MUST be called at startup ONCE before
+ * any other keyword hash routine is used.
+ * 
+ * -Dianora, orabidoo
+ */
+
+/* for qsort'ing the msgtab in place -orabidoo */
+static int mcmp(struct Message *m1, struct Message *m2)
+{
+    return strcmp(m1->cmd, m2->cmd);
+}
+
+/* Initialize the msgtab parsing tree -orabidoo */
+void init_tree_parse(struct Message *mptr)
+{
+    int i;
+    struct Message *mpt = mptr;
+    
+    for (i = 0; mpt->cmd; mpt++)
+       i++;
+    qsort((void *) mptr, i, sizeof(struct Message),
+         (int (*)(const void *, const void *)) mcmp);
+    
+    msg_tree_root = (MESSAGE_TREE *) MyMalloc(sizeof(MESSAGE_TREE));
+    num_msg_trees++;
+    mpt = do_msg_tree(msg_tree_root, "", mptr);
+    /*
+     * this happens if one of the msgtab entries included characters
+     * other than capital letters  -orabidoo
+     */
+    if (mpt->cmd)
+    {
+       fprintf(stderr, "bad msgtab entry: ``%s''\n", mpt->cmd);
+       exit(1);
+    }
+}
+
+/* Recursively make a prefix tree out of the msgtab -orabidoo */
+static struct Message *do_msg_tree(MESSAGE_TREE * mtree, char *prefix,
+                                  struct Message *mptr)
+{
+    char newpref[64];  /* must be longer than any command */
+    int c, c2, lp;
+    MESSAGE_TREE *mtree1;
+    
+    lp = strlen(prefix);
+    if (!lp || !strncmp(mptr->cmd, prefix, lp))
+    {
+       if (!mptr[1].cmd || (lp && strncmp(mptr[1].cmd, prefix, lp)))
+       {
+           /* non ambiguous, make a final case */
+           mtree->final = mptr->cmd + lp;
+           mtree->msg = mptr;
+           for (c = 0; c <= 25; c++)
+               mtree->pointers[c] = NULL;
+           return mptr + 1;
+       }
+       else
+       {
+           /* ambigous, make new entries for each of the letters that match */
+           if (!mycmp(mptr->cmd, prefix))
+           {
+               mtree->final = (void *) 1;
+               mtree->msg = mptr;
+               mptr++;
+           }
+           else
+               mtree->final = NULL;
+           
+           for (c = 'A'; c <= 'Z'; c++)
+           {
+               if (mptr->cmd[lp] == c)
+               {
+                   mtree1 = (MESSAGE_TREE *) MyMalloc(sizeof(MESSAGE_TREE));
+                   num_msg_trees++;
+                   mtree1->final = NULL;
+                   mtree->pointers[c - 'A'] = mtree1;
+                   strcpy(newpref, prefix);
+                   newpref[lp] = c;
+                   newpref[lp + 1] = '\0';
+                   mptr = do_msg_tree(mtree1, newpref, mptr);
+                   if (!mptr->cmd || strncmp(mptr->cmd, prefix, lp))
+                   {
+                       for (c2 = c + 1 - 'A'; c2 <= 25; c2++)
+                           mtree->pointers[c2] = NULL;
+                       return mptr;
+                   }
+               }
+               else
+               {
+                   mtree->pointers[c - 'A'] = NULL;
+               }
+           }
+           return mptr;
+       }
+    }
+    else
+    {
+       fprintf(stderr, "do_msg_tree: this should never happen!\n");
+       exit(1);
+    }
+}
+
+/*
+ * tree_parse()
+ * 
+ * inputs               
+ * - pointer to command in upper case output NULL pointer if not found 
+ * struct Message pointer to command entry if found 
+ * side effects        - NONE
+ * 
+ * -Dianora, orabidoo
+ */
+static struct Message *tree_parse(char *cmd)
+{
+    char    r;
+    MESSAGE_TREE *mtree = msg_tree_root;
+    
+    while ((r = *cmd++))
+    {
+       r &= 0xdf;              /*
+                                * some touppers have trouble w/ 
+                                * lowercase, says Dianora 
+                                */
+       if (r < 'A' || r > 'Z')
+           return NULL;
+       mtree = mtree->pointers[r - 'A'];
+       if (!mtree)
+           return NULL;
+       if (mtree->final == (void *) 1)
+       {
+           if (!*cmd)
+               return mtree->msg;
+       }
+       else if (mtree->final && !mycmp(mtree->final, cmd))
+           return mtree->msg;
+    }
+    return ((struct Message *) NULL);
+}
+
+/* field breakup for ircd.conf file. */
+char *getfield(char *newline)
+{
+    static char *line = (char *) NULL;
+    char       *end, *field;
+
+    if (newline)
+       line = newline;
+    
+    if (line == (char *) NULL)
+       return ((char *) NULL);
+    
+    field = line;
+    if ((end = strchr(line, ':')) == NULL)
+    {
+       line = (char *) NULL;
+       if ((end = strchr(field, '\n')) == (char *) NULL)
+           end = field + strlen(field);
+    }
+    else
+       line = end + 1;
+    *end = '\0';
+    return (field);
+}
+
+static int cancel_clients(aClient *cptr, aClient *sptr, char *cmd)
+{
+    /*
+     * kill all possible points that are causing confusion here, I'm not
+     * sure I've got this all right... - avalon
+     * 
+     * knowing avalon, probably not.
+     */
+    /*
+     * with TS, fake prefixes are a common thing, during the connect
+     * burst when there's a nick collision, and they must be ignored
+     * rather than killed because one of the two is surviving.. so we
+     * don't bother sending them to all ops everytime, as this could
+     * send 'private' stuff from lagged clients. we do send the ones
+     * that cause servers to be dropped though, as well as the ones
+     * from * non-TS servers -orabidoo
+     */
+    /*
+     * Incorrect prefix for a server from some connection.  If it is a
+     * client trying to be annoying, just QUIT them, if it is a server
+     * then the same deal.
+    */
+    if (IsServer(sptr) || IsMe(sptr))
+    {
+       /* Sorry, but at the moment this is just too much for even opers 
+          to see. -Rak */
+       /* or we could just take out the message. <EG>  -wd */
+       sendto_realops_lev(DEBUG_LEV, "Message for %s[%s] from %s",
+                          sptr->name, sptr->from->name,
+                          get_client_name(cptr, 
+                                          (IsServer(cptr) ? HIDEME : TRUE)));
+       if (IsServer(cptr))
+       {
+           sendto_realops_lev(DEBUG_LEV,
+                              "Not dropping server %s (%s) for "
+                              "Fake Direction", cptr->name, sptr->name);
+           return -1;
+       }
+       
+       if (IsClient(cptr))
+           sendto_realops_lev(DEBUG_LEV,
+                              "Would have dropped client %s (%s@%s) "
+                              "[%s from %s]", cptr->name, 
+                              cptr->user->username, cptr->user->host,
+                              cptr->user->server, cptr->from->name);
+       return -1;
+    }
+    /*
+     * Ok, someone is trying to impose as a client and things are
+     * confused.  If we got the wrong prefix from a server, send out a
+     * kill, else just exit the lame client.
+     */
+    if (IsServer(cptr))
+    {
+       /*
+        * If the fake prefix is coming from a TS server, discard it
+        * silently -orabidoo
+        * also drop it if we're gonna kill services by not doing so }:/
+        */
+       if (DoesTS(cptr))
+       {
+           if (sptr->user)
+               sendto_realops_lev(DEBUG_LEV,
+                                  "Message for %s[%s@%s!%s] from %s "
+                                  "(TS, ignored)", sptr->name,
+                                  sptr->user->username, sptr->user->host,
+                                  sptr->from->name,
+                                  get_client_name(cptr, TRUE));
+           return 0;
+       }
+       else
+       {
+           if (sptr->user)
+               sendto_realops_lev(DEBUG_LEV,
+                                  "Message for %s[%s@%s!%s] from %s",
+                                  sptr->name, sptr->user->username,
+                                  sptr->user->host,
+                                  sptr->from->name,
+                                  get_client_name(cptr, (IsServer(cptr) ? 
+                                                         HIDEME : TRUE)));
+           if(IsULine(sptr))
+           {
+               sendto_realops_lev(DEBUG_LEV,
+                                  "Would have killed U:lined client %s "
+                                  "for fake direction", sptr->name);
+               return 0;
+           }
+           sendto_serv_butone(NULL,
+                              ":%s KILL %s :%s (%s[%s] != %s, Fake Prefix)",
+                              me.name, sptr->name, me.name,
+                              sptr->name, sptr->from->name,
+                              get_client_name(cptr, (IsServer(cptr) ?
+                                                     HIDEME : TRUE)));
+           sptr->flags |= FLAGS_KILLED;
+           return exit_client(cptr, sptr, &me, "Fake Prefix");
+       }
+    }
+    return exit_client(cptr, cptr, &me, "Fake prefix");
+}
+
+static void remove_unknown(aClient *cptr, char *sender, char *buffer)
+{
+    if (!IsRegistered(cptr))
+       return;
+
+    if (IsClient(cptr))
+    {
+       sendto_realops_lev(DEBUG_LEV,
+                          "Weirdness: Unknown client prefix (%s) from %s, "
+                          "Ignoring %s", buffer,
+                          get_client_name(cptr, FALSE), sender);
+       return;
+    }
+    /* Not from a server so don't need to worry about it. */
+    if (!IsServer(cptr))
+       return;
+    /*
+     * Do kill if it came from a server because it means there is a
+     * ghost user on the other server which needs to be removed. -avalon
+     * Tell opers about this. -Taner
+     */
+    if (!strchr(sender, '.'))
+       sendto_one(cptr, ":%s KILL %s :%s (%s(?) <- %s)",
+                  me.name, sender, me.name, sender,
+                  get_client_name(cptr, FALSE));
+    else
+    {
+       sendto_realops_lev(DEBUG_LEV, 
+                          "Unknown prefix (%s) from %s, Squitting %s",
+                          buffer, get_client_name(cptr, HIDEME), sender);
+       sendto_one(cptr, ":%s SQUIT %s :(Unknown prefix (%s) from %s)",
+                  me.name, sender, buffer, get_client_name(cptr, HIDEME));
+    }
+}
diff --git a/src/pcre.c b/src/pcre.c
new file mode 100644 (file)
index 0000000..f479dcc
--- /dev/null
@@ -0,0 +1,8308 @@
+/*************************************************
+*      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 <ph10@cam.ac.uk>
+
+           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 DEBUG to get debugging output on stdout. */
+/* #define DEBUG */
+
+/* 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 DEBUG
+#define DPRINTF(p) printf p
+#else
+#define DPRINTF(p) /*nothing*/
+#endif
+
+/* Include the internals header, which itself includes "config.h", the Standard
+C headers, and the external pcre header. */
+
+#include "pcre_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 const 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. */
+
+#if !EBCDIC   /* This is the "normal" table for ASCII systems */
+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 */
+};
+
+#else         /* This is the "abnormal" table for EBCDIC systems */
+static const short int escapes[] = {
+/*  48 */     0,     0,      0,     '.',    '<',   '(',    '+',    '|',
+/*  50 */   '&',     0,      0,       0,      0,     0,      0,      0,
+/*  58 */     0,     0,    '!',     '$',    '*',   ')',    ';',    '~',
+/*  60 */   '-',   '/',      0,       0,      0,     0,      0,      0,
+/*  68 */     0,     0,    '|',     ',',    '%',   '_',    '>',    '?',
+/*  70 */     0,     0,      0,       0,      0,     0,      0,      0,
+/*  78 */     0,   '`',    ':',     '#',    '@',  '\'',    '=',    '"',
+/*  80 */     0,     7, -ESC_b,       0, -ESC_d, ESC_e,  ESC_f,      0,
+/*  88 */     0,     0,      0,     '{',      0,     0,      0,      0,
+/*  90 */     0,     0,      0,     'l',      0, ESC_n,      0,      0,
+/*  98 */     0, ESC_r,      0,     '}',      0,     0,      0,      0,
+/*  A0 */     0,   '~', -ESC_s, ESC_tee,      0,     0, -ESC_w,      0,
+/*  A8 */     0,-ESC_z,      0,       0,      0,   '[',      0,      0,
+/*  B0 */     0,     0,      0,       0,      0,     0,      0,      0,
+/*  B8 */     0,     0,      0,       0,      0,   ']',    '=',    '-',
+/*  C0 */   '{',-ESC_A, -ESC_B,  -ESC_C, -ESC_D,-ESC_E,      0, -ESC_G,
+/*  C8 */     0,     0,      0,       0,      0,     0,      0,      0,
+/*  D0 */   '}',     0,      0,       0,      0,     0,      0,      0,
+/*  D8 */-ESC_Q,     0,      0,       0,      0,     0,      0,      0,
+/*  E0 */  '\\',     0, -ESC_S,       0,      0,     0, -ESC_W,      0,
+/*  E8 */     0,-ESC_Z,      0,       0,      0,     0,      0,      0,
+/*  F0 */     0,     0,      0,       0,      0,     0,      0,      0,
+/*  F8 */     0,     0,      0,       0,      0,     0,      0,      0
+};
+#endif
+
+
+/* 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 *const 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 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. */
+
+#if !EBCDIC    /* This is the "normal" case, for ASCII systems */
+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 */
+
+#else          /* This is the "abnormal" case, for EBCDIC systems */
+static const unsigned char digitab[] =
+  {
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   0-  7  0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   8- 15    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  16- 23 10 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  24- 31    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  32- 39 20 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  40- 47    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  48- 55 30 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  56- 63    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*    - 71 40 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  72- |     */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  & - 87 50 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  88- Â¬     */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  - -103 60 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 104- ?     */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 70 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- "     */
+  0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* 128- g  80 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  h -143    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144- p  90 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  q -159    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160- x  A0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  y -175    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  ^ -183 B0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191    */
+  0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /*  { - G  C0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  H -207    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  } - P  D0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  Q -223    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  \ - X  E0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  Y -239    */
+  0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /*  0 - 7  F0 */
+  0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00};/*  8 -255    */
+
+static const unsigned char ebcdic_chartab[] = { /* chartable partial dup */
+  0x80,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /*   0-  7 */
+  0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00, /*   8- 15 */
+  0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /*  16- 23 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  24- 31 */
+  0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /*  32- 39 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  40- 47 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  48- 55 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  56- 63 */
+  0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*    - 71 */
+  0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80, /*  72- |  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  & - 87 */
+  0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00, /*  88- Â¬  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  - -103 */
+  0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x80, /* 104- ?  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- "  */
+  0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* 128- g  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  h -143 */
+  0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* 144- p  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  q -159 */
+  0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /* 160- x  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  y -175 */
+  0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  ^ -183 */
+  0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
+  0x80,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /*  { - G  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  H -207 */
+  0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  } - P  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  Q -223 */
+  0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /*  \ - X  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  Y -239 */
+  0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /*  0 - 7  */
+  0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x00};/*  8 -255 */
+#endif
+
+
+/* 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. When NO_RECURSE is set, these blocks
+are on the heap, not on the stack. */
+
+typedef struct eptrblock {
+  struct eptrblock *epb_prev;
+  const uschar *epb_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 four
+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
+#ifdef __cplusplus
+extern "C" void *(*pcre_malloc)(size_t) = malloc;
+extern "C" void  (*pcre_free)(void *) = free;
+extern "C" void *(*pcre_stack_malloc)(size_t) = malloc;
+extern "C" void  (*pcre_stack_free)(void *) = free;
+extern "C" int   (*pcre_callout)(pcre_callout_block *) = NULL;
+#else
+void *(*pcre_malloc)(size_t) = malloc;
+void  (*pcre_free)(void *) = free;
+void *(*pcre_stack_malloc)(size_t) = malloc;
+void  (*pcre_stack_free)(void *) = free;
+int   (*pcre_callout)(pcre_callout_block *) = NULL;
+#endif
+#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 "pcre_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 DEBUG
+#include "printint.c"
+#endif
+
+
+
+/*************************************************
+*          Return version string                 *
+*************************************************/
+
+#define STRING(a)  # a
+#define XSTRING(s) STRING(s)
+
+EXPORT 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
+*/
+
+EXPORT 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
+*/
+
+EXPORT 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 = (const pcre_study_data *)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
+*/
+
+EXPORT 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;
+
+  case PCRE_CONFIG_STACKRECURSE:
+#ifdef NO_RECURSE
+  *((int *)where) = 0;
+#else
+  *((int *)where) = 1;
+#endif
+  break;
+
+  default: return PCRE_ERROR_BADOPTION;
+  }
+
+return 0;
+}
+
+
+
+#ifdef DEBUG
+/*************************************************
+*        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
+
+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)
+{
+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;
+
+/* Non-alphamerics are literals. For digits or letters, do an initial lookup in
+a table. A non-zero result is something that can be returned immediately.
+Otherwise further processing may be required. */
+
+#if !EBCDIC    /* ASCII coding */
+else if (c < '0' || c > 'z') {}                           /* Not alphameric */
+else if ((i = escapes[c - '0']) != 0) c = i;
+
+#else          /* EBCDIC coding */
+else if (c < 'a' || (ebcdic_chartab[c] & 0x0E) == 0) {}   /* Not alphameric */
+else if ((i = escapes[c - 0x48]) != 0)  c = i;
+#endif
+
+/* 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++;
+        count++;
+#if !EBCDIC    /* ASCII coding */
+        if (cc >= 'a') cc -= 32;               /* Convert to upper case */
+        c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10));
+#else          /* EBCDIC coding */
+        if (cc >= 'a' && cc <= 'z') cc += 64;  /* Convert to upper case */
+        c = c * 16 + cc - ((cc >= '0')? '0' : ('A' - 10));
+#endif
+        }
+      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;                               /* Some compilers don't like ++ */
+      cc = *(++ptr);                        /* in initializers */
+#if !EBCDIC    /* ASCII coding */
+      if (cc >= 'a') cc -= 32;              /* Convert to upper case */
+      c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10));
+#else          /* EBCDIC coding */
+      if (cc <= 'z') cc += 64;              /* Convert to upper case */
+      c = c * 16 + cc - ((cc >= '0')? '0' : ('A' - 10));
+#endif
+      }
+    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.
+    (However, an EBCDIC equivalent has now been added.) */
+
+#if !EBCDIC    /* ASCII coding */
+    if (c >= 'a' && c <= 'z') c -= 32;
+    c ^= 0x40;
+#else          /* EBCDIC coding */
+    if (c >= 'a' && c <= 'z') c += 64;
+    c ^= 0xC0;
+#endif
+    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 '{'
+
+Returns:    TRUE or FALSE
+*/
+
+static BOOL
+is_counted_repeat(const uschar *p)
+{
+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
+
+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)
+{
+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];
+
+#ifdef SUPPORT_UTF8
+
+    /* 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. */
+
+    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;
+
+      /* XCLASS is used for classes that cannot be represented just by a bit
+      map. This includes negated single high-valued characters. The length in
+      the table is zero; the actual length is stored in the compled code. */
+
+      case OP_XCLASS:
+      code += GET(code, 1) + 1;
+      break;
+      }
+#endif
+    }
+  }
+}
+
+
+
+/*************************************************
+*   Scan compiled regex for recursion reference  *
+*************************************************/
+
+/* This little function scans through a compiled pattern until it finds an
+instance of OP_RECURSE.
+
+Arguments:
+  code        points to start of expression
+  utf8        TRUE in UTF-8 mode
+
+Returns:      pointer to the opcode for OP_RECURSE, or NULL if not found
+*/
+
+static const uschar *
+find_recurse(const uschar *code, BOOL utf8)
+{
+#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_RECURSE) return code;
+  else if (c == OP_CHARS) code += code[1] + OP_lengths[c];
+  else if (c > OP_BRA)
+    {
+    code += OP_lengths[OP_BRA];
+    }
+  else
+    {
+    code += OP_lengths[c];
+
+#ifdef SUPPORT_UTF8
+
+    /* 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. */
+
+    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;
+
+      /* XCLASS is used for classes that cannot be represented just by a bit
+      map. This includes negated single high-valued characters. The length in
+      the table is zero; the actual length is stored in the compled code. */
+
+      case OP_XCLASS:
+      code += GET(code, 1) + 1;
+      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;
+}
+
+
+/*************************************************
+*    Adjust OP_RECURSE items in repeated group   *
+*************************************************/
+
+/* OP_RECURSE items contain an offset from the start of the regex to the group
+that is referenced. This means that groups can be replicated for fixed
+repetition simply by copying (because the recursion is allowed to refer to
+earlier groups that are outside the current group). However, when a group is
+optional (i.e. the minimum quantifier is zero), OP_BRAZERO is inserted before
+it, after it has been compiled. This means that any OP_RECURSE items within it
+that refer to the group itself or any contained groups have to have their
+offsets adjusted. That is the job of this function. Before it is called, the
+partially compiled regex must be temporarily terminated with OP_END.
+
+Arguments:
+  group      points to the start of the group
+  adjust     the amount by which the group is to be moved
+  utf8       TRUE in UTF-8 mode
+  cd         contains pointers to tables etc.
+
+Returns:     nothing
+*/
+
+static void
+adjust_recurse(uschar *group, int adjust, BOOL utf8, compile_data *cd)
+{
+uschar *ptr = group;
+while ((ptr = (uschar *)find_recurse(ptr, utf8)) != NULL)
+  {
+  int offset = GET(ptr, 1);
+  if (cd->start_code + offset >= group) PUT(ptr, 1, offset + adjust);
+  ptr += 1 + LINK_SIZE;
+  }
+}
+
+
+
+/*************************************************
+*           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 blankclass = 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 (blankclass) class[1] |= 0x3c;
+            }
+          else
+            {
+            for (c = 0; c < 32; c++) class[c] |= cbits[c+taboffset];
+            if (blankclass) 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);
+        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);
+
+          /* \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)) goto NORMAL_CHAR;
+    ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorptr);
+    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. However, we do need to adjust
+        any OP_RECURSE calls inside the group that refer to the group itself or
+        any internal group, because the offset is from the start of the whole
+        regex. Temporarily terminate the pattern while doing this. */
+
+        if (repeat_max <= 1)
+          {
+          *code = OP_END;
+          adjust_recurse(previous, 1, utf8, cd);
+          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 have to
+        adjust the value or repeat_max, since one less copy is required. Once
+        again, we may have to adjust any OP_RECURSE calls inside the group. */
+
+        else
+          {
+          int offset;
+          *code = OP_END;
+          adjust_recurse(previous, 2 + LINK_SIZE, utf8, cd);
+          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);
+
+    /* 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);
+        if (c < 0) { ptr = tempptr; break; }
+
+        /* If a character is > 127 in UTF-8 mode, we have to turn it into
+        two or more bytes 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;
+}
+
+
+
+
+#ifdef SUPPORT_UTF8
+/*************************************************
+*         Validate a UTF-8 string                *
+*************************************************/
+
+/* This function is called (optionally) at the start of compile or match, to
+validate that a supposed UTF-8 string is actually valid. The early check means
+that subsequent code can assume it is dealing with a valid string. The check
+can be turned off for maximum performance, but then consequences of supplying
+an invalid string are then undefined.
+
+Arguments:
+  string       points to the string
+  length       length of string, or -1 if the string is zero-terminated
+
+Returns:       < 0    if the string is a valid UTF-8 string
+               >= 0   otherwise; the value is the offset of the bad byte
+*/
+
+static int
+valid_utf8(const uschar *string, int length)
+{
+register const uschar *p;
+
+if (length < 0)
+  {
+  for (p = string; *p != 0; p++);
+  length = p - string;
+  }
+
+for (p = string; length-- > 0; p++)
+  {
+  register int ab;
+  register int c = *p;
+  if (c < 128) continue;
+  if ((c & 0xc0) != 0xc0) return p - string;
+  ab = utf8_table4[c & 0x3f];  /* Number of additional bytes */
+  if (length < ab) return p - string;
+  length -= ab;
+
+  /* Check top bits in the second byte */
+  if ((*(++p) & 0xc0) != 0x80) return p - string;
+
+  /* Check for overlong sequences for each different length */
+  switch (ab)
+    {
+    /* Check for xx00 000x */
+    case 1:
+    if ((c & 0x3e) == 0) return p - string;
+    continue;   /* We know there aren't any more bytes to check */
+
+    /* Check for 1110 0000, xx0x xxxx */
+    case 2:
+    if (c == 0xe0 && (*p & 0x20) == 0) return p - string;
+    break;
+
+    /* Check for 1111 0000, xx00 xxxx */
+    case 3:
+    if (c == 0xf0 && (*p & 0x30) == 0) return p - string;
+    break;
+
+    /* Check for 1111 1000, xx00 0xxx */
+    case 4:
+    if (c == 0xf8 && (*p & 0x38) == 0) return p - string;
+    break;
+
+    /* Check for leading 0xfe or 0xff, and then for 1111 1100, xx00 00xx */
+    case 5:
+    if (c == 0xfe || c == 0xff ||
+       (c == 0xfc && (*p & 0x3c) == 0)) return p - string;
+    break;
+    }
+
+  /* Check for valid bytes after the 2nd, if any; all must start 10 */
+  while (--ab > 0)
+    {
+    if ((*(++p) & 0xc0) != 0x80) return p - string;
+    }
+  }
+
+return -1;
+}
+#endif
+
+
+
+/*************************************************
+*        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
+*/
+
+EXPORT 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;
+if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0 &&
+     (*erroroffset = valid_utf8((uschar *)pattern, -1)) >= 0)
+  {
+  *errorptr = ERR44;
+  return NULL;
+  }
+#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);
+      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))
+        {
+        ptr = read_repeat_counts(ptr+2, &min, &max, errorptr);
+        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)) goto NORMAL_CHAR;
+    ptr = read_repeat_counts(ptr+1, &min, &max, errorptr);
+    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);
+        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 it is a possessive quantifier,
+      we also need extra for wrapping the whole thing in a sub-pattern. */
+
+      if (*ptr != 0 && ptr[1] == '{' && is_counted_repeat(ptr+2))
+        {
+        ptr = read_repeat_counts(ptr+2, &min, &max, errorptr);
+        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++;
+          length += 2 + 2*LINK_SIZE;
+          }
+        else 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))
+      {
+      ptr = read_repeat_counts(ptr+2, &min, &max, errorptr);
+      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);
+        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 DEBUG
+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 DEBUG
+
+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;
+  const 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;
+  const 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 DEBUG
+if (eptr >= md->end_subject)
+  printf("matching subject <null>");
+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
+
+
+/***************************************************************************
+****************************************************************************
+                   RECURSION IN THE match() FUNCTION
+
+The match() function is highly recursive. Some regular expressions can cause
+it to recurse thousands of times. I was writing for Unix, so I just let it
+call itself recursively. This uses the stack for saving everything that has
+to be saved for a recursive call. On Unix, the stack can be large, and this
+works fine.
+
+It turns out that on non-Unix systems there are problems with programs that
+use a lot of stack. (This despite the fact that every last chip has oodles
+of memory these days, and techniques for extending the stack have been known
+for decades.) So....
+
+There is a fudge, triggered by defining NO_RECURSE, which avoids recursive
+calls by keeping local variables that need to be preserved in blocks of memory
+obtained from malloc instead instead of on the stack. Macros are used to
+achieve this so that the actual code doesn't look very different to what it
+always used to.
+****************************************************************************
+***************************************************************************/
+
+
+/* These versions of the macros use the stack, as normal */
+
+#ifndef NO_RECURSE
+#define REGISTER register
+#define RMATCH(rx,ra,rb,rc,rd,re,rf,rg) rx = match(ra,rb,rc,rd,re,rf,rg)
+#define RRETURN(ra) return ra
+#else
+
+
+/* These versions of the macros manage a private stack on the heap. Note
+that the rd argument of RMATCH isn't actually used. It's the md argument of
+match(), which never actually changes. */
+
+#define REGISTER
+
+#define RMATCH(rx,ra,rb,rc,rd,re,rf,rg)\
+  {\
+  heapframe *newframe = (pcre_stack_malloc)(sizeof(heapframe));\
+  if (setjmp(frame->Xwhere) == 0)\
+    {\
+    newframe->Xeptr = ra;\
+    newframe->Xecode = rb;\
+    newframe->Xoffset_top = rc;\
+    newframe->Xims = re;\
+    newframe->Xeptrb = rf;\
+    newframe->Xflags = rg;\
+    newframe->Xprevframe = frame;\
+    frame = newframe;\
+    DPRINTF(("restarting from line %d\n", __LINE__));\
+    goto HEAP_RECURSE;\
+    }\
+  else\
+    {\
+    DPRINTF(("longjumped back to line %d\n", __LINE__));\
+    frame = md->thisframe;\
+    rx = frame->Xresult;\
+    }\
+  }
+
+#define RRETURN(ra)\
+  {\
+  heapframe *newframe = frame;\
+  frame = newframe->Xprevframe;\
+  (pcre_stack_free)(newframe);\
+  if (frame != NULL)\
+    {\
+    frame->Xresult = ra;\
+    md->thisframe = frame;\
+    longjmp(frame->Xwhere, 1);\
+    }\
+  return ra;\
+  }
+
+
+/* Structure for remembering the local variables in a private frame */
+
+typedef struct heapframe {
+  struct heapframe *Xprevframe;
+
+  /* Function arguments that may change */
+
+  const uschar *Xeptr;
+  const uschar *Xecode;
+  int Xoffset_top;
+  long int Xims;
+  eptrblock *Xeptrb;
+  int Xflags;
+
+  /* Function local variables */
+
+  const uschar *Xcallpat;
+  const uschar *Xcharptr;
+  const uschar *Xdata;
+  const uschar *Xlastptr;
+  const uschar *Xnext;
+  const uschar *Xpp;
+  const uschar *Xprev;
+  const uschar *Xsaved_eptr;
+
+  recursion_info Xnew_recursive;
+
+  BOOL Xcur_is_word;
+  BOOL Xcondition;
+  BOOL Xminimize;
+  BOOL Xprev_is_word;
+
+  unsigned long int Xoriginal_ims;
+
+  int Xctype;
+  int Xfc;
+  int Xfi;
+  int Xlength;
+  int Xmax;
+  int Xmin;
+  int Xnumber;
+  int Xoffset;
+  int Xop;
+  int Xsave_capture_last;
+  int Xsave_offset1, Xsave_offset2, Xsave_offset3;
+  int Xstacksave[REC_STACK_SAVE_MAX];
+
+  eptrblock Xnewptrb;
+
+  /* Place to pass back result, and where to jump back to */
+
+  int  Xresult;
+  jmp_buf Xwhere;
+
+} heapframe;
+
+#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)
+{
+/* These variables do not need to be preserved over recursion in this function,
+so they can be ordinary variables in all cases. Mark them with "register"
+because they are used a lot in loops. */
+
+register int rrc;    /* Returns from recursive calls */
+register int i;      /* Used for loops not involving calls to RMATCH() */
+register int c;      /* Character values not kept over RMATCH() calls */
+
+/* When recursion is not being used, all "local" variables that have to be
+preserved over calls to RMATCH() are part of a "frame" which is obtained from
+heap storage. Set up the top-level frame here; others are obtained from the
+heap whenever RMATCH() does a "recursion". See the macro definitions above. */
+
+#ifdef NO_RECURSE
+heapframe *frame = (pcre_stack_malloc)(sizeof(heapframe));
+frame->Xprevframe = NULL;            /* Marks the top level */
+
+/* Copy in the original argument variables */
+
+frame->Xeptr = eptr;
+frame->Xecode = ecode;
+frame->Xoffset_top = offset_top;
+frame->Xims = ims;
+frame->Xeptrb = eptrb;
+frame->Xflags = flags;
+
+/* This is where control jumps back to to effect "recursion" */
+
+HEAP_RECURSE:
+
+/* Macros make the argument variables come from the current frame */
+
+#define eptr               frame->Xeptr
+#define ecode              frame->Xecode
+#define offset_top         frame->Xoffset_top
+#define ims                frame->Xims
+#define eptrb              frame->Xeptrb
+#define flags              frame->Xflags
+
+/* Ditto for the local variables */
+
+#define callpat            frame->Xcallpat
+#define charptr            frame->Xcharptr
+#define data               frame->Xdata
+#define lastptr            frame->Xlastptr
+#define next               frame->Xnext
+#define pp                 frame->Xpp
+#define prev               frame->Xprev
+#define saved_eptr         frame->Xsaved_eptr
+
+#define new_recursive      frame->Xnew_recursive
+
+#define cur_is_word        frame->Xcur_is_word
+#define condition          frame->Xcondition
+#define minimize           frame->Xminimize
+#define prev_is_word       frame->Xprev_is_word
+
+#define original_ims       frame->Xoriginal_ims
+
+#define ctype              frame->Xctype
+#define fc                 frame->Xfc
+#define fi                 frame->Xfi
+#define length             frame->Xlength
+#define max                frame->Xmax
+#define min                frame->Xmin
+#define number             frame->Xnumber
+#define offset             frame->Xoffset
+#define op                 frame->Xop
+#define save_capture_last  frame->Xsave_capture_last
+#define save_offset1       frame->Xsave_offset1
+#define save_offset2       frame->Xsave_offset2
+#define save_offset3       frame->Xsave_offset3
+#define stacksave          frame->Xstacksave
+
+#define newptrb            frame->Xnewptrb
+
+/* When recursion is being used, local variables are allocated on the stack and
+get preserved during recursion in the normal way. In this environment, fi and
+i, and fc and c, can be the same variables. */
+
+#else
+#define fi i
+#define fc c
+
+const uschar *callpat;             /* Many of these variables are used ony */
+#if 0
+const uschar *charptr;             /* small blocks of the code. My normal  */
+#endif
+const uschar *data;                /* style of coding would have declared  */
+#if 0
+const uschar *lastptr;             /* them within each of those blocks.    */
+#endif
+const uschar *next;                /* However, in order to accommodate the */
+const uschar *pp;                  /* version of this code that uses an    */
+const uschar *prev;                /* external "stack" implemented on the  */
+const uschar *saved_eptr;          /* heap, it is easier to declare them   */
+                                   /* all here, so the declarations can    */
+recursion_info new_recursive;      /* be cut out in a block. The only      */
+                                   /* declarations within blocks below are */
+BOOL cur_is_word;                  /* for variables that do not have to    */
+BOOL condition;                    /* be preserved over a recursive call   */
+BOOL minimize;                     /* to RMATCH().                         */
+BOOL prev_is_word;
+
+unsigned long int original_ims;
+
+int ctype;
+int length;
+int max;
+int min;
+int number;
+int offset;
+int op;
+int save_capture_last;
+int save_offset1, save_offset2, save_offset3;
+int stacksave[REC_STACK_SAVE_MAX];
+
+eptrblock newptrb;
+#endif
+
+
+/* OK, now we can get on with the real code of the function. Recursion is
+specified by the macros RMATCH and RRETURN. When NO_RECURSE is *not* defined,
+these just turn into a recursive call to match() and a "return", respectively.
+However, RMATCH isn't like a function call because it's quite a complicated
+macro. It has to be used in one particular way. This shouldn't, however, impact
+performance when true recursion is being used. */
+
+if (md->match_call_count++ >= md->match_limit) RRETURN(PCRE_ERROR_MATCHLIMIT);
+
+original_ims = ims;    /* Save for resetting on ')' */
+
+/* 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
+this stack. */
+
+if ((flags & match_isgroup) != 0)
+  {
+  newptrb.epb_prev = eptrb;
+  newptrb.epb_saved_eptr = eptr;
+  eptrb = &newptrb;
+  }
+
+/* Now start processing the operations. */
+
+for (;;)
+  {
+  op = *ecode;
+  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)
+    {
+    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 DEBUG
+    printf("start bracket %d subject=", number);
+    pchars(eptr, 16, TRUE, md);
+    printf("\n");
+#endif
+
+    if (offset < md->offset_max)
+      {
+      save_offset1 = md->offset_vector[offset];
+      save_offset2 = md->offset_vector[offset+1];
+      save_offset3 = md->offset_vector[md->offset_end - number];
+      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
+        {
+        RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb,
+          match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(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;
+
+      RRETURN(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
+      {
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb,
+        match_isgroup);
+      if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+      ecode += GET(ecode, 1);
+      }
+    while (*ecode == OP_ALT);
+    DPRINTF(("bracket 0 failed\n"));
+    RRETURN(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 */
+      {
+      offset = GET2(ecode, LINK_SIZE+2) << 1;  /* Doubled ref number */
+      condition = (offset == CREF_RECURSE * 2)?
+        (md->recursive != NULL) :
+        (offset < offset_top && md->offset_vector[offset] >= 0);
+      RMATCH(rrc, eptr, ecode + (condition?
+        (LINK_SIZE + 4) : (LINK_SIZE + 1 + GET(ecode, 1))),
+        offset_top, md, ims, eptrb, match_isgroup);
+      RRETURN(rrc);
+      }
+
+    /* 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
+      {
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
+          match_condassert | match_isgroup);
+      if (rrc == MATCH_MATCH)
+        {
+        ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE+2);
+        while (*ecode == OP_ALT) ecode += GET(ecode, 1);
+        }
+      else if (rrc != MATCH_NOMATCH)
+        {
+        RRETURN(rrc);         /* Need braces because of following else */
+        }
+      else ecode += GET(ecode, 1);
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb,
+        match_isgroup);
+      RRETURN(rrc);
+      }
+    /* 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->prevrec;
+      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) RRETURN(MATCH_NOMATCH);
+    md->end_match_ptr = eptr;          /* Record where we ended */
+    md->end_offset_top = offset_top;   /* and how many extracts were taken */
+    RRETURN(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
+      {
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
+        match_isgroup);
+      if (rrc == MATCH_MATCH) break;
+      if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+      ecode += GET(ecode, 1);
+      }
+    while (*ecode == OP_ALT);
+    if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH);
+
+    /* If checking an assertion for a condition, return MATCH_MATCH. */
+
+    if ((flags & match_condassert) != 0) RRETURN(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
+      {
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
+        match_isgroup);
+      if (rrc == MATCH_MATCH) RRETURN(MATCH_NOMATCH);
+      if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+      ecode += GET(ecode,1);
+      }
+    while (*ecode == OP_ALT);
+
+    if ((flags & match_condassert) != 0) RRETURN(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) RRETURN(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) RRETURN(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) RRETURN(MATCH_NOMATCH);
+      if (rrc < 0) RRETURN(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. (This is so that it works from duplicated subpatterns.)
+
+    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:
+      {
+      callpat = md->start_code + GET(ecode, 1);
+      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.prevrec = 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) RRETURN(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
+        {
+        RMATCH(rrc, eptr, callpat + 1 + LINK_SIZE, offset_top, md, ims,
+            eptrb, match_isgroup);
+        if (rrc == MATCH_MATCH)
+          {
+          md->recursive = new_recursive.prevrec;
+          if (new_recursive.offset_save != stacksave)
+            (pcre_free)(new_recursive.offset_save);
+          RRETURN(MATCH_MATCH);
+          }
+        else if (rrc != MATCH_NOMATCH) RRETURN(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.prevrec;
+      if (new_recursive.offset_save != stacksave)
+        (pcre_free)(new_recursive.offset_save);
+      RRETURN(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:
+      {
+      prev = ecode;
+      saved_eptr = eptr;
+
+      do
+        {
+        RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims,
+          eptrb, match_isgroup);
+        if (rrc == MATCH_MATCH) break;
+        if (rrc != MATCH_NOMATCH) RRETURN(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) RRETURN(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)
+        {
+        RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        }
+      else  /* OP_KETRMAX */
+        {
+        RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        RMATCH(rrc, eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        }
+      }
+    RRETURN(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:
+      {
+      next = ecode+1;
+      RMATCH(rrc, eptr, next, offset_top, md, ims, eptrb, match_isgroup);
+      if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+      do next += GET(next,1); while (*next == OP_ALT);
+      ecode = next + 1+LINK_SIZE;
+      }
+    break;
+
+    case OP_BRAMINZERO:
+      {
+      next = ecode+1;
+      do next += GET(next,1); while (*next == OP_ALT);
+      RMATCH(rrc, eptr, next + 1+LINK_SIZE, offset_top, md, ims, eptrb,
+        match_isgroup);
+      if (rrc != MATCH_NOMATCH) RRETURN(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:
+      {
+      prev = ecode - GET(ecode, 1);
+      saved_eptr = eptrb->epb_saved_eptr;
+
+      /* Back up the stack of bracket start pointers. */
+
+      eptrb = eptrb->epb_prev;
+
+      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;
+        RRETURN(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)
+        {
+        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 DEBUG
+        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->prevrec;
+            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)
+        {
+        RMATCH(rrc, eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        }
+      else  /* OP_KETRMAX */
+        {
+        RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        RMATCH(rrc, eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        }
+      }
+
+    RRETURN(MATCH_NOMATCH);
+
+    /* Start of subject unless notbol, or after internal newline if multiline */
+
+    case OP_CIRC:
+    if (md->notbol && eptr == md->start_subject) RRETURN(MATCH_NOMATCH);
+    if ((ims & PCRE_MULTILINE) != 0)
+      {
+      if (eptr != md->start_subject && eptr[-1] != NEWLINE)
+        RRETURN(MATCH_NOMATCH);
+      ecode++;
+      break;
+      }
+    /* ... else fall through */
+
+    /* Start of subject assertion */
+
+    case OP_SOD:
+    if (eptr != md->start_subject) RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    /* Start of match assertion */
+
+    case OP_SOM:
+    if (eptr != md->start_subject + md->start_offset) RRETURN(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) RRETURN(MATCH_NOMATCH); }
+      else
+        { if (md->noteol) RRETURN(MATCH_NOMATCH); }
+      ecode++;
+      break;
+      }
+    else
+      {
+      if (md->noteol) RRETURN(MATCH_NOMATCH);
+      if (!md->endonly)
+        {
+        if (eptr < md->end_subject - 1 ||
+           (eptr == md->end_subject - 1 && *eptr != NEWLINE))
+          RRETURN(MATCH_NOMATCH);
+        ecode++;
+        break;
+        }
+      }
+    /* ... else fall through */
+
+    /* End of subject assertion (\z) */
+
+    case OP_EOD:
+    if (eptr < md->end_subject) RRETURN(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)) RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    /* Word boundary assertions */
+
+    case OP_NOT_WORD_BOUNDARY:
+    case OP_WORD_BOUNDARY:
+      {
+
+      /* 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
+          {
+          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)
+        RRETURN(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)
+      RRETURN(MATCH_NOMATCH);
+    if (eptr++ >= md->end_subject) RRETURN(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) RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_NOT_DIGIT:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c < 256 &&
+#endif
+       (md->ctypes[c] & ctype_digit) != 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_DIGIT:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c >= 256 ||
+#endif
+       (md->ctypes[c] & ctype_digit) == 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_NOT_WHITESPACE:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c < 256 &&
+#endif
+       (md->ctypes[c] & ctype_space) != 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_WHITESPACE:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c >= 256 ||
+#endif
+       (md->ctypes[c] & ctype_space) == 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_NOT_WORDCHAR:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c < 256 &&
+#endif
+       (md->ctypes[c] & ctype_word) != 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_WORDCHAR:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c >= 256 ||
+#endif
+       (md->ctypes[c] & ctype_word) == 0
+       )
+      RRETURN(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:
+      {
+      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)) RRETURN(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)) RRETURN(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 (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || !match_ref(offset, eptr, length, md, ims))
+            RRETURN(MATCH_NOMATCH);
+          eptr += length;
+          }
+        /* Control never gets here */
+        }
+
+      /* If maximizing, find the longest string and work backwards */
+
+      else
+        {
+        pp = eptr;
+        for (i = min; i < max; i++)
+          {
+          if (!match_ref(offset, eptr, length, md, ims)) break;
+          eptr += length;
+          }
+        while (eptr >= pp)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          eptr -= length;
+          }
+        RRETURN(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:
+      {
+      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) RRETURN(MATCH_NOMATCH);
+          GETCHARINC(c, eptr);
+          if (c > 255)
+            {
+            if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+            }
+          else
+            {
+            if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+            }
+          }
+        }
+      else
+#endif
+      /* Not UTF-8 mode */
+        {
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          c = *eptr++;
+          if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(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 (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+            GETCHARINC(c, eptr);
+            if (c > 255)
+              {
+              if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+              }
+            else
+              {
+              if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+              }
+            }
+          }
+        else
+#endif
+        /* Not UTF-8 mode */
+          {
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+            c = *eptr++;
+            if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+            }
+          }
+        /* Control never gets here */
+        }
+
+      /* If maximizing, find the longest possible run, then work backwards. */
+
+      else
+        {
+        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 (;;)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(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)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            eptr--;
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            }
+          }
+
+        RRETURN(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:
+      {
+      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) RRETURN(MATCH_NOMATCH);
+        GETCHARINC(c, eptr);
+        if (!match_xclass(c, data)) RRETURN(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 (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          GETCHARINC(c, eptr);
+          if (!match_xclass(c, data)) RRETURN(MATCH_NOMATCH);
+          }
+        /* Control never gets here */
+        }
+
+      /* If maximizing, find the longest possible run, then work backwards. */
+
+      else
+        {
+        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(;;)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (eptr-- == pp) break;        /* Stop if tried at original pos */
+          BACKCHAR(eptr)
+          }
+        RRETURN(MATCH_NOMATCH);
+        }
+
+      /* Control never gets here */
+      }
+#endif    /* End of XCLASS */
+
+    /* Match a run of characters */
+
+    case OP_CHARS:
+      {
+      register int slen = ecode[1];
+      ecode += 2;
+
+#ifdef DEBUG    /* Sigh. Some compilers never learn. */
+      if (eptr >= md->end_subject)
+        printf("matching subject <null> against pattern ");
+      else
+        {
+        printf("matching subject ");
+        pchars(eptr, slen, TRUE, md);
+        printf(" against pattern ");
+        }
+      pchars(ecode, slen, FALSE, md);
+      printf("\n");
+#endif
+
+      if (slen > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+      if ((ims & PCRE_CASELESS) != 0)
+        {
+        while (slen-- > 0)
+          if (md->lcc[*ecode++] != md->lcc[*eptr++])
+            RRETURN(MATCH_NOMATCH);
+        }
+      else
+        {
+        while (slen-- > 0) if (*ecode++ != *eptr++) RRETURN(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)
+      {
+      length = 1;
+      charptr = ecode;
+      GETCHARLEN(fc, ecode, length);
+      if (min * length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+      ecode += length;
+
+      /* Handle multibyte character matching specially here. There is no
+      support for any kind of casing for multibyte characters. */
+
+      if (length > 1)
+        {
+        for (i = 1; i <= min; i++)
+          {
+          if (memcmp(eptr, charptr, length) != 0) RRETURN(MATCH_NOMATCH);
+          eptr += length;
+          }
+
+        if (min == max) continue;
+
+        if (minimize)
+          {
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max ||
+                eptr >= md->end_subject ||
+                memcmp(eptr, charptr, length) != 0)
+              RRETURN(MATCH_NOMATCH);
+            eptr += length;
+            }
+          /* Control never gets here */
+          }
+        else
+          {
+          pp = eptr;
+          for (i = min; i < max; i++)
+            {
+            if (eptr > md->end_subject - length ||
+                memcmp(eptr, charptr, length) != 0)
+              break;
+            eptr += length;
+            }
+          while (eptr >= pp)
+           {
+           RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+           if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+           eptr -= length;
+           }
+          RRETURN(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 fc will always be < 128. */
+      }
+    else
+#endif
+
+    /* When not in UTF-8 mode, load a single-byte character. */
+      {
+      if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+      fc = *ecode++;
+      }
+
+    /* The value of fc 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", fc, min, max,
+      max, eptr));
+
+    if ((ims & PCRE_CASELESS) != 0)
+      {
+      fc = md->lcc[fc];
+      for (i = 1; i <= min; i++)
+        if (fc != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
+      if (min == max) continue;
+      if (minimize)
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject ||
+              fc != md->lcc[*eptr++])
+            RRETURN(MATCH_NOMATCH);
+          }
+        /* Control never gets here */
+        }
+      else
+        {
+        pp = eptr;
+        for (i = min; i < max; i++)
+          {
+          if (eptr >= md->end_subject || fc != md->lcc[*eptr]) break;
+          eptr++;
+          }
+        while (eptr >= pp)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          eptr--;
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          }
+        RRETURN(MATCH_NOMATCH);
+        }
+      /* Control never gets here */
+      }
+
+    /* Caseful comparisons (includes all multi-byte characters) */
+
+    else
+      {
+      for (i = 1; i <= min; i++) if (fc != *eptr++) RRETURN(MATCH_NOMATCH);
+      if (min == max) continue;
+      if (minimize)
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject || fc != *eptr++)
+            RRETURN(MATCH_NOMATCH);
+          }
+        /* Control never gets here */
+        }
+      else
+        {
+        pp = eptr;
+        for (i = min; i < max; i++)
+          {
+          if (eptr >= md->end_subject || fc != *eptr) break;
+          eptr++;
+          }
+        while (eptr >= pp)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          eptr--;
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          }
+        RRETURN(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) RRETURN(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) RRETURN(MATCH_NOMATCH);
+      }
+    else
+      {
+      if (*ecode++ == c) RRETURN(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) RRETURN(MATCH_NOMATCH);
+    fc = *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", fc, min, max,
+      max, eptr));
+
+    if ((ims & PCRE_CASELESS) != 0)
+      {
+      fc = md->lcc[fc];
+
+#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 (fc == d) RRETURN(MATCH_NOMATCH);
+          }
+        }
+      else
+#endif
+
+      /* Not UTF-8 mode */
+        {
+        for (i = 1; i <= min; i++)
+          if (fc == md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
+        }
+
+      if (min == max) continue;
+
+      if (minimize)
+        {
+#ifdef SUPPORT_UTF8
+        /* UTF-8 mode */
+        if (md->utf8)
+          {
+          register int d;
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            GETCHARINC(d, eptr);
+            if (d < 256) d = md->lcc[d];
+            if (fi >= max || eptr >= md->end_subject || fc == d)
+              RRETURN(MATCH_NOMATCH);
+            }
+          }
+        else
+#endif
+        /* Not UTF-8 mode */
+          {
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max || eptr >= md->end_subject || fc == md->lcc[*eptr++])
+              RRETURN(MATCH_NOMATCH);
+            }
+          }
+        /* Control never gets here */
+        }
+
+      /* Maximize case */
+
+      else
+        {
+        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 (fc == d) break;
+            eptr += len;
+            }
+          for(;;)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(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 || fc == md->lcc[*eptr]) break;
+            eptr++;
+            }
+          while (eptr >= pp)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            eptr--;
+            }
+          }
+
+        RRETURN(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 (fc == d) RRETURN(MATCH_NOMATCH);
+          }
+        }
+      else
+#endif
+      /* Not UTF-8 mode */
+        {
+        for (i = 1; i <= min; i++)
+          if (fc == *eptr++) RRETURN(MATCH_NOMATCH);
+        }
+
+      if (min == max) continue;
+
+      if (minimize)
+        {
+#ifdef SUPPORT_UTF8
+        /* UTF-8 mode */
+        if (md->utf8)
+          {
+          register int d;
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            GETCHARINC(d, eptr);
+            if (fi >= max || eptr >= md->end_subject || fc == d)
+              RRETURN(MATCH_NOMATCH);
+            }
+          }
+        else
+#endif
+        /* Not UTF-8 mode */
+          {
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max || eptr >= md->end_subject || fc == *eptr++)
+              RRETURN(MATCH_NOMATCH);
+            }
+          }
+        /* Control never gets here */
+        }
+
+      /* Maximize case */
+
+      else
+        {
+        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 (fc == d) break;
+            eptr += len;
+            }
+          for(;;)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(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 || fc == *eptr) break;
+            eptr++;
+            }
+          while (eptr >= pp)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            eptr--;
+            }
+          }
+
+        RRETURN(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) RRETURN(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))
+            RRETURN(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) RRETURN(MATCH_NOMATCH);
+          GETCHARINC(c, eptr);
+          if (c < 256 && (md->ctypes[c] & ctype_digit) != 0)
+            RRETURN(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)
+            RRETURN(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))
+            RRETURN(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)
+            RRETURN(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))
+            RRETURN(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)
+            RRETURN(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) RRETURN(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) RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_DIGIT:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_NOT_WHITESPACE:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_space) != 0) RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_WHITESPACE:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_space) == 0) RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_NOT_WORDCHAR:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_word) != 0)
+            RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_WORDCHAR:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_word) == 0)
+            RRETURN(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 (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+
+          GETCHARINC(c, eptr);
+          switch(ctype)
+            {
+            case OP_ANY:
+            if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_ANYBYTE:
+            break;
+
+            case OP_NOT_DIGIT:
+            if (c < 256 && (md->ctypes[c] & ctype_digit) != 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_DIGIT:
+            if (c >= 256 || (md->ctypes[c] & ctype_digit) == 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_NOT_WHITESPACE:
+            if (c < 256 && (md->ctypes[c] & ctype_space) != 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_WHITESPACE:
+            if  (c >= 256 || (md->ctypes[c] & ctype_space) == 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_NOT_WORDCHAR:
+            if (c < 256 && (md->ctypes[c] & ctype_word) != 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_WORDCHAR:
+            if (c >= 256 && (md->ctypes[c] & ctype_word) == 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+            }
+          }
+        }
+      else
+#endif
+      /* Not UTF-8 mode */
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          c = *eptr++;
+          switch(ctype)
+            {
+            case OP_ANY:
+            if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_ANYBYTE:
+            break;
+
+            case OP_NOT_DIGIT:
+            if ((md->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_DIGIT:
+            if ((md->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_NOT_WHITESPACE:
+            if ((md->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_WHITESPACE:
+            if  ((md->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_NOT_WORDCHAR:
+            if ((md->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_WORDCHAR:
+            if ((md->ctypes[c] & ctype_word) == 0) RRETURN(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
+      {
+      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(;;)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(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)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          eptr--;
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          }
+        }
+
+      /* Get here if we can't make it match with any permitted repetitions */
+
+      RRETURN(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));
+    RRETURN(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 */
+}
+
+
+/***************************************************************************
+****************************************************************************
+                   RECURSION IN THE match() FUNCTION
+
+Undefine all the macros that were defined above to handle this. */
+
+#ifdef NO_RECURSE
+#undef eptr
+#undef ecode
+#undef offset_top
+#undef ims
+#undef eptrb
+#undef flags
+
+#undef callpat
+#undef charptr
+#undef data
+#undef lastptr
+#undef next
+#undef pp
+#undef prev
+#undef saved_eptr
+
+#undef new_recursive
+
+#undef cur_is_word
+#undef condition
+#undef minimize
+#undef prev_is_word
+
+#undef original_ims
+
+#undef ctype
+#undef length
+#undef max
+#undef min
+#undef number
+#undef offset
+#undef op
+#undef save_capture_last
+#undef save_offset1
+#undef save_offset2
+#undef save_offset3
+#undef stacksave
+
+#undef newptrb
+
+#endif
+
+/* These two are defined as macros in both cases */
+
+#undef fc
+#undef fi
+
+/***************************************************************************
+***************************************************************************/
+
+
+
+/*************************************************
+*         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
+*/
+
+EXPORT 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 = (const pcre_study_data *)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;
+
+/* Check a UTF-8 string if required. Unfortunately there's no way of passing
+back the character offset. */
+
+#ifdef SUPPORT_UTF8
+if (match_block.utf8 && (options & PCRE_NO_UTF8_CHECK) == 0)
+  {
+  if (valid_utf8((uschar *)subject, length) >= 0)
+    return PCRE_ERROR_BADUTF8;
+  if (start_offset > 0 && start_offset < length)
+    {
+    int tb = ((uschar *)subject)[start_offset];
+    if (tb > 127)
+      {
+      tb &= 0xc0;
+      if (tb != 0 && tb != 0xc0) return PCRE_ERROR_BADUTF8_OFFSET;
+      }
+    }
+  }
+#endif
+
+/* 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 DEBUG  /* 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/src/pcre_chartables.c b/src/pcre_chartables.c
new file mode 100644 (file)
index 0000000..55e413c
--- /dev/null
@@ -0,0 +1,183 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+/* This file is automatically written by the dftables auxiliary 
+program. If you edit it by hand, you might like to edit the Makefile to 
+prevent its ever being regenerated.
+
+This file is #included in the compilation of pcre.c to build the default
+character tables which are used when no tables are passed to the compile
+function. */
+
+static unsigned char pcre_default_tables[] = {
+
+/* This table is a lower casing table. */
+
+    0,  1,  2,  3,  4,  5,  6,  7,
+    8,  9, 10, 11, 12, 13, 14, 15,
+   16, 17, 18, 19, 20, 21, 22, 23,
+   24, 25, 26, 27, 28, 29, 30, 31,
+   32, 33, 34, 35, 36, 37, 38, 39,
+   40, 41, 42, 43, 44, 45, 46, 47,
+   48, 49, 50, 51, 52, 53, 54, 55,
+   56, 57, 58, 59, 60, 61, 62, 63,
+   64, 97, 98, 99,100,101,102,103,
+  104,105,106,107,108,109,110,111,
+  112,113,114,115,116,117,118,119,
+  120,121,122, 91, 92, 93, 94, 95,
+   96, 97, 98, 99,100,101,102,103,
+  104,105,106,107,108,109,110,111,
+  112,113,114,115,116,117,118,119,
+  120,121,122,123,124,125,126,127,
+  128,129,130,131,132,133,134,135,
+  136,137,138,139,140,141,142,143,
+  144,145,146,147,148,149,150,151,
+  152,153,154,155,156,157,158,159,
+  160,161,162,163,164,165,166,167,
+  168,169,170,171,172,173,174,175,
+  176,177,178,179,180,181,182,183,
+  184,185,186,187,188,189,190,191,
+  192,193,194,195,196,197,198,199,
+  200,201,202,203,204,205,206,207,
+  208,209,210,211,212,213,214,215,
+  216,217,218,219,220,221,222,223,
+  224,225,226,227,228,229,230,231,
+  232,233,234,235,236,237,238,239,
+  240,241,242,243,244,245,246,247,
+  248,249,250,251,252,253,254,255,
+
+/* This table is a case flipping table. */
+
+    0,  1,  2,  3,  4,  5,  6,  7,
+    8,  9, 10, 11, 12, 13, 14, 15,
+   16, 17, 18, 19, 20, 21, 22, 23,
+   24, 25, 26, 27, 28, 29, 30, 31,
+   32, 33, 34, 35, 36, 37, 38, 39,
+   40, 41, 42, 43, 44, 45, 46, 47,
+   48, 49, 50, 51, 52, 53, 54, 55,
+   56, 57, 58, 59, 60, 61, 62, 63,
+   64, 97, 98, 99,100,101,102,103,
+  104,105,106,107,108,109,110,111,
+  112,113,114,115,116,117,118,119,
+  120,121,122, 91, 92, 93, 94, 95,
+   96, 65, 66, 67, 68, 69, 70, 71,
+   72, 73, 74, 75, 76, 77, 78, 79,
+   80, 81, 82, 83, 84, 85, 86, 87,
+   88, 89, 90,123,124,125,126,127,
+  128,129,130,131,132,133,134,135,
+  136,137,138,139,140,141,142,143,
+  144,145,146,147,148,149,150,151,
+  152,153,154,155,156,157,158,159,
+  160,161,162,163,164,165,166,167,
+  168,169,170,171,172,173,174,175,
+  176,177,178,179,180,181,182,183,
+  184,185,186,187,188,189,190,191,
+  192,193,194,195,196,197,198,199,
+  200,201,202,203,204,205,206,207,
+  208,209,210,211,212,213,214,215,
+  216,217,218,219,220,221,222,223,
+  224,225,226,227,228,229,230,231,
+  232,233,234,235,236,237,238,239,
+  240,241,242,243,244,245,246,247,
+  248,249,250,251,252,253,254,255,
+
+/* This table contains bit maps for various character classes.
+Each map is 32 bytes long and the bits run from the least
+significant end of each byte. The classes that have their own
+maps are: space, xdigit, digit, upper, lower, word, graph
+print, punct, and cntrl. Other classes are built from combinations. */
+
+  0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
+  0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+  0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
+  0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+  0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff,
+  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+  0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
+  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+  0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc,
+  0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+  0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+/* This table identifies various classes of character by individual bits:
+  0x01   white space character
+  0x02   letter
+  0x04   decimal digit
+  0x08   hexadecimal digit
+  0x10   alphanumeric or '_'
+  0x80   regular expression metacharacter or binary zero
+*/
+
+  0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   0-  7 */
+  0x00,0x01,0x01,0x00,0x01,0x01,0x00,0x00, /*   8- 15 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  16- 23 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  24- 31 */
+  0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /*    - '  */
+  0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /*  ( - /  */
+  0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /*  0 - 7  */
+  0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /*  8 - ?  */
+  0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /*  @ - G  */
+  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  H - O  */
+  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  P - W  */
+  0x12,0x12,0x12,0x80,0x00,0x00,0x80,0x10, /*  X - _  */
+  0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /*  ` - g  */
+  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  h - o  */
+  0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  p - w  */
+  0x12,0x12,0x12,0x80,0x80,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 */
+
+/* End of chartables.c */
diff --git a/src/rc4.c b/src/rc4.c
new file mode 100644 (file)
index 0000000..09d8096
--- /dev/null
+++ b/src/rc4.c
@@ -0,0 +1,129 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/rc4.c
+ *   Copyright (C) 2000 Lucas Madar
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: rc4.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Transparent rc4 implementation
+ * Based upon sample in crypto++ library,
+ * which was based upon an anonymous usenet posting.
+ * Implemented by Lucas Madar <lucas@dal.net>
+ *
+ * Remember that it is IMPERITAVE to generate a new key
+ * for each state. DO NOT USE THE SAME KEY FOR ANY TWO STATES.
+ */
+
+typedef unsigned char RC4BYTE;
+typedef unsigned int RC4DWORD;
+
+struct rc4_state
+{
+    RC4BYTE mstate[256];
+    RC4BYTE x;
+    RC4BYTE y;
+};
+
+void *rc4_initstate(unsigned char *key, int keylen)
+{
+   RC4DWORD i;
+   RC4BYTE tmp, idx1, idx2;
+   struct rc4_state *rc4;
+   
+   if(sizeof(RC4BYTE) != 1)  abort(); /* MUST BE 1 BYTE! */
+   if(sizeof(RC4DWORD) != 4) abort(); /* MUST BE 4 BYTES! */
+   
+   rc4 = (struct rc4_state *) malloc(sizeof(struct rc4_state));
+   memset(rc4, 0, sizeof(struct rc4_state));
+   
+   for(i = 0; i < 256; i++) /* initialize our state array */
+       rc4->mstate[i] = (RC4BYTE) i;
+   
+   for(i = 0, idx1 = idx2 = 0; i < 256; i++)
+   {
+       idx2 = (key[idx1++] + rc4->mstate[i] + idx2);
+       
+      tmp = rc4->mstate[i];
+      rc4->mstate[i] = rc4->mstate[idx2];
+      rc4->mstate[idx2] = tmp;
+      
+      if(idx1 >= keylen)
+         idx1 = 0;
+   }
+   
+   return (void *) rc4;
+}
+
+void rc4_process_stream(void *rc4_context, unsigned char *istring,
+                       unsigned int stringlen)
+{
+    struct rc4_state *rc4 = (struct rc4_state *) rc4_context;
+    RC4BYTE *s = rc4->mstate;
+    RC4DWORD x = rc4->x, y = rc4->y;
+    
+    while(stringlen--)
+    {
+       RC4DWORD a, b;
+       
+       x = (x+1) & 0xFF;
+       a = s[x];
+       y = (y+a) & 0xFF;
+       b = s[y];
+       s[x] = b;
+       s[y] = a;
+       *istring++ ^= s[(a + b) & 0xFF];
+    }
+    
+    rc4->x = (RC4BYTE) x;
+    rc4->y = (RC4BYTE) y;
+}
+
+void rc4_process_stream_to_buf(void *rc4_context, 
+                              const unsigned char *istring, 
+                               unsigned char *ostring, unsigned int stringlen)
+{
+    struct rc4_state *rc4 = (struct rc4_state *) rc4_context;
+    RC4BYTE *s = rc4->mstate;
+    RC4DWORD x = rc4->x, y = rc4->y;
+    
+    while(stringlen--)
+    {
+       RC4DWORD a, b;
+       
+       x = (x+1) & 0xFF;
+       a = s[x];
+       y = (y+a) & 0xFF;
+       b = s[y];
+       s[x] = b;
+       s[y] = a;
+       *ostring++ = *istring++ ^ s[(a + b) & 0xFF];
+    }
+    
+    rc4->x = (RC4BYTE) x;
+    rc4->y = (RC4BYTE) y;
+}
+
+void rc4_destroystate(void *a)
+{
+    memset(a, 0, sizeof(struct rc4_state));
+    free(a);
+}
diff --git a/src/res.c b/src/res.c
new file mode 100644 (file)
index 0000000..06fb3e3
--- /dev/null
+++ b/src/res.c
@@ -0,0 +1,1971 @@
+/*
+ * src/res.c (C)opyright 1992 Darren Reed. All rights reserved. This
+ * file may not be distributed without the author's permission in any
+ * shape or form. The author takes no responsibility for any damage or
+ * loss of property which results from the use of this software.
+ */
+
+/* $Id: res.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "res.h"
+#include "numeric.h"
+#include "h.h"
+#include "fds.h"
+
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include "nameser.h"
+#include "resolv.h"
+#include "inet.h"
+
+/* ALLOW_CACHE_NAMES
+ *
+ * If enabled, this allows our resolver code to keep a hash table
+ * of names, for which we find in gethost_byname calls.
+ * This presents a few problems with anti-spoofing code.
+ *
+ * Since the majority of our host lookups are reverse, having
+ * a cached record for reverse records (addresses) seems useful.
+ * If, for some reason, you want this on, you may define it.
+ */
+#undef ALLOW_CACHE_NAMES
+
+/* SEARCH_CACHE_ADDRESSES
+ *
+ * All of our records will probably only have one valid IP address.
+ * If you want to search for multiple addresses, define this.
+ * (In the current implementation, it should not really be possible
+ * to get multiple addresses.)
+ *
+ * If not, it saves CPU as a cache miss does not traverse the
+ * entire cache tree for a result.
+ */
+#undef SEARCH_CACHE_ADDRESSES
+
+#define PROCANSWER_STRANGE   -2 /* invalid answer or query, try again */
+#define PROCANSWER_MALICIOUS -3 /* obviously malicious reply, 
+                                                * don't do DNS on this ip. */
+
+#undef DEBUG                   /* because theres alot of debug code in here */
+
+extern int  dn_expand(char *, char *, char *, char *, int);
+extern int  dn_skipname(char *, char *);
+extern int
+res_mkquery(int, char *, int, int, char *, int,
+           struct rrec *, char *, int);
+
+#ifndef AIX
+extern int  errno, h_errno;
+#endif
+extern int  highest_fd;
+extern aClient *local[];
+
+static char hostbuf[HOSTLEN + 1];
+static int  incache = 0;
+static CacheTable hashtable[ARES_CACSIZE];
+static ResHash idcphashtable[ARES_IDCACSIZE];
+aCache *cachetop = NULL;
+static ResRQ *last, *first;
+
+static void rem_cache(aCache *);
+static void rem_request(ResRQ *);
+static int  do_query_name(Link *, char *, ResRQ *);
+static int  do_query_number(Link *, struct in_addr *, ResRQ *);
+static void resend_query(ResRQ *);
+static int  proc_answer(ResRQ *, HEADER *, char *, char *);
+static int  query_name(char *, int, int, ResRQ *);
+static aCache *make_cache(ResRQ *);
+static aCache *find_cache_name(char *);
+static aCache *find_cache_number(ResRQ *, char *);
+static int  add_request(ResRQ *);
+static ResRQ *make_request(Link *);
+static int  send_res_msg(char *, int, int);
+static ResRQ *find_id(int);
+static int  hash_number(unsigned char *);
+static unsigned int hash_id(unsigned int);
+static unsigned int hash_cp(char *);
+static void update_list(ResRQ *, aCache *);
+#ifdef ALLOW_CACHE_NAMES
+static int  hash_name(char *);
+#endif
+static struct hostent *getres_err(ResRQ *, char *);
+
+static struct cacheinfo
+{
+    int         ca_adds;
+    int         ca_dels;
+    int         ca_expires;
+    int         ca_lookups;
+    int         ca_na_hits;
+    int         ca_nu_hits;
+    int         ca_updates;
+} cainfo;
+
+static struct resinfo
+{
+    int         re_errors;
+    int         re_nu_look;
+    int         re_na_look;
+    int         re_replies;
+    int         re_requests;
+    int         re_resends;
+    int         re_sent;
+    int         re_timeouts;
+    int         re_shortttl;
+    int         re_unkrep;
+} reinfo;
+
+int init_resolver(int op)
+{
+    int         ret = 0;
+    
+#ifdef LRAND48
+    srand48(timeofday);
+#endif
+    if (op & RES_INITLIST)
+    {
+       memset((char *) &reinfo, '\0', sizeof(reinfo));
+       first = last = NULL;
+    }
+    if (op & RES_CALLINIT)
+    {
+       ret = res_init();
+       if (!_res.nscount)
+       {
+           _res.nscount = 1;
+           _res.nsaddr_list[0].sin_addr.s_addr = inet_addr("127.0.0.1");
+       }
+    }
+    
+    if (op & RES_INITSOCK)
+    {
+       int         on = 0;
+       
+       ret = resfd = socket(AF_INET, SOCK_DGRAM, 0);
+       (void) setsockopt(ret, SOL_SOCKET, SO_BROADCAST,
+                         (char *) &on, sizeof(on));
+    }
+#ifdef DEBUG
+    if (op & RES_INITDEBG);
+    _res.options |= RES_DEBUG;
+#endif
+    if (op & RES_INITCACH)
+    {
+       memset((char *) &cainfo, '\0', sizeof(cainfo));
+       memset((char *) hashtable, '\0', sizeof(hashtable));
+       memset((char *) idcphashtable, '\0', sizeof(idcphashtable));
+    }
+    if (op == 0)
+       ret = resfd;
+    return ret;
+}
+
+static int add_request(ResRQ * new)
+{
+    if (!new)
+       return -1;
+    if (!first)
+       first = last = new;
+    else {
+       last->next = new;
+       last = new;
+    }
+    new->next = NULL;
+    reinfo.re_requests++;
+    return 0;
+}
+
+static void rem_request_id(ResRQ *req)
+{
+   unsigned int hv = hash_id(req->id);
+   ResRQ *rptr, *r2ptr = NULL;
+
+   for(rptr = idcphashtable[hv].id_list; rptr; r2ptr = rptr, rptr = rptr->id_hashnext)
+   {
+      if(rptr != req)
+         continue;
+
+      if(r2ptr != NULL)
+         r2ptr->id_hashnext = req->id_hashnext;
+      else
+         idcphashtable[hv].id_list = req->id_hashnext;
+      break;
+   }
+}
+
+static void add_request_id(ResRQ *req)
+{
+   unsigned int hv = hash_id(req->id);
+
+   req->id_hashnext = idcphashtable[hv].id_list;
+   idcphashtable[hv].id_list = req;
+}
+
+static ResRQ *find_request_id(int id)
+{
+   unsigned int hv = hash_id(id);
+   ResRQ *res = idcphashtable[hv].id_list;
+   
+   while(res)
+   {
+      if(res->id == id)
+         return res;
+      res = res->id_hashnext;
+   }
+   return NULL;
+}
+
+static void rem_request_cp(ResRQ *req)
+{
+   unsigned int hv = hash_cp(req->cinfo.value.cp);
+   ResRQ *rptr, *r2ptr = NULL;
+
+   for(rptr = idcphashtable[hv].cp_list; rptr; r2ptr = rptr, rptr = rptr->cp_hashnext)
+   {
+      if(rptr != req)
+         continue;
+
+      if(r2ptr != NULL)
+         r2ptr->cp_hashnext = req->cp_hashnext;
+      else
+         idcphashtable[hv].cp_list = req->cp_hashnext;
+      break;
+   }
+}
+
+static void add_request_cp(ResRQ *req)
+{
+   unsigned int hv = hash_cp(req->cinfo.value.cp);
+
+   req->cp_hashnext = idcphashtable[hv].cp_list;
+   idcphashtable[hv].cp_list = req;
+}
+
+static ResRQ *find_request_cp(char *cp)
+{
+   unsigned int hv = hash_cp(cp);
+   ResRQ *res = idcphashtable[hv].cp_list;
+   
+   while(res)
+   {
+      if(res->cinfo.value.cp == cp)
+         return res;
+      res = res->cp_hashnext;
+   }
+   return NULL;
+}
+
+/*
+ * remove a request from the list. This must also free any memory that
+ * has been allocated for temporary storage of DNS results.
+ */
+static void rem_request(ResRQ * old)
+{
+    ResRQ **rptr, *r2ptr = NULL;
+    int     i;
+    char   *s;
+    
+    if (!old)
+       return;
+
+    if(old->id != -1)
+    {
+        rem_request_id(old);
+        old->id = -1;
+    }
+
+    if(old->cinfo.value.cp != NULL)
+       rem_request_cp(old);
+
+    for (rptr = &first; *rptr; r2ptr = *rptr, rptr = &(*rptr)->next)
+       if (*rptr == old)
+       {
+           *rptr = old->next;
+           if (last == old)
+               last = r2ptr;
+           break;
+       }
+#ifdef DEBUG
+    Debug((DEBUG_INFO, "rem_request:Remove %#x at %#x %#x",
+          old, *rptr, r2ptr));
+#endif
+    r2ptr = old;
+    
+    if (r2ptr->he.h_name)
+       MyFree(r2ptr->he.h_name);
+    for (i = 0; i < IRC_MAXALIASES; i++)
+       if ((s = r2ptr->he.h_aliases[i]))
+           MyFree(s);
+    
+    if (r2ptr->he_rev.h_name)
+       MyFree(r2ptr->he_rev.h_name);
+    for (i = 0; i < IRC_MAXALIASES; i++)
+       if ((s = r2ptr->he_rev.h_aliases[i]))
+           MyFree(s);
+    
+    if (r2ptr->name)
+       MyFree(r2ptr->name);
+    MyFree(r2ptr);
+
+    return;
+}
+
+/* Create a DNS request record for the server. */
+static ResRQ *make_request(Link *lp)
+{
+    ResRQ  *nreq;
+    
+    nreq = (ResRQ *) MyMalloc(sizeof(ResRQ));
+    memset((char *) nreq, '\0', sizeof(ResRQ));
+    nreq->next = NULL;         /*  where NULL is non-zero */
+    nreq->sentat = timeofday;
+    nreq->retries = 3;
+    nreq->resend = 1;
+    nreq->srch = -1;
+    nreq->id = -1;
+    if (lp)
+    {
+       memcpy((char *) &nreq->cinfo, (char *) lp, sizeof(Link));
+        add_request_cp(nreq);
+    }
+    else
+       memset((char *) &nreq->cinfo, '\0', sizeof(Link));
+    
+    nreq->timeout = 4;         /* start at 4 and exponential inc. */
+    nreq->he.h_addrtype = AF_INET;
+    nreq->he.h_name = NULL;
+    nreq->he.h_aliases[0] = NULL;
+    (void) add_request(nreq);
+    return nreq;
+}
+
+/*
+ * Remove queries from the list which have been there too long without
+ * being resolved.
+ */
+time_t timeout_query_list(time_t now)
+{
+    ResRQ  *rptr, *r2ptr;
+    time_t  next = 0, tout;
+    aClient    *cptr;
+
+    Debug((DEBUG_DNS, "timeout_query_list at %s", myctime(now)));
+    for (rptr = first; rptr; rptr = r2ptr)
+    {
+       r2ptr = rptr->next;
+       tout = rptr->sentat + rptr->timeout;
+       if (now >= tout)
+       {
+           if (--rptr->retries <= 0)
+           {
+#ifdef DEBUG
+               Debug((DEBUG_ERROR, "timeout %x now %d cptr %x",
+                      rptr, now, rptr->cinfo.value.cptr));
+#endif
+               reinfo.re_timeouts++;
+               cptr = rptr->cinfo.value.cptr;
+               switch (rptr->cinfo.flags)
+               {
+               case ASYNC_CLIENT:
+#ifdef SHOW_HEADERS
+                   sendto_one(cptr, REPORT_FAIL_DNS);
+#endif
+                   ClearDNS(cptr);
+                    check_client_fd(cptr);
+                   break;
+
+               case ASYNC_CONNECT:
+                   sendto_ops("Host %s unknown",
+                              rptr->name);
+                   break;
+               }
+               rem_request(rptr);
+               continue;
+           }
+           else
+           {
+               rptr->sentat = now;
+               rptr->timeout += rptr->timeout;
+               resend_query(rptr);
+#ifdef DEBUG
+               Debug((DEBUG_INFO, "r %x now %d retry %d c %x",
+                      rptr, now, rptr->retries,
+                      rptr->cinfo.value.cptr));
+#endif
+           }
+       }
+       if (!next || tout < next)
+           next = tout;
+    }
+    return (next > now) ? next : (now + AR_TTL);
+}
+
+/*
+ * del_queries - called by the server to cleanup outstanding queries
+ * for which there no longer exist clients or conf lines.
+ */
+void del_queries(char *cp)
+{
+    ResRQ  *ret = find_request_cp(cp);
+
+    if(ret)
+       rem_request(ret);
+}
+
+/*
+ * sends msg to all nameservers found in the "_res" structure. This
+ * should reflect /etc/resolv.conf. We will get responses which arent
+ * needed but is easier than checking to see if nameserver isnt
+ * present. Returns number of messages successfully sent to nameservers
+ * or -1 if no successful sends.
+ */
+static int send_res_msg(char *msg, int len, int rcount)
+{
+    int     i;
+    int         sent = 0, max;
+
+    if (!msg)
+       return -1;
+    
+    max = MIN(_res.nscount, rcount);
+    if (_res.options & RES_PRIMARY)
+       max = 1;
+    if (!max)
+       max = 1;
+
+    for (i = 0; i < max; i++)
+    {
+       _res.nsaddr_list[i].sin_family = AF_INET;
+       if (sendto(resfd, msg, len, 0,
+                  (struct sockaddr *) &(_res.nsaddr_list[i]),
+                  sizeof(struct sockaddr)) == len)
+       {
+           reinfo.re_sent++;
+           sent++;
+       }
+       else
+           Debug((DEBUG_ERROR, "s_r_m:sendto: %d on %d",
+                  errno, resfd));
+    }
+    
+    return (sent) ? sent : -1;
+}
+
+/* find a dns request id (id is determined by dn_mkquery) */
+static ResRQ *find_id(int id)
+{
+    ResRQ  *ret = find_request_id(id);
+
+    return ret;
+}
+
+struct hostent *gethost_byname(char *name, Link *lp)
+{
+    aCache *cp;
+    
+    if (name == (char *) NULL)
+       return ((struct hostent *) NULL);
+    
+    reinfo.re_na_look++;
+    if ((cp = find_cache_name(name)))
+       return (struct hostent *) &(cp->he);
+    if (!lp)
+       return NULL;
+    (void) do_query_name(lp, name, NULL);
+    return ((struct hostent *) NULL);
+}
+
+struct hostent *gethost_byaddr(char *addr, Link *lp)
+{
+    aCache     *cp;
+
+    if (addr == (char *) NULL)
+       return ((struct hostent *) NULL);
+
+    reinfo.re_nu_look++;
+    if ((cp = find_cache_number(NULL, addr)))
+       return (struct hostent *) &(cp->he);
+    if (!lp)
+       return NULL;
+    (void) do_query_number(lp, (struct in_addr *) addr, NULL);
+    return ((struct hostent *) NULL);
+}
+
+static int do_query_name(Link *lp, char *name, ResRQ * rptr)
+{
+    char        hname[HOSTLEN + 1];
+    int         len;
+    
+    strncpyzt(hname, name, HOSTLEN);
+    len = strlen(hname);
+    
+    if (rptr && !strchr(hname, '.') && _res.options & RES_DEFNAMES)
+    {
+       if ((sizeof(hname) - len - 1) >= 2)
+       {
+           (void) strncat(hname, ".", sizeof(hname) - len - 1);
+           len++;
+           if ((sizeof(hname) - len - 1) >= 1)
+               (void) strncat(hname, _res.defdname, sizeof(hname) - len - 1);
+       }
+    }
+    /*
+     * Store the name passed as the one to lookup and generate other
+     * host names to pass onto the nameserver(s) for lookups.
+     */
+    if (!rptr)
+    {
+       rptr = make_request(lp);
+       rptr->type = T_A;
+       rptr->name = (char *) MyMalloc(strlen(name) + 1);
+       (void) strcpy(rptr->name, name);
+    }
+    return (query_name(hname, C_IN, T_A, rptr));
+}
+
+/* Use this to do reverse IP# lookups. */
+static int do_query_number(Link *lp, struct in_addr *numb, ResRQ * rptr)
+{
+    char        ipbuf[32];
+    u_char *cp;
+
+    cp = (u_char *) &numb->s_addr;
+    (void) ircsprintf(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
+                     (u_int) (cp[3]), (u_int) (cp[2]),
+                     (u_int) (cp[1]), (u_int) (cp[0]));
+
+    if (!rptr)
+    {
+       rptr = make_request(lp);
+       rptr->type = T_PTR;
+       rptr->addr.s_addr = numb->s_addr;
+       memcpy((char *) &rptr->he.h_addr,
+              (char *) &numb->s_addr, sizeof(struct in_addr));
+       rptr->he.h_length = sizeof(struct in_addr);
+    }
+    return (query_name(ipbuf, C_IN, T_PTR, rptr));
+}
+
+/* generate a query based on class, type and name. */
+static int query_name(char *name, int class, int type, ResRQ * rptr)
+{
+    struct timeval tv;
+    char        buf[MAXPACKET];
+    int         r, s, k = 0;
+    HEADER     *hptr;
+
+    memset(buf, '\0', sizeof(buf));
+    r = res_mkquery(QUERY, name, class, type, NULL, 0, NULL,
+                   buf, sizeof(buf));
+    if (r <= 0)
+    {
+       h_errno = NO_RECOVERY;
+       return r;
+    }
+
+    if(rptr->id != -1)
+        rem_request_id(rptr);
+
+    hptr = (HEADER *) buf;
+#ifdef LRAND48
+    do
+    {
+       hptr->id = htons(ntohs(hptr->id) + k + lrand48() & 0xffff);
+#else
+       (void) gettimeofday(&tv, NULL);
+    do
+    {
+#if 0 /* emacs kludge */
+    }
+#endif
+        hptr->id = htons(ntohs(hptr->id) + k +
+                        (u_short) (tv.tv_usec & 0xffff));
+#endif /* LRAND48 */
+       k++;
+    } while (find_id(ntohs(hptr->id)));
+    rptr->id = ntohs(hptr->id);
+    add_request_id(rptr);
+    rptr->sends++;
+    s = send_res_msg(buf, r, rptr->sends);
+    if (s == -1)
+    {
+       h_errno = TRY_AGAIN;
+       return -1;
+    }
+    else
+       rptr->sent += s;
+    return 0;
+}
+    
+static void resend_query(ResRQ * rptr)
+{
+    if (rptr->resend == 0)
+       return;
+    reinfo.re_resends++;
+    switch (rptr->type)
+    {
+    case T_PTR:
+       (void) do_query_number(NULL, &rptr->addr, rptr);
+       break;
+    case T_A:
+       (void) do_query_name(NULL, rptr->name, rptr);
+       break;
+    default:
+       break;
+    }
+    return;
+}
+
+/* returns 0 on failure, nonzero on success */
+int arpa_to_ip(char *arpastring, unsigned int *saddr)
+{
+    int idx = 0, onum = 0;
+    char ipbuf[HOSTLEN + 1];
+    char *fragptr[4];
+    u_char *ipptr;
+         
+    strcpy(ipbuf, arpastring);
+
+    /* ipbuf should contain a string in the format of 4.3.2.1.in-addr.arpa */
+    
+    fragptr[onum++] = ipbuf;
+
+    while(ipbuf[idx])
+    {
+       if(ipbuf[idx] == '.')
+       {
+           ipbuf[idx++] = '\0';
+           if(onum == 4)
+               break;
+           fragptr[onum++] = ipbuf + idx;
+       }
+       else
+           idx++;
+    }
+
+    if(onum != 4)
+       return 0;
+
+    if(mycmp(ipbuf + idx, "in-addr.arpa"))
+       return 0;
+
+    ipptr = (u_char *) saddr;
+
+    ipptr[0] = (u_char) atoi(fragptr[3]);
+    ipptr[1] = (u_char) atoi(fragptr[2]);
+    ipptr[2] = (u_char) atoi(fragptr[1]);
+    ipptr[3] = (u_char) atoi(fragptr[0]);
+    return 1;
+}
+
+#undef DNS_ANS_DEBUG_MAX
+#undef DNS_ANS_DEBUG
+
+#define MAX_ACCEPTABLE_ANS 10
+
+static char acceptable_answers[MAX_ACCEPTABLE_ANS][HOSTLEN + 1];
+static int num_acc_answers = 0;
+
+#define add_acceptable_answer(x) do { \
+           if(num_acc_answers < MAX_ACCEPTABLE_ANS) \
+           strcpy(acceptable_answers[num_acc_answers++], x); } while (0);
+          
+static inline char *is_acceptable_answer(char *h)
+{
+    int i;
+
+    for (i = 0; i < num_acc_answers; i++) 
+    {
+       if(mycmp(acceptable_answers[i], h) == 0)
+           return acceptable_answers[i];
+    }
+    return 0;
+}
+
+#ifdef DNS_ANS_DEBUG_MAX
+static char dhostbuf[HOSTLEN + 1];
+#endif
+
+/* process name server reply. */
+static int proc_answer(ResRQ * rptr, HEADER *hptr, char *buf, char *eob)
+{
+    char   *cp, **alias, *acc;
+    struct hent *hp;
+    int class, type, dlen, len, ans = 0, n, origtype = rptr->type;
+    int adr = 0;
+    struct in_addr ptrrep, dr;
+
+    num_acc_answers = 0;
+    
+    cp = buf + sizeof(HEADER);
+    hp = (struct hent *) &(rptr->he);
+
+    while ((hp->h_addr_list[adr].s_addr) && (adr < IRC_MAXADDRS))
+       adr++;
+
+    alias = hp->h_aliases;
+    while (*alias)
+       alias++;
+
+    if(hptr->qdcount != 1)
+    {
+       sendto_realops_lev(DEBUG_LEV,
+                          "DNS packet with question count of %d ",
+                          hptr->qdcount);
+       return -1;
+    }
+
+    /*
+     * ensure the question we're getting a reply for
+     * is a the right question.
+     */
+
+    if((n = dn_expand(buf, eob, cp, hostbuf, sizeof(hostbuf))) <= 0)
+    {
+       /* broken dns packet, toss it out */
+       return -1;
+    }
+    else
+    {
+       int strangeness = 0;
+       char tmphost[HOSTLEN];
+
+       hostbuf[HOSTLEN] = '\0';
+       cp += n;
+       type = (int) _getshort(cp);
+       cp += sizeof(short);
+       class = (int) _getshort(cp);
+       cp += sizeof(short);
+       if(class != C_IN)
+       {
+           sendto_realops_lev(DEBUG_LEV,
+                              "Expected DNS packet class C_IN, got %d ",
+                              class);
+           strangeness++;
+       }
+
+       if(type != rptr->type)
+       {
+           sendto_realops_lev(DEBUG_LEV,
+                              "Expected DNS packet type %d, got %d ",
+                              rptr->type, type);
+           strangeness++;
+       }
+
+       if(rptr->type == T_A && rptr->name)
+       {
+           strcpy(tmphost, rptr->name);
+       }
+       else if(rptr->type == T_PTR)
+       {
+           u_char *ipp;
+           
+           ipp = (u_char *) &rptr->addr.s_addr;
+           ircsprintf(tmphost, "%u.%u.%u.%u.in-addr.arpa",
+                      (u_int) (ipp[3]), (u_int) (ipp[2]),
+                      (u_int) (ipp[1]), (u_int) (ipp[0]));  
+       }
+       else
+       {
+           sendto_realops_lev(DEBUG_LEV,
+                              "rptr->type is unknown type %d! "
+                              "(rptr->name == %x)", 
+                              rptr->type, rptr->name);
+           return -1;
+       }    
+
+       if(mycmp(tmphost, hostbuf) != 0)
+       {
+           sendto_realops_lev(DEBUG_LEV, "Asked question for %s, but got "
+                              "reply about question %s (!!!)",
+                              tmphost, hostbuf);
+           strangeness++;
+       }
+       
+       if(strangeness)
+           return PROCANSWER_STRANGE;
+    }
+
+    /* proccess each answer sent to us blech. */
+    while (hptr->ancount-- > 0 && cp && cp < eob) 
+    {
+       n = dn_expand(buf, eob, cp, hostbuf, sizeof(hostbuf));
+       hostbuf[HOSTLEN] = '\0';
+       
+       if (n <= 0)
+           break;
+       cp += n;
+       type = (int) _getshort(cp);
+       cp += sizeof(short);
+       
+       class = (int) _getshort(cp);
+       cp += sizeof(short);
+       
+       rptr->ttl = _getlong(cp);
+       cp += sizeof(rptr->ttl);
+       dlen = (int) _getshort(cp);
+       cp += sizeof(short);
+       
+       /* Wait to set rptr->type until we verify this structure */
+
+       len = strlen(hostbuf);
+       /* name server never returns with trailing '.' */
+       if (!strchr(hostbuf, '.') && (_res.options & RES_DEFNAMES))
+       {
+           (void) strcat(hostbuf, ".");
+           len++;
+           if ((len + 2) < sizeof(hostbuf))
+           {
+               strncpy(hostbuf, _res.defdname,
+                       sizeof(hostbuf) - 1 - len);
+               hostbuf[HOSTLEN] = '\0';
+               len = MIN(len + strlen(_res.defdname),
+                         sizeof(hostbuf)) - 1;
+           }
+       }
+       
+#ifdef DNS_ANS_DEBUG_MAX
+       strcpy(dhostbuf, hostbuf);
+#endif
+       
+       switch (type)
+       {
+       case T_A:
+           if(rptr->name == NULL)
+           {
+               sendto_realops_lev(DEBUG_LEV,"Received DNS_A answer, but null "
+                                  "rptr->name!");
+               return PROCANSWER_STRANGE;
+           }
+           if(mycmp(rptr->name, hostbuf) != 0)
+           {
+               if(!num_acc_answers || !(acc = is_acceptable_answer(hostbuf)))
+               {
+#ifdef DNS_ANS_DEBUG
+                   sendto_realops_lev(DEBUG_LEV,
+                                      "Received DNS_A answer for %s, but "
+                                      "asked question for %s", hostbuf,
+                                      rptr->name);
+#endif
+                   return PROCANSWER_STRANGE;
+               }
+#ifdef DNS_ANS_DEBUG
+               sendto_realops_lev(DEBUG_LEV,
+                                  "DNS_A answer from an acceptable (%s)",
+                                  acc);
+#endif
+           }
+           hp->h_length = dlen;
+           if (ans == 1)
+               hp->h_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC;
+           /* from Christophe Kalt <kalt@stealth.net> */
+           if (dlen != sizeof(dr)) 
+           {
+               sendto_realops("Bad IP length (%d) returned for %s",
+                              dlen, hostbuf);
+               Debug((DEBUG_DNS, "Bad IP length (%d) returned for %s",
+                      dlen, hostbuf));
+               return PROCANSWER_MALICIOUS;
+           }
+
+           if(adr < IRC_MAXADDRS)
+           {
+               /* ensure we never go over the bounds of our adr array */
+               memcpy((char *)&dr, cp, sizeof(dr));
+               hp->h_addr_list[adr].s_addr = dr.s_addr;
+               Debug((DEBUG_INFO, "got ip # %s for %s",
+                      inetntoa((char *) &hp->h_addr_list[adr]), hostbuf));
+               
+#ifdef DNS_ANS_DEBUG_MAX
+               sendto_realops_lev(DEBUG_LEV, "%s A %s", dhostbuf,
+                                  inetntoa((char *) &hp->h_addr_list[adr]));
+#endif
+               adr++;
+           }
+           
+           if (!hp->h_name) 
+           {
+               hp->h_name = (char *) MyMalloc(len + 1);
+               strcpy(hp->h_name, hostbuf);
+           }
+           ans++;
+           cp += dlen;
+           rptr->type = type;
+           break;
+           
+       case T_PTR:
+           acc = NULL;
+           if(!num_acc_answers || !(acc = is_acceptable_answer(hostbuf)))
+           {
+               if(!(arpa_to_ip(hostbuf, &ptrrep.s_addr)))
+               {
+#ifdef DNS_ANS_DEBUG
+                   sendto_realops_lev(DEBUG_LEV, 
+                                      "Received strangely formed PTR answer "
+                                      "for %s (asked for %s) -- ignoring", 
+                                      hostbuf, inetntoa((char *)&rptr->addr));
+#endif
+                   return PROCANSWER_STRANGE;
+               }
+               
+               if(ptrrep.s_addr != rptr->addr.s_addr)
+               {
+#ifdef DNS_ANS_DEBUG
+                   char ipbuf[16];
+                   
+                   strcpy(ipbuf, inetntoa((char *)&ptrrep));
+                   sendto_realops_lev(DEBUG_LEV,
+                                      "Received DNS_PTR answer for %s, "
+                                      "but asked question for %s", 
+                                      ipbuf, inetntoa((char*)&rptr->addr));
+#endif
+                   return PROCANSWER_STRANGE;
+               }
+           }
+           
+#ifdef DNS_ANS_DEBUG
+           if(acc)
+               sendto_realops_lev(DEBUG_LEV, 
+                                  "DNS_PTR from an acceptable (%s)", acc);
+#endif
+           
+           if ((n = dn_expand(buf, eob, cp, hostbuf,
+                              sizeof(hostbuf))) < 0) 
+           {
+               cp = NULL;
+               break;
+           }
+           
+           /*
+            * This comment is based on analysis by Shadowfax,
+            * Jolo and johan, not me. (Dianora) I am only
+            * commenting it.
+            * 
+            * dn_expand is guaranteed to not return more than
+            * sizeof(hostbuf) but do all implementations of
+            * dn_expand also guarantee buffer is terminated with
+            * null byte? Lets not take chances. -Dianora
+            */
+           hostbuf[HOSTLEN] = '\0';
+           cp += n;
+           len = strlen(hostbuf);
+           
+#ifdef DNS_ANS_DEBUG_MAX
+           sendto_realops_lev(DEBUG_LEV, "%s PTR %s", dhostbuf, hostbuf);
+#endif
+           
+           Debug((DEBUG_INFO, "got host %s", hostbuf));
+           /*
+            * copy the returned hostname into the host name or
+            * alias field if there is a known hostname already.
+            */
+           if (hp->h_name) 
+           {
+               /*
+                * This is really fishy. In fact, so fishy,
+                * that I say we just don't do this in this case.
+                *
+                * seems to happen with a whole host of .my addresses.
+                * interesting. - lucas
+                */
+               
+               if (alias >= &(hp->h_aliases[IRC_MAXALIASES - 1]))
+                   break;
+               *alias = (char *) MyMalloc(len + 1);
+               strcpy(*alias++, hostbuf);
+               *alias = NULL;
+           }
+           else 
+           {
+               hp->h_name = (char *) MyMalloc(len + 1);
+               strcpy(hp->h_name, hostbuf);
+           }
+           ans++;
+           rptr->type = type;
+           break;
+           
+       case T_CNAME:
+           acc = NULL;
+           
+           if(origtype == T_PTR)
+           {
+               if(!num_acc_answers || !(acc = is_acceptable_answer(hostbuf)))
+               {
+                   if(!(arpa_to_ip(hostbuf, &ptrrep.s_addr)))
+                   {
+#ifdef DNS_ANS_DEBUG
+                       sendto_realops_lev(DEBUG_LEV,
+                                          "Received strangely formed "
+                                          "CNAME(PTR) answer for %s (asked "
+                                          "for %s) -- ignoring", 
+                                          hostbuf,
+                                          inetntoa((char *)&rptr->addr));
+#endif
+                       return PROCANSWER_STRANGE;
+                   }
+
+                   if(ptrrep.s_addr != rptr->addr.s_addr)
+                   {
+#ifdef DNS_ANS_DEBUG
+                       char ipbuf[16];
+                       
+                       strcpy(ipbuf, inetntoa((char *)&ptrrep));
+                       sendto_realops_lev(DEBUG_LEV, "Received "
+                                          "DNS_CNAME(PTR) answer for %s, "
+                                          "but asked question for %s", 
+                                          ipbuf, 
+                                          inetntoa((char*)&rptr->addr));
+#endif
+                       return PROCANSWER_STRANGE;
+                   }
+               }
+#ifdef DNS_ANS_DEBUG
+               if(acc)
+                   sendto_realops_lev(DEBUG_LEV, "DNS_CNAME (PTR) answer "
+                                      "from an acceptable (%s)", acc);
+#endif
+           }
+           else if(origtype == T_A)
+           {
+               if(mycmp(rptr->name, hostbuf) != 0)
+               {
+                   if(!num_acc_answers || !(acc = is_acceptable_answer(hostbuf)))
+                   {
+#ifdef DNS_ANS_DEBUG
+                       sendto_realops_lev(DEBUG_LEV, "Received DNS_CNAME(A) "
+                                          "answer for %s, but asked "
+                                          "question for %s", 
+                                          hostbuf, rptr->name);
+#endif
+                       return PROCANSWER_STRANGE;
+                   }
+#ifdef DNS_ANS_DEBUG
+                   sendto_realops_lev(DEBUG_LEV, "DNS_CNAME (A) answer from "
+                                      "an acceptable (%s)", acc);
+#endif
+               }
+           }
+           
+           Debug((DEBUG_INFO, "got cname %s", hostbuf));
+           
+           if (alias >= &(hp->h_aliases[IRC_MAXALIASES - 1]))
+               break;
+           *alias = (char *) MyMalloc(len + 1);
+           strcpy(*alias++, hostbuf);
+           *alias = NULL;
+           ans++;
+           rptr->type = type;
+           
+           if ((n = dn_expand(buf, eob, cp, hostbuf, sizeof(hostbuf))) < 0)
+           {
+               cp = NULL;
+               break;
+           }
+           
+           hostbuf[HOSTLEN] = '\0';
+           cp += n;
+           
+           add_acceptable_answer(hostbuf);
+           
+#ifdef DNS_ANS_DEBUG_MAX
+           sendto_realops_lev(DEBUG_LEV, "%s CNAME %s", dhostbuf, hostbuf);
+#endif
+           
+           break;
+           
+       default:
+#ifdef DEBUG
+           Debug((DEBUG_INFO, "proc_answer: type:%d for:%s",
+                  type, hostbuf));
+#endif
+           break;
+       }
+    }
+    return ans;
+}
+
+/*
+ * read a dns reply from the nameserver and process it.
+ */
+struct hostent *get_res(char *lp)
+{
+    static char buf[sizeof(HEADER) + MAXPACKET];
+    HEADER *hptr;
+    ResRQ  *rptr = NULL;
+    aCache     *cp = (aCache *) NULL;
+    struct sockaddr_in sin;
+    int         rc, a, len = sizeof(sin), max;
+    
+    rc = recvfrom(resfd, buf, sizeof(buf), 0, (struct sockaddr *) &sin, &len);
+    if (rc <= sizeof(HEADER))
+       return getres_err(rptr, lp);
+    
+    /*
+     * convert DNS reply reader from Network byte order to CPU byte
+     * order.
+     */
+    hptr = (HEADER *) buf;
+    hptr->id = ntohs(hptr->id);
+    hptr->ancount = ntohs(hptr->ancount);
+    hptr->qdcount = ntohs(hptr->qdcount);
+    hptr->nscount = ntohs(hptr->nscount);
+    hptr->arcount = ntohs(hptr->arcount);
+#ifdef DEBUG
+    Debug((DEBUG_NOTICE, "get_res:id = %d rcode = %d ancount = %d",
+          hptr->id, hptr->rcode, hptr->ancount));
+#endif
+    reinfo.re_replies++;
+    /*
+     * response for an id which we have already received an answer for
+     * just ignore this response.
+     */
+    rptr = find_id(hptr->id);
+    if (!rptr)
+       return getres_err(rptr, lp);
+    /*
+     * check against possibly fake replies
+     */
+    max = MIN(_res.nscount, rptr->sends);
+    if (!max)
+       max = 1;
+
+    for (a = 0; a < max; a++)
+       if (!_res.nsaddr_list[a].sin_addr.s_addr ||
+           !memcmp((char *) &sin.sin_addr,
+                   (char *) &_res.nsaddr_list[a].sin_addr,
+                   sizeof(struct in_addr)))
+           break;
+
+    if (a == max) 
+    {
+       reinfo.re_unkrep++;
+       return getres_err(rptr, lp);
+    }
+
+    if ((hptr->rcode != NOERROR) || (hptr->ancount == 0))
+    {
+       switch (hptr->rcode)
+       {
+       case NXDOMAIN:
+           h_errno = TRY_AGAIN;
+           break;
+       case SERVFAIL:
+           h_errno = TRY_AGAIN;
+           break;
+       case NOERROR:
+           h_errno = NO_DATA;
+           break;
+       case FORMERR:
+       case NOTIMP:
+       case REFUSED:
+       default:
+           h_errno = NO_RECOVERY;
+           break;
+       }
+       reinfo.re_errors++;
+       /*
+        * If a bad error was returned, we stop here and dont send
+        * send any more (no retries granted).
+        */
+       if (h_errno != TRY_AGAIN)
+       {
+           Debug((DEBUG_DNS, "Fatal DNS error %d for %d",
+                  h_errno, hptr->rcode));
+           rptr->resend = 0;
+           rptr->retries = 0;
+       }
+       return getres_err(rptr, lp);
+    }
+    a = proc_answer(rptr, hptr, buf, buf + rc);
+    
+#ifdef DEBUG
+    Debug((DEBUG_INFO, "get_res:Proc answer = %d", a));
+#endif
+
+    switch(a)
+    {
+    case PROCANSWER_STRANGE:
+       rptr->resend = 1;
+       rptr->retries--;
+       if(rptr->retries <= 0)
+       {
+           h_errno = TRY_AGAIN; /* fail this lookup.. */
+           return getres_err(rptr, lp);
+       }
+       else 
+           resend_query(rptr);
+       return NULL;
+       
+    case PROCANSWER_MALICIOUS:
+       if (lp)
+           memcpy(lp, (char *) &rptr->cinfo, sizeof(Link));
+       rem_request(rptr);
+       return NULL;
+       
+    default:
+       break;
+    }
+    
+    if (a > 0 && rptr->type == T_PTR) 
+    {
+       struct hostent *hp2 = NULL;
+       
+       Debug((DEBUG_DNS, "relookup %s <-> %s",
+              rptr->he.h_name, inetntoa((char *) &rptr->he.h_addr)));
+       /*
+        * Lookup the 'authoritive' name that we were given for the ip#.
+        * By using this call rather than regenerating the type we
+        * automatically gain the use of the cache with no extra kludges.
+        */
+       if ((hp2 = gethost_byname(rptr->he.h_name, &rptr->cinfo)))
+           if (lp)
+               memcpy(lp, (char *) &rptr->cinfo, sizeof(Link));
+       
+       if(!hp2)
+       {
+           memcpy(&last->he_rev, &rptr->he, sizeof(struct hent));
+           memset(&rptr->he, 0, sizeof(struct hent));
+           last->has_rev = 1;
+       }
+
+       rem_request(rptr);
+       return hp2;
+    }
+
+    if(a > 0 && rptr->type == T_A)
+    {
+       if(rptr->has_rev == 0)
+       {
+           sendto_realops_lev(DEBUG_LEV, "Blindly accepting dns result for %s", 
+                          rptr->he.h_name ? rptr->he.h_name : 
+                          inetntoa((char *)&rptr->addr));
+       }
+       else
+       {
+           int invalid_parms_name = 0;
+           int invalid_parms_ip = 0;
+           int found_match_ip = 0;
+           int nidx, tidx;
+           int numaddr, numnewaddr;
+           struct in_addr new_addr_list[IRC_MAXADDRS];
+
+           if(!(rptr->he.h_name && rptr->he_rev.h_name))
+               invalid_parms_name++;
+           
+           if(!(rptr->he.h_addr_list[0].s_addr && 
+                rptr->he_rev.h_addr_list[0].s_addr))
+               invalid_parms_ip++;
+
+           if(invalid_parms_name || invalid_parms_ip)
+           {
+               sendto_realops_lev(DEBUG_LEV, 
+                              "DNS query missing things! name: %s ip: %s",
+                              invalid_parms_name ? "MISSING" :
+                              rptr->he.h_name,
+                              invalid_parms_ip ? "MISSING" :
+                              inetntoa((char *)&rptr->he.h_addr_list[0]));
+               if (lp)
+                   memcpy(lp, (char *) &rptr->cinfo, sizeof(Link));
+               rem_request(rptr);
+               return NULL;
+           }
+
+           /* 
+            * This must ensure that all IPs in the forward query (he)
+            * are also in the reverse query (he_rev).
+            * Those not in the reverse query must be zeroed out!
+            */
+           
+           for(numaddr = numnewaddr = nidx = 0; nidx < IRC_MAXADDRS; nidx++)
+           {
+               int does_match;
+               
+               if(rptr->he.h_addr_list[nidx].s_addr == 0)
+                   break;
+               
+               numaddr++;
+               
+               for(tidx = does_match = 0; tidx < IRC_MAXADDRS; tidx++)
+               {
+                   if(rptr->he_rev.h_addr_list[tidx].s_addr == 0)
+                       break;
+                   
+                   if(rptr->he_rev.h_addr_list[tidx].s_addr == 
+                      rptr->he.h_addr_list[nidx].s_addr) /* MATCH */
+                   {
+                       found_match_ip++;
+                       does_match = 1;
+                       break;
+                   }
+               }
+            
+               if(does_match)
+               {
+                   new_addr_list[numnewaddr++].s_addr =
+                       rptr->he.h_addr_list[nidx].s_addr;
+                   new_addr_list[numnewaddr].s_addr = 0;
+               }
+           }
+         
+           if(!found_match_ip)
+           {
+               char ntoatmp_r[64];
+               char ntoatmp_f[64];
+
+               strcpy(ntoatmp_f, inetntoa((char *)&rptr->he.h_addr_list[0]));
+               strcpy(ntoatmp_r, 
+                      inetntoa((char *)&rptr->he_rev.h_addr_list[0]));
+#ifdef DNS_ANS_DEBUG
+               sendto_realops_lev(DEBUG_LEV, "Forward and Reverse queries do "
+                              "not have matching IP! %s<>%s %s<>%s",
+                              rptr->he.h_name, rptr->he_rev.h_name,
+                              ntoatmp_f, ntoatmp_r);
+#endif
+               if(rptr->cinfo.flags == ASYNC_CLIENT && rptr->cinfo.value.cptr)
+               {
+                   sendto_one(rptr->cinfo.value.cptr,
+                              ":%s NOTICE AUTH :*** Your forward and "
+                              "reverse DNS do not match, "
+                              "ignoring hostname. [%s != %s]",
+                              me.name, ntoatmp_f, ntoatmp_r);
+               }
+               
+               if (lp)
+                   memcpy(lp, (char *) &rptr->cinfo, sizeof(Link));
+               
+               rem_request(rptr);
+               return NULL;
+           }
+           
+           if(numnewaddr != numaddr)
+           {
+               memcpy(rptr->he.h_addr_list, new_addr_list,
+                      sizeof(struct in_addr) * IRC_MAXADDRS);
+#ifdef DNS_ANS_DEBUG
+               sendto_realops_lev(DEBUG_LEV, "numaddr = %d, numnewaddr = %d",
+                              numaddr, numnewaddr);
+#endif
+           }
+           
+           /*
+            * Our DNS query was made based on the hostname, so the hostname
+            * part should be fine.
+            */
+       }
+    }
+    
+    if (a > 0)
+    {
+       if (lp)
+           memcpy(lp, (char *) &rptr->cinfo, sizeof(Link));
+
+       cp = make_cache(rptr);
+#ifdef DEBUG
+       Debug((DEBUG_INFO, "get_res:cp=%#x rptr=%#x (made)", cp, rptr));
+#endif
+       
+       rem_request(rptr);
+    }
+    else if (!rptr->sent)
+       rem_request(rptr);
+    return cp ? (struct hostent *) &cp->he : NULL;
+}
+
+static struct hostent *getres_err(ResRQ * rptr, char *lp)
+{
+    /*
+     * Reprocess an error if the nameserver didnt tell us to
+     * "TRY_AGAIN".
+     */
+    if (rptr)
+    {
+       if (h_errno != TRY_AGAIN)
+       {
+           /*
+            * If we havent tried with the default domain and its set,
+            * then give it a try next.
+            */
+           if (_res.options & RES_DEFNAMES && ++rptr->srch == 0)
+           {
+               rptr->retries = _res.retry;
+               rptr->sends = 0;
+               rptr->resend = 1;
+               resend_query(rptr);
+           }
+           else
+               resend_query(rptr);
+       }
+       else if (lp)
+           memcpy(lp, (char *) &rptr->cinfo, sizeof(Link));
+    }
+    return (struct hostent *) NULL;
+}
+
+static int hash_number(unsigned char *ip)
+{
+    u_int   hashv = 0;
+
+    /* could use loop but slower */
+    hashv += (int) *ip++;
+    hashv += hashv + (int) *ip++;
+    hashv += hashv + (int) *ip++;
+    hashv += hashv + (int) *ip++;
+    hashv %= ARES_CACSIZE;
+    return (hashv);
+}
+
+#ifdef ALLOW_CACHE_NAMES
+static int hash_name(char *name)
+{
+    u_int   hashv = 0;
+    
+    for (; *name && *name != '.'; name++)
+       hashv += *name;
+    hashv %= ARES_CACSIZE;
+    return (hashv);
+}
+#endif
+
+static unsigned int hash_id(unsigned int id)
+{
+   return id % ARES_IDCACSIZE;
+}
+
+static unsigned int hash_cp(char *cp)
+{
+   return ((unsigned int) cp) % ARES_IDCACSIZE;
+}
+
+/* Add a new cache item to the queue and hash table. */
+static aCache *add_to_cache(aCache * ocp)
+{
+    aCache *cp = NULL;
+    int     hashv;
+    
+#ifdef DEBUG
+    Debug((DEBUG_INFO,
+          "add_to_cache:ocp %#x he %#x name %#x addrl %#x 0 %#x",
+          ocp, &ocp->he, ocp->he.h_name, ocp->he.h_addr_list,
+          ocp->he.h_addr_list[0]));
+#endif
+    ocp->list_next = cachetop;
+    cachetop = ocp;
+    /* Make sure non-bind resolvers don't blow up (Thanks to Yves) */
+    if (!ocp)
+       return NULL;
+    if (!(ocp->he.h_name))
+       return NULL;
+    if (!(ocp->he.h_addr))
+       return NULL;
+    
+#ifdef ALLOW_CACHE_NAMES
+    hashv = hash_name(ocp->he.h_name);
+    
+    ocp->hname_next = hashtable[hashv].name_list;
+    hashtable[hashv].name_list = ocp;
+#endif
+    
+    hashv = hash_number((u_char *) ocp->he.h_addr);
+    
+    ocp->hnum_next = hashtable[hashv].num_list;
+    hashtable[hashv].num_list = ocp;
+    
+#ifdef DEBUG
+    Debug((DEBUG_INFO, "add_to_cache:added %s[%08x] cache %#x.",
+          ocp->he.h_name, ocp->he.h_addr_list[0], ocp));
+    Debug((DEBUG_INFO,
+          "add_to_cache:h1 %d h2 %x lnext %#x namnext %#x numnext %#x",
+          hash_name(ocp->he.h_name), hashv, ocp->list_next,
+          ocp->hname_next, ocp->hnum_next));
+#endif
+    /* LRU deletion of excessive cache entries. */
+    if (++incache > IRC_MAXCACHED)
+    {
+       for (cp = cachetop; cp->list_next; cp = cp->list_next);
+       rem_cache(cp);
+    }
+    cainfo.ca_adds++;
+
+    return ocp;
+}
+
+/*
+ * update_list does not alter the cache structure passed. It is
+ * assumed that * it already contains the correct expire time, if it is
+ * a new entry. Old * entries have the expirey time updated.
+ */
+static void update_list(ResRQ * rptr, aCache * cachep)
+{
+    aCache **cpp, *cp = cachep;
+    char   *s, *t, **base;
+    int     i, j;
+    int     addrcount;
+
+    /*
+     * search for the new cache item in the cache list by hostname. *
+     * If found, move the entry to the top of the list and return.
+     */
+    cainfo.ca_updates++;
+
+    for (cpp = &cachetop; *cpp; cpp = &((*cpp)->list_next))
+       if (cp == *cpp)
+           break;
+    if (!*cpp)
+       return;
+    *cpp = cp->list_next;
+    cp->list_next = cachetop;
+    cachetop = cp;
+    if (!rptr)
+       return;
+    
+#ifdef DEBUG
+    Debug((DEBUG_DEBUG, "u_l:cp %#x na %#x al %#x ad %#x",
+          cp, cp->he.h_name, cp->he.h_aliases, cp->he.h_addr));
+    Debug((DEBUG_DEBUG, "u_l:rptr %#x h_n %#x", rptr, rptr->he.h_name));
+#endif
+    /*
+     * Compare the cache entry against the new record.  Add any
+     * previously missing names for this entry.
+     */
+    for (i = 0; cp->he.h_aliases[i]; i++);
+    addrcount = i;
+    for (i = 0, s = rptr->he.h_name; s && i < IRC_MAXALIASES;
+        s = rptr->he.h_aliases[i++])
+    {
+       for (j = 0, t = cp->he.h_name; t && j < IRC_MAXALIASES;
+            t = cp->he.h_aliases[j++])
+           if (!mycmp(t, s))
+               break;
+       if (!t && j < IRC_MAXALIASES - 1)
+       {
+           base = cp->he.h_aliases;
+           
+           addrcount++;
+           base = (char **) MyRealloc(base,
+                                      sizeof(char *) * (addrcount + 1));
+           
+           cp->he.h_aliases = base;
+#ifdef DEBUG
+           Debug((DEBUG_DNS, "u_l:add name %s hal %x ac %d",
+                  s, cp->he.h_aliases, addrcount));
+#endif
+           base[addrcount - 1] = s;
+           base[addrcount] = NULL;
+           if (i)
+               rptr->he.h_aliases[i - 1] = NULL;
+           else
+               rptr->he.h_name = NULL;
+       }
+    }
+    for (i = 0; cp->he.h_addr_list[i]; i++);
+    addrcount = i;
+    /* Do the same again for IP#'s. */
+    for (s = (char *) &rptr->he.h_addr.s_addr;
+        ((struct in_addr *) s)->s_addr; s += sizeof(struct in_addr)) {
+       for (i = 0; (t = cp->he.h_addr_list[i]); i++)
+           if (!memcmp(s, t, sizeof(struct in_addr)))
+               break;
+
+       if (i >= IRC_MAXADDRS || addrcount >= IRC_MAXADDRS)
+           break;
+       /*
+        * Oh man this is bad...I *HATE* it. -avalon
+        * 
+        * Whats it do ?  Reallocate two arrays, one of pointers to "char *"
+        * and the other of IP addresses.  Contents of the IP array *MUST*
+        * be preserved and the pointers into it recalculated.
+        */
+       if (!t)
+       {
+           base = cp->he.h_addr_list;
+           addrcount++;
+           t = (char *) MyRealloc(*base,
+                                  addrcount * sizeof(struct in_addr));
+           
+           base = (char **) MyRealloc(base,
+                                      (addrcount + 1) * sizeof(char *));
+           
+           cp->he.h_addr_list = base;
+#ifdef DEBUG
+           Debug((DEBUG_DNS, "u_l:add IP %x hal %x ac %d",
+                  ntohl(((struct in_addr *) s)->s_addr),
+                  cp->he.h_addr_list,
+                  addrcount));
+#endif
+           for (; addrcount; addrcount--)
+           {
+               *base++ = t;
+               t += sizeof(struct in_addr);
+           }
+           *base = NULL;
+           memcpy(*--base, s, sizeof(struct in_addr));
+       }
+    }
+    return;
+}
+
+static aCache *find_cache_name(char *name)
+{
+#ifdef ALLOW_CACHE_NAMES
+    aCache *cp;
+    char   *s;
+    int     hashv, i;
+    
+    if (name == (char *) NULL)
+       return (aCache *) NULL;
+    hashv = hash_name(name);
+    
+    cp = hashtable[hashv].name_list;
+#ifdef DEBUG
+    Debug((DEBUG_DNS, "find_cache_name:find %s : hashv = %d", name, hashv));
+#endif
+    
+    for (; cp; cp = cp->hname_next)
+       for (i = 0, s = cp->he.h_name; s; s = cp->he.h_aliases[i++])
+           if (mycmp(s, name) == 0)
+           {
+               cainfo.ca_na_hits++;
+               update_list(NULL, cp);
+               return cp;
+           }
+    
+    for (cp = cachetop; cp; cp = cp->list_next)
+    {
+       /*
+        * if no aliases or the hash value matches, we've already done
+        * this entry and all possiblilities concerning it.
+        */
+       if (!*cp->he.h_aliases)
+           continue;
+       if (cp->he.h_name == (char *) NULL)     /*
+                                                * don't trust anything
+                                                * -Dianora 
+                                                */
+           continue;
+       if (hashv == hash_name(cp->he.h_name))
+           continue;
+       for (i = 0, s = cp->he.h_aliases[i]; s && i < IRC_MAXALIASES; i++)
+           if (!mycmp(name, s))
+           {
+               cainfo.ca_na_hits++;
+               update_list(NULL, cp);
+               return cp;
+           }
+    }
+#endif
+    return NULL;
+}
+
+/* find a cache entry by ip# and update its expire time */
+static aCache *
+find_cache_number(ResRQ * rptr, char *numb)
+{
+    aCache *cp;
+    int     hashv, i;
+    struct in_addr *ip = (struct in_addr *) numb;
+
+    if ((u_char *) numb == (u_char *) NULL)
+       return ((aCache *) NULL);
+    hashv = hash_number((u_char *) numb);
+    cp = hashtable[hashv].num_list;
+#ifdef DEBUG
+    Debug((DEBUG_DNS, "find_cache_number:find %s[%08x]: hashv = %d",
+          inetntoa(numb), ntohl(ip->s_addr), hashv));
+#endif
+
+    for (; cp; cp = cp->hnum_next)
+    {
+       for (i = 0; cp->he.h_addr_list[i]; i++)
+       {
+           /* 
+            * A 32 bit integer compare should be faster than this...
+            *  if (!memcmp(cp->he.h_addr_list[i], numb,
+            *     sizeof(struct in_addr))) 
+            */
+           if(((struct in_addr *)cp->he.h_addr_list[i])->s_addr == ip->s_addr)
+           {
+               cainfo.ca_nu_hits++;
+               update_list(NULL, cp);
+               return cp;
+           }
+       }
+    }
+    
+#ifdef SEARCH_CACHE_ADDRESSES
+    for (cp = cachetop; cp; cp = cp->list_next)
+    {
+       /*
+        * single address entry...would have been done by hashed search 
+        * above...
+        */
+       if (!cp->he.h_addr_list[1])
+           continue;
+       /*
+        * if the first IP# has the same hashnumber as the IP# we are
+        * looking for, its been done already.
+        */
+       if (hashv == hash_number((u_char *) cp->he.h_addr_list[0]))
+           continue;
+       for (i = 1; cp->he.h_addr_list[i]; i++)
+           if (!memcmp(cp->he.h_addr_list[i], numb,
+                       sizeof(struct in_addr)))
+           {
+               cainfo.ca_nu_hits++;
+               update_list(NULL, cp);
+               return cp;
+           }
+    }
+#endif
+    return NULL;
+}
+
+static aCache *make_cache(ResRQ * rptr)
+{
+    aCache *cp;
+    int     i, n;
+    struct hostent *hp;
+    char   *s, **t;
+
+    /* shouldn't happen but it just might... */
+    if (!rptr->he.h_name || !rptr->he.h_addr.s_addr)
+       return NULL;
+    /*
+     * Make cache entry.  First check to see if the cache already
+     * exists and if so, return a pointer to it.
+     */
+    if ((cp = find_cache_number(rptr, (char *) &rptr->he.h_addr.s_addr)))
+       return cp;
+    for (i = 1; rptr->he.h_addr_list[i].s_addr && i < IRC_MAXADDRS; i++)
+       if ((cp = 
+            find_cache_number(rptr,
+                              (char *) &(rptr->he.h_addr_list[i].s_addr))))
+           return cp;
+    /* a matching entry wasnt found in the cache so go and make one up. */
+    cp = (aCache *) MyMalloc(sizeof(aCache));
+    memset((char *) cp, '\0', sizeof(aCache));
+    hp = &cp->he;
+    for (i = 0; i < IRC_MAXADDRS; i++)
+       if (!rptr->he.h_addr_list[i].s_addr)
+           break;
+    /* build two arrays, one for IP#'s, another of pointers to them. */
+    t = hp->h_addr_list = (char **) MyMalloc(sizeof(char *) * (i + 1));
+    memset((char *) t, '\0', sizeof(char *) * (i + 1));
+    
+    s = (char *) MyMalloc(sizeof(struct in_addr) * i);
+    memset(s, '\0', sizeof(struct in_addr) * i);
+    
+    for (n = 0; n < i; n++, s += sizeof(struct in_addr))
+    {
+       *t++ = s;
+       memcpy(s, (char *) &(rptr->he.h_addr_list[n].s_addr),
+              sizeof(struct in_addr));
+    }
+    *t = (char *) NULL;
+    /* an array of pointers to CNAMEs. */
+    for (i = 0; i < IRC_MAXALIASES; i++)
+       if (!rptr->he.h_aliases[i])
+           break;
+    i++;
+    t = hp->h_aliases = (char **) MyMalloc(sizeof(char *) * i);
+    
+    for (n = 0; n < i; n++, t++)
+    {
+       *t = rptr->he.h_aliases[n];
+       rptr->he.h_aliases[n] = NULL;
+    }
+    
+    hp->h_addrtype = rptr->he.h_addrtype;
+    hp->h_length = rptr->he.h_length;
+    hp->h_name = rptr->he.h_name;
+    if (rptr->ttl < 600)
+    {
+       reinfo.re_shortttl++;
+       cp->ttl = 600;
+    }
+    else
+       cp->ttl = rptr->ttl;
+    cp->expireat = timeofday + cp->ttl;
+    rptr->he.h_name = NULL;
+#ifdef DEBUG
+    Debug((DEBUG_INFO, "make_cache:made cache %#x", cp));
+#endif
+    return add_to_cache(cp);
+}
+
+/*
+ * rem_cache delete a cache entry from the cache structures and lists
+ * and return all memory used for the cache back to the memory pool.
+ */
+static void rem_cache(aCache * ocp)
+{
+    aCache **cp;
+    struct hostent *hp = &ocp->he;
+    int     hashv;
+    aClient *cptr;
+    
+#ifdef DEBUG
+    Debug((DEBUG_DNS, "rem_cache: ocp %#x hp %#x l_n %#x aliases %#x",
+          ocp, hp, ocp->list_next, hp->h_aliases));
+#endif
+    /*
+     * * Cleanup any references to this structure by destroying the *
+     * pointer.
+     */
+    for (hashv = highest_fd; hashv >= 0; hashv--)
+       if ((cptr = local[hashv]) && (cptr->hostp == hp))
+           cptr->hostp = NULL;
+    /*
+     * remove cache entry from linked list
+     */
+    for (cp = &cachetop; *cp; cp = &((*cp)->list_next))
+       if (*cp == ocp)
+       {
+           *cp = ocp->list_next;
+           break;
+       }
+    /* remove cache entry from hashed name lists */
+    if (hp->h_name == (char *) NULL)
+       return;
+#ifdef ALLOW_CACHE_NAMES
+    hashv = hash_name(hp->h_name);
+    
+# ifdef        DEBUG
+    Debug((DEBUG_DEBUG, "rem_cache: h_name %s hashv %d next %#x first %#x",
+          hp->h_name, hashv, ocp->hname_next,
+          hashtable[hashv].name_list));
+# endif
+    for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next))
+       if (*cp == ocp)
+       {
+           *cp = ocp->hname_next;
+           break;
+       }
+#endif
+    /* remove cache entry from hashed number list */
+    hashv = hash_number((u_char *) hp->h_addr);
+    if (hashv < 0)
+       return;
+#ifdef DEBUG
+    Debug((DEBUG_DEBUG, "rem_cache: h_addr %s hashv %d next %#x first %#x",
+          inetntoa(hp->h_addr), hashv, ocp->hnum_next,
+          hashtable[hashv].num_list));
+#endif
+    for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next))
+       if (*cp == ocp)
+       {
+           *cp = ocp->hnum_next;
+           break;
+       }
+    /*
+     * free memory used to hold the various host names and the array of
+     * alias pointers.
+     */
+    if (hp->h_name)
+       MyFree(hp->h_name);
+    if (hp->h_aliases)
+    {
+       for (hashv = 0; hp->h_aliases[hashv]; hashv++)
+           MyFree(hp->h_aliases[hashv]);
+       MyFree(hp->h_aliases);
+    }
+    /* free memory used to hold ip numbers and the array of them. */
+    if (hp->h_addr_list)
+    {
+       if (*hp->h_addr_list)
+           MyFree(*hp->h_addr_list);
+       MyFree(hp->h_addr_list);
+    }
+    
+    MyFree(ocp);
+    
+    incache--;
+    cainfo.ca_dels++;
+    
+    return;
+}
+
+/*
+ * removes entries from the cache which are older than their expirey
+ * times. returns the time at which the server should next poll the
+ * cache.
+ */
+time_t expire_cache(time_t now)
+{
+    aCache *cp, *cp2;
+    time_t  next = 0;
+    time_t  mmax = now + AR_TTL;
+
+    for (cp = cachetop; cp; cp = cp2)
+    {
+       cp2 = cp->list_next;
+       
+       if (now >= cp->expireat)
+       {
+           cainfo.ca_expires++;
+           rem_cache(cp);
+       }
+       else if (!next || next > cp->expireat)
+           next = cp->expireat;
+    }
+    /*
+     * don't let one DNS record that happens to be first
+     * stop others from expiring.
+     */
+    return (next > now) ? (next < mmax ? next : mmax) : mmax;
+}
+
+/* remove all dns cache entries. */
+void flush_cache()
+{
+    aCache *cp;
+    
+    while ((cp = cachetop))
+       rem_cache(cp);
+}
+
+int m_dns(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aCache *cp;
+    int     i;
+    
+    if (parv[1] && *parv[1] == 'l')
+    {
+        if (!MyClient(sptr) || !IsAdmin(sptr))
+        {
+          sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+          return 0;
+        }
+       for (cp = cachetop; cp; cp = cp->list_next)
+       {
+           sendto_one(sptr, "NOTICE %s :Ex %d ttl %d host %s(%s)",
+                      parv[0], cp->expireat - timeofday, cp->ttl,
+                      cp->he.h_name, inetntoa(cp->he.h_addr));
+           for (i = 0; cp->he.h_aliases[i]; i++)
+               sendto_one(sptr, "NOTICE %s : %s = %s (CN)",
+                          parv[0], cp->he.h_name,
+                          cp->he.h_aliases[i]);
+           for (i = 1; cp->he.h_addr_list[i]; i++)
+               sendto_one(sptr, "NOTICE %s : %s = %s (IP)",
+                          parv[0], cp->he.h_name,
+                          inetntoa(cp->he.h_addr_list[i]));
+       }
+       return 0;
+    }
+    sendto_one(sptr, "NOTICE %s :Ca %d Cd %d Ce %d Cl %d Ch %d:%d Cu %d",
+              sptr->name,
+              cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
+              cainfo.ca_lookups,
+              cainfo.ca_na_hits, cainfo.ca_nu_hits, cainfo.ca_updates);
+    
+    sendto_one(sptr, "NOTICE %s :Re %d Rl %d/%d Rp %d Rq %d",
+              sptr->name, reinfo.re_errors, reinfo.re_nu_look,
+              reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
+    sendto_one(sptr, "NOTICE %s :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr->name,
+              reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
+              reinfo.re_resends, reinfo.re_timeouts);
+    return 0;
+}
diff --git a/src/res_comp.c b/src/res_comp.c
new file mode 100644 (file)
index 0000000..8235d98
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1985 Regents of the University of California. All
+ * rights reserved.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its
+ * contributors'' in the documentation or other materials provided with
+ * the distribution and in all advertising materials mentioning
+ * features or use of this software. Neither the name of the University
+ * nor the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ */
+
+/* $Id: res_comp.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "nameser.h"
+
+static      dn_find();
+
+/*
+ * Expand compressed domain name 'comp_dn' to full domain name. 'msg'
+ * is a pointer to the begining of the message, 'eomorig' points to the
+ * first location after the message, 'exp_dn' is a pointer to a buffer
+ * of size 'length' for the result. Return size of compressed name or
+ * -1 if there was an error.
+ */
+int dn_expand(u_char *msg, u_char *eomorig, u_char *comp_dn, u_char *exp_dn,
+             int length)
+{
+    u_char *cp, *dn;
+    int n, c;
+    u_char     *eom;
+    int         len = -1, checked = 0;
+    
+    dn = exp_dn;
+    cp = comp_dn;
+    eom = exp_dn + length;
+    /* fetch next label in domain name */
+    while (n = *cp++)
+    {
+       /* Check for indirection */
+       switch (n & INDIR_MASK) 
+       {
+       case 0:
+           if (dn != exp_dn)
+           {
+               if (dn >= eom)
+                   return (-1);
+               *dn++ = '.';
+           }
+           if (dn + n >= eom)
+               return (-1);
+           checked += n + 1;
+           while (--n >= 0)
+           {
+               if ((c = *cp++) == '.')
+               {
+                   if (dn + n + 2 >= eom)
+                       return (-1);
+                   *dn++ = '\\';
+               }
+               *dn++ = c;
+               if (cp >= eomorig)      /* out of range */
+                   return (-1);
+           }
+           break;
+           
+       case INDIR_MASK:
+           if (len < 0)
+               len = cp - comp_dn + 1;
+           cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff));
+           if (cp < msg || cp >= eomorig)      /* out of range */
+               return (-1);
+           checked += 2;
+           /*
+            * Check for loops in the compressed name; if we've
+            * looked at the whole message, there must be a loop.
+            */
+           if (checked >= eomorig - msg)
+               return (-1);
+           break;
+
+       default:
+           return (-1);        /*
+                                * flag error 
+                                */
+       }
+    }
+    *dn = '\0';
+    if (len < 0)
+       len = cp - comp_dn;
+    return (len);
+}
+
+/*
+ * Compress domain name 'exp_dn' into 'comp_dn'. Return the size of the
+ * compressed name or -1. 'length' is the size of the array pointed to
+ * by 'comp_dn'. 'dnptrs' is a list of pointers to previous compressed
+ * names. dnptrs[0] is a pointer to the beginning of the message. The
+ * list ends with NULL. 'lastdnptr' is a pointer to the end of the
+ * arrary pointed to by 'dnptrs'. Side effect is to update the list of
+ * pointers for labels inserted into the message as we compress the
+ * name. If 'dnptr' is NULL, we don't try to compress names. If
+ * 'lastdnptr' is NULL, we don't update the list.
+ */
+int dn_comp(u_char *exp_dn, u_char *comp_dn, int length, u_char **dnptrs,
+       u_char **lastdnptr)
+{
+    u_char *cp, *dn;
+    int c, l;
+    u_char    **cpp, **lpp, *sp, *eob;
+    u_char     *msg;
+    
+    dn = exp_dn;
+    cp = comp_dn;
+    eob = cp + length;
+    if (dnptrs != NULL)
+    {
+       if ((msg = *dnptrs++) != NULL)
+       {
+           for (cpp = dnptrs; *cpp != NULL; cpp++);
+           lpp = cpp;          /* end of list to search */
+       }
+    }
+    else
+       msg = NULL;
+
+    for (c = *dn++; c != '\0';)
+    {
+       /* look to see if we can use pointers */
+       if (msg != NULL)
+       {
+           if ((l = dn_find(dn - 1, msg, dnptrs, lpp)) >= 0)
+           {
+               if (cp + 1 >= eob)
+                   return (-1);
+               *cp++ = (l >> 8) | INDIR_MASK;
+               *cp++ = l % 256;
+               return (cp - comp_dn);
+           }
+           /* not found, save it */
+           if (lastdnptr != NULL && cpp < lastdnptr - 1) 
+           {
+               *cpp++ = cp;
+               *cpp = NULL;
+           }
+       }
+       sp = cp++;              /* save ptr to length byte */
+       do
+       {
+           if (c == '.')
+           {
+               c = *dn++;
+               break;
+           }
+           if (c == '\\')
+           {
+               if ((c = *dn++) == '\0')
+                   break;
+           }
+           if (cp >= eob)
+           {
+               if (msg != NULL)
+                   *lpp = NULL;
+               return (-1);
+           }
+           *cp++ = c;
+       } while ((c = *dn++) != '\0');
+
+       /* catch trailing '.'s but not '..' */
+       if ((l = cp - sp - 1) == 0 && c == '\0')
+       {
+           cp--;
+           break;
+       }
+       if (l <= 0 || l > MAXLABEL)
+       {
+           if (msg != NULL)
+               *lpp = NULL;
+           return (-1);
+       }
+       *sp = l;
+    }
+    if (cp >= eob)
+    {
+       if (msg != NULL)
+           *lpp = NULL;
+       return (-1);
+    }
+    *cp++ = '\0';
+    return (cp - comp_dn);
+}
+
+/* Skip over a compressed domain name. Return the size or -1. */
+dn_skipname(u_char *comp_dn, u_char *eom)
+{
+    u_char *cp;
+    int n;
+
+    cp = comp_dn;
+    while (cp < eom && (n = *cp++))
+    {
+       /* check for indirection */
+       switch (n & INDIR_MASK) 
+       {
+       case 0:         /* normal case, n == len */
+           cp += n;
+           continue;
+       default:                /* illegal type */
+           return (-1);
+       case INDIR_MASK:        /* indirection */
+           cp++;
+       }
+       break;
+    }
+    return (cp - comp_dn);
+}
+
+/*
+ * Search for expanded name from a list of previously compressed names.
+ * Return the offset from msg if found or -1. dnptrs is the pointer to
+ * the first name on the list, not the pointer to the start of the
+ * message.
+ */
+int staticdn_find(u_char *exp_dn, u_char *msg, u_char **dnptrs, 
+                 u_char **lastdnptr)
+{
+    u_char *dn, *cp, **cpp;
+    int n;
+    u_char     *sp;
+    
+    for (cpp = dnptrs; cpp < lastdnptr; cpp++)
+    {
+       dn = exp_dn;
+       sp = cp = *cpp;
+       while (n = *cp++)
+       {
+           /* check for indirection */
+           switch (n & INDIR_MASK) 
+           {
+           case 0:             /* normal case, n == len */
+               while (--n >= 0) 
+               {
+                   if (*dn == '.')
+                       goto next;
+                   if (*dn == '\\')
+                       dn++;
+                   if (*dn++ != *cp++)
+                       goto next;
+               }
+               if ((n = *dn++) == '\0' && *cp == '\0')
+                   return (sp - msg);
+               if (n == '.')
+                   continue;
+               goto next;
+
+           default:            /* illegal type */
+               return (-1);
+
+           case INDIR_MASK:    /* indirection */
+               cp = msg + (((n & 0x3f) << 8) | *cp);
+           }
+       }
+       if (*dn == '\0')
+           return (sp - msg);
+    next:;
+    }
+    return (-1);
+}
+
+/*
+ * Routines to insert/extract short/long's. Must account for byte order
+ * and non-alignment problems. This code at least has the advantage of
+ * being portable.
+ * 
+ * used by sendmail.
+ */
+
+u_short _getshort(u_char *msgp)
+{
+    u_char *p = (u_char *) msgp;
+
+#ifdef vax
+    /* vax compiler doesn't put shorts in registers */
+    u_long u;
+#else
+    u_short u;
+#endif
+
+    u = *p++ << 8;
+    return ((u_short) (u | *p));
+}
+
+u_long _getlong(u_char *msgp)
+{
+    u_char *p = (u_char *) msgp;
+    u_long u;
+
+    u = *p++;
+    u <<= 8;
+    u |= *p++;
+    u <<= 8;
+    u |= *p++;
+    u <<= 8;
+    return (u | *p);
+}
+
+int putshort(u_short s, u_char *msgp)
+{
+
+    msgp[1] = s;
+    msgp[0] = s >> 8;
+}
+
+int putlong(u_long l, u_char *msgp)
+{
+    msgp[3] = l;
+    msgp[2] = (l >>= 8);
+    msgp[1] = (l >>= 8);
+    msgp[0] = l >> 8;
+}
diff --git a/src/res_init.c b/src/res_init.c
new file mode 100644 (file)
index 0000000..b89fc97
--- /dev/null
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement:  ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Id: res_init.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include "config.h"            /* To get #define SOL20 */
+#include "sys.h"
+#include "nameser.h"
+#include "resolv.h"
+
+/* Resolver state default settings */
+struct state _res =
+{
+    RES_TIMEOUT,               /* retransmition time interval */
+    4,                         /* number of times to retransmit */
+    RES_DEFAULT,               /* options flags */
+    1,                         /* number of name servers */
+};
+
+/*
+ * Set up default settings.  If the configuration file exist, the
+ * values there will have precedence.  Otherwise, the server address is
+ * set to INADDR_ANY and the default domain name comes from the
+ * gethostname().
+ * 
+ * The configuration file should only be used if you want to redefine your
+ * domain or run without a server on your machine.
+ * 
+ * Return 0 if completes successfully, -1 on error
+ */
+int res_init()
+{
+    FILE *fp;
+    char *cp, *dp, **pp;
+    int n;
+    char        buf[BUFSIZ];
+    extern u_long inet_addr();
+    extern char *getenv();
+    int         nserv = 0;   /* number of nameserver records read from file */
+    int         norder = 0;
+    int         haveenv = 0;
+    int         havesearch = 0;
+
+    _res.nsaddr.sin_addr.s_addr = INADDR_ANY;
+    _res.nsaddr.sin_family = AF_INET;
+    _res.nsaddr.sin_port = htons(NAMESERVER_PORT);
+    _res.nscount = 1;
+
+    /* Allow user to override the local domain definition */
+    if ((cp = getenv("LOCALDOMAIN")) != NULL)
+    {
+       (void) strncpy(_res.defdname, cp, sizeof(_res.defdname));
+       haveenv++;
+    }
+
+    if ((fp = fopen(_PATH_RESCONF, "r")) != NULL)
+    {
+       /* read the config file */
+       while (fgets(buf, sizeof(buf), fp) != NULL)
+       {
+           /* read default domain name */
+           if (!strncmp(buf, "domain", sizeof("domain") - 1))
+           {
+               if (haveenv)    /* skip if have from environ */
+                   continue;
+               cp = buf + sizeof("domain") - 1;
+               while (*cp == ' ' || *cp == '\t')
+                   cp++;
+               if ((*cp == '\0') || (*cp == '\n'))
+                   continue;
+               (void) strncpy(_res.defdname, cp, sizeof(_res.defdname) - 1);
+               if ((cp = strchr(_res.defdname, '\n')) != NULL)
+                   *cp = '\0';
+               havesearch = 0;
+               continue;
+           }
+           /* set search list */
+           if (!strncmp(buf, "search", sizeof("search") - 1)) {
+               if (haveenv)    /* skip if have from environ */
+                   continue;
+               cp = buf + sizeof("search") - 1;
+               while (*cp == ' ' || *cp == '\t')
+                   cp++;
+               if ((*cp == '\0') || (*cp == '\n'))
+                   continue;
+               (void) strncpy(_res.defdname, cp, sizeof(_res.defdname) - 1);
+               if ((cp = strchr(_res.defdname, '\n')) != NULL)
+                   *cp = '\0';
+               /*
+                * Set search list to be blank-separated strings on rest of
+                * line.
+                */
+               cp = _res.defdname;
+               pp = _res.dnsrch;
+               *pp++ = cp;
+               for (n = 0; *cp && pp < _res.dnsrch + MAXDNSRCH; cp++)
+               {
+                   if (*cp == ' ' || *cp == '\t')
+                   {
+                       *cp = 0;
+                       n = 1;
+                   }
+                   else if (n)
+                   {
+                       *pp++ = cp;
+                       n = 0;
+                   }
+               }
+               /* null terminate last domain if there are excess */
+               while (*cp != '\0' && *cp != ' ' && *cp != '\t')
+                   cp++;
+               *cp = '\0';
+               *pp++ = 0;
+               havesearch = 1;
+               continue;
+           }
+           /* read nameservers to query */
+           if (!strncmp(buf, "nameserver", sizeof("nameserver") - 1) &&
+               nserv < MAXNS)
+           {
+               cp = buf + sizeof("nameserver") - 1;
+               while (*cp == ' ' || *cp == '\t')
+                   cp++;
+               if ((*cp == '\0') || (*cp == '\n'))
+                   continue;
+               if ((_res.nsaddr_list[nserv].sin_addr.s_addr =
+                    inet_addr(cp)) == (unsigned) -1)
+               {
+                   _res.nsaddr_list[nserv].sin_addr.s_addr
+                       = INADDR_ANY;
+                   continue;
+               }
+               _res.nsaddr_list[nserv].sin_family = AF_INET;
+               _res.nsaddr_list[nserv].sin_port = htons(NAMESERVER_PORT);
+               nserv++;
+               continue;
+           }
+           /* read service order */
+           if (!strncmp(buf, "order", sizeof("order") - 1))
+           {
+               cp = buf + sizeof("order") - 1;
+               while (*cp == ' ' || *cp == '\t')
+                   cp++;
+               if ((*cp == '\0') || (*cp == '\n'))
+                   continue;
+               norder = 0;
+               do 
+               {
+                   if ((dp = strchr(cp, ',')) != NULL)
+                       *dp = '\0';
+                   if (norder >= MAXSERVICES)
+                       continue;
+                   if (!strncmp(cp, "bind", sizeof("bind") - 1))
+                       _res.order[norder++] = RES_SERVICE_BIND;
+                   else if (!strncmp(cp, "local", sizeof("local") - 1))
+                       _res.order[norder++] = RES_SERVICE_LOCAL;
+                   cp = dp + 1;
+               } while (dp != NULL);
+               _res.order[norder] = RES_SERVICE_NONE;
+               continue;
+           }
+       }
+       if (nserv > 1)
+           _res.nscount = nserv;
+       (void) fclose(fp);
+    }
+    if (_res.defdname[0] == 0)
+    {
+       if (gethostname(buf, sizeof(_res.defdname)) == 0 &&
+           (cp = strchr(buf, '.')))
+           (void) strcpy(_res.defdname, cp + 1);
+    }
+    
+    /* find components of local domain that might be searched */
+    if (havesearch == 0) 
+    {
+       pp = _res.dnsrch;
+       *pp++ = _res.defdname;
+       for (cp = _res.defdname, n = 0; *cp; cp++)
+           if (*cp == '.')
+               n++;
+       cp = _res.defdname;
+       for (; n >= LOCALDOMAINPARTS && pp < _res.dnsrch + MAXDFLSRCH; n--)
+       {
+           cp = strchr(cp, '.');
+           *pp++ = ++cp;
+       }
+       *pp++ = 0;
+    }
+    /* default search order to bind only */
+    if (norder == 0) 
+    {
+       _res.order[0] = RES_SERVICE_BIND;
+       _res.order[1] = RES_SERVICE_NONE;
+    }
+    _res.options |= RES_INIT;
+    return (0);
+}
diff --git a/src/res_mkquery.c b/src/res_mkquery.c
new file mode 100644 (file)
index 0000000..d3ae70d
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1985 Regents of the University of California. All
+ * rights reserved.
+ * 
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its
+ * contributors'' in the documentation or other materials provided with
+ * the distribution and in all advertising materials mentioning
+ * features or use of this software. Neither the name of the University
+ * nor the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ */
+
+/* $Id: res_mkquery.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include "config.h"
+#include "sys.h"
+#include "nameser.h"
+#include "resolv.h"
+
+/* Form all types of queries. Returns the size of the result or -1. */
+int res_mkquery(int op, char *dname, int class, int type, char *data,
+               int datalen, struct rrec *newrr, char *buf, int buflen)
+{
+    HEADER *hp;
+    char *cp;
+    int n;
+    char       *dnptrs[10], **dpp, **lastdnptr;
+    
+#ifdef DEBUG
+    if (_res.options & RES_DEBUG)
+       printf("res_mkquery(%d, %s, %d, %d)\n", op, dname, class, type);
+#endif /* DEBUG */
+    /* Initialize header fields. */
+    if ((buf == NULL) || (buflen < sizeof(HEADER)))
+       return (-1);
+    memset(buf, '\0', sizeof(HEADER));
+
+    hp = (HEADER *) buf;
+    hp->id = htons(++_res.id);
+    hp->opcode = op;
+    hp->pr = (_res.options & RES_PRIMARY) != 0;
+    hp->rd = (_res.options & RES_RECURSE) != 0;
+    hp->rcode = NOERROR;
+    cp = buf + sizeof(HEADER);
+    buflen -= sizeof(HEADER);
+    
+    dpp = dnptrs;
+    *dpp++ = buf;
+    *dpp++ = NULL;
+    lastdnptr = dnptrs + sizeof(dnptrs) / sizeof(dnptrs[0]);
+    /* perform opcode specific processing */
+    switch (op) 
+    {
+    case QUERY:
+       if ((buflen -= QFIXEDSZ) < 0)
+           return (-1);
+       if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
+           return (-1);
+       cp += n;
+       buflen -= n;
+       putshort(type, cp);
+       cp += sizeof(u_short);
+       
+       putshort(class, cp);
+       cp += sizeof(u_short);
+
+       hp->qdcount = htons(1);
+       if (op == QUERY || data == NULL)
+           break;
+       /* Make an additional record for completion domain. */
+       buflen -= RRFIXEDSZ;
+       if ((n = dn_comp(data, cp, buflen, dnptrs, lastdnptr)) < 0)
+           return (-1);
+       cp += n;
+       buflen -= n;
+       putshort(T_NULL, cp);
+       cp += sizeof(u_short);
+       
+       putshort(class, cp);
+       cp += sizeof(u_short);
+
+       putlong(0, cp);
+       cp += sizeof(u_long);
+
+       putshort(0, cp);
+       cp += sizeof(u_short);
+
+       hp->arcount = htons(1);
+       break;
+
+    case IQUERY:
+       /* Initialize answer section */
+       if (buflen < 1 + RRFIXEDSZ + datalen)
+           return (-1);
+       *cp++ = '\0';           /* no domain name */
+       putshort(type, cp);
+       cp += sizeof(u_short);
+       
+       putshort(class, cp);
+       cp += sizeof(u_short);
+
+       putlong(0, cp);
+       cp += sizeof(u_long);
+
+       putshort(datalen, cp);
+       cp += sizeof(u_short);
+
+       if (datalen)
+       {
+           memcpy(cp, data, datalen);
+           cp += datalen;
+       }
+       hp->ancount = htons(1);
+       break;
+       
+#ifdef ALLOW_UPDATES
+       /*
+        * For UPDATEM/UPDATEMA, do UPDATED/UPDATEDA followed by
+        * UPDATEA (Record to be modified is followed by its
+        * replacement in msg.)
+        */
+    case UPDATEM:
+    case UPDATEMA:
+
+    case UPDATED:
+       /*
+        * The res code for UPDATED and UPDATEDA is the same;
+        * user calls them differently: specifies data for
+        * UPDATED; server ignores data if specified for
+        * UPDATEDA.
+        */
+    case UPDATEDA:
+       buflen -= RRFIXEDSZ + datalen;
+       if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
+           return (-1);
+       cp += n;
+       putshort(type, cp);
+       cp += sizeof(u_short);
+
+       putshort(class, cp);
+       cp += sizeof(u_short);
+
+       putlong(0, cp);
+       cp += sizeof(u_long);
+
+       putshort(datalen, cp);
+       cp += sizeof(u_short);
+
+       if (datalen)
+       {
+           memcpy(cp, data, datalen);
+           cp += datalen;
+       }
+       if ((op == UPDATED) || (op == UPDATEDA))
+       {
+           hp->ancount = htons(0);
+           break;
+       }
+       /* Else UPDATEM/UPDATEMA, so drop into code for UPDATEA */
+
+    case UPDATEA:              /* Add new resource record */
+       buflen -= RRFIXEDSZ + datalen;
+       if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
+           return (-1);
+       cp += n;
+       putshort(newrr->r_type, cp);
+       cp += sizeof(u_short);
+
+       putshort(newrr->r_class, cp);
+       cp += sizeof(u_short);
+
+       putlong(0, cp);
+       cp += sizeof(u_long);
+
+       putshort(newrr->r_size, cp);
+       cp += sizeof(u_short);
+
+       if (newrr->r_size)
+       {
+           memcpy(cp, newrr->r_data, newrr->r_size);
+           cp += newrr->r_size;
+       }
+       hp->ancount = htons(0);
+       break;
+
+#endif /* ALLOW_UPDATES */
+    }
+    return (cp - buf);
+}
diff --git a/src/s_auth.c b/src/s_auth.c
new file mode 100644 (file)
index 0000000..3d5f434
--- /dev/null
@@ -0,0 +1,317 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/s_auth.c
+ *   Copyright (C) 1992 Darren Reed
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: s_auth.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "res.h"
+#include "numeric.h"
+#include "patchlevel.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#if defined(__hpux)
+#include "inet.h"
+#endif
+#include <fcntl.h>
+#include "sock.h"    /* If FD_ZERO isn't define up to this point */
+/* define it (BSD4.2 needs this) */
+#include "h.h"
+#include "fdlist.h"
+#include "fds.h"
+
+static void authsenderr(aClient *);
+
+/*
+ * start_auth
+ * 
+ * Flag the client to show that an attempt to contact the ident server on
+ * the client's host.  The connect and subsequently the socket are all
+ * put into 'non-blocking' mode.  Should the connect or any later phase
+ * of the identifing process fail, it is aborted and the user is given
+ * a username of "unknown".
+ */
+void start_auth(aClient *cptr)
+{
+    struct sockaddr_in sock;
+    struct sockaddr_in localaddr;
+    int         locallen;
+
+    Debug((DEBUG_NOTICE, "start_auth(%x) fd %d status %d",
+          cptr, cptr->fd, cptr->status));
+    if ((cptr->authfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+    {
+#ifdef USE_SYSLOG
+       syslog(LOG_ERR, "Unable to create auth socket for %s:%m",
+              get_client_name(cptr, TRUE));
+#endif
+       ircstp->is_abad++;
+       return;
+    }
+    if (cptr->authfd >= MAXCONNECTIONS)
+    {
+       sendto_realops_lev(DEBUG_LEV,"Can't allocate fd for auth on %s",
+                  get_client_name(cptr, (IsServer(cptr) ? HIDEME : TRUE)));
+       close(cptr->authfd);
+        cptr->authfd = -1;
+       return;
+    }
+#ifdef SHOW_HEADERS
+    sendto_one(cptr, REPORT_DO_ID);
+#endif
+    set_non_blocking(cptr->authfd, cptr);
+    /*
+     * get the local address of the client and bind to that to make the
+     * auth request.  This used to be done only for ifdef VIRTTUAL_HOST,
+     * but needs to be done for all clients since the ident request must
+     * originate from that same address-- and machines with multiple IP
+     * addresses are common now
+     */
+    locallen = sizeof(struct sockaddr_in);
+
+    memset(&localaddr, '\0', locallen);
+    getsockname(cptr->fd, (struct sockaddr *) &localaddr, &locallen);
+    localaddr.sin_port = htons(0);
+
+    if (bind(cptr->authfd, (struct sockaddr *) &localaddr,
+            sizeof(localaddr)) == -1) 
+    {
+       report_error("binding auth stream socket %s:%s", cptr);
+       close(cptr->authfd);
+        cptr->authfd = -1;
+       return;
+    }
+    
+    memcpy((char *) &sock.sin_addr, (char *) &cptr->ip,
+          sizeof(struct in_addr));
+
+    sock.sin_port = htons(113);
+    sock.sin_family = AF_INET;
+
+    if (connect(cptr->authfd, (struct sockaddr *) &sock,
+               sizeof(sock)) == -1 && errno != EINPROGRESS)
+    {
+       ircstp->is_abad++;
+       /* No error report from this... */
+       close(cptr->authfd);
+       cptr->authfd = -1;
+#ifdef SHOW_HEADERS
+       sendto_one(cptr, REPORT_FAIL_ID);
+#endif
+       return;
+    }
+
+    cptr->flags |= (FLAGS_WRAUTH | FLAGS_AUTH);
+    if (cptr->authfd > highest_fd)
+       highest_fd = cptr->authfd;
+
+    add_fd(cptr->authfd, FDT_AUTH, cptr);
+    return;
+}
+
+/*
+ * send_authports
+ * 
+ * Send the ident server a query giving "theirport , ourport". The write
+ * is only attempted *once* so it is deemed to be a fail if the entire
+ * write doesn't write all the data given.  This shouldnt be a problem
+ * since the socket should have a write buffer far greater than this
+ * message to store it in should problems arise. -avalon
+ */
+void send_authports(aClient *cptr)
+{
+    struct sockaddr_in us, them;
+    char        authbuf[32];
+    int         ulen, tlen;
+
+    Debug((DEBUG_NOTICE, "write_authports(%x) fd %d authfd %d stat %d",
+          cptr, cptr->fd, cptr->authfd, cptr->status));
+    tlen = ulen = sizeof(us);
+
+    if (getsockname(cptr->fd, (struct sockaddr *) &us, &ulen) ||
+       getpeername(cptr->fd, (struct sockaddr *) &them, &tlen))
+    {
+#ifdef USE_SYSLOG
+       syslog(LOG_DEBUG, "auth get{sock,peer}name error for %s:%m",
+              get_client_name(cptr, TRUE));
+#endif
+       authsenderr(cptr);
+       return;
+    }
+
+    (void) ircsprintf(authbuf, "%u , %u\r\n",
+                     (unsigned int) ntohs(them.sin_port),
+                     (unsigned int) ntohs(us.sin_port));
+    
+    Debug((DEBUG_SEND, "sending [%s] to auth port %s.113",
+          authbuf, inetntoa((char *) &them.sin_addr)));
+
+    if (send(cptr->authfd, authbuf, strlen(authbuf), 0) != strlen(authbuf)) {
+       authsenderr(cptr);
+       return;
+    }
+    
+    cptr->flags &= ~FLAGS_WRAUTH;
+    
+    return;
+}
+
+/*
+ * authsenderr() *  
+ * input - pointer to aClient output
+ */
+static void authsenderr(aClient *cptr)
+{
+    ircstp->is_abad++;
+
+    del_fd(cptr->authfd);
+
+    close(cptr->authfd);
+    if (cptr->authfd == highest_fd)
+       while (!local[highest_fd])
+           highest_fd--;
+    cptr->authfd = -1;
+    cptr->flags &= ~(FLAGS_AUTH | FLAGS_WRAUTH);
+#ifdef SHOW_HEADERS
+    sendto_one(cptr, REPORT_FAIL_ID);
+#endif
+
+    return;
+}
+
+/*
+ * read_authports
+ *
+ * read the reply (if any) from the ident server we connected to. The
+ * actual read processing here is pretty weak - no handling of the
+ * reply if it is fragmented by IP.
+ *
+ * Whoever wrote this code should be shot.
+ * Looks like it's trouncing on memory it shouldn't be.
+ * Rewriting, some credit goes to wd for saving me time with his code.
+ * - lucas
+ */
+
+#define AUTHBUFLEN 128
+
+void read_authports(aClient *cptr)
+{
+   char buf[AUTHBUFLEN], usern[USERLEN + 1];
+   int len, userncnt;
+   char *userid = "", *s, *reply, *os, *tmp;
+
+   len = recv(cptr->authfd, buf, AUTHBUFLEN, 0);
+
+   if(len > 0)
+   {
+      do
+      {
+         if(buf[len - 1] != '\n')
+            break;
+
+         buf[--len] = '\0';
+
+         if(len == 0)
+            break;
+
+         if(buf[len - 1] == '\r')
+            buf[--len] = '\0';
+
+         if(len == 0)
+            break;
+
+         s = strchr(buf, ':');
+         if(!s)
+            break;
+         s++;
+
+         while(IsSpace(*s))
+            s++;
+
+         reply = s;
+         if(strncmp(reply, "USERID", 6))
+            break;
+
+         s = strchr(reply, ':');
+         if(!s)
+            break;
+         s++;
+
+         while(IsSpace(*s))
+            s++;
+
+         os = s;
+
+         s = strchr(os, ':');
+         if(!s)
+            break;
+         s++;
+
+         while(IsSpace(*s))
+            s++;
+
+         userid = tmp = usern;
+         /* s is the pointer to the beginning of the userid field */
+         for(userncnt = USERLEN; *s && userncnt; s++)
+         {
+            if(*s == '@')
+               break;
+
+            if(!IsSpace(*s) && *s != ':')
+            {
+               *tmp++ = *s;
+               userncnt--;
+            }
+         }
+         *tmp = '\0';
+
+      } while(0);
+   }
+
+   del_fd(cptr->authfd);
+   close(cptr->authfd);
+   if (cptr->authfd == highest_fd)
+      while (!local[highest_fd])
+         highest_fd--;
+   cptr->authfd = -1;
+   ClearAuth(cptr);
+
+   if (!*userid)
+   {
+      ircstp->is_abad++;
+      strcpy(cptr->username, "unknown");
+#ifdef SHOW_HEADERS
+      sendto_one(cptr, REPORT_FAIL_ID);
+#endif
+      return;
+   }
+#ifdef SHOW_HEADERS
+   else
+      sendto_one(cptr, REPORT_FIN_ID);
+#endif
+
+   ircstp->is_asuc++;
+   strncpyzt(cptr->username, userid, USERLEN + 1);
+   cptr->flags |= FLAGS_GOTID;
+   return;
+}
+
diff --git a/src/s_bsd.c b/src/s_bsd.c
new file mode 100644 (file)
index 0000000..3af59b8
--- /dev/null
@@ -0,0 +1,1960 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/s_bsd.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: s_bsd.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "res.h"
+#include "numeric.h"
+#include "patchlevel.h"
+#include "zlink.h"
+#include "throttle.h"
+#include "userban.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#if defined(SOL20)
+#include <sys/filio.h>
+#include <sys/select.h>
+#include <unistd.h>
+#endif
+#include "inet.h"
+#include <stdio.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <sys/resource.h>
+#include "hooks.h"
+
+#ifdef  AIX
+#include <time.h>
+#include <arpa/nameser.h>
+#else
+#include "nameser.h"
+#endif
+#include "resolv.h"
+
+/* If FD_ZERO isn't define up to this point,
+ * define it (BSD4.2 needs this) */
+
+#include "h.h"
+#include "fdlist.h"
+#include "fds.h"
+
+extern void      engine_init();
+extern fdlist default_fdlist;
+extern int forked;
+extern void free_port(aPort *);
+
+#ifndef IN_LOOPBACKNET
+#define IN_LOOPBACKNET  0x7f
+#endif
+
+#if defined(MAXBUFFERS)
+int rcvbufmax = 0, sndbufmax = 0;
+#endif
+
+#ifdef MAXBUFFERS
+void reset_sock_opts(int, int);
+#endif
+
+static void set_listener_sock_opts(int, aListener *);
+static void set_listener_non_blocking(int, aListener *);
+/* listener list, count */
+aListener *listen_list = NULL;
+int listen_count = 0;
+
+
+aClient *local[MAXCONNECTIONS];
+int highest_fd = 0, resfd = -1;
+time_t timeofday;
+static struct sockaddr_in mysk;
+
+static struct sockaddr *connect_inet(aConnect *, aClient *, int *);
+static int check_init(aClient *, char *);
+static void set_sock_opts(int, aClient *);
+
+#if defined(MAXBUFFERS)
+static char *readbuf;
+#else
+static char readbuf[8192];
+#endif
+
+/* Silly macro to ignore certain report error statements */
+#define silent_report_error(x,y)
+
+
+/*
+ * Try and find the correct name to use with getrlimit() for setting
+ * the max. number of files allowed to be open by this process.
+ */
+
+#ifdef RLIMIT_FDMAX
+#define RLIMIT_FD_MAX   RLIMIT_FDMAX
+#else
+#ifdef RLIMIT_NOFILE
+#define RLIMIT_FD_MAX RLIMIT_NOFILE
+#else
+#ifdef RLIMIT_OPEN_MAX
+#define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
+#else
+#undef RLIMIT_FD_MAX
+#endif
+#endif
+#endif
+
+/*
+ * add_local_domain() 
+ * Add the domain to hostname, if it is missing
+ * (as suggested by eps@TOASTER.SFSU.EDU)
+ */
+
+void add_local_domain(char *hname, int size)
+{
+#ifdef RES_INIT
+    /* try to fix up unqualified name */
+    if (!strchr(hname, '.')) 
+    {
+        if (size < 3)
+            return;
+        if (!(_res.options & RES_INIT))
+        {
+            Debug((DEBUG_DNS, "res_init()"));
+            res_init();
+        }
+        if (_res.defdname[0])
+        {
+            strncat(hname, ".", size - 1);
+            strncat(hname, _res.defdname, size - 2);
+        }
+    }
+#endif
+    return;
+}
+
+/*
+ * Cannot use perror() within daemon. stderr is closed in 
+ * ircd and cannot be used. And, worse yet, it might have 
+ * been reassigned to a normal connection...
+ */
+
+/*
+ * report_error 
+ * This a replacement for perror(). Record error to log and 
+ * also send a copy to all *LOCAL* opers online. 
+ * text    is a *format* string for outputting error. It must
+ * contain only two '%s', the first will be replaced by the
+ * sockhost from the cptr, and the latter will be taken from 
+ * sys_errlist[errno].
+ * 
+ * cptr, if not NULL, is the *LOCAL* client associated with
+ * the error.
+ */
+
+void report_error(char *text, aClient * cptr)
+{
+    int errtmp = errno;     /* debug may change 'errno' */
+    char *host;
+    int err, len = sizeof(err);
+    extern char *strerror();
+
+    host = (cptr) ? get_client_name(cptr, (IsServer(cptr) ? HIDEME : FALSE)) 
+                  : "";
+
+    Debug((DEBUG_ERROR, text, host, strerror(errtmp)));
+    /* 
+     * Get the *real* error from the socket (well try to anyway..). This
+     * may only work when SO_DEBUG is enabled but its worth the gamble
+     * anyway.
+     */
+
+#ifdef  SO_ERROR
+    if (!IsMe(cptr) && cptr->fd >= 0)
+        if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len))
+            if (err)
+                errtmp = err;
+#endif
+    sendto_realops_lev(DEBUG_LEV, text, host, strerror(errtmp));
+#ifdef USE_SYSLOG
+    syslog(LOG_WARNING, text, host, strerror(errtmp));
+    if (!forked)
+    {
+        fprintf(stderr, text, host, strerror(errtmp));
+        fprintf(stderr, "\n");
+    }
+#endif
+    return;
+}
+
+void report_listener_error(char *text, aListener *lptr)
+{
+    int errtmp = errno;          /* debug may change 'errno' */
+    char *host;
+    int err, len = sizeof(err);
+    extern char *strerror();
+
+    host = get_listener_name(lptr);
+
+    Debug((DEBUG_ERROR, text, host, strerror(errtmp)));
+
+#ifdef  SO_ERROR
+    if (lptr->fd >= 0)
+        if (!getsockopt(lptr->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len))
+            if (err)
+                errtmp = err;
+#endif
+    sendto_realops_lev(DEBUG_LEV, text, host, strerror(errtmp));
+#ifdef USE_SYSLOG
+    syslog(LOG_WARNING, text, host, strerror(errtmp));
+#endif
+    if (!forked) 
+    {
+        fprintf(stderr, text, host, strerror(errtmp));
+        fprintf(stderr, "\n");
+    }
+    return;
+}
+
+/*
+ * open_listeners()
+ *
+ * cycle through our entire ports list and open them if they
+ * arent already open.
+ * Added Feb/04 -epi
+ */
+
+void
+open_listeners()
+{
+    aPort *tmp;
+    if(!ports)
+        sendto_realops("Lost all port configurations!");
+    for(tmp = ports; tmp; tmp = tmp->next)
+    {
+        if(tmp->lstn)
+            continue;
+        add_listener(tmp);
+    }
+    return;
+}
+
+/*
+ * add_listener
+ *
+ * Create a new client which is essentially the stub like 'me' to be used
+ * for a socket that is passive (listen'ing for connections to be
+ * accepted).
+ * Backported from defunct 1.6 and updated for aPort structure in Feb04.
+ * I'm assuming lucas rewrote this originally. -epi
+ */
+int add_listener(aPort *aport)
+{
+    aListener *lptr;
+    aListener lstn;
+    struct sockaddr_in server;
+    int ad[4], len = sizeof(server);
+    char ipname[20];
+
+    memset(&lstn, 0, sizeof(aListener));
+    ad[0] = ad[1] = ad[2] = ad[3] = 0;
+
+    if(!BadPtr(aport->allow))
+    {
+        strncpyzt(lstn.allow_string, aport->allow, sizeof(lstn.allow_string));
+        sscanf(lstn.allow_string, "%d.%d.%d.%d", &ad[0], &ad[1], 
+                                                 &ad[2], &ad[3]);
+        ircsprintf(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
+        lstn.allow_ip.s_addr = inet_addr(ipname);
+    }
+
+    if (!BadPtr(aport->address) && (*aport->address != '*'))
+    {
+        lstn.vhost_ip.s_addr = inet_addr(aport->address);
+        strncpyzt(lstn.vhost_string, aport->address, sizeof(lstn.vhost_string));
+    }
+
+    lstn.port = aport->port;
+
+    if(lstn.port <= 0) /* stop stupidity cold */
+        return -1;
+
+    lstn.fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (lstn.fd < 0)
+    {
+        report_listener_error("opening stream socket %s:%s", &lstn);
+        return -1;
+    }
+
+    set_listener_sock_opts(lstn.fd, &lstn);
+
+    memset(&server, '\0', sizeof(server));
+    server.sin_family = AF_INET;
+
+    if (lstn.vhost_ip.s_addr)
+        server.sin_addr.s_addr = lstn.vhost_ip.s_addr;
+    else
+        server.sin_addr.s_addr = INADDR_ANY;
+
+    server.sin_port = htons(lstn.port);
+
+    if (bind(lstn.fd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)))
+    {
+        report_listener_error("binding stream socket %s:%s", &lstn);
+        close(lstn.fd);
+        return -1;
+    }
+
+    if (getsockname(lstn.fd, (struct sockaddr *) &server, &len)) 
+    {
+        report_listener_error("getsockname failed for %s:%s", &lstn);
+        close(lstn.fd);
+        return -1;
+    }
+
+    if (lstn.fd > highest_fd)
+        highest_fd = lstn.fd;
+
+#ifdef SOMAXCONN
+    if(listen(lstn.fd, SOMAXCONN))
+#else
+    if(listen(lstn.fd, HYBRID_SOMAXCONN))
+#endif
+    {
+        report_listener_error("error listening on FD %s:%s", &lstn);
+        close(lstn.fd);
+        return -1;
+    }
+
+
+    lptr = (aListener *) MyMalloc(sizeof(aListener));
+    memcpy(lptr, &lstn, sizeof(aListener));
+
+    if(local[lptr->fd])
+    {
+        report_listener_error("!!!! listener fd is held by client"
+                              " in local[] array %s:%s", &lstn);
+        abort();
+    }
+
+    lptr->aport = aport;
+    aport->lstn = lptr;
+
+    set_listener_non_blocking(lptr->fd, lptr);
+    add_fd(lptr->fd, FDT_LISTENER, lptr);
+    set_fd_flags(lptr->fd, FDF_WANTREAD);
+
+    listen_count++;
+    lptr->next = listen_list;
+    listen_list = lptr;
+
+    lptr->lasttime = timeofday;
+
+    return 0;
+}
+
+void close_listener(aListener *lptr)
+{
+    aListener *alptr, *alptrprev = NULL;
+    aPort *aport, *aportl, *aportn = NULL;
+
+    del_fd(lptr->fd);
+    close(lptr->fd);
+
+    /* drop our conf link */
+    aport = lptr->aport;
+    aport->lstn = NULL;
+
+    /* and now drop the conf itself */
+
+    for(aportl = ports ; aportl ; aportl = aportl->next)
+    {
+        if(aportl == aport)
+        {
+            if(aportn)
+                aportn->next = aportl->next;
+            else
+                ports = aportl->next;
+            free_port(aportl);
+            break;
+        }
+        aportn = aportl;
+    }
+
+    /* now drop the listener */
+
+    for(alptr = listen_list; alptr; alptr = alptr->next)
+    {
+        if(alptr == lptr)
+        {
+            if(alptrprev)
+                alptrprev->next = alptr->next;
+            else
+                listen_list = alptr->next;
+            MyFree(lptr);
+            listen_count--;
+            break;
+        }
+        alptrprev = alptr;
+    }
+}
+
+/*
+ * close_listeners
+ *
+ * Close and free all clients which are marked as having their socket open
+ * and in a state where they can accept connections.  Unix sockets have
+ * the path to the socket unlinked for cleanliness.
+ */
+void close_listeners()
+{
+    aListener *lptr, *lptrnext;
+
+    lptr = listen_list;
+
+    while(lptr)
+    {
+        lptrnext = lptr->next;
+        if(lptr->clients <= 0)
+            close_listener(lptr);
+        else                
+            /* if we cant close it, mark it for closing
+             * when we loose all of our connections */
+            lptr->aport->legal = -1;
+        lptr = lptrnext;
+    }
+}
+
+/* init_sys */
+void init_sys()
+{
+    int fd;
+
+#ifdef RLIMIT_FD_MAX
+    struct rlimit limit;
+
+    if (!getrlimit(RLIMIT_FD_MAX, &limit))
+    {
+        if (limit.rlim_max < MAXCONNECTIONS)
+        {
+            printf("FATAL: System is only allowing %ld open files.\n",
+                   (long)limit.rlim_max);
+            printf("ircd requires at least %ld.\n", (long)MAXCONNECTIONS);
+            printf("Fix the system account limits or recompile ircd.\n");
+            printf("Aborting...\n");
+            exit(-1);
+        }
+        /* set limit to exactly what we can handle */
+        limit.rlim_cur = MAXCONNECTIONS;
+        if (setrlimit(RLIMIT_FD_MAX, &limit) == -1)
+        {
+            printf("FATAL: Unable to set open file limit to %ld:\n%s\n",
+                   (long)limit.rlim_cur, strerror(errno));
+            printf("Aborting...\n");
+            exit(-1);
+        }
+    }
+#endif
+
+    printf("\nIrcd is now becoming a daemon.\n");
+
+#if !defined(SOL20)
+    setlinebuf(stderr);
+#endif
+
+    for (fd = 3; fd < MAXCONNECTIONS; fd++)
+    {
+        close(fd);
+        local[fd] = NULL;
+    }
+    local[1] = NULL;
+
+    if (bootopt & BOOT_TTY)
+    {
+        engine_init();
+
+        /* debugging is going to a tty */
+        resfd = init_resolver(0x1f);
+        add_fd(resfd, FDT_RESOLVER, NULL);
+        set_fd_flags(resfd, FDF_WANTREAD);
+        return;
+    }
+
+    close(1);
+
+    if (!(bootopt & BOOT_DEBUG) && !(bootopt & BOOT_STDERR))
+        close(2);
+
+
+    if ((isatty(0)) && !(bootopt & BOOT_OPER) && !(bootopt & BOOT_STDERR))
+    {
+        int pid;
+
+        if ((pid = fork()) < 0)
+        {
+            if ((fd = open("/dev/tty", O_RDWR)) >= 0)
+            write(fd, "Couldn't fork!\n", 15);  /* crude, but effective */
+            exit(0);
+        } 
+        else if (pid > 0)
+            exit(0);
+
+        setsid();
+
+        close(0);       /* fd 0 opened by inetd */
+        local[0] = NULL;
+    }
+
+    engine_init();
+    resfd = init_resolver(0x1f);
+    add_fd(resfd, FDT_RESOLVER, NULL);
+    set_fd_flags(resfd, FDF_WANTREAD);
+    return;
+}
+
+void write_pidfile()
+{
+#ifdef IRCD_PIDFILE
+    int fd;
+    char buff[20];
+
+    if ((fd = open(IRCD_PIDFILE, O_CREAT | O_WRONLY, 0600)) >= 0)
+    {
+        ircsprintf(buff, "%5d\n", (int) getpid());
+        if (write(fd, buff, strlen(buff)) == -1)
+            Debug((DEBUG_NOTICE, "Error writing to pid file %s", IRCD_PIDFILE));
+        close(fd);
+        return;
+    }
+#ifdef  DEBUGMODE
+    else
+        Debug((DEBUG_NOTICE, "Error opening pid file %s", IRCD_PIDFILE));
+#endif
+#endif
+}
+
+/*
+ * Initialize the various name strings used to store hostnames. This is
+ * set from either the server's sockhost (if client fd is a tty or
+ * localhost) or from the ip# converted into a string. 0 = success, -1
+ * = fail.
+ */
+static int check_init(aClient * cptr, char *sockn)
+{
+    struct sockaddr_in sk;
+    int len = sizeof(struct sockaddr_in);
+
+    /* If descriptor is a tty, special checking... * IT can't EVER be a tty */
+
+    if (getpeername(cptr->fd, (struct sockaddr *) &sk, &len) == -1)
+        return -1;
+
+    strcpy(sockn, (char *) inetntoa((char *) &sk.sin_addr));
+    if (inet_netof(sk.sin_addr) == IN_LOOPBACKNET)
+    {
+        cptr->hostp = NULL;
+        strncpyzt(sockn, me.sockhost, HOSTLEN);
+    }
+    memcpy((char *) &cptr->ip, (char *) &sk.sin_addr, sizeof(struct in_addr));
+    
+    cptr->port = (int) (ntohs(sk.sin_port));
+
+    return 0;
+}
+
+/*
+ * Ordinary client access check. Look for conf lines which have the
+ * same status as the flags passed. 0 = Success -1 = Access denied -2 =
+ * Bad socket.
+ */
+int check_client(aClient *cptr)
+{
+    static char sockname[HOSTLEN + 1];
+    struct hostent *hp = NULL;
+    int i;
+
+    Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
+           cptr->name, inetntoa((char *) &cptr->ip)));
+
+    if (check_init(cptr, sockname))
+        return -2;
+
+    hp = cptr->hostp;
+    /* 
+     * Verify that the host to ip mapping is correct both ways and that
+     * the ip#(s) for the socket is listed for the host.
+     */
+    if (hp)
+    {
+        for (i = 0; hp->h_addr_list[i]; i++)
+            if (!memcmp(hp->h_addr_list[i], (char *) &cptr->ip,
+                    sizeof(struct in_addr))) 
+                break;
+
+        if (!hp->h_addr_list[i])
+        {
+            sendto_one(cptr, "NOTICE AUTH :*** Your forward and reverse"
+                             " DNS do not match, ignoring hostname.");
+            hp = NULL;
+        }
+    }
+
+    if ((i = attach_Iline(cptr, hp, sockname)))
+    {
+        Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]",
+                cptr->name, sockname));
+        return i;
+    }
+
+    Debug((DEBUG_DNS, "ch_cl: access ok: %s[%s]", cptr->name, sockname));
+
+    if (inet_netof(cptr->ip) == IN_LOOPBACKNET ||
+    inet_netof(cptr->ip) == inet_netof(mysk.sin_addr))
+    {
+        ircstp->is_loc++;
+        cptr->flags |= FLAGS_LOCAL;
+    }
+    return 0;
+}
+
+#define CFLAG   CONF_CONNECT_SERVER
+#define NFLAG   CONF_NOCONNECT_SERVER
+
+/*
+ * check_server_init(), check_server() check access for a server given
+ * its name (passed in cptr struct). Must check for all C/N lines which
+ * have a name which matches the name given and a host which matches. A
+ * host alias which is the same as the server name is also acceptable
+ * in the host field of a C/N line. 0 = Success -1 = Access denied -2 =
+ * Bad socket.
+ *
+ * This was terrible code.  Terrible!  Almost fucking scary! Rewritten into
+ * a single function, much prettier.  Feb04 -epi
+ */
+int check_server_init(aClient * cptr)
+{
+    aConnect *aconn = NULL;
+    struct hostent *hp = NULL;
+    char sockname[HOSTLEN + 1], fullname[HOSTLEN + 1];
+    char abuff[HOSTLEN + USERLEN + 2];
+    int i = 0, ok = 0;
+
+    if (check_init(cptr, sockname))
+        return -2;
+
+    if (!(aconn = find_aConnect(cptr->name)))
+    {
+        Debug((DEBUG_DNS, "No Connect block for %s", cptr->name));
+        return -1;
+    }
+
+    /* 
+     * * If the servername is a hostname, either an alias (CNAME) or
+     * real name, then check with it as the host. Use gethostbyname()
+     * to check for servername as hostname.
+     */
+    if (!cptr->hostp)
+    {
+        char *s;
+        Link lin;
+
+        /* 
+         * * Do a lookup for the CONF line *only* and not the server
+         * connection else we get stuck in a nasty state since it
+         * takes a SERVER message to get us here and we cant
+         * interrupt that very well.
+         */
+        lin.value.aconn = aconn;
+        lin.flags = ASYNC_CONF;
+        nextdnscheck = 1;
+        if ((s = strchr(aconn->host, '@')))
+            s++;
+        else
+            s = aconn->host;
+        Debug((DEBUG_DNS, "sv_ci:cache lookup (%s)", s));
+        if((hp = gethost_byname(s, &lin)))
+        {
+            for (i = 0; hp->h_addr_list[i]; i++)
+                if (!memcmp(hp->h_addr_list[i], (char *) &cptr->ip, 
+                        sizeof(struct in_addr)))
+                    break;
+            if (!hp->h_addr_list[i])
+            {
+                sendto_realops_lev(DEBUG_LEV,
+                    "Server IP# Mismatch: %s != %s[%08x]",
+                    inetntoa((char *) &cptr->ip), hp->h_name,
+                    *((unsigned long *) hp->h_addr));
+                hp = NULL;
+            }
+        }
+    }
+    else
+    {
+        hp = cptr->hostp;
+        for (i = 0; hp->h_addr_list[i]; i++)
+            if (!memcmp(hp->h_addr_list[i], (char *) &cptr->ip,
+                    sizeof(struct in_addr)))
+                break;
+    }
+
+    if(hp)
+    {
+        strncpyzt(fullname, cptr->name, sizeof(fullname));
+        add_local_domain(fullname, HOSTLEN - strlen(fullname));
+        Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s",
+                sockname, fullname));
+        ircsprintf(abuff, "%s@%s", cptr->username, fullname);
+        get_sockhost(cptr, fullname);
+        for (i = 0; hp->h_addr_list[i]; i++)
+        {
+            if(!memcmp((char *) &aconn->ipnum, (char *) hp->h_addr_list[i],
+                        sizeof(struct in_addr)))
+                ok = 1;
+            else
+                ok = 0;
+        }
+    }
+    else
+    {
+        /* having no luck finding a host.. check against IP */
+        if(!memcmp((char *) &aconn->ipnum, (char *) &cptr->ip,
+                   sizeof(struct in_addr)))
+            ok = 1;
+        else
+            ok = 0;
+    }
+
+    /* if they dont match up, then away with them */
+    if (!ok)
+    {
+        get_sockhost(cptr, sockname);
+        return -1;
+    }
+    /* check for Ulined access and link it if nessecary */
+    if(find_aUserver(cptr->name))
+        cptr->flags |= FLAGS_ULINE;
+    make_server(cptr);
+    cptr->serv->aconn = aconn;
+    aconn->acpt = cptr;
+    set_effective_class(cptr);
+
+    if ((aconn->ipnum.s_addr == -1))
+    memcpy((char *) &aconn->ipnum, (char *) &cptr->ip,
+           sizeof(struct in_addr));
+
+    get_sockhost(cptr, aconn->host);
+    
+    Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cptr->name, cptr->sockhost));
+    return 0;
+}
+
+/*
+ * completed_connection 
+ * Complete non-blocking
+ * connect()-sequence. Check access and terminate connection,
+ * if trouble detected. 
+ *
+ * Return TRUE if successfully completed - FALSE if failed and ClientExit
+ */
+int completed_connection(aClient * cptr)
+{
+    aConnect *aconn;
+
+    if(!(cptr->flags & FLAGS_BLOCKED))
+        unset_fd_flags(cptr->fd, FDF_WANTWRITE);
+    unset_fd_flags(cptr->fd, FDF_WANTREAD);
+
+    SetHandshake(cptr);
+
+    if (!(aconn = find_aConnect(cptr->name)))
+    {
+        sendto_realops("Lost Config for %s", get_client_name(cptr, HIDEME));
+        return -1;
+    }
+    if (!BadPtr(aconn->cpasswd))
+        sendto_one(cptr, "PASS %s :TS", aconn->cpasswd);
+
+    /* pass on our capabilities to the server we /connect'd */
+#ifdef HAVE_ENCRYPTION_ON
+    if(!(aconn->flags & CONN_DKEY))
+        sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP"
+                         " NICKIP TSMODE");
+    else
+    {
+        sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT DKEY"
+                         " ZIP NICKIP TSMODE");
+        SetWantDKEY(cptr);
+    }
+#else
+    sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP NICKIP TSMODE");
+#endif
+
+    if(aconn->flags & CONN_ZIP)
+        cptr->capabilities |= CAPAB_DOZIP;
+
+    sendto_one(cptr, "SERVER %s 1 :%s", my_name_for_link(me.name, aconn), 
+                                        me.info);
+#ifdef DO_IDENTD
+    /* Is this the right place to do this?  dunno... -Taner */
+    if (!IsDead(cptr))
+        start_auth(cptr);
+#endif
+
+    check_client_fd(cptr);
+    return (IsDead(cptr)) ? -1 : 0;
+}
+
+/*
+ * close_connection 
+ * Close the physical connection. This function must make 
+ * MyConnect(cptr) == FALSE, and set cptr->from == NULL.
+ */
+void close_connection(aClient *cptr)
+{
+    aConnect *aconn;
+
+    if (IsServer(cptr))
+    {
+        ircstp->is_sv++;
+        ircstp->is_sbs += cptr->sendB;
+        ircstp->is_sbr += cptr->receiveB;
+        ircstp->is_sks += cptr->sendK;
+        ircstp->is_skr += cptr->receiveK;
+        ircstp->is_sti += timeofday - cptr->firsttime;
+        if (ircstp->is_sbs > 2047)
+        {
+            ircstp->is_sks += (ircstp->is_sbs >> 10);
+            ircstp->is_sbs &= 0x3ff;
+        }
+        if (ircstp->is_sbr > 2047)
+        {
+            ircstp->is_skr += (ircstp->is_sbr >> 10);
+            ircstp->is_sbr &= 0x3ff;
+        }
+        /* schedule a quick reconnect if we've been connected a long time */
+        if((aconn = find_aConnect_match(cptr->name, cptr->username,
+                                    cptr->sockhost)))
+        {
+            aconn->hold = time(NULL);
+            aconn->hold += (aconn->hold - cptr->since > HANGONGOODLINK) ?
+                HANGONRETRYDELAY : aconn->class->connfreq;
+            if (nextconnect > aconn->hold)
+                nextconnect = aconn->hold;
+        }
+    } 
+    else if (IsClient(cptr))
+    {
+        ircstp->is_cl++;
+        ircstp->is_cbs += cptr->sendB;
+        ircstp->is_cbr += cptr->receiveB;
+        ircstp->is_cks += cptr->sendK;
+        ircstp->is_ckr += cptr->receiveK;
+        ircstp->is_cti += timeofday - cptr->firsttime;
+        if (ircstp->is_cbs > 2047)
+        {
+            ircstp->is_cks += (ircstp->is_cbs >> 10);
+            ircstp->is_cbs &= 0x3ff;
+        }
+        if (ircstp->is_cbr > 2047)
+        {
+            ircstp->is_ckr += (ircstp->is_cbr >> 10);
+            ircstp->is_cbr &= 0x3ff;
+        }
+    } 
+    else
+        ircstp->is_ni++;
+
+    /* remove outstanding DNS queries. */
+    del_queries((char *) cptr);
+
+    if (cptr->authfd >= 0)
+    {
+        del_fd(cptr->authfd);
+        close(cptr->authfd);
+        cptr->authfd = -1;
+    }
+
+    if (cptr->fd >= 0)
+    {
+        dump_connections(cptr->fd);
+        local[cptr->fd] = NULL;
+        del_fd(cptr->fd);
+        close(cptr->fd);
+        cptr->fd = -2;
+        SBufClear(&cptr->sendQ);
+        SBufClear(&cptr->recvQ);
+        memset(cptr->passwd, '\0', sizeof(cptr->passwd));
+        if(cptr->lstn)
+            cptr->lstn->clients--;
+    }
+    for (; highest_fd > 0; highest_fd--)
+    if (local[highest_fd])
+        break;
+
+    clear_conflinks(cptr);
+
+    cptr->from = NULL;      /* ...this should catch them! >:) --msa */
+
+    /* if we're the last socket open on this listener,
+     * check to make sure the listener is even supposed to be
+     * open, and close it if its not -epi
+     */
+     if (cptr->lstn && (cptr->lstn->clients <= 0) && 
+                (cptr->lstn->aport->legal == -1))
+        close_listener(cptr->lstn);
+
+    return;
+}
+
+#ifdef MAXBUFFERS
+/* reset_sock_opts type =  0 = client, 1 = server */
+void reset_sock_opts(int fd, int type)
+{
+#define CLIENT_BUFFER_SIZE  4096
+#define SEND_BUF_SIZE       2920
+    int opt;
+
+    opt = type ? rcvbufmax : CLIENT_BUFFER_SIZE;
+    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0) 
+        sendto_realops("REsetsockopt(SO_RCVBUF) for fd %d (%s) failed",
+                        fd, type ? "server" : "client");
+    opt = type ? sndbufmax : SEND_BUF_SIZE;
+    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) 
+        sendto_realops("REsetsockopt(SO_SNDBUF) for fd %d (%s) failed",
+                        fd, type ? "server" : "client");
+}
+#endif              /* MAXBUFFERS */
+
+/* set_sock_opts */
+static void set_sock_opts(int fd, aClient * cptr)
+{
+    int opt;
+    
+#ifdef SO_REUSEADDR
+    opt = 1;
+    if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt,
+            sizeof(opt)) < 0)
+        silent_report_error("setsockopt(SO_REUSEADDR) %s:%s", cptr);
+#endif
+#if  defined(SO_DEBUG) && defined(DEBUGMODE) && 0
+    /* Solaris with SO_DEBUG writes to syslog by default */
+#if !defined(SOL20) || defined(USE_SYSLOG)
+    opt = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_DEBUG, (char *) &opt, sizeof(opt)) < 0)
+        silent_report_error("setsockopt(SO_DEBUG) %s:%s", cptr);
+#endif              /* SOL20 */
+#endif
+#ifdef  SO_USELOOPBACK
+    opt = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, (char *) &opt,
+           sizeof(opt)) < 0)
+        silent_report_error("setsockopt(SO_USELOOPBACK) %s:%s", cptr);
+#endif
+#ifdef  SO_RCVBUF
+#if defined(MAXBUFFERS)
+    if (rcvbufmax == 0)
+    {
+        int optlen;
+
+        optlen = sizeof(rcvbufmax);
+        getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen);
+        while ((rcvbufmax < 16385) && (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 
+               (char *) (char *) &rcvbufmax,optlen) >= 0))
+            rcvbufmax += 1024;
+        getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen);
+        readbuf = (char *) malloc(rcvbufmax * sizeof(char));
+    }
+    if (IsServer(cptr))
+        opt = rcvbufmax;
+    else
+        opt = 4096;
+#else
+    opt = 8192;
+#endif
+    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0)
+        silent_report_error("setsockopt(SO_RCVBUF) %s:%s", cptr);
+#endif
+#ifdef  SO_SNDBUF
+#if defined(MAXBUFFERS)
+    if (sndbufmax == 0)
+    {
+        int optlen;
+    
+        optlen = sizeof(sndbufmax);
+        getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen);
+        while ((sndbufmax < 16385) && (setsockopt (fd, SOL_SOCKET, SO_SNDBUF,
+                           (char *) &sndbufmax, optlen) >= 0))
+            sndbufmax += 1024;
+        getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen);
+    }
+    if (IsServer(cptr))
+        opt = sndbufmax;
+    else
+        opt = 4096;
+#else
+    opt = 8192;
+#endif
+    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0)
+        silent_report_error("setsockopt(SO_SNDBUF) %s:%s", cptr);
+#endif
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
+    {
+#if defined(MAXBUFFERS)
+        char *s = readbuf, *t = readbuf + (rcvbufmax * sizeof(char)) / 2;
+        opt = (rcvbufmax * sizeof(char)) / 8;
+#else
+        char *s = readbuf, *t = readbuf + sizeof(readbuf) / 2;
+    
+        opt = sizeof(readbuf) / 8;
+#endif
+        if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS, t, &opt) < 0)
+            silent_report_error("getsockopt(IP_OPTIONS) %s:%s", cptr);
+        else if (opt > 0)
+        {
+            for (*readbuf = '\0'; opt > 0; opt--, s += 3)
+                ircsprintf(s, "%02.2x:", *t++);
+            *s = '\0';
+            sendto_realops("Connection %s using IP opts: (%s)",
+                            get_client_name(cptr, HIDEME), readbuf);
+        }
+        if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, (char *) NULL, 0) < 0)
+            silent_report_error("setsockopt(IP_OPTIONS) %s:%s", cptr);
+    }
+#endif
+}
+
+static void set_listener_sock_opts(int fd, aListener *lptr)
+{
+    int opt;
+
+#ifdef SO_REUSEADDR
+    opt = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, 
+                   sizeof(opt)) < 0)
+        report_listener_error("setsockopt(SO_REUSEADDR) %s:%s", lptr);
+#endif
+#if  defined(SO_DEBUG) && defined(DEBUGMODE) && 0
+   /*
+    * Solaris with SO_DEBUG writes to syslog by default
+    */
+#if !defined(SOL20) || defined(USE_SYSLOG)
+    opt = 1;
+    if (setsockopt(fd, SOL_SOCKET, SO_DEBUG, (char *) &opt, sizeof(opt)) < 0) 
+        report_listener_error("setsockopt(SO_DEBUG) %s:%s", lptr);
+#endif                         /* SOL20 */
+#endif
+#ifdef  SO_USELOOPBACK
+    opt = 1;
+    if (setsockopt (fd, SOL_SOCKET, SO_USELOOPBACK, (char *) &opt, 
+                    sizeof(opt)) < 0)
+        report_listener_error("setsockopt(SO_USELOOPBACK) %s:%s", lptr);
+#endif
+#ifdef  SO_RCVBUF
+# if defined(MAXBUFFERS)
+    if (rcvbufmax == 0) 
+    {
+        int optlen;
+
+        optlen = sizeof(rcvbufmax);
+        getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen);
+        while ((rcvbufmax < 16385) && (setsockopt (fd, SOL_SOCKET, SO_RCVBUF, 
+               (char *) (char *) &rcvbufmax, optlen) >= 0)) 
+            rcvbufmax += 1024;
+        getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen);
+        readbuf = (char *) malloc(rcvbufmax * sizeof(char));
+    }
+    opt = 4096;
+# else
+    opt = 8192;
+# endif
+    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0) 
+        report_listener_error("setsockopt(SO_RCVBUF) %s:%s", lptr);
+#endif
+#ifdef  SO_SNDBUF
+#if defined(MAXBUFFERS)
+    if (sndbufmax == 0) 
+    {
+        int optlen;
+
+        optlen = sizeof(sndbufmax);
+        getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen);
+        while ((sndbufmax < 16385) && (setsockopt (fd, SOL_SOCKET, SO_SNDBUF, 
+                (char *) &sndbufmax, optlen) >= 0)) 
+            sndbufmax += 1024;
+        getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen);
+    }
+    opt = 4096;
+#else
+    opt = 8192;
+#endif
+    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0)
+        report_listener_error("setsockopt(SO_SNDBUF) %s:%s", lptr);
+#endif
+}
+
+
+int get_sockerr(aClient * cptr)
+{
+    int errtmp = errno, err = 0, len = sizeof(err);
+    
+#ifdef  SO_ERROR
+    if (cptr->fd >= 0)
+    if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len))
+        if (err)
+            errtmp = err;
+#endif
+    return errtmp;
+}
+
+char *irc_get_sockerr(aClient *cptr)
+{
+    if(cptr->sockerr == 0)
+        return "No error";
+    
+    if(cptr->sockerr > 0)
+        return strerror(cptr->sockerr);
+    
+    switch(cptr->sockerr)
+    { 
+        case -1: /* this is the default */
+            return "Unset error message [this is a bug!]";
+        case IRCERR_BUFALLOC:
+            return "dbuf allocation error";
+        case IRCERR_ZIP:
+            return "compression general failure";
+        default:
+            return "Unknown error!";
+    }
+    
+    /* unreachable code, but the compiler is complaining.. */
+    return NULL;
+}
+
+/*
+ * * set_non_blocking 
+ *       Set the client connection into non-blocking mode. 
+ */
+void set_non_blocking(int fd, aClient * cptr)
+{
+    int res, nonb = 0;
+
+    nonb |= O_NONBLOCK;
+    if ((res = fcntl(fd, F_GETFL, 0)) == -1)
+        silent_report_error("fcntl(fd, F_GETFL) failed for %s:%s", cptr);
+    else if (fcntl(fd, F_SETFL, res | nonb) == -1)
+        silent_report_error("fcntl(fd, F_SETL, nonb) failed for %s:%s", cptr);
+    return;
+}
+
+void set_listener_non_blocking(int fd, aListener *lptr)
+{
+    int res, nonb = 0;
+
+    nonb |= O_NONBLOCK;
+    if ((res = fcntl(fd, F_GETFL, 0)) == -1)
+        report_listener_error("fcntl(fd, F_GETFL) failed for %s:%s", lptr);
+    else if (fcntl(fd, F_SETFL, res | nonb) == -1)
+        report_listener_error("fcntl(fd, F_SETL, nonb) failed for %s:%s", lptr);
+    return;
+}
+
+
+/*
+ * Creates a client which has just connected to us on the given fd. The
+ * sockhost field is initialized with the ip# of the host. The client
+ * is added to the linked list of clients but isnt added to any hash
+ * tables yuet since it doesnt have a name.
+ */
+aClient *add_connection(aListener *lptr, int fd)
+{
+    Link lin;
+    aClient *acptr = NULL;
+    char *s, *t;
+    struct sockaddr_in addr;
+    int len = sizeof(struct sockaddr_in);
+    struct userBan *ban;
+    
+    if (getpeername(fd, (struct sockaddr *) &addr, &len) == -1)
+    { 
+        ircstp->is_ref++;
+        close(fd);
+        return NULL;
+    }
+
+    acptr = make_client(NULL, &me);
+
+    /* 
+     * Copy ascii address to 'sockhost' just in case. Then we have
+     * something valid to put into error messages...
+     */
+    get_sockhost(acptr, (char *) inetntoa((char *) &addr.sin_addr));
+    memcpy((char *) &acptr->ip, (char *) &addr.sin_addr,
+    sizeof(struct in_addr));
+    
+    acptr->port = ntohs(addr.sin_port);
+    /* 
+     * Check that this socket (client) is allowed to accept
+     * connections from this IP#.
+     */
+    for (s = (char *) &lptr->allow_ip, t = (char *) &acptr->ip, len = 4;
+         len > 0; len--, s++, t++)
+    {
+        if (!*s)
+            continue;
+        if (*s != *t)
+            break;
+    }
+    if (len)
+    {
+        ircstp->is_ref++;
+        acptr->fd = -2;
+        free_client(acptr);
+        close(fd);
+        return NULL;
+    }
+
+    lptr->ccount++;
+    lptr->clients++;
+    add_fd(fd, FDT_CLIENT, acptr);
+    local[fd] = acptr;
+
+    acptr->fd = fd;
+    if (fd > highest_fd)
+        highest_fd = fd;
+
+    /* sockets inherit the options of their parents.. do we need these? */
+    set_non_blocking(acptr->fd, acptr);
+    set_sock_opts(acptr->fd, acptr);
+
+    acptr->lstn = lptr;
+    add_client_to_list(acptr);
+
+    ban = check_userbanned(acptr, UBAN_IP|UBAN_CIDR4|UBAN_WILDUSER, 0);
+    if(ban)
+    {
+        char *reason, *ktype;
+        int local;
+
+        local = (ban->flags & UBAN_LOCAL) ? 1 : 0;
+        ktype = local ? LOCAL_BANNED_NAME : NETWORK_BANNED_NAME;
+        reason = ban->reason ? ban->reason : ktype;
+
+        sendto_one(acptr, err_str(ERR_YOUREBANNEDCREEP), me.name, "*", ktype);
+        sendto_one(acptr, ":%s NOTICE * :*** You are not welcome on this %s.",
+                   me.name, local ? "server" : "network");
+        sendto_one(acptr, ":%s NOTICE * :*** %s for %s", 
+                   me.name, ktype, reason);
+        sendto_one(acptr, ":%s NOTICE * :*** Your IP is %s",
+                   me.name, inetntoa((char *)&acptr->ip.s_addr));
+        sendto_one(acptr, ":%s NOTICE * :*** For assistance, please email %s"
+                          " and include everything shown here.", me.name, 
+                   local ? Local_Kline_Address : Network_Kline_Address);
+
+        ircstp->is_ref++;
+        ircstp->is_ref_1++;
+        throttle_force(inetntoa((char *)&acptr->ip.s_addr));
+        exit_client(acptr, acptr, &me, reason);
+
+        return NULL;
+    }
+
+    if(call_hooks(CHOOK_PREACCESS, acptr) == FLUSH_BUFFER)
+        return NULL;
+
+#ifdef SHOW_HEADERS
+    sendto_one(acptr, REPORT_DO_DNS);
+#endif
+    lin.flags = ASYNC_CLIENT;
+    lin.value.cptr = acptr;
+    Debug((DEBUG_DNS, "lookup %s", inetntoa((char *) &addr.sin_addr)));
+    acptr->hostp = gethost_byaddr((char *) &acptr->ip, &lin);
+    if (!acptr->hostp)
+        SetDNS(acptr);
+#ifdef SHOW_HEADERS
+    else
+        sendto_one(acptr, REPORT_FIN_DNSC);
+#endif
+    nextdnscheck = 1;
+    
+#ifdef DO_IDENTD
+    start_auth(acptr);
+#endif
+    check_client_fd(acptr);
+    return acptr;
+}
+
+/* handle taking care of the client's recvq here */
+int do_client_queue(aClient *cptr)
+{
+    int dolen = 0, done;
+    
+    while (SBufLength(&cptr->recvQ) && !NoNewLine(cptr) &&
+       ((cptr->status < STAT_UNKNOWN) || (cptr->since - timeofday < 10) ||
+        IsNegoServer(cptr))) 
+    {
+        /* If it's become registered as a server, just parse the whole block */
+        if (IsServer(cptr) || IsNegoServer(cptr)) 
+        {
+#if defined(MAXBUFFERS)
+            dolen = sbuf_get(&cptr->recvQ, readbuf, rcvbufmax * sizeof(char));
+#else
+            dolen = sbuf_get(&cptr->recvQ, readbuf, sizeof(readbuf));
+#endif
+            if (dolen <= 0)
+                break;
+            if ((done = dopacket(cptr, readbuf, dolen)))
+                return done;
+            break;
+        }
+#if defined(MAXBUFFERS)
+        dolen = sbuf_getmsg(&cptr->recvQ, readbuf, rcvbufmax * sizeof(char));
+#else
+        dolen = sbuf_getmsg(&cptr->recvQ, readbuf, sizeof(readbuf));
+#endif
+    
+        if (dolen <= 0) 
+        {
+            if (dolen < 0)
+                return exit_client(cptr, cptr, cptr, "sbuf_getmsg fail");
+        
+            if (SBufLength(&cptr->recvQ) < 510) 
+            {
+                cptr->flags |= FLAGS_NONL;
+                break;
+            }
+            /* The buffer is full (more than 512 bytes) and it has no \n
+             * Some user is trying to trick us. Kill their recvq. */
+            SBufClear(&cptr->recvQ);
+            break;
+        }
+        else if(client_dopacket(cptr, readbuf, dolen) == FLUSH_BUFFER)
+            return FLUSH_BUFFER;
+    }
+
+    if(!(cptr->flags & FLAGS_HAVERECVQ) && SBufLength(&cptr->recvQ) && 
+        !NoNewLine(cptr))
+    {
+       add_to_list(&recvq_clients, cptr);
+       cptr->flags |= FLAGS_HAVERECVQ;
+    }
+
+    return 1;
+}
+
+/*
+ * read_packet
+ *
+ * Read a 'packet' of data from a connection and process it.  Read in 8k 
+ * chunks to give a better performance rating (for server connections). 
+ * Do some tricky stuff for client connections to make sure they don't
+ * do any flooding >:-) -avalon
+ */
+
+#define MAX_CLIENT_RECVQ 8192
+
+int read_packet(aClient * cptr)
+{
+    int length = 0, done;
+
+    /* If data is ready, and the user is either not a person or
+     * is a person and has a recvq of less than MAX_CLIENT_RECVQ,
+     * read from this client
+     */ 
+    if (!(IsPerson(cptr) && SBufLength(&cptr->recvQ) > MAX_CLIENT_RECVQ)) 
+    {
+        errno = 0;
+    
+#if defined(MAXBUFFERS)
+        if (IsPerson(cptr))
+            length = recv(cptr->fd, readbuf, 8192 * sizeof(char), 0);
+        else
+            length = recv(cptr->fd, readbuf, rcvbufmax * sizeof(char), 0);
+#else
+        length = recv(cptr->fd, readbuf, sizeof(readbuf), 0);
+#endif
+
+        cptr->lasttime = timeofday;
+        if (cptr->lasttime > cptr->since)
+            cptr->since = cptr->lasttime;
+        cptr->flags &= ~(FLAGS_PINGSENT | FLAGS_NONL);
+        /* If not ready, fake it so it isnt closed */
+        if (length == -1 && ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
+            return 1;
+        if (length <= 0)
+        {
+            cptr->sockerr = length ? errno : 0;
+            return length;
+        }
+    }
+
+    /* 
+     * For server connections, we process as many as we can without
+     * worrying about the time of day or anything :)
+     */
+    if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr) ||
+        IsNegoServer(cptr)) 
+    {
+        if (length > 0)
+            if ((done = dopacket(cptr, readbuf, length)))
+                return done;
+    } 
+    else 
+    {
+        /* 
+         * Before we even think of parsing what we just read, stick 
+         * it on the end of the receive queue and do it when its turn
+         * comes around. */
+        if (sbuf_put(&cptr->recvQ, readbuf, length) < 0)
+            return exit_client(cptr, cptr, cptr, "sbuf_put fail");
+    
+        if (IsPerson(cptr) &&
+#ifdef NO_OPER_FLOOD
+        !IsAnOper(cptr) &&
+#endif
+        SBufLength(&cptr->recvQ) > CLIENT_FLOOD)
+        {
+            sendto_realops_lev(FLOOD_LEV, "Flood -- %s!%s@%s (%d) Exceeds %d"
+                               " RecvQ", cptr->name[0] ? cptr->name : "*",
+                               cptr->user ? cptr->user->username : "*",
+                               cptr->user ? cptr->user->host : "*",
+                               SBufLength(&cptr->recvQ), CLIENT_FLOOD);
+            return exit_client(cptr, cptr, cptr, "Excess Flood");
+        }
+        return do_client_queue(cptr);
+    }
+    return 1;
+}
+
+void read_error_exit(aClient *cptr, int length, int err)
+{
+    char fbuf[512];
+    char errmsg[512];
+    
+    if (IsServer(cptr) || IsHandshake(cptr) || IsConnecting(cptr)) 
+    {
+        if (length == 0) 
+        {
+            char *errtxt = "Server %s closed the connection";
+        
+            ircsprintf(fbuf, "from %s: %s", me.name, errtxt);
+            sendto_gnotice(fbuf, get_client_name(cptr, HIDEME));
+            ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt);
+            sendto_serv_butone(cptr, fbuf, get_client_name(cptr, HIDEME));
+        }
+        else 
+        {
+            char *errtxt = (IsConnecting(cptr) || IsHandshake(cptr)) ? 
+                            "Connect error to %s (%s)" : 
+                            "Read error from %s, closing link (%s)";
+
+            ircsprintf(fbuf, "from %s: %s", me.name, errtxt);
+            sendto_gnotice(fbuf, get_client_name(cptr, HIDEME), strerror(err));
+            ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt);
+            sendto_serv_butone(cptr, fbuf, get_client_name(cptr, HIDEME), 
+                               strerror(err));
+        }
+    }
+    
+    if (err)
+        ircsprintf(errmsg, "Read error: %s", strerror(err));
+    else
+        ircsprintf(errmsg, "Client closed connection");
+    
+    exit_client(cptr, cptr, &me, errmsg);
+}
+
+void accept_connection(aListener *lptr)
+{
+    static struct sockaddr_in addr;
+    int addrlen = sizeof(struct sockaddr_in);
+    char host[HOSTLEN + 2];
+    int newfd;
+    int i;
+    
+    lptr->lasttime = timeofday;
+    
+    for (i = 0; i < 100; i++) /* accept up to 100 times per call 
+                               * to deal with high connect rates */
+    {
+        if((newfd = accept(lptr->fd, (struct sockaddr *) &addr, &addrlen)) < 0) 
+        {
+            switch(errno)
+            {
+#ifdef EMFILE
+                case EMFILE:
+                    report_listener_error("Cannot accept connections %s:%s", 
+                                           lptr);
+                    break;
+#endif
+#ifdef ENFILE
+                case ENFILE:
+                    report_listener_error("Cannot accept connections %s:%s", 
+                                           lptr);
+                    break;
+#endif
+            }
+        return;
+        }
+
+        strncpyzt(host, (char *) inetntoa((char *) &addr.sin_addr), 
+                  sizeof(host));
+
+        /* if they are throttled, drop them silently. */
+        if (throttle_check(host, newfd, NOW) == 0) 
+        {
+            ircstp->is_ref++;
+            ircstp->is_throt++;
+            close(newfd);
+            return;
+        }
+
+        if (newfd >= MAX_ACTIVECONN) 
+        {
+            ircstp->is_ref++;
+            sendto_realops_lev(CCONN_LEV,"All connections in use. fd: %d (%s)",
+                newfd,get_listener_name(lptr));
+            send(newfd, "ERROR :All connections in use\r\n", 32, 0);
+            close(newfd);
+            return;
+        }
+        if((lptr->aport->legal == -1))
+        {
+            ircstp->is_ref++;
+            send(newfd, "ERROR :This port is closed\r\n", 29, 0);
+            close(newfd);
+            return;
+        }
+        ircstp->is_ac++;
+
+        add_connection(lptr, newfd);
+#ifdef PINGNAZI
+        nextping = timeofday;
+#endif
+    }
+}
+
+int readwrite_client(aClient *cptr, int isread, int iswrite)
+{
+    /*
+     * NOTE
+     * We now do this in a more logical way.
+     * We request a write poll on a socket for two reasons
+     * - the socket is waiting for a connect() call
+     * - the socket is blocked
+     */
+
+    if(iswrite)
+    {
+        if (IsConnecting(cptr) && completed_connection(cptr))
+        {
+            char errmsg[512];
+
+            ircsprintf(errmsg, "Connect Error: %s", irc_get_sockerr(cptr));
+            return exit_client(cptr, cptr, &me, errmsg);
+        }
+
+        if(cptr->flags & FLAGS_BLOCKED)
+        {
+            cptr->flags &= ~FLAGS_BLOCKED;
+            unset_fd_flags(cptr->fd, FDF_WANTWRITE);
+        }
+        else 
+        {
+            /* this may be our problem with occational 100% cpu looping
+             * we've experienced.  jason suggested this, here we will try
+             * this and see if it happens at all -epi */
+            sendto_realops_lev(DEBUG_LEV, "Removing socket %d: reported ready"
+                               " for write, but not blocking", cptr->fd);
+            /* This unset_fd_flags() does not appear to make any difference
+             * to the write set.  The socket appears stuck, and there has
+             * to be a reason for it.  Since we're experiencing a very low
+             * number of these, simply drop the client entirely, and treat
+             * this as a socket handling error.  This is essentially a kludge
+             * however tracking down this bug will take a serious amount of
+             * time and testing - since its not easily reproducable.  This 
+             * will in the meantime prevent maxing the CPU.  -epi 
+             *
+             * unset_fd_flags(cptr->fd, FDF_WANTWRITE);
+             */
+            exit_client(cptr, cptr, &me, "Socket error (write)");
+            return FLUSH_BUFFER;
+        }
+
+    }
+
+    if (isread)
+    {
+        int length = read_packet(cptr);
+
+        if(length == FLUSH_BUFFER)
+            return length;
+
+        if(length <= 0)
+        {
+            read_error_exit(cptr, length, cptr->sockerr);
+            return FLUSH_BUFFER;
+        }
+    }
+
+    if (IsDead(cptr))
+    {
+        char errmsg[512];
+
+        ircsprintf(errmsg, "Write Error: %s", (cptr->flags & FLAGS_SENDQEX) ?
+                   "SendQ Exceeded" : irc_get_sockerr(cptr));
+        return exit_client(cptr, cptr, &me, errmsg);
+    }
+
+    return 1;
+}
+
+/* connect_server */
+int connect_server(aConnect *aconn, aClient * by, struct hostent *hp)
+{
+    struct sockaddr *svp;
+    aClient *cptr, *c2ptr;
+    char *s;
+    int errtmp, len;
+
+    Debug((DEBUG_NOTICE, "Connect to %s[%s] @%s", aconn->name, aconn->host, 
+                                    inetntoa((char *) &aconn->ipnum)));
+    
+    if ((c2ptr = find_server(aconn->name, NULL)))
+    {
+        sendto_ops("Server %s already present from %s",
+                    aconn->name, get_client_name(c2ptr, HIDEME));
+        if (by && IsPerson(by) && !MyClient(by))
+            sendto_one(by, ":%s NOTICE %s :Server %s already present from %s",
+                       me.name, by->name, aconn->name,
+                       get_client_name(c2ptr, HIDEME));
+        return -1;
+    }
+    /* 
+     * If we dont know the IP# for this host and itis a hostname and not
+     * a ip# string, then try and find the appropriate host record.
+     */
+    if ((!aconn->ipnum.s_addr))
+    {
+        Link lin;
+
+        lin.flags = ASYNC_CONNECT;
+        lin.value.aconn = aconn;
+        nextdnscheck = 1;
+        s = (char *) strchr(aconn->host, '@');
+        s++;            /* should NEVER be NULL */
+        if ((aconn->ipnum.s_addr = inet_addr(s)) == -1) 
+        {
+            aconn->ipnum.s_addr = 0;
+            hp = gethost_byname(s, &lin);
+            Debug((DEBUG_NOTICE, "co_sv: hp %x ac %x na %s ho %s",
+                                 hp, aconn, aconn->name, s));
+            if (!hp)
+                return 0;
+            memcpy((char *) &aconn->ipnum, hp->h_addr, sizeof(struct in_addr));
+        }
+    }
+    cptr = make_client(NULL, &me);
+    cptr->hostp = hp;
+    /* Copy these in so we have something for error detection. */
+    strncpyzt(cptr->name, aconn->name, sizeof(cptr->name));
+    strncpyzt(cptr->sockhost, aconn->host, HOSTLEN + 1);
+    svp = connect_inet(aconn, cptr, &len);
+
+    if (!svp)
+    {
+        if (cptr->fd >= 0)
+            close(cptr->fd);
+        cptr->fd = -2;
+        free_client(cptr);
+        return -1;
+    }
+    
+    set_non_blocking(cptr->fd, cptr);
+    set_sock_opts(cptr->fd, cptr);
+    signal(SIGALRM, dummy);
+    if (connect(cptr->fd, svp, len) < 0 && errno != EINPROGRESS) 
+    {
+        errtmp = errno;     /* other system calls may eat errno */
+        report_error("Connect to host %s failed: %s", cptr);
+        if (by && IsPerson(by) && !MyClient(by))
+            sendto_one(by, ":%s NOTICE %s :Connect to server %s failed.",
+                       me.name, by->name, cptr->name);
+        close(cptr->fd);
+        cptr->fd = -2;
+        free_client(cptr);
+        errno = errtmp;
+        if (errno == EINTR)
+            errno = ETIMEDOUT;
+        return -1;
+    }
+    
+    make_server(cptr);
+    cptr->serv->aconn = aconn;
+    
+    /* The socket has been connected or connect is in progress. */
+    if (by && IsPerson(by))
+    {
+        strcpy(cptr->serv->bynick, by->name);
+        strcpy(cptr->serv->byuser, by->user->username);
+        strcpy(cptr->serv->byhost, by->user->host);
+    }
+    else
+    {
+        strcpy(cptr->serv->bynick, "AutoConn.");
+        *cptr->serv->byuser = '\0';
+        *cptr->serv->byhost = '\0';
+    }
+    cptr->serv->up = me.name;
+    if (cptr->fd > highest_fd)
+        highest_fd = cptr->fd;
+    local[cptr->fd] = cptr;
+    SetConnecting(cptr);
+
+    get_sockhost(cptr, aconn->host);
+    add_client_to_list(cptr);
+#ifdef PINGNAZI
+    nextping = timeofday;
+#endif
+
+    add_fd(cptr->fd, FDT_CLIENT, cptr);
+    cptr->flags |= FLAGS_BLOCKED;
+    set_fd_flags(cptr->fd, FDF_WANTREAD|FDF_WANTWRITE);
+
+    return 0;
+}
+
+static struct sockaddr *
+connect_inet(aConnect *aconn, aClient *cptr, int *lenp)
+{
+    static struct sockaddr_in server;
+    struct hostent *hp;
+    struct sockaddr_in sin;
+    
+    /* 
+     * Might as well get sockhost from here, the connection is attempted
+     * with it so if it fails its useless.
+     */
+    cptr->fd = socket(AF_INET, SOCK_STREAM, 0);
+    if (cptr->fd >= MAX_ACTIVECONN)
+    {
+        sendto_realops("No more connections allowed (%s)", cptr->name);
+        return NULL;
+    }
+    memset((char *) &server, '\0', sizeof(server));
+    memset((char *) &sin, '\0', sizeof(sin));
+    server.sin_family = sin.sin_family = AF_INET;
+    get_sockhost(cptr, aconn->host);
+
+    if (aconn->source)
+        sin.sin_addr.s_addr = inet_addr(aconn->source);
+
+    if (cptr->fd < 0)
+    {
+        report_error("opening stream socket to server %s:%s", cptr);
+        cptr->fd = -2;
+        return NULL;
+    }
+    /* 
+     * Bind to a local IP# (with unknown port - let unix decide) so *
+     * we have some chance of knowing the IP# that gets used for a host *
+     * with more than one IP#.
+     * 
+     * No we don't bind it, not all OS's can handle connecting with an
+     * already bound socket, different ip# might occur anyway leading to
+     * a freezing select() on this side for some time.
+     */
+    if (aconn->source)
+    {
+        /* 
+         * * No, we do bind it if we have virtual host support. If we
+         * don't explicitly bind it, it will default to IN_ADDR_ANY and
+         * we lose due to the other server not allowing our base IP
+         * --smg
+         */
+        if (bind(cptr->fd, (struct sockaddr *) &sin, sizeof(sin)) == -1)
+        {
+            report_error("error binding to local port for %s:%s", cptr);
+            close(cptr->fd);
+            return NULL;
+        }
+    }
+    /* 
+     * By this point we should know the IP# of the host listed in the
+     * conf line, whether as a result of the hostname lookup or the ip#
+     * being present instead. If we dont know it, then the connect
+     * fails.
+     */
+    if (IsDigit(*aconn->host) && (aconn->ipnum.s_addr == -1))
+        aconn->ipnum.s_addr = inet_addr(aconn->host);
+    if (aconn->ipnum.s_addr == -1)
+    {
+        hp = cptr->hostp;
+        if (!hp)
+        {
+            Debug((DEBUG_FATAL, "%s: unknown host", aconn->host));
+            return NULL;
+        }
+        memcpy((char *) &aconn->ipnum, hp->h_addr, sizeof(struct in_addr));
+    }
+    memcpy((char *) &server.sin_addr, (char *) &aconn->ipnum, 
+            sizeof(struct in_addr));
+    
+    memcpy((char *) &cptr->ip, (char *) &aconn->ipnum,
+            sizeof(struct in_addr));
+
+    server.sin_port = htons((aconn->port > 0) ? aconn->port : PORTNUM);
+    *lenp = sizeof(server);
+    return (struct sockaddr *) &server;
+}
+
+/*
+ * find the real hostname for the host running the server (or one
+ * which matches the server's name) and its primary IP#.  Hostname is
+ * stored in the client structure passed as a pointer.
+ */
+void get_my_name(aClient * cptr, char *name, int len)
+{
+    static char tmp[HOSTLEN + 1];
+    struct hostent *hp;
+
+    /* 
+     * The following conflicts with both AIX and linux prototypes oh
+     * well, we can put up with the errors from other systems -Dianora
+     */
+
+    char *cname = cptr->name;
+    
+    /* Setup local socket structure to use for binding to. */
+    memset((char *) &mysk, '\0', sizeof(mysk));
+    mysk.sin_family = AF_INET;
+
+    if (gethostname(name, len) == -1)
+        return;
+    name[len] = '\0';
+    
+    /* assume that a name containing '.' is a FQDN */
+    if (!strchr(name, '.'))
+        add_local_domain(name, len - strlen(name));
+    /* 
+     * If hostname gives another name than cname, then check if there
+     * is a CNAME record for cname pointing to hostname. If so accept
+     * cname as our name.   meLazy
+     */
+    if (BadPtr(cname))
+        return;
+    if ((hp = gethostbyname(cname)) || (hp = gethostbyname(name)))
+    {
+        char *hname;
+        int i = 0;
+    
+        for (hname = hp->h_name; hname; hname = hp->h_aliases[i++])
+        {
+            strncpyzt(tmp, hname, sizeof(tmp));
+            add_local_domain(tmp, sizeof(tmp) - strlen(tmp));
+            /* 
+             * Copy the matching name over and store the 'primary' IP#
+             * as 'myip' which is used later for making the right one is
+             * used for connecting to other hosts.
+             */
+            if (!mycmp(me.name, tmp))
+                break;
+        }
+        if (mycmp(me.name, tmp))
+            strncpyzt(name, hp->h_name, len);
+        else
+            strncpyzt(name, tmp, len);
+        memcpy((char *) &mysk.sin_addr, hp->h_addr, sizeof(struct in_addr));
+
+        Debug((DEBUG_DEBUG, "local name is %s", get_client_name(&me, TRUE)));
+    }
+    return;
+}
+
+/*
+ * do_dns_async
+ *
+ * Called when the fd returned from init_resolver() has been selected for
+ * reading.
+ */
+void do_dns_async()
+{
+    static Link ln;
+    aClient *cptr;
+    aConnect *aconn;
+    struct hostent *hp;
+    int bytes, packets = 0;
+
+    do
+    {
+        ln.flags = -1;
+        hp = get_res((char *) &ln);
+        Debug((DEBUG_DNS, "%#x = get_res(%d,%#x)", hp, ln.flags, 
+               ln.value.cptr));
+
+        switch (ln.flags)
+        {
+            case ASYNC_NONE:
+            /* 
+             * no reply was processed that was outstanding or had
+             * a client still waiting.
+             */
+                break;
+            case ASYNC_CLIENT:
+                if ((cptr = ln.value.cptr))
+                {
+                    del_queries((char *) cptr);
+#ifdef SHOW_HEADERS
+                    sendto_one(cptr, REPORT_FIN_DNS);
+#endif
+                    ClearDNS(cptr);
+                    cptr->hostp = hp;
+                    check_client_fd(cptr);
+                }
+                break;
+            case ASYNC_CONNECT:
+                aconn = ln.value.aconn;
+                if (hp && aconn)
+                {
+                    memcpy((char *) &aconn->ipnum, hp->h_addr,
+                           sizeof(struct in_addr));
+        
+                    connect_server(aconn, NULL, hp);
+                } 
+                else
+                    sendto_ops("Connect to %s failed: host lookup",
+                                (aconn) ? aconn->host : "unknown");
+                break;
+            case ASYNC_CONF:
+                aconn = ln.value.aconn;
+                if (hp && aconn)
+                    memcpy((char *) &aconn->ipnum, hp->h_addr,
+                            sizeof(struct in_addr));
+
+                break;
+            default:
+                break;
+        }
+        if (ioctl(resfd, FIONREAD, &bytes) == -1)
+            bytes = 0;
+        packets++;
+    } while ((bytes > 0) && (packets < 512));
+}
diff --git a/src/s_conf.c b/src/s_conf.c
new file mode 100644 (file)
index 0000000..5554494
--- /dev/null
@@ -0,0 +1,2332 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/s_conf.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: s_conf.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "inet.h"
+#include <signal.h>
+#include "h.h"
+#include "userban.h"
+#include "confparse.h"
+
+/* This entire file has basically been rewritten from scratch with the
+ * exception of lookup_confhost and attach_Iline/attach_iline fucntions
+ * Feb04 -epi
+ */
+
+extern int  rehashed;
+extern int  forked;
+extern tConf tconftab[];
+extern sConf sconftab[];
+
+/* internally defined functions  */
+
+static int          lookup_confhost(aConnect *);
+static int          attach_iline(aClient *, aAllow *, char *, int);
+
+/* externally defined functions  */
+
+extern aClass       *make_class();
+extern aOper        *make_oper();
+extern aConnect     *make_connect();
+extern aAllow       *make_allow();
+extern struct Conf_Me   *make_me();
+extern aPort        *make_port();
+extern void          read_shortmotd(char *);
+
+/* these are our global lists of ACTIVE conf entries */
+
+#define MAXUSERVS 24
+
+aConnect   *connects  = NULL;       /* connects, C/N pairs  */
+aAllow     *allows    = NULL;       /* allows  - I lines    */
+Conf_Me    *MeLine    = NULL;       /* meline - only one    */
+aOper      *opers     = NULL;       /* opers - Olines       */
+aPort      *ports     = NULL;       /* ports - P/M lines    */
+aClass     *classes   = NULL;;      /* classes - Ylines     */
+char       *uservers[MAXUSERVS];    /* uservers = Ulines    */
+Conf_Modules *modules = NULL;
+
+/* this set of lists is used for loading and rehashing the config file */
+
+aConnect    *new_connects   = NULL;
+aAllow      *new_allows     = NULL;
+Conf_Me     *new_MeLine     = NULL;
+aOper       *new_opers      = NULL;
+aPort       *new_ports      = NULL;
+aClass      *new_classes    = NULL;
+char        *new_uservers[MAXUSERVS+1];    /* null terminated array */
+Conf_Modules *new_modules       = NULL;
+
+#ifdef LOCKFILE
+extern void do_pending_klines(void);
+#endif
+extern void confparse_error(char *, int);
+
+/* initclass()
+ * initialize the default class
+ */
+
+void initclass()
+{
+    new_classes = make_class();
+
+    DupString(new_classes->name, "default");
+    new_classes->connfreq = CONNECTFREQUENCY;
+    new_classes->pingfreq = PINGFREQUENCY;
+    new_classes->maxlinks = MAXIMUM_LINKS;
+    new_classes->maxsendq = MAXSENDQLENGTH;
+    new_classes->links = 0;
+}
+
+/* init_globals
+ * initialize our major globals to the defaults first
+ */
+
+void init_globals()
+{
+    strncpyzt(ProxyMonURL, DEFAULT_WGMON_URL, sizeof(ProxyMonURL));
+    strncpyzt(ProxyMonHost, DEFAULT_WGMON_HOST, sizeof(ProxyMonHost));
+    strncpyzt(Network_Name, DEFAULT_NETWORK, sizeof(Network_Name));
+    strncpyzt(Services_Name, DEFAULT_SERVICES_NAME, sizeof(Services_Name));
+    strncpyzt(Stats_Name, DEFAULT_STATS_NAME, sizeof(Stats_Name));
+    snprintf(NS_Services_Name, sizeof(NS_Services_Name), "%s@%s", 
+                NICKSERV, Services_Name);
+    snprintf(CS_Services_Name, sizeof(CS_Services_Name), "%s@%s", 
+                CHANSERV, Services_Name);
+    snprintf(MS_Services_Name, sizeof(MS_Services_Name), "%s@%s", 
+                MEMOSERV, Services_Name);
+    snprintf(RS_Services_Name, sizeof(RS_Services_Name), "%s@%s", 
+                ROOTSERV, Services_Name);
+    snprintf(OS_Stats_Name, sizeof(OS_Stats_Name), "%s@%s", 
+                OPERSERV, Stats_Name);
+    snprintf(SS_Stats_Name, sizeof(SS_Stats_Name), "%s@%s", 
+                STATSERV, Stats_Name);
+    snprintf(HS_Stats_Name, sizeof(HS_Stats_Name), "%s@%s", 
+                HELPSERV, Stats_Name);
+    strncpyzt(NS_Register_URL, DEFAULT_NS_REGISTER_URL,
+              sizeof(NS_Register_URL));
+    strncpyzt(Network_Kline_Address, DEFAULT_NKLINE_ADDY,
+                                            sizeof(Network_Kline_Address));
+    strncpyzt(Local_Kline_Address, DEFAULT_LKLINE_ADDY,
+                                            sizeof(Local_Kline_Address));
+    strncpyzt(Staff_Address, DEFAULT_STAFF_ADDRESS, sizeof(Staff_Address));
+    maxchannelsperuser = DEFAULT_MAXCHANNELSPERUSER;
+    tsmaxdelta = DEFAULT_TSMAXDELTA;
+    tswarndelta = DEFAULT_TSWARNDELTA;
+    local_ip_limit = DEFAULT_LOCAL_IP_CLONES;
+    local_ip24_limit = DEFAULT_LOCAL_IP24_CLONES;
+    global_ip_limit = DEFAULT_GLOBAL_IP_CLONES;
+    global_ip24_limit = DEFAULT_GLOBAL_IP24_CLONES;
+}
+
+
+/* free_ routines
+ * free the requested conf structure
+ * feb.04 -epi
+ */
+
+void
+free_connect(aConnect *ptr)
+{
+    MyFree(ptr->host);
+    MyFree(ptr->apasswd);
+    MyFree(ptr->cpasswd);
+    MyFree(ptr->source);
+    MyFree(ptr->name);
+    MyFree(ptr->class_name);
+    MyFree(ptr);
+    return;
+}
+
+void
+free_allow(aAllow *ptr)
+{
+    MyFree(ptr->ipmask);
+    MyFree(ptr->passwd);
+    MyFree(ptr->hostmask);
+    MyFree(ptr->class_name);
+    MyFree(ptr);
+    return;
+}
+
+void
+free_oper(aOper *ptr)
+{
+    int i = 0;
+    while(ptr->hosts[i])
+    {
+        MyFree(ptr->hosts[i]);
+        i++;
+    }
+    MyFree(ptr->passwd);
+    MyFree(ptr->nick);
+    MyFree(ptr->class_name);
+    MyFree(ptr);
+    return;
+}
+
+void
+free_port(aPort *ptr)
+{
+    MyFree(ptr->allow);
+    MyFree(ptr->address);
+    MyFree(ptr);
+    return;
+}
+
+void
+free_class(aClass *ptr)
+{
+    MyFree(ptr->name);
+    MyFree(ptr);
+    return;
+}
+
+void expire_class(aClass *cl)
+{
+    aClass *ccl, *pcl = NULL;
+    if (cl->refs == 0 && cl->maxlinks == -1)
+    {
+        for (ccl = classes; ccl; ccl = ccl->next)
+        {
+            if (ccl == cl)
+            {
+                if (pcl)
+                    pcl->next = ccl->next;
+                else
+                    classes = ccl->next;
+                free_class(ccl);
+                break;
+            }
+            pcl = ccl;
+        }
+    }
+}
+
+/* clear_conflinks()
+ * remove associated confs from this client
+ * and free the conf if it is scheduled to be deleted
+ * Feb04 -epi
+ */
+
+void
+clear_conflinks(aClient *cptr)
+{
+    if (cptr->class)
+    {
+        cptr->class->links--;
+        cptr->class->refs--;
+        expire_class(cptr->class);
+    }
+    if(IsServer(cptr))
+    {
+        aConnect *x;
+        if((x = cptr->serv->aconn))
+        {
+            x->acpt = NULL;
+            if (x->legal == -1)     /* scheduled for removal? */
+            {
+                aConnect *aconn = NULL;
+
+                if (x == connects)
+                    connects = x->next;
+                else
+                {
+                    for (aconn = connects;
+                         aconn != NULL && aconn->next != x;
+                         aconn = aconn->next);
+                    if (aconn != NULL)
+                        aconn->next = x->next;
+                    else
+                        sendto_realops_lev(DEBUG_LEV, "Deleting scheduled "
+                                           "connect, but it isn't in the "
+                                           "list?? [%s]", x->name);
+                }
+                x->class->refs--;
+                expire_class(x->class);
+                free_connect(x);
+            }
+            cptr->serv->aconn = NULL;
+        }
+    }
+    else if (cptr->user != NULL)
+    {
+        aAllow *x;
+        aOper *y;
+        if((x = cptr->user->allow))
+        {
+            x->clients--;
+            if(x->clients <= 0 && x->legal == -1)
+            {
+                /* remove this allow now that its empty */
+                aAllow *allow = NULL;
+                if (allows == x)
+                    allows = x->next;
+                else
+                {
+                    for (allow = allows;
+                         allow != NULL && allow->next != x;
+                         allow = allow->next);
+                    if (allow != NULL)
+                        allow->next = x->next;
+                    else
+                        sendto_realops_lev(DEBUG_LEV, "Deleting scheduled "
+                                           "allow, but it isn't in the list?? "
+                                           "[%s / %s]", x->ipmask,x->hostmask);
+                }
+                x->class->refs--;
+                expire_class(x->class);
+                free_allow(x);
+            }
+            cptr->user->allow = NULL;
+        }
+        if((y = cptr->user->oper))
+        {
+            y->opers--;
+            if(y->legal == -1 && y->opers <= 0)
+            {
+                aOper *oper = NULL;
+                if (opers == y)
+                    opers = y->next;
+                else
+                {
+                    for (oper = opers;
+                         oper != NULL && oper->next != y;
+                         oper = oper->next);
+                    if (oper != NULL)
+                        oper->next = y->next;
+                    else
+                        sendto_realops_lev(DEBUG_LEV, "Deleting scheduled "
+                                           "oper, but it isn't in the list?? "
+                                           "[%s]", y->nick);
+                }
+                y->class->refs--;
+                expire_class(y->class);
+                free_oper(y);
+            }
+            cptr->user->oper = NULL;
+        }
+    }
+    return;
+}
+
+/* find the appropriate conf and return it */
+
+aConnect *
+find_aConnect(char *name)
+{
+    aConnect *tmp;
+    for(tmp = connects; tmp; tmp = tmp->next)
+        if(!match(name, tmp->name))
+            break;
+    return tmp;
+}
+
+static inline aPort *
+find_port(int port, char *bind)
+{
+    aPort *tmp;
+    for(tmp = ports; tmp; tmp = tmp->next)
+        if (tmp->port == port)
+        {
+            if (tmp->address == bind)  /* both NULL */
+                break;
+            if (tmp->address && bind && !mycmp(tmp->address, bind))
+                break;
+        }
+    return tmp;
+}
+
+aConnect *
+find_aConnect_match(char *name, char *username, char *host)
+{
+    aConnect *aconn;
+    char userhost[USERLEN + HOSTLEN + 3];
+
+    ircsprintf(userhost, "%s@%s", username, host);
+
+    for(aconn = connects; aconn; aconn = aconn->next)
+    {
+        if (aconn->legal == -1)
+            continue;
+        if(!mycmp(name, aconn->name) && !match(userhost, aconn->host))
+            break;
+    }
+    return aconn;
+}
+
+int
+find_aUserver(char *name)
+{
+    int i;
+    for(i = 0; uservers[i]; i++)
+    {
+        if(!mycmp(name, uservers[i]))
+            return 1;
+    }
+    return 0;
+}
+
+aOper *
+find_oper(char *name, char *username, char *sockhost, char *hostip)
+{
+    aOper *aoper;
+    char userhost[USERLEN + HOSTLEN + 3];
+    char userip[USERLEN + HOSTLEN + 3];
+    int i;
+
+    /* sockhost OR hostip must match our host field */
+
+
+    ircsprintf(userhost, "%s@%s", username, sockhost);
+    ircsprintf(userip, "%s@%s", username, hostip);
+
+    for(aoper = opers; aoper; aoper = aoper->next)
+    {
+        if (aoper->legal == -1)
+            continue;
+
+        for(i = 0; aoper->hosts[i]; i++)
+        {
+            if(!mycmp(name, aoper->nick) && (!match(aoper->hosts[i], userhost) 
+                    || !match(aoper->hosts[i], userip)))
+                return aoper;
+        }
+    }
+    return NULL;
+}
+
+static inline aOper *
+find_oper_byname(char *name)
+{
+    aOper *aoper;
+    for(aoper = opers; aoper; aoper = aoper->next)
+        if(!mycmp(name, aoper->nick))
+            break;
+    return aoper;
+}
+
+static inline aClass *
+find_class(char *name)
+{
+    aClass *tmp;
+    if(!name)
+        return find_class("default");
+    for(tmp = classes; tmp; tmp = tmp->next)
+        if(!mycmp(name, tmp->name))
+            break;
+    return tmp;
+}
+
+/* set_effective_class
+ * sets the class for cptr properly
+ */
+
+void
+set_effective_class(aClient *cptr)
+{
+    if (cptr->class)
+    {
+        cptr->class->links--;
+        cptr->class->refs--;
+        expire_class(cptr->class);
+    }
+    if(IsServer(cptr))
+    {
+        if(cptr->serv->aconn->class)
+            cptr->class = cptr->serv->aconn->class;
+        else
+            cptr->class = find_class("default");
+    }
+    else
+    {
+        if(cptr->user && cptr->user->oper)
+            cptr->class = cptr->user->oper->class;
+        else if(cptr->user && cptr->user->allow)
+            cptr->class = cptr->user->allow->class;
+        else
+            cptr->class = find_class("default");
+    }
+    cptr->class->refs++;
+    cptr->class->links++;
+    return;
+}
+    
+
+/* find the first (best) I line to attach.
+ * rewritten in feb04 for the overdue death of aConfItem
+ * and all the shit that came with it.  -epi
+ * Rewritten again in Mar04 to optimize and get rid of deceptive logic.
+ * Whoever wrote this originally must have been drunk...  -Quension
+ */
+int 
+attach_Iline(aClient *cptr, struct hostent *hp, char *sockhost)
+{
+    aAllow *allow;
+
+    static char useriphost[USERLEN + 1 + HOSTLEN + 1];
+    static char usernamehost[USERLEN + 1 + HOSTLEN + 1];
+    char   *iphost;
+    char   *namehost = NULL;    /* squish compiler warning */
+    int     len;
+
+    /* user@host in both buffers, plus pointers to host only */
+    len = ircsprintf(useriphost, "%s@", cptr->username);
+    iphost = useriphost + len;
+    strcpy(iphost, sockhost);
+    if (hp)
+    {
+        memcpy(usernamehost, useriphost, USERLEN+2);
+        namehost = usernamehost + len;
+        len = sizeof(usernamehost) - len;
+        strncpy(namehost, hp->h_name, len);
+        add_local_domain(namehost, len - strlen(namehost));
+    }
+
+    for (allow = allows; allow; allow = allow->next) 
+    {
+        if(allow->legal == -1)
+            continue;
+
+        if (allow->port && (allow->port != cptr->lstn->port))
+            continue;
+
+        if (!allow->ipmask || !allow->hostmask)
+            return (attach_iline(cptr, allow, iphost, 0));
+
+        /* match hostmask against both resolved name and IP, prefer name */
+        if (allow->flags & CONF_FLAGS_I_MATCH_NAME)
+        {
+            if (allow->flags & CONF_FLAGS_I_NAME_HAS_AT)
+            {
+                if (hp && !match(allow->hostmask, usernamehost))
+                    return (attach_iline(cptr, allow, namehost, 1));
+                if (!match(allow->hostmask, useriphost))
+                    return (attach_iline(cptr, allow, hp?namehost:iphost, 1));
+            }
+            else
+            {
+                if (hp && !match(allow->hostmask, namehost))
+                    return (attach_iline(cptr, allow, namehost, 0));
+                if (!match(allow->hostmask, iphost))
+                    return (attach_iline(cptr, allow, hp?namehost:iphost, 0));
+            }
+        }
+
+        if (allow->flags & CONF_FLAGS_I_MATCH_HOST)
+        {
+            if (allow->flags & CONF_FLAGS_I_HOST_HAS_AT)
+            {
+                if (!match(allow->ipmask, useriphost))
+                    return (attach_iline(cptr, allow, iphost, 1));
+            }
+            else
+            {
+                if (!match(allow->ipmask, iphost))
+                    return (attach_iline(cptr, allow, iphost, 0));
+            }
+        }
+    }
+    return -1;          /* no match */
+}
+
+/*
+ * rewrote to remove the "ONE" lamity *BLEH* I agree with comstud on
+ * this one. - Dianora
+ */
+static int 
+attach_iline(aClient *cptr, aAllow *allow, char *uhost, int doid)
+{
+    if(allow->class->links >= allow->class->maxlinks)
+        return -3;
+
+    if (doid)
+        cptr->flags |= FLAGS_DOID;
+    get_sockhost(cptr, uhost);
+    
+    cptr->user->allow = allow;
+    allow->clients++;
+
+    return 0;
+}
+
+/* confadd_ functions
+ * add a config item
+ * Feb.15/04 -epi
+ */
+static int oper_access[] =
+{
+    ~(OFLAG_ADMIN|OFLAG_SADMIN), '*',
+    OFLAG_LOCAL,   'o',
+    OFLAG_GLOBAL,  'O',
+    OFLAG_REHASH,  'r',
+    OFLAG_DIE,     'D',
+    OFLAG_RESTART, 'R',
+    OFLAG_GLOBOP,  'h',
+    OFLAG_WALLOP,  'w',
+    OFLAG_LOCOP,   'l',
+    OFLAG_LROUTE,  'c',
+    OFLAG_GROUTE,  'C',
+    OFLAG_LKILL,   'k',
+    OFLAG_GKILL,   'K',
+    OFLAG_KLINE,   'b',
+    OFLAG_UNKLINE, 'B',
+    OFLAG_LNOTICE, 'n',
+    OFLAG_GNOTICE, 'N',
+    OFLAG_ADMIN,   'A',
+    OFLAG_SADMIN,  'a',
+    OFLAG_UMODEc,  'u',
+    OFLAG_UMODEf,  'f',
+    OFLAG_UMODEF,  'F',
+    0, 0 };
+
+int
+confadd_oper(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    aOper *x = make_oper();
+    int *i, flag, c = 0, hc = 0;
+    char *m = "*";
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & SCONFF_NAME))
+        {
+            if(x->nick)
+            {
+                confparse_error("Multiple name definitions", lnum);
+                free_oper(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->nick, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_HOST))
+        {
+            if((hc+1) > MAXHOSTS)
+            {
+                confparse_error("Excessive host definitions", lnum);
+                free_oper(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            if (!strchr(tmp->value, '@') && *tmp->value != '/')
+            {
+                char       *newhost;
+                int         len = 3;
+                len += strlen(tmp->value);
+                newhost = (char *) MyMalloc(len);
+                ircsprintf(newhost, "*@%s", tmp->value);
+                x->hosts[hc] = newhost;
+            }
+            else
+                DupString(x->hosts[hc], tmp->value);
+            hc++;
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_PASSWD))
+        {
+            if(x->passwd)
+            {
+                confparse_error("Multiple password definitions", lnum);
+                free_oper(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->passwd, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_ACCESS))
+        {
+            if(x->flags > 0)
+            {
+                confparse_error("Multiple access definitions", lnum);
+                free_oper(x);
+                return -1;
+            }
+            for (m=(*tmp->value) ? tmp->value : m; *m; m++)
+            {
+                for (i=oper_access; (flag = *i); i+=2)
+                    if (*m==(char)(*(i+1)))
+                    {
+                        x->flags |= flag;
+                        break;
+                    }
+            }
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_CLASS))
+        {
+            if(x->class_name)
+            {
+                confparse_error("Multiple class definitions", lnum);
+                free_oper(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->class_name, tmp->value);
+        }
+    }
+    if(!x->nick)
+    {
+        confparse_error("Lacking name in oper block", lnum);
+        free_oper(x);
+        return -1;
+    }
+    if(!x->hosts[0])
+    {
+        confparse_error("Lacking host in oper block", lnum);
+        free_oper(x);
+        return -1;
+    }
+    if(!x->passwd)
+    {
+        confparse_error("Lacking passwd in oper block", lnum);
+        free_oper(x);
+        return -1;
+    }
+    if(x->flags == 0)
+    {
+        confparse_error("Lacking access in oper block", lnum);
+        free_oper(x);
+        return -1;
+    }
+    x->next = new_opers;
+    new_opers = x;
+    return lnum;
+}
+
+static int server_info[] =
+{
+    CONN_ZIP, 'Z',
+    CONN_DKEY, 'E',
+    CONN_HUB, 'H',
+    0, 0
+};
+
+int
+confadd_connect(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    aConnect *x = make_connect();
+    int *i, flag, c = 0;
+    char *m = "*";
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & SCONFF_NAME))
+        {
+            if(x->name)
+            {
+                confparse_error("Multiple name definitions", lnum);
+                free_connect(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->name, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_HOST))
+        {
+            if(x->host)
+            {
+                confparse_error("Multiple host definitions", lnum);
+                free_connect(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            if (!strchr(tmp->value, '@') && *tmp->value != '/')
+            {
+                char       *newhost;
+                int         len = 3;
+                len += strlen(tmp->value);
+                newhost = (char *) MyMalloc(len);
+                ircsprintf(newhost, "*@%s", tmp->value);
+                x->host = newhost;
+            }
+            else
+                DupString(x->host, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_APASSWD))
+        {
+            if(x->apasswd)
+            {
+                confparse_error("Multiple apasswd definitions", lnum);
+                free_connect(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->apasswd, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_CPASSWD))
+        {
+            if(x->cpasswd)
+            {
+                confparse_error("Multiple cpasswd definitions", lnum);
+                free_connect(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->cpasswd, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_FLAGS))
+        {
+            if(x->flags > 0)
+            {
+                confparse_error("Multiple flag definitions", lnum);
+                free_connect(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            x->flags = 0;
+            for (m=(*tmp->value) ? tmp->value : m; *m; m++)
+            {
+                for (i=server_info; (flag = *i); i+=2)
+                if (*m==(char)(*(i+1)))
+                {
+                    x->flags |= flag;
+                    break;
+                }
+            }
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_PORT))
+        {
+            if(x->port > 0)
+            {
+                confparse_error("Multiple port definitions", lnum);
+                free_connect(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            x->port = atoi(tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_BIND))
+        {
+            if(x->source)
+            {
+                confparse_error("Multiple source definitions", lnum);
+                free_connect(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->source, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_CLASS))
+        {
+            if(x->class_name)
+            {
+                confparse_error("Multiple class definitions", lnum);
+                free_connect(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->class_name, tmp->value);
+        }
+    }
+    if(!x->name)
+    {
+        confparse_error("Lacking name in connect block", lnum);
+        free_connect(x);
+        return -1;
+    }
+    if(!x->apasswd)
+    {
+        confparse_error("Lacking apasswd in connect block", lnum);
+        free_connect(x);
+        return -1;
+    }
+    if(!x->cpasswd)
+    {
+        confparse_error("Lacking cpasswd in connect block", lnum);
+        free_connect(x);
+        return -1;
+    }
+    if(!x->host)
+    {
+        confparse_error("Lacking host in connect block", lnum);
+        free_connect(x);
+        return -1;
+    }
+    x->next = new_connects;
+    new_connects = x;
+    return lnum;
+}
+
+int
+confadd_options(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    int c = 0;
+    char ctmp[512];
+    char *s;
+
+    /* here, because none of the option peice are interdependent
+     * all the items are added immediately.   Makes life easier
+     * ...except the option flags, which are handled specially -Quension
+     */
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & OPTF_NETNAME))
+        {
+            tmp->type = NULL;
+            strncpyzt(Network_Name, tmp->value, sizeof(Network_Name));
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_SERVNAME))
+        {
+            tmp->type = NULL;
+            strncpyzt(Services_Name, tmp->value, sizeof(Services_Name));
+            sprintf(ctmp, "%s@%s", NICKSERV, Services_Name);
+            strncpyzt(NS_Services_Name, ctmp, sizeof(NS_Services_Name));
+            sprintf(ctmp, "%s@%s", CHANSERV, Services_Name);
+            strncpyzt(CS_Services_Name, ctmp, sizeof(CS_Services_Name));
+            sprintf(ctmp, "%s@%s", MEMOSERV, Services_Name);
+            strncpyzt(MS_Services_Name, ctmp, sizeof(MS_Services_Name));
+            sprintf(ctmp, "%s@%s", ROOTSERV, Services_Name);
+            strncpyzt(RS_Services_Name, ctmp, sizeof(RS_Services_Name));
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_STATSNAME))
+        {
+            tmp->type = NULL;
+            strncpyzt(Stats_Name, tmp->value, sizeof(Stats_Name));
+            sprintf(ctmp, "%s@%s", OPERSERV, Stats_Name);
+            strncpyzt(OS_Stats_Name, ctmp, sizeof(OS_Stats_Name));
+            sprintf(ctmp, "%s@%s", STATSERV, Stats_Name);
+            strncpyzt(SS_Stats_Name, ctmp, sizeof(SS_Stats_Name));
+            sprintf(ctmp, "%s@%s", HELPSERV, Stats_Name);
+            strncpyzt(HS_Stats_Name, ctmp, sizeof(HS_Stats_Name));
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_WGMONHOST))
+        {
+            tmp->type = NULL;
+            new_confopts |= FLAGS_WGMONHOST;
+            strncpyzt(ProxyMonHost, tmp->value, sizeof(ProxyMonHost));
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_WGMONURL))
+        {
+            tmp->type = NULL;
+            new_confopts |= FLAGS_WGMONURL;
+            strncpyzt(ProxyMonURL, tmp->value, sizeof(ProxyMonURL));
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_NSREGURL))
+        {
+            tmp->type = NULL;
+            strncpyzt(NS_Register_URL, tmp->value, sizeof(NS_Register_URL));
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_MAXCHAN))
+        {
+            tmp->type = NULL;
+            maxchannelsperuser = atoi(tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_SERVTYPE))
+        {
+            tmp->type = NULL;
+            if(!mycmp("HUB", tmp->value))
+            {
+                new_confopts |= FLAGS_HUB;
+                new_confopts &= ~FLAGS_SERVHUB;
+            }
+            else if(!mycmp("SERVICESHUB", tmp->value))
+            {
+                new_confopts |= FLAGS_SERVHUB;
+                new_confopts |= FLAGS_HUB;
+            }
+            else if(!mycmp("CLIENT", tmp->value))
+                new_confopts &= ~(FLAGS_HUB|FLAGS_SERVHUB);
+            else
+            {
+                confparse_error("Unknown servtype in option block", lnum);
+                return -1;
+            }
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_NKLINEADDY))
+        {
+            tmp->type = NULL;
+            strncpyzt(Network_Kline_Address, tmp->value,
+                                    sizeof(Network_Kline_Address));
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_LKLINEADDY))
+        {
+            tmp->type = NULL;
+            strncpyzt(Local_Kline_Address, tmp->value,
+                                    sizeof(Local_Kline_Address));
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_STAFFADDY))
+        {
+            tmp->type = NULL;
+            strncpyzt(Staff_Address, tmp->value, sizeof(Staff_Address));
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_LCLONES))
+        {
+            tmp->type = NULL;
+            local_ip_limit = strtol(tmp->value, &s, 10);
+            if (*s == ':')
+                local_ip24_limit = atoi(s+1);
+            if (local_ip_limit < 1)
+                local_ip_limit = DEFAULT_LOCAL_IP_CLONES;
+            if (local_ip24_limit < 1)
+                local_ip24_limit = DEFAULT_LOCAL_IP24_CLONES;
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_GCLONES))
+        {
+            tmp->type = NULL;
+            global_ip_limit = strtol(tmp->value, &s, 10);
+            if (*s == ':')
+                global_ip24_limit = atoi(s+1);
+            if (global_ip_limit < 1)
+                global_ip_limit = DEFAULT_GLOBAL_IP_CLONES;
+            if (global_ip24_limit < 1)
+                global_ip24_limit = DEFAULT_GLOBAL_IP24_CLONES;
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_SMOTD))
+        {
+            tmp->type = NULL;
+            new_confopts |= FLAGS_SMOTD;
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_SMOTD))
+        {
+            tmp->type = NULL;
+            new_confopts |= FLAGS_SMOTD;
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_CRYPTPASS))
+        {
+            tmp->type = NULL;
+            new_confopts |= FLAGS_CRYPTPASS;
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_SHOWLINKS))
+        {
+            tmp->type = NULL;
+            new_confopts |= FLAGS_SHOWLINKS;
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_SPLITOPOK))
+        {
+            tmp->type = NULL;
+            new_confopts |= FLAGS_SPLITOPOK;
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_TSMAXDELTA))
+        {
+            tmp->type = NULL;
+            tsmaxdelta = atoi(tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & OPTF_TSWARNDELTA))
+        {
+            tmp->type = NULL;
+            tswarndelta = atoi(tmp->value);
+        }
+    }
+    return lnum;
+}
+
+int
+confadd_allow(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    aAllow *x = make_allow();
+    int c = 0;
+    /* Currently, Allows are the only config types without
+     * easy identifiers - so we dont worry about duplicate types.
+     * -epi
+     */
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & SCONFF_IPMASK))
+        {
+            if(x->ipmask)
+            {
+                confparse_error("Multiple ipmask definitions", lnum);
+                free_allow(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->ipmask, tmp->value);
+            if(strchr(x->ipmask, '@'))
+                x->flags |= CONF_FLAGS_I_HOST_HAS_AT;
+            x->flags |= CONF_FLAGS_I_MATCH_HOST;
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_HOST))
+        {
+            if(x->hostmask)
+            {
+                confparse_error("Multiple host definitions", lnum);
+                free_allow(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->hostmask, tmp->value);
+            if(strchr(x->hostmask, '@'))
+                x->flags |= CONF_FLAGS_I_NAME_HAS_AT;
+            x->flags |= CONF_FLAGS_I_MATCH_NAME;
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_PASSWD))
+        {
+            if(x->passwd)
+            {
+                confparse_error("Multiple passwd definitions", lnum);
+                free_allow(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->passwd, tmp->value);
+#if (RIDICULOUS_PARANOIA_LEVEL>=1)
+            if(myncmp(x->passwd, "oper", 4) == 0)
+            {
+                if((x->passwd[4] == '.') || (x->passwd[4] == '\0'))
+                {
+                    char *tmpd = x->passwd;
+                    char *tmp = x->passwd + 4;
+
+                    x->flags |= CONF_FLAGS_I_OPERPORT;
+                    if(*tmp)
+                        tmp++;
+                    DupString(x->passwd, tmp);
+                    MyFree(tmpd);
+                }
+            }
+#endif
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_PORT))
+        {
+            if(x->port > 0)
+            {
+                confparse_error("Multiple host definitions", lnum);
+                free_allow(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            x->port = atoi(tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_CLASS))
+        {
+            if(x->class_name)
+            {
+                confparse_error("Multiple class definitions", lnum);
+                free_allow(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->class_name, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_FLAGS))
+        {
+            char *s = tmp->value;
+
+            while (*s)
+                switch (*s++)
+                {
+                    case 'm': x->flags |= CONF_FLAGS_I_OPERPORT; break;
+                    case 'T': x->flags |= CONF_FLAGS_NOTHROTTLE; break;
+                    case 'F': x->flags |= CONF_FLAGS_FORCEFLOOD; break;
+                    case 'C': x->flags |= CONF_FLAGS_SKIPCLONES; break;
+                    default:
+                        confparse_error("Unknown flag", lnum);
+                        free_allow(x);
+                        return -1;
+                }
+
+            tmp->type = NULL;
+        }
+    }
+    if(!x->ipmask && !x->hostmask)
+    {
+        confparse_error("Lacking both ipmask and host for allow", lnum);
+        free_allow(x);
+        return -1;
+    }
+    if(!x->ipmask)
+        DupString(x->ipmask, "-");
+    if(!x->hostmask)
+        DupString(x->hostmask, "-");
+    x->next = new_allows;
+    new_allows = x;
+    return lnum;
+}
+
+int
+confadd_port(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    aPort *x;
+    int    c = 0;
+
+    x = make_port();
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & SCONFF_IPMASK))
+        {
+            if(x->allow)
+            {
+                confparse_error("Multiple ipmask definitions", lnum);
+                free_port(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->allow, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_BIND))
+        {
+            if(x->address)
+            {
+                confparse_error("Multiple bind definitions", lnum);
+                free_port(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->address, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_PORT))
+        {
+            if(x->port > 0)
+            {
+                confparse_error("Multiple port definitions", lnum);
+                free_port(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            x->port = atoi(tmp->value);
+        }
+    }
+    if(!(x->port > 0))
+    {
+        confparse_error("Lacking port in port block", lnum);
+        free_port(x);
+        return -1;
+    }
+    x->next = new_ports;
+    new_ports = x;
+    return lnum;
+}
+
+int
+confadd_global(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    Conf_Me *x = new_MeLine;
+    int c = 0;
+
+    /* note:
+     * we dont free this here because we'll do that if we pull out
+     */
+
+    if(!x)
+    {
+        x = make_me();
+        new_MeLine = x;
+    }
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & SCONFF_NAME))
+        {
+            unsigned char *s;
+            int valid = 0;
+            if(x->servername)
+            {
+                confparse_error("Multiple name definitions", lnum);
+                return -1;
+            }
+            /* validate server name, based on m_server() */
+            for (s = tmp->value; *s; s++)
+            {
+                if (*s < ' ' || *s > '~')
+                {
+                    valid = 0;
+                    break;
+                }
+                if (*s == '.')
+                    valid = 1;
+            }
+            if (!valid)
+            {
+                confparse_error("Invalid server name", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->servername, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_INFO))
+        {
+            if(x->info)
+            {
+                confparse_error("Multiple info definitions", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->info, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_DPASS))
+        {
+            if(x->diepass)
+            {
+                confparse_error("Multiple dpass definitions", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->diepass, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_RPASS))
+        {
+            if(x->restartpass)
+            {
+                confparse_error("Multiple rpass definitions", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->restartpass, tmp->value);
+        }
+    }
+    if(!x->servername)
+    {
+        confparse_error("Lacking name definition in global block", lnum);
+        return -1;
+    }
+    if(!x->info)
+    {
+        confparse_error("Lacking info definition in global block", lnum);
+        return -1;
+    }
+    return lnum;
+}
+
+int
+confadd_admin(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    Conf_Me *x = new_MeLine;
+    int c = 0;
+
+    if(!x)
+    {
+        x = make_me();
+        new_MeLine = x;
+    }
+
+    if (x->admin[0])
+    {
+        confparse_error("Multiple admin blocks", lnum);
+        return -1;
+    }
+
+    for(tmp = vars[c]; tmp && (c != 3); tmp = vars[++c])
+        DupString(x->admin[c], tmp->value);
+
+    return lnum;
+}
+
+int
+confadd_class(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    aClass *x = make_class();
+    int c = 0;
+    char *s;
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & SCONFF_NAME))
+        {
+            if(x->name)
+            {
+                confparse_error("Multiple name definitions", lnum);
+                free_class(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->name, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_PINGFREQ))
+        {
+            if(x->pingfreq > 0)
+            {
+                confparse_error("Multiple pingfreq definitions", lnum);
+                free_class(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            x->pingfreq = atoi(tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_CONNFREQ))
+        {
+            if(x->connfreq > 0)
+            {
+                confparse_error("Multiple maxclones/connfreq definitions",
+                                lnum);
+                free_class(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            x->connfreq = strtol(tmp->value, &s, 10);
+            if (*s == ':')
+                x->ip24clones = atoi(s+1);
+            if (x->connfreq < 1)
+                x->connfreq = 0;
+            if (x->ip24clones < 1)
+                x->ip24clones = 0;
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_MAXUSERS))
+        {
+            if(x->maxlinks > 0)
+            {
+                confparse_error("Multiple maxusers/maxlinks definitions"
+                " (you can only have one or the other)",
+                                lnum);
+                free_class(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            x->maxlinks = atoi(tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_MAXSENDQ))
+        {
+            if(x->maxsendq > 0)
+            {
+                confparse_error("Multiple maxsendq definitions", lnum);
+                free_class(x);
+                return -1;
+            }
+            tmp->type = NULL;
+            x->maxsendq = atoi(tmp->value);
+        }
+    }
+    if(!x->name)
+    {
+        confparse_error("Lacking name definition", lnum);
+        free_class(x);
+        return -1;
+    }
+    if(!(x->maxsendq > 0))
+    {
+        confparse_error("Lacking maxsendq definition", lnum);
+        free_class(x);
+        return -1;
+    }
+    x->next = new_classes;
+    new_classes = x;
+    return lnum;
+}
+
+int
+confadd_kill(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    struct userBan *ban;
+    int i, c = 0;
+    char *ub_u = NULL, *ub_r = NULL, *host = NULL;
+    char fbuf[512];
+    aClient *ub_acptr;
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & SCONFF_MASK))
+        {
+            if(host)
+            {
+                confparse_error("Multiple mask definitions", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            if((host = strchr(tmp->value, '@')))
+            {
+                *host = '\0';
+                host++;
+                ub_u = tmp->value;
+            }
+            else
+                host = tmp->value;
+        }
+        if(tmp->type && (tmp->type->flag & SCONFF_REASON))
+        {
+            if(ub_r)
+            {
+                confparse_error("Multiple reason definitions", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            ub_r = tmp->value;
+            break;
+        }
+    }
+    if(!host)
+    {
+        confparse_error("Lacking mask definition", lnum);
+        return -1;
+    }
+    ub_u = BadPtr(ub_u) ? "*" : ub_u;
+    ub_r = BadPtr(ub_r) ? "<No Reason>" : ub_r;
+
+    ban = make_hostbased_ban(ub_u, host);
+    if(!ban)
+        return lnum;    /* this isnt a parser problem - dont pull out */
+
+    ban->flags |= UBAN_LOCAL;
+    DupString(ban->reason, ub_r);
+    ban->timeset = NOW;
+
+    add_hostbased_userban(ban);
+
+    /* Check local users against it */
+    for (i = 0; i <= highest_fd; i++)
+    {
+        if (!(ub_acptr = local[i]) || IsMe(ub_acptr) ||
+              IsLog(ub_acptr))
+            continue;
+
+        if (IsPerson(ub_acptr) && user_match_ban(ub_acptr, ban))
+        {
+            sendto_ops(LOCAL_BAN_NAME " active for %s",
+                       get_client_name(ub_acptr, FALSE));
+            ircsprintf(fbuf, LOCAL_BANNED_NAME ": %s", ub_r);
+            exit_client(ub_acptr, ub_acptr, &me, fbuf);
+            i--;
+        }
+    }
+    return lnum;
+}
+
+int
+confadd_super(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    int c = 0;
+    int i;
+
+    /* If multiple super blocks are specified, set up to append */
+    for (i = 0; new_uservers[i]; i++)
+        ;
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if (i == MAXUSERVS)
+        {
+            confparse_error("Excessive super server definitions", lnum);
+            return -1;
+        }
+        DupString(new_uservers[i], tmp->value);
+        i++;
+    }
+    new_uservers[i] = NULL;
+    return lnum;
+}
+
+int
+confadd_restrict(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    int c = 0, type = 0;
+    char *mask = NULL, *reason = NULL;
+    struct simBan *ban;
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & SCONFF_TYPE))
+        {
+            if(type > 0)
+            {
+                confparse_error("Multiple type definitions", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            if(!mycmp("CHAN", tmp->value))
+                type = SBAN_CHAN;
+            else if(!mycmp("NICK", tmp->value))
+                type = SBAN_NICK;
+            else if(!mycmp("GCOS", tmp->value))
+                type = SBAN_GCOS;
+            else
+            {
+                confparse_error("Unknown type in restrict block", lnum);
+                return -1;
+            }
+            type |= SBAN_LOCAL;
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_MASK))
+        {
+            if(mask)
+            {
+                confparse_error("Mutliple mask definitions", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            mask = tmp->value;
+        }
+        else if(tmp->type && (tmp->type->flag & SCONFF_REASON))
+        {
+            if(reason)
+            {
+                confparse_error("Multiple reason definitions", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            reason = tmp->value;
+        }
+    }
+    if(!mask)
+    {
+        confparse_error("Missing mask in restrict block", lnum);
+        return -1;
+    }
+    if(!(type > 0))
+    {
+        confparse_error("Missing type in restrict block", lnum);
+        return -1;
+    }
+    ban = make_simpleban(type, mask);
+    if(!ban)
+        return lnum;
+    if(find_simban_exact(ban) != NULL)  /* dont add duplicates */
+    {
+        simban_free(ban);
+        return lnum;
+    }
+    if(!reason)
+    {
+        if(type & SBAN_CHAN)
+            reason = "Reserved Channel";
+        else if(type & SBAN_NICK)
+            reason = "Reserved Nick";
+        else if(type & SBAN_GCOS)
+            reason = "Bad GCOS";
+    }
+    DupString(ban->reason, reason);
+    ban->timeset = NOW;
+
+    add_simban(ban);
+    return lnum;
+}
+
+int
+confadd_modules(cVar *vars[], int lnum)
+{
+    cVar *tmp;
+    Conf_Modules *x = new_modules;
+    int c = 0, ac = 0, oc = 0;
+
+    /* this is like the global block - we dont free here because we do
+     * it if we fail
+     */
+
+    if(!x)
+    {
+        x = (Conf_Modules *) MyMalloc(sizeof(Conf_Modules));
+        memset((char *) x, '\0', sizeof(Conf_Modules));
+        new_modules = x;
+    }
+    else
+    {
+        confparse_error("Multiple module blocks in config file", lnum);
+        return -1;
+    }
+
+    for(tmp = vars[c]; tmp; tmp = vars[++c])
+    {
+        if(tmp->type && (tmp->type->flag & MBTF_PATH))
+        {
+            if(x->module_path)
+            {
+                confparse_error("Multiple module paths defined", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->module_path, tmp->value);
+        }
+        else if(tmp->type && (tmp->type->flag & MBTF_AUTOLOAD))
+        {
+            if((ac+1) > 128)
+            {
+                confparse_error("Excessive autoloading modules (max 128)",
+                                 lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->autoload[ac], tmp->value);
+            ac++;
+        }
+        else if(tmp->type && (tmp->type->flag & MBTF_OPTLOAD))
+        {
+            if((oc+1) > 128)
+            {
+                confparse_error("Excessive optional modules (max 128)", lnum);
+                return -1;
+            }
+            tmp->type = NULL;
+            DupString(x->optload[oc], tmp->value);
+            oc++;
+        }
+    }
+    if(!x->autoload[0] && !x->optload[0])
+    {
+        confparse_error("No modules defined in module block", lnum);
+        return -1;
+    }
+    return lnum;
+}
+    
+
+/* set_classes
+ * after loading the config into temporary lists, we must
+ * set the appropriate classes for each conf.  If we run into
+ * problems, then back out.
+ */
+
+static inline aClass *
+find_new_class(char *name)
+{
+    aClass *tmp;
+    if(!name)
+        return find_new_class("default");
+    for(tmp = new_classes; tmp; tmp = tmp->next)
+        if(!mycmp(name, tmp->name))
+            break;
+    return tmp;
+}
+
+char *
+set_classes(void)
+{
+    aConnect *aconn;
+    aAllow   *allow;
+    aOper    *aoper;
+
+    /* Note:
+     * You may be wondering why we're doing this here and appearently
+     * again in our merge routines!  well, this is for sanity.  if
+     * for whatever reason we dont have a class for each definition here,
+     * back out of the conf load immediately and we wont have distroyed 
+     * or overwritten any of our active data.
+     * After we run our merge_classes() routine at the start of our
+     * merge, then some of these classes will update currently active
+     * classes and be free()'d - meaning some of these references are useless.
+     * That is why we run it again inside the merge routines.
+     * -epi
+     */
+
+    for(aconn = new_connects; aconn; aconn = aconn->next)
+        if(!(aconn->class = find_new_class(aconn->class_name)))
+            return aconn->class_name;
+    for(allow = new_allows; allow; allow = allow->next)
+        if(!(allow->class = find_new_class(allow->class_name)))
+            return allow->class_name;
+    for(aoper = new_opers; aoper; aoper = aoper->next)
+        if(!(aoper->class = find_new_class(aoper->class_name)))
+            return aoper->class_name;
+    return NULL;
+}
+
+
+/* merge routines.  used to mirge together new lists and old lists
+ * after a rehash. Feb27/04 -epi
+ */
+
+static void
+merge_me()
+{
+    if(MeLine)
+    {
+        MyFree(MeLine->info);
+        MyFree(MeLine->diepass);
+        MyFree(MeLine->restartpass);
+        MyFree(MeLine->admin[0]);
+        MyFree(MeLine->admin[1]);
+        MyFree(MeLine->admin[2]);
+        /* MeLine->info is guaranteed to be replaced */
+        MeLine->diepass = NULL;
+        MeLine->restartpass = NULL;
+        MeLine->admin[0] = NULL;
+        MeLine->admin[1] = NULL;
+        MeLine->admin[2] = NULL;
+    }
+    else
+    {
+        MeLine = new_MeLine;
+        strncpyzt(me.name, MeLine->servername, sizeof(me.name));
+        strncpyzt(me.info, MeLine->info, sizeof(me.info));
+        new_MeLine = NULL;
+        return;
+    }
+    DupString(MeLine->info, new_MeLine->info);
+    strncpyzt(me.info, MeLine->info, sizeof(me.info));
+    if(new_MeLine->diepass)
+        DupString(MeLine->diepass, new_MeLine->diepass);
+    if(new_MeLine->restartpass)
+        DupString(MeLine->restartpass, new_MeLine->restartpass);
+    if(new_MeLine->admin[0])
+        DupString(MeLine->admin[0], new_MeLine->admin[0]);
+    if(new_MeLine->admin[1])
+        DupString(MeLine->admin[1], new_MeLine->admin[1]);
+    if(new_MeLine->admin[2])
+        DupString(MeLine->admin[2], new_MeLine->admin[2]);
+    MyFree(new_MeLine->servername);
+    MyFree(new_MeLine->info);
+    MyFree(new_MeLine->diepass);
+    MyFree(new_MeLine->restartpass);
+    MyFree(new_MeLine->admin[0]);
+    MyFree(new_MeLine->admin[1]);
+    MyFree(new_MeLine->admin[2]);
+    MyFree(new_MeLine);
+    new_MeLine = NULL;
+    return;
+}
+
+static void
+merge_connects()
+{
+    aConnect    *aconn, *old_aconn, *ptr = NULL, *ptrn;
+
+    /* first merge the list, then prune the list */
+
+    /* set old as deletable */
+    for(old_aconn = connects; old_aconn; old_aconn = old_aconn->next)
+        old_aconn->legal = -1;
+    /* update or add new */
+    for (aconn = new_connects; aconn; aconn = ptrn)
+    {
+        ptrn = aconn->next;
+        if ((old_aconn = find_aConnect(aconn->name)))
+        {
+            MyFree(old_aconn->host);
+            MyFree(old_aconn->apasswd);
+            MyFree(old_aconn->cpasswd);
+            MyFree(old_aconn->source);
+            MyFree(old_aconn->class_name);
+            old_aconn->class->refs--;
+            expire_class(old_aconn->class);
+
+            old_aconn->host = aconn->host;
+            old_aconn->apasswd = aconn->apasswd;
+            old_aconn->cpasswd = aconn->cpasswd;
+            old_aconn->source = aconn->source;
+            old_aconn->class_name = aconn->class_name;
+            old_aconn->port = aconn->port;
+            old_aconn->flags = aconn->flags;
+            old_aconn->class = find_class(aconn->class_name);
+            old_aconn->class->refs++;
+            old_aconn->legal = 1;
+            lookup_confhost(old_aconn);
+
+            MyFree(aconn->name);
+            MyFree(aconn);
+        }
+        else
+        {
+            aconn->class = find_class(aconn->class_name);
+            aconn->class->refs++;
+            aconn->legal = 1;
+            lookup_confhost(aconn);
+            aconn->next = connects;
+            connects = aconn;
+        }
+    }
+    new_connects = NULL;
+
+    ptr = NULL;
+    /* and prune the active list */
+    aconn = connects;
+    while(aconn)
+    {
+        ptrn = aconn->next;
+        if((aconn->legal == -1) && !aconn->acpt)
+        {
+            if(ptr)
+                ptr->next = aconn->next;
+            else
+                connects = aconn->next;
+            aconn->class->refs--;
+            expire_class(aconn->class);
+            free_connect(aconn);
+        }
+        else
+            ptr = aconn;
+        aconn = ptrn;
+    }
+    return;
+}
+
+static void
+merge_allows()
+{
+    aAllow *allow, *ptr = NULL, *ptrn;
+
+    for(allow = allows; allow; allow = allow->next)
+        allow->legal = -1;
+    allow = new_allows;
+    while(allow)
+    {
+        allow->class = find_class(allow->class_name);
+        allow->class->refs++;
+        /* we dont really have to merge anything here.. */
+        /* ..but we should avoid duplicates anyway */
+        for (ptr = allows; ptr; ptr = ptr->next)
+        {
+            if (ptr->class != allow->class)
+                continue;
+            if (ptr->port != allow->port)
+                continue;
+            if (ptr->flags != allow->flags)
+                continue;
+            if (mycmp(ptr->ipmask, allow->ipmask))
+                continue;
+            if (mycmp(ptr->hostmask, allow->hostmask))
+                continue;
+            /* inverted logic below */
+            if (ptr->passwd && allow->passwd
+                && !mycmp(ptr->passwd, allow->passwd))
+                break;
+            if (ptr->passwd == allow->passwd)
+                break;
+        }
+        /* if duplicate, mark for deletion but add anyway */
+        if (ptr)
+        {
+            ptr->legal = 1;
+            allow->legal = -1;
+        }
+        ptr = allow->next;
+        allow->next = allows;
+        allows = allow;
+        allow = ptr;
+    }
+    new_allows = NULL;
+    ptr = NULL;
+    allow = allows;
+    while(allow)
+    {
+        ptrn = allow->next;
+        if((allow->legal == -1) && (allow->clients <= 0))
+        {
+            if(ptr)
+                ptr->next = allow->next;
+            else
+                allows = allow->next;
+            allow->class->refs--;
+            expire_class(allow->class);
+            free_allow(allow);
+        }
+        else
+            ptr = allow;
+        allow = ptrn;
+    }
+    return;     /* this one is easy */
+}
+    
+static void
+merge_opers()
+{
+    aOper *aoper, *old_oper, *ptrn = NULL, *ptr = NULL;
+
+    for(old_oper = opers; old_oper; old_oper = old_oper->next)
+        old_oper->legal = -1;
+
+    /* add or merge and del new ones */
+    for (aoper = new_opers; aoper; aoper = ptrn)
+    {
+        ptrn = aoper->next;
+        if ((old_oper = find_oper_byname(aoper->nick)))
+        {
+            int i;
+
+            for (i = 0; old_oper->hosts[i]; i++)
+                MyFree(old_oper->hosts[i]);
+            MyFree(old_oper->passwd);
+            MyFree(old_oper->class_name);
+            old_oper->class->refs--;
+            expire_class(old_oper->class);
+
+            for (i = 0; aoper->hosts[i]; i++)
+                old_oper->hosts[i] = aoper->hosts[i];
+            old_oper->hosts[i] = NULL;
+            old_oper->passwd = aoper->passwd;
+            old_oper->class_name = aoper->class_name;
+            old_oper->class = find_class(aoper->class_name);
+            old_oper->class->refs++;
+            old_oper->flags = aoper->flags;
+            old_oper->legal = 1;
+
+            MyFree(aoper->nick);
+            MyFree(aoper);
+        }
+        else
+        {
+            aoper->class = find_class(aoper->class_name);
+            aoper->class->refs++;
+            aoper->legal = 1;
+            aoper->next = opers;
+            opers = aoper;
+        }
+    }
+    new_opers = NULL;
+
+    /* del old ones */
+    ptr = NULL;
+    aoper = opers;
+    while(aoper)
+    {
+        ptrn = aoper->next;
+        if((aoper->legal == -1) && (aoper->opers <= 0))
+        {
+            if(ptr)
+                ptr->next = aoper->next;
+            else
+                opers = aoper->next;
+            free_oper(aoper);
+        }
+        else
+            ptr = aoper;
+        aoper = ptrn;
+    }
+    return;
+}
+
+static void
+merge_ports()
+{
+    aPort *aport, *old_port, *ptrn;
+    
+    if(forked)
+        close_listeners();      /* marks ports for deletion */
+
+    /* add or merge and del new ones */
+    for (aport = new_ports; aport; aport = ptrn)
+    {
+        ptrn = aport->next;
+        if ((old_port = find_port(aport->port, aport->address)))
+        {
+            MyFree(old_port->allow);
+            old_port->allow = aport->allow;
+            old_port->legal = 1;
+            MyFree(aport->address);
+            MyFree(aport);
+        }
+        else
+        {
+            aport->next = ports;
+            ports = aport;
+        }
+    }
+    new_ports = NULL;
+
+    if(forked)
+        open_listeners();
+    return;
+}
+
+static void
+merge_classes()
+{
+    aClass  *class, *old_class, *ptr;
+
+    for(old_class = classes; old_class; old_class = old_class->next)
+        old_class->maxlinks = -1;
+
+    for (class = new_classes; class; class = class->next)
+    {
+        if((old_class = find_class(class->name)))
+        {
+            old_class->connfreq = class->connfreq;
+            old_class->pingfreq = class->pingfreq;
+            old_class->maxlinks = class->maxlinks;
+            old_class->maxsendq = class->maxsendq;
+            old_class->ip24clones = class->ip24clones;
+            class->maxlinks = -1;
+        }
+    }
+
+    /* add classes from new_classes that are not maxlinks = -1 */
+    for (class = new_classes; class; class = old_class)
+    {
+        old_class = class->next;
+        if (class->maxlinks == -1)
+            free_class(class);
+        else
+        {
+            class->next = classes;
+            classes = class;
+        }
+    }
+    new_classes = NULL;
+
+    /* now remove any classes from the list marked and w/o refs */
+    for (class = classes; class; class = ptr)
+    {
+        ptr = class->next;
+        expire_class(class);
+    }
+    return;
+}
+
+void
+merge_options(void)
+{
+    if (forked && !(confopts & FLAGS_SMOTD) && (new_confopts & FLAGS_SMOTD))
+        read_shortmotd(SHORTMOTD);
+    confopts = new_confopts;
+}
+
+void
+merge_confs()
+{
+    int i;
+
+    merge_classes();        /* this should always be done first */
+    merge_me();
+    merge_connects();
+    merge_allows();
+    merge_opers();
+    merge_ports();
+    merge_options();
+    for(i = 0; uservers[i]; i++)
+        MyFree(uservers[i]);
+    for(i = 0; new_uservers[i]; i++)
+    {
+        DupString(uservers[i], new_uservers[i]);
+        MyFree(new_uservers[i]);
+    }
+    new_uservers[0] = NULL;
+    /* dont worry about accually merging module data - its fairly
+     * inactive and static data.  Just replace it.
+     */
+    if(modules)
+    {
+        MyFree(modules->module_path);
+        for(i = 0; modules->autoload[i]; i++)
+            MyFree(modules->autoload[i]);
+        for(i = 0; modules->optload[i]; i++)
+            MyFree(modules->optload[i]);
+        MyFree(modules);
+    }
+    modules = new_modules;
+    new_modules = NULL;
+    return;
+}
+
+static void
+clear_newconfs()
+{
+    aConnect *aconn = new_connects, *aconn_p;
+    aClass   *class = new_classes, *class_p;
+    aOper    *aoper = new_opers, *aoper_p;
+    aPort    *aport = new_ports, *aport_p;
+    aAllow   *allow = new_allows, *allow_p;
+    int i = 0;
+
+    while(aconn)
+    {
+        aconn_p = aconn->next;
+        free_connect(aconn);
+        aconn = aconn_p;
+    }
+    new_connects = NULL;
+    while(class)
+    {
+        class_p = class->next;
+        free_class(class);
+        class = class_p;
+    }
+    new_classes = NULL;
+    while(aoper)
+    {
+        aoper_p = aoper->next;
+        free_oper(aoper);
+        aoper = aoper_p;
+    }
+    new_opers = NULL;
+    while(aport)
+    {
+        aport_p = aport->next;
+        free_port(aport);
+        aport = aport_p;
+    }
+    new_ports = NULL;
+    while(allow)
+    {
+        allow_p = allow->next;
+        free_allow(allow);
+        allow = allow_p;
+    }
+    new_allows = NULL;
+    if(new_MeLine)
+    {
+        MyFree(new_MeLine->servername);
+        MyFree(new_MeLine->info);
+        MyFree(new_MeLine->diepass);
+        MyFree(new_MeLine->restartpass);
+        MyFree(new_MeLine->admin[0]);
+        MyFree(new_MeLine->admin[1]);
+        MyFree(new_MeLine->admin[2]);
+        MyFree(new_MeLine);
+        new_MeLine = NULL;
+    }
+    while(new_uservers[i])
+    {
+        DupString(uservers[i], new_uservers[i]);
+        MyFree(new_uservers[i]);
+        i++;
+    }
+    new_uservers[0] = NULL;
+    if(new_modules)
+    {
+        for(i = 0; new_modules->autoload[i]; i++)
+            MyFree(new_modules->autoload[i]);
+        for(i = 0; new_modules->optload[i]; i++)
+            MyFree(new_modules->optload[i]);
+        MyFree(new_modules->module_path);
+        MyFree(new_modules);
+        new_modules = NULL;
+    }
+    return;
+}
+
+/*
+ * rehash
+ * 
+ * Actual REHASH service routine. Called with sig == 0 if it has been
+ * called as a result of an operator issuing this command, else assume
+ * it has been called as a result of the server receiving a HUP signal.
+ */
+int rehash(aClient *cptr, aClient *sptr, int sig)
+{
+    aClient    *acptr;
+    int         i;
+    char       *conferr;
+
+    if (sig == SIGHUP) 
+    {
+        sendto_ops("Got signal SIGHUP, reloading ircd conf. file");
+        remove_userbans_match_flags(UBAN_NETWORK, 0);
+        remove_userbans_match_flags(UBAN_LOCAL|UBAN_TEMPORARY, 0);
+        remove_simbans_match_flags(SBAN_NICK|SBAN_LOCAL|SBAN_TEMPORARY, 0);
+        remove_simbans_match_flags(SBAN_CHAN|SBAN_LOCAL|SBAN_TEMPORARY, 0);
+        remove_simbans_match_flags(SBAN_GCOS|SBAN_LOCAL|SBAN_TEMPORARY, 0);
+
+    }
+
+    /* Shadowfax's LOCKFILE code */
+#ifdef LOCKFILE
+    do_pending_klines();
+#endif
+
+    for (i = 0; i <= highest_fd; i++)
+        if ((acptr = local[i]) && !IsMe(acptr)) 
+        {
+            /*
+             * Nullify any references from client structures to this host
+             * structure which is about to be freed. Could always keep
+             * reference counts instead of this....-avalon
+             */
+            acptr->hostp = NULL;
+        }
+
+    if (sig != SIGINT)
+        flush_cache();      /* Flush DNS cache */
+
+    /* remove perm klines */
+    remove_userbans_match_flags(UBAN_LOCAL, UBAN_TEMPORARY);
+    remove_simbans_match_flags(SBAN_NICK|SBAN_LOCAL, SBAN_TEMPORARY);
+    remove_simbans_match_flags(SBAN_CHAN|SBAN_LOCAL, SBAN_TEMPORARY);
+    remove_simbans_match_flags(SBAN_GCOS|SBAN_LOCAL, SBAN_TEMPORARY);
+
+
+    initclass();
+    new_confopts = 0;
+
+    if(initconf(configfile) == -1)
+    {
+        sendto_realops("Rehash Aborted");
+        clear_newconfs();
+        return 1;
+    }
+
+    conferr = finishconf();
+    if (conferr)
+    {
+        sendto_realops("Rehash Aborted: %s", conferr);
+        clear_newconfs();
+        return 1;
+    }
+    
+    merge_confs();
+    build_rplcache();
+
+    rehashed = 1;
+
+    return 1;
+}
+
+/*
+ * lookup_confhost Do (start) DNS lookups of all hostnames in the conf
+ * line and convert an IP addresses in a.b.c.d number for to IP#s.
+ * 
+ * cleaned up Aug 3'97 - Dianora
+ * rewritten to kill aConfItem, Feb/04 - epi
+ */
+static int lookup_confhost(aConnect *aconn)
+{
+    char   *s;
+    struct hostent *hp;
+    Link        ln;
+    if (BadPtr(aconn->host) || BadPtr(aconn->name)) 
+    {
+    if (aconn->ipnum.s_addr == -1)
+        memset((char *) &aconn->ipnum, '\0', sizeof(struct in_addr));
+
+    Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
+           aconn->host, aconn->name));
+    return -1;
+    }
+    if ((s = strchr(aconn->host, '@')))
+    s++;
+    else
+    s = aconn->host;
+    /*
+     * Do name lookup now on hostnames given and store the ip
+     * numbers in conf structure.
+     */
+    if (!IsAlpha(*s) && !IsDigit(*s)) 
+    {
+    if (aconn->ipnum.s_addr == -1)
+        memset((char *) &aconn->ipnum, '\0', sizeof(struct in_addr));
+
+    Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
+           aconn->host, aconn->name));
+    return -1;
+    }
+    /*
+     * Prepare structure in case we have to wait for a reply which
+     * we get later and store away.
+     */
+    ln.value.aconn = aconn;
+    ln.flags = ASYNC_CONF;
+    
+    if (IsDigit(*s))
+    aconn->ipnum.s_addr = inet_addr(s);
+    else if ((hp = gethost_byname(s, &ln)))
+    memcpy((char *) &(aconn->ipnum), hp->h_addr,
+           sizeof(struct in_addr));
+
+    if (aconn->ipnum.s_addr == -1)
+    memset((char *) &aconn->ipnum, '\0', sizeof(struct in_addr));
+    {
+    Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
+           aconn->host, aconn->name));
+    return -1;
+    }
+    /* NOTREACHED */
+    return 0;
+}
diff --git a/src/s_debug.c b/src/s_debug.c
new file mode 100644 (file)
index 0000000..9cbd6e3
--- /dev/null
@@ -0,0 +1,284 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/s_debug.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: s_debug.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "patchlevel.h"
+#include "numeric.h"
+#include "channel.h"
+
+/* This file is hereby declared the nexus of all things ugly and preprocessed */
+
+static char rplisupport1[BUFSIZE];
+static char rplisupport2[BUFSIZE];
+static char rplversion[BUFSIZE];
+static char scratchbuf[BUFSIZE];
+
+/* send cached RPL_ISUPPORT */
+void send_rplisupport(aClient *acptr)
+{
+    sendto_one(acptr, rplisupport1, acptr->name);
+    sendto_one(acptr, rplisupport2, acptr->name);
+}
+
+/* send cached RPL_VERSION */
+void send_rplversion(aClient *acptr)
+{
+    sendto_one(acptr, rplversion, acptr->name);
+}
+
+
+/* build and cache complex strings */
+void build_rplcache(void)
+{
+    char *s;
+
+    /* build RPL_ISUPPORT */
+
+    /* Most of this tracks draft-brocklesby-irc-isupport-03, with a
+    * few differences:
+    * STD is not sent since there is no RFC
+    * MAXCHANNELS and MAXBANS are sent for compatibility with old clients
+    * SILENCE WATCH and ELIST are sent but not documented
+    */
+
+    /* put MAXBANS and MAXCHANNELS first so better tokens override them */
+    ircsprintf(scratchbuf,"NETWORK=%s SAFELIST MAXBANS=%i MAXCHANNELS=%i "
+               "CHANNELLEN=%i KICKLEN=%i NICKLEN=%i TOPICLEN=%i MODES=%i "
+               "CHANTYPES=# CHANLIMIT=#:%i PREFIX=(ov)@+ STATUSMSG=@+",
+               Network_Name, MAXBANS, maxchannelsperuser, CHANNELLEN,
+               TOPICLEN, NICKLEN, TOPICLEN, MAXMODEPARAMSUSER,
+               maxchannelsperuser);
+
+    ircsprintf(rplisupport1, rpl_str(RPL_ISUPPORT), me.name, "%s", scratchbuf);
+
+    s = scratchbuf;
+    s += ircsprintf(s, "CASEMAPPING=ascii WATCH=%i SILENCE=%i ELIST=cmntu",
+                    MAXWATCH, MAXSILES);
+#ifdef EXEMPT_LISTS
+    s += ircsprintf(s, " EXCEPTS");
+#endif
+#ifdef INVITE_LISTS
+    s += ircsprintf(s, " INVEX");
+#endif
+    s += ircsprintf(s, " CHANMODES=b");
+#ifdef EXEMPT_LISTS
+    *s++ = 'e';
+#endif
+#ifdef INVITE_LISTS
+    *s++ = 'I';
+#endif
+    s += ircsprintf(s, ",k,jl,ci");
+#ifdef USE_CHANMODE_L
+    *s++ = 'L';
+#endif
+    s += ircsprintf(s, "mMnOprRst MAXLIST=b:%i", MAXBANS);
+#ifdef EXEMPT_LISTS
+    s += ircsprintf(s, ",e:%i", MAXEXEMPTLIST);
+#endif
+#ifdef INVITE_LISTS
+    s += ircsprintf(s, ",I:%i", MAXINVITELIST);
+#endif
+    s += ircsprintf(s, " TARGMAX=DCCALLOW:,JOIN:,KICK:4,KILL:20,NOTICE:%i,"
+                    "PART:,PRIVMSG:%i,WHOIS:,WHOWAS:", MAXRECIPIENTS,
+                    MAXRECIPIENTS);
+
+    ircsprintf(rplisupport2, rpl_str(RPL_ISUPPORT), me.name, "%s", scratchbuf);
+
+
+    /* build RPL_VERSION */
+    s = scratchbuf;
+
+#ifdef ANTI_SPAMBOT
+    *s++ = 'a';
+#endif
+#ifdef ALWAYS_SEND_DURING_SPLIT
+    *s++ = 'A';
+#endif
+#ifdef MAXBUFFERS
+    *s++ = 'B';
+#endif
+#ifdef CMDLINE_CONFIG
+    *s++ = 'C';
+#endif
+#ifdef DO_IDENTD
+    *s++ = 'd';
+#endif
+#ifdef DEBUGMODE
+    *s++ = 'D';
+#endif
+#ifdef HAVE_ENCRYPTION_ON
+    *s++ = 'E';
+#endif
+#ifdef FLUD
+    *s++ = 'F';
+#endif
+#ifdef SHOW_HEADERS
+    *s++ = 'h';
+#endif
+#ifdef SHOW_INVISIBLE_LUSERS
+    *s++ = 'i';
+#endif
+#ifdef NO_DEFAULT_INVISIBLE
+    *s++ = 'I';
+#endif
+#ifdef NO_DEFAULT_JOINRATE
+    *s++ = 'J';
+#endif
+#ifdef USE_HOOKMODULES
+    *s++ = 'M';
+#endif
+#ifdef DNS_DEBUG
+    *s++ = 'N';
+#endif
+#ifdef DENY_SERVICES_MSGS
+    *s++ = 'r';
+#endif
+#ifdef SUPER_TARGETS_ONLY
+    *s++ = 's';
+#endif
+#ifdef MSG_TARGET_LIMIT
+    *s++ = 't';
+#endif
+#ifdef THROTTLE_ENABLE
+    *s++ = 'T';
+#endif
+#ifdef IRCII_KLUDGE
+    *s++ = 'u';
+#endif
+#ifdef USE_SYSLOG
+    *s++ = 'Y';
+#endif
+    *s++ = '/';
+    if (confopts & FLAGS_HUB)
+        *s++ = 'H';
+    if (confopts & FLAGS_SMOTD)
+        *s++ = 'm';
+    if (confopts & FLAGS_SPLITOPOK)
+        *s++ = 'o';
+    if (confopts & FLAGS_CRYPTPASS)
+        *s++ = 'p';
+    if (confopts & FLAGS_SERVHUB)
+        *s++ = 'S';
+    if ((confopts & FLAGS_WGMON) == FLAGS_WGMON)
+        *s++ = 'w';
+    
+    s += ircsprintf(s, " TS%iow", TS_CURRENT);
+#ifdef BRANCHSTATUS
+    s += ircsprintf(s, "-r[%s]",
+# if BRANCHSTATUS == CURRENT
+                    "CURRENT"
+# elif BRANCHSTATUS == RELEASE
+                    "RELEASE"
+# elif BRANCHSTATUS == STABLE
+                    "STABLE"
+# elif BRANCHSTATUS == BETA
+                    "BETA"
+# else
+                    "UNKNOWN"
+# endif
+                    );
+#endif
+
+#ifdef RIDICULOUS_PARANOIA_LEVEL
+    s += ircsprintf(s, " RPL%i", RIDICULOUS_PARANOIA_LEVEL);
+#endif
+
+    s += ircsprintf(s, " NP[");
+#ifdef FORCE_EVERYONE_HIDDEN
+    *s++ = 'A';
+#endif
+#ifdef ALLOW_HIDDEN_OPERS
+    *s++ = 'I';
+#endif
+#ifdef HIDE_KILL_ORIGINS
+    *s++ = 'K';
+#endif
+#ifdef NO_USER_SERVERKILLS
+    *s++ = 'k';
+#endif
+    if (!(confopts & FLAGS_SHOWLINKS))
+        *s++ = 'L';
+#ifdef HIDE_SERVERMODE_ORIGINS
+    *s++ = 'M';
+#endif
+#ifdef HIDE_NUMERIC_SOURCE
+    *s++ = 'N';
+#endif
+#ifdef NO_USER_OPERTARGETED_COMMANDS
+    *s++ = 'O';
+#endif
+#ifdef HIDE_SPLIT_SERVERS
+    *s++ = 'P';
+#endif
+#ifdef NO_USER_STATS
+    *s++ = 'S';
+#endif
+#ifdef NO_USER_OPERKILLS
+    *s++ = 's';
+#endif
+#ifdef NO_USER_TRACE
+    *s++ = 'T';
+#endif
+#ifdef HIDEULINEDSERVS
+    *s++ = 'U';
+#endif
+    *s++ = ']';
+    *s++ = 0;
+
+    ircsprintf(rplversion, rpl_str(RPL_VERSION), me.name, "%s", version,
+                    debugmode, me.name, scratchbuf);
+}
+
+
+#if defined(DNS_DEBUG) || defined(DEBUGMODE)
+static char debugbuf[1024];
+
+void debug(int level, char *pattern, ...)
+{
+    va_list      vl;
+    int         err = errno;
+    
+    va_start(vl, pattern);
+    (void) vsprintf(debugbuf, pattern, vl);
+    va_end(vl);
+
+#ifdef USE_SYSLOG
+    if (level == DEBUG_ERROR)
+        syslog(LOG_ERR, "%s", debugbuf);
+#endif
+
+    if ((debuglevel >= 0) && (level <= debuglevel)) {
+
+        if (local[2]) {
+            local[2]->sendM++;
+            local[2]->sendB += strlen(debugbuf);
+        }
+        (void) fprintf(stderr, "%s", debugbuf);
+        (void) fputc('\n', stderr);
+    }
+    errno = err;
+}
+
+#endif
diff --git a/src/s_err.c b/src/s_err.c
new file mode 100644 (file)
index 0000000..565f831
--- /dev/null
@@ -0,0 +1,1094 @@
+/* Copyright (C) 1992 Darren Reed
+ *
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "numeric.h"
+#include "h.h"
+
+static char *replies[] = 
+{
+    /* 000 */  NULL,
+    /* 001 RPL_WELCOME */      ":%s 001 %s :Welcome to the %s IRC "
+                                "Network %s!%s@%s",
+    /* 002 RPL_YOURHOST */     ":%s 002 %s :Your host is %s, running "
+                                "version %s",
+    /* 003 RPL_CREATED */      ":%s 003 %s :This server was created %s",
+    /* 004 RPL_MYINFO */       ":%s 004 %s %s %s aAbcdefFghiIjkKmnoOrRswxXy "
+                                "bceiIjklLmMnoOprRstv",
+    /* 005 RPL_ISUPPORT */      ":%s 005 %s %s :are available on this server",
+    /* 006 */                  NULL,
+    /* 007 */                  NULL,
+    /* 008 */                  NULL,
+    /* 009 */                  NULL,
+    /* 010 */                  NULL,
+    /* 011 */                  NULL,
+    /* 012 */                  NULL,
+    /* 013 */                  NULL,
+    /* 014 */                  NULL,
+    /* 015 */                  NULL,
+    /* 016 */                  NULL,
+    /* 017 */                  NULL,
+    /* 018 */                  NULL,
+    /* 019 */                  NULL,
+    /* 020 */                  NULL,
+    /* 021 */                  NULL,
+    /* 022 */                  NULL,
+    /* 023 */                  NULL,
+    /* 024 */                  NULL,
+    /* 025 */                  NULL,
+    /* 026 */                  NULL,
+    /* 027 */                  NULL,
+    /* 028 */                  NULL,
+    /* 029 */                  NULL,
+    /* 030 */                  NULL,
+    /* 031 */                  NULL,
+    /* 032 */                  NULL,
+    /* 033 */                  NULL,
+    /* 034 */                  NULL,
+    /* 035 */                  NULL,
+    /* 036 */                  NULL,
+    /* 037 */                  NULL,
+    /* 038 */                  NULL,
+    /* 039 */                  NULL,
+    /* 040 */                  NULL,
+    /* 041 */                  NULL,
+    /* 042 */                  NULL,
+    /* 043 */                  NULL,
+    /* 044 */                  NULL,
+    /* 045 */                  NULL,
+    /* 046 */                  NULL,
+    /* 047 */                  NULL,
+    /* 048 */                  NULL,
+    /* 049 */                  NULL,
+    /* 050 */                  NULL,
+    /* 051 */                  NULL,
+    /* 052 */                  NULL,
+    /* 053 */                  NULL,
+    /* 054 */                  NULL,
+    /* 055 */                  NULL,
+    /* 056 */                  NULL,
+    /* 057 */                  NULL,
+    /* 058 */                  NULL,
+    /* 059 */                  NULL,
+    /* 060 */                  NULL,
+    /* 061 */                  NULL,
+    /* 062 */                  NULL,
+    /* 063 */                  NULL,
+    /* 064 */                  NULL,
+    /* 065 */                  NULL,
+    /* 066 */                  NULL,
+    /* 067 */                  NULL,
+    /* 068 */                  NULL,
+    /* 069 */                  NULL,
+    /* 070 */                  NULL,
+    /* 071 */                  NULL,
+    /* 072 */                  NULL,
+    /* 073 */                  NULL,
+    /* 074 */                  NULL,
+    /* 075 */                  NULL,
+    /* 076 */                  NULL,
+    /* 077 */                  NULL,
+    /* 078 */                  NULL,
+    /* 079 */                  NULL,
+    /* 080 */                  NULL,
+    /* 081 */                  NULL,
+    /* 082 */                  NULL,
+    /* 083 */                  NULL,
+    /* 084 */                  NULL,
+    /* 085 */                  NULL,
+    /* 086 */                  NULL,
+    /* 087 */                  NULL,
+    /* 088 */                  NULL,
+    /* 089 */                  NULL,
+    /* 090 */                  NULL,
+    /* 091 */                  NULL,
+    /* 092 */                  NULL,
+    /* 093 */                  NULL,
+    /* 094 */                  NULL,
+    /* 095 */                  NULL,
+    /* 096 */                  NULL,
+    /* 097 */                  NULL,
+    /* 098 */                  NULL,
+    /* 099 */                  NULL,
+    /* 100 */                  NULL,
+    /* 101 */                  NULL,
+    /* 102 */                  NULL,
+    /* 103 */                  NULL,
+    /* 104 */                  NULL,
+    /* 105 */                  NULL,
+    /* 106 */                  NULL,
+    /* 107 */                  NULL,
+    /* 108 */                  NULL,
+    /* 109 */                  NULL,
+    /* 110 */                  NULL,
+    /* 111 */                  NULL,
+    /* 112 */                  NULL,
+    /* 113 */                  NULL,
+    /* 114 */                  NULL,
+    /* 115 */                  NULL,
+    /* 116 */                  NULL,
+    /* 117 */                  NULL,
+    /* 118 */                  NULL,
+    /* 119 */                  NULL,
+    /* 120 */                  NULL,
+    /* 121 */                  NULL,
+    /* 122 */                  NULL,
+    /* 123 */                  NULL,
+    /* 124 */                  NULL,
+    /* 125 */                  NULL,
+    /* 126 */                  NULL,
+    /* 127 */                  NULL,
+    /* 128 */                  NULL,
+    /* 129 */                  NULL,
+    /* 130 */                  NULL,
+    /* 131 */                  NULL,
+    /* 132 */                  NULL,
+    /* 133 */                  NULL,
+    /* 134 */                  NULL,
+    /* 135 */                  NULL,
+    /* 136 */                  NULL,
+    /* 137 */                  NULL,
+    /* 138 */                  NULL,
+    /* 139 */                  NULL,
+    /* 140 */                  NULL,
+    /* 141 */                  NULL,
+    /* 142 */                  NULL,
+    /* 143 */                  NULL,
+    /* 144 */                  NULL,
+    /* 145 */                  NULL,
+    /* 146 */                  NULL,
+    /* 147 */                  NULL,
+    /* 148 */                  NULL,
+    /* 149 */                  NULL,
+    /* 150 */                  NULL,
+    /* 151 */                  NULL,
+    /* 152 */                  NULL,
+    /* 153 */                  NULL,
+    /* 154 */                  NULL,
+    /* 155 */                  NULL,
+    /* 156 */                  NULL,
+    /* 157 */                  NULL,
+    /* 158 */                  NULL,
+    /* 159 */                  NULL,
+    /* 160 */                  NULL,
+    /* 161 */                  NULL,
+    /* 162 */                  NULL,
+    /* 163 */                  NULL,
+    /* 164 */                  NULL,
+    /* 165 */                  NULL,
+    /* 166 */                  NULL,
+    /* 167 */                  NULL,
+    /* 168 */                  NULL,
+    /* 169 */                  NULL,
+    /* 170 */                  NULL,
+    /* 171 */                  NULL,
+    /* 172 */                  NULL,
+    /* 173 */                  NULL,
+    /* 174 */                  NULL,
+    /* 175 */                  NULL,
+    /* 176 */                  NULL,
+    /* 177 */                  NULL,
+    /* 178 */                  NULL,
+    /* 179 */                  NULL,
+    /* 180 */                  NULL,
+    /* 181 */                  NULL,
+    /* 182 */                  NULL,
+    /* 183 */                  NULL,
+    /* 184 */                  NULL,
+    /* 185 */                  NULL,
+    /* 186 */                  NULL,
+    /* 187 */                  NULL,
+    /* 188 */                  NULL,
+    /* 189 */                  NULL,
+    /* 190 */                  NULL,
+    /* 191 */                  NULL,
+    /* 192 */                  NULL,
+    /* 193 */                  NULL,
+    /* 194 */                  NULL,
+    /* 195 */                  NULL,
+    /* 196 */                  NULL,
+    /* 197 */                  NULL,
+    /* 198 */                  NULL,
+    /* 199 */                  NULL,
+    /* 200 RPL_TRACELINK */    ":%s 200 %s Link %s%s %s %s",
+    /* 201 RPL_TRACECONNECTING */       ":%s 201 %s Attempt %s %s",
+    /* 202 RPL_TRACEHANDSHAKE */       ":%s 202 %s Handshaking %s %s",
+    /* 203 RPL_TRACEUNKNOWN */ ":%s 203 %s ???? %s %s %d",
+    /* 204 RPL_TRACEOPERATOR */        ":%s 204 %s Operator %s %s %ld",
+    /* 205 RPL_TRACEUSER */            ":%s 205 %s User %s %s %ld",
+    /* 206 RPL_TRACESERVER */  ":%s 206 %s Server %s %dS %dC %s %s!%s@%s %ld",
+    /* 207 */                  NULL,
+    /* 208 RPL_TRACENEWTYPE */ ":%s 208 %s <newtype> 0 %s",
+    /* 209 RPL_TRACECLASS */   ":%s 209 %s Class %s %d",
+    /* 210 */                  NULL,
+    /* 211 */                  NULL,
+    /* 212 RPL_STATSCOMMANDS */        ":%s 212 %s %s %u %u",
+    /* 213 RPL_STATSCLINE */   ":%s 213 %s %s %s * %s %d %s",
+    /* 214 RPL_STATSNLINE */   ":%s 214 %s %s %s * %s %d %s",
+    /* 215 RPL_STATSILINE */   ":%s 215 %s %s %s %d %s %d %s",
+    /* 216 RPL_STATSKLINE */   ":%s 216 %s %s %s * %s %d :%s",
+    /* 217 RPL_STATSQLINE */   ":%s 217 %s %s * * %s %d :%s",
+    /* 218 RPL_STATSYLINE */   ":%s 218 %s %c %s %d %d:%d %d %ld",
+    /* 219 RPL_ENDOFSTATS */   ":%s 219 %s %c :End of /STATS report.",
+    /* 220 */                  NULL,
+    /* 221 RPL_UMODEIS */              ":%s 221 %s %s",
+    /* 222 */                  ":%s 222 %s %c %s * %s %d %d",
+    /* 223 */                  ":%s 223 %s %c %s * %s %d %d",
+    /* 224 */                  ":%s 224 %s %c %s * %s %d %d",
+    /* 225 RPL_STATSCLONE */   ":%s 225 %s D %s %d %d %d",
+    /* 226 RPL_STATSCOUNT */   ":%s 226 %s %s %l",
+    /* 227 RPL_STATSGLINE */   ":%s 227 %s %s * * %s %d :%s",
+    /* 228 */                  NULL,
+    /* 229 */                  NULL,
+    /* 230 */                  NULL,
+    /* 231 */                  NULL,   /* In use by Undernet */
+    /* 232 */                  NULL,   /* In use by Undernet */
+    /* 233 */                  NULL,   /* In use by Undernet */
+    /* 234 */                  NULL,
+    /* 235 */                  NULL,
+    /* 236 */                  NULL,
+    /* 237 */                  NULL,
+    /* 238 */                  NULL,
+    /* 239 */                  NULL,
+    /* 240 */                  NULL,
+    /* 241 RPL_STATSLLINE */   ":%s 241 %s %c %s * %s %d %d",
+    /* 242 RPL_STATSUPTIME */  ":%s 242 %s :Server Up %d days, %d:%02d:%02d",
+    /* 243 RPL_STATSOLINE */   ":%s 243 %s %s %s * %s %lu %s",
+    /* 244 RPL_STATSHLINE */   NULL,
+    /* 245 RPL_STATSSLINE */   NULL,
+    /* 246 RPL_STATSXLINE */   ":%s 246 %s %s %s * %s %d %d",
+    /* 247 */                  NULL,           /* Undernet's STATSGLINE */
+    /* 248 */                  NULL,           /* Undernet's STATSULINE */
+    /* 249 */                  NULL,
+    /* 250 */                  NULL,
+    /* 251 RPL_LUSERCLIENT */  ":%s 251 %s :There are %d users and %d "
+                                "invisible on %d servers",
+    /* 252 RPL_LUSEROP */      ":%s 252 %s %d :IRC Operators online",
+    /* 253 RPL_LUSERUNKNOWN */ ":%s 253 %s %d :unknown connection(s)",
+    /* 254 RPL_LUSERCHANNELS */        ":%s 254 %s %d :channels formed",
+    /* 255 RPL_LUSERME */      ":%s 255 %s :I have %d clients and %d servers",
+    /* 256 RPL_ADMINME */      ":%s 256 %s :Administrative info about %s",
+    /* 257 RPL_ADMINLOC1 */    ":%s 257 %s :%s",
+    /* 258 RPL_ADMINLOC2 */    ":%s 258 %s :%s",
+    /* 259 RPL_ADMINEMAIL */   ":%s 259 %s :%s",
+    /* 260 */                  NULL,
+    /* 261 RPL_TRACELOG */     ":%s 261 %s File %s %d",
+    /* 262 */                  ":%s 262 %s %s :End of TRACE",
+    /* 263 */                  ":%s 263 %s :Server load is temporarily too "
+                                "heavy. Please wait a while and try again.",
+    /* 264 */                  NULL,
+    /* 265 RPL_LOCALUSERS */   ":%s 265 %s :Current local users: %d Max: %d",
+    /* 266 RPL_GLOBALUSERS */  ":%s 266 %s :Current global users: %d Max: %d",
+    /* 267 */                  NULL,
+    /* 268 */                  NULL,
+    /* 269 */                  NULL,
+    /* 270 */                  NULL,
+    /* 271 RPL_SILELIST */      ":%s 271 %s %s %s",
+    /* 272 RPL_ENDOFSILELIST*/ ":%s 272 %s :End of /SILENCE list.",
+    /* 273 */                  NULL,
+    /* 274 */                  NULL,
+    /* 275 */                  NULL,   /* In use by Undernet */
+    /* 276 */                  NULL,
+    /* 277 */                  NULL,
+    /* 278 */                  NULL,
+    /* 279 */                  NULL,
+    /* 280 */                  NULL,   /* In use by Undernet */
+    /* 281 */                  NULL,   /* In use by Undernet */
+    /* 282 */                  NULL,
+    /* 283 */                  NULL,
+    /* 284 */                  NULL,
+    /* 285 */                  NULL,
+    /* 286 */                  NULL,
+    /* 287 */                  NULL,
+    /* 288 */                  NULL,
+    /* 289 */                  NULL,
+    /* 290 */                  NULL,
+    /* 291 */                  NULL,
+    /* 292 */                  NULL,
+    /* 293 */                  NULL,
+    /* 294 */                  NULL,
+    /* 295 */                  NULL,
+    /* 296 */                  NULL,
+    /* 297 */                  NULL,
+    /* 298 */                  NULL,
+    /* 299 */                  NULL,
+    /* 300 */                  NULL,
+    /* 301 RPL_AWAY */         ":%s 301 %s %s :%s",
+    /* 302 RPL_USERHOST */     ":%s 302 %s :",
+    /* 303 RPL_ISON */         ":%s 303 %s :",
+    /* 304 */                  NULL,
+    /* 305 RPL_UNAWAY */       ":%s 305 %s :You are no longer marked as "
+                                "being away",
+    /* 306 RPL_NOWAWAY */      ":%s 306 %s :You have been marked as being "
+                                "away",
+    /* 307 RPL_WHOISREGNICK */ ":%s 307 %s %s :has identified for this nick",
+    /* 308 RPL_WHOISADMIN */   ":%s 308 %s %s :is an IRC Server "
+                                "Administrator",
+    /* 309 RPL_WHOISSADMIN */  ":%s 309 %s %s :is a Services Administrator",
+    /* 310 RPL_WHOISSVCMSG */  ":%s 310 %s %s",
+    /* 311 RPL_WHOISUSER */    ":%s 311 %s %s %s %s * :%s",
+    /* 312 RPL_WHOISSERVER */  ":%s 312 %s %s %s :%s",
+    /* 313 RPL_WHOISOPERATOR */        ":%s 313 %s %s :is %s",
+    /* 314 RPL_WHOWASUSER */   ":%s 314 %s %s %s %s * :%s",
+    /* 315 RPL_ENDOFWHO */     ":%s 315 %s %s :End of /%s list.",
+    /* 316 RPL_WHOISCHANOP */  NULL,
+    /* 317 RPL_WHOISIDLE */    ":%s 317 %s %s %ld %ld :seconds idle, "
+                                "signon time",
+    /* 318 RPL_ENDOFWHOIS */   ":%s 318 %s %s :End of /WHOIS list.",
+    /* 319 RPL_WHOISCHANNELS */        ":%s 319 %s %s :%s",
+    /* 320 */                  NULL,
+    /* 321 RPL_LISTSTART */    ":%s 321 %s Channel :Users Name",
+    /* 322 RPL_LIST */         ":%s 322 %s %s %d :%s",
+    /* 323 RPL_LISTEND */      ":%s 323 %s :End of /LIST",
+    /* 324 RPL_CHANNELMODEIS */        ":%s 324 %s %s %s %s",
+    /* 325 */                  NULL,
+    /* 326 */                  NULL,
+    /* 327 */                  NULL,
+    /* 328 */                  NULL,
+    /* 329 RPL_CREATIONTIME */ ":%s 329 %s %s %lu",
+    /* 330 */                  NULL,
+    /* 331 RPL_NOTOPIC */      ":%s 331 %s %s :No topic is set.",
+    /* 332 RPL_TOPIC */                ":%s 332 %s %s :%s",
+    /* 333 RPL_TOPICWHOTIME */ ":%s 333 %s %s %s %lu",
+    /* 334 RPL_COMMANDSYNTAX */        ":%s 334 %s :%s",
+    /* 335 */                  NULL,
+    /* 336 */                  NULL,
+    /* 337 RPL_WHOISTEXT*/     ":%s 337 %s :%s",
+    /* 338 RPL_WHOISACTUALLY */        ":%s 338 %s :%s is actually %s@%s [%s]",
+    /* 339 */                  NULL,
+    /* 340 */                  NULL,
+    /* 341 RPL_INVITING */     ":%s 341 %s %s %s",
+    /* 342 RPL_SUMMONING */     ":%s 342 %s %s :User summoned to irc",
+    /* 343 */                  NULL,
+    /* 344 */                  NULL,
+    /* 345 */                  NULL,
+#ifdef INVITE_LISTS
+    /* 346 RPL_INVITELIST */   ":%s 346 %s %s %s %s %lu",
+    /* 347 RPL_ENDOFINVITELIST */ ":%s 347 %s %s :End of Channel Invite List",
+#else
+    /* 346 */                   NULL,
+    /* 347 */                   NULL,
+#endif
+#ifdef EXEMPT_LISTS
+    /* 348 RPL_EXEMPTLIST */   ":%s 348 %s %s %s %s %lu",
+    /* 349 RPL_ENDOFEXEMPTLIST */ ":%s 349 %s %s :End of Channel Exempt List",
+#else
+    /* 348 */                   NULL,
+    /* 349 */                   NULL,
+#endif
+    /* 350 */                  NULL,
+    /* 351 RPL_VERSION */      ":%s 351 %s %s.%s %s :%s",
+    /* 352 RPL_WHOREPLY */     ":%s 352 %s %s %s %s %s %s %s :%d %s",
+    /* 353 RPL_NAMREPLY */     ":%s 353 %s %s",
+    /* 354 RPL_RWHOREPLY */ ":%s 354 %s",   /* also used for Undernet's WHOX */
+    /* 355 */                  NULL,
+    /* 356 */                  NULL,
+    /* 357 */                  NULL,
+    /* 358 */                  NULL,
+    /* 359 */                  NULL,
+    /* 360 */                  NULL,
+    /* 361 */                  NULL,
+    /* 362 RPL_CLOSING */      ":%s 362 %s %s :Closed. Status = %d",
+    /* 363 RPL_CLOSEEND */     ":%s 363 %s %d: Connections Closed",
+    /* 364 RPL_LINKS */                ":%s 364 %s %s %s :%d %s",
+    /* 365 RPL_ENDOFLINKS */   ":%s 365 %s %s :End of /LINKS list.",
+    /* 366 RPL_ENDOFNAMES */   ":%s 366 %s %s :End of /NAMES list.",
+    /* 367 RPL_BANLIST */      ":%s 367 %s %s %s %s %lu",
+    /* 368 RPL_ENDOFBANLIST */ ":%s 368 %s %s :End of Channel Ban List",
+    /* 369 RPL_ENDOFWHOWAS */  ":%s 369 %s %s :End of WHOWAS",
+    /* 370 */                  NULL,
+    /* 371 RPL_INFO */         ":%s 371 %s :%s",
+    /* 372 RPL_MOTD */         ":%s 372 %s :- %s",
+    /* 373 RPL_INFOSTART */    ":%s 373 %s :Server INFO",
+    /* 374 RPL_ENDOFINFO */    ":%s 374 %s :End of /INFO list.",
+    /* 375 RPL_MOTDSTART */    ":%s 375 %s :- %s Message of the Day - ",
+    /* 376 RPL_ENDOFMOTD */    ":%s 376 %s :End of /MOTD command.",
+    /* 377 */                  NULL,
+    /* 378 */                  NULL,
+    /* 379 */                  NULL,
+    /* 380 */                  NULL,
+    /* 381 RPL_YOUREOPER */    ":%s 381 %s :You are now an IRC Operator",
+    /* 382 RPL_REHASHING */    ":%s 382 %s %s :Rehashing",
+    /* 383 */                  NULL,
+    /* 384 RPL_MYPORTIS */     ":%s 384 %s %d :Port to local server is\r\n",
+    /* 385 */                  NULL,
+    /* 386 */                  NULL,
+    /* 387 */                  NULL,
+    /* 388 */                  NULL,
+    /* 389 */                  NULL,
+    /* 390 */                  NULL,
+    /* 391 RPL_TIME */         ":%s 391 %s %s :%s",
+    /* 392 */                  NULL,
+    /* 393 */                  NULL,
+    /* 394 */                  NULL,
+    /* 395 */                  NULL,
+    /* 396 */                  NULL,
+    /* 397 */                  NULL,
+    /* 398 */                  NULL,
+    /* 399 */                  NULL,
+    /* 400 */                  NULL,
+    /* 401 ERR_NOSUCHNICK */   ":%s 401 %s %s :No such nick/channel",
+    /* 402 ERR_NOSUCHSERVER */ ":%s 402 %s %s :No such server",
+    /* 403 ERR_NOSUCHCHANEL */ ":%s 403 %s %s :No such channel",
+    /* 404 ERR_CANNOTSENDTOCHAN */     ":%s 404 %s %s :Cannot send to "
+                                        "channel",
+    /* 405 ERR_TOOMANYCHANNELS */      ":%s 405 %s %s :You have joined too "
+                                        "many channels",
+    /* 406 ERR_WASNOSUCHNICK */        ":%s 406 %s %s :There was no such nickname",
+    /* 407 ERR_TOOMANYTARGETS */       ":%s 407 %s %s :Duplicate recipients. "
+                                        "No message delivered",
+    /* 408 ERR_NOCOLORSONCHAN */       ":%s 408 %s %s :You cannot use colors "
+                                        "on this channel. Not sent: %s",
+    /* 409 ERR_NOORIGIN */             ":%s 409 %s :No origin specified",
+    /* 410 */                  NULL,
+    /* 411 ERR_NORECIPIENT */  ":%s 411 %s :No recipient given (%s)",
+    /* 412 ERR_NOTEXTOSEND */  ":%s 412 %s :No text to send",
+    /* 413 ERR_NOTOPLEVEL */   ":%s 413 %s %s :No toplevel domain specified",
+    /* 414 ERR_WILDTOPLEVEL */ ":%s 414 %s %s :Wildcard in toplevel Domain",
+    /* 415 */                  NULL,
+    /* 416 */                  NULL,   /* In use by Undernet */
+    /* 417 */                  NULL,
+    /* 418 */                  NULL,
+    /* 419 */                  NULL,
+    /* 420 */                  NULL,
+    /* 421 ERR_UNKNOWNCOMMAND */       ":%s 421 %s %s :Unknown command",
+    /* 422 ERR_NOMOTD */       ":%s 422 %s :MOTD File is missing",
+    /* 423 ERR_NOADMININFO */  ":%s 423 %s %s :No administrative info "
+                                "available",
+    /* 424 ERR_FILEERROR */    ":%s 424 %s :File error doing %s on %s",
+    /* 425 */                  NULL,
+    /* 426 */                  NULL,
+    /* 427 */                  NULL,
+    /* 428 */                  NULL,
+    /* 429 ERR_TOOMANYAWAY */  ":%s 429 %s :Too Many aways - Flood "
+                                "Protection activated",
+    /* 430 */                  NULL,
+    /* 431 ERR_NONICKNAMEGIVEN */      ":%s 431 %s :No nickname given",
+    /* 432 ERR_ERRONEOUSNICKNAME */     ":%s 432 %s %s :%s",
+    /* 433 ERR_NICKNAMEINUSE */        ":%s 433 %s %s :Nickname is already in use.",
+    /* 434 */                  NULL,
+    /* 435 ERR_BANONCHAN */     ":%s 435 %s %s %s :Cannot change to a banned "
+                                "nickname",
+    /* 436 ERR_NICKCOLLISION */        ":%s 436 %s %s :Nickname collision KILL",
+    /* 437 ERR_BANNICKCHANGE */        ":%s 437 %s %s :Cannot change nickname while "
+                                "banned or moderated on channel",
+    /* 438 */                  NULL,   /* In use by Undernet */
+    /* 439 ERR_TARGETTOOFAST */        ":%s 439 %s %s :Message target change too fast. "
+                                "Please wait %d seconds and then try again.",
+    /* 440 ERR_SERVICESDOWN */ ":%s 440 %s %s :Services is currently down. "
+                                "Please wait a few moments, and then try "
+                                "again.",
+    /* 441 ERR_USERNOTINCHANNEL */     ":%s 441 %s %s %s :They aren't on "
+                                        "that channel",
+    /* 442 ERR_NOTONCHANNEL */ ":%s 442 %s %s :You're not on that channel",
+    /* 443 ERR_USERONCHANNEL */        ":%s 443 %s %s %s :is already on channel",
+    /* 444 ERR_NOLOGIN */      ":%s 444 %s %s :User not logged in",
+    /* 445 ERR_SUMMONDISABLED */       ":%s 445 %s :SUMMON has been removed",
+    /* 446 ERR_USERSDISABLED */        ":%s 446 %s :USERS has been removed",
+    /* 447 */                  NULL,
+    /* 448 */                  NULL,
+    /* 449 */                  NULL,
+    /* 450 */                  NULL,
+    /* 451 ERR_NOTREGISTERED */        ":%s 451 %s :You have not registered",
+    /* 452 */                  NULL,
+    /* 453 */                  NULL,
+    /* 454 */                  NULL,
+    /* 455 */                  NULL,
+    /* 456 */                  NULL,
+    /* 457 */                  NULL,
+    /* 458 */                  NULL,
+    /* 459 */                  NULL,
+    /* 460 */                  NULL,
+    /* 461 ERR_NEEDMOREPARAMS */       ":%s 461 %s %s :Not enough parameters",
+    /* 462 ERR_ALREADYREGISTERED */    ":%s 462 %s :You may not reregister",
+    /* 463 ERR_NOPERMFORHOST */        ":%s 463 %s :Your host isn't among the "
+                                "privileged",
+    /* 464 ERR_PASSWDMISMATCH */       ":%s 464 %s :Password Incorrect",
+    /* 465 ERR_YOUREBANNEDCREEP */     ":%s 465 %s :You have been %s.",
+    /* 466 */                  NULL,
+    /* 467 ERR_KEYSET */       ":%s 467 %s %s :Channel key already set",
+    /* 468 ERR_ONLYSERVERSCANCHANGE */ ":%s 468 %s %s :Only servers can "
+                                        "change that mode",
+    /* 469 */                  NULL,
+    /* 470 */                  NULL,
+    /* 471 ERR_CHANNELISFULL */        ":%s 471 %s %s :Cannot join channel (%s)",
+    /* 472 ERR_UNKNOWNMODE */  ":%s 472 %s %c :is unknown mode char to me",
+    /* 473 ERR_INVITEONLYCHAN */        ":%s 473 %s %s :Cannot join channel "
+                                        "(%s)",
+    /* 474 ERR_BANNEDFROMCHAN */       ":%s 474 %s %s :Cannot join channel "
+                                        "(+b)",
+    /* 475 ERR_BADCHANNELKEY */        ":%s 475 %s %s :Cannot join channel (+k)",
+    /* 476 ERR_BADCHANMASK */  ":%s 476 %s %s :Bad Channel Mask",
+    /* 477 ERR_NEEDREGGEDNICK */       ":%s 477 %s %s :You need to identify "
+                                       "to a registered nick to %s that "
+                                       "channel. For help with registering "
+                                       "your nickname, type \"/msg %s help "
+                                       "register\" or see %s",
+    /* 478 ERR_BANLISTFULL */  ":%s 478 %s %s %s :Channel %s list is full",
+    /* 479 ERR_BADCHANNAME */   ":%s 479 %s %s :Channel name contains illegal "
+                                "characters",
+    /* 480 */                  NULL,
+    /* 481 ERR_NOPRIVILEGES */ ":%s 481 %s :Permission Denied, You do not "
+                                "have the correct irc operator privileges",
+    /* 482 ERR_CHANOPRIVSNEEDED */     ":%s 482 %s %s :You're not channel "
+                                        "operator",
+    /* 483 ERR_CANTKILLSERVER */       ":%s 483 %s :You cant kill a server!",
+    /* 484 */                  NULL,   /* In use by Undernet */
+    /* 485 ERR_CHANBANREASON */        ":%s 485 %s %s :Cannot join channel (%s)",
+    /* 486 ERR_NONONREG */     ":%s 486 %s :You must identify to a "
+                                "registered nick to private message %s",
+    /* 487 ERR_MSGSERVICES */  ":%s 487 %s :Error! \"/msg %s\" is no longer supported. "
+                                "Use \"/msg %s@%s\" or \"/%s\" instead.",
+    /* 488 */                  NULL,
+    /* 489 */                  NULL,   /* In use by Undernet */
+    /* 490 */                  NULL,
+    /* 491 ERR_NOOPERHOST */   ":%s 491 %s :No Oper block for your host",
+    /* 492 */                  NULL,
+    /* 493 */                  NULL,
+    /* 494 */                  NULL,
+    /* 495 */                  NULL,
+    /* 496 */                  NULL,
+    /* 497 */                  NULL,
+    /* 498 */                  NULL,
+    /* 499 */                  NULL,
+    /* 500 */                  NULL,
+    /* 501 ERR_UMODEUNKNOWNFLAG */     ":%s 501 %s :Unknown MODE flag",
+    /* 502 ERR_USERSDONTMATCH */       ":%s 502 %s :Can't change mode for "
+                                        "other users",
+    /* 503 */                  ":%s 503 %s :Message could not be delivered "
+                                "to %s",
+    /* 504 */                  NULL,
+    /* 505 */                  NULL,
+    /* 506 */                  NULL,
+    /* 507 */                  NULL,
+    /* 508 */                  NULL,
+    /* 509 */                  NULL,
+    /* 510 */                  NULL,
+    /* 511 ERR_SILELISTFULL */ ":%s 511 %s %s :Your silence list is full",
+    /* 512 ERR_TOOMANYWATCH */ ":%s 512 %s %s :Maximum size for WATCH-list "
+                                "is 128 entries",
+    /* 513 */                  NULL,   /* In use by Undernet */
+    /* 514 ERR_TOOMANYDCC */   ":%s 514 %s %s :Your dcc allow list is full. "
+                                "Maximum size is %d entries",
+    /* 515 */                  NULL,
+    /* 516 */                  NULL,
+    /* 517 */                  NULL,
+    /* 518 */                  NULL,
+    /* 519 */                  NULL,
+    /* 520 */                  NULL,
+    /* 521 ERR_LISTSYNTAX */   ":%s 521 %s :Bad list syntax, type /quote "
+                                "list ? or /raw list ?",
+    /* 522 ERR_WHOSYNTAX */    ":%s 522 %s :/%s Syntax incorrect, use "
+                                "/%s ? for help",
+    /* 523 ERR_WHOLIMEXCEED */ ":%s 523 %s %d :%s search limit exceeded.",
+    /* 524 */                  NULL,
+    /* 525 */                  NULL,
+    /* 526 */                  NULL,
+    /* 527 */                  NULL,
+    /* 528 */                  NULL,
+    /* 529 */                  NULL,
+    /* 530 */                  NULL,
+    /* 531 */                  NULL,
+    /* 532 */                  NULL,
+    /* 533 */                  NULL,
+    /* 534 */                  NULL,
+    /* 535 */                  NULL,
+    /* 536 */                  NULL,
+    /* 537 */                  NULL,
+    /* 538 */                  NULL,
+    /* 539 */                  NULL,
+    /* 540 */                  NULL,
+    /* 541 */                  NULL,
+    /* 542 */                  NULL,
+    /* 543 */                  NULL,
+    /* 544 */                  NULL,
+    /* 545 */                  NULL,
+    /* 546 */                  NULL,
+    /* 547 */                  NULL,
+    /* 548 */                  NULL,
+    /* 549 */                  NULL,
+    /* 550 */                  NULL,
+    /* 551 */                  NULL,
+    /* 552 */                  NULL,
+    /* 553 */                  NULL,
+    /* 554 */                  NULL,
+    /* 555 */                  NULL,
+    /* 556 */                  NULL,
+    /* 557 */                  NULL,
+    /* 558 */                  NULL,
+    /* 559 */                  NULL,
+    /* 560 */                  NULL,
+    /* 561 */                  NULL,
+    /* 562 */                  NULL,
+    /* 563 */                  NULL,
+    /* 564 */                  NULL,
+    /* 565 */                  NULL,
+    /* 566 */                  NULL,
+    /* 567 */                  NULL,
+    /* 568 */                  NULL,
+    /* 569 */                  NULL,
+    /* 570 */                  NULL,
+    /* 571 */                  NULL,
+    /* 572 */                  NULL,
+    /* 573 */                  NULL,
+    /* 574 */                  NULL,
+    /* 575 */                  NULL,
+    /* 576 */                  NULL,
+    /* 577 */                  NULL,
+    /* 578 */                  NULL,
+    /* 579 */                  NULL,
+    /* 580 */                  NULL,
+    /* 581 */                  NULL,
+    /* 582 */                  NULL,
+    /* 583 */                  NULL,
+    /* 584 */                  NULL,
+    /* 585 */                  NULL,
+    /* 586 */                  NULL,
+    /* 587 */                  NULL,
+    /* 588 */                  NULL,
+    /* 589 */                  NULL,
+    /* 590 */                  NULL,
+    /* 591 */                  NULL,
+    /* 592 */                  NULL,
+    /* 593 */                  NULL,
+    /* 594 */                  NULL,
+    /* 595 */                  NULL,
+    /* 596 */                  NULL,
+    /* 597 */                  NULL,
+    /* 598 */                  NULL,
+    /* 599 */                  NULL,
+    /* 600 RPL_LOGON */                ":%s 600 %s %s %s %s %d :logged online",
+    /* 601 RPL_LOGOFF */       ":%s 601 %s %s %s %s %d :logged offline",
+    /* 602 RPL_WATCHOFF */     ":%s 602 %s %s %s %s %d :stopped watching",
+    /* 603 RPL_WATCHSTAT */     ":%s 603 %s :You have %d and are on %d WATCH "
+                                "entries",
+    /* 604 RPL_NOWON */                ":%s 604 %s %s %s %s %d :is online",
+    /* 605 RPL_NOWOFF */       ":%s 605 %s %s %s %s %d :is offline",
+    /* 606 RPL_WATCHLIST */    ":%s 606 %s :%s",
+    /* 607 RPL_ENDOFWATCHLIST */       ":%s 607 %s :End of WATCH %c",
+    /* 608 */                  NULL, /* Do not use */ 
+    /* 609 */                  NULL,
+    /* 610 */                  NULL,
+    /* 611 */                  NULL,
+    /* 612 */                  NULL,
+    /* 613 */                  NULL,
+    /* 614 */                  NULL,
+    /* 615 */                  NULL,
+    /* 616 */                  NULL,
+    /* 617 RPL_DCCSTATUS */    ":%s 617 %s :%s has been %s your DCC allow "
+                                "list",
+    /* 618 RPL_DCCLIST */      ":%s 618 %s :%s",
+    /* 619 RPL_ENDOFDCCLIST */ ":%s 619 %s :End of DCCALLOW %s",
+    /* 620 RPL_DCCINFO */      ":%s 620 %s :%s",
+    /* 621 */                  NULL,
+    /* 622 */                  NULL,
+    /* 623 */                  NULL,
+    /* 624 */                  NULL,
+    /* 625 */                  NULL,
+    /* 626 */                  NULL,
+    /* 627 */                  NULL,
+    /* 628 */                  NULL,
+    /* 629 */                  NULL,
+    /* 630 */                  NULL,
+    /* 631 */                  NULL,
+    /* 632 */                  NULL,
+    /* 633 */                  NULL,
+    /* 634 */                  NULL,
+    /* 635 */                  NULL,
+    /* 636 */                  NULL,
+    /* 637 */                  NULL,
+    /* 638 */                  NULL,
+    /* 639 */                  NULL,
+    /* 640 */                  NULL,
+    /* 641 */                  NULL,
+    /* 642 */                  NULL,
+    /* 643 */                  NULL,
+    /* 644 */                  NULL,
+    /* 645 */                  NULL,
+    /* 646 */                  NULL,
+    /* 647 */                  NULL,
+    /* 648 */                  NULL,
+    /* 649 */                  NULL,
+    /* 650 */                  NULL,
+    /* 651 */                  NULL,
+    /* 652 */                  NULL,
+    /* 653 */                  NULL,
+    /* 654 */                  NULL,
+    /* 655 */                  NULL,
+    /* 656 */                  NULL,
+    /* 657 */                  NULL,
+    /* 658 */                  NULL,
+    /* 659 */                  NULL,
+    /* 660 */                  NULL,
+    /* 661 */                  NULL,
+    /* 662 */                  NULL,
+    /* 663 */                  NULL,
+    /* 664 */                  NULL,
+    /* 665 */                  NULL,
+    /* 666 */                  NULL,
+    /* 667 */                  NULL,
+    /* 668 */                  NULL,
+    /* 669 */                  NULL,
+    /* 670 */                  NULL,
+    /* 671 */                  NULL,
+    /* 672 */                  NULL,
+    /* 673 */                  NULL,
+    /* 674 */                  NULL,
+    /* 675 */                  NULL,
+    /* 676 */                  NULL,
+    /* 677 */                  NULL,
+    /* 678 */                  NULL,
+    /* 679 */                  NULL,
+    /* 680 */                  NULL,
+    /* 681 */                  NULL,
+    /* 682 */                  NULL,
+    /* 683 */                  NULL,
+    /* 684 */                  NULL,
+    /* 685 */                  NULL,
+    /* 686 */                  NULL,
+    /* 687 */                  NULL,
+    /* 688 */                  NULL,
+    /* 689 */                  NULL,
+    /* 690 */                  NULL,
+    /* 691 */                  NULL,
+    /* 692 */                  NULL,
+    /* 693 */                  NULL,
+    /* 694 */                  NULL,
+    /* 695 */                  NULL,
+    /* 696 */                  NULL,
+    /* 697 */                  NULL,
+    /* 698 */                  NULL,
+    /* 699 */                  NULL,
+    /* 700 */                  NULL,
+    /* 701 */                  NULL,
+    /* 702 */                  NULL,
+    /* 703 */                  NULL,
+    /* 704 */                  NULL,
+    /* 705 */                  NULL,
+    /* 706 */                  NULL,
+    /* 707 */                  NULL,
+    /* 708 */                  NULL,
+    /* 709 */                  NULL,
+    /* 710 */                  NULL,
+    /* 711 */                  NULL,
+    /* 712 */                  NULL,
+    /* 713 */                  NULL,
+    /* 714 */                  NULL,
+    /* 715 */                  NULL,
+    /* 716 */                  NULL,
+    /* 717 */                  NULL,
+    /* 718 */                  NULL,
+    /* 719 */                  NULL,
+    /* 720 */                  NULL,
+    /* 721 */                  NULL,
+    /* 722 */                  NULL,
+    /* 723 */                  NULL,
+    /* 724 */                  NULL,
+    /* 725 */                  NULL,
+    /* 726 */                  NULL,
+    /* 727 */                  NULL,
+    /* 728 */                  NULL,
+    /* 729 */                  NULL,
+    /* 730 */                  NULL,
+    /* 731 */                  NULL,
+    /* 732 */                  NULL,
+    /* 733 */                  NULL,
+    /* 734 */                  NULL,
+    /* 735 */                  NULL,
+    /* 736 */                  NULL,
+    /* 737 */                  NULL,
+    /* 738 */                  NULL,
+    /* 739 */                  NULL,
+    /* 740 */                  NULL,
+    /* 741 */                  NULL,
+    /* 742 */                  NULL,
+    /* 743 */                  NULL,
+    /* 744 */                  NULL,
+    /* 745 */                  NULL,
+    /* 746 */                  NULL,
+    /* 747 */                  NULL,
+    /* 748 */                  NULL,
+    /* 749 */                  NULL,
+    /* 750 */                  NULL,
+    /* 751 */                  NULL,
+    /* 752 */                  NULL,
+    /* 753 */                  NULL,
+    /* 754 */                  NULL,
+    /* 755 */                  NULL,
+    /* 756 */                  NULL,
+    /* 757 */                  NULL,
+    /* 758 */                  NULL,
+    /* 759 */                  NULL,
+    /* 760 */                  NULL,
+    /* 761 */                  NULL,
+    /* 762 */                  NULL,
+    /* 763 */                  NULL,
+    /* 764 */                  NULL,
+    /* 765 */                  NULL,
+    /* 766 */                  NULL,
+    /* 767 */                  NULL,
+    /* 768 */                  NULL,
+    /* 769 */                  NULL,
+    /* 770 */                  NULL,
+    /* 771 */                  NULL,
+    /* 772 */                  NULL,
+    /* 773 */                  NULL,
+    /* 774 */                  NULL,
+    /* 775 */                  NULL,
+    /* 776 */                  NULL,
+    /* 777 */                  NULL,
+    /* 778 */                  NULL,
+    /* 779 */                  NULL,
+    /* 780 */                  NULL,
+    /* 781 */                  NULL,
+    /* 782 */                  NULL,
+    /* 783 */                  NULL,
+    /* 784 */                  NULL,
+    /* 785 */                  NULL,
+    /* 786 */                  NULL,
+    /* 787 */                  NULL,
+    /* 788 */                  NULL,
+    /* 789 */                  NULL,
+    /* 790 */                  NULL,
+    /* 791 */                  NULL,
+    /* 792 */                  NULL,
+    /* 793 */                  NULL,
+    /* 794 */                  NULL,
+    /* 795 */                  NULL,
+    /* 796 */                  NULL,
+    /* 797 */                  NULL,
+    /* 798 */                  NULL,
+    /* 799 */                  NULL,
+    /* 800 */                  NULL,
+    /* 801 */                  NULL,
+    /* 802 */                  NULL,
+    /* 803 */                  NULL,
+    /* 804 */                  NULL,
+    /* 805 */                  NULL,
+    /* 806 */                  NULL,
+    /* 807 */                  NULL,
+    /* 808 */                  NULL,
+    /* 809 */                  NULL,
+    /* 810 */                  NULL,
+    /* 811 */                  NULL,
+    /* 812 */                  NULL,
+    /* 813 */                  NULL,
+    /* 814 */                  NULL,
+    /* 815 */                  NULL,
+    /* 816 */                  NULL,
+    /* 817 */                  NULL,
+    /* 818 */                  NULL,
+    /* 819 */                  NULL,
+    /* 820 */                  NULL,
+    /* 821 */                  NULL,
+    /* 822 */                  NULL,
+    /* 823 */                  NULL,
+    /* 824 */                  NULL,
+    /* 825 */                  NULL,
+    /* 826 */                  NULL,
+    /* 827 */                  NULL,
+    /* 828 */                  NULL,
+    /* 829 */                  NULL,
+    /* 830 */                  NULL,
+    /* 831 */                  NULL,
+    /* 832 */                  NULL,
+    /* 833 */                  NULL,
+    /* 834 */                  NULL,
+    /* 835 */                  NULL,
+    /* 836 */                  NULL,
+    /* 837 */                  NULL,
+    /* 838 */                  NULL,
+    /* 839 */                  NULL,
+    /* 840 */                  NULL,
+    /* 841 */                  NULL,
+    /* 842 */                  NULL,
+    /* 843 */                  NULL,
+    /* 844 */                  NULL,
+    /* 845 */                  NULL,
+    /* 846 */                  NULL,
+    /* 847 */                  NULL,
+    /* 848 */                  NULL,
+    /* 849 */                  NULL,
+    /* 850 */                  NULL,
+    /* 851 */                  NULL,
+    /* 852 */                  NULL,
+    /* 853 */                  NULL,
+    /* 854 */                  NULL,
+    /* 855 */                  NULL,
+    /* 856 */                  NULL,
+    /* 857 */                  NULL,
+    /* 858 */                  NULL,
+    /* 859 */                  NULL,
+    /* 860 */                  NULL,
+    /* 861 */                  NULL,
+    /* 862 */                  NULL,
+    /* 863 */                  NULL,
+    /* 864 */                  NULL,
+    /* 865 */                  NULL,
+    /* 866 */                  NULL,
+    /* 867 */                  NULL,
+    /* 868 */                  NULL,
+    /* 869 */                  NULL,
+    /* 870 */                  NULL,
+    /* 871 */                  NULL,
+    /* 872 */                  NULL,
+    /* 873 */                  NULL,
+    /* 874 */                  NULL,
+    /* 875 */                  NULL,
+    /* 876 */                  NULL,
+    /* 877 */                  NULL,
+    /* 878 */                  NULL,
+    /* 879 */                  NULL,
+    /* 880 */                  NULL,
+    /* 881 */                  NULL,
+    /* 882 */                  NULL,
+    /* 883 */                  NULL,
+    /* 884 */                  NULL,
+    /* 885 */                  NULL,
+    /* 886 */                  NULL,
+    /* 887 */                  NULL,
+    /* 888 */                  NULL,
+    /* 889 */                  NULL,
+    /* 890 */                  NULL,
+    /* 891 */                  NULL,
+    /* 892 */                  NULL,
+    /* 893 */                  NULL,
+    /* 894 */                  NULL,
+    /* 895 */                  NULL,
+    /* 896 */                  NULL,
+    /* 897 */                  NULL,
+    /* 898 */                  NULL,
+    /* 899 */                  NULL,
+    /* 900 */                  NULL,
+    /* 901 */                  NULL,
+    /* 902 */                  NULL,
+    /* 903 */                  NULL,
+    /* 904 */                  NULL,
+    /* 905 */                  NULL,
+    /* 906 */                  NULL,
+    /* 907 */                  NULL,
+    /* 908 */                  NULL,
+    /* 909 */                  NULL,
+    /* 910 */                  NULL,
+    /* 911 */                  NULL,
+    /* 912 */                  NULL,
+    /* 913 */                  NULL,
+    /* 914 */                  NULL,
+    /* 915 */                  NULL,
+    /* 916 */                  NULL,
+    /* 917 */                  NULL,
+    /* 918 */                  NULL,
+    /* 919 */                  NULL,
+    /* 920 */                  NULL,
+    /* 921 */                  NULL,
+    /* 922 */                  NULL,
+    /* 923 */                  NULL,
+    /* 924 */                  NULL,
+    /* 925 */                  NULL,
+    /* 926 */                  NULL,
+    /* 927 */                  NULL,
+    /* 928 */                  NULL,
+    /* 929 */                  NULL,
+    /* 930 */                  NULL,
+    /* 931 */                  NULL,
+    /* 932 */                  NULL,
+    /* 933 */                  NULL,
+    /* 934 */                  NULL,
+    /* 935 */                  NULL,
+    /* 936 */                  NULL,
+    /* 937 */                  NULL,
+    /* 938 */                  NULL,
+    /* 939 */                  NULL,
+    /* 940 */                  NULL,
+    /* 941 */                  NULL,
+    /* 942 */                  NULL,
+    /* 943 */                  NULL,
+    /* 944 */                  NULL,
+    /* 945 */                  NULL,
+    /* 946 */                  NULL,
+    /* 947 */                  NULL,
+    /* 948 */                  NULL,
+    /* 949 */                  NULL,
+    /* 950 */                  NULL,
+    /* 951 */                  NULL,
+    /* 952 */                  NULL,
+    /* 953 */                  NULL,
+    /* 954 */                  NULL,
+    /* 955 */                  NULL,
+    /* 956 */                  NULL,
+    /* 957 */                  NULL,
+    /* 958 */                  NULL,
+    /* 959 */                  NULL,
+    /* 960 */                  NULL,
+    /* 961 */                  NULL,
+    /* 962 */                  NULL,
+    /* 963 */                  NULL,
+    /* 964 */                  NULL,
+    /* 965 */                  NULL,
+    /* 966 */                  NULL,
+    /* 967 */                  NULL,
+    /* 968 */                  NULL,
+    /* 969 */                  NULL,
+    /* 970 */                  NULL,
+    /* 971 */                  NULL,
+    /* 972 */                  NULL,
+    /* 973 */                  NULL,
+    /* 974 */                  NULL,
+    /* 975 */                  NULL,
+    /* 976 */                  NULL,
+    /* 977 */                  NULL,
+    /* 978 */                  NULL,
+    /* 979 */                  NULL,
+    /* 980 */                  NULL,
+    /* 981 */                  NULL,
+    /* 982 */                  NULL,
+    /* 983 */                  NULL,
+    /* 984 */                  NULL,
+    /* 985 */                  NULL,
+    /* 986 */                  NULL,
+    /* 987 */                  NULL,
+    /* 988 */                  NULL,
+    /* 989 */                  NULL,
+    /* 990 */                  NULL,
+    /* 991 */                  NULL,
+    /* 992 */                  NULL,
+    /* 993 */                  NULL,
+    /* 994 */                  NULL,
+    /* 995 */                  NULL,
+    /* 996 */                  NULL,
+    /* 997 */                  NULL,
+    /* 998 */                  NULL,
+    /* 999 */                  ":%s 999 %s Numeric error! yikes!",
+    /* 1000 */                 NULL
+};
+
+char *getreply(int numeric)
+{
+    if((numeric<0 || numeric>999) || !replies[numeric])
+       return(replies[ERR_NUMERIC_ERR]);
+    else
+       return(replies[numeric]);
+}
+
+char *err_str(int numeric)
+{
+    return getreply(numeric); 
+}
+char *rpl_str(int numeric) 
+{
+    return getreply(numeric);
+}
+
diff --git a/src/s_misc.c b/src/s_misc.c
new file mode 100644 (file)
index 0000000..6ddfe4c
--- /dev/null
@@ -0,0 +1,850 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/s_misc.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: s_misc.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include <sys/time.h>
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "zlink.h"
+#include "hooks.h"
+#include "clones.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#if !defined(ULTRIX) && !defined(SGI) && !defined(sequent) && \
+    !defined(__convex__)
+#include <sys/param.h>
+#endif
+#if defined(AIX) || defined(SVR3) || \
+   ((__GNU_LIBRARY__ == 6) && (__GLIBC__ >=2) && (__GLIBC_MINOR__ >= 2))
+#include <time.h>
+#endif
+#include "h.h"
+#include "fdlist.h"
+
+extern float curSendK, curRecvK;
+
+extern int  server_was_split;
+
+#ifdef ALWAYS_SEND_DURING_SPLIT
+int currently_processing_netsplit = NO;
+#endif
+
+static void exit_one_client(aClient *, aClient *, aClient *, char *);
+
+static char *months[] =
+{
+    "January", "February", "March", "April",
+    "May", "June", "July", "August",
+    "September", "October", "November", "December"
+};
+
+static char *weekdays[] =
+{
+    "Sunday", "Monday", "Tuesday", "Wednesday",
+    "Thursday", "Friday", "Saturday"
+};
+
+/* stats stuff */
+struct stats ircst, *ircstp = &ircst;
+
+char *
+date(time_t clock)
+{
+    static char buf[80], plus;
+    struct tm *lt, *gm;
+    struct tm   gmbuf;
+    int         minswest;
+
+    if (!clock)
+        time(&clock);
+    gm = gmtime(&clock);
+    memcpy((char *) &gmbuf, (char *) gm, sizeof(gmbuf));
+    gm = &gmbuf;
+    lt = localtime(&clock);
+
+    if (lt->tm_yday == gm->tm_yday)
+        minswest = (gm->tm_hour - lt->tm_hour) * 60 + (gm->tm_min - lt->tm_min);
+    else if (lt->tm_yday > gm->tm_yday)
+        minswest = (gm->tm_hour - (lt->tm_hour + 24)) * 60;
+    else
+        minswest = ((gm->tm_hour + 24) - lt->tm_hour) * 60;
+
+    plus = (minswest > 0) ? '-' : '+';
+    if (minswest < 0)
+        minswest = -minswest;
+    
+    ircsprintf(buf, "%s %s %d %04d -- %02d:%02d %c%02d:%02d",
+               weekdays[lt->tm_wday], months[lt->tm_mon], lt->tm_mday,
+               lt->tm_year + 1900, lt->tm_hour, lt->tm_min,
+               plus, minswest / 60, minswest % 60);
+
+    return buf;
+}
+
+char *
+smalldate(time_t clock)
+{
+    static char buf[MAX_DATE_STRING];
+    struct tm *lt, *gm;
+    struct tm   gmbuf;
+
+    if (!clock)
+        time(&clock);
+    gm = gmtime(&clock);
+    memcpy((char *) &gmbuf, (char *) gm, sizeof(gmbuf));
+    gm = &gmbuf;
+    lt = localtime(&clock);
+
+    ircsprintf(buf, "%04d/%02d/%02d %02d.%02d", lt->tm_year + 1900, 
+               lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min);
+
+    return buf;
+}
+
+/**
+ ** myctime()
+ **   This is like standard ctime()-function, but it zaps away
+ **   the newline from the end of that string. Also, it takes
+ **   the time value as parameter, instead of pointer to it.
+ **   Note that it is necessary to copy the string to alternate
+ **   buffer (who knows how ctime() implements it, maybe it statically
+ **   has newline there and never 'refreshes' it -- zapping that
+ **   might break things in other places...)
+ **
+ **/
+char *
+myctime(time_t value)
+{
+    static char buf[28];
+    char   *p;
+
+    strcpy(buf, ctime(&value));
+    if ((p = (char *) strchr(buf, '\n')) != NULL)
+        *p = '\0';
+
+    return buf;
+}
+
+/*
+ * * check_registered_user is used to cancel message, if the *
+ * originator is a server or not registered yet. In other * words,
+ * passing this test, *MUST* guarantee that the * sptr->user exists
+ * (not checked after this--let there * be coredumps to catch bugs...
+ * this is intentional --msa ;) *
+ * 
+ * There is this nagging feeling... should this NOT_REGISTERED * error
+ * really be sent to remote users? This happening means * that remote
+ * servers have this user registered, although this * one has it not...
+ * Not really users fault... Perhaps this * error message should be
+ * restricted to local clients and some * other thing generated for
+ * remotes...
+ */
+inline int 
+check_registered_user(aClient *sptr)
+{
+    if (!IsRegisteredUser(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOTREGISTERED), me.name, "*");
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * * check_registered user cancels message, if 'x' is not * registered
+ * (e.g. we don't know yet whether a server * or user)
+ */
+inline int 
+check_registered(aClient *sptr)
+{
+    if (!IsRegistered(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOTREGISTERED), me.name, "*");
+        return -1;
+    }
+    return 0;
+}
+
+inline char *
+get_listener_name(aListener *lptr)
+{
+   static char nbuf[HOSTLEN * 2 + USERLEN + 5];
+
+   ircsprintf(nbuf, "%s[@%s.%d][%s]", me.name, BadPtr(lptr->vhost_string) ?
+              "0.0.0.0" : lptr->vhost_string, lptr->port, 
+               BadPtr(lptr->allow_string) ?  "*" : lptr->allow_string);
+
+   return nbuf;
+}
+
+/*
+ * * get_client_name *      Return the name of the client for various
+ * tracking and *      admin purposes. The main purpose of this
+ * function is to *      return the "socket host" name of the client,
+ * if that *    differs from the advertised name (other than case). *
+ * But, this can be used to any client structure. *
+ * 
+ *      Returns: *        "name[user@ip#.port]" if 'showip' is true; *
+ * "name[sockethost]", if name and sockhost are different and *
+ * showip is false; else *        "name". *
+ * 
+ * NOTE 1: *    Watch out the allocation of "nbuf", if either
+ * sptr->name * or sptr->sockhost gets changed into pointers instead of *
+ * directly allocated within the structure... *
+ * 
+ * NOTE 2: *    Function return either a pointer to the structure
+ * (sptr) or *  to internal buffer (nbuf). *NEVER* use the returned
+ * pointer *    to modify what it points!!!
+ */
+char *
+get_client_name(aClient *sptr, int showip)
+{
+    static char nbuf[HOSTLEN * 2 + USERLEN + 5];
+
+    if (MyConnect(sptr)) 
+    {
+        switch (showip) 
+        {
+            case TRUE:
+#ifdef SHOW_UH
+                ircsprintf(nbuf, "%s[%s%s@%s]", sptr->name,
+                           (!(sptr->flags & FLAGS_GOTID)) ? "" : "(+)",
+                           sptr->user ? sptr->user->username : sptr->username,
+                           inetntoa((char *) &sptr->ip));
+#else
+                ircsprintf(nbuf, "%s[%s@%s]", sptr->name,
+                           (!(sptr->flags & FLAGS_GOTID)) ? "" : sptr->username,
+                           inetntoa((char *) &sptr->ip));
+#endif
+                break;
+            case HIDEME:
+#ifdef SHOW_UH
+                ircsprintf(nbuf, "%s[%s%s@%s]", sptr->name,
+                           (!(sptr->flags & FLAGS_GOTID)) ? "" : "(+)",
+                           sptr->user ? sptr->user->username : sptr->username,
+                           "0.0.0.0");
+#else
+                ircsprintf(nbuf, "%s[%s@%s]", sptr->name,
+                           (!(sptr->flags & FLAGS_GOTID)) ? "" : sptr->username,
+                           "0.0.0.0");
+#endif
+                break;
+            default:
+                if (mycmp(sptr->name, sptr->sockhost))
+#ifdef USERNAMES_IN_TRACE
+                    ircsprintf(nbuf, "%s[%s@%s]", sptr->name,
+                               sptr->user ? sptr->user->username :
+                               sptr->username, sptr->sockhost);
+#else
+                    ircsprintf(nbuf, "%s[%s]", sptr->name, sptr->sockhost);
+#endif
+                else
+                    return sptr->name;
+        }
+        return nbuf;
+    }
+    return sptr->name;
+}
+
+char *
+get_client_host(aClient *cptr)
+{
+    static char nbuf[HOSTLEN * 2 + USERLEN + 5];
+    
+    if (!MyConnect(cptr))
+        return cptr->name;
+    if (!cptr->hostp)
+        return get_client_name(cptr, FALSE);
+    else
+        ircsprintf(nbuf, "%s[%-.*s@%-.*s]", cptr->name, USERLEN,
+                          (!(cptr->flags & FLAGS_GOTID)) ? "" : cptr->username,
+                          HOSTLEN, cptr->hostp->h_name);
+    return nbuf;
+}
+
+/*
+ * Form sockhost such that if the host is of form user@host, only the
+ * host portion is copied.
+ */
+void 
+get_sockhost(aClient *cptr, char *host)
+{
+    char *s;
+
+    if ((s = (char *) strchr(host, '@')))
+        s++;
+    else
+        s = host;
+    strncpyzt(cptr->sockhost, s, sizeof(cptr->sockhost));
+}
+
+/*
+ * Return wildcard name of my server name according to given config
+ * entry --Jto
+ */
+char *
+my_name_for_link(char *name, aConnect *aconn)
+{
+    static char namebuf[HOSTLEN];
+    int count = aconn->port;
+    char *start = name;
+
+    if (count <= 0 || count > 5)
+        return start;
+
+    while (count-- && name) 
+    {
+        name++;
+        name = (char *) strchr(name, '.');
+    }
+    if (!name)
+        return start;
+
+    namebuf[0] = '*';
+    strncpy(&namebuf[1], name, HOSTLEN - 1);
+    namebuf[HOSTLEN - 1] = '\0';
+    return namebuf;
+}
+
+int remove_dcc_references(aClient *sptr)
+{  
+    aClient *acptr;
+    Link *lp, *nextlp;
+    Link **lpp, *tmp;
+    int found;
+            
+    lp = sptr->user->dccallow;
+            
+    while(lp)
+    {  
+        nextlp = lp->next;
+        acptr = lp->value.cptr;
+        for(found = 0, lpp = &(acptr->user->dccallow); 
+            *lpp; lpp=&((*lpp)->next))
+        {  
+            if(lp->flags == (*lpp)->flags)
+                continue; /* match only opposite types for sanity */
+            if((*lpp)->value.cptr == sptr)
+            {
+                if((*lpp)->flags == DCC_LINK_ME)
+                {  
+                    sendto_one(acptr, ":%s %d %s :%s has been removed from "
+                               "your DCC allow list for signing off",
+                               me.name, RPL_DCCINFO, acptr->name, sptr->name);
+                }
+                tmp = *lpp;
+                *lpp = tmp->next;
+                free_link(tmp);
+                found++;
+                break;
+            }
+        }
+         
+        if(!found)
+            sendto_realops_lev(DEBUG_LEV, "rdr(): %s was in dccallowme "
+                               "list[%d] of %s but not in dccallowrem list!",
+                               acptr->name, lp->flags, sptr->name);
+        free_link(lp);
+        lp = nextlp;
+    }
+    return 0;
+}  
+
+/*
+ * NOQUIT
+ * a method of reducing the stress on the network during server splits
+ * by sending only a simple "SQUIT" message for the server that is dropping,
+ * instead of thousands upon thousands of QUIT messages for each user,
+ * plus an SQUIT for each server behind the dead link.
+ *
+ * Original idea by Cabal95, implementation by lucas
+ */
+
+void 
+exit_one_client_in_split(aClient *cptr, aClient *dead, char *reason)
+{
+    Link *lp;
+
+    /* send all the quit reasons to all the non-noquit servers we have */
+    
+    /* yikes. We only want to do this if dead was OUR server. */
+    /* erm, no, that's not true. Doing that breaks things. 
+     * If a non-noquit server is telling us a server has split,
+     * we will have already recieved hundreds of QUIT messages
+     * from it, which will be passed anyway, and this procedure
+     * will never be called. - lucas
+     */
+
+#ifdef NOQUIT
+    sendto_non_noquit_servs_butone(dead, ":%s QUIT :%s", cptr->name, reason);
+#endif
+
+    sendto_common_channels(cptr, ":%s QUIT :%s", cptr->name, reason);
+    
+    while ((lp = cptr->user->channel))
+        remove_user_from_channel(cptr, lp->value.chptr);
+    while ((lp = cptr->user->invited))
+        del_invite(cptr, lp->value.chptr);
+    while ((lp = cptr->user->silence))
+        del_silence(cptr, lp->value.cp);
+
+    if (cptr->ip.s_addr)
+        clones_remove(cptr);
+
+    remove_dcc_references(cptr);
+
+    del_from_client_hash_table(cptr->name, cptr); 
+
+    hash_check_watch(cptr, RPL_LOGOFF);
+
+    remove_client_from_list(cptr);
+}
+
+/* exit_one_server
+ *
+ * recursive function!
+ * therefore, we pass dead and reason to ourselves.
+ * in the beginning, dead == cptr, so it will be the one
+ *  out of the loop last. therefore, dead should remain a good pointer.
+ * cptr: the server being exited
+ * dead: the actual server that split (if this belongs to us, we
+ *       absolutely CANNOT send to it)
+ * from: the client that caused this split
+ * lcptr: the local client that initiated this
+ * spinfo: split reason, as generated in exit_server
+ * comment: comment provided
+ */
+
+void 
+exit_one_server(aClient *cptr, aClient *dead, aClient *from, 
+                aClient *lcptr, char *spinfo, char *comment)
+{
+    aClient *acptr, *next;
+    DLink *lp;
+
+    /* okay, this is annoying.
+     * first off, we need two loops.
+     * one: to remove all the clients.
+     * two: to remove all the servers.
+     * HOWEVER! removing a server may cause removal of more servers 
+     * and more clients.
+     * and this may make our pointer to next bad. therefore, we have to restart
+     *  the server loop each time we find a server.
+     * We _NEED_ two different loops: all clients must be removed "
+     * before the server is
+     *  removed. Otherwise, bad things (tm) can happen.
+     */
+
+    Debug((DEBUG_NOTICE, "server noquit: %s", cptr->name));
+
+    for (acptr = client; acptr; acptr = next) 
+    {
+        next = acptr->next; /* we might destroy this client record 
+                             * in the loop. */
+        
+        if(acptr->uplink != cptr || !IsPerson(acptr)) 
+            continue;
+
+        exit_one_client_in_split(acptr, dead, spinfo);
+    }
+
+    for (acptr = client; acptr; acptr = next) 
+    {
+        next = acptr->next; /* we might destroy this client record in 
+                             * the loop. */
+
+        if(acptr->uplink != cptr || !IsServer(acptr)) 
+            continue;
+
+        exit_one_server(acptr, dead, from, lcptr, spinfo, comment);
+        next = client; /* restart the loop */
+    }
+
+    Debug((DEBUG_NOTICE, "done exiting server: %s", cptr->name));
+
+    for (lp = server_list; lp; lp = lp->next)
+    {
+        acptr = lp->value.cptr;
+
+        if (acptr == cptr || IsMe(acptr) ||
+            acptr == dead || acptr == lcptr)
+            continue;
+
+        /* if the server is noquit, we only want to send it
+         *  information about 'dead'
+         * if it's not, this server gets split information for ALL
+         * dead servers.
+         */
+
+        if(cptr != dead)
+            continue;
+
+        if (cptr->from == acptr) /* "upstream" squit */
+            sendto_one(acptr, ":%s SQUIT %s :%s", from->name, cptr->name,
+                       comment);
+        else 
+            sendto_one(acptr, "SQUIT %s :%s", cptr->name, comment);
+    }
+
+    del_from_client_hash_table(cptr->name, cptr); 
+    hash_check_watch(cptr, RPL_LOGOFF);
+    remove_client_from_list(cptr);
+}
+
+/* exit_server
+ *
+ * lcptr: the local client that initiated this
+ * cptr: the server that is being dropped.
+ * from: the client/server that caused this to happen
+ * comment: reason this is happening
+ * we then call exit_one_server, the recursive function.
+ */
+
+void exit_server(aClient *lcptr, aClient *cptr, aClient *from, char *comment)
+{
+    char splitname[HOSTLEN + HOSTLEN + 2];
+
+#ifdef HIDE_SPLIT_SERVERS
+    ircsprintf(splitname, "%s %s", HIDDEN_SERVER_NAME, HIDDEN_SERVER_NAME);
+#else
+    ircsprintf(splitname, "%s %s", cptr->uplink->name, cptr->name);
+#endif
+
+    Debug((DEBUG_NOTICE, "exit_server(%s, %s, %s)", cptr->name, from->name,
+           comment));
+
+    exit_one_server(cptr, cptr, from, lcptr, splitname, comment);
+}
+
+/*
+ *  exit_client 
+ * This is old "m_bye". Name  changed, because this is not a
+ * protocol function, but a general server utility function.
+ * 
+ *      This function exits a client of *any* type (user, server, etc) 
+ * from this server. Also, this generates all necessary prototol 
+ * messages that this exit may cause. 
+ * 
+ *   1) If the client is a local client, then this implicitly exits
+ * all other clients depending on this connection (e.g. remote
+ * clients having 'from'-field that points to this. 
+ * 
+ *   2) If the client is a remote client, then only this is exited. 
+ * 
+ * For convenience, this function returns a suitable value for 
+ * m_function return value: 
+ * 
+ *      FLUSH_BUFFER    if (cptr == sptr) 
+ *      0 if (cptr != sptr)
+ */
+int 
+exit_client(aClient *cptr, aClient *sptr, aClient *from, char *comment)
+{
+#ifdef  FNAME_USERLOG
+    time_t on_for;
+#endif
+    
+    if (MyConnect(sptr)) 
+    {
+        call_hooks(CHOOK_SIGNOFF, sptr);
+
+        if (IsAnOper(sptr)) 
+            remove_from_list(&oper_list, sptr, NULL);
+        if (sptr->flags & FLAGS_HAVERECVQ)
+            remove_from_list(&recvq_clients, sptr, NULL);
+        if (IsClient(sptr))
+            Count.local--;
+        if (IsNegoServer(sptr))
+            sendto_realops("Lost server %s during negotiation: %s", 
+                           sptr->name, comment);
+        
+        if (IsServer(sptr)) 
+        {
+            Count.myserver--;
+            if (IsULine(sptr))
+                Count.myulined--;
+            remove_from_list(&server_list, sptr, NULL);
+            if (server_list == NULL) 
+                server_was_split = YES;
+        }
+        sptr->flags |= FLAGS_CLOSING;
+        if (IsPerson(sptr)) 
+        {
+            Link *lp, *next;
+            LOpts *lopt = sptr->user->lopt;
+            /* poof goes their watchlist! */
+            hash_del_watch_list(sptr);
+            /* if they have listopts, axe those, too */
+            if(lopt != NULL) 
+            {
+                remove_from_list(&listing_clients, sptr, NULL);
+                for (lp = lopt->yeslist; lp; lp = next) 
+                {
+                    next = lp->next;
+                    free_link(lp);
+                }
+                for (lp = lopt->nolist; lp; lp = next) 
+                {
+                    next = lp->next;
+                    free_link(lp);
+                }
+                                
+                MyFree(sptr->user->lopt);
+                sptr->user->lopt = NULL;
+            }
+            sendto_realops_lev(CCONN_LEV,
+                               "Client exiting: %s (%s@%s) [%s] [%s]",
+                               sptr->name, sptr->user->username,
+                               sptr->user->host,
+                               (sptr->flags & FLAGS_NORMALEX) ?
+                               "Client Quit" : comment,
+                               sptr->hostip);
+        }
+#ifdef FNAME_USERLOG
+        on_for = timeofday - sptr->firsttime;
+#endif
+#if defined(USE_SYSLOG) && defined(SYSLOG_USERS)
+        if (IsPerson(sptr))
+            syslog(LOG_NOTICE, "%s (%3d:%02d:%02d): %s!%s@%s %d/%d\n",
+                   myctime(sptr->firsttime),
+                   on_for / 3600, (on_for % 3600) / 60,
+                   on_for % 60, sptr->name,
+                   sptr->user->username, sptr->user->host,
+                   sptr->sendK, sptr->receiveK);
+#endif
+#if defined(FNAME_USERLOG)
+        {
+            char        linebuf[300];
+            static int  logfile = -1;
+            static long lasttime;
+            
+            /*
+             * This conditional makes the logfile active only after it's
+             * been created - thus logging can be turned off by removing
+             * the file.
+             * 
+             * stop NFS hangs...most systems should be able to open a file in
+             * 3 seconds. -avalon (curtesy of wumpus)
+             * 
+             * Keep the logfile open, syncing it every 10 seconds -Taner
+             */
+            if (IsPerson(sptr)) 
+            {
+                if (logfile == -1) 
+                {
+                    alarm(3);
+                    logfile = open(FNAME_USERLOG, O_WRONLY | O_APPEND);
+                    alarm(0);
+                }
+                ircsprintf(linebuf, "%s (%3d:%02d:%02d): %s!%s@%s %d/%d\n",
+                           myctime(sptr->firsttime), on_for / 3600,
+                           (on_for % 3600) / 60, on_for % 60,
+                           sptr->name, sptr->user->username,
+                           sptr->user->host, sptr->sendK, sptr->receiveK);
+                alarm(3);
+                write(logfile, linebuf, strlen(linebuf));
+                alarm(0);
+                /* Resync the file evey 10 seconds*/
+                if (timeofday - lasttime > 10) 
+                {
+                    alarm(3);
+                    close(logfile);
+                    alarm(0);
+                    logfile = -1;
+                    lasttime = timeofday;
+                }
+            }
+        }
+#endif
+        if (sptr->fd >= 0) 
+        {
+            if (cptr != NULL && sptr != cptr)
+                sendto_one(sptr, "ERROR :Closing Link: %s %s (%s)",
+                           IsPerson(sptr) ? sptr->sockhost : "0.0.0.0", 
+                           sptr->name, comment);
+            else
+                sendto_one(sptr, "ERROR :Closing Link: %s (%s)",
+                           IsPerson(sptr) ? sptr->sockhost : "0.0.0.0", 
+                           comment);
+        }
+        /*
+         * * Currently only server connections can have * depending
+         * remote clients here, but it does no * harm to check for all
+         * local clients. In * future some other clients than servers
+         * might * have remotes too... *
+         * 
+         * Close the Client connection first and mark it * so that no
+         * messages are attempted to send to it. *, The following *must*
+         * make MyConnect(sptr) == FALSE!). * It also makes sptr->from ==
+         * NULL, thus it's unnecessary * to test whether "sptr != acptr"
+         * in the following loops.
+         */
+        if (IsServer(sptr)) 
+        {
+            sendto_ops("%s was connected for %lu seconds.  %lu/%lu "
+                       "sendK/recvK.", sptr->name, timeofday - sptr->firsttime,
+                       sptr->sendK, sptr->receiveK);
+#ifdef USE_SYSLOG
+            syslog(LOG_NOTICE, "%s was connected for %lu seconds.  %lu/%lu "
+                   "sendK/recvK.", sptr->name, 
+                        (u_long) timeofday - sptr->firsttime,
+                   sptr->sendK, sptr->receiveK);
+#endif
+            close_connection(sptr);
+            sptr->sockerr = 0;
+            sptr->flags |= FLAGS_DEADSOCKET;
+        }
+        else
+        {
+            close_connection(sptr);
+            sptr->sockerr = 0;
+            sptr->flags |= FLAGS_DEADSOCKET;
+        }
+                
+    }
+    exit_one_client(cptr, sptr, from, comment);
+    return cptr == sptr ? FLUSH_BUFFER : 0;
+}
+
+/*
+ * Exit one client, local or remote. Assuming all dependants have
+ * been already removed, and socket closed for local client.
+ */
+static void 
+exit_one_client(aClient *cptr, aClient *sptr, aClient *from, char *comment)
+{
+    Link   *lp;
+    
+    /*
+     * For a server or user quitting, propogate the information to
+     * other servers (except to the one where is came from (cptr))
+     */
+    if (IsMe(sptr))
+    {
+        sendto_ops("ERROR: tried to exit me! : %s", comment);
+        return;                 /* ...must *never* exit self!! */
+    }
+    else if (IsServer(sptr))
+    {
+#ifdef ALWAYS_SEND_DURING_SPLIT
+        currently_processing_netsplit = YES;
+#endif
+
+        exit_server(cptr, sptr, from, comment);
+        
+#ifdef ALWAYS_SEND_DURING_SPLIT
+        currently_processing_netsplit = NO;
+#endif
+        return;
+    }
+    else if (!(IsPerson(sptr)))
+        /*
+         * ...this test is *dubious*, would need * some thought.. but for
+         * now it plugs a * nasty hole in the server... --msa
+         */
+        ;                               /* Nothing */
+    else if (sptr->name[0])
+    {   
+        /* ...just clean all others with QUIT... */
+        /*
+         * If this exit is generated from "m_kill", then there is no
+         * sense in sending the QUIT--KILL's have been sent instead.
+         */
+        if ((sptr->flags & FLAGS_KILLED) == 0) 
+        {
+            sendto_serv_butone(cptr, ":%s QUIT :%s",
+                               sptr->name, comment);
+        }
+        /*
+         * * If a person is on a channel, send a QUIT notice * to every
+         * client (person) on the same channel (so * that the client can
+         * show the "**signoff" message). * (Note: The notice is to the
+         * local clients *only*)
+         */
+        if (sptr->user)
+        {
+            send_part_to_common_channels(sptr, comment);
+            send_quit_to_common_channels(sptr, comment);
+            while ((lp = sptr->user->channel))
+                remove_user_from_channel(sptr, lp->value.chptr);
+
+            if (sptr->ip.s_addr)
+                clones_remove(sptr);
+            
+            /* Clean up invitefield */
+            while ((lp = sptr->user->invited))
+                del_invite(sptr, lp->value.chptr);
+            /* Clean up silences */
+            while ((lp = sptr->user->silence)) 
+                del_silence(sptr, lp->value.cp);
+            remove_dcc_references(sptr);
+            /* again, this is all that is needed */
+        }
+    }
+
+    /* Remove sptr from the client list */
+    if (del_from_client_hash_table(sptr->name, sptr) != 1) 
+    {
+        Debug((DEBUG_ERROR, "%#x !in tab %s[%s] %#x %#x %#x %d %d %#x",
+               sptr, sptr->name,
+               sptr->from ? sptr->from->sockhost : "??host",
+               sptr->from, sptr->next, sptr->prev, sptr->fd,
+               sptr->status, sptr->user));
+    }
+    /* remove user from watchlists */
+    if(IsRegistered(sptr))
+        hash_check_watch(sptr, RPL_LOGOFF);
+    remove_client_from_list(sptr);
+    return;
+}
+
+void 
+initstats()
+{
+    memset((char *) &ircst, '\0', sizeof(ircst));
+}
+
+char *
+make_parv_copy(char *pbuf, int parc, char *parv[])
+{
+   int pbpos = 0, i;
+
+   for(i = 1; i < parc; i++)
+   {
+      char *tmp = parv[i];
+
+      if(i != 1)
+         pbuf[pbpos++] = ' ';
+      if(i == (parc - 1))
+         pbuf[pbpos++] = ':';
+
+      while(*tmp)
+         pbuf[pbpos++] = *(tmp++);
+   }
+   pbuf[pbpos] = '\0';
+
+   return pbuf;
+}
diff --git a/src/s_numeric.c b/src/s_numeric.c
new file mode 100644 (file)
index 0000000..ddafcd5
--- /dev/null
@@ -0,0 +1,134 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/s_numeric.c
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   Numerous fixes by Markku Savela
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: s_numeric.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "channel.h"
+#include "h.h"
+
+static char buffer[1024];
+
+/*
+ * * DoNumeric (replacement for the old do_numeric) *
+ * 
+ * parc         number of arguments ('sender' counted as one!)
+ * parv[0]      pointer to 'sender' (may point to empty string)
+ * parv[1]..parv[parc-1] 
+ *              pointers to additional parameters, this is a NULL 
+ *              terminated list (parv[parc] == NULL).
+ * 
+ * *WARNING* 
+ * Numerics are mostly error reports. If there is something 
+ * wrong with the message, just *DROP* it! Don't even think of 
+ * sending back a neat error message -- big danger of creating 
+ * a ping pong error message...
+ */
+int do_numeric(int numeric, aClient *cptr, aClient *sptr, int parc, 
+              char *parv[])
+{
+    aClient    *acptr;
+    aChannel   *chptr;
+    char       *nick, *p;
+    int         i;
+
+    if (parc < 1 || !IsServer(sptr))
+       return 0;
+    /* Remap low number numerics. */
+    if (numeric < 100)
+       numeric += 100;
+    /*
+     * Prepare the parameter portion of the message into 'buffer'. 
+     * (Because the buffer is twice as large as the message buffer for
+     * the socket, no overflow can occur here... ...on current
+     * assumptions--bets are off, if these are changed --msa) 
+     * Note: if buffer is non-empty, it will begin with SPACE.
+     */
+    buffer[0] = '\0';
+    if (parc > 1)
+    {
+       int bpos = 0;
+       char *p;
+
+       for (i = 2; i < (parc - 1); i++)
+       {
+           buffer[bpos++] = ' ';
+           for(p = parv[i]; *p; p++)
+               buffer[bpos++] = *p;
+       }
+       buffer[bpos++] = ' ';
+       buffer[bpos++] = ':';
+       for(p = parv[parc - 1]; *p; p++)
+           buffer[bpos++] = *p;
+       buffer[bpos] = '\0';
+    }
+    for (; (nick = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+    {
+       if ((acptr = find_client(nick, (aClient *) NULL)))
+       {
+           int dohide;
+
+           /*
+            * Drop to bit bucket if for me... ...one might consider
+            * sendto_ops * here... --msa * And so it was done. -avalon *
+            * And regretted. Dont do it that way. Make sure * it goes
+            * only to non-servers. -avalon * Check added to make sure
+            * servers don't try to loop * with numerics which can happen
+            * with nick collisions. * - Avalon
+            */
+
+#ifdef HIDE_NUMERIC_SOURCE
+           dohide = MyClient(acptr) ? 1 : 0;
+#else
+           dohide = 0;
+#endif
+
+           if (!IsMe(acptr) && IsPerson(acptr))
+               sendto_prefix_one(acptr, dohide ? &me : sptr, ":%s %d %s%s",
+                                 dohide ? me.name : parv[0], numeric, nick, buffer);
+           else if (IsServer(acptr) && acptr->from != cptr)
+               sendto_prefix_one(acptr, sptr, ":%s %d %s%s",
+                                 parv[0], numeric, nick, buffer);
+       }
+       else if ((chptr = find_channel(nick, (aChannel *) NULL)))
+       {
+           int dohide;
+
+#ifdef HIDE_NUMERIC_SOURCE
+           dohide = 1;
+#else
+           dohide = 0;
+#endif
+           sendto_channel_butserv(chptr, dohide ? &me : sptr, ":%s %d %s%s",
+                                  dohide ? me.name : parv[0], numeric, 
+                                  chptr->chname, buffer);
+
+           sendto_channel_remote_butone(cptr, sptr, chptr, 
+                                        parv[0], numeric, 
+                                        chptr->chname, buffer);
+
+       }
+    }
+    return 0;
+}
diff --git a/src/s_serv.c b/src/s_serv.c
new file mode 100644 (file)
index 0000000..7acba49
--- /dev/null
@@ -0,0 +1,3683 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/s_serv.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: s_serv.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "channel.h"
+#include "nameser.h"
+#include "resolv.h"
+#include "dh.h"
+#include "zlink.h"
+#include "userban.h"
+
+#if defined(AIX) || defined(SVR3)
+#include <time.h>
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include "h.h"
+#if defined( HAVE_STRING_H )
+#include <string.h>
+#else
+/* older unices don't have strchr/strrchr .. help them out */
+#include <strings.h>
+#undef strchr
+#define strchr index
+#endif
+#include "fdlist.h"
+#include "throttle.h"
+#include "clones.h"
+
+static char buf[BUFSIZE];
+extern int  rehashed;
+extern int  forked;
+
+/* external variables */
+
+/* external functions */
+
+extern char *smalldate(time_t); /* defined in s_misc.c */
+extern void outofmemory(void);  /* defined in list.c */
+extern void s_die(void);
+extern int  match(char *, char *);      /* defined in match.c */
+
+/* Local function prototypes */
+
+static int  isnumber(char *);   /* return 0 if not, else return number */
+static char *cluster(char *);
+
+int         send_motd(aClient *, aClient *, int, char **);
+void        read_motd(char *);
+void        read_shortmotd(char *);
+
+char        motd_last_changed_date[MAX_DATE_STRING]; /* enough room for date */ 
+
+void fakeserver_list(aClient *);
+int fakelinkscontrol(int, char **);
+void fakelinkserver_update(char *, char *);
+void fakeserver_sendserver(aClient *);
+
+int is_luserslocked();
+void send_fake_users(aClient *);
+void send_fake_lusers(aClient *);
+void fakelusers_sendlock(aClient *);
+
+#ifdef LOCKFILE
+/* Shadowfax's lockfile code */
+void        do_pending_klines(void);
+
+struct pkl
+{
+    char       *comment;        /* Kline Comment */
+    char       *kline;          /* Actual Kline */
+    struct pkl *next;           /* Next Pending Kline */
+} *pending_klines = NULL;
+
+time_t      pending_kline_time = 0;
+
+#endif /* LOCKFILE */
+
+/*
+ * m_functions execute protocol messages on this server: *
+ * 
+ * cptr: 
+ ** always NON-NULL, pointing to a *LOCAL* client
+ ** structure (with an open socket connected!). This 
+ ** is the physical socket where the message originated (or
+ ** which caused the m_function to be executed--some
+ ** m_functions may call others...). 
+ * 
+ * sptr:
+ ** the source of the message, defined by the
+ ** prefix part of the message if present. If not or
+ ** prefix not found, then sptr==cptr. 
+ * 
+ *      *Always* true (if 'parse' and others are working correct): 
+ * 
+ *      1)      sptr->from == cptr  (note: cptr->from == cptr) 
+ * 
+ *      2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr 
+ * cannot be a local connection, unless it's actually cptr!). 
+ *
+ * MyConnect(x) should probably  be defined as (x == x->from) --msa
+ * 
+ * parc:    
+ ** number of variable parameter strings (if zero, 
+ ** parv is allowed to be NULL)
+ * 
+ * parv:    
+ ** a NULL terminated list of parameter pointers,
+ *** parv[0], sender (prefix string), if not present his points to 
+ *** an empty string.
+ *
+ ** [parc-1]:
+ *** pointers to additional parameters 
+ *** parv[parc] == NULL, *always* 
+ * 
+ * note:   it is guaranteed that parv[0]..parv[parc-1] are all
+ *         non-NULL pointers.
+ */
+
+/*
+ * * m_version 
+ *      parv[0] = sender prefix 
+ *      parv[1] = remote server
+ */
+int 
+m_version(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+
+    if (hunt_server(cptr, sptr, ":%s VERSION :%s", 1, parc, parv) ==
+        HUNTED_ISME)
+        send_rplversion(sptr);
+
+    return 0;
+}
+
+/*
+ * m_squit
+ * there are two types of squits: those going downstream (to the target server)
+ * and those going back upstream (from the target server).
+ * previously, it wasn't necessary to distinguish between these two types of 
+ * squits because they neatly echoed back all of the QUIT messages during
+ * an squit.  This, however, is no longer practical.
+ * 
+ * To clarify here, DOWNSTREAM signifies an SQUIT heading towards the target
+ * server UPSTREAM signifies an SQUIT which has successfully completed,
+ * heading out everywhere.
+ *
+ * acptr is the server being squitted.
+ * a DOWNSTREAM squit is where the notice did not come from acptr->from.
+ * an UPSTREAM squit is where the notice DID come from acptr->from.
+ *
+ *        parv[0] = sender prefix 
+ *        parv[1] = server name 
+ *        parv[2] = comment
+ */
+int 
+m_squit(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aConnect *aconn;
+    char *server;
+    aClient *acptr;
+    char *comment = (parc > 2) ? parv[2] : sptr->name;
+
+    if (!IsPrivileged(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    if (parc > 1) 
+    {
+        server = parv[1];
+        /* To accomodate host masking, a squit for a masked server
+         * name is expanded if the incoming mask is the same as the
+         * server name for that link to the name of link.
+         */
+        while ((*server == '*') && IsServer(cptr))
+        {
+            aconn = cptr->serv->aconn;
+            if (!aconn)
+                break;
+            if (!mycmp(server, my_name_for_link(me.name, aconn)))
+                server = cptr->name;
+            break;                      /* WARNING is normal here */
+            /* NOTREACHED */
+        }
+        /*
+         * The following allows wild cards in SQUIT. Only useful when
+         * the command is issued by an oper.
+         */
+        for (acptr = client; (acptr = next_client(acptr, server)); 
+             acptr = acptr->next)
+            if (IsServer(acptr) || IsMe(acptr))
+                break;
+        if (acptr && IsMe(acptr)) 
+        {
+            acptr = cptr;
+            server = cptr->sockhost;
+        }
+    }
+    else
+    {
+        /* This is actually protocol error. But, well, closing the
+         * link is very proper answer to that...
+         */
+        server = cptr->sockhost;
+        acptr = cptr;
+    }
+
+    if (!acptr) 
+    {
+        sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
+                   me.name, parv[0], server);
+        return 0;
+    }
+
+    if (MyClient(sptr) && ((!OPCanGRoute(sptr) && !MyConnect(acptr)) || 
+                           (!OPCanLRoute(sptr) && MyConnect(acptr)))) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    /* If the server is mine, we don't care about upstream or downstream,
+       just kill it and do the notice. */
+    
+    if (MyConnect(acptr)) 
+    {
+        sendto_gnotice("from %s: Received SQUIT %s from %s (%s)",
+                       me.name, acptr->name, get_client_name(sptr, HIDEME),
+                       comment);
+        sendto_serv_butone(NULL, ":%s GNOTICE :Received SQUIT %s from %s (%s)",
+                           me.name, server, get_client_name(sptr, HIDEME),
+                           comment);
+        
+#if defined(USE_SYSLOG) && defined(SYSLOG_SQUIT)
+        syslog(LOG_DEBUG, "SQUIT From %s : %s (%s)",
+               parv[0], server, comment);
+#endif
+        /* I am originating this squit! Not cptr! */
+        /* ack, but if cptr is squitting itself.. */
+        if(cptr == sptr)
+        {
+            exit_client(&me, acptr, sptr, comment);
+            return FLUSH_BUFFER; /* kludge */
+        }
+        return exit_client(&me, acptr, sptr, comment);
+    }
+    
+    /* the server is not connected to me. Determine whether this is an upstream
+       or downstream squit */
+    
+    if(sptr->from == acptr->from) /* upstream */
+    {
+        sendto_realops_lev(DEBUG_LEV,
+                           "Exiting server %s due to upstream squit by %s [%s]",
+                           acptr->name, sptr->name, comment);
+        return exit_client(cptr, acptr, sptr, comment);
+    }
+
+    /* fallthrough: downstream */
+
+    if(!(IsUnconnect(acptr->from))) /* downstream not unconnect capable */
+    {
+        sendto_realops_lev(DEBUG_LEV,
+                    "Exiting server %s due to non-unconnect server %s [%s]",
+                    acptr->name, acptr->from->name, comment);
+        return exit_client(&me, acptr, sptr, comment);
+    }
+
+    
+    sendto_realops_lev(DEBUG_LEV, "Passing along SQUIT for %s by %s [%s]",
+                       acptr->name, sptr->name, comment);
+    sendto_one(acptr->from, ":%s SQUIT %s :%s", parv[0], acptr->name, comment);
+
+    return 0;
+}
+
+/*
+ * m_svinfo 
+ *       parv[0] = sender prefix 
+ *       parv[1] = TS_CURRENT for the server 
+ *       parv[2] = TS_MIN for the server 
+ *       parv[3] = server is standalone or connected to non-TS only 
+ *       parv[4] = server's idea of UTC time
+ */
+int m_svinfo(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    time_t      deltat, tmptime, theirtime;
+    
+    if (!IsServer(sptr) || !MyConnect(sptr))
+        return 0;
+
+    if(parc == 2 && mycmp(parv[1], "ZIP") == 0)
+    {
+        SetZipIn(sptr);
+        sptr->serv->zip_in = zip_create_input_session();
+        sendto_gnotice("from %s: Input from %s is now compressed",
+                       me.name, get_client_name(sptr, HIDEME));
+        sendto_serv_butone(sptr,
+                           ":%s GNOTICE :Input from %s is now compressed",
+                           me.name, get_client_name(sptr, HIDEME));
+        return ZIP_NEXT_BUFFER;
+    }
+    
+    if(parc < 5 || !DoesTS(sptr))
+        return 0;
+    
+    if (TS_CURRENT < atoi(parv[2]) || atoi(parv[1]) < TS_MIN) 
+    {
+        /* a server with the wrong TS version connected; since we're
+         * TS_ONLY we can't fall back to the non-TS protocol so we drop
+         * the link  -orabidoo
+         */
+        sendto_ops("Link %s dropped, wrong TS protocol version (%s,%s)",
+                   get_client_name(sptr, HIDEME), parv[1], parv[2]);
+        return exit_client(sptr, sptr, sptr, "Incompatible TS version");
+    }
+    
+    tmptime = time(NULL);
+    theirtime = atol(parv[4]);
+    deltat = abs(theirtime - tmptime);
+    
+    if (deltat > tsmaxdelta) 
+    {
+        sendto_gnotice("from %s: Link %s dropped, excessive TS delta (my "
+                       "TS=%d, their TS=%d, delta=%d)",
+                       me.name, get_client_name(sptr, HIDEME), tmptime,
+                       theirtime, deltat);
+        sendto_serv_butone(sptr, ":%s GNOTICE :Link %s dropped, excessive "
+                           "TS delta (delta=%d)",
+                           me.name, get_client_name(sptr, HIDEME), deltat);
+        return exit_client(sptr, sptr, sptr, "Excessive TS delta");
+    }
+
+    if (deltat > tswarndelta) 
+    {
+        sendto_realops("Link %s notable TS delta (my TS=%d, their TS=%d, "
+                       "delta=%d)", get_client_name(sptr, HIDEME), tmptime,
+                       theirtime, deltat);
+    }
+
+    return 0;
+}
+
+/* 
+ * m_burst
+ *      parv[0] = sender prefix
+ *      parv[1] = SendQ if an EOB
+ */
+int 
+m_burst(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+  
+    if (!IsServer(sptr) || sptr != cptr || parc > 2 || !IsBurst(sptr))
+        return 0;
+    if (parc == 2) { /* This is an EOB */
+        sptr->flags &= ~(FLAGS_EOBRECV);
+        if (sptr->flags & (FLAGS_SOBSENT|FLAGS_BURST)) return 0;
+        
+        /* we've already sent our EOB.. we synched first
+         * no need to check IsBurst because we shouldn't receive a BURST if 
+         * they're not BURST capab
+         */
+        
+        sendto_gnotice("from %s: synch to %s in %d %s at %s sendq", me.name,
+                       *parv, (timeofday-sptr->firsttime), 
+                       (timeofday-sptr->firsttime)==1?"sec":"secs", parv[1]);
+        sendto_serv_butone(NULL,
+                           ":%s GNOTICE :synch to %s in %d %s at %s sendq",
+                           me.name, sptr->name, (timeofday-sptr->firsttime),
+                           (timeofday-sptr->firsttime)==1?"sec":"secs",
+                           parv[1]);
+        
+    }
+    else
+    {
+        sptr->flags |= FLAGS_EOBRECV;
+    }
+    return 0;
+}
+
+/*
+ * * m_info 
+ *      parv[0] = sender prefix 
+ *      parv[1] = servername
+ */
+int 
+m_info(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char      **text = infotext;
+
+    static time_t last_used = 0L;
+    if (hunt_server(cptr,sptr,":%s INFO :%s",1,parc,parv) == HUNTED_ISME) 
+    {
+        sendto_realops_lev(SPY_LEV, "INFO requested by %s (%s@%s) [%s]",
+                           sptr->name, sptr->user->username, sptr->user->host,
+                           sptr->user->server);
+                        
+        if (!IsAnOper(sptr)) 
+        {
+            if (IsSquelch(sptr)) {
+                sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
+                return 0;
+            }
+            if (!MyConnect(sptr))
+                return 0;
+            if ((last_used + MOTD_WAIT) > NOW) 
+                return 0;
+            else 
+                last_used = NOW;
+        }
+        while (*text)
+            sendto_one(sptr, rpl_str(RPL_INFO),
+                       me.name, parv[0], *text++);
+                        
+        sendto_one(sptr, rpl_str(RPL_INFO), me.name, parv[0], "");
+
+        /* I am -definately- going to come up with a replacement for this! */
+        /* you didnt, so i removed it.. kinda stupid anyway.  -epi */
+        
+        sendto_one(sptr,
+                   ":%s %d %s :Birth Date: %s, compile #%s",
+                   me.name, RPL_INFO, parv[0], creation, generation);
+        sendto_one(sptr, ":%s %d %s :On-line since %s",
+                   me.name, RPL_INFO, parv[0],
+                   myctime(me.firsttime));
+        sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
+    }
+    return 0;
+}
+
+/*
+ * * m_links 
+ *      parv[0] = sender prefix 
+ *      parv[1] = servername mask 
+ * or 
+ *      parv[0] = sender prefix 
+ *      parv[1] = server to query 
+ *      parv[2] = servername mask
+ */
+int 
+m_links(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       *mask;
+    aClient    *acptr;
+    char        clean_mask[(2 * HOSTLEN) + 1];
+    char       *s;
+    char       *d;
+    int         n;
+
+    /* reject non-local requests */
+    if (IsServer(sptr) || (!IsAnOper(sptr) && !MyConnect(sptr)))
+        return 0;
+
+    mask = (parc < 2) ? NULL : parv[1];
+    
+    /*
+     * * sigh* Before the kiddies find this new and exciting way of
+     * * annoying opers, lets clean up what is sent to all opers
+     * * -Dianora
+     */
+
+    if (mask) 
+    {      /* only necessary if there is a mask */
+        s = mask;
+        d = clean_mask;
+        n = (2 * HOSTLEN) - 2;
+        while (*s && n > 0) 
+        {
+            /* Is it a control character? */
+            if ((unsigned char) *s < (unsigned char) ' ') 
+            {
+                *d++ = '^';
+                /* turn it into a printable */
+                *d++ = (char) ((unsigned char)*s + 0x40); 
+                s++;
+                n -= 2;
+            }
+            else if ((unsigned char) *s > (unsigned char) '~') 
+            {
+                *d++ = '.';
+                s++;
+                n--;
+            }
+            else 
+            {
+                *d++ = *s++;
+                n--;
+            }
+        }
+        *d = '\0';
+    }
+
+    if (MyConnect(sptr))
+        sendto_realops_lev(SPY_LEV,
+                           "LINKS %s requested by %s (%s@%s) [%s]",
+                           mask ? clean_mask : "all",
+                           sptr->name, sptr->user->username,
+                           sptr->user->host, sptr->user->server);
+
+    if(!(confopts & FLAGS_SHOWLINKS) && !IsAnOper(sptr))
+        fakeserver_list(sptr);
+    else
+    for (acptr = client, (void) collapse(mask); acptr; acptr = acptr->next) 
+    {
+        if (!IsServer(acptr) && !IsMe(acptr))
+            continue;
+        if (!BadPtr(mask) && match(mask, acptr->name))
+            continue;
+#ifdef HIDEULINEDSERVS
+        if (!IsOper(sptr) && IsULine(acptr))
+            continue;
+#endif
+        sendto_one(sptr, rpl_str(RPL_LINKS),
+                   me.name, parv[0], acptr->name, acptr->serv->up,
+                   acptr->hopcount, (acptr->info[0] ? acptr->info :
+                                     "(Unknown Location)"));
+    }
+    sendto_one(sptr, rpl_str(RPL_ENDOFLINKS), me.name, parv[0],
+               BadPtr(mask) ? "*" : clean_mask);
+    return 0;
+}
+
+/*
+ * * m_users 
+ *        parv[0] = sender prefix 
+ *        parv[1] = servername
+ */
+int 
+m_users(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    if (hunt_server(cptr, sptr, ":%s USERS :%s", 1, parc, parv) == HUNTED_ISME) 
+    {
+        if(is_luserslocked())
+        {
+            send_fake_users(sptr);
+            return 0;
+        }
+        /* No one uses this any more... so lets remap it..   -Taner */
+        sendto_one(sptr, rpl_str(RPL_LOCALUSERS), me.name, parv[0],
+                   Count.local, Count.max_loc);
+        sendto_one(sptr, rpl_str(RPL_GLOBALUSERS), me.name, parv[0],
+                   Count.total, Count.max_tot);
+    }
+    return 0;
+}
+
+/*
+ * * Note: At least at protocol level ERROR has only one parameter, 
+ * although this is called internally from other functions  --msa 
+ *
+ *      parv[0] = sender prefix 
+ *      parv[*] = parameters
+ */
+int 
+m_error(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char   *para;
+
+    para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>";
+
+    Debug((DEBUG_ERROR, "Received ERROR message from %s: %s",
+           sptr->name, para));
+    /*
+     * * Ignore error messages generated by normal user clients 
+     * (because ill-behaving user clients would flood opers screen
+     * otherwise). Pass ERROR's from other sources to the local
+     * operator...
+     */
+    if (IsPerson(cptr) || IsUnknown(cptr))
+        return 0;
+    if (cptr == sptr)
+        sendto_ops("ERROR :from %s -- %s",
+                   get_client_name(cptr, HIDEME), para);
+    else
+        sendto_ops("ERROR :from %s via %s -- %s", sptr->name,
+                   get_client_name(cptr, HIDEME), para);
+    return 0;
+}
+
+/*
+ * m_help
+ * parv[0] = sender prefix
+ * 
+ * Forward help requests to HelpServ if defined, and is invoked
+ * by non-opers, otherwise sends opers.txt to opers (if present),
+ * or sends a big list of commands to non-opers (and opers if
+ * opers.txt is not present). -srd
+ */
+int 
+m_help(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int         i;
+    aMotd *helpfile_ptr;
+
+    static time_t last_used = 0L;
+
+#ifdef HELP_FORWARD_HS
+    if (!IsAnOper(sptr))
+    {
+       if (parc < 2 || *parv[1] == '\0')
+       {
+          sendto_one(sptr, ":%s NOTICE %s :For a list of help topics, type "
+                     "/%s %s", me.name, sptr->name, HELPSERV, DEF_HELP_CMD);
+          return -1;
+       }
+       return m_hs(cptr, sptr, parc, parv);
+       return 0;
+    }
+#endif
+    
+    if (!IsAnOper(sptr))
+    {
+       /* reject non local requests */
+       if ((last_used + MOTD_WAIT) > NOW)
+          return 0;   
+       else
+          last_used = NOW;
+    }
+
+    if (!IsAnOper(sptr) || (helpfile == (aMotd *) NULL))
+    {
+        for (i = 0; msgtab[i].cmd; i++)
+            sendto_one(sptr, ":%s NOTICE %s :%s",
+                       me.name, parv[0], msgtab[i].cmd);
+        return 0;
+    }
+       
+    helpfile_ptr = helpfile;
+    while (helpfile_ptr)
+    {
+        sendto_one(sptr,
+                   ":%s NOTICE %s :%s",
+                   me.name, parv[0], helpfile_ptr->line);
+        helpfile_ptr = helpfile_ptr->next;
+    }
+       
+    return 0;
+}
+
+/*
+ * parv[0] = sender parv[1] = host/server mask. 
+ * parv[2] = server to query
+ * 
+ * 199970918 JRL hacked to ignore parv[1] completely and require parc > 3
+ * to cause a force
+ */
+int 
+m_lusers(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int send_lusers(aClient *, aClient *, int, char **);
+
+    if (parc > 2) 
+    {
+        if (hunt_server(cptr, sptr, ":%s LUSERS %s :%s", 2, parc, parv) !=
+            HUNTED_ISME)
+            return 0;
+    }
+
+    if(is_luserslocked())
+    {
+       send_fake_lusers(sptr);
+       return 0;
+    }           
+
+    return send_lusers(cptr,sptr,parc,parv);
+}
+
+/*
+ * send_lusers
+ *     parv[0] = sender
+ *     parv[1] = host/server mask.
+ *     parv[2] = server to query
+ */
+int send_lusers(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+#define LUSERS_CACHE_TIME 180
+    static long                  last_time=0;
+    static int           s_count = 0, c_count = 0, u_count = 0, i_count = 0;
+    static int           o_count = 0, m_client = 0, m_server = 0, m_ulined = 0;
+    int                          forced;
+    aClient             *acptr;
+
+    forced = (IsAnOper(sptr) && (parc > 3));
+
+    Count.unknown = 0;
+    m_server = Count.myserver;
+    m_ulined = Count.myulined;
+    m_client = Count.local;
+    i_count = Count.invisi;
+    u_count = Count.unknown;
+    c_count = Count.total - Count.invisi;
+    s_count = Count.server;
+    o_count = Count.oper;
+    if (forced || (timeofday > last_time + LUSERS_CACHE_TIME)) 
+    {
+        last_time = timeofday;
+        /* only recount if more than a second has passed since last request
+         * use LUSERS_CACHE_TIME instead... 
+         */
+        s_count = 0;
+        c_count = 0;
+        u_count = 0;
+        i_count = 0;
+        o_count = 0;
+        m_client = 0;
+        m_server = 0;
+        m_ulined = 0;
+        
+        for (acptr = client; acptr; acptr = acptr->next) 
+        {
+            switch (acptr->status) 
+            {
+            case STAT_SERVER:
+                if (MyConnect(acptr))
+                {
+                    m_server++;
+                    if(IsULine(acptr))
+                        m_ulined++;
+                }
+            case STAT_ME:
+                s_count++;
+                break;
+            case STAT_CLIENT:
+                if (IsAnOper(acptr))
+                    o_count++;
+#ifdef  SHOW_INVISIBLE_LUSERS
+                if (MyConnect(acptr))
+                    m_client++;
+                if (!IsInvisible(acptr))
+                    c_count++;
+                else
+                    i_count++;
+#else
+                if (MyConnect(acptr)) 
+                {
+                    if (IsInvisible(acptr)) 
+                    {
+                        if (IsAnOper(sptr))
+                            m_client++;
+                    }
+                    else
+                        m_client++;
+                }
+                if (!IsInvisible(acptr))
+                    c_count++;
+                else
+                    i_count++;
+#endif
+                break;
+            default:
+                u_count++;
+                break;
+            }
+        }
+        /*
+         * We only want to reassign the global counts if the recount time
+         * has expired, and NOT when it was forced, since someone may
+         * supply a mask which will only count part of the userbase
+         * -Taner
+         */
+        if (!forced) 
+        {
+            if (m_server != Count.myserver) 
+            {
+                sendto_realops_lev(DEBUG_LEV,
+                                   "Local server count off by %d",
+                                   Count.myserver - m_server);
+                Count.myserver = m_server;
+            }
+            if (m_ulined != Count.myulined) 
+            {
+                sendto_realops_lev(DEBUG_LEV,
+                                   "Local superserver count off by %d",
+                                   Count.myulined - m_ulined);
+                Count.myulined = m_ulined;
+            }
+            if (s_count != Count.server) 
+            {
+                sendto_realops_lev(DEBUG_LEV,
+                                   "Server count off by %d",
+                                   Count.server - s_count);
+                Count.server = s_count;
+            }
+            if (i_count != Count.invisi) 
+            {
+                sendto_realops_lev(DEBUG_LEV,
+                                   "Invisible client count off by %d",
+                                   Count.invisi - i_count);
+                Count.invisi = i_count;
+            }
+            if ((c_count + i_count) != Count.total) 
+            {
+                sendto_realops_lev(DEBUG_LEV, "Total client count off by %d",
+                                   Count.total - (c_count + i_count));
+                Count.total = c_count + i_count;
+            }
+            if (m_client != Count.local) 
+            {
+                sendto_realops_lev(DEBUG_LEV,
+                                   "Local client count off by %d",
+                                   Count.local - m_client);
+                Count.local = m_client;
+            }
+            if (o_count != Count.oper) 
+            {
+                sendto_realops_lev(DEBUG_LEV,
+                                   "Oper count off by %d",
+                                   Count.oper - o_count);
+                Count.oper = o_count;
+            }
+            Count.unknown = u_count;
+        }                       /* Complain & reset loop */
+    }                           /* Recount loop */
+    
+    /* save stats */
+    if ((timeofday - last_stat_save) > 3600)
+    {
+        FILE *fp;
+        char tmp[PATH_MAX];
+        
+        last_stat_save = timeofday;
+        ircsprintf(tmp, "%s/.maxclients", dpath);
+        fp = fopen(tmp, "w");
+        if (fp != NULL)
+        {
+            fprintf(fp, "%d %d %li %li %li %ld %ld %ld %ld", Count.max_loc,
+                    Count.max_tot, Count.weekly, Count.monthly,
+                    Count.yearly, Count.start, Count.week, Count.month,
+                    Count.year);
+            fclose(fp);
+            sendto_realops_lev(DEBUG_LEV, "Saved maxclient statistics");
+        }
+    }
+    
+    
+#ifndef SHOW_INVISIBLE_LUSERS
+    if (IsAnOper(sptr) && i_count)
+#endif
+        sendto_one(sptr, rpl_str(RPL_LUSERCLIENT), me.name, parv[0],
+                   c_count, i_count, s_count);
+#ifndef SHOW_INVISIBLE_LUSERS
+    else
+        sendto_one(sptr,
+                   ":%s %d %s :There are %d users on %d servers", me.name,
+                   RPL_LUSERCLIENT, parv[0], c_count,
+                   s_count);
+#endif
+    if (o_count)
+        sendto_one(sptr, rpl_str(RPL_LUSEROP),
+                   me.name, parv[0], o_count);
+    if (u_count > 0)
+        sendto_one(sptr, rpl_str(RPL_LUSERUNKNOWN),
+                   me.name, parv[0], u_count);
+    
+    /* This should be ok */
+    if (Count.chan > 0)
+        sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS),
+                   me.name, parv[0], Count.chan);
+    sendto_one(sptr, rpl_str(RPL_LUSERME),
+#ifdef HIDEULINEDSERVS
+               me.name, parv[0], m_client, 
+               IsOper(sptr) ? m_server : m_server - m_ulined);
+#else
+               me.name, parv[0], m_client, m_server);
+#endif
+    sendto_one(sptr, rpl_str(RPL_LOCALUSERS), me.name, parv[0],
+                   Count.local, Count.max_loc);
+    sendto_one(sptr, rpl_str(RPL_GLOBALUSERS), me.name, parv[0],
+               Count.total, Count.max_tot);
+    return 0;
+}
+
+/***********************************************************************
+ * m_connect() - Added by Jto 11 Feb 1989
+ ***********************************************************************/
+/*
+ * * m_connect 
+ *      parv[0] = sender prefix 
+ *      parv[1] = servername 
+ *      parv[2] = port number 
+ *      parv[3] = remote server
+ */
+int 
+m_connect(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int         port, tmpport, retval;
+    aConnect   *aconn;
+    aClient    *acptr;
+
+    if (!IsPrivileged(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return -1;
+    }
+    
+    if ((MyClient(sptr) && !OPCanGRoute(sptr) && parc > 3) ||
+        (MyClient(sptr) && !OPCanLRoute(sptr) && parc <= 3))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+    
+    if (hunt_server(cptr, sptr, ":%s CONNECT %s %s :%s",
+                    3, parc, parv) != HUNTED_ISME)
+        return 0;
+
+    if (parc < 2 || *parv[1] == '\0') 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "CONNECT");
+        return -1;
+    }
+
+    if ((acptr = find_server(parv[1], NULL)))
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
+                   me.name, parv[0], parv[1], "already exists from",
+                   acptr->from->name);
+        return 0;
+    }
+
+    if (!(aconn = find_aConnect(parv[1])))
+    {
+        sendto_one(sptr, "NOTICE %s :Connect: No Connect block found for %s.",
+                   parv[0], parv[1]);
+        return 0;
+    }
+    /*
+     * * Get port number from user, if given. If not specified, use
+     * the default form configuration structure. If missing from
+     * there, then use the precompiled default.
+     */
+    tmpport = port = aconn->port;
+    if (parc > 2 && !BadPtr(parv[2])) 
+    {
+        if ((port = atoi(parv[2])) <= 0) 
+        {
+            sendto_one(sptr,
+                       "NOTICE %s :Connect: Illegal port number",
+                       parv[0]);
+            return 0;
+        }
+    }
+    else if (port <= 0 && (port = PORTNUM) <= 0) 
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Connect: missing port number",
+                   me.name, parv[0]);
+        return 0;
+    }
+    /*
+     * * Notify all operators about remote connect requests
+     * Let's notify about local connects, too. - lucas
+     * sendto_ops_butone -> sendto_serv_butone(), like in df. -mjs
+     */
+    sendto_gnotice("from %s: %s CONNECT %s %s from %s",
+                   me.name,  IsAnOper(cptr) ? "Local" : "Remote", 
+                   parv[1], parv[2] ? parv[2] : "",
+                   get_client_name(sptr, HIDEME));
+    sendto_serv_butone(NULL, ":%s GNOTICE :%s CONNECT %s %s from %s", 
+                       me.name, IsAnOper(cptr) ? "Local" : "Remote",
+                       parv[1], parv[2] ? parv[2] : "",
+                       get_client_name(sptr, HIDEME));
+
+#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECT)
+    syslog(LOG_DEBUG, "CONNECT From %s : %s %s", parv[0], parv[1], 
+           parv[2] ? parv[2] : "");
+#endif
+    
+    aconn->port = port;
+    switch (retval = connect_server(aconn, sptr, NULL))
+    {
+        case 0:
+            sendto_one(sptr, ":%s NOTICE %s :*** Connecting to %s.",
+                       me.name, parv[0], aconn->name);
+            break;
+        case -1:
+            sendto_one(sptr, ":%s NOTICE %s :*** Couldn't connect to %s.",
+                       me.name, parv[0], aconn->name);
+            break;
+        case -2:
+            sendto_one(sptr, ":%s NOTICE %s :*** Host %s is unknown.",
+                       me.name, parv[0], aconn->name);
+            break;
+        default:
+            sendto_one(sptr, ":%s NOTICE %s :*** Connection to %s failed: %s",
+                       me.name, parv[0], aconn->name, strerror(retval));
+    }
+    aconn->port = tmpport;
+    return 0;
+}
+
+/*
+ * * m_wallops (write to *all* opers currently online) 
+ *      parv[0] = sender prefix 
+ *      parv[1] = message text
+ */
+int 
+m_wallops(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       *message = parc > 1 ? parv[1] : NULL;
+    
+    if (BadPtr(message)) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "WALLOPS");
+        return 0;
+    }
+    
+    if (!IsServer(sptr) && MyConnect(sptr) && !OPCanWallOps(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return (0);
+    }
+    
+    sendto_wallops_butone(IsServer(cptr) ? cptr : NULL, sptr,
+                          ":%s WALLOPS :%s", parv[0], message);
+    return 0;
+}
+
+/*
+ * * m_locops (write to *all* local opers currently online) 
+ *      parv[0] = sender prefix 
+ *      parv[1] = message text
+ */
+int 
+m_locops(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       *message = parc > 1 ? parv[1] : NULL;
+
+    if (BadPtr(message)) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "LOCOPS");
+        return 0;
+    }
+
+    if (!IsServer(sptr) && MyConnect(sptr) && !OPCanLocOps(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return (0);
+    }
+    sendto_locops("from %s: %s", parv[0], message);
+    return (0);
+}
+
+/*
+ * m_goper  (Russell) sort of like wallop, but only to ALL +o clients
+ * on every server. 
+ *      parv[0] = sender prefix 
+ *      parv[1] = message text 
+ * Taken from df465, ported to hybrid. -mjs
+ */
+int 
+m_goper(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       *message = parc > 1 ? parv[1] : NULL;
+
+    if (check_registered(sptr))
+        return 0;
+
+    if (BadPtr(message)) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "GOPER");
+        return 0;
+    }
+    if (!IsServer(sptr) || !IsULine(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    sendto_serv_butone(IsServer(cptr) ? cptr : NULL, ":%s GOPER :%s",
+                       parv[0], message);
+    sendto_ops("from %s: %s", parv[0], message);
+    return 0;
+}
+
+/*
+ * m_gnotice  (Russell) sort of like wallop, but only to +g clients on *
+ * this server. 
+ *      parv[0] = sender prefix 
+ *      parv[1] = message text 
+ * ported from df465 to hybrid -mjs
+ *
+ * This function itself doesnt need any changes for the move to +n routing
+ * notices, to sendto takes care of it.  Now only sends to +n clients -epi
+ */
+int 
+m_gnotice(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+
+    char       *message = parc > 1 ? parv[1] : NULL;
+    
+    if (check_registered(sptr))
+        return 0;
+
+    if (BadPtr(message)) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "GNOTICE");
+        return 0;
+    }
+    if (!IsServer(sptr) && MyConnect(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    sendto_serv_butone_services(IsServer(cptr) ? cptr : NULL, ":%s GNOTICE :%s",
+                       parv[0], message);
+    sendto_gnotice("from %s: %s", parv[0], message);
+    return 0;
+}
+
+int 
+m_globops(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       *message = parc > 1 ? parv[1] : NULL;
+
+    /* a few changes, servers weren't able to globop -mjs */
+
+    if (check_registered(sptr))
+        return 0;
+
+    if (BadPtr(message)) 
+    {
+        if (MyClient(sptr))
+            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                       me.name, parv[0], "GLOBOPS");
+        return 0;
+    }
+
+    /* must be a client, must be an oper or a Ulined server -mjs */
+
+    if (MyClient(sptr) && !OPCanGlobOps(sptr) && !IsULine(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+    if (strlen(message) > TOPICLEN)
+        message[TOPICLEN] = '\0';
+    sendto_serv_butone_services(IsServer(cptr) ? cptr : NULL, ":%s GLOBOPS :%s",
+                       parv[0], message);
+    send_globops("from %s: %s", parv[0], message);
+    return 0;
+}
+
+int 
+m_chatops(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       *message = parc > 1 ? parv[1] : NULL;
+
+    if (check_registered(sptr))
+        return 0;
+    if (BadPtr(message)) 
+    {
+        if (MyClient(sptr))
+            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                       me.name, parv[0], "CHATOPS");
+        return 0;
+    }
+
+    if (MyClient(sptr) && (!IsAnOper(sptr) || !SendChatops(sptr)) &&
+        !IsULine(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+    
+    if (strlen(message) > TOPICLEN)
+        message[TOPICLEN] = '\0';
+    sendto_serv_butone_services(IsServer(cptr) ? cptr : NULL, ":%s CHATOPS :%s",
+                       parv[0], message);
+    send_chatops("from %s: %s", parv[0], message);
+    return 0;
+}
+
+/*
+ * * m_time 
+ *       parv[0] = sender prefix 
+ *       parv[1] = servername
+ */
+
+int 
+m_time(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    if (hunt_server(cptr, sptr, ":%s TIME :%s", 1, parc, parv) == HUNTED_ISME)
+        sendto_one(sptr, rpl_str(RPL_TIME), me.name,
+                   parv[0], me.name, date((long) 0));
+    return 0;
+}
+
+/*
+ * * m_admin 
+ *        parv[0] = sender prefix 
+ *        parv[1] = servername
+ */
+int 
+m_admin(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    
+    if (hunt_server(cptr, sptr, ":%s ADMIN :%s", 1, parc, parv) != HUNTED_ISME)
+        return 0;
+    
+    if (IsPerson(sptr))
+        sendto_realops_lev(SPY_LEV, "ADMIN requested by %s (%s@%s) [%s]",
+                           sptr->name, sptr->user->username, sptr->user->host,
+                           sptr->user->server);
+    
+        sendto_one(sptr, rpl_str(RPL_ADMINME),
+                   me.name, parv[0], me.name);
+        sendto_one(sptr, rpl_str(RPL_ADMINLOC1),
+                   me.name, parv[0], MeLine->admin[0] ? MeLine->admin[0] : "");
+        sendto_one(sptr, rpl_str(RPL_ADMINLOC2),
+                   me.name, parv[0], MeLine->admin[1] ? MeLine->admin[1] : "");
+        sendto_one(sptr, rpl_str(RPL_ADMINEMAIL),
+                   me.name, parv[0], MeLine->admin[2] ? MeLine->admin[2] : "");
+    return 0;
+}
+
+/* Shadowfax's server side, anti flood code */
+#ifdef FLUD
+extern int  flud_num;
+extern int  flud_time;
+extern int  flud_block;
+#endif
+
+#ifdef ANTI_SPAMBOT
+extern int  spam_num;
+extern int  spam_time;
+#endif
+
+/* m_set - set options while running */
+int 
+m_set(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       *command;
+
+    if (!MyClient(sptr) || !IsOper(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    if (parc > 1) 
+    {
+        command = parv[1];
+        if (!strncasecmp(command, "MAX", 3)) 
+        {
+            if (parc > 2) 
+            {
+                int new_value = atoi(parv[2]);
+
+                if (new_value > MAX_ACTIVECONN) 
+                {
+                    sendto_one(sptr,
+                               ":%s NOTICE %s :You cannot set MAXCLIENTS "
+                               "above the compiled FD limit (%d)", me.name,
+                               parv[0], MAX_ACTIVECONN);
+                    return 0;
+                }
+                if (new_value < 0)
+                    new_value = 0;
+                MAXCLIENTS = new_value;
+                sendto_one(sptr, ":%s NOTICE %s :NEW MAXCLIENTS = %d (Current "
+                           "= %d)", me.name, parv[0], MAXCLIENTS, Count.local);
+                sendto_realops("%s!%s@%s set new MAXCLIENTS to %d "
+                               "(%d current)", parv[0], sptr->user->username,
+                               sptr->sockhost, MAXCLIENTS, Count.local);
+                return 0;
+            }
+            sendto_one(sptr, ":%s NOTICE %s :MAXCLIENTS is %d (%d online)",
+                       me.name, parv[0], MAXCLIENTS, Count.local);
+            return 0;
+        }
+#ifdef FLUD
+        else if (!strncasecmp(command, "FLUDNUM", 7)) 
+        {
+            if (parc > 2) 
+            {
+                int newval = atoi(parv[2]);
+                if (newval <= 0) 
+                {
+                    sendto_one(sptr, ":%s NOTICE %s :flud NUM must be > 0",
+                               me.name, parv[0]);
+                    return 0;
+                }
+                flud_num = newval;
+                sendto_ops("%s has changed flud NUM to %i", parv[0], flud_num);
+                sendto_one(sptr, ":%s NOTICE %s :flud NUM is now set to %i",
+                           me.name, parv[0], flud_num);
+                return 0;
+            }
+            else
+            {
+                sendto_one(sptr, ":%s NOTICE %s :flud NUM is currently %i",
+                           me.name, parv[0], flud_num);
+                return 0;
+            }
+        }
+        else if (!strncasecmp(command, "FLUDTIME", 8)) 
+        {
+            if (parc > 2) 
+            {
+                int newval = atoi(parv[2]);
+                if (newval <= 0) 
+                {
+                    sendto_one(sptr, ":%s NOTICE %s :flud TIME must be > 0",
+                               me.name, parv[0]);
+                    return 0;
+                }
+                flud_time = newval;
+                sendto_ops("%s has changed flud TIME to %i", parv[0],
+                           flud_time);
+                sendto_one(sptr, ":%s NOTICE %s :flud TIME is now set to %i",
+                           me.name, parv[0], flud_time);
+                return 0;
+            }
+            else 
+            {
+                sendto_one(sptr, ":%s NOTICE %s :flud TIME is currently %i",
+                           me.name, parv[0], flud_time);
+                return 0;
+            }
+        }
+        else if (!strncasecmp(command, "FLUDBLOCK", 9)) 
+        {
+            if (parc > 2) 
+            {
+                int newval = atoi(parv[2]);
+                if (newval < 0) 
+                {
+                    sendto_one(sptr, ":%s NOTICE %s :flud BLOCK must be >= 0",
+                               me.name, parv[0]);
+                    return 0;
+                }
+                flud_block = newval;
+                if (flud_block == 0)
+                {
+                    sendto_ops("%s has disabled flud detection/protection",
+                               parv[0]);
+                    sendto_one(sptr, ":%s NOTICE %s :flud detection disabled",
+                               me.name, parv[0]);
+                }
+                else
+                {
+                    sendto_ops("%s has changed flud BLOCK to %i",
+                               parv[0], flud_block);
+                    sendto_one(sptr, ":%s NOTICE %s :flud BLOCK is now set "
+                               "to %i", me.name, parv[0], flud_block);
+                }
+                return 0;
+            }
+            else
+            {
+                sendto_one(sptr, ":%s NOTICE %s :flud BLOCK is currently %i",
+                           me.name, parv[0], flud_block);
+                return 0;
+            }
+        }
+#endif
+#ifdef ANTI_SPAMBOT
+        /* int spam_time = MIN_JOIN_LEAVE_TIME; 
+         * int spam_num = MAX_JOIN_LEAVE_COUNT;
+         */
+        else if (!strncasecmp(command, "SPAMNUM", 7)) 
+        {
+            if (parc > 2) 
+            {
+                int newval = atoi(parv[2]);
+                if (newval <= 0) 
+                {
+                    sendto_one(sptr, ":%s NOTICE %s :spam NUM must be > 0",
+                               me.name, parv[0]);
+                    return 0;
+                }
+                if (newval < MIN_SPAM_NUM)
+                    spam_num = MIN_SPAM_NUM;
+                else
+                    spam_num = newval;
+                sendto_ops("%s has changed spam NUM to %i", parv[0], spam_num);
+                sendto_one(sptr, ":%s NOTICE %s :spam NUM is now set to %i",
+                           me.name, parv[0], spam_num);
+                return 0;
+            }
+            else 
+            {
+                sendto_one(sptr, ":%s NOTICE %s :spam NUM is currently %i",
+                           me.name, parv[0], spam_num);
+                return 0;
+            }
+        }
+        else if (!strncasecmp(command, "SPAMTIME", 8)) 
+        {
+            if (parc > 2) 
+            {
+                int newval = atoi(parv[2]);
+                if (newval <= 0) 
+                {
+                    sendto_one(sptr, ":%s NOTICE %s :spam TIME must be > 0",
+                               me.name, parv[0]);
+                    return 0;
+                }
+                if (newval < MIN_SPAM_TIME)
+                    spam_time = MIN_SPAM_TIME;
+                else
+                    spam_time = newval;
+                sendto_ops("%s has changed spam TIME to %i", parv[0],
+                           spam_time);
+                sendto_one(sptr, ":%s NOTICE %s :SPAM TIME is now set to %i",
+                           me.name, parv[0], spam_time);
+                return 0;
+            }
+            else 
+            {
+                sendto_one(sptr, ":%s NOTICE %s :spam TIME is currently %i",
+                           me.name, parv[0], spam_time);
+                return 0;
+            }
+        }
+
+#endif
+#ifdef THROTTLE_ENABLE
+        else if (!strncasecmp(command, "THROTTLE", 8))  
+        {
+           char *changed = NULL;
+           char *to = NULL;
+           /* several values available:
+            * ENABLE [on|off] to enable the code
+            * COUNT [n] to set a max count, must be > 1
+            * TIME [n] to set a max time before expiry, must be > 5
+            * RECORDTIME [n] to set a time for the throttle records to expire
+            * HASH [n] to set the size of the hash table, must be bigger than
+            *          the default */
+
+
+           /* only handle individual settings if parc > 3 (they're actually
+            * changing stuff) */
+           if (parc > 3) {
+               if (!strcasecmp(parv[2], "ENABLE"))  {
+                  changed = "ENABLE";
+                  if (ToLower(*parv[3]) == 'y' || !strcasecmp(parv[3], "on")) {
+                     throttle_enable = 1;
+                     to = "ON";
+                  } else if (ToLower(*parv[3]) == 'n' ||
+                           !strcasecmp(parv[3], "off")) {
+                     throttle_enable = 0;
+                     to = "OFF";
+                  }
+               } else if (!strcasecmp(parv[2], "COUNT")) {
+                  int cnt;
+                  changed = "COUNT";
+                  cnt = atoi(parv[3]);
+                  if (cnt > 1) {
+                     throttle_tcount = cnt;
+                     to = parv[3];
+                  }
+               } else if (!strcasecmp(parv[2], "TIME")) {
+                  int cnt;
+                  changed = "TIME";
+                  cnt = atoi(parv[3]);
+                  if (cnt >= 5) {
+                     throttle_ttime = cnt;
+                     to = parv[3];
+                  } 
+               } else if (!strcasecmp(parv[2], "RECORDTIME")) {
+                  int cnt;
+                  changed = "RECORDTIME";
+                  cnt = atoi(parv[3]);
+                  if (cnt >= 30) {
+                     throttle_rtime = cnt;
+                     to = parv[3];
+                  }
+               } else if (!strcasecmp(parv[2], "HASH")) {
+                  int cnt;
+                  changed = "HASH";
+                  cnt = atoi(parv[3]);
+                  if (cnt >= THROTTLE_HASHSIZE) {
+                     throttle_resize(cnt);
+                     to = parv[3];
+                  }
+               }
+
+               if (to != NULL) {
+                  sendto_ops("%s has changed throttle %s to %s", parv[0],
+                        changed, to);
+                  sendto_one(sptr, ":%s NOTICE %s :set throttle %s to %s",
+                        me.name, parv[0], changed, to);
+               }
+           } else {
+              /* report various things, we cannot easily get the hash size, so
+               * leave that alone. */
+              sendto_one(sptr, ":%s NOTICE %s :THROTTLE %s", me.name, parv[0],
+                    throttle_enable ? "enabled" : "disabled");
+              sendto_one(sptr, ":%s NOTICE %s :THROTTLE COUNT=%d", me.name,
+                    parv[0], throttle_tcount);
+              sendto_one(sptr, ":%s NOTICE %s :THROTTLE TIME=%d sec", me.name,
+                    parv[0], throttle_ttime);
+              sendto_one(sptr, ":%s NOTICE %s :THROTTLE RECORDTIME=%d sec", me.name,
+                    parv[0], throttle_rtime);
+           }
+        }
+        else if (!strncasecmp(command, "LCLONES", 3))
+        {
+            if (parc > 3)
+            {
+                int limit, rval;
+
+                limit = atoi(parv[3]);
+                rval = clones_set(parv[2], CLIM_SOFT_LOCAL, limit);
+
+                if (rval < 0)
+                    sendto_one(sptr, ":%s NOTICE %s :Invalid IP or limit.",
+                               me.name, parv[0]);
+                else if (rval > 0 && limit == 0)
+                {
+                    sendto_ops("%s removed soft local clone limit for %s",
+                               parv[0], parv[2]);
+                    sendto_one(sptr, ":%s NOTICE %s :removed soft local clone"
+                               " limit for %s", me.name, parv[0], parv[2]);
+                }
+                else if (rval > 0)
+                {
+                    sendto_ops("%s changed soft local clone limit for %s from"
+                               " %d to %d", parv[0], parv[2], rval, limit);
+                    sendto_one(sptr, ":%s NOTICE %s :changed soft local clone"
+                               " limit for %s from %d to %d", me.name, parv[0],
+                               parv[2], rval, limit);
+                }
+                else if (limit == 0)
+                {
+                    sendto_one(sptr, "%s NOTICE %s :no soft local clone limit"
+                               " for %s", me.name, parv[0], parv[2]);
+                }
+                else
+                {
+                    sendto_ops("%s set soft local clone limit for %s to %d",
+                               parv[0], parv[2], limit);
+                    sendto_one(sptr, ":%s NOTICE %s :set soft local clone"
+                               " limit for %s to %d", me.name, parv[0],
+                               parv[2], limit);
+                }
+            }
+            else if (parc > 2)
+            {
+                int hglimit, sglimit, sllimit;
+
+                clones_get(parv[2], &hglimit, &sglimit, &sllimit);
+
+                if (!sllimit)
+                    sendto_one(sptr, ":%s NOTICE %s :no soft local clone limit"
+                               " for %s", me.name, parv[0], parv[2]);
+                else
+                    sendto_one(sptr, ":%s NOTICE %s :soft local clone limit"
+                               " for %s is %d", me.name, parv[0], parv[2],
+                               sllimit);
+            }
+            else
+                sendto_one(sptr, ":%s NOTICE %s :Usage: LCLONES <ip> [<limit>]",
+                           me.name, parv[0]);
+        }
+        else if (!strncasecmp(command, "GCLONES", 3))
+        {
+            int hglimit, sglimit, sllimit, limit, rval;
+
+            if (parc > 3)
+            {
+                limit = atoi(parv[3]);
+                clones_get(parv[2], &hglimit, &sglimit, &sllimit);
+
+                if (hglimit && limit > hglimit)
+                    sendto_one(sptr, ":%s NOTICE %s :Cannot set soft global"
+                               " clone limit for %s above services-set hard"
+                               " limit (%d)", me.name, parv[0], parv[2],
+                               hglimit);
+                else
+                {
+                    rval = clones_set(parv[2], CLIM_SOFT_GLOBAL, limit);
+
+                    if (rval < 0)
+                        sendto_one(sptr, ":%s NOTICE %s :Invalid IP or limit.",
+                                   me.name, parv[0]);
+                    else if (rval > 0 && limit == 0)
+                    {
+                        sendto_ops("%s removed soft global clone limit for %s",
+                                   parv[0], parv[2]);
+                        sendto_one(sptr, ":%s NOTICE %s :removed soft global"
+                                   " clone limit for %s", me.name, parv[0],
+                                   parv[2]);
+                    }
+                    else if (rval > 0)
+                    {
+                        sendto_ops("%s changed soft global clone limit for %s"
+                                   " from %d to %d", parv[0], parv[2], rval,
+                                   limit);
+                        sendto_one(sptr, ":%s NOTICE %s :changed soft global"
+                                   " clone limit for %s from %d to %d",
+                                   me.name, parv[0], parv[2], rval, limit);
+                    }
+                    else if (limit == 0)
+                    {
+                        sendto_one(sptr, ":%s NOTICE %s :no soft global clone"
+                                   " limit for %s", me.name, parv[0], parv[2]);
+                    }
+                    else
+                    {
+                        sendto_ops("%s set soft global clone limit for %s to"
+                                   " %d", parv[0], parv[2], limit);
+                        sendto_one(sptr, ":%s NOTICE %s :set soft global clone"
+                                   " limit for %s to %d", me.name, parv[0],
+                                   parv[2], limit);
+                    }
+                }
+            }
+            else if (parc > 2)
+            {
+                clones_get(parv[2], &hglimit, &sglimit, &sllimit);
+
+                if (sglimit)
+                    sendto_one(sptr, ":%s NOTICE %s :soft global clone limit"
+                               " for %s is %d", me.name, parv[0], parv[2],
+                               sglimit);
+                else
+                    sendto_one(sptr, ":%s NOTICE %s :no soft global clone"
+                               " limit for %s", me.name, parv[0], parv[2]);
+
+                if (hglimit)
+                    sendto_one(sptr, ":%s NOTICE %s :hard global clone limit"
+                               " for %s is %d", me.name, parv[0], parv[2],
+                               hglimit);
+            }
+            else
+                sendto_one(sptr, ":%s NOTICE %s :Usage: GCLONES <ip> [<limit>]",
+                           me.name, parv[0]);
+        }
+        else if (!strncasecmp(command, "DEFLCLONE", 6))
+        {
+            char *eptr;
+
+            if (parc > 2)
+            {
+                local_ip_limit = strtol(parv[2], &eptr, 10);
+                if (*eptr != 0)
+                    local_ip24_limit = atoi(eptr+1);
+
+                if (local_ip_limit < 1)
+                    local_ip_limit = DEFAULT_LOCAL_IP_CLONES;
+                if (local_ip24_limit < 1)
+                    local_ip24_limit = DEFAULT_LOCAL_IP24_CLONES;
+
+                sendto_ops("%s set default local clone limit to %d:%d"
+                           " (host:site)", parv[0], local_ip_limit,
+                           local_ip24_limit);
+                sendto_one(sptr, ":%s NOTICE %s :set default local clone limit"
+                           " to %d:%d (host:site)", me.name, parv[0],
+                           local_ip_limit, local_ip24_limit);
+            }
+            else
+                sendto_one(sptr, ":%s NOTICE %s :default local clone limit is"
+                           " %d:%d (host:site)", me.name, parv[0],
+                           local_ip_limit, local_ip24_limit);
+        }
+        else if (!strncasecmp(command, "DEFGCLONE", 6))
+        {
+            char *eptr;
+
+            if (parc > 2)
+            {
+                global_ip_limit = strtol(parv[2], &eptr, 10);
+                if (*eptr != 0)
+                    global_ip24_limit = atoi(eptr+1);
+
+                if (global_ip_limit < 1)
+                    global_ip_limit = DEFAULT_GLOBAL_IP_CLONES;
+                if (global_ip24_limit < 1)
+                    global_ip24_limit = DEFAULT_GLOBAL_IP24_CLONES;
+
+                sendto_ops("%s set default global clone limit to %d:%d"
+                           " (host:site)", parv[0], global_ip_limit,
+                           global_ip24_limit);
+                sendto_one(sptr, ":%s NOTICE %s :set default global clone"
+                           " limit to %d:%d (host:site)", me.name, parv[0],
+                           global_ip_limit, global_ip24_limit);
+            }
+            else
+                sendto_one(sptr, ":%s NOTICE %s :default global clone limit is"
+                           " %d:%d (host:site)", me.name, parv[0],
+                           global_ip_limit, global_ip24_limit);
+        }
+#endif
+    }
+    else 
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Options: MAX",
+                   me.name, parv[0]);
+#ifdef FLUD
+        sendto_one(sptr, ":%s NOTICE %s :Options: FLUDNUM, FLUDTIME, "
+                   "FLUDBLOCK", me.name, parv[0]);
+#endif
+
+#ifdef ANTI_SPAMBOT
+        sendto_one(sptr, ":%s NOTICE %s :Options: SPAMNUM, SPAMTIME",
+                   me.name, parv[0]);
+#endif
+
+#ifdef THROTTLE_ENABLE
+        sendto_one(sptr, ":%s NOTICE %s :Options: THROTTLE "
+              "<ENABLE|COUNT|TIME|RECORDTIME|HASH> [setting]", me.name, parv[0]);
+        sendto_one(sptr, ":%s NOTICE %s :Options: LCLONES, GCLONES, "
+                   "DEFLCLONES, DEFGCLONES", me.name, parv[0]);
+#endif
+    }
+    return 0;
+}
+
+/*
+ * cluster() input            
+ * - pointer to a hostname output 
+ * pointer to a static of the hostname masked for use in a kline. side 
+ * effects - NONE
+ * 
+ * reworked a tad -Dianora
+ */
+
+static char *cluster(char *hostname)
+{
+    static char result[HOSTLEN + 1];    /* result to return */
+    char        temphost[HOSTLEN + 1];  /* workplace */
+    char       *ipp;            /* used to find if host is ip # only */
+    char       *host_mask;      /* used to find host mask portion to '*' */
+    /* used to zap last nnn portion of an ip # */
+    char       *zap_point = (char *) NULL; 
+    char       *tld;            /* Top Level Domain */
+    int         is_ip_number;   /* flag if its an IP # */
+    int         number_of_dots; /* count # of dots for ip# and domain klines */
+
+    if (!hostname)
+        return (char *) NULL;   /* EEK! */
+
+    /*
+     * If a '@' is found in the hostname, this is bogus and must have
+     * been introduced by server that doesn't check for bogus domains
+     * (dns spoof) very well. *sigh* just return it... I could also
+     * legitimately return (char *)NULL as above.
+     * 
+     * -Dianora
+     */
+
+    if (strchr(hostname, '@')) 
+    {
+        strncpyzt(result, hostname, HOSTLEN);
+        return (result);
+    }
+
+    strncpyzt(temphost, hostname, HOSTLEN);
+
+    is_ip_number = YES;         /* assume its an IP# */
+    ipp = temphost;
+    number_of_dots = 0;
+
+    while (*ipp) 
+    {
+        if (*ipp == '.') 
+        {
+            number_of_dots++;
+            if (number_of_dots == 3)
+                zap_point = ipp;
+            ipp++;
+        }
+        else if (!IsDigit(*ipp)) 
+        {
+            is_ip_number = NO;
+            break;
+        }
+        ipp++;
+    }
+
+    if (is_ip_number && (number_of_dots == 3)) 
+    {
+        zap_point++;
+        *zap_point++ = '*';     /* turn 111.222.333.444 into ... */
+        *zap_point = '\0';      /* 111.222.333.* */
+        strncpy(result, temphost, HOSTLEN);
+        return (result);
+    }
+    else 
+    {
+        tld = strrchr(temphost, '.');
+        if (tld) 
+        {
+            number_of_dots = 2;
+            if (tld[3])                 /* its at least a 3 letter tld */
+                number_of_dots = 1;
+            if (tld != temphost)        /* in these days of dns spoofers ... */
+                host_mask = tld - 1;    /* Look for host portion to '*' */
+            else
+                host_mask = tld;  /* degenerate case hostname is '.com' ect. */
+            
+            while (host_mask != temphost) 
+            {
+                if (*host_mask == '.')
+                    number_of_dots--;
+                if (number_of_dots == 0) 
+                {
+                    result[0] = '*';
+                    strncpy(result + 1, host_mask, HOSTLEN - 1);
+                    return (result);
+                }
+                host_mask--;
+            }
+            result[0] = '*';    /* foo.com => *foo.com */
+            strncpy(result + 1, temphost, HOSTLEN);
+        }
+        else
+        {               /*  no tld found oops. just return it as is */
+            strncpy(result, temphost, HOSTLEN);
+            return (result);
+        }
+    }
+
+    return (result);
+}
+
+int m_kline(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    struct userBan *ban, *oban;
+#if defined (LOCKFILE)
+    struct pkl *k;
+#else
+    int         out;
+#endif
+    
+    char        buffer[1024];
+
+    char       *filename;       /* filename to use for kline */
+    char       *user, *host;
+    char       *reason;
+    char       *current_date;
+    aClient    *acptr;
+    char        tempuser[USERLEN + 2];
+    char        temphost[HOSTLEN + 1];
+    int         temporary_kline_time = 0;       /* -Dianora */
+    time_t      temporary_kline_time_seconds = 0;
+    int         time_specified = 0;
+    char       *argv;
+    int         i;
+    char       fbuf[512];
+
+    if (!MyClient(sptr) || !IsAnOper(sptr) || !OPCanKline(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    if (parc < 2) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "KLINE");
+        return 0;
+    }
+
+    argv = parv[1];
+
+    if ((temporary_kline_time = isnumber(argv)) >= 0) 
+    {
+        if (parc < 3) 
+        {
+            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                       me.name, parv[0], "KLINE");
+            return 0;
+        }
+        if (temporary_kline_time > (24 * 60 * 7))
+            temporary_kline_time = (24 * 60 * 7);       /*  Max it at 1 week */
+
+        temporary_kline_time_seconds = 
+            (time_t) temporary_kline_time *(time_t) 60;
+        
+        /* turn it into minutes */
+        argv = parv[2];
+        parc--;
+        time_specified = 1;
+    }
+    else
+    {
+        temporary_kline_time = 0; /* -1 minute klines are bad... :) - lucas */
+    }
+    
+    if(strchr(argv, ' '))
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Poorly formatted hostname "
+                         "(contains spaces). Be sure you are using the form: "
+                         "/quote KLINE [time] <user@host/nick> :<reason>",
+                         me.name, parv[0]);
+        return 0;
+    }
+    
+
+    if ((host = strchr(argv, '@')) || *argv == '*') 
+    {
+        /* Explicit user@host mask given */
+
+        if (host)               /* Found user@host */
+        {
+            user = argv;                /* here is user part */
+            *(host++) = '\0';   /* and now here is host */
+        }
+        else 
+        {
+            user = "*";         /* no @ found, assume its *@somehost */
+            host = argv;
+        }
+        
+        if (!*host)             /* duh. no host found, assume its '*' host */
+            host = "*";
+        strncpyzt(tempuser, user, USERLEN + 2); /* allow for '*' in front */
+        strncpyzt(temphost, host, HOSTLEN);
+        user = tempuser;
+        host = temphost;
+    }
+    else 
+    {
+        /* Try to find user@host mask from nick */
+        
+        if (!(acptr = find_chasing(sptr, argv, NULL)))
+            return 0;
+
+        if (!acptr->user)
+            return 0;
+
+        if (IsServer(acptr)) 
+        {
+            sendto_one(sptr, ":%s NOTICE %s :Can't KLINE a server, use @'s "
+                       "where appropriate", me.name, parv[0]);
+            return 0;
+        }
+        /*
+         * turn the "user" bit into "*user", blow away '~' if found in
+         * original user name (non-idented)
+         */
+
+        tempuser[0] = '*';
+        if (*acptr->user->username == '~')
+            strcpy(tempuser + 1, (char *) acptr->user->username + 1);
+        else
+            strcpy(tempuser + 1, acptr->user->username);
+        user = tempuser;
+        host = cluster(acptr->user->host);
+    }
+
+    if (time_specified)
+        argv = parv[3];
+    else
+        argv = parv[2];
+
+#ifdef DEFAULT_KLINE_TIME
+    if (time_specified == 0)
+    {
+        temporary_kline_time = DEFAULT_KLINE_TIME;
+        temporary_kline_time_seconds =
+            (time_t) temporary_kline_time *(time_t) 60;
+    }
+#endif
+
+    if (parc > 2) 
+    {
+        if (*argv)
+            reason = argv;
+        else
+            reason = "No reason";
+    }
+    else
+        reason = "No reason";
+
+    if (!match(user, "akjhfkahfasfjd") &&
+        !match(host, "ldksjfl.kss...kdjfd.jfklsjf")) 
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Can't K-Line *@*", me.name,
+                   parv[0]);
+        return 0;
+    }
+
+    /* we can put whatever we want in temp K: lines */
+    if (temporary_kline_time == 0 && strchr(reason, ':')) 
+    {
+        sendto_one(sptr,
+                   ":%s NOTICE %s :Invalid character ':' in comment",
+                   me.name, parv[0]);
+        return 0;
+    }
+
+    if (temporary_kline_time == 0 && strchr(reason, '#')) 
+    {
+        sendto_one(sptr,
+                   ":%s NOTICE %s :Invalid character '#' in comment",
+                   me.name, parv[0]);
+        return 0;
+    }
+
+    ban = make_hostbased_ban(user, host);
+    if(!ban)
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Malformed ban %s@%s", me.name, parv[0],
+                   user, host);
+        return 0;
+    }
+
+    if ((oban = find_userban_exact(ban, 0)))
+    {
+        char *ktype = (oban->flags & UBAN_LOCAL) ? 
+                      LOCAL_BANNED_NAME : NETWORK_BANNED_NAME;
+
+        sendto_one(sptr, ":%s NOTICE %s :[%s@%s] already %s for %s",
+                   me.name, parv[0], user, host, ktype, 
+                   oban->reason ? oban->reason : "<No Reason>");
+
+        userban_free(ban);
+        return 0;
+    }
+
+    current_date = smalldate((time_t) 0);
+    ircsprintf(buffer, "%s (%s)", reason, current_date);
+
+    ban->flags |= UBAN_LOCAL;
+    ban->reason = (char *) MyMalloc(strlen(buffer) + 1);
+    strcpy(ban->reason, buffer);
+    
+    if (temporary_kline_time) 
+    {
+        ban->flags |= UBAN_TEMPORARY;
+        ban->timeset = timeofday;
+        ban->duration = temporary_kline_time_seconds;
+    }
+
+    if(user_match_ban(sptr, ban))
+    {
+        sendto_one(sptr, ":%s NOTICE %s :You attempted to add a ban [%s@%s]"
+                         " which would affect yourself. Aborted.",
+                   me.name, parv[0], user, host);
+        userban_free(ban);
+        return 0;
+    }
+
+    add_hostbased_userban(ban);
+
+    /* Check local users against it */
+    for (i = 0; i <= highest_fd; i++)
+    {
+        if (!(acptr = local[i]) || IsMe(acptr) || IsLog(acptr))
+            continue;
+
+        if (IsPerson(acptr) && user_match_ban(acptr, ban))
+        {
+            sendto_ops(LOCAL_BAN_NAME" active for %s",
+                       get_client_name(acptr, FALSE));
+            ircsprintf(fbuf, LOCAL_BANNED_NAME": %s", reason);
+            exit_client(acptr, acptr, &me, fbuf);
+            i--;
+        }
+    }
+
+    host = get_userban_host(ban, fbuf, 512);
+
+    if(temporary_kline_time)
+    {   
+        sendto_realops("%s added temporary %d min. "LOCAL_BAN_NAME" for"
+                       " [%s@%s] [%s]", parv[0], temporary_kline_time, user, 
+                       host, reason);
+        return 0;
+    }
+
+    /* from here on, we're dealing with a perm kline */
+
+    filename = configfile;
+
+    sendto_one(sptr, ":%s NOTICE %s :Added K-Line [%s@%s] to server "
+               "configfile", me.name, parv[0], user, host);
+
+    sendto_realops("%s added K-Line for [%s@%s] [%s]",
+                   parv[0], user, host, reason);
+    
+#if defined(LOCKFILE)
+    if ((k = (struct pkl *) malloc(sizeof(struct pkl))) == NULL) 
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Problem allocating memory",
+                   me.name, parv[0]);
+        return (0);
+    }
+
+    ircsprintf(buffer, "/* %s!%s@%s Added kill for: %s@%s\n"
+                       " * at %s */\n",
+               sptr->name, sptr->user->username,
+               sptr->user->host, user, host, current_date);
+
+    if ((k->comment = strdup(buffer)) == NULL) 
+    {
+        MyFree(k);
+        sendto_one(sptr, ":%s NOTICE %s :Problem allocating memory",
+                   me.name, parv[0]);
+        return (0);
+    }
+
+    ircsprintf(buffer, "kill {\n"
+                       "    mask \"%s@%s\";\n"
+                       "    reason \"%s\";\n"
+                       "};\n\n",
+                      user, host, reason);
+
+    if ((k->kline = strdup(buffer)) == NULL) 
+    {
+        MyFree(k->comment);
+        MyFree(k);
+        sendto_one(sptr, ":%s NOTICE %s :Problem allocating memory",
+                   me.name, parv[0]);
+        return (0);
+    }
+    k->next = pending_klines;
+    pending_klines = k;
+
+    do_pending_klines();
+    return (0);
+
+#else /*  LOCKFILE - MDP */
+
+    if ((out = open(filename, O_RDWR | O_APPEND | O_CREAT)) == -1) 
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Problem opening %s ",
+                   me.name, parv[0], filename);
+        return 0;
+    }
+
+    ircsprintf(buffer, "/* %s!%s@%s Added kill for: %s@%s\n"
+                       " * at %s */\n",
+               sptr->name, sptr->user->username,
+               sptr->user->host, user, host, current_date);
+
+    if (write(out, buffer, strlen(buffer)) <= 0) 
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Problem writing to %s",
+                   me.name, parv[0], filename);
+        close(out);
+        return 0;
+    }
+
+    ircsprintf(buffer, "kill {\n"
+                       "    mask \"%s@%s\";\n"
+                       "    reason \"%s\";\n"
+                       "};\n\n",
+                      user, host, reason);
+
+    if (write(out, buffer, strlen(buffer)) <= 0) 
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Problem writing to %s",
+                   me.name, parv[0], filename);
+        close(out);
+        return 0;
+    }
+
+    close(out);
+
+#ifdef USE_SYSLOG
+    syslog(LOG_NOTICE, "%s added K-Line for [%s@%s] [%s]", parv[0],
+           user, host, reason);
+#endif
+
+    return 0;
+#endif /* LOCKFILE */
+}
+
+/*
+ * isnumber()
+ * 
+ * inputs               
+ * - pointer to ascii string in output             
+ * - 0 if not an integer number, else the number side effects  
+ * - none return -1 if not an integer. 
+ * (if someone types in maxint, oh well..) - lucas
+ */
+
+static int isnumber(char *p)
+{
+    int         result = 0;
+
+    while (*p) 
+    {
+        if (IsDigit(*p)) 
+        {
+            result *= 10;
+            result += ((*p) & 0xF);
+            p++;
+        }
+        else
+            return (-1);
+    }
+    /*
+     * in the degenerate case where oper does a /quote kline 0 user@host
+     * :reason i.e. they specifically use 0, I am going to return 1
+     * instead as a return value of non-zero is used to flag it as a
+     * temporary kline
+     */
+
+    /*
+     * er, no. we return 0 because 0 means that it's a permanent kline. -lucas
+     * oh, and we only do this if DEFAULT_KLINE_TIME is specified.
+     */
+
+#ifndef DEFAULT_KLINE_TIME
+    if(result == 0)
+        result = 1;
+#endif
+    
+    return (result);
+}
+
+#ifdef UNKLINE
+/*
+ * * m_unkline 
+ * Added Aug 31, 1997 
+ * common (Keith Fralick) fralick@gate.net 
+ * 
+ *      parv[0] = sender 
+ *      parv[1] = address to remove
+ * 
+ * re-worked and cleanedup for use in hybrid-5 -Dianora
+ * 
+ */
+int m_unkline(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    struct userBan *ban;
+    char       *user, *host;
+
+    if (!IsAnOper(sptr) || !OPCanUnKline(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    if (parc < 2) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "UNKLINE");
+        return 0;
+    }
+
+    if ((host = strchr(parv[1], '@')) || *parv[1] == '*') 
+    {
+        /* Explicit user@host mask given */
+
+        if (host)       /* Found user@host */
+        {
+            user = parv[1];     /* here is user part */
+            *(host++) = '\0';   /* and now here is host */
+        }
+        else 
+        {
+            user = "*";         /* no @ found, assume its *@somehost */
+            host = parv[1];
+        }
+    }
+    else
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Invalid parameters",
+                   me.name, parv[0]);
+        return 0;
+    }
+
+    if ((user[0] == '*') && (user[1] == '\0') && (host[0] == '*') &&
+        (host[1] == '\0')) 
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Cannot UNK-Line everyone",
+                   me.name, parv[0]);
+        return 0;
+    }
+
+    ban = make_hostbased_ban(user, host);
+    if(ban)
+    {
+        struct userBan *oban;
+
+        ban->flags |= (UBAN_LOCAL|UBAN_TEMPORARY);
+        if((oban = find_userban_exact(ban, UBAN_LOCAL|UBAN_TEMPORARY)))
+        {
+            char tmp[512];
+
+            host = get_userban_host(oban, tmp, 512);
+
+            remove_userban(oban);
+            userban_free(oban);
+            userban_free(ban);
+
+            sendto_one(sptr, ":%s NOTICE %s :K-Line for [%s@%s] is removed",
+                       me.name, parv[0], user, host);
+            sendto_ops("%s has removed the K-Line for: [%s@%s] (%d matches)",
+                       parv[0], user, host, 1);
+
+            return 0;
+        }
+        userban_free(ban);
+    }    
+    sendto_one(sptr, ":%s NOTICE %s :No Kline matches [%s@%s]",
+               me.name, parv[0], user, host);
+    return 0;
+}
+
+#endif /* UNKLINE */
+
+/* m_rehash */
+int 
+m_rehash(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    if (!OPCanRehash(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+        
+    if (parc > 1) 
+    {
+        if (mycmp(parv[1], "DNS") == 0) 
+        {
+            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0], "DNS");
+            flush_cache();              /* flush the dns cache */
+            res_init();         /* re-read /etc/resolv.conf */
+            sendto_ops("%s is rehashing DNS while whistling innocently",
+                       parv[0]);
+            return 0;
+        }
+        else if (mycmp(parv[1], "TKLINES") == 0)
+        {
+            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
+                       "temp klines");
+            remove_userbans_match_flags(UBAN_LOCAL|UBAN_TEMPORARY, 0);
+            sendto_ops("%s is clearing temp klines while whistling innocently",
+                       parv[0]);
+            return 0;
+        }
+        else if (mycmp(parv[1], "GC") == 0) 
+        {
+            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
+                       "garbage collecting");
+            block_garbage_collect();
+            sendto_ops("%s is garbage collecting while whistling innocently",
+                       parv[0]);
+            return 0;
+        }
+        else if (mycmp(parv[1], "MOTD") == 0) 
+        {
+            sendto_ops("%s is forcing re-reading of MOTD file", parv[0]);
+            read_motd(MOTD);
+        if(confopts & FLAGS_SMOTD)
+                read_shortmotd(SHORTMOTD);
+            return (0);
+        }
+        else if(mycmp(parv[1], "AKILLS") == 0) 
+        {
+            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
+                       "akills");
+            remove_userbans_match_flags(UBAN_NETWORK, 0);
+            sendto_ops("%s is rehashing akills", parv[0]);
+            return 0;
+        }
+        else if(mycmp(parv[1], "THROTTLES") == 0) {
+            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
+                 "throttles");
+            throttle_rehash();
+            sendto_ops("%s is rehashing throttles", parv[0]);
+            return 0;
+        }
+        else if(mycmp(parv[1], "SQLINES") == 0) {
+            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
+                 "sqlines");
+            sendto_ops("%s is rehashing sqlines", parv[0]);
+            remove_simbans_match_flags(SBAN_NICK|SBAN_NETWORK, 0);
+            remove_simbans_match_flags(SBAN_CHAN|SBAN_NETWORK, 0);
+            return 0;
+        }
+        else if(mycmp(parv[1], "SGLINES") == 0) {
+            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
+                 "sglines");
+            sendto_ops("%s is rehashing sglines", parv[0]);
+            remove_simbans_match_flags(SBAN_GCOS|SBAN_NETWORK, 0);
+            return 0;
+        }
+        else if(mycmp(parv[1], "TSQGLINES") == 0) {
+            sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0],
+                 "tsqglines");
+            sendto_ops("%s is rehashing temporary sqlines/glines", parv[0]);
+            remove_simbans_match_flags(SBAN_GCOS|SBAN_TEMPORARY, 0);
+            remove_simbans_match_flags(SBAN_NICK|SBAN_TEMPORARY, 0);
+            remove_simbans_match_flags(SBAN_CHAN|SBAN_TEMPORARY, 0);
+            return 0;
+        }
+    }
+    else 
+    {
+        sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0], configfile);
+        sendto_ops("%s is rehashing Server config file while whistling "
+                   "innocently", parv[0]);
+# ifdef USE_SYSLOG
+        syslog(LOG_INFO, "REHASH From %s\n", get_client_name(sptr, FALSE));
+# endif
+        return rehash(cptr, sptr, 
+                      (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0);
+    }
+    return 0;                   /* shouldn't ever get here */
+}
+
+/* m_restart */
+int 
+m_restart(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char       *pass = NULL;
+    
+    if (!OPCanRestart(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+    /*
+     * m_restart is now password protected as in df465 only change --
+     * this one doesn't allow a reason to be specified. future changes:
+     * crypt()ing of password, reason to be re-added -mjs
+     */
+    if ((pass = MeLine->restartpass)) 
+    {
+        if (parc < 2) 
+        {
+            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+                       "RESTART");
+            return 0;
+        }
+        if (strcmp(pass, parv[1])) 
+        {
+            sendto_one(sptr, err_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
+            return 0;
+        }
+    }
+    
+#ifdef USE_SYSLOG
+    syslog(LOG_WARNING, "Server RESTART by %s\n",
+           get_client_name(sptr, FALSE));
+#endif
+    sprintf(buf, "Server RESTART by %s", get_client_name(sptr, TRUE));
+    restart(buf);
+    return 0;                   /* NOT REACHED */
+}
+
+/*
+ * * m_trace 
+ *        parv[0] = sender prefix 
+ *        parv[1] = servername
+ */
+int 
+m_trace(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int      i;
+    aClient *acptr=NULL;
+    aClass      *cltmp;
+    char        *tname;
+    int          doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
+    int          wilds = 0, dow = 0;
+        
+    tname = (parc > 1) ? parv[1] : me.name;
+
+#ifdef NO_USER_TRACE
+    if(!IsAnOper(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+#endif
+
+#ifdef HIDEULINEDSERVS
+    if((acptr = next_client_double(client, tname)))
+    {
+        if (!(IsAnOper(sptr)) && IsULine(acptr))
+        {
+            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            return 0;
+        }
+        acptr = NULL; /* shrug, we borrowed it, reset it just in case */
+    }
+#endif
+    
+    if (parc > 2)
+        if (hunt_server(cptr, sptr, ":%s TRACE %s :%s", 2, parc, parv))
+            return 0;
+
+    switch (hunt_server(cptr, sptr, ":%s TRACE :%s", 1, parc, parv)) 
+    {
+        case HUNTED_PASS:   /*  note: gets here only if parv[1] exists */
+        {
+            aClient    *ac2ptr = next_client_double(client, tname);
+            if (ac2ptr)
+                sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
+                           version, debugmode, tname, 
+                           ac2ptr->from->name);
+            else
+                sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
+                           version, debugmode, tname, 
+                           "ac2ptr_is_NULL!!");
+            return 0;
+        }
+        case HUNTED_ISME:
+            break;
+        default:
+            return 0;
+    }
+    if(!IsAnOper(sptr)) 
+    {
+        if (parv[1] && !strchr(parv[1],'.') && 
+            (strchr(parv[1], '*') || strchr(parv[1], '?'))) 
+            /* bzzzt, no wildcard nicks for nonopers */
+        {
+            sendto_one(sptr, rpl_str(RPL_ENDOFTRACE),me.name,
+                       parv[0], parv[1]);
+            return 0;        
+        }
+    }
+    sendto_realops_lev(SPY_LEV, "TRACE requested by %s (%s@%s) [%s]",
+                       sptr->name, sptr->user->username, sptr->user->host,
+                       sptr->user->server);
+        
+    doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : TRUE;
+    wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
+    dow = wilds || doall;
+    if(!IsAnOper(sptr) || !dow) /* non-oper traces must be full nicks */
+        /* lets also do this for opers tracing nicks */
+    {
+        char      *name, *class;
+        acptr = hash_find_client(tname,(aClient *)NULL);
+        if(!acptr || !IsPerson(acptr)) 
+        {
+            /* this should only be reached if the matching
+               target is this server */
+            sendto_one(sptr, rpl_str(RPL_ENDOFTRACE),me.name,
+                       parv[0], tname);
+            return 0;
+                          
+        }
+        if(acptr->class)
+            class = acptr->class->name;
+        else
+            class = "NONE";
+        name = get_client_name(acptr,FALSE);
+        if (IsAnOper(acptr)) 
+        {
+            sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
+                       me.name, parv[0], class, name,
+                       timeofday - acptr->lasttime);
+        }
+        else
+        {
+            sendto_one(sptr,rpl_str(RPL_TRACEUSER),
+                       me.name, parv[0], class, name,
+                       timeofday - acptr->lasttime);
+        }
+        sendto_one(sptr, rpl_str(RPL_ENDOFTRACE),me.name,
+                   parv[0], tname);
+        return 0;        
+    }
+
+    memset((char *) link_s, '\0', sizeof(link_s));
+    memset((char *) link_u, '\0', sizeof(link_u));
+    /* Count up all the servers and clients in a downlink. */
+    if (doall)
+        for (acptr = client; acptr; acptr = acptr->next) 
+        {
+            if (IsPerson(acptr) && (!IsInvisible(acptr) || IsAnOper(sptr)))
+                link_u[acptr->from->fd]++;
+            else if (IsServer(acptr))
+#ifdef HIDEULINEDSERVS
+                if (IsOper(sptr) || !IsULine(acptr))
+#endif
+                    link_s[acptr->from->fd]++;
+        }
+                
+        
+    /* report all direct connections */
+        
+    for (i = 0; i <= highest_fd; i++) 
+    {
+        char       *name, *class;
+                
+        if (!(acptr = local[i]))        /* Local Connection? */
+            continue;
+#ifdef HIDEULINEDSERVS
+        if (!IsOper(sptr) && IsULine(acptr))
+            continue;
+#endif
+        if (IsInvisible(acptr) && dow &&
+            !(MyConnect(sptr) && IsAnOper(sptr)) &&
+            !IsAnOper(acptr) && (acptr != sptr))
+            continue;
+        if (!doall && wilds && match(tname, acptr->name))
+            continue;
+        if (!dow && mycmp(tname, acptr->name))
+            continue;
+        /* only show IPs of unknowns or clients to opers */
+        if (IsAnOper(sptr) && 
+                (acptr->status == STAT_CLIENT || acptr->status == STAT_UNKNOWN))
+            name = get_client_name(acptr, FALSE);
+        else
+            name = get_client_name(acptr, HIDEME);
+        if(acptr->class)
+            class = acptr->class->name;
+        else
+            class = "NONE";
+                
+        switch (acptr->status) 
+        {
+            case STAT_CONNECTING:
+                sendto_one(sptr, rpl_str(RPL_TRACECONNECTING), me.name,
+                           parv[0], class, name);
+                break;
+            case STAT_HANDSHAKE:
+                sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE), me.name,
+                           parv[0], class, name);
+                break;
+            case STAT_ME:
+                break;
+            case STAT_UNKNOWN:
+                /* added time -Taner */
+                sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
+                          me.name, parv[0], class, name,
+                          acptr->firsttime ? timeofday - acptr->firsttime : -1);
+                break;
+            case STAT_CLIENT:
+                /*
+                 * Only opers see users if there is a wildcard but
+                 * anyone can see all the opers.
+                 */
+                if (((IsAnOper(sptr) && (MyClient(sptr))) 
+                        || !(dow && IsInvisible(acptr))) || !dow || 
+                    IsAnOper(acptr)) 
+                {
+                    if (IsAnOper(acptr))
+                        sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
+                               me.name, parv[0], class, name,
+                               timeofday - acptr->lasttime);
+                }
+                break;
+            case STAT_SERVER:
+                sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+                           me.name, parv[0], class, link_s[i],
+                           link_u[i], name, 
+                           *(acptr->serv->bynick) ? acptr->serv->bynick : "*", 
+                           *(acptr->serv->byuser) ? acptr->serv->byuser : "*", 
+                           *(acptr->serv->byhost) ? acptr->serv->byhost : 
+                           me.name);
+                break;
+            case STAT_LOG:
+                sendto_one(sptr, rpl_str(RPL_TRACELOG), me.name,
+                           parv[0], LOGFILE, acptr->port);
+                break;
+            default:                /* ...we actually shouldn't come here... */
+                sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name,
+                           parv[0], name);
+                break;
+        }
+    }
+
+    /* let the user have some idea that its at the end of the trace */
+    sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+                me.name, parv[0], "NONE", link_s[me.fd],
+                link_u[me.fd], me.name, "*", "*", me.name,
+                acptr ? timeofday - acptr->lasttime : 0);
+#ifdef HIDEULINEDSERVS
+    if (IsOper(sptr))
+#endif
+        for (cltmp = classes; doall && cltmp; cltmp = cltmp->next)
+            if (cltmp->links > 0)
+                sendto_one(sptr, rpl_str(RPL_TRACECLASS), me.name,
+                           parv[0], cltmp->name, cltmp->links);
+        
+    sendto_one(sptr, rpl_str(RPL_ENDOFTRACE), me.name, parv[0], tname);
+    return 0;
+}
+
+/*
+ * * m_motd 
+ *       parv[0] = sender prefix 
+ *       parv[1] = servername
+ */
+int 
+m_motd(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    static time_t last_used = 0L;
+    if (hunt_server(cptr, sptr, ":%s MOTD :%s", 1, parc, parv) != HUNTED_ISME)
+        return 0;
+    if(!IsAnOper(sptr)) 
+    {
+        if (IsSquelch(sptr)) 
+        {
+            sendto_one(sptr, rpl_str(RPL_ENDOFMOTD), me.name, parv[0]);
+            return 0;
+        }
+        if ((last_used + MOTD_WAIT) > NOW)
+            return 0;
+        else
+            last_used = NOW;
+
+    }
+    sendto_realops_lev(SPY_LEV, "MOTD requested by %s (%s@%s) [%s]",
+                       sptr->name, sptr->user->username, sptr->user->host,
+                       sptr->user->server);
+    send_motd(cptr, sptr, parc, parv);
+    return 0;
+}
+
+/*
+** send_motd
+**  parv[0] = sender prefix
+**  parv[1] = servername
+**
+** This function split off so a server notice could be generated on a
+** user requested motd, but not on each connecting client.
+** -Dianora
+*/
+int 
+send_motd(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aMotd *temp;
+    struct tm  *tm;
+    
+    tm = motd_tm;
+    if (motd == (aMotd *) NULL) 
+    {
+        sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv[0]);
+        return 0;
+    }
+    sendto_one(sptr, rpl_str(RPL_MOTDSTART), me.name, parv[0], me.name);
+
+    if (tm)
+        sendto_one(sptr, ":%s %d %s :- %d/%d/%d %d:%02d", me.name, RPL_MOTD,
+                   parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
+                   tm->tm_hour, tm->tm_min);
+
+    temp = motd;
+    while (temp) 
+    {
+        sendto_one(sptr, rpl_str(RPL_MOTD),  me.name, parv[0], temp->line);
+        temp = temp->next;
+    }
+    sendto_one(sptr, rpl_str(RPL_ENDOFMOTD), me.name, parv[0]);
+    return 0;
+}
+
+/*
+ * read_motd() - From CoMSTuD, added Aug 29, 1996
+ */
+void 
+read_motd(char *filename)
+{
+    aMotd *temp, *last;
+    struct stat sb;
+    char        buffer[MOTDLINELEN], *tmp;
+    int         fd;
+
+    /* Clear out the old MOTD */
+
+    while (motd) 
+    {
+        temp = motd->next;
+        MyFree(motd);
+        motd = temp;
+    }
+    fd = open(filename, O_RDONLY);
+    if (fd == -1)
+    {
+        if(!forked)
+                printf("WARNING:  MOTD file %s could not be found.  "
+                       "Skipping MOTD load.\n", filename);
+        return;
+    }
+    fstat(fd, &sb);
+    motd_tm = localtime(&sb.st_mtime);
+    last = (aMotd *) NULL;
+
+    while (dgets(fd, buffer, MOTDLINELEN - 1) > 0) 
+    {
+        if ((tmp = (char *) strchr(buffer, '\n')))
+            *tmp = '\0';
+        if ((tmp = (char *) strchr(buffer, '\r')))
+            *tmp = '\0';
+        temp = (aMotd *) MyMalloc(sizeof(aMotd));
+
+        strncpyzt(temp->line, buffer, MOTDLINELEN);
+        temp->next = (aMotd *) NULL;
+        if (!motd)
+            motd = temp;
+        else
+            last->next = temp;
+        last = temp;
+    }
+    close(fd);
+
+    if (motd_tm)
+        sprintf(motd_last_changed_date, "%d/%d/%d %d:%02d", motd_tm->tm_mday,
+                motd_tm->tm_mon + 1, 1900 + motd_tm->tm_year, motd_tm->tm_hour,
+                motd_tm->tm_min);
+}
+
+void 
+read_shortmotd(char *filename)
+{
+    aMotd *temp, *last;
+    char        buffer[MOTDLINELEN], *tmp;
+    int         fd;
+
+    /* Clear out the old MOTD */
+
+    while (shortmotd)
+    {
+        temp = shortmotd->next;
+        MyFree(shortmotd);
+        shortmotd = temp;
+    }
+    fd = open(filename, O_RDONLY);
+    if (fd == -1)
+    {
+        if(!forked)
+                printf("WARNING:  sMOTD file %s could not be found.  "
+                       "Skipping sMOTD load.\n", filename);
+        return;
+    }
+    
+    last = (aMotd *) NULL;
+
+    while (dgets(fd, buffer, MOTDLINELEN - 1) > 0) 
+    {
+        if ((tmp = (char *) strchr(buffer, '\n')))
+            *tmp = '\0';
+        if ((tmp = (char *) strchr(buffer, '\r')))
+            *tmp = '\0';
+        temp = (aMotd *) MyMalloc(sizeof(aMotd));
+
+        strncpyzt(temp->line, buffer, MOTDLINELEN);
+        temp->next = (aMotd *) NULL;
+        if (!shortmotd)
+            shortmotd = temp;
+        else
+            last->next = temp;
+        last = temp;
+    }
+    close(fd);
+}
+
+/*
+ * read_help() - modified from from CoMSTuD's read_motd added Aug 29,
+ * 1996 modifed  Aug 31 1997 - Dianora
+ * 
+ * Use the same idea for the oper helpfile
+ */
+void 
+read_help(char *filename)
+{
+    aMotd *temp, *last;
+    char        buffer[MOTDLINELEN], *tmp;
+    int         fd;
+
+    /* Clear out the old HELPFILE */
+
+    while (helpfile) 
+    {
+        temp = helpfile->next;
+        MyFree(helpfile);
+        helpfile = temp;
+    }
+
+    fd = open(filename, O_RDONLY);
+    if (fd == -1)
+    {
+        if(!forked)
+                printf("WARNING:  Help file %s could not be found.  "
+                       "Skipping Help file load.\n", filename);
+        return;
+    }
+
+    last = (aMotd *) NULL;
+
+    while (dgets(fd, buffer, MOTDLINELEN - 1) > 0) 
+    {
+        if ((tmp = (char *) strchr(buffer, '\n')))
+            *tmp = '\0';
+        if ((tmp = (char *) strchr(buffer, '\r')))
+            *tmp = '\0';
+        temp = (aMotd *) MyMalloc(sizeof(aMotd));
+
+        strncpyzt(temp->line, buffer, MOTDLINELEN);
+        temp->next = (aMotd *) NULL;
+        if (!helpfile)
+            helpfile = temp;
+        else
+            last->next = temp;
+        last = temp;
+    }
+    close(fd);
+}
+
+/* m_close - added by Darren Reed Jul 13 1992. */
+int 
+m_close(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient *acptr;
+    int     i;
+    int         closed = 0;
+
+    if (!MyOper(sptr)) 
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    for (i = highest_fd; i; i--) 
+    {
+        if (!(acptr = local[i]))
+            continue;
+        if (!IsUnknown(acptr) && !IsConnecting(acptr) &&
+            !IsHandshake(acptr))
+            continue;
+        sendto_one(sptr, rpl_str(RPL_CLOSING), me.name, parv[0],
+                   get_client_name(acptr, TRUE), acptr->status);
+        exit_client(acptr, acptr, acptr, "Oper Closing");
+        closed++;
+    }
+    sendto_one(sptr, rpl_str(RPL_CLOSEEND), me.name, parv[0], closed);
+    return 0;
+}
+
+int 
+m_die(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient *acptr;
+    int     i;
+    char       *pass = NULL;
+
+    if (!OPCanDie(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+    /* X line -mjs */
+
+    if ((pass = MeLine->diepass))
+    {
+        if (parc < 2) 
+        {
+            sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name,
+                       parv[0], "DIE");
+            return 0;
+        }
+        if (strcmp(pass, parv[1])) 
+        {
+            sendto_one(sptr, err_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
+            return 0;
+        }
+    }
+
+    for (i = 0; i <= highest_fd; i++) 
+    {
+        if (!(acptr = local[i]))
+            continue;
+        if (IsClient(acptr))
+            sendto_one(acptr,
+                       ":%s NOTICE %s :Server Terminating. %s",
+                       me.name, acptr->name,
+                       get_client_name(sptr, FALSE));
+        else if (IsServer(acptr))
+            sendto_one(acptr, ":%s ERROR :Terminated by %s",
+                       me.name, get_client_name(sptr, TRUE));
+    }
+    s_die();
+    return 0;
+}
+
+/*
+ * m_capab 
+ * Communicate what I can do to another server 
+ * This has to be able to be sent and understood while
+ * the client is UNREGISTERED. Therefore, we
+ * absolutely positively must not check to see if
+ * this is a server or a client. It's probably an unknown!
+ */
+int 
+m_capab(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int         i;
+
+    /* If it's not local, or it has already set capabilities,
+     * silently ignore it.
+     * Dont ignore clients where we have set some capabilities already
+     * that would suck for connecting TO servers.
+     */
+    
+    if(cptr != sptr)
+        return 0;
+
+    for (i = 1; i < parc; i++) 
+    {
+        if (strcmp(parv[i], "BURST") == 0)
+            SetBurst(sptr);
+        else if (strcmp(parv[i], "UNCONNECT") == 0)
+            SetUnconnect(cptr);
+        else if (strcmp(parv[i], "DKEY") == 0)
+            SetDKEY(cptr);
+        else if (strcmp(parv[i], "ZIP") == 0)
+            SetZipCapable(cptr);
+#ifdef NOQUIT
+        else if (strcmp(parv[i], "NOQUIT") == 0)
+            SetNoquit(cptr);
+#endif
+    }
+
+    return 0;
+}
+
+/* Shadowfax's LOCKFILE code */
+#ifdef LOCKFILE
+
+int 
+lock_kline_file()
+{
+    int         fd;
+
+    /* Create Lockfile */
+
+    if ((fd = open(LOCKFILE, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) 
+    {
+        sendto_realops("%s is locked, klines pending", configfile);
+        pending_kline_time = time(NULL);
+        return (-1);
+    }
+    close(fd);
+    return 1;
+}
+
+void 
+do_pending_klines()
+{
+    int         fd;
+    char        s[20];
+    struct pkl *k, *ok;
+
+    if (!pending_klines)
+        return;
+
+    /* Create Lockfile */
+    if ((fd = open(LOCKFILE, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) 
+    {
+        sendto_realops("%s is locked, klines pending", configfile);
+        pending_kline_time = time(NULL);
+        return;
+    }
+    ircsprintf(s, "%d\n", getpid());
+    write(fd, s, strlen(s));
+    close(fd);
+
+    /* Open klinefile */
+    if ((fd = open(configfile, O_WRONLY | O_APPEND)) == -1) 
+    {
+        sendto_realops("Pending klines cannot be written, cannot open %s",
+                       configfile);
+        unlink(LOCKFILE);
+        return;
+    }
+
+    /* Add the Pending Klines */
+
+    k = pending_klines;
+    while (k) 
+    {
+        write(fd, k->comment, strlen(k->comment));
+        write(fd, k->kline, strlen(k->kline));
+        MyFree(k->comment);
+        MyFree(k->kline);
+        ok = k;
+        k = k->next;
+        MyFree(ok);
+    }
+    pending_klines = NULL;
+    pending_kline_time = 0;
+
+    close(fd);
+
+    /* Delete the Lockfile */
+    unlink(LOCKFILE);
+}
+#endif
+         
+/* m_svskill - Just about the same as outta df
+ *  - Raistlin
+ * parv[0] = servername
+ * parv[1] = client
+ * parv[2] = nick stamp
+ * parv[3] = kill message
+ */
+         
+int 
+m_svskill(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient *acptr;
+    char *comment;
+    char reason[TOPICLEN + 1];
+    ts_val ts = 0;
+
+    if (parc < 2)
+        return 0;
+
+    if (parc > 3) 
+    {
+        comment = parv[3] ? parv[3] : parv[0];
+        ts = atol(parv[2]);
+    }
+    else
+        comment = (parc > 2 && parv[2]) ? parv[2] : parv[0];
+      
+    if(!IsULine(sptr)) return -1;
+    if((acptr = find_client(parv[1], NULL)) && (!ts || ts == acptr->tsinfo))
+    {
+        if(MyClient(acptr))
+        {
+            strcpy(reason, "SVSKilled: ");
+            strncpy(reason + 11, comment, TOPICLEN - 11);
+            reason[TOPICLEN] = '\0';
+            exit_client(acptr, acptr, sptr, reason);
+            return (acptr == cptr) ? FLUSH_BUFFER : 0;
+        }
+        if(acptr->from == cptr)
+        {
+            sendto_realops_lev(DEBUG_LEV, "Received wrong-direction SVSKILL"
+                               " for %s (behind %s) from %s", 
+                               acptr->name, cptr->name,
+                               get_client_name(sptr, HIDEME));
+            return 0;
+        }
+        else if(ts == 0) 
+            sendto_one(acptr->from, ":%s SVSKILL %s :%s", parv[0], parv[1],
+                               comment);
+        else
+            sendto_one(acptr->from, ":%s SVSKILL %s %ld :%s", parv[0], parv[1],
+                               ts, comment);
+    }
+    return 0;
+}
+         
+/* m_akill -
+ * Parse AKILL command
+ * parv[1]=host 
+ * parv[2]=user
+ * parv[3]=length
+ * parv[4]=akiller
+ * parv[5]=time set
+ * parv[6]=reason
+ */      
+int 
+m_akill(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient *acptr;
+    char *user, *host, *reason, *akiller, buffer[1024], *current_date, 
+        fbuf[512];
+    time_t length=0, timeset=0;
+    int i;
+    struct userBan *ban, *oban;
+
+    if(!IsServer(sptr) || (parc < 6))
+        return 0;
+        
+    if(!IsULine(sptr)) 
+    {
+        sendto_serv_butone(&me,":%s GLOBOPS :Non-ULined server %s trying to "
+                           "AKILL!", me.name, sptr->name);
+        send_globops("From %s: Non-ULined server %s trying to AKILL!", me.name,
+                     sptr->name);
+        return 0;
+    }
+        
+    host=parv[1];
+    user=parv[2];
+    akiller=parv[4];
+    length=atoi(parv[3]);
+    timeset=atoi(parv[5]);
+    reason=(parv[6] ? parv[6] : "<no reason>");
+
+    if(length == 0) /* a "permanent" akill? */
+       length = (86400 * 7); /* hold it for a week */
+
+    /* is this an old bogus akill? */
+    if(timeset + length <= NOW)
+       return 0;
+
+    current_date=smalldate((time_t)timeset);
+    /* cut reason down a little, eh? */
+    /* 250 chars max */
+    if(strlen(reason)>250)
+        reason[251]=0;
+
+    ban = make_hostbased_ban(user, host);
+    if(!ban)
+    {
+       sendto_realops_lev(DEBUG_LEV, "make_hostbased_ban(%s, %s) failed"
+                                     " on akill", user, host);
+       return 0;
+    }
+
+    /* if it already exists, pass it on */
+    oban = find_userban_exact(ban, 0);
+    if(oban)
+    {
+        /* pass along the akill anyways */
+        sendto_serv_butone(cptr, ":%s AKILL %s %s %d %s %d :%s",
+                           sptr->name, host, user, length, akiller,
+                           timeset, reason);
+       userban_free(ban);
+       return 0;
+    }
+        
+    ircsprintf(buffer, "%s (%s)", reason, current_date);
+    ban->flags |= (UBAN_NETWORK|UBAN_TEMPORARY);
+    ban->reason = (char *) MyMalloc(strlen(buffer) + 1);
+    strcpy(ban->reason, buffer);
+    ban->timeset = timeset;
+    ban->duration = length;
+
+    add_hostbased_userban(ban);
+
+    /* send it off to any other servers! */
+    sendto_serv_butone(cptr, ":%s AKILL %s %s %d %s %d :%s",
+                       sptr->name, host, user, length, akiller,
+                       timeset, reason);
+
+    /* Check local users against it */
+    for (i = 0; i <= highest_fd; i++)
+    {
+        if (!(acptr = local[i]) || IsMe(acptr) || IsLog(acptr))
+            continue;
+        if (IsPerson(acptr) && user_match_ban(acptr, ban))
+        {
+            sendto_ops(NETWORK_BAN_NAME" active for %s",
+                       get_client_name(acptr, FALSE));
+            ircsprintf(fbuf, NETWORK_BANNED_NAME": %s", reason);
+            exit_client(acptr, acptr, &me, fbuf);
+            i--;
+        }
+    }
+        
+    return 0;
+}
+  
+int 
+m_rakill(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    struct userBan *ban, *oban;
+
+    if(!IsServer(sptr))
+        return 0;
+
+    /* just quickly find the akill and be rid of it! */
+    if(parc<3) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "RAKILL");
+        return 0;
+    }
+
+    if(!IsULine(sptr)) 
+    {
+        sendto_serv_butone(&me, ":%s GLOBOPS :Non-ULined server %s trying to "
+                           "RAKILL!",  me.name, sptr->name);
+        send_globops("From %s: Non-ULined server %s trying to RAKILL!",
+                     me.name,
+                     sptr->name);
+        return 0;
+    }
+
+    ban = make_hostbased_ban(parv[2], parv[1]);
+    if(!ban)
+       return 0;
+
+    ban->flags |= UBAN_NETWORK;
+    oban = find_userban_exact(ban, UBAN_NETWORK);
+    if(oban)
+    {
+       remove_userban(oban);
+       userban_free(oban);
+    }
+
+    userban_free(ban);
+
+    sendto_serv_butone(cptr, ":%s RAKILL %s %s", sptr->name, parv[1], parv[2]);
+    return 0;
+}
+
+        
+/*
+ * RPL_NOWON   - Online at the moment (Succesfully added to WATCH-list)
+ * RPL_NOWOFF  - Offline at the moement (Succesfully added to WATCH-list)
+ * RPL_WATCHOFF   - Succesfully removed from WATCH-list.
+ * ERR_TOOMANYWATCH - Take a guess :>  Too many WATCH entries.
+ */
+static void 
+show_watch(aClient *cptr, char *name, int rpl1, int rpl2) 
+{
+    aClient *acptr;     
+    
+    if ((acptr = find_person(name, NULL)))
+        sendto_one(cptr, rpl_str(rpl1), me.name, cptr->name,
+                   acptr->name, acptr->user->username,
+                   acptr->user->host, acptr->lasttime);
+    else
+        sendto_one(cptr, rpl_str(rpl2), me.name, cptr->name,
+                   name, "*", "*", 0);
+}
+        
+/* m_watch */
+int 
+m_watch(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    aClient  *acptr;
+    char  *s, *p, *user;
+    char def[2] = "l";
+        
+    if (parc < 2) 
+    {
+        /* Default to 'l' - list who's currently online */
+        parc = 2;
+        parv[1] = def;
+    }
+    
+    for (p = NULL, s = strtoken(&p, parv[1], ", "); s;
+         s = strtoken(&p, NULL, ", ")) 
+    {
+        if ((user = (char *)strchr(s, '!')))
+            *user++ = '\0'; /* Not used */
+                
+        /*
+         * Prefix of "+", they want to add a name to their WATCH
+         * list. 
+         */
+        if (*s == '+') 
+        {
+            if (*(s+1)) 
+            {
+                if (sptr->watches >= MAXWATCH) 
+                {
+                    sendto_one(sptr, err_str(ERR_TOOMANYWATCH),
+                               me.name, cptr->name, s+1);                                       
+                    continue;
+                }                               
+                add_to_watch_hash_table(s+1, sptr);
+            }
+            show_watch(sptr, s+1, RPL_NOWON, RPL_NOWOFF);
+            continue;
+        }
+        
+        /*
+         * Prefix of "-", coward wants to remove somebody from their
+         * WATCH list.  So do it. :-)
+         */
+        if (*s == '-') 
+        {
+            del_from_watch_hash_table(s+1, sptr);
+            show_watch(sptr, s+1, RPL_WATCHOFF, RPL_WATCHOFF);
+            continue;
+        }
+                                        
+        /*
+         * Fancy "C" or "c", they want to nuke their WATCH list and start
+         * over, so be it.
+         */
+        if (*s == 'C' || *s == 'c') 
+        {
+            hash_del_watch_list(sptr);
+            continue;
+        }
+                
+        /*
+         * Now comes the fun stuff, "S" or "s" returns a status report of
+         * their WATCH list.  I imagine this could be CPU intensive if its
+         * done alot, perhaps an auto-lag on this?
+         */
+        if (*s == 'S' || *s == 's') 
+        {
+            Link *lp;
+            aWatch *anptr;
+            int  count = 0;
+                                                        
+            /*
+             * Send a list of how many users they have on their WATCH list
+             * and how many WATCH lists they are on.
+             */
+            anptr = hash_get_watch(sptr->name);
+            if (anptr)
+                for (lp = anptr->watch, count = 1; (lp = lp->next); count++);
+            sendto_one(sptr, rpl_str(RPL_WATCHSTAT), me.name, parv[0],
+                       sptr->watches, count);
+                        
+            /*
+             * Send a list of everybody in their WATCH list. Be careful
+             * not to buffer overflow.
+             */
+            if ((lp = sptr->watch) == NULL) 
+            {
+                sendto_one(sptr, rpl_str(RPL_ENDOFWATCHLIST), me.name, parv[0],
+                           *s);
+                continue;
+            }
+            *buf = '\0';
+            strcpy(buf, lp->value.wptr->nick);
+            count = strlen(parv[0])+strlen(me.name)+10+strlen(buf);
+            while ((lp = lp->next)) 
+            {
+                if (count+strlen(lp->value.wptr->nick)+1 > BUFSIZE - 2) 
+                {
+                    sendto_one(sptr, rpl_str(RPL_WATCHLIST), me.name,
+                               parv[0], buf);
+                    *buf = '\0';
+                    count = strlen(parv[0])+strlen(me.name)+10;
+                }
+                strcat(buf, " ");
+                strcat(buf, lp->value.wptr->nick);
+                count += (strlen(lp->value.wptr->nick)+1);
+            }
+            sendto_one(sptr, rpl_str(RPL_WATCHLIST), me.name, parv[0], buf);
+            sendto_one(sptr, rpl_str(RPL_ENDOFWATCHLIST), me.name, parv[0],
+                       *s);
+            continue;
+        }
+                
+        /*
+         * Well that was fun, NOT.  Now they want a list of everybody in
+         * their WATCH list AND if they are online or offline? Sheesh,
+         * greedy arn't we?
+         */
+        if (*s == 'L' || *s == 'l') 
+        {
+            Link *lp = sptr->watch;
+                        
+            while (lp) 
+            {
+                if ((acptr = find_person(lp->value.wptr->nick, NULL)))
+                    sendto_one(sptr, rpl_str(RPL_NOWON), me.name, parv[0],
+                               acptr->name, acptr->user->username,
+                               acptr->user->host, acptr->tsinfo);
+                /*
+                 * But actually, only show them offline if its a capital
+                 * 'L' (full list wanted).
+                 */
+                else if (IsUpper(*s))
+                    sendto_one(sptr, rpl_str(RPL_NOWOFF), me.name, parv[0],
+                               lp->value.wptr->nick, "*", "*",
+                               lp->value.wptr->lasttime);
+                lp = lp->next;
+            }
+                        
+            sendto_one(sptr, rpl_str(RPL_ENDOFWATCHLIST), me.name, parv[0],
+                       *s);
+            continue;
+        }
+        /* Hmm.. unknown prefix character.. Ignore it. :-) */
+    }
+        
+    return 0;
+}
+
+int 
+m_sqline(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    struct simBan *ban;
+    unsigned int flags;
+    char *reason;
+
+    if(!(IsServer(sptr) || IsULine(sptr)))
+        return 0;
+
+    if(parc < 2) 
+    {
+        /* we should not get malformed sqlines.  complain loud */
+        sendto_realops("%s attempted to add sqline with insufficient params!"
+                       " (ignored) Contact coders!", sptr->name);
+        return 0;
+    }
+        
+    /* if we have any Q:lines (SQ or Q) that match
+     * this Q:line, just return (no need to waste cpu */
+
+    flags = SBAN_NETWORK;
+    if(parv[1][0] == '#')
+       flags |= SBAN_CHAN;
+    else
+       flags |= SBAN_NICK;
+    ban = make_simpleban(flags, parv[1]);
+    if(!ban)
+    {
+        sendto_realops("make_simpleban(%s) failed on sqline!", parv[1]);
+        return 0;
+    }
+
+    reason = BadPtr(parv[2]) ? "Reserved" : parv[2];
+    ban->reason = NULL;
+
+    if (find_simban_exact(ban) == NULL)
+    {
+        ban->reason = (char *) MyMalloc(strlen(reason) + 1);
+        strcpy(ban->reason, reason);
+        ban->timeset = NOW;
+        add_simban(ban);
+    }
+    else
+        simban_free(ban);
+
+    sendto_serv_butone(cptr, ":%s SQLINE %s :%s", sptr->name, parv[1],
+                       reason);
+    return 0;
+}
+        
+int 
+m_unsqline(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    int matchit = 0;
+    char *mask;
+    
+    if(!(IsServer(sptr) || IsULine(sptr)))
+        return 0;
+    
+    if(parc < 2) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "UNSQLINE");
+        return 0;
+    }
+    
+    if (parc == 3) 
+    {
+        matchit = atoi(parv[1]);
+        mask = parv[2];
+    }
+    else
+        mask = parv[1];
+
+    /* special case for "UNSQLINE 1 :*" */
+    if(mycmp(mask, "*") == 0 && matchit)
+    {
+       remove_simbans_match_mask(SBAN_CHAN|SBAN_NETWORK, mask, 1);
+       remove_simbans_match_mask(SBAN_NICK|SBAN_NETWORK, mask, 1);
+    }
+    else if(mask[0] == '#')
+       remove_simbans_match_mask(SBAN_CHAN|SBAN_NETWORK, mask, matchit);
+    else
+       remove_simbans_match_mask(SBAN_NICK|SBAN_NETWORK, mask, matchit);
+
+    if (parc == 3) 
+        sendto_serv_butone(cptr, ":%s UNSQLINE %d :%s", sptr->name, matchit,
+                           mask);
+    else
+        sendto_serv_butone(cptr, ":%s UNSQLINE :%s", sptr->name, mask);
+    return 0;
+}
+
+int m_sgline(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    struct simBan *ban;
+    int len;
+    unsigned int flags;
+    char *mask, *reason;
+    
+    if(!(IsServer(sptr) || IsULine(sptr)))
+        return 0;
+
+    if(parc<3) 
+    {
+        sendto_realops("%s attempted to add sgline with insufficient params!"
+                       " (ignored) Contact coders!", sptr->name);
+        return 0;
+    }
+        
+    len=atoi(parv[1]);
+    mask=parv[2];
+    if ((strlen(mask) > len) && (mask[len])==':') 
+    {
+        mask[len] = '\0';
+        reason = mask+len+1;
+    } 
+    else 
+    { /* Bogus */
+        return 0;
+    }
+    
+    /* if we have any G:lines (SG or G) that match
+     * this G:line, just return (no need to waste cpu */
+
+    flags = SBAN_NETWORK|SBAN_GCOS;
+    ban = make_simpleban(flags, mask);
+    if(!ban)
+    {
+        sendto_realops("make_simpleban(%s) failed on sgline!", parv[1]);
+        return 0;
+    }
+
+    if(BadPtr(reason))
+       reason = "Reserved";
+    ban->reason = NULL;
+
+    if (find_simban_exact(ban) == NULL)
+    {
+        ban->reason = (char *) MyMalloc(strlen(reason) + 1);
+        strcpy(ban->reason, reason);
+        ban->timeset = NOW;
+        add_simban(ban);
+    }
+    else
+        simban_free(ban);
+
+    sendto_serv_butone(cptr, ":%s SGLINE %d :%s:%s", sptr->name, len,
+                       mask, reason);
+    return 0;
+}
+        
+int 
+m_unsgline(aClient *cptr, aClient *sptr, int parc, char *parv[]) 
+{
+    int matchit=0;
+    char *mask;
+   
+    if(!(IsServer(sptr) || IsULine(sptr)))
+        return 0;
+   
+    if(parc<2) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "UNSGLINE");
+        return 0;
+    }
+   
+
+    if (parc==3) 
+    {
+        matchit=atoi(parv[1]);
+        mask=parv[2];
+    }
+    else
+        mask=parv[1];
+    
+    remove_simbans_match_mask(SBAN_GCOS|SBAN_NETWORK, mask, matchit);
+    
+    if (parc==3)
+        sendto_serv_butone(cptr, ":%s UNSGLINE %d :%s", sptr->name, matchit,
+                           mask);
+    else
+        sendto_serv_butone(cptr, ":%s UNSGLINE :%s",sptr->name,mask);
+    return 0;
+}
diff --git a/src/s_user.c b/src/s_user.c
new file mode 100644 (file)
index 0000000..c47c7bc
--- /dev/null
@@ -0,0 +1,3881 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/s_user.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   This program is free softwmare; 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: s_user.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "channel.h"
+#include "throttle.h"
+#include "clones.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include "h.h"
+#ifdef FLUD
+#include "blalloc.h"
+#endif /* FLUD */
+#include "userban.h"
+#include "hooks.h"
+
+#if defined( HAVE_STRING_H)
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+int do_user(char *, aClient *, aClient *, char *, char *, char *,
+            unsigned long, unsigned int, char *);
+extern char motd_last_changed_date[];
+extern int  send_motd(aClient *, aClient *, int, char **);
+extern void send_topic_burst(aClient *);
+extern void outofmemory(void);  /* defined in list.c */
+#ifdef MAXBUFFERS
+extern void reset_sock_opts();
+extern int send_lusers(aClient *,aClient *,int, char **);
+#endif
+extern int server_was_split;
+
+static char buf[BUFSIZE], buf2[BUFSIZE];
+int  user_modes[] =
+{
+    UMODE_o, 'o',
+    UMODE_O, 'O',
+    UMODE_i, 'i',
+    UMODE_w, 'w',
+    UMODE_s, 's',
+    UMODE_c, 'c',
+    UMODE_r, 'r',
+    UMODE_R, 'R',
+    UMODE_k, 'k',
+    UMODE_y, 'y',
+    UMODE_d, 'd',
+    UMODE_e, 'e',
+    UMODE_g, 'g',
+    UMODE_b, 'b',
+    UMODE_a, 'a',
+    UMODE_A, 'A',
+    UMODE_f, 'f',
+    UMODE_n, 'n',
+    UMODE_m, 'm',
+    UMODE_h, 'h',
+#ifdef NO_OPER_FLOOD
+    UMODE_F, 'F',
+#endif
+    UMODE_x, 'x',
+    UMODE_X, 'X',
+    UMODE_j, 'j',
+    UMODE_K, 'K',
+    UMODE_I, 'I',
+    0, 0
+};
+
+/* externally defined functions */
+extern Link *find_channel_link(Link *, aChannel *);     /* defined in list.c */
+#ifdef FLUD
+int         flud_num = FLUD_NUM;
+int         flud_time = FLUD_TIME;
+int         flud_block = FLUD_BLOCK;
+extern BlockHeap *free_fludbots;
+extern BlockHeap *free_Links;
+
+void        announce_fluder(aClient *, aClient *, aChannel *, int);
+struct fludbot *remove_fluder_reference(struct fludbot **, aClient *);
+Link       *remove_fludee_reference(Link **, void *);
+int         check_for_fludblock(aClient *, aClient *, aChannel *, int);
+int         check_for_flud(aClient *, aClient *, aChannel *, int);
+void        free_fluders(aClient *, aChannel *);
+void        free_fludees(aClient *);
+#endif
+
+#ifdef ANTI_SPAMBOT
+int         spam_time = MIN_JOIN_LEAVE_TIME;
+int         spam_num = MAX_JOIN_LEAVE_COUNT;
+#endif
+
+/* defines for check_ctcp results */
+#define CTCP_NONE       0
+#define CTCP_YES        1
+#define CTCP_DCC        2
+#define CTCP_DCCSEND    3
+
+
+/*
+ * cptr:
+ ** always NON-NULL, pointing to a *LOCAL* client
+ ** structure (with an open socket connected!). This
+ ** is the physical socket where the message originated (or
+ ** which caused the m_function to be executed--some
+ ** m_functions may call others...).
+ *
+ * sptr:
+ ** the source of the message, defined by the
+ ** prefix part of the message if present. If not or
+ ** prefix not found, then sptr==cptr.
+ *
+ *      *Always* true (if 'parse' and others are working correct):
+ *
+ *      1      sptr->from == cptr  (note: cptr->from == cptr)
+ *
+ *      2      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
+ * cannot be a local connection, unless it's actually cptr!).
+ *
+ * MyConnect(x) should probably  be defined as (x == x->from) --msa
+ *
+ * parc:
+ ** number of variable parameter strings (if zero,
+ ** parv is allowed to be NULL)
+ *
+ * parv:
+ ** a NULL terminated list of parameter pointers,
+ *** parv[0], sender (prefix string), if not present his points to
+ *** an empty string.
+ *
+ ** [parc-1]:
+ *** pointers to additional parameters
+ *** parv[parc] == NULL, *always*
+ *
+ * note:   it is guaranteed that parv[0]..parv[parc-1] are all
+ *         non-NULL pointers.
+ */
+/*
+ * * next_client 
+ * Local function to find the next matching
+ * client. The search can be continued from the specified client
+ * entry. Normal usage loop is: 
+ * 
+ *      for (x = client; x = next_client(x,mask); x = x->next) 
+ *          HandleMatchingClient; 
+ * 
+ */
+aClient *
+next_client(aClient *next, char *ch)
+{                               
+    /* search string (may include wilds) */
+    aClient *tmp = next;
+    
+    next = find_client(ch, tmp);
+    if (tmp && tmp->prev == next)
+        return ((aClient *) NULL);
+
+    if (next != tmp)
+        return next;
+    while(next) 
+    {
+        if (!match(ch, next->name))
+            break;
+        next = next->next;
+    }
+    return next;
+}
+
+/* this slow version needs to be used for hostmasks *sigh * */
+
+aClient *
+next_client_double(aClient *next, char *ch)
+{                               
+    /* search string (may include wilds) */
+    aClient *tmp = next;
+
+    next = find_client(ch, tmp);
+    if (tmp && tmp->prev == next)
+        return NULL;
+    if (next != tmp)
+        return next;
+    while(next) 
+    {
+        if (!match(ch, next->name) || !match(next->name, ch))
+            break;
+        next = next->next;
+    }
+    return next;
+}
+
+/*
+ * hunt_server
+ * 
+ *      Do the basic thing in delivering the message (command)
+ * across the relays to the specific server (server) for
+ * actions.
+ * 
+ *      Note:   The command is a format string and *MUST* be
+ * of prefixed style (e.g. ":%s COMMAND %s ...").
+ * Command can have only max 8 parameters.
+ * 
+ * server  parv[server] is the parameter identifying the target server.
+ * 
+ *      *WARNING* 
+ * parv[server] is replaced with the pointer to the 
+ * real servername from the matched client
+ * I'm lazy now --msa
+ * 
+ *      returns: (see #defines)
+ */
+int 
+hunt_server(aClient *cptr, aClient *sptr, char *command, int server,
+                int parc, char *parv[])
+{
+    aClient    *acptr;
+    int         wilds;
+
+    /* Assume it's me, if no server */
+    if (parc <= server || BadPtr(parv[server]) ||
+        match(me.name, parv[server]) == 0 ||
+        match(parv[server], me.name) == 0)
+        return (HUNTED_ISME);
+    /*
+     * These are to pickup matches that would cause the following
+     * message to go in the wrong direction while doing quick fast
+     * non-matching lookups.
+     */
+    if ((acptr = find_client(parv[server], NULL)))
+        if (acptr->from == sptr->from && !MyConnect(acptr))
+            acptr = NULL;
+    if (!acptr && (acptr = find_server(parv[server], NULL)))
+        if (acptr->from == sptr->from && !MyConnect(acptr))
+            acptr = NULL;
+
+    collapse(parv[server]);
+    wilds = (strchr(parv[server], '?') || strchr(parv[server], '*'));
+    /*
+     * Again, if there are no wild cards involved in the server name,
+     * use the hash lookup - Dianora
+     */
+
+    if (!acptr) 
+    {
+        if (!wilds) 
+        {
+            acptr = find_name(parv[server], (aClient *) NULL);
+            if (!acptr || !IsRegistered(acptr) || !IsServer(acptr)) 
+            {
+                sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name,
+                           parv[0], parv[server]);
+                return (HUNTED_NOSUCH);
+            }
+        }
+        else 
+        {
+            for (acptr = client;
+                 (acptr = next_client(acptr, parv[server]));
+                 acptr = acptr->next) 
+            {
+                if (acptr->from == sptr->from && !MyConnect(acptr))
+                    continue;
+                /*
+                 * Fix to prevent looping in case the parameter for some
+                 * reason happens to match someone from the from link --jto
+                 */
+                if (IsRegistered(acptr) && (acptr != cptr))
+                    break;
+            }
+        }
+    }
+
+    if (acptr) 
+    {
+#ifdef NO_USER_OPERTARGETED_COMMANDS
+        if (!(IsAnOper(sptr) || IsULine(sptr) || IsServer(sptr)) &&
+            !IsServer(acptr) && IsUmodeI(acptr))
+        {
+            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            return (HUNTED_NOSUCH);
+        }
+#endif
+        if (IsMe(acptr) || MyClient(acptr))
+            return HUNTED_ISME;
+        if (match(acptr->name, parv[server]))
+            parv[server] = acptr->name;
+        sendto_one(acptr, command, parv[0],
+                   parv[1], parv[2], parv[3], parv[4],
+                   parv[5], parv[6], parv[7], parv[8]);
+        return (HUNTED_PASS);
+    }
+    sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name,
+               parv[0], parv[server]);
+    return (HUNTED_NOSUCH);
+}
+
+/*
+ * canonize
+ * 
+ * reduce a string of duplicate list entries to contain only the unique
+ * items.  Unavoidably O(n^2).
+ */
+char *
+canonize(char *buffer)
+{
+    static char cbuf[BUFSIZ];
+    char *s, *t, *cp = cbuf;
+    int l = 0;
+    char *p = NULL, *p2;
+
+    *cp = '\0';
+    
+    for (s = strtoken(&p, buffer, ","); s; s = strtoken(&p, NULL, ",")) 
+    {
+        if (l) 
+        {
+            for (p2 = NULL, t = strtoken(&p2, cbuf, ","); t;
+                 t = strtoken(&p2, NULL, ","))
+                if (!mycmp(s, t))
+                    break;
+                else if (p2)
+                    p2[-1] = ',';
+        }
+        else
+            t = NULL;
+
+        if (!t) 
+        {
+            if (l)
+                *(cp - 1) = ',';
+            else
+                l = 1;
+            (void) strcpy(cp, s);
+            if (p)
+                cp += (p - s);
+        }
+        else if (p2)
+            p2[-1] = ',';
+    }
+    return cbuf;
+}
+
+#if (RIDICULOUS_PARANOIA_LEVEL>=1)
+static int
+check_oper_can_mask(aClient *sptr, char *name, char *password, char **onick)
+{
+    aOper *aoper;
+    char *encr;
+    extern char *crypt();
+
+    if(!(aoper = find_oper(name, sptr->user->username, sptr->user->host,
+                           sptr->hostip)))
+    {
+        sendto_realops("Failed OPERMASK attempt by %s (%s@%s) [No Entry for "
+                       "%s]", sptr->name, sptr->user->username,
+                       sptr->user->host, name);
+        return 0;
+    }
+
+    /* use first two chars of the password they send in as salt */
+    /* passwd may be NULL pointer. Head it off at the pass... */
+    if(confopts & FLAGS_CRYPTPASS)
+    {
+        if (password && *aoper->passwd)
+            encr = crypt(password, aoper->passwd);
+        else
+            encr = "";
+    }
+    else
+        encr = password;
+
+    if(StrEq(encr, aoper->passwd))
+    {
+#ifdef USE_SYSLOG
+        syslog(LOG_INFO, "OPERMASK: %s (%s!%s@%s)", aoper->nick, sptr->name,
+               sptr->user->username, sptr->user->host);
+#endif
+        *onick = aoper->nick;
+        sendto_realops("%s [%s] (%s@<hidden>) has masked their hostname.",
+                       sptr->name, aoper->nick, sptr->user->username);
+        return 1;
+    }
+
+    sendto_realops("Failed OPERMASK attempt by %s (%s@%s) [Bad Password]",
+                   sptr->name, sptr->user->username, sptr->user->host);
+
+    return 0;
+}
+#endif
+
+/*
+ * * register_user 
+ *  This function is called when both NICK and USER messages 
+ *  have been accepted for the client, in whatever order.  Only 
+ *  after this, is the USER message propagated.
+ * 
+ *      NICK's must be propagated at once when received, although
+ * it would be better to delay them too until full info is
+ * available. Doing it is not so simple though, would have to
+ * implement the following:
+ * 
+ *      (actually it has been implemented already for a while)
+ * -orabidoo
+ * 
+ * 1 user telnets in and gives only "NICK foobar" and waits
+ * 2 another user far away logs in normally with the nick
+ * "foobar" quite legal, as this server didnt propagate it.
+ * 3 now this server gets nick "foobar" from outside, but has
+ * already the same defined locally. Current server would just
+ * issue "KILL foobar" to clean out dups. But, this is not
+ * fair. It should actually request another nick from local user
+ * or kill him/her...
+ */
+
+int 
+register_user(aClient *cptr, aClient *sptr, char *nick, char *username)
+{
+    aClient *nsptr;
+    aAllow  *pwaconf = NULL;
+    char       *parv[3];
+    static char ubuf[12];
+    char       *p;
+    short       oldstatus = sptr->status;
+    anUser     *user = sptr->user;
+    struct userBan    *ban;
+    aMotd      *smotd;
+    int         i, dots;
+    int         bad_dns;                /* flag a bad dns name */
+#ifdef ANTI_SPAMBOT
+    char        spamchar = '\0';
+
+#endif
+    char        tmpstr2[512];
+
+    user->last = timeofday;
+    parv[0] = sptr->name;
+    parv[1] = parv[2] = NULL;
+          
+    p = inetntoa((char *) &sptr->ip);
+    strncpyzt(sptr->hostip, p, HOSTIPLEN + 1);
+    if (MyConnect(sptr)) 
+    {
+        if ((i = check_client(sptr))) 
+        {
+            switch (i)
+            {
+                case -1:
+                    ircstp->is_ref++;
+                    sendto_realops_lev(REJ_LEV, "%s from %s [Unauthorized"
+                                       " client connection]",
+                                       get_client_host(sptr), p);
+                    return exit_client(cptr, sptr, &me, "You are not"
+                                       " authorized to use this server");
+
+                case -2:
+                    return exit_client(cptr, sptr, &me, "Socket Error");
+
+                case -3:
+                    ircstp->is_ref++;
+                    sendto_realops_lev(REJ_LEV, "%s for %s [Allow class is"
+                                       " full (server is full)]",
+                                       get_client_host(sptr), p);
+                    return exit_client(cptr, sptr, &me, "No more connections"
+                                       " allowed in your connection class (the"
+                                       " server is full)");
+
+                default:
+                    sendto_realops_lev(DEBUG_LEV, "I don't know why I dropped"
+                                       " %s (%d)", get_client_host(sptr), i);
+                    return exit_client(cptr, sptr, &me, "Internal error");
+            }
+        }
+
+        if (sptr->user->allow->flags & CONF_FLAGS_NOTHROTTLE)
+            throttle_remove(cptr->hostip);
+
+        if (sptr->user->allow->flags & CONF_FLAGS_FORCEFLOOD)
+            SetNoMsgThrottle(sptr);
+
+#ifdef ANTI_SPAMBOT
+        /* This appears to be broken */
+        /* Check for single char in user->host -ThemBones */
+        if (*(user->host + 1) == '\0')
+            spamchar = *user->host;
+#endif
+                
+        strncpyzt(user->host, sptr->sockhost, HOSTLEN);
+                
+        dots = 0;
+        p = user->host;
+        bad_dns = NO;
+        while (*p) 
+        {
+            if (!IsAlnum(*p)) 
+            {
+#ifdef RFC1035_ANAL
+                if ((*p != '-') && (*p != '.'))
+#else
+                    if ((*p != '-') && (*p != '.') && (*p != '_') &&
+                        (*p != '/'))
+#endif /* RFC1035_ANAL */
+                        bad_dns = YES;
+            }
+            if (*p == '.')
+                dots++;
+            p++;
+        }
+        /*
+         * Check that the hostname has AT LEAST ONE dot (.) in it. If
+         * not, drop the client (spoofed host) -ThemBones
+         */
+        if (!dots) 
+        {
+            sendto_realops("Invalid hostname for %s, dumping user %s",
+                           sptr->hostip, sptr->name);
+            return exit_client(cptr, sptr, &me, "Invalid hostname");
+        }
+        
+        if (bad_dns) 
+        {
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- You have a bad "
+                       "character in your hostname", me.name, cptr->name);
+            strcpy(user->host, sptr->hostip);
+            strcpy(sptr->sockhost, sptr->hostip);
+        }
+        
+        pwaconf = sptr->user->allow;
+
+        if (sptr->flags & FLAGS_DOID && !(sptr->flags & FLAGS_GOTID)) 
+        {
+            /* because username may point to user->username */
+            char        temp[USERLEN + 1];
+            
+            strncpyzt(temp, username, USERLEN + 1);
+            *user->username = '~';
+            (void) strncpy(&user->username[1], temp, USERLEN);
+            user->username[USERLEN] = '\0';
+#ifdef IDENTD_COMPLAIN
+            /* tell them to install identd -Taner */
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- It seems that you "
+                       "don't have identd installed on your host.",
+                       me.name, cptr->name);
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- If you wish to "
+                       "have your username show up without the ~ (tilde),",
+                       me.name, cptr->name);
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- then install "
+                       "identd.", me.name, cptr->name);
+            /* end identd hack */
+#endif
+        }
+        else if (sptr->flags & FLAGS_GOTID && *sptr->username != '-')
+            strncpyzt(user->username, sptr->username, USERLEN + 1);
+        else if(username != user->username) /* don't overlap */
+            strncpyzt(user->username, username, USERLEN + 1);
+
+        if (!BadPtr(pwaconf->passwd))
+        {
+            char *tmpptr = strchr(sptr->passwd, ':');
+            char tmppwd[PASSWDLEN + 1];
+
+            /*
+             * If there's a : in the password, fix it so after this function,
+             * sptr->passwd changes from:
+             * moo:cow:test:asdf
+             * to
+             * cow:test:asdf
+             */
+
+            if(tmpptr)
+            {
+                *tmpptr++ = '\0';
+                strcpy(tmppwd, tmpptr);
+            }
+
+            if(!StrEq(sptr->passwd, pwaconf->passwd)) 
+            {
+                ircstp->is_ref++;
+                sendto_one(sptr, err_str(ERR_PASSWDMISMATCH),
+                           me.name, parv[0]);
+                return exit_client(cptr, sptr, &me, "Bad Password");
+            }
+            if(tmpptr)
+                strcpy(sptr->passwd, tmppwd);
+            else
+                sptr->passwd[0] = '\0';
+        }
+
+                
+        /* Limit clients */
+        /*
+         * We want to be able to have servers and F-line clients connect,
+         * so save room for "buffer" connections. Smaller servers may
+         * want to decrease this, and it should probably be just a
+         * percentage of the MAXCLIENTS... -Taner
+         * Flines are now no different than Elines
+         * And now there are no special clients, and this is the only thing
+         * MAXCLIENTS is checked against.  So no more buffer space.
+         */
+        if (Count.local > MAXCLIENTS)
+        { 
+            sendto_realops_lev(SPY_LEV, "Too many clients, rejecting %s[%s].",
+                               nick, sptr->sockhost);
+            ircstp->is_ref++;
+            return exit_client(cptr, sptr, &me,
+                               "Sorry, server is full - try later");
+        }
+        
+#ifdef ANTI_SPAMBOT
+        /* It appears, this is catching normal clients */
+        /* Reject single char user-given user->host's */
+        if (spamchar == 'x') 
+        {
+            sendto_realops_lev(REJ_LEV, "Rejecting possible Spambot: %s "
+                               "(Single char user-given userhost: %c)",
+                               get_client_name(sptr, FALSE), spamchar);
+            ircstp->is_ref++;
+            return exit_client(cptr, sptr, sptr, "Spambot detected, "
+                               "rejected.");
+        }
+#endif
+                
+
+        /* i really dont like the fact that we're calling m_oper from here.
+         * perhaps there is a better method...?  Will investigate later
+         * -epi */
+        if (oldstatus == STAT_MASTER && MyConnect(sptr))
+            m_oper(&me, sptr, 1, parv);
+
+        /* hostile username checks begin here */
+        
+        {
+            char *tmpstr;
+            u_char      c, cc;
+            int lower, upper, special;
+            
+            lower = upper = special = cc = 0;
+                          
+            /* check for "@" in identd reply -Taner */
+            if ((strchr(user->username, '@') != NULL) ||
+                (strchr(username, '@') != NULL)) 
+            {
+                sendto_realops_lev(REJ_LEV,
+                                   "Illegal \"@\" in username: %s (%s)",
+                                   get_client_name(sptr, FALSE), username);
+                ircstp->is_ref++;
+                (void) ircsprintf(tmpstr2,
+                                  "Invalid username [%s] - '@' is not allowed",
+                                  username);
+                return exit_client(cptr, sptr, sptr, tmpstr2);
+            }
+            /* First check user->username... */
+#ifdef IGNORE_FIRST_CHAR
+            tmpstr = (user->username[0] == '~' ? &user->username[2] :
+                      &user->username[1]);
+            /*
+             * Ok, we don't want to TOTALLY ignore the first character. We
+             * should at least check it for control characters, etc -
+             * ThemBones
+             */
+            cc = (user->username[0] == '~' ? user->username[1] :
+                  user->username[0]);
+            if ((!IsAlnum(cc) && !strchr(" -_.", cc)) || (cc > 127))
+                special++;
+#else
+            tmpstr = (user->username[0] == '~' ? &user->username[1] :
+                      user->username);
+#endif /* IGNORE_FIRST_CHAR */
+            
+            while (*tmpstr) 
+            {
+                c = *(tmpstr++);
+                if (IsLower(c)) 
+                {
+                    lower++;
+                    continue;
+                }
+                if (IsUpper(c)) 
+                {
+                    upper++;
+                    continue;
+                }
+                if ((!IsAlnum(c) && !strchr(" -_.", c)) || (c > 127) || (c<32))
+                    special++;
+            }
+            if (special) 
+            {
+                sendto_realops_lev(REJ_LEV, "Invalid username: %s (%s@%s)",
+                                   nick, user->username, user->host);
+                ircstp->is_ref++;
+                ircsprintf(tmpstr2, "Invalid username [%s]", user->username);
+                return exit_client(cptr, sptr, &me, tmpstr2);
+            }
+            /* Ok, now check the username they provided, if different */
+            lower = upper = special = cc = 0;
+                          
+            if (strcmp(user->username, username)) 
+            {
+                                  
+#ifdef IGNORE_FIRST_CHAR
+                tmpstr = (username[0] == '~' ? &username[2] : &username[1]);
+                /*
+                 * Ok, we don't want to TOTALLY ignore the first character.
+                 * We should at least check it for control charcters, etc
+                 * -ThemBones
+                 */
+                cc = (username[0] == '~' ? username[1] : username[0]);
+                                  
+                if ((!IsAlnum(cc) && !strchr(" -_.", cc)) || (cc > 127))
+                    special++;
+#else
+                tmpstr = (username[0] == '~' ? &username[1] : username);
+#endif /* IGNORE_FIRST_CHAR */
+                while (*tmpstr) 
+                {
+                    c = *(tmpstr++);
+                    if (IsLower(c)) 
+                    {
+                        lower++;
+                        continue;
+                    }
+                    if (IsUpper(c)) 
+                    {
+                        upper++;
+                        continue;
+                    }
+                    if ((!IsAlnum(c) && !strchr(" -_.", c)) || (c > 127))
+                        special++;
+                }
+#ifdef NO_MIXED_CASE
+                if (lower && upper) 
+                {
+                    sendto_realops_lev(REJ_LEV, "Invalid username: %s (%s@%s)",
+                                       nick, username, user->host);
+                    ircstp->is_ref++;
+                    ircsprintf(tmpstr2, "Invalid username [%s]", username);
+                    return exit_client(cptr, sptr, &me, tmpstr2);
+                }
+#endif /* NO_MIXED_CASE */
+                if (special) 
+                {
+                    sendto_realops_lev(REJ_LEV, "Invalid username: %s (%s@%s)",
+                                       nick, username, user->host);
+                    ircstp->is_ref++;
+                    ircsprintf(tmpstr2, "Invalid username [%s]", username);
+                    return exit_client(cptr, sptr, &me, tmpstr2);
+                }
+            }                   /* usernames different  */
+        }
+
+        /*
+         * reject single character usernames which aren't alphabetic i.e.
+         * reject jokers who have '?@somehost' or '.@somehost'
+         * 
+         * -Dianora
+         */
+                
+        if ((user->username[1] == '\0') && !IsAlpha(user->username[0])) 
+        {
+            sendto_realops_lev(REJ_LEV, "Invalid username: %s (%s@%s)",
+                               nick, user->username, user->host);
+            ircstp->is_ref++;
+            ircsprintf(tmpstr2, "Invalid username [%s]", user->username);
+            return exit_client(cptr, sptr, &me, tmpstr2);
+        }
+
+        if (!(user->allow->flags & CONF_FLAGS_SKIPCLONES) &&
+            (i = clones_check(cptr)))
+        {
+            ircstp->is_ref++;
+            return exit_client(cptr, sptr, &me, i == 1
+                               ? "Too many connections from your host"
+                               : "Too many connections from your site");
+        }
+
+        if(!(ban = check_userbanned(sptr, UBAN_IP|UBAN_CIDR4, UBAN_WILDUSER)))
+            ban = check_userbanned(sptr, UBAN_HOST, 0);
+
+        if(ban)
+        {
+            char *reason, *ktype;
+            int local;
+
+            local = (ban->flags & UBAN_LOCAL) ? 1 : 0;
+            ktype = local ? LOCAL_BANNED_NAME : NETWORK_BANNED_NAME;
+            reason = ban->reason ? ban->reason : ktype;
+
+            sendto_one(sptr, err_str(ERR_YOUREBANNEDCREEP), me.name, 
+                       sptr->name, ktype);
+            sendto_one(sptr, ":%s NOTICE %s :*** You are not welcome on"
+                             " this %s.", me.name, sptr->name, 
+                             local ? "server" : "network");
+            sendto_one(sptr, ":%s NOTICE %s :*** %s for %s",
+                       me.name, sptr->name, ktype, reason);
+            sendto_one(sptr, ":%s NOTICE %s :*** Your hostmask is %s!%s@%s",
+                       me.name, sptr->name, sptr->name, sptr->user->username,
+                       sptr->sockhost);
+            sendto_one(sptr, ":%s NOTICE %s :*** Your IP is %s",
+                       me.name, sptr->name, inetntoa((char *)&sptr->ip.s_addr));
+            sendto_one(sptr, ":%s NOTICE %s :*** For assistance, please"
+                             " email %s and include everything shown here.", 
+                       me.name, sptr->name,
+                       local ? Local_Kline_Address : Network_Kline_Address);
+
+            ircstp->is_ref++;
+            ircstp->is_ref_2++;
+
+            throttle_force(sptr->hostip);
+            return exit_client(cptr, sptr, &me, reason);
+        }
+
+        if(call_hooks(CHOOK_POSTACCESS, sptr) == FLUSH_BUFFER)
+            return FLUSH_BUFFER;
+
+        if ((++Count.local) > Count.max_loc) 
+        {
+            Count.max_loc = Count.local;
+            if (!(Count.max_loc % 10))
+                sendto_ops("New Max Local Clients: %d", Count.max_loc);
+        }
+        if ((NOW - Count.day) > 86400) 
+        {
+            Count.today = 0;
+            Count.day = NOW;
+        }
+        if ((NOW - Count.week) > 604800) 
+        {
+            Count.weekly = 0;
+            Count.week = NOW;
+        }
+        if ((NOW - Count.month) > 2592000) 
+        {
+            Count.monthly = 0;
+            Count.month = NOW;
+        }
+        if ((NOW - Count.year) > 31536000) 
+        {
+            Count.yearly = 0;
+            Count.year = NOW;
+        }
+        Count.today++;
+        Count.weekly++;
+        Count.monthly++;
+        Count.yearly++;
+        if(sptr->flags & FLAGS_BAD_DNS) 
+            sendto_realops_lev(SPY_LEV, "DNS lookup: %s (%s@%s) is a possible "
+                               "cache polluter", sptr->name, 
+                               sptr->user->username, sptr->user->host); 
+    }
+    else
+        strncpyzt(user->username, username, USERLEN + 1);
+
+    SetClient(sptr);
+    /* Increment our total user count here */
+    if (++Count.total > Count.max_tot)
+        Count.max_tot = Count.total;
+
+    if(IsInvisible(sptr)) Count.invisi++;
+        
+    if (MyConnect(sptr))
+    {
+        set_effective_class(sptr);
+#ifdef MAXBUFFERS
+        /* Let's try changing the socket options for the client here... */
+        reset_sock_opts(sptr->fd, 0);
+        /* End sock_opt hack */
+#endif
+        sendto_one(sptr, rpl_str(RPL_WELCOME), me.name, nick, Network_Name,
+                   nick, sptr->user->username, sptr->user->host);
+        /*
+         * This is a duplicate of the NOTICE but see below...
+         * um, why were we hiding it? they did make it on to the
+         * server and all.. -wd
+         */
+        sendto_one(sptr, rpl_str(RPL_YOURHOST), me.name, nick, me.name,
+                   version);
+#ifdef  IRCII_KLUDGE
+        /* Don't mess with this one - IRCII needs it! -Avalon */
+        sendto_one(sptr, "NOTICE %s :*** Your host is %s, running version %s",
+                   nick, me.name, version);
+#endif
+        sendto_one(sptr, rpl_str(RPL_CREATED), me.name, nick, creation);
+        sendto_one(sptr, rpl_str(RPL_MYINFO), me.name, parv[0],
+                   me.name, version);
+
+        send_rplisupport(sptr);
+
+#ifdef FORCE_EVERYONE_HIDDEN
+        sptr->umode |= UMODE_I;
+#endif
+
+#if (RIDICULOUS_PARANOIA_LEVEL>=1)
+        if(!BadPtr(sptr->passwd) && (pwaconf->flags & CONF_FLAGS_I_OPERPORT))
+            do 
+            {
+                char *onptr = sptr->passwd;
+                char *opptr;
+                char *onick;
+                char *tmpptr;
+                char tmppwd[PASSWDLEN + 1];
+                
+                if(!(opptr = strchr(onptr, ':')))
+                    break;
+                
+                *opptr++ = '\0';
+                if((tmpptr = strchr(opptr, ':')))
+                    *tmpptr++ = '\0';
+                if(check_oper_can_mask(sptr, onptr, opptr, &onick) != 0)
+                {
+                    sendto_one(sptr, ":%s NOTICE %s :*** Your hostname has "
+                               "been masked.",
+                               me.name, sptr->name);
+
+#ifdef DEFAULT_MASKED_HIDDEN
+                    sptr->umode |= UMODE_I;
+#endif
+
+                    throttle_remove(sptr->hostip);
+                    sptr->user->real_oper_host = 
+                        MyMalloc(strlen(sptr->user->host) + 1);
+                    sptr->user->real_oper_username = 
+                        MyMalloc(strlen(sptr->user->username) + 1);
+                    sptr->user->real_oper_ip = 
+                        MyMalloc(strlen(sptr->hostip) + 1);
+                    strcpy(sptr->user->real_oper_host, sptr->user->host);
+                    strcpy(sptr->user->real_oper_username, sptr->user->username);
+                    strcpy(sptr->user->real_oper_ip, sptr->hostip);
+                    strncpyzt(sptr->user->host, Staff_Address, HOSTLEN + 1);
+                    strncpyzt(sptr->user->username, onick, USERLEN + 1);
+                    strncpyzt(sptr->username, onick, USERLEN + 1);
+                    sptr->flags |= FLAGS_GOTID; /* fake ident */
+                    sptr->ip.s_addr = 0;
+                    strcpy(sptr->hostip, "0.0.0.0");
+                    strncpy(sptr->sockhost, Staff_Address, HOSTLEN + 1);
+                }
+
+                if(tmpptr)
+                {
+                    strcpy(tmppwd, tmpptr);
+                    strcpy(sptr->passwd, tmppwd);
+                }
+                else
+                    sptr->passwd[0] = '\0';
+            } while(0);
+#endif
+
+        sendto_realops_lev(CCONN_LEV, "Client connecting: %s (%s@%s) [%s] {%s}",
+                           nick, user->username, user->host, sptr->hostip,
+                           sptr->class->name);
+
+        send_lusers(sptr, sptr, 1, parv);
+                
+        sendto_one(sptr, ":%s NOTICE %s :*** Notice -- motd was last"
+                   " changed at %s", me.name, nick, motd_last_changed_date);
+        if(confopts & FLAGS_SMOTD)
+        {
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- Please read the"
+                             " motd if you haven't read it", me.name, nick);
+            sendto_one(sptr, rpl_str(RPL_MOTDSTART), me.name, parv[0], me.name);
+            if((smotd = shortmotd) == NULL)
+                sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv[0],
+                                    "*** This is the short motd ***");
+            else 
+                while (smotd) 
+                {
+                    sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv[0], 
+                                smotd->line);
+                    smotd = smotd->next;
+                }
+        
+            sendto_one(sptr, rpl_str(RPL_ENDOFMOTD), me.name, parv[0]);
+        }
+        else
+            send_motd(sptr, sptr, 1, parv);
+
+        if((confopts & FLAGS_WGMON) == FLAGS_WGMON)
+        {
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- This server runs an "
+                    "open proxy monitor to prevent abuse.", me.name, nick);
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- If you see"
+                    " connections on various ports from %s", me.name, 
+                    nick, ProxyMonHost);
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- please disregard"
+                    " them, as they are the monitor in action.", me.name, 
+                    nick);
+            sendto_one(sptr, ":%s NOTICE %s :*** Notice -- For more information"
+                    " please visit %s", me.name, nick, ProxyMonURL);
+        }
+                
+    }
+    else if (IsServer(cptr)) 
+    {
+        aClient    *acptr;
+        
+        if ((acptr = find_server(user->server, NULL)) &&
+            acptr->from != sptr->from)
+        {
+            sendto_realops_lev(DEBUG_LEV,
+                               "Bad User [%s] :%s USER %s@%s %s, != %s[%s]",
+                               cptr->name, nick, user->username,
+                               user->host, user->server,
+                               acptr->name, acptr->from->name);
+            sendto_one(cptr, ":%s KILL %s :%s (%s != %s[%s] USER from wrong "
+                       "direction)", me.name, sptr->name, me.name,
+                       user->server, acptr->from->name, acptr->from->sockhost);
+            sptr->flags |= FLAGS_KILLED;
+            return exit_client(sptr, sptr, &me, "USER server wrong direction");
+                        
+        }
+        /*
+         * Super GhostDetect: If we can't find the server the user is
+         * supposed to be on, then simply blow the user away.     -Taner
+         */
+        if (!acptr)
+        {
+            sendto_one(cptr,
+                       ":%s KILL %s :%s GHOST (no server %s on the net)",
+                       me.name, sptr->name, me.name, user->server);
+            sendto_realops("No server %s for user %s[%s@%s] from %s",
+                           user->server, sptr->name, user->username,
+                           user->host, sptr->from->name);
+            sptr->flags |= FLAGS_KILLED;
+            return exit_client(sptr, sptr, &me, "Ghosted Client");
+        }
+    }
+    send_umode(NULL, sptr, 0, SEND_UMODES, ubuf);
+    if (!*ubuf)
+    {
+        ubuf[0] = '+';
+        ubuf[1] = '\0';
+    }
+    hash_check_watch(sptr, RPL_LOGON);
+    sendto_serv_butone(cptr, "NICK %s %d %ld %s %s %s %s %lu %lu :%s",
+                               nick, sptr->hopcount + 1, sptr->tsinfo, ubuf,
+                               user->username, user->host, user->server, 
+                               sptr->user->servicestamp,
+                               htonl(sptr->ip.s_addr), sptr->info);
+   
+    if(MyClient(sptr))
+    {
+        /* if the I:line doesn't have a password and the user does
+         * send it over to NickServ
+         */
+        if(sptr->passwd[0] && (nsptr=find_person(NICKSERV,NULL))!=NULL)
+            sendto_one(nsptr,":%s PRIVMSG %s@%s :SIDENTIFY %s", sptr->name,
+                       NICKSERV, Services_Name, sptr->passwd);
+        
+        memset(sptr->passwd, '\0', PASSWDLEN);
+        
+        if (ubuf[1]) send_umode(cptr, sptr, 0, ALL_UMODES, ubuf);
+
+        if(call_hooks(CHOOK_POSTMOTD, sptr) == FLUSH_BUFFER)
+            return FLUSH_BUFFER;
+    }
+
+    if (sptr->ip.s_addr)
+        clones_add(sptr);
+
+    return 0;
+}
+
+char *exploits_2char[] =
+{
+    "js",
+    "pl",
+    NULL
+};
+char *exploits_3char[] = 
+{
+    "exe",
+    "com",
+    "bat",
+    "dll",
+    "ini",
+    "vbs",
+    "pif",
+    "mrc",
+    "scr",
+    "doc",
+    "xls",
+    "lnk",
+    "shs",
+    "htm",
+    "zip",
+    NULL
+};
+
+char *exploits_4char[] =
+{
+    "html",
+    NULL
+};
+
+static int
+allow_dcc(aClient *to, aClient *from)
+{
+    Link *lp;
+
+    for(lp = to->user->dccallow; lp; lp = lp->next)
+    {
+        if(lp->flags == DCC_LINK_ME && lp->value.cptr == from)
+            return 1;
+    }
+    return 0;
+}
+
+static int 
+check_dccsend(aClient *from, aClient *to, char *msg)
+{
+    /*
+     * we already know that msg will consist of "DCC SEND" so we can skip
+     * to the end
+     */
+    char *filename = msg + 8;
+    char *ext;
+    char **farray = NULL;
+    int arraysz;
+    int len = 0, extlen = 0, i;
+
+    /* people can send themselves stuff all the like..
+     * opers need to be able to send cleaner files 
+     * sanity checks..
+     */
+
+    if(from == to || !IsPerson(from) || IsAnOper(from) || !MyClient(to)) 
+        return 0;
+
+    while(*filename == ' ')
+        filename++;
+
+    if(!(*filename)) return 0;
+
+    while(*(filename + len) != ' ')
+    {
+        if(!(*(filename + len))) break;
+        len++;
+    }
+    
+    for(ext = filename + len;; ext--)
+    {
+        if(ext == filename)
+            return 0;
+
+        if(*ext == '.') 
+        {
+            ext++;
+            extlen--;
+            break;
+        }
+        extlen++;
+    }
+
+    switch(extlen)
+    {
+        case 0:
+            arraysz = 0;
+            break;
+
+        case 2:
+            farray = exploits_2char;
+            arraysz = 2;
+            break;
+
+        case 3:
+            farray = exploits_3char;
+            arraysz = 3;
+            break;
+
+        case 4:
+            farray = exploits_4char;
+            arraysz = 4;
+            break;
+
+        /* no executable file here.. */
+        default:
+            return 0;
+    }
+
+    if (arraysz != 0)
+    {
+        for(i = 0; farray[i]; i++)
+        {
+            if(myncmp(farray[i], ext, arraysz) == 0)
+            break;
+        }
+
+        if(farray[i] == NULL)
+            return 0;
+    }
+
+    if(!allow_dcc(to, from))
+    {
+        char tmpext[8];
+        char tmpfn[128];
+        Link *tlp, *flp;
+        aChannel *chptr = NULL;
+
+        strncpy(tmpext, ext, extlen);
+        tmpext[extlen] = '\0';
+
+        if(len > 127) 
+            len = 127;
+        strncpy(tmpfn, filename, len);
+        tmpfn[len] = '\0';
+
+        /* use notices! 
+         *   server notices are hard to script around.
+         *   server notices are not ignored by clients.
+         */ 
+
+        sendto_one(from, ":%s NOTICE %s :The user %s is not accepting DCC "
+                   "sends of filetype *.%s from you.  Your file %s was not "
+                   "sent.", me.name, from->name, to->name, tmpext, tmpfn);
+
+        sendto_one(to, ":%s NOTICE %s :%s (%s@%s) has attempted to send you a "
+                   "file named %s, which was blocked.", me.name, to->name,
+                   from->name, from->user->username, from->user->host, tmpfn);
+
+        if(!SeenDCCNotice(to))
+        {
+            SetDCCNotice(to);
+            sendto_one(to, ":%s NOTICE %s :The majority of files sent of this "
+                       "type are malicious viruses and trojan horses."
+                       " In order to prevent the spread of this problem, we "
+                       "are blocking DCC sends of these types of"
+                       " files by default.", me.name, to->name);
+            sendto_one(to, ":%s NOTICE %s :If you trust %s, and want him/her "
+                       "to send you this file, you may obtain"
+                       " more information on using the dccallow system by "
+                       "typing /dccallow help",
+                       me.name, to->name, from->name, to->name);
+        }
+        
+        for(tlp = to->user->channel; tlp && !chptr; tlp = tlp->next)
+        {
+            for(flp = from->user->channel; flp && !chptr; flp = flp->next)
+            {
+                if(tlp->value.chptr == flp->value.chptr)
+                    chptr = tlp->value.chptr;
+            }
+        }
+        
+        if(chptr)
+            sendto_realops_lev(DCCSEND_LEV, "%s (%s@%s) sending forbidden "
+                               "filetyped file %s to %s (channel %s)",
+                               from->name, from->user->username,
+                               from->user->host, tmpfn, to->name,
+                               chptr->chname); 
+        else
+            sendto_realops_lev(DCCSEND_LEV, "%s (%s@%s) sending forbidden "
+                               "filetyped file %s to %s", from->name, 
+                               from->user->username, from->user->host, tmpfn,
+                               to->name); 
+
+        return 1;
+    }
+    return 0;
+}
+
+/*
+ * check target limit: message target rate limiting
+ * anti spam control!
+ * should only be called for local PERSONS!
+ * sptr: client sending message
+ * acptr: client receiving message
+ *
+ * return value:
+ * 1: block
+ * 0: do nothing
+ */
+
+#ifdef MSG_TARGET_LIMIT
+int check_target_limit(aClient *sptr, aClient *acptr)
+{
+    int ti;
+    int max_targets;
+    time_t tmin = MSG_TARGET_TIME;  /* minimum time to wait before
+                                    * another message can be sent */
+
+    /* don't limit opers, people talking to themselves,
+     * or people talking to services */
+    if(IsOper(sptr) || sptr == acptr || IsULine(acptr) || NoMsgThrottle(sptr))
+        return 0;
+
+    max_targets = ((NOW - sptr->firsttime) > MSG_TARGET_MINTOMAXTIME) 
+                 ? MSG_TARGET_MAX : MSG_TARGET_MIN;
+
+    for(ti = 0; ti < max_targets; ti++)
+    {
+        if (sptr->targets[ti].cli == NULL || sptr->targets[ti].cli == acptr || 
+            sptr->targets[ti].sent < (NOW - MSG_TARGET_TIME))
+        {
+            sptr->targets[ti].cli = acptr;
+            sptr->targets[ti].sent = NOW;
+            break;
+        }
+        else if((NOW - sptr->targets[ti].sent) < tmin)
+            tmin = NOW - sptr->targets[ti].sent;
+    }
+
+    if(ti == max_targets)
+    {
+        sendto_one(sptr, err_str(ERR_TARGETTOFAST), me.name, sptr->name,
+                   acptr->name, MSG_TARGET_TIME - tmin);
+        sptr->since += 2; /* penalize them 2 seconds for this! */
+        sptr->num_target_errors++;
+
+        if(sptr->last_target_complain + 60 <= NOW)
+        {
+            sendto_realops_lev(SPAM_LEV, "Target limited: %s (%s@%s)"
+                               " [%d failed targets]", sptr->name,
+                                sptr->user->username, sptr->user->host, 
+                                sptr->num_target_errors);
+            sptr->num_target_errors = 0;
+            sptr->last_target_complain = NOW;
+        }
+        return 1;
+    }
+
+    return 0;
+}
+#endif
+
+/*
+ * m_message (used in m_private() and m_notice()) the general
+ * function to deliver MSG's between users/channels
+ * 
+ * parv[0] = sender prefix
+ * parv[1] = receiver list
+ * parv[2] = message text
+ * 
+ * massive cleanup * rev argv 6/91
+ * 
+ */
+/*
+ * This function checks to see if a CTCP message (other than ACTION) is
+ * contained in the passed string.  This might seem easier than I am
+ * doing it, but a CTCP message can be changed together, even after a
+ * normal message.
+ *
+ * If the message is found, and it's a DCC message, pass it back in
+ * *dccptr.
+ *
+ * Unfortunately, this makes for a bit of extra processing in the
+ * server.
+ */
+static int 
+check_for_ctcp(char *str, char **dccptr)
+{
+    char       *p = str;
+
+    while ((p = strchr(p, 1)) != NULL)
+    {
+        if (myncmp(++p, "DCC", 3) == 0)
+        {
+            if(dccptr)
+                *dccptr = p;
+            if(myncmp(p+3, " SEND", 5) == 0)
+                return CTCP_DCCSEND;
+            else
+                return CTCP_DCC;
+        }
+        if (myncmp(++p, "ACTION", 6) != 0)
+            return CTCP_YES;
+        if ((p = strchr(p, 1)) == NULL)
+            return CTCP_NONE;
+        if(!(*(++p)))
+            break;;
+    }
+    return CTCP_NONE;
+}
+
+/* is_silenced - Returns 1 if a sptr is silenced by acptr */
+static int
+is_silenced(aClient *sptr, aClient *acptr)
+{
+    Link *lp;
+    anUser *user;
+    char sender[HOSTLEN+NICKLEN+USERLEN+5];
+
+    if (!(acptr->user)||!(lp=acptr->user->silence)||!(user=sptr->user))
+        return 0;
+    ircsprintf(sender,"%s!%s@%s",sptr->name,user->username,user->host);
+    while(lp)
+    {
+        if (!match(lp->value.cp, sender))
+        {
+            if (!MyConnect(sptr))
+            {
+                sendto_one(sptr->from, ":%s SILENCE %s :%s",acptr->name,
+                           sptr->name, lp->value.cp);
+                lp->flags = 1;
+            }
+            return 1;
+        }
+        lp = lp->next;
+    }
+    return 0;
+}
+
+static inline void 
+send_msg_error(aClient *sptr, char *parv[], char *nick, int ret) 
+{
+    if(ret == ERR_NOCOLORSONCHAN)
+        sendto_one(sptr, err_str(ERR_NOCOLORSONCHAN), me.name,
+                   parv[0], nick, parv[2]);
+    else if(ret == ERR_NEEDREGGEDNICK)
+        sendto_one(sptr, err_str(ERR_NEEDREGGEDNICK), me.name,
+                   parv[0], nick, "speak in", NS_Services_Name,
+                   NS_Register_URL);
+    else
+        sendto_one(sptr, err_str(ERR_CANNOTSENDTOCHAN), me.name,
+                   parv[0], nick);
+}
+
+static inline int 
+m_message(aClient *cptr, aClient *sptr, int parc, char *parv[], int notice)
+{
+    aClient *acptr;
+    char *s;
+    int i, ret, ischan, ismine;
+    aChannel *chptr;
+    char *nick, *server, *p, *cmd, *dccmsg;
+
+    cmd = notice ? MSG_NOTICE : MSG_PRIVATE;
+    ismine = MyClient(sptr);
+
+    if (parc < 2 || *parv[1] == '\0') 
+    {
+        sendto_one(sptr, err_str(ERR_NORECIPIENT),
+                   me.name, parv[0], cmd);
+        return -1;
+    }
+    
+    if (parc < 3 || *parv[2] == '\0') 
+    {
+        sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+        return -1;
+    }
+
+    if (ismine) 
+    {
+        /* if its a spambot, just ignore it */
+        if ((IsSquelch(sptr))
+#if defined(ANTI_SPAMBOT) && !defined(ANTI_SPAMBOT_WARN_ONLY)
+            || (sptr->join_leave_count >= MAX_JOIN_LEAVE_COUNT)
+#endif
+            ) 
+        {
+            if (mycmp(parv[0],parv[1])) 
+            {
+                if (IsWSquelch(sptr))
+                    sendto_one(sptr, ":%s NOTICE %s :You are currently "
+                               "squelched.  Message not sent.", me.name,
+                               parv[0]);                
+                return 0;
+            }
+        }
+
+        parv[1] = canonize(parv[1]);
+    }
+
+    if(ismine && call_hooks(CHOOK_MSG, sptr, notice, parv[2]) == FLUSH_BUFFER)
+        return FLUSH_BUFFER;
+
+    for (p = NULL, nick = strtoken(&p, parv[1], ","), i = 0; nick && i<20 ;
+         nick = strtoken(&p, NULL, ",")) 
+    {
+        /*
+         * If someone is spamming via "/msg nick1,nick2,nick3,nick4 SPAM"
+         * (or even to channels) then subject them to flood control!
+         * -Taner
+         */
+        if (ismine && i++ > 10 && !NoMsgThrottle(sptr))
+#ifdef NO_OPER_FLOOD
+            if (!IsAnOper(sptr))        
+#endif
+                sptr->since += 4;               
+
+        /* channel msg? */
+        ischan = IsChannelName(nick);
+        if (ischan && (chptr = find_channel(nick,NullChn))) 
+        {
+            if(ismine && call_hooks(CHOOK_CHANMSG, sptr, chptr, 
+                        notice, parv[2]) == FLUSH_BUFFER)
+                return FLUSH_BUFFER;
+
+            if (!notice)
+                switch(check_for_ctcp(parv[2], NULL))
+                {
+                    case CTCP_NONE:
+                        break;
+
+                    case CTCP_DCCSEND:
+                    case CTCP_DCC:
+                        sendto_one(sptr, ":%s NOTICE %s :You may not send a"
+                                   " DCC command to a channel (%s)", 
+                                   me.name, parv[0], nick);
+                        continue;
+
+                    default:
+#ifdef FLUD
+                        if(!(confopts & FLAGS_HUB))
+                            if (check_for_flud(sptr, NULL, chptr, 1))
+                                return 0;
+#endif
+                        break;
+                }
+            ret = IsULine(sptr) ? 0 : can_send(sptr, chptr, parv[2]);
+
+            if(ret)
+            {
+                if(!notice)
+                    send_msg_error(sptr, parv, nick, ret);
+            }
+            else
+                sendto_channel_butone(cptr, sptr, chptr, ":%s %s %s :%s",
+                                      parv[0], cmd, nick, parv[2]);
+            continue;
+        }
+        
+        /* nickname addressed? */
+        if (!ischan)
+        {
+            if ((acptr = find_client(nick, NULL))) 
+            {
+                /* A PRIVMSG or NOTICE to me.name! */
+                if (IsMe(acptr) && ismine)
+                {
+                    if(call_hooks(CHOOK_MYMSG, sptr, notice, parv[2]) 
+                                    == FLUSH_BUFFER)
+                        return FLUSH_BUFFER;
+                    continue;
+                }
+                if(ismine && call_hooks(CHOOK_USERMSG, sptr, acptr, notice, 
+                               parv[2]) == FLUSH_BUFFER)
+                    return FLUSH_BUFFER;
+
+
+                if (!IsClient(acptr))
+                    acptr = NULL;
+            }
+
+            if(acptr)
+            {
+                if (IsNoNonReg(acptr) && !IsRegNick(sptr) && !IsULine(sptr) &&
+                    !IsOper(sptr))
+                {
+                    sendto_one(sptr, rpl_str(ERR_NONONREG), me.name, parv[0],
+                                acptr->name);
+                    continue;
+                }
+#ifdef MSG_TARGET_LIMIT
+                /* Only check target limits for my clients */
+                if (ismine && check_target_limit(sptr, acptr))
+                    continue;
+#endif
+#ifdef FLUD
+                if (!notice && MyFludConnect(acptr))
+#else
+                if (!notice && MyConnect(acptr))
+#endif
+                {
+                    switch(check_for_ctcp(parv[2], &dccmsg))
+                    {
+                        case CTCP_NONE:
+                            break;
+                    
+                        case CTCP_DCCSEND:
+#ifdef FLUD
+                            if (check_for_flud(sptr, acptr, NULL, 1))
+                                return 0;
+#endif
+                            if(check_dccsend(sptr, acptr, dccmsg))
+                                continue;
+                            break;
+                    
+                        default:
+#ifdef FLUD
+                            if (check_for_flud(sptr, acptr, NULL, 1))
+                                return 0;
+#endif
+                             break;
+                    }
+                }
+#ifdef DENY_SERVICES_MSGS
+                if(ismine)
+                {
+                    char *tservice = NULL;
+                    if(!mycmp(NICKSERV, nick))
+                        tservice = NICKSERV;
+                    else if(!mycmp(CHANSERV, nick))
+                        tservice = CHANSERV;
+                    else if(!mycmp(MEMOSERV, nick))
+                        tservice = MEMOSERV;
+                    else if(!mycmp(ROOTSERV, nick))
+                        tservice = ROOTSERV;
+
+                    if(tservice != NULL)
+                    {
+                        if(!notice)
+                            sendto_one(sptr, rpl_str(ERR_MSGSERVICES), 
+                                       me.name, parv[0], tservice, tservice, 
+                                       Services_Name, tservice);
+                        continue;
+                    }
+                }
+#endif
+                if (!is_silenced(sptr, acptr)) 
+                {                          
+                    if (!notice && MyClient(acptr) && acptr->user &&
+                            acptr->user->away)
+                        sendto_one(sptr, rpl_str(RPL_AWAY), me.name, parv[0],
+                                 acptr->name, acptr->user->away);
+                        sendto_prefix_one(acptr, sptr, ":%s %s %s :%s", 
+                                 parv[0], cmd, nick, parv[2]);
+                }
+                continue;
+            }
+        } /* if(!ischan) */
+        
+        if (nick[1] == '#' && nick[0]!='#') 
+        {
+            if (nick[0] == '@') 
+            {
+                if ((chptr = find_channel(nick + 1, NullChn))) 
+                {
+                    ret = IsULine(sptr) ? 0 : can_send(sptr, chptr, parv[2]);
+                    if (ret)
+                    {
+                        if(!notice)
+                            send_msg_error(sptr, parv, nick, ret);
+                    }
+                    else 
+                        sendto_channelops_butone(cptr, sptr, chptr,
+                                                 ":%s %s %s :%s", parv[0], cmd,
+                                                 nick, parv[2]);
+                }
+            }
+            else if (nick[0] == '+') 
+            {
+                if ((chptr = find_channel(nick + 1, NullChn))) 
+                {
+                    ret = IsULine(sptr) ? 0 : can_send(sptr, chptr, parv[2]);
+                    if (ret)
+                    {
+                        if(!notice)
+                            send_msg_error(sptr, parv, nick, ret);
+                    }
+                    else
+                        sendto_channelvoice_butone(cptr, sptr, chptr, 
+                                                   ":%s %s %s :%s", parv[0],
+                                                   cmd, nick, parv[2]);
+                }
+            }
+            else
+                sendto_one_services(sptr, err_str(ERR_NOSUCHNICK), 
+                                    me.name, parv[0], nick);
+            continue;
+        }
+        if (nick[0] == '@' && nick[1] == '+' && nick[2] == '#') 
+        {
+            if ((chptr = find_channel(nick + 2, NullChn))) 
+            {
+                ret = IsULine(sptr) ? 0 : can_send(sptr, chptr, parv[2]);
+                if (ret)
+                {
+                    if(!notice)
+                        send_msg_error(sptr, parv, nick, ret);
+                }
+                else
+                    sendto_channelvoiceops_butone(cptr, sptr, chptr,
+                                                  ":%s %s %s :%s", parv[0],
+                                                  cmd, nick, parv[2]);
+
+            }
+            else
+                sendto_one_services(sptr, err_str(ERR_NOSUCHNICK), 
+                                    me.name, parv[0], nick);
+            continue;
+        }
+                
+        if(IsAnOper(sptr)) 
+        {
+            /*
+             * the following two cases allow masks in NOTICEs
+             * (for OPERs only) 
+             * 
+             * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
+             */
+            if ((*nick == '$' || *nick == '#')) 
+            {
+                if (!(s = (char *) strrchr(nick, '.'))) 
+                {
+                    sendto_one(sptr, err_str(ERR_NOTOPLEVEL), me.name,
+                               parv[0], nick);
+                    continue;
+                }
+                while (*++s)
+                    if (*s == '.' || *s == '*' || *s == '?')
+                        break;
+                if (*s == '*' || *s == '?') 
+                {
+                    sendto_one(sptr, err_str(ERR_WILDTOPLEVEL), me.name,
+                               parv[0], nick);
+                    continue;
+                }
+                sendto_match_butone(IsServer(cptr) ? cptr : NULL, sptr,
+                                    nick + 1, 
+                                    (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
+                                    ":%s %s %s :%s", parv[0],
+                                    cmd, nick, parv[2]);
+                continue;
+            }
+        }
+        
+        /* user@server addressed? */
+        if (!ischan && (server = (char *) strchr(nick, '@')) &&
+            (acptr = find_server(server + 1, NULL))) 
+        {
+            /* Not destined for a user on me :-( */
+            if (!IsMe(acptr)) 
+            {
+                if(confopts & FLAGS_SERVHUB)
+                {
+                    char *myparv[2];
+
+                    if(mycmp(server+1, Services_Name)==0) 
+                    {
+                        if(!mycmp(nick,NS_Services_Name)) 
+                        {
+                            myparv[0]=parv[0];
+                            myparv[1]=parv[2];                    
+                            m_ns(cptr, sptr, parc-1, myparv);  
+                        } 
+                        else if(!mycmp(nick,CS_Services_Name)) 
+                        {
+                            myparv[0]=parv[0];
+                            myparv[1]=parv[2];                    
+                            m_cs(cptr, sptr, parc-1, myparv);  
+                        } 
+                        else if(!mycmp(nick,MS_Services_Name)) 
+                        {
+                            myparv[0]=parv[0];
+                            myparv[1]=parv[2];                    
+                            m_ms(cptr, sptr, parc-1, myparv);  
+                        } 
+                        else if(!mycmp(nick,RS_Services_Name)) 
+                        {
+                            myparv[0]=parv[0];
+                            myparv[1]=parv[2];                    
+                            m_rs(cptr, sptr, parc-1, myparv);  
+                        } 
+                        else 
+                        {
+                            sendto_one(acptr, ":%s %s %s :%s", parv[0], cmd,
+                                       nick, parv[2]);
+                        }
+                        continue;                    
+                    }
+                }
+                sendto_one(acptr, ":%s %s %s :%s", parv[0], cmd, nick, parv[2]);
+                continue;
+            }
+            *server = '\0';
+                        
+            /*
+             * Look for users which match the destination host 
+             * (no host == wildcard) and if one and one only is found
+             * connected to me, deliver message!
+             */
+            acptr = find_person(nick, NULL);
+            if (server)
+                *server = '@';
+            if (acptr) 
+            {
+                /*
+                 * Don't allow a /msg nick@server (we weren't before, anyway)
+                 *
+                 * if (count == 1)
+                 *    sendto_prefix_one(acptr, sptr, ":%s %s %s :%s", parv[0],
+                 *                      cmd, nick, parv[2]);
+                 */
+                if (!notice)
+                    sendto_one(sptr, err_str(ERR_TOOMANYTARGETS), me.name,
+                               parv[0], nick);
+                continue;
+            }
+        }
+        sendto_one_services(sptr, err_str(ERR_NOSUCHNICK), me.name, 
+                            parv[0], nick);
+    }
+    if ((i > 20) && sptr->user)
+        sendto_realops_lev(SPY_LEV, "User %s (%s@%s) tried to msg %d users",
+                           sptr->name, sptr->user->username, sptr->user->host,
+                           i);
+    return 0;
+}
+
+/*
+ * m_private 
+ * parv[0] = sender prefix 
+ * parv[1] = receiver list 
+ * parv[2] = message text
+ */
+
+int 
+m_private(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    return m_message(cptr, sptr, parc, parv, 0);
+}
+
+/*
+ * m_notice *
+ * parv[0] = sender prefix 
+ * parv[1] = receiver list
+ * parv[2] = notice text
+ */
+
+int 
+m_notice(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    return m_message(cptr, sptr, parc, parv, 1);
+}
+
+
+/*
+ * m_whois 
+ * parv[0] = sender prefix 
+ * parv[1] = nickname masklist
+ */
+int 
+m_whois(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    Link   *lp;
+    anUser *user;
+    aClient    *acptr, *a2cptr;
+    aChannel   *chptr;
+    char       *nick, *tmp, *name;
+    char       *p = NULL;
+    int         len, mlen;
+
+    if (parc < 2)
+    {
+        sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN),
+                   me.name, parv[0]);
+        return 0;
+    }
+
+    if (parc > 2)
+    {
+#ifdef NO_USER_OPERTARGETED_COMMANDS
+        /*
+         * Block /whois <anything> <nick1,nick2,nick3>
+         * Also block /whois <server> <nick> for +I users
+         */
+        if(!IsAnOper(sptr))
+        {
+            acptr = hash_find_client(parv[2], (aClient *) NULL);
+            if (!acptr || !IsPerson(acptr))
+            {
+                sendto_one(sptr, err_str(ERR_NOSUCHNICK),
+                           me.name, parv[0], parv[2]);
+                return 0;
+            }
+
+            if(IsUmodeI(acptr))
+            {
+                /* allow /whois nick nick, but nothing else */
+                if(mycmp(parv[1], parv[2]) == 0)
+                    parv[1] = acptr->user->server; /* And kludge it */
+                else if(MyClient(sptr))
+                {
+                    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, 
+                               parv[0]);
+                    return 0;
+                }
+            }
+        }
+#endif
+        if (hunt_server(cptr, sptr, ":%s WHOIS %s :%s", 1, parc, parv) !=
+            HUNTED_ISME)
+            return 0;
+        parv[1] = parv[2];    
+    }
+
+    for (p = NULL, tmp = parv[1]; (nick = strtoken(&p, tmp, ",")); tmp = NULL)
+    {
+        int invis, member, showchan;
+                
+        acptr = hash_find_client(nick, (aClient *) NULL);
+        if (!acptr || !IsPerson(acptr))
+        {
+            sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
+            continue;
+        }
+                
+        user = acptr->user;
+        name = (!*acptr->name) ? "?" : acptr->name;
+        invis = IsInvisible(acptr);
+        member = (user->channel) ? 1 : 0;
+                
+        a2cptr = acptr->uplink;
+                
+        sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, parv[0], name,
+                   user->username, user->host, acptr->info);
+#if (RIDICULOUS_PARANOIA_LEVEL>=1)
+#if (RIDICULOUS_PARANOIA_LEVEL==1)
+        if(MyConnect(acptr) && user->real_oper_host && 
+                (IsAdmin(sptr) || (sptr == acptr)))
+            sendto_one(sptr, rpl_str(RPL_WHOISACTUALLY), me.name, sptr->name, 
+                       name, user->real_oper_username, user->real_oper_host, 
+                       user->real_oper_ip);
+#endif
+#if (RIDICULOUS_PARANOIA_LEVEL==2)
+        if(MyConnect(acptr) && user->real_oper_host && 
+                (IsAdmin(sptr) || (sptr == acptr)) && MyConnect(sptr))
+            sendto_one(sptr, rpl_str(RPL_WHOISACTUALLY), me.name, sptr->name, 
+                       name, user->real_oper_username, user->real_oper_host,
+                       user->real_oper_ip);
+#endif
+#endif          
+        mlen = strlen(me.name) + strlen(parv[0]) + 6 + strlen(name);
+        for (len = 0, *buf = '\0', lp = user->channel; lp; lp = lp->next)
+        {
+            chptr = lp->value.chptr;
+            showchan=ShowChannel(sptr,chptr);
+            if (showchan || IsAdmin(sptr))
+            {
+                if (len + strlen(chptr->chname) > (size_t) BUFSIZE - 4 - mlen)
+                {
+                    sendto_one(sptr, ":%s %d %s %s :%s", me.name, 
+                               RPL_WHOISCHANNELS, parv[0], name, buf);
+                    *buf = '\0';
+                    len = 0;
+                }
+                if(!showchan) /* if we're not really supposed to show the chan
+                               * but do it anyways, mark it as such! */
+                    *(buf + len++) = '%';
+                if (is_chan_op(acptr, chptr))
+                    *(buf + len++) = '@';
+                else if (has_voice(acptr, chptr))
+                    *(buf + len++) = '+';
+                if (len)
+                    *(buf + len) = '\0';
+                strcpy(buf + len, chptr->chname);
+                len += strlen(chptr->chname);
+                strcat(buf + len, " ");
+                len++;
+            }
+        }
+        if (buf[0] != '\0')
+            sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS), me.name, 
+                       parv[0], name, buf);
+        if(!(IsUmodeI(acptr) && !IsAnOper(sptr)) || (acptr == sptr))
+        {       
+             sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name, parv[0], name,
+                     user->server, a2cptr ? a2cptr->info : "*Not On This Net*");
+        }
+        else /* hidden oper! */
+        {       
+             sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name, parv[0], 
+                        name, HIDDEN_SERVER_NAME, HIDDEN_SERVER_DESC);
+        }
+        
+        if(IsAnOper(sptr) && IsSquelch(acptr))
+            sendto_one(sptr, rpl_str(RPL_WHOISTEXT), me.name, parv[0], 
+                       IsWSquelch(acptr) ?  "User is squelched (warned)" :
+                       "User is squelched (silent)");
+        
+        if(IsRegNick(acptr))
+            sendto_one(sptr, rpl_str(RPL_WHOISREGNICK), me.name, parv[0], name);
+        if (user->away)
+            sendto_one(sptr, rpl_str(RPL_AWAY), me.name, parv[0], name, 
+                       user->away);
+        
+        buf[0] = '\0';
+        if (IsAnOper(acptr))
+            strcat(buf, "an IRC Operator");
+        if (IsAdmin(acptr))
+            strcat(buf, " - Server Administrator");
+        else if (IsSAdmin(acptr))
+            strcat(buf, " - Services Administrator");
+        if (buf[0])
+            sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR), me.name, parv[0], 
+                       name, buf);
+        
+        /* don't give away that this oper is on this server if they're hidden! */
+        if (acptr->user && MyConnect(acptr) && ((sptr == acptr) || 
+                !IsUmodeI(acptr) || (parc > 2) || IsAnOper(sptr)))
+            sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name, parv[0], name,
+                       timeofday - user->last, acptr->firsttime);
+        
+        continue;
+    }
+    sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]);
+    return 0;
+}
+
+/*
+ * m_user 
+ * parv[0] = sender prefix
+ * parv[1] = username (login name, account) 
+ * parv[2] = client host name (used only from other servers) 
+ * parv[3] = server host name (used only from other servers)
+ * parv[4] = users real name info
+ */
+int 
+m_user(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+#define UFLAGS  (UMODE_i|UMODE_w|UMODE_s)
+    char       *username, *host, *server, *realname;
+    struct simBan *ban;
+    
+    if (parc > 2 && (username = (char *) strchr(parv[1], '@')))
+        *username = '\0';
+    if (parc < 5 || *parv[1] == '\0' || *parv[2] == '\0' ||
+        *parv[3] == '\0' || *parv[4] == '\0')
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "USER");
+        if (IsServer(cptr))
+            sendto_realops("bad USER param count for %s from %s",
+                           parv[0], get_client_name(cptr, FALSE));
+        else
+            return 0;
+    }
+    /* Copy parameters into better documenting variables */   
+    username = (parc < 2 || BadPtr(parv[1])) ? "<bad-boy>" : parv[1];
+    host = (parc < 3 || BadPtr(parv[2])) ? "<nohost>" : parv[2];
+    server = (parc < 4 || BadPtr(parv[3])) ? "<noserver>" : parv[3];
+    realname = (parc < 5 || BadPtr(parv[4])) ? "<bad-realname>" : parv[4];
+    if ((ban = check_mask_simbanned(realname, SBAN_GCOS))) 
+        return exit_client(cptr, sptr, sptr, BadPtr(ban->reason) ?
+                           "Bad GCOS: Reason unspecified" : ban->reason);
+    return do_user(parv[0], cptr, sptr, username, host, server, 0,0, realname);
+}
+
+/* do_user */
+int 
+do_user(char *nick, aClient *cptr, aClient *sptr, char *username, char *host, 
+        char *server, unsigned long serviceid, unsigned int ip, char *realname)
+{
+    anUser     *user;
+    
+    long        oflags;
+    
+    user = make_user(sptr);
+    oflags = sptr->umode;
+    
+    /*
+     * changed the goto into if-else...   -Taner 
+     * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GOOD FOR YOU Taner!!! - Dianora 
+     */
+    
+    if (!MyConnect(sptr))
+    {
+        user->server = find_or_add(server);
+        strncpyzt(user->host, host, sizeof(user->host));
+    } 
+    else
+    {
+        if (!IsUnknown(sptr))
+        {
+            sendto_one(sptr, err_str(ERR_ALREADYREGISTRED),
+                       me.name, nick);
+            return 0;
+        }
+        sptr->umode |= (USER_UMODES & atoi(host));
+#ifndef NO_DEFAULT_INVISIBLE
+        sptr->umode |= UMODE_i;
+#endif
+#ifdef NO_USER_SERVERKILLS
+        sptr->umode &= ~UMODE_k;
+#endif
+#ifdef NO_USER_OPERKILLS
+        sptr->umode &= ~UMODE_s;
+#endif
+        strncpyzt(user->host, host, sizeof(user->host));
+        user->server = me.name;
+    }
+    strncpyzt(sptr->info, realname, sizeof(sptr->info));
+    
+    sptr->user->servicestamp = serviceid;
+    if (!MyConnect(sptr))  
+    {
+        sptr->ip.s_addr=ntohl(ip);
+        
+        /* add non-local clients to the throttle checker.  obviously, we only
+         * do this for REMOTE clients!@$$@!  throttle_check() is called
+         * elsewhere for the locals! -wd */
+#ifdef THROTTLE_ENABLE
+        if (ip != 0) 
+           throttle_check(inetntoa((char *)&sptr->ip), -1, sptr->tsinfo);
+#endif
+    }
+    if(MyConnect(sptr))
+        sptr->oflag=0;
+    if (sptr->name[0])          /* NICK already received, now I have USER... */
+        return register_user(cptr, sptr, sptr->name, username);
+    else
+        strncpyzt(sptr->user->username, username, USERLEN + 1);
+    return 0;
+}
+
+/*
+ * m_quit 
+ * parv[0] = sender prefix 
+ * parv[1] = comment
+ */
+int 
+m_quit(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char *reason = (parc > 1 && parv[1]) ? parv[1] : cptr->name;
+    char  comment[TOPICLEN + 1];
+    
+    sptr->flags |= FLAGS_NORMALEX;
+    if (!IsServer(cptr))
+    {
+        strcpy(comment, "Quit: ");
+        strncpy(comment + 6, reason, TOPICLEN - 6); 
+        comment[TOPICLEN] = 0;
+        return exit_client(cptr, sptr, sptr, comment);
+    }
+    else
+        return exit_client(cptr, sptr, sptr, reason);
+}
+
+/*
+ * m_kill 
+ * parv[0] = sender prefix 
+ * parv[1] = kill victim 
+ * parv[2] = kill path
+ */
+int 
+m_kill(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient    *acptr;
+    char       *user, *path, *p, *nick, *reason;
+    char        mypath[KILLLEN + 1];
+    char        mymsg[KILLLEN + 1];
+    char       *unknownfmt = "<Unknown>";       /*
+                                                 * AFAIK this shouldnt happen
+                                                 * but -Raist 
+                                                 */
+    int         chasing = 0, kcount = 0;
+    
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KILL");
+        return 0;
+    }
+    
+    user = parv[1];
+    path = parv[2];             /* Either defined or NULL (parc >= 2!!) */
+    
+    if (!IsPrivileged(cptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+
+    if (!BadPtr(path))
+        if (strlen(path) > (size_t) KILLLEN)
+            path[KILLLEN] = '\0';
+
+    if (MyClient(sptr))
+        user = canonize(user);
+    for (p = NULL, nick = strtoken(&p, user, ","); nick; 
+         nick = strtoken(&p, NULL, ","))
+    {
+        chasing = 0;
+        if (!(acptr = find_client(nick, NULL)))
+        {
+            /*
+             * If the user has recently changed nick, we automaticly
+             * rewrite the KILL for this new nickname--this keeps
+             * servers in synch when nick change and kill collide
+             */
+            if (!(acptr = get_history(nick, (long) KILLCHASETIMELIMIT)))
+            {
+                sendto_one(sptr, err_str(ERR_NOSUCHNICK),
+                           me.name, parv[0], nick);
+                return 0;
+            }
+            sendto_one(sptr, ":%s NOTICE %s :KILL changed from %s to %s",
+                       me.name, parv[0], nick, acptr->name);
+            chasing = 1;
+        }
+        if((!MyConnect(acptr) && MyClient(cptr) && !OPCanGKill(cptr)) ||
+            (MyConnect(acptr) && MyClient(cptr) && !OPCanLKill(cptr)))
+        {
+            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            continue;
+        }
+        if(IsServer(acptr) || IsMe(acptr) || (MyClient(sptr) && IsULine(acptr)))
+        {
+            sendto_one(sptr, err_str(ERR_CANTKILLSERVER),
+                       me.name, parv[0]);
+            continue;
+        }
+        kcount++;
+        if (!IsServer(sptr) && (kcount > MAXKILLS))
+        {
+            sendto_one(sptr,":%s NOTICE %s :Too many targets, kill list was "
+                       "truncated. Maximum is %d.", me.name, sptr->name,
+                       MAXKILLS);
+            break;
+        }
+        if(MyClient(sptr)) 
+        {
+            char myname[HOSTLEN+1], *s;
+
+            if(!BadPtr(path))
+            {
+                ircsnprintf(mymsg, KILLLEN + 1, "(%s)", path);
+                reason = mymsg;
+            }
+            else
+                reason = "(No reason specified)";
+
+            strncpy(myname, me.name, HOSTLEN + 1);
+            if((s = strchr(myname, '.')))
+                *s = 0;
+            
+            ircsnprintf(mypath, KILLLEN + 1, "%s!%s!%s", myname, 
+                        sptr->user->host, sptr->name); 
+        }
+        else
+        {
+            if(BadPtr(path) || !(reason = strchr(path, ' ')))
+            {
+                path = sptr->name;
+                reason = "(No reason specified)";
+            }
+            else
+            {
+                *reason = '\0';
+                reason++;
+            }
+            strncpyzt(mypath, path, KILLLEN + 1);
+        }
+        /*
+         * Notify all *local* opers about the KILL, this includes the
+         * one originating the kill, if from this server--the special
+         * numeric reply message is not generated anymore.
+         * 
+         * Note: "acptr->name" is used instead of "user" because we may
+         * have changed the target because of the nickname change.
+         */
+        if (IsLocOp(sptr) && !MyConnect(acptr)) 
+        {
+            sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+            return 0;
+        }
+        if(IsULine(sptr))
+            sendto_realops_lev(USKILL_LEV, 
+                           "Received KILL message for %s!%s@%s. "
+                           "From %s Path: %s %s", acptr->name,
+                           acptr->user ? acptr->user->username : unknownfmt,
+                           acptr->user ? acptr->user->host : unknownfmt,
+                           parv[0], mypath, reason);
+        else if (IsAnOper(sptr))
+            sendto_ops_lev(0,
+                           "Received KILL message for %s!%s@%s. From %s "
+                           "Path: %s %s", acptr->name, 
+                           acptr->user ? acptr->user->username : unknownfmt,
+                           acptr->user ? acptr->user->host : unknownfmt,
+                           parv[0], mypath, reason);
+        else
+            sendto_ops_lev(SKILL_LEV, 
+                           "Received KILL message for %s!%s@%s. "
+                           "From %s Path: %s %s", acptr->name,
+                           acptr->user ? acptr->user->username : unknownfmt,
+                           acptr->user ? acptr->user->host : unknownfmt,
+                           parv[0], mypath, reason);
+                
+#if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
+        if (IsOper(sptr))
+            syslog(LOG_INFO, "KILL From %s!%s@%s For %s Path %s %s",
+                  parv[0], acptr->name,
+                  acptr->user ? acptr->user->username : unknownfmt,
+                  acptr->user ? acptr->user->host : unknownfmt, mypath, reason);
+#endif
+        /*
+         * And pass on the message to other servers. Note, that if KILL
+         * was changed, the message has to be sent to all links, also
+         * back. Suicide kills are NOT passed on --SRB
+         */
+        /*
+         * Set FLAGS_KILLED. This prevents exit_one_client from sending
+         * the unnecessary QUIT for this. ,This flag should never be
+         * set in any other place...
+         */
+        if(!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr))
+        {
+            sendto_serv_butone(cptr, ":%s KILL %s :%s %s",
+                               parv[0], acptr->name, mypath, reason);
+            if (chasing && IsServer(cptr))
+                sendto_one(cptr, ":%s KILL %s :%s %s",
+                           me.name, acptr->name, mypath, reason);
+            acptr->flags |= FLAGS_KILLED;
+        }
+        /*
+         * Tell the victim she/he has been zapped, but *only* if the
+         * victim is on current server--no sense in sending the
+         * notification chasing the above kill, it won't get far anyway
+         * as this user don't exist there any more either
+         */
+#ifndef HIDE_KILL_ORIGINS
+        if (MyConnect(acptr))
+            sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s %s",
+                              parv[0], acptr->name, mypath, reason);
+
+        if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr))
+            ircsprintf(buf2, "Local kill by %s %s", sptr->name, reason);
+        else 
+            ircsprintf(buf2, "Killed (%s %s)", sptr->name, reason);
+#else
+        if (MyConnect(acptr))
+            sendto_one(acptr, ":%s KILL %s :%s %s",
+                       HIDDEN_SERVER_NAME, acptr->name,
+                       HIDDEN_SERVER_NAME, reason);
+
+        ircsprintf(buf2, "Killed (%s %s)", HIDDEN_SERVER_NAME, reason);
+#endif
+
+        if (exit_client(cptr, acptr, sptr, buf2) == FLUSH_BUFFER)
+            return FLUSH_BUFFER;
+    }
+    return 0;
+}
+
+/***********************************************************************
+ * m_away() - Added 14 Dec 1988 by jto.
+ *            Not currently really working, I don't like this
+ *            call at all...
+ *
+ *            ...trying to make it work. I don't like it either,
+ *            but perhaps it's worth the load it causes to net.
+ *            This requires flooding of the whole net like NICK,
+ *            USER, MODE, etc messages...  --msa
+ *
+ *            Added FLUD-style limiting for those lame scripts out there.
+ ***********************************************************************/
+/*
+ * m_away 
+ * parv[0] = sender prefix 
+ * parv[1] = away message
+ */
+int 
+m_away(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char   *away, *awy2 = parv[1];
+    /* make sure the user exists */
+    if (!(sptr->user)) 
+    {
+        sendto_realops_lev(DEBUG_LEV, "Got AWAY from nil user, from %s (%s)\n",
+                           cptr->name, sptr->name);
+        return 0;
+    }
+    
+    away = sptr->user->away;
+    
+#ifdef NO_AWAY_FLUD
+    if(MyClient(sptr))
+    {
+        if ((sptr->alas + MAX_AWAY_TIME) < NOW)
+            sptr->acount = 0;
+        sptr->alas = NOW;
+        sptr->acount++;
+    }
+#endif 
+    
+    if (parc < 2 || !*awy2)
+    {
+        /* Marking as not away */
+        if (away) 
+        {
+            MyFree(away);
+            sptr->user->away = NULL;
+            /* Don't spam unaway unless they were away - lucas */
+            sendto_serv_butone_services(cptr, ":%s AWAY", parv[0]);
+        }
+        
+        if (MyConnect(sptr))
+            sendto_one(sptr, rpl_str(RPL_UNAWAY), me.name, parv[0]);
+        return 0;
+    }
+
+    /* Marking as away */
+#ifdef NO_AWAY_FLUD
+    /* we dont care if they are just unsetting away, hence this is here */
+    /* only care about local non-opers */
+    if (MyClient(sptr) && (sptr->acount > MAX_AWAY_COUNT) && !IsAnOper(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_TOOMANYAWAY), me.name, parv[0]);
+        return 0;
+    }
+#endif
+    if (strlen(awy2) > (size_t) TOPICLEN)
+        awy2[TOPICLEN] = '\0';
+    /*
+     * some lamers scripts continually do a /away, hence making a lot of
+     * unnecessary traffic. *sigh* so... as comstud has done, I've
+     * commented out this sendto_serv_butone() call -Dianora
+     * readded because of anti-flud stuffs -epi
+     */
+    
+    sendto_serv_butone_services(cptr, ":%s AWAY :%s ", parv[0], parv[1]);
+
+    if (away)
+        MyFree(away);
+    
+    away = (char *) MyMalloc(strlen(awy2) + 1);
+    strcpy(away, awy2);
+
+    sptr->user->away = away;
+
+    if (MyConnect(sptr))
+        sendto_one(sptr, rpl_str(RPL_NOWAWAY), me.name, parv[0]);
+    return 0;
+}
+
+/*
+ * m_ping 
+ * parv[0] = sender prefix 
+ * parv[1] = origin
+ * parv[2] = destination
+ */
+int 
+m_ping(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient    *acptr;
+    char       *origin, *destination;
+    
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        sendto_one(sptr, err_str(ERR_NOORIGIN), me.name, parv[0]);
+        return 0;
+    }
+    origin = parv[1];
+    destination = parv[2];      /* Will get NULL or pointer (parc >= 2!!) */
+    
+    acptr = find_client(origin, NULL);
+    if (!acptr)
+        acptr = find_server(origin, NULL);
+    if (acptr && acptr != sptr)
+        origin = cptr->name;
+    if (!BadPtr(destination) && mycmp(destination, me.name) != 0)
+    {
+        if ((acptr = find_server(destination, NULL)))
+            sendto_one(acptr, ":%s PING %s :%s", parv[0], origin, destination);
+        else
+        {
+            sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], 
+                       destination);
+            return 0;
+        }
+    }
+    else
+        sendto_one(sptr, ":%s PONG %s :%s", me.name,
+                   (destination) ? destination : me.name, origin);
+    return 0;
+}
+
+/*
+ * m_pong 
+ * parv[0] = sender prefix 
+ * parv[1] = origin
+ * parv[2] = destination
+ */
+int 
+m_pong(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient    *acptr;
+    char       *origin, *destination;
+
+    if (parc < 2 || *parv[1] == '\0')
+    {
+        sendto_one(sptr, err_str(ERR_NOORIGIN), me.name, parv[0]);
+        return 0;
+    }
+
+    origin = parv[1];
+    destination = parv[2];
+    cptr->flags &= ~FLAGS_PINGSENT;
+    sptr->flags &= ~FLAGS_PINGSENT;
+
+    /* if it's my client and it's a server.. */
+    if(sptr == cptr && IsServer(cptr))
+    {
+        if(sptr->flags & FLAGS_USERBURST)
+        {
+            sptr->flags &= ~FLAGS_USERBURST;
+            sendto_gnotice("from %s: %s has processed user/channel burst, "
+                           "sending topic burst.", me.name, sptr->name);
+            if(confopts & FLAGS_SERVHUB)
+            {
+                /* services doesn't care about 
+                 * TOPICs during a sync or AWAY messages
+                 * ryan, i keeel you for putting C++ style comments in here. 
+                 * -epi */
+                if(mycmp(cptr->name,Services_Name)!=0 && 
+                   mycmp(cptr->name,Stats_Name)!=0)
+                    send_topic_burst(sptr);
+            }
+            else
+                send_topic_burst(sptr);
+            sptr->flags |= FLAGS_PINGSENT|FLAGS_SOBSENT;
+            sendto_one(sptr, "PING :%s", me.name);
+        }
+        else if(sptr->flags & FLAGS_TOPICBURST)
+        {
+            sptr->flags &= ~FLAGS_TOPICBURST;
+            sendto_gnotice("from %s: %s has processed topic burst (synched "
+                           "to network data).", me.name, sptr->name);
+
+            if(server_was_split)
+                server_was_split = NO;
+
+            if(confopts & FLAGS_HUB)
+                sendto_serv_butone(sptr, ":%s GNOTICE :%s has synched to"
+                               " network data.", me.name, sptr->name);
+                /* Kludge: Get the "sync" message on small networks 
+                 * immediately */ 
+            sendto_one(sptr, "PING :%s", me.name);
+        }
+    }
+
+    /*
+     * Now attempt to route the PONG, comstud pointed out routable PING
+     * is used for SPING.  routable PING should also probably be left in
+     * -Dianora That being the case, we will route, but only for
+     * registered clients (a case can be made to allow them only from
+     * servers). -Shadowfax
+     */
+    if (!BadPtr(destination) && (mycmp(destination, me.name) != 0)
+        && IsRegistered(sptr))
+    {
+        if ((acptr = find_client(destination, NULL)) ||
+            (acptr = find_server(destination, NULL)))
+            sendto_one(acptr, ":%s PONG %s %s", parv[0], origin, destination);
+        else
+        {
+            sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], 
+                       destination);
+            return 0;
+        }
+    }
+#ifdef  DEBUGMODE
+    else
+        Debug((DEBUG_NOTICE, "PONG: %s %s", origin, 
+               destination ? destination : "*"));
+#endif
+    return 0;
+}
+
+/* added Sat Jul 25 07:30:42 EST 1992 */
+/*
+ * extra argument evenTS added to send to TS servers or not -orabidoo
+ *
+ * extra argument evenTS no longer needed with TS only th+hybrid server
+ * -Dianora
+ */
+static inline void
+send_umode_out(aClient *cptr, aClient *sptr, int old)
+{
+    aClient *acptr;
+    DLink *lp;
+
+    send_umode(NULL, sptr, old, SEND_UMODES, buf);
+
+    if(*buf)
+    {
+        for(lp = server_list; lp; lp = lp->next)
+        {
+            acptr = lp->value.cptr;
+            if((acptr != cptr) && (acptr != sptr))
+                sendto_one(acptr, ":%s MODE %s :%s", sptr->name,
+                           sptr->name, buf);
+        }
+    }
+
+    if (cptr && MyClient(cptr))
+        send_umode(cptr, sptr, old, ALL_UMODES, buf);
+}
+
+/*
+ * m_oper 
+ * parv[0] = sender prefix 
+ * parv[1] = oper name 
+ * parv[2] = oper password
+ */
+int m_oper(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aOper       *aoper;
+    char        *name, *password, *encr, *oper_ip;
+    extern char *crypt();
+
+    name = parc > 1 ? parv[1] : (char *) NULL;
+    password = parc > 2 ? parv[2] : (char *) NULL;
+
+    if (!IsServer(cptr) && (BadPtr(name) || BadPtr(password)))
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "OPER");
+        return 0;
+    }
+
+    /* if message arrived from server, trust it, and set to oper */
+    /* an OPER message should never come from a server. complain */
+
+    if ((IsServer(cptr) || IsMe(cptr)) && !IsOper(sptr))
+    {
+        sendto_realops("Why is %s sending me an OPER? Contact Coders",
+                        cptr->name);
+
+        /* sanity */
+        if (!IsPerson(sptr))
+            return 0;
+
+#ifdef DEFAULT_HELP_MODE
+        sptr->umode |= UMODE_o;
+        sptr->umode |= UMODE_h;
+        sendto_serv_butone(cptr, ":%s MODE %s :+oh", parv[0], parv[0]);
+#else
+        sptr->umode |= UMODE_o;
+        sendto_serv_butone(cptr, ":%s MODE %s :+o", parv[0], parv[0]);
+#endif
+#ifdef ALL_OPERS_HIDDEN
+        sptr->umode |= UMODE_I;
+        sendto_serv_butone(cptr, ":%s MODE %s :+I", parv[0], parv[0]);
+#endif
+        Count.oper++;
+        if (IsMe(cptr))
+            sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
+        return 0;
+    }
+    else if (IsAnOper(sptr) && MyConnect(sptr))
+    {
+        sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
+        return 0;
+    }
+#if (RIDICULOUS_PARANOIA_LEVEL>=1)
+    if(!sptr->user->real_oper_host)
+    {
+#endif
+        if(!(aoper = find_oper(name, sptr->user->username, sptr->user->host, 
+                               sptr->hostip)))
+        {
+            sendto_one(sptr, err_str(ERR_NOOPERHOST), me.name, parv[0]);
+            sendto_realops("Failed OPER attempt by %s (%s@%s)", parv[0],
+                           sptr->user->username, sptr->user->host);
+            return 0;
+        }
+        oper_ip = sptr->hostip;
+#if (RIDICULOUS_PARANOIA_LEVEL>=1)
+    }
+    else
+    {
+        if (!(aoper = find_oper(name, sptr->user->real_oper_username,
+                                      sptr->user->real_oper_host,
+                                      sptr->user->real_oper_ip))) 
+        {
+            sendto_one(sptr, err_str(ERR_NOOPERHOST), me.name, parv[0]);
+            sendto_realops("Failed OPER attempt by %s (%s@%s)", parv[0],
+                           sptr->user->username, sptr->user->host);
+            return 0;
+        }
+        oper_ip = sptr->user->real_oper_ip;
+    }
+#endif
+    /* use first two chars of the password they send in as salt */
+    /* passwd may be NULL pointer. Head it off at the pass... */
+    if(confopts & FLAGS_CRYPTPASS)
+    {
+        if (password && *aoper->passwd)
+                encr = crypt(password, aoper->passwd);
+        else
+                encr = "";
+    }
+    else 
+        encr = password;
+    
+    if (StrEq(encr, aoper->passwd))
+    {
+        int old = (sptr->umode & ALL_UMODES);
+        /* attach our conf */
+        sptr->user->oper = aoper;
+        aoper->opers++;
+        if (!(aoper->flags & OFLAG_ISGLOBAL))
+            SetLocOp(sptr);
+        else
+            SetOper(sptr);
+#ifdef DEFAULT_HELP_MODE                        
+        sptr->umode|=(UMODE_s|UMODE_g|UMODE_w|UMODE_n|UMODE_h);
+#else                   
+        sptr->umode|=(UMODE_s|UMODE_g|UMODE_w|UMODE_n);
+#endif
+        sptr->oflag = aoper->flags;
+        Count.oper++;
+        add_to_list(&oper_list, sptr);
+        throttle_remove(oper_ip);
+        sendto_ops("%s (%s@%s) is now operator (%c)", parv[0],
+                   sptr->user->username, sptr->sockhost,
+                   IsOper(sptr) ? 'O' : 'o');
+        send_umode_out(cptr, sptr, old);
+        sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
+        set_effective_class(sptr);
+#if defined(USE_SYSLOG) && defined(SYSLOG_OPER)
+        syslog(LOG_INFO, "OPER (%s) (%s) by (%s!%s@%s)",
+               name, encr, parv[0], sptr->user->username, sptr->sockhost);
+#endif
+#ifdef MAXBUFFERS
+        /* give them server-sized socket buffers, throughput++ */
+        reset_sock_opts(sptr->fd, 1);
+#endif
+#if defined(FNAME_OPERLOG)
+        {
+            int logfile;
+                        
+            /*
+             * This conditional makes the logfile active only after it's
+             * been created - thus logging can be turned off by removing
+             * the file.
+             * 
+             * stop NFS hangs...most systems should be able to open a file in
+             * 3 seconds. -avalon (curtesy of wumpus)
+             */
+            alarm(3);
+            if (IsPerson(sptr) &&
+                (logfile = open(FNAME_OPERLOG, O_WRONLY | O_APPEND)) != -1)
+            {
+                alarm(0);
+                ircsprintf(buf, "%s OPER (%s) (%s) by (%s!%s@%s)\n",
+                                  myctime(timeofday), name, encr,
+                                  parv[0], sptr->user->username,
+                                  sptr->sockhost);
+                alarm(3);
+                write(logfile, buf, strlen(buf));
+                alarm(0);
+                close(logfile);
+            }
+            alarm(0);
+            /* Modification by pjg */
+        }
+#endif
+    }
+    else 
+    {
+        sendto_one(sptr, err_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
+#ifdef FAILED_OPER_NOTICE
+        sendto_realops("Failed OPER attempt by %s (%s@%s)",
+                       parv[0], sptr->user->username, sptr->sockhost);
+#endif
+    }
+    return 0;
+}
+
+/***************************************************************************
+ * m_pass() - Added Sat, 4 March 1989
+ ***************************************************************************/
+/*
+ * m_pass 
+ * parv[0] = sender prefix 
+ * parv[1] = password
+ * parv[2] = optional extra version information
+ */
+int 
+m_pass(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char *password = parc > 1 ? parv[1] : NULL;
+    
+    if (BadPtr(password))
+    {
+        sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "PASS");
+        return 0;
+    }
+    if (!MyConnect(sptr) || (!IsUnknown(cptr) && !IsHandshake(cptr)))
+    {
+        sendto_one(cptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]);
+        return 0;
+    }
+    strncpyzt(cptr->passwd, password, sizeof(cptr->passwd));
+    if (parc > 2)
+    {
+        int l = strlen(parv[2]);
+        
+        if (l < 2)
+            return 0;
+        if (parv[2][0] == 'T' && parv[2][1] == 'S')
+            cptr->tsinfo = (ts_val) TS_DOESTS;
+    }
+    return 0;
+}
+
+/*
+ * m_userhost added by Darren Reed 13/8/91 to aid clients and reduce
+ * the need for complicated requests like WHOIS. It returns user/host
+ * information only (no spurious AWAY labels or channels).
+ */
+int 
+m_userhost(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char *s, *p = NULL;
+    aClient *acptr;
+    int i, len, res = 0;
+    
+    ircsprintf(buf, rpl_str(RPL_USERHOST), me.name, parv[0]);
+    len = strlen(buf);
+
+    for (i = 5, s = strtoken(&p, parv[1], " "); i && s;
+         s = strtoken(&p, (char *) NULL, " "), i--)
+        if ((acptr = find_person(s, NULL)))
+        {
+            if (++res > 1)
+                buf[len++] = ' ';
+            len += ircsnprintf(buf + len, sizeof(buf) - (len + 1), 
+                               "%s%s=%c%s@%s", acptr->name,
+                              IsAnOper(acptr) ? "*" : "",
+                              (acptr->user->away) ? '-' : '+',
+                              acptr->user->username, acptr->user->host);
+        }
+    sendto_one(sptr, "%s", buf);
+    return 0;
+}
+
+int 
+m_userip(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    char *s, *p = NULL;
+    aClient *acptr;
+    int i, len, res = 0;
+
+    if(!IsAnOper(sptr))
+    {
+        sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+        return 0;
+    }
+    ircsprintf(buf, rpl_str(RPL_USERHOST), me.name, parv[0]);
+    len = strlen(buf);
+
+    for (i = 5, s = strtoken(&p, parv[1], " "); i && s;
+         s = strtoken(&p, (char *) NULL, " "), i--)
+        if ((acptr = find_person(s, NULL)))
+        {
+            if (++res > 1)
+               buf[len++] = ' ';
+            len += ircsnprintf(buf + len, sizeof(buf) - (len + 1), 
+                               "%s%s=%c%s@%s", acptr->name,
+                              IsAnOper(acptr) ? "*" : "",
+                              (acptr->user->away) ? '-' : '+',
+                              acptr->user->username,
+                              IsULine(acptr) ? "0.0.0.0" : acptr->hostip);
+        }
+    sendto_one(sptr, "%s", buf);
+    return 0;
+}
+
+/*
+ * m_ison added by Darren Reed 13/8/91 to act as an efficent user
+ * indicator with respect to cpu/bandwidth used. Implemented for NOTIFY
+ * feature in clients. Designed to reduce number of whois requests. Can
+ * process nicknames in batches as long as the maximum buffer length.
+ * 
+ * format: ISON :nicklist
+ */
+/* Take care of potential nasty buffer overflow problem -Dianora */
+
+int 
+m_ison(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aClient *acptr;
+    char   *s, **pav = parv;
+    char       *p = (char *) NULL;
+    int     len, len2;
+
+    if (parc < 2) 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "ISON");
+        return 0;
+    }
+
+    ircsprintf(buf, rpl_str(RPL_ISON), me.name, *parv);
+    len = strlen(buf);
+    if (!IsOper(cptr))
+        cptr->priority += 20;   /* this keeps it from moving to 'busy' list  */
+    for (s = strtoken(&p, *++pav, " "); s;
+         s = strtoken(&p, (char *) NULL, " "))
+        if ((acptr = find_person(s, NULL))) 
+        {
+            len2 = strlen(acptr->name);
+            if ((len + len2 + 5) < sizeof(buf)) /* make sure can never */
+            {                                   /* overflow */
+                strcat(buf, acptr->name);
+                len += len2;
+                strcat(buf, " ");
+                len++;
+            }
+            else
+                break;
+        }
+    sendto_one(sptr, "%s", buf);
+    return 0;
+}
+
+/*
+ * m_umode() added 15/10/91 By Darren Reed.
+ * parv[0] - sender
+ * parv[1] - username to change mode for
+ * parv[2] - modes to change
+ */
+int 
+m_umode(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int     flag, *s, setflags, what = MODE_ADD, badflag = NO;
+    char  **p, *m;
+    aClient    *acptr;
+    
+    if (parc < 2)
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "MODE");
+        return 0;
+    }
+
+    if(IsServer(sptr))
+        return 0;
+    
+    if (!(acptr = find_person(parv[1], NULL)))
+    {
+        if (MyConnect(sptr))
+            sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], 
+                       parv[1]);
+        return 0;
+    }
+
+    if ((sptr != acptr) || (acptr->from != sptr->from))
+    {
+        sendto_one(sptr, err_str(ERR_USERSDONTMATCH), me.name, parv[0]);
+        return 0;
+    }
+    
+   
+    if (parc < 3)
+    {
+        m = buf;
+        *m++ = '+';
+        for (s = user_modes; (flag = *s) && (m - buf < BUFSIZE - 4); s += 2)
+        {
+            if (sptr->umode & (flag & ALL_UMODES))
+                *m++ = (char) (*(s + 1));
+        }
+        *m = '\0';
+        sendto_one(sptr, rpl_str(RPL_UMODEIS), me.name, parv[0], buf);
+        return 0;
+    }
+        
+    /* find flags already set for user */
+    setflags = 0;
+    for (s = user_modes; (flag = *s); s += 2)
+        if (sptr->umode & flag)
+            setflags |= flag;
+    /* parse mode change string(s) */
+    for (p = &parv[2]; p && *p; p++)
+        for (m = *p; *m; m++)
+            switch (*m)
+            {
+                case '+':
+                    what = MODE_ADD;
+                    break;
+                case '-':
+                    what = MODE_DEL;
+                    break;
+                    /* we may not get these, but they shouldnt be in default */
+                case ' ':
+                case '\r':
+                case '\n':
+                case '\t':
+                    break;
+                case 'r':
+                case 'x':
+                case 'X':
+                    break; /* users can't set themselves +r,+x, or +X! */
+                case 'A':
+                    /* set auto +a if user is setting +A */
+                    if (MyClient(sptr) && (what == MODE_ADD))
+                        sptr->umode |= UMODE_a;
+                default:
+                    for (s = user_modes; (flag = *s); s += 2)
+                        if (*m == (char) (*(s + 1)))
+                        {
+                            if (what == MODE_ADD)
+                                sptr->umode |= flag;
+                            else
+                                sptr->umode &= ~flag;
+                            break;
+                        }
+                    if (flag == 0 && MyConnect(sptr))
+                        badflag = YES;
+                    break;
+            }
+    
+    if (badflag)
+        sendto_one(sptr, err_str(ERR_UMODEUNKNOWNFLAG), me.name, parv[0]);
+
+    /* stop users making themselves operators too easily */
+    if (!(setflags & UMODE_o) && IsOper(sptr) && !IsServer(cptr))
+        ClearOper(sptr);
+        
+    if (!(setflags & UMODE_O) && IsLocOp(sptr) && !IsServer(cptr))
+        sptr->umode &= ~UMODE_O;
+        
+    if ((setflags & (UMODE_o | UMODE_O)) && !IsAnOper(sptr) && MyConnect(sptr))
+    {
+        set_effective_class(sptr);
+        sptr->oflag = 0;
+    }
+
+    if (!(setflags & (UMODE_o | UMODE_O)) && IsAnOper(sptr))
+        Count.oper++;
+        
+    if ((setflags & (UMODE_o | UMODE_O)) && !IsAnOper(sptr))
+    {
+        Count.oper--;
+        if (MyConnect(sptr))
+        {
+            remove_from_list(&oper_list, sptr, NULL);
+
+            /*
+             * Now that the user is no longer opered, let's return
+             * them back to the appropriate Y:class -srd
+             */
+            sptr->user->oper->opers--;
+            sptr->user->oper = NULL;
+            set_effective_class(sptr);
+        }
+    }
+    
+    if (!(setflags & UMODE_i) && IsInvisible(sptr))
+        Count.invisi++;
+    if ((setflags & UMODE_i) && !IsInvisible(sptr))
+        Count.invisi--;
+    
+    /*
+     * compare new flags with old flags and send string which will cause
+     * servers to update correctly.
+     */
+    if (!IsAnOper(sptr) && !IsServer(cptr))
+    {
+        sptr->umode &= ~OPER_UMODES;
+#ifdef NO_USER_SERVERKILLS
+        sptr->umode &= ~UMODE_k;
+#endif
+#ifdef NO_USER_OPERKILLS
+        sptr->umode &= ~UMODE_s;
+#endif
+    }
+    if(MyClient(sptr))
+    {
+        if (IsAdmin(sptr) && !OPIsAdmin(sptr)) ClearAdmin(sptr);
+        if (IsSAdmin(sptr) && !OPIsSAdmin(sptr)) ClearSAdmin(sptr);
+        if (IsUmodef(sptr) && !OPCanUModef(sptr)) ClearUmodef(sptr);
+        if (IsUmodec(sptr) && !OPCanUModec(sptr)) ClearUmodec(sptr);
+        if (IsUmodej(sptr) && !OPCanUModec(sptr)) ClearUmodej(sptr);
+        if (IsUmodey(sptr) && !OPCanUModey(sptr)) ClearUmodey(sptr);
+        if (IsUmoded(sptr) && !OPCanUModed(sptr)) ClearUmoded(sptr);
+        if (IsUmodeb(sptr) && !OPCanUModeb(sptr)) ClearUmodeb(sptr);
+        if (NoMsgThrottle(sptr) && !OPCanUModeF(sptr)) ClearNoMsgThrottle(sptr);
+#ifdef ALLOW_HIDDEN_OPERS
+# ifdef FORCE_EVERYONE_HIDDEN
+        sptr->umode |= UMODE_I;
+# else
+#  if (RIDICULOUS_PARANOIA_LEVEL>=1)
+        if (IsUmodeI(sptr) && !(sptr->user->real_oper_host || IsAnOper(sptr))) 
+            ClearUmodeI(sptr);
+#  endif
+
+#  ifdef FORCE_OPERS_HIDDEN
+        if (IsAnOper(sptr)
+#   if (RIDICULOUS_PARANOIA_LEVEL>=1)
+            || (sptr->user->real_oper_host != NULL)
+#   endif
+           ) sptr->umode |= UMODE_I;
+#  endif /* FORCE_OPERS_HIDDEN */
+# endif /* FORCE_EVERYONE_HIDDEN */
+#else /* ALLOW_HIDDEN_OPERS */
+        if (IsUmodeI(sptr)) ClearUmodeI(sptr);
+#endif
+        if (sptr->user->allow->flags & CONF_FLAGS_FORCEFLOOD)
+            SetNoMsgThrottle(sptr);
+    }
+    send_umode_out(cptr, sptr, setflags);
+    
+    return 0;
+}
+
+/* send the MODE string for user (user) to connection cptr -avalon */
+void 
+send_umode(aClient *cptr, aClient *sptr, int old, int sendmask, char *umode_buf)
+{
+    int *s, flag, what = MODE_NULL;
+    char *m;
+
+    /*
+     * build a string in umode_buf to represent the change in the user's
+     * mode between the new (sptr->flag) and 'old'.
+     */
+    m = umode_buf;
+    *m = '\0';
+    for (s = user_modes; (flag = *s); s += 2)
+    {
+        if (MyClient(sptr) && !(flag & sendmask))
+            continue;
+        if ((flag & old) && !(sptr->umode & flag))
+        {
+            if (what == MODE_DEL)
+                *m++ = *(s + 1);
+            else
+            {
+                what = MODE_DEL;
+                *m++ = '-';
+                *m++ = *(s + 1);
+            }
+        }
+        else if (!(flag & old) && (sptr->umode & flag))
+        {
+            if (what == MODE_ADD)
+                *m++ = *(s + 1);
+            else
+            {
+                what = MODE_ADD;
+                *m++ = '+';
+                *m++ = *(s + 1);
+            }
+        }
+    }
+    *m = '\0';
+    if (*umode_buf && cptr)
+        sendto_one(cptr, ":%s MODE %s :%s", sptr->name, sptr->name, umode_buf);
+}
+
+/* Shadowfax's FLUD code */
+#ifdef FLUD
+void 
+announce_fluder(aClient *fluder, aClient *cptr, aChannel *chptr, int type)
+{                               
+    char *fludee;
+    
+    if (cptr)
+        fludee = cptr->name;
+    else
+        fludee = chptr->chname;
+    
+    sendto_realops_lev(FLOOD_LEV, "Flooder %s [%s@%s] on %s target: %s",
+                       fluder->name, fluder->user->username, fluder->user->host,
+                       fluder->user->server, fludee);
+}
+
+/*
+ * This is really just a "convenience" function.  I can only keep three
+ * or * four levels of pointer dereferencing straight in my head.  This
+ * remove * an entry in a fluders list.  Use this when working on a
+ * fludees list :)
+ */
+struct fludbot *
+remove_fluder_reference(struct fludbot **fluders, aClient *fluder)
+{
+    struct fludbot *current, *prev, *next;
+    
+    prev = NULL;
+    current = *fluders;
+    while (current)
+    {
+        next = current->next;
+        if (current->fluder == fluder)
+        {
+            if (prev)
+                prev->next = next;
+            else
+                *fluders = next;
+            
+            BlockHeapFree(free_fludbots, current);
+        }
+        else
+            prev = current;
+        current = next;
+    }
+    return (*fluders);
+}
+
+/* Another function to unravel my mind. */
+Link *
+remove_fludee_reference(Link **fludees, void *fludee)
+{
+    Link *current, *prev, *next;
+
+    prev = NULL;
+    current = *fludees;
+    while (current)
+    {
+        next = current->next;
+        if (current->value.cptr == (aClient *) fludee)
+        {
+            if (prev)
+                prev->next = next;
+            else
+                *fludees = next;
+
+            BlockHeapFree(free_Links, current);
+        }
+        else
+            prev = current;
+        current = next;
+    }
+    return (*fludees);
+}
+
+int 
+check_for_fludblock(aClient *fluder, aClient *cptr, aChannel *chptr, int type)
+{                               
+    time_t now;
+    int blocking;
+
+    /* If it's disabled, we don't need to process all of this */
+    if ((confopts & FLAGS_HUB) || (flud_block == 0))
+        return 0;
+
+    /* It's either got to be a client or a channel being fluded */
+    if ((cptr == NULL) && (chptr == NULL))
+        return 0;
+
+    if (cptr && !MyFludConnect(cptr))
+    {
+        sendto_ops("check_for_fludblock() called for non-local client");
+        return 0;
+    }
+
+    /* Are we blocking fluds at this moment? */
+    time(&now);
+    if (cptr)
+        blocking = (cptr->fludblock > (now - flud_block));
+    else
+        blocking = (chptr->fludblock > (now - flud_block));
+
+    return (blocking);
+}
+
+int 
+check_for_flud(aClient *fluder, aClient *cptr, aChannel *chptr, int type)
+{                               
+    time_t      now;
+    struct fludbot *current, *prev, *next;
+    int         blocking, count, found;
+    Link       *newfludee;
+    
+    /* If it's disabled, we don't need to process all of this */
+    if ((confopts & (FLAGS_HUB|FLAGS_SERVHUB)) || (flud_block == 0))
+        return 0;
+        
+    /* It's either got to be a client or a channel being fluded */
+    if ((cptr == NULL) && (chptr == NULL))
+        return 0;
+        
+    if (cptr && !MyFludConnect(cptr)) 
+    {
+        sendto_ops("check_for_flud() called for non-local client");
+        return 0;
+    }
+        
+    /* Are we blocking fluds at this moment? */
+    time(&now);
+    if (cptr)
+        blocking = (cptr->fludblock > (now - flud_block));
+    else
+        blocking = (chptr->fludblock > (now - flud_block));
+        
+    /* Collect the Garbage */
+    if (!blocking) 
+    {
+        if (cptr)
+            current = cptr->fluders;
+        else
+            current = chptr->fluders;
+        prev = NULL;
+        while (current) 
+        {
+            next = current->next;
+            if (current->last_msg < (now - flud_time))
+            {
+                if (cptr)
+                    remove_fludee_reference(&current->fluder->fludees,
+                                            (void *) cptr);
+                else
+                    remove_fludee_reference(&current->fluder->fludees,
+                                            (void *) chptr);
+                if (prev)
+                    prev->next = current->next;
+                else if (cptr)
+                    cptr->fluders = current->next;
+                else
+                    chptr->fluders = current->next;
+                BlockHeapFree(free_fludbots, current);
+            }
+            else
+                prev = current;
+            current = next;
+        }
+    }
+    /*
+     * Find or create the structure for the fluder, and update the
+     * counter * and last_msg members.  Also make a running total count
+     */
+    if (cptr)
+        current = cptr->fluders;
+    else
+        current = chptr->fluders;
+    count = found = 0;
+    while (current) 
+    {
+        if (current->fluder == fluder)
+        {
+            current->last_msg = now;
+            current->count++;
+            found = 1;
+        }
+        if (current->first_msg < (now - flud_time))
+            count++;
+        else
+            count += current->count;
+        current = current->next;
+    }
+    if (!found) 
+    {
+        if ((current = BlockHeapALLOC(free_fludbots, struct fludbot)) != NULL) 
+        {
+            current->fluder = fluder;
+            current->count = 1;
+            current->first_msg = now;
+            current->last_msg = now;
+            if (cptr) 
+            {
+                current->next = cptr->fluders;
+                cptr->fluders = current;
+            }
+            else 
+            {
+                current->next = chptr->fluders;
+                chptr->fluders = current;
+            }
+                        
+            count++;
+                        
+            if ((newfludee = BlockHeapALLOC(free_Links, Link)) != NULL) 
+            {
+                if (cptr) 
+                {
+                    newfludee->flags = 0;
+                    newfludee->value.cptr = cptr;
+                }
+                else 
+                {
+                    newfludee->flags = 1;
+                    newfludee->value.chptr = chptr;
+                }
+                newfludee->next = fluder->fludees;
+                fluder->fludees = newfludee;
+            }
+            else
+                outofmemory();
+            /*
+             * If we are already blocking now, we should go ahead * and
+             * announce the new arrival
+             */
+            if (blocking)
+                announce_fluder(fluder, cptr, chptr, type);
+        }
+        else
+            outofmemory();
+    }
+    /*
+     * Okay, if we are not blocking, we need to decide if it's time to *
+     * begin doing so.  We already have a count of messages received in *
+     * the last flud_time seconds
+     */
+    if (!blocking && (count > flud_num)) 
+    {
+        blocking = 1;
+        ircstp->is_flud++;
+        /*
+         * if we are going to say anything to the fludee, now is the *
+         * time to mention it to them.
+         */
+        if (cptr)
+            sendto_one(cptr,
+                       ":%s NOTICE %s :*** Notice -- Server flood protection "
+                       "activated for %s", me.name, cptr->name, cptr->name);
+        else
+            sendto_channel_butserv(chptr, &me,
+                                   ":%s NOTICE %s :*** Notice -- Server "
+                                   "flood protection activated for %s",
+                                   me.name, chptr->chname, chptr->chname);
+        /*
+         * Here we should go back through the existing list of * fluders
+         * and announce that they were part of the game as * well.
+         */
+        if (cptr)
+            current = cptr->fluders;
+        else
+            current = chptr->fluders;
+        while (current) 
+        {
+            announce_fluder(current->fluder, cptr, chptr, type);
+            current = current->next;
+        }
+    }
+    /*
+     * update blocking timestamp, since we received a/another CTCP
+     * message
+     */
+    if (blocking) 
+    {
+        if (cptr)
+            cptr->fludblock = now;
+        else
+            chptr->fludblock = now;
+    }
+    return (blocking);
+}
+
+void 
+free_fluders(aClient *cptr, aChannel *chptr)
+{
+    struct fludbot *fluders, *next;
+
+    if ((cptr == NULL) && (chptr == NULL)) 
+    {
+        sendto_ops("free_fluders(NULL, NULL)");
+        return;
+    }
+
+    if (cptr && !MyFludConnect(cptr))
+        return;
+
+    if (cptr)
+        fluders = cptr->fluders;
+    else
+        fluders = chptr->fluders;
+
+    while (fluders) 
+    {
+        next = fluders->next;
+
+        if (cptr)
+            remove_fludee_reference(&fluders->fluder->fludees, (void *) cptr);
+        else
+            remove_fludee_reference(&fluders->fluder->fludees, (void *) chptr);
+
+        BlockHeapFree(free_fludbots, fluders);
+        fluders = next;
+    }
+}
+
+void 
+free_fludees(aClient *badguy)
+{
+    Link       *fludees, *next;
+
+    if (badguy == NULL) 
+    {
+        sendto_ops("free_fludees(NULL)");
+        return;
+    }
+    fludees = badguy->fludees;
+    while (fludees) 
+    {
+        next = fludees->next;
+
+        if (fludees->flags)
+            remove_fluder_reference(&fludees->value.chptr->fluders, badguy);
+        else 
+        {
+            if (!MyFludConnect(fludees->value.cptr))
+                sendto_ops("free_fludees() encountered non-local client");
+            else
+                remove_fluder_reference(&fludees->value.cptr->fluders, badguy);
+        }
+
+        BlockHeapFree(free_Links, fludees);
+        fludees = next;
+    }
+}
+#endif /* FLUD */
+
+
+int del_silence(aClient *sptr, char *mask) 
+{
+    Link **lp, *tmp;
+    for (lp=&(sptr->user->silence);*lp;lp=&((*lp)->next))
+        if (mycmp(mask, (*lp)->value.cp)==0) 
+        {
+            tmp = *lp;
+            *lp = tmp->next;
+            MyFree(tmp->value.cp);
+            free_link(tmp);
+            return 0;
+        }
+    return 1;
+}
+
+static int add_silence(aClient *sptr,char *mask) 
+{
+    Link *lp;
+    int cnt=0, len=0;
+    for (lp=sptr->user->silence;lp;lp=lp->next) 
+    {
+        len += strlen(lp->value.cp);
+        if (MyClient(sptr)) 
+        {
+            if ((len > MAXSILELENGTH) || (++cnt >= MAXSILES)) 
+            {
+                sendto_one(sptr, err_str(ERR_SILELISTFULL), me.name,
+                           sptr->name, mask);
+                return -1;
+            } 
+            else
+            {
+                if (!match(lp->value.cp, mask))
+                    return -1;
+            }
+        }
+        else if (!mycmp(lp->value.cp, mask))
+            return -1;
+    }
+    lp = make_link();
+    lp->next = sptr->user->silence;
+    lp->value.cp = (char *)MyMalloc(strlen(mask)+1);
+    strcpy(lp->value.cp, mask);
+    sptr->user->silence = lp;
+    return 0;
+}
+
+/* m_silence
+ * parv[0] = sender prefix
+ * From local client:
+ * parv[1] = mask (NULL sends the list)
+ * From remote client:
+ * parv[1] = nick that must be silenced
+ * parv[2] = mask
+ */
+int 
+m_silence(aClient *cptr,aClient *sptr,int parc,char *parv[]) 
+{
+    Link *lp;
+    aClient *acptr=NULL;
+    char c, *cp;
+
+    if (check_registered_user(sptr)) 
+        return 0;
+
+    if (MyClient(sptr)) 
+    {
+        acptr = sptr;
+        if (parc < 2 || *parv[1]=='\0' || (acptr = find_person(parv[1], NULL))) 
+        {
+            if (!(acptr->user)) 
+                return 0;
+
+            for (lp = acptr->user->silence; lp; lp = lp->next)
+                sendto_one(sptr, rpl_str(RPL_SILELIST), me.name,
+                           sptr->name, acptr->name, lp->value.cp);
+
+            sendto_one(sptr, rpl_str(RPL_ENDOFSILELIST), me.name, acptr->name);
+            return 0;
+        }
+        cp = parv[1];
+        c = *cp;
+        if (c=='-' || c=='+') 
+            cp++;
+        else if (!(strchr(cp, '@') || strchr(cp, '.') ||
+                   strchr(cp, '!') || strchr(cp, '*'))) 
+        {
+            sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0],
+                       parv[1]);
+            return 0;
+        }
+        else c = '+';
+        cp = pretty_mask(cp);
+        if ((c=='-' && !del_silence(sptr,cp)) ||
+            (c!='-' && !add_silence(sptr,cp))) 
+        {
+            sendto_prefix_one(sptr, sptr, ":%s SILENCE %c%s", parv[0], c, cp);
+            if (c=='-')
+                sendto_serv_butone(NULL, ":%s SILENCE * -%s", sptr->name, cp);
+        }
+    }
+    else if (parc < 3 || *parv[2]=='\0') 
+    {
+        sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "SILENCE");
+        return -1;
+    } 
+    else if ((c = *parv[2])=='-' || (acptr = find_person(parv[1], NULL))) 
+    {
+        if (c=='-') 
+        {
+            if (!del_silence(sptr,parv[2]+1))
+                sendto_serv_butone(cptr, ":%s SILENCE %s :%s",
+                                   parv[0], parv[1], parv[2]);
+        }
+        else
+        {
+            add_silence(sptr,parv[2]);
+            if (!MyClient(acptr))
+                sendto_one(acptr, ":%s SILENCE %s :%s",
+                           parv[0], parv[1], parv[2]);
+        } 
+    } 
+    else
+    {
+        sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
+        return 0;
+    }
+    return 0;
+}
+
+static int 
+add_dccallow(aClient *sptr, aClient *optr)
+{
+    Link *lp;
+    int cnt = 0;
+
+    for(lp = sptr->user->dccallow; lp; lp = lp->next)
+    {
+        if(lp->flags != DCC_LINK_ME)
+            continue;
+        if(++cnt >= MAXDCCALLOW)
+        {
+            sendto_one(sptr, err_str(ERR_TOOMANYDCC), me.name, sptr->name,
+                       optr->name, MAXDCCALLOW);
+            return 0;
+        }
+        else if(lp->value.cptr == optr)
+            return 0;
+    }
+
+    lp = make_link();
+    lp->value.cptr = optr;
+    lp->flags = DCC_LINK_ME;
+    lp->next = sptr->user->dccallow;
+    sptr->user->dccallow = lp;
+
+    lp = make_link();
+    lp->value.cptr = sptr;
+    lp->flags = DCC_LINK_REMOTE;
+    lp->next = optr->user->dccallow;
+    optr->user->dccallow = lp;   
+
+    sendto_one(sptr, rpl_str(RPL_DCCSTATUS), me.name, sptr->name, optr->name,
+               "added to");
+    return 0;
+}
+
+static int 
+del_dccallow(aClient *sptr, aClient *optr) 
+{
+    Link **lpp, *lp;
+    int found = 0;
+
+    for (lpp = &(sptr->user->dccallow); *lpp; lpp=&((*lpp)->next))
+    {
+        if((*lpp)->flags != DCC_LINK_ME)
+            continue;
+
+        if((*lpp)->value.cptr == optr)
+        {
+            lp = *lpp;
+            *lpp = lp->next;
+            free_link(lp);
+            found++;
+            break;
+        }
+    }
+
+    if(!found)
+    {
+        sendto_one(sptr, ":%s %d %s :%s is not in your DCC allow list",
+                   me.name, RPL_DCCINFO, sptr->name, optr->name);
+        return 0;
+    }
+
+    for (found = 0, lpp = &(optr->user->dccallow); *lpp; lpp=&((*lpp)->next))
+    {
+        if((*lpp)->flags != DCC_LINK_REMOTE)
+            continue;
+
+        if((*lpp)->value.cptr == sptr)
+        {
+            lp = *lpp;
+            *lpp = lp->next;
+            free_link(lp);
+            found++;
+            break;
+        }
+    }
+
+    if(!found)
+        sendto_realops_lev(DEBUG_LEV, "%s was in dccallowme list of %s but "
+                           "not in dccallowrem list!", optr->name, sptr->name);
+
+    sendto_one(sptr, rpl_str(RPL_DCCSTATUS), me.name, sptr->name, optr->name,
+               "removed from");
+    
+    return 0;
+}
+
+int 
+m_dccallow(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    Link *lp;
+    char *p, *s;
+    char *cn;
+    aClient *acptr, *lastcptr = NULL;
+    int didlist = 0, didhelp = 0, didanything = 0;
+    char **ptr;
+    static char *dcc_help[] = 
+        {
+            "/DCCALLOW [<+|->nick[,<+|->nick, ...]] [list] [help]",
+            "You may allow DCCs of filetypes which are otherwise blocked by "
+            "the IRC server",
+            "by specifying a DCC allow for the user you want to recieve files "
+            "from.",
+            "For instance, to allow the user bob to send you file.exe, you "
+            "would type:",
+            "/dccallow +bob",
+            "and bob would then be able to send you files. bob will have to "
+            "resend the file",
+            "if the server gave him an error message before you added him to "
+            "your allow list.",
+            "/dccallow -bob",
+            "Will do the exact opposite, removing him from your dcc allow "
+            "list.",
+            "/dccallow list",
+            "Will list the users currently on your dcc allow list.",
+            NULL 
+        };
+
+    if(!MyClient(sptr)) 
+        return 0;
+    
+    if(parc < 2)
+    {
+        sendto_one(sptr, ":%s NOTICE %s :No command specified for DCCALLOW. "
+                   "Type /dccallow help for more information.", me.name,
+                   sptr->name);
+        return 0;
+    }
+
+    for (p = NULL, s = strtoken(&p, parv[1], ", "); s;
+         s = strtoken(&p, NULL, ", "))
+    {
+        if(*s == '+')
+        {
+            didanything++;
+            cn = s + 1;
+            if(*cn == '\0')
+                continue;
+
+            acptr = find_person(cn, NULL);
+            
+            if(acptr == sptr) continue;
+            
+            if(!acptr)
+            {
+                sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name,
+                           sptr->name, cn);
+                continue;
+            }
+
+            if(lastcptr == acptr)
+                sendto_realops_lev(SPY_LEV, "User %s (%s@%s) may be flooding "
+                                   "dccallow: add %s", sptr->name,
+                                   sptr->user->username, sptr->user->host,
+                                   acptr->name);
+            lastcptr = acptr;
+            add_dccallow(sptr, acptr);
+        }
+        else if(*s == '-')
+        {
+            didanything++;
+            cn = s + 1;
+            if(*cn == '\0')
+                continue;
+
+            acptr = find_person(cn, NULL);
+            if(acptr == sptr) 
+                continue;
+
+            if(!acptr)
+            {
+                sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, 
+                           sptr->name, cn);
+                continue;
+            }
+
+            if(lastcptr == acptr)
+                sendto_realops_lev(SPY_LEV, "User %s (%s@%s) may be flooding "
+                                   "dccallow: del %s", sptr->name,
+                                   sptr->user->username, sptr->user->host,
+                                   acptr->name);
+            
+            lastcptr = acptr;
+            del_dccallow(sptr, acptr);
+        }
+        else
+        {
+            if(!didlist && myncmp(s, "list", 4) == 0)
+            {
+                didanything++;
+                didlist++;
+                sendto_one(sptr, ":%s %d %s :The following users are on your "
+                           "dcc allow list:", me.name, RPL_DCCINFO,
+                           sptr->name);
+                for(lp = sptr->user->dccallow; lp; lp = lp->next)
+                {
+                    if(lp->flags == DCC_LINK_REMOTE) 
+                        continue;
+                    sendto_one(sptr, ":%s %d %s :%s (%s@%s)", me.name,
+                               RPL_DCCLIST, sptr->name, lp->value.cptr->name,
+                               lp->value.cptr->user->username,
+                               lp->value.cptr->user->host);
+                }
+                sendto_one(sptr, rpl_str(RPL_ENDOFDCCLIST), me.name,
+                           sptr->name, s);
+            }
+            else if(!didhelp && myncmp(s, "help", 4) == 0)
+            {
+                didanything++;
+                didhelp++;
+                for(ptr = dcc_help; *ptr; ptr++)
+                    sendto_one(sptr, ":%s %d %s :%s", me.name, RPL_DCCINFO,
+                               sptr->name, *ptr);
+                sendto_one(sptr, rpl_str(RPL_ENDOFDCCLIST), me.name,
+                           sptr->name, s);
+            }
+        }
+    }
+
+    if(!didanything)
+    {
+        sendto_one(sptr, ":%s NOTICE %s :Invalid syntax for DCCALLOW. Type "
+                   "/dccallow help for more information.", me.name,
+                   sptr->name);
+        return 0;
+    }
+    
+    return 0;
+}
diff --git a/src/sbuf.c b/src/sbuf.c
new file mode 100644 (file)
index 0000000..ec64efe
--- /dev/null
@@ -0,0 +1,551 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/sbuf.c
+ *   Copyright (C) 2004 David Parton
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: sbuf.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $
+ */
+
+#include "sbuf.h"
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void outofmemory(void);
+
+typedef struct _SBuffer 
+{
+    struct _SBuffer *next;
+    int        shared;
+    int        bufsize;
+    int        refcount;
+    char       *end;
+} SBuffer;
+
+typedef struct _SBufBlock
+{
+    int        num;
+    SBuffer*   bufs;
+    struct _SBufBlock *next;
+} SBufBlock;
+
+typedef struct _SBufUser
+{
+    char       *start;
+    SBuffer    *buf;
+    struct _SBufUser *next;
+} SBufUser;
+
+typedef struct _SBufUserBlock
+{
+    int        num;
+    SBufUser   *users;
+    struct _SBufUserBlock *next;
+} SBufUserBlock;
+
+SBuffer             *largesbuf_pool = NULL, *smallsbuf_pool = NULL;
+SBufUser            *user_pool = NULL;
+SBufBlock           *sbuf_blocks = NULL;
+SBufUserBlock       *sbufuser_blocks = NULL;
+int                 sbufuser_total = 0, sbufuser_used = 0;
+int                 sbufsmall_total = 0, sbufsmall_used = 0;
+int                 sbuflarge_total = 0, sbuflarge_used = 0;
+int                 sbufblock_used = 0, sbufuserblock_used = 0;
+
+#define SBUF_BASE                              sizeof(SBuffer)
+#define SBUF_LARGE_TOTAL               (SBUF_BASE + SBUF_LARGE_BUFFER)
+#define SBUF_SMALL_TOTAL        (SBUF_BASE + SBUF_SMALL_BUFFER)
+
+void sbuf_count(int* userUsed, int* userTotal, int* userSize,
+                int* smallUsed, int* smallTotal, int* smallSize,
+                int* largeUsed, int* largeTotal, int* largeSize,
+                int* blockUsed, int* blockSize, int* userblockUsed, int* userblockSize)
+{
+    *userUsed = sbufuser_used;
+    *userTotal = sbufuser_total;
+    *userSize = sizeof(SBufUser);
+    
+    *smallUsed = sbufsmall_used;
+    *smallTotal = sbufsmall_total;
+    *smallSize = SBUF_SMALL_TOTAL;
+    
+    *largeUsed = sbuflarge_used;
+    *largeTotal = sbuflarge_total;
+    *largeSize = SBUF_LARGE_TOTAL;
+    
+    *blockUsed = sbufblock_used;
+    *blockSize = sizeof(SBufBlock);
+    
+    *userblockUsed = sbufuserblock_used;
+    *userblockSize = sizeof(SBufUserBlock);
+}
+
+
+int sbuf_allocblock_general(int theMemorySize, int num, SBuffer** thePool)
+{
+    SBufBlock*    block;
+    SBuffer*      bufs;
+    int           i;
+    
+    block = (SBufBlock*)malloc(sizeof(SBufBlock));
+    if (!block)
+        outofmemory();
+        
+    block->bufs = (SBuffer*)malloc(theMemorySize * num);
+    if (!block->bufs)
+        outofmemory();
+        
+    block->num = num;
+    block->next = sbuf_blocks;
+    sbuf_blocks = block;
+    sbufblock_used++;
+    
+    bufs = block->bufs;
+    for (i = 0; i < block->num - 1; ++i)
+    {
+        bufs->bufsize = theMemorySize - SBUF_BASE;
+        bufs->next = (SBuffer*)(((char*)bufs) + theMemorySize);
+        bufs = bufs->next;
+    }
+    bufs->bufsize = theMemorySize - SBUF_BASE;
+    bufs->next = *thePool;
+    *thePool = block->bufs;
+    
+    return 0;
+}
+    
+int sbuf_allocblock_small(int theMemorySize)
+{
+    if (theMemorySize % SBUF_SMALL_TOTAL != 0)
+        theMemorySize = (theMemorySize + SBUF_SMALL_TOTAL);
+        
+    sbufsmall_total += theMemorySize / SBUF_SMALL_TOTAL;
+        
+    return sbuf_allocblock_general(SBUF_SMALL_TOTAL, theMemorySize / SBUF_SMALL_TOTAL, &smallsbuf_pool);
+}
+
+int sbuf_allocblock_large(int theMemorySize)
+{
+    if (theMemorySize % SBUF_LARGE_TOTAL != 0)
+        theMemorySize = (theMemorySize + SBUF_LARGE_TOTAL);
+        
+    sbuflarge_total += theMemorySize / SBUF_LARGE_TOTAL;
+    
+    return sbuf_allocblock_general(SBUF_LARGE_TOTAL, theMemorySize / SBUF_LARGE_TOTAL, &largesbuf_pool);
+}
+
+int sbuf_allocblock_users(int theCount)
+{
+    SBufUserBlock* block;
+    SBufUser*      users;
+    int            i;
+    
+    block = (SBufUserBlock*)malloc(sizeof(SBufUserBlock));
+    if (!block)
+        outofmemory();
+        
+    block->users = (SBufUser*)malloc(sizeof(SBufUser) * theCount);
+    if (!block->users)
+        outofmemory();
+        
+    block->num = theCount;
+    block->next = sbufuser_blocks;
+    sbufuser_blocks = block;
+
+    sbufuserblock_used++;
+    sbufuser_total += block->num;
+    
+    users = block->users;
+    for (i = 0; i < block->num - 1; ++i)
+    {
+        users->next = users+1;
+        users++;
+    }
+    users->next = user_pool;
+    user_pool = block->users;
+
+    return 0;
+}
+    
+
+int sbuf_init()
+{
+    sbuf_allocblock_small(INITIAL_SBUFS_SMALL);
+    sbuf_allocblock_large(INITIAL_SBUFS_LARGE);
+    sbuf_allocblock_users(INITIAL_SBUFS_USERS);
+    return 0;
+}
+
+int sbuf_free(SBuffer* buf)
+{
+    switch (buf->bufsize)
+    {
+        case SBUF_LARGE_BUFFER:
+            buf->next = largesbuf_pool;
+            largesbuf_pool = buf;
+            sbuflarge_used--;
+            break;
+        
+        case SBUF_SMALL_BUFFER:
+            buf->next = smallsbuf_pool;
+            smallsbuf_pool = buf;
+            sbufsmall_used--;
+            break;
+        
+        default:
+            return -1;
+    }
+        
+    return 0;
+}
+
+int sbuf_user_free(SBufUser* user)
+{
+    user->next = user_pool;
+    user_pool = user;
+    sbufuser_used--;
+    return 0;
+}
+       
+SBuffer* sbuf_alloc(int theSize)
+{
+    SBuffer* buf;
+    
+    if (theSize >= SBUF_SMALL_BUFFER)
+    {
+        buf = largesbuf_pool;
+        if (!buf) {
+            sbuf_allocblock_large(INITIAL_SBUFS_LARGE);
+            buf = largesbuf_pool;
+            if (!buf) return NULL;
+        }
+        largesbuf_pool = largesbuf_pool->next;
+        
+        sbuflarge_used++;
+        
+        buf->bufsize = SBUF_LARGE_BUFFER;
+        buf->refcount = 0;
+        buf->end = ((char*)buf) + SBUF_BASE;
+        buf->next = NULL;
+        buf->shared = 0;
+        return buf;
+    }
+    else
+    {
+        buf = smallsbuf_pool;
+        if (!buf) {
+            sbuf_allocblock_small(INITIAL_SBUFS_SMALL);
+            buf = smallsbuf_pool;
+            if (!buf) return sbuf_alloc(SBUF_SMALL_BUFFER+1); /* attempt to substitute a large buffer instead */
+        }
+        smallsbuf_pool = smallsbuf_pool->next;
+        
+        sbufsmall_used++;
+        
+        buf->bufsize = SBUF_SMALL_BUFFER;
+        buf->refcount = 0;
+        buf->end = ((char*)buf) + SBUF_BASE;
+        buf->next = NULL;
+        buf->shared = 0;
+        return buf;
+    }
+}
+
+SBufUser* sbuf_user_alloc()
+{
+    SBufUser* user;
+    
+    user = user_pool;
+    if (!user)
+    {
+        sbuf_allocblock_users(INITIAL_SBUFS_USERS);
+        user = user_pool;
+        if (!user) return NULL;
+    }
+    user_pool = user_pool->next;
+    
+    sbufuser_used++;
+    
+    user->next = NULL;
+    user->start = NULL;
+    user->buf = NULL;
+    return user;
+}
+        
+
+int sbuf_alloc_error()
+{
+    outofmemory();
+    return -1;
+}
+
+/* Global functions */
+
+int sbuf_begin_share(const char* theData, int theLength, void **thePtr)
+{
+    SBuffer *s;
+    
+    if (theLength > 510) theLength = 510;
+    
+    s = sbuf_alloc(theLength + 2); /* +2 for the \r\n we're tacking on to the buffer */
+    if (!s || theLength + 2 > s->bufsize) return sbuf_alloc_error();
+    
+    memcpy(s->end, theData, theLength);
+    s->end += theLength;
+    *s->end++ = '\r';
+    *s->end++ = '\n';
+    s->refcount = 0;
+    s->shared = 1;
+    
+    *thePtr = (void*)s;
+    return 1;
+}
+
+int sbuf_end_share(void **thePtr, int theNum)
+{
+    SBuffer **shares = (SBuffer**)thePtr;
+    int i;
+      
+    for (i = 0; i < theNum; ++i)
+    {
+        if (!shares[i]) continue;
+        
+        shares[i]->shared = 0;
+        if (shares[i]->refcount == 0) sbuf_free(shares[i]);
+    }
+    
+    return 0;
+}
+    
+int sbuf_put_share(SBuf* theBuf, void* theSBuffer)
+{
+    SBufUser *user;
+    SBuffer  *s = (SBuffer*)theSBuffer;
+    
+    if (!s) return -1;
+    
+    s->refcount++;
+    user = sbuf_user_alloc();
+    user->buf = s;
+    user->start = (char*)(user->buf) + SBUF_BASE;
+    
+    if (theBuf->length == 0)
+        theBuf->head = theBuf->tail = user;
+    else
+    {
+        theBuf->tail->next = user;
+        theBuf->tail = user;
+    }
+    theBuf->length += user->buf->end - user->start;
+    return 1;
+}
+        
+int sbuf_put(SBuf* theBuf, const char* theData, int theLength)
+{
+    SBufUser        **user, *u;
+    int             chunk;
+    
+    if (theBuf->length == 0)
+        user = &theBuf->head;
+    else
+        user = &theBuf->tail;
+        
+    if ((u = *user) != NULL && u->buf->refcount > 1)
+    {
+        u->next = sbuf_user_alloc();
+        u = u->next;
+        if (!u) return sbuf_alloc_error();
+        *user = u; /* tail = u */
+        
+        u->buf = sbuf_alloc(theLength);
+        u->buf->refcount = 1;
+        u->start = u->buf->end;
+    }
+    
+    theBuf->length += theLength;
+    
+    for (; theLength > 0; user = &(u->next))
+    {
+        if ((u = *user) == NULL)
+        {
+            u = sbuf_user_alloc();
+            if (!u) return sbuf_alloc_error();
+            *user = u;
+            theBuf->tail = u;
+            
+            u->buf = sbuf_alloc(theLength);
+            u->buf->refcount = 1;
+            u->start = u->buf->end;
+        }
+        chunk = (((char*)u->buf) + SBUF_BASE + u->buf->bufsize) - u->buf->end;
+        if (chunk)
+        {
+            if (chunk > theLength) chunk = theLength;
+            memcpy(u->buf->end, theData, chunk);
+           
+            u->buf->end += chunk; 
+            theData     += chunk;
+            theLength   -= chunk;
+        }
+    }
+    return 1;
+}    
+
+int sbuf_delete(SBuf* theBuf, int theLength)
+{
+    if (theLength > theBuf->length) theLength = theBuf->length;
+    
+    theBuf->length -= theLength;
+    
+    while (theLength)
+    {
+        int chunk = theBuf->head->buf->end - theBuf->head->start;
+        if (chunk > theLength) chunk = theLength;
+        
+        theBuf->head->start += chunk;
+        theLength           -= chunk;
+        
+        if (theBuf->head->start == theBuf->head->buf->end)
+        {
+            SBufUser *tmp = theBuf->head;
+            theBuf->head = theBuf->head->next;
+            
+            tmp->buf->refcount--;
+            if (tmp->buf->refcount == 0 && tmp->buf->shared == 0)
+                sbuf_free(tmp->buf);
+            sbuf_user_free(tmp);
+        }
+    }
+    if (theBuf->head == NULL) theBuf->tail = NULL;
+    
+    return 1;
+}
+
+char* sbuf_map(SBuf* theBuf, int* theLength)
+{
+    if (theBuf->length != 0)
+    {
+        *theLength = theBuf->head->buf->end - theBuf->head->start;
+        return theBuf->head->start;
+    }
+    *theLength = 0;
+    return NULL;
+}
+
+#ifdef WRITEV_IOV
+int sbuf_mapiov(SBuf *theBuf, struct iovec *iov)
+{
+    int i = 0;
+    SBufUser *sbu;
+
+    if (theBuf->length == 0)
+        return 0;
+
+    for (sbu = theBuf->head; sbu; sbu = sbu->next)
+    {
+        iov[i].iov_base = sbu->start;
+        iov[i].iov_len = sbu->buf->end - sbu->start;
+        if (++i == WRITEV_IOV)
+            break;
+    }
+
+    return i;
+}
+#endif
+
+int sbuf_flush(SBuf* theBuf)
+{
+    SBufUser *tmp;
+    
+    if (theBuf->length == 0) return 0;
+    
+    while (theBuf->head)
+    {
+        char *ptr = theBuf->head->start;
+        while (ptr < theBuf->head->buf->end && IsEol(*ptr)) ptr++;
+        
+        theBuf->length -= ptr - theBuf->head->start;
+        theBuf->head->start = ptr;
+        if (ptr < theBuf->head->buf->end) break;
+        
+        tmp = theBuf->head;
+        theBuf->head = tmp->next;
+        
+        tmp->buf->refcount--;
+        if (tmp->buf->refcount == 0 && tmp->buf->shared == 0)
+            sbuf_free(tmp->buf);
+        sbuf_user_free(tmp);
+    }
+    if (theBuf->head == NULL) theBuf->tail = NULL;
+    return theBuf->length;   
+}
+
+int sbuf_getmsg(SBuf* theBuf, char* theData, int theLength)
+{
+    SBufUser    *user;
+    int         copied;
+    
+    if (sbuf_flush(theBuf) == 0) return 0;
+    
+    copied = 0;
+    for (user = theBuf->head; user && theLength; user = user->next)
+    {
+        char *ptr, *max = user->start + theLength; 
+        if (max > user->buf->end) max = user->buf->end;
+        
+        for (ptr = user->start; ptr < max && !IsEol(*ptr); )
+            *theData++ = *ptr++;
+            
+        copied    += ptr - user->start;
+        theLength -= ptr - user->start;
+        
+        if (ptr < max)
+        {
+            *theData = 0;
+            sbuf_delete(theBuf, copied);
+            sbuf_flush(theBuf);
+            return copied;
+        }
+    }
+    return 0;
+}    
+
+int sbuf_get(SBuf* theBuf, char* theData, int theLength)
+{
+    char   *buf;
+    int    chunk, copied;
+    
+    if (theBuf->length == 0) return 0;
+    
+    copied = 0;
+    while (theLength && (buf = sbuf_map(theBuf, &chunk)) != NULL)
+    {
+        if (chunk > theLength) chunk = theLength;
+        
+        memcpy(theData, buf, chunk);
+        copied    += chunk;
+        theData   += chunk;
+        theLength -= chunk;
+        sbuf_delete(theBuf, chunk);
+    }
+    return copied;
+}
+        
+        
diff --git a/src/scache.c b/src/scache.c
new file mode 100644 (file)
index 0000000..e31cef5
--- /dev/null
@@ -0,0 +1,134 @@
+
+/* $Id: scache.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "h.h"
+
+static int  hash(char *);      /* keep it hidden here */
+/*
+ * ircd used to store full servernames in anUser as well as in the
+ * whowas info.  there can be some 40k such structures alive at any
+ * given time, while the number of unique server names a server sees in
+ * its lifetime is at most a few hundred.  by tokenizing server names
+ * internally, the server can easily save 2 or 3 megs of RAM.
+ * -orabidoo
+ */
+/*
+ * I could have tucked this code into hash.c I suppose but lets keep it
+ * separate for now -Dianora
+ */
+
+#define SCACHE_HASH_SIZE 257
+
+typedef struct scache_entry
+{
+    char        name[HOSTLEN + 1];
+    struct scache_entry *next;
+} SCACHE;
+
+static SCACHE *scache_hash[SCACHE_HASH_SIZE];
+
+/* renamed to keep it consistent with the other hash functions -Dianora */
+/* orabidoo had named it init_scache_hash(); */
+
+void clear_scache_hash_table(void)
+{
+    memset((char *) scache_hash, '\0', sizeof(scache_hash));
+}
+
+static int hash(char *string)
+{
+    int         hash_value;
+   
+    hash_value = 0;
+    while (*string)
+       hash_value += (*string++ & 0xDF);
+
+    return hash_value % SCACHE_HASH_SIZE;
+}
+
+/*
+ * this takes a server name, and returns a pointer to the same string
+ * (up to case) in the server name token list, adding it to the list if
+ * it's not there.  care must be taken not to call this with
+ * user-supplied arguments that haven't been verified to be a valid,
+ * existing, servername.  use the hash in list.c for those.  -orabidoo
+ */
+char *find_or_add(char *name)
+{
+    int         hash_index;
+    SCACHE     *ptr, *newptr;
+
+    ptr = scache_hash[hash_index = hash(name)];
+    while (ptr) 
+    {
+       if (!mycmp(ptr->name, name))
+           return (ptr->name);
+       else
+           ptr = ptr->next;
+    }
+
+    /* not found -- add it */
+    if ((ptr = scache_hash[hash_index])) 
+    {
+       newptr = scache_hash[hash_index] = (SCACHE *) MyMalloc(sizeof(SCACHE));
+       strncpyzt(newptr->name, name, HOSTLEN);
+       newptr->next = ptr;
+       return (newptr->name);
+    }
+    else
+    {
+       ptr = scache_hash[hash_index] = (SCACHE *) MyMalloc(sizeof(SCACHE));
+       strncpyzt(ptr->name, name, HOSTLEN);
+       ptr->next = (SCACHE *) NULL;
+       return (ptr->name);
+    }
+}
+
+/* Added so s_debug could check memory usage in here -Dianora */
+
+void count_scache(int *number_servers_cached, u_long *mem_servers_cached)
+{
+    SCACHE     *scache_ptr;
+    int         i;
+
+    *number_servers_cached = 0;
+    *mem_servers_cached = 0;
+
+    for (i = 0; i < SCACHE_HASH_SIZE; i++)
+    {
+       scache_ptr = scache_hash[i];
+       while (scache_ptr)
+       {
+           *number_servers_cached = *number_servers_cached + 1;
+           *mem_servers_cached = *mem_servers_cached +
+               (strlen(scache_ptr->name) +
+                sizeof(SCACHE *));
+           
+           scache_ptr = scache_ptr->next;
+       }
+    }
+}
+
+/* list all server names in scache very verbose */
+
+void list_scache(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    int         hash_index;
+    SCACHE     *ptr;
+
+    for (hash_index = 0; hash_index < SCACHE_HASH_SIZE; hash_index++) 
+    {
+       ptr = scache_hash[hash_index];
+       while (ptr) 
+       {
+           if (ptr->name)
+               sendto_one(sptr, ":%s NOTICE %s :%s",
+                          me.name, parv[0], ptr->name);
+           ptr = ptr->next;
+       }
+    }
+}
diff --git a/src/send.c b/src/send.c
new file mode 100644 (file)
index 0000000..871ba9f
--- /dev/null
@@ -0,0 +1,2162 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/send.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                    University of Oulu, Computing Center
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: send.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include <stdio.h>
+#include "numeric.h"
+#include "dh.h"
+#include "zlink.h"
+#include "fds.h"
+
+/*
+ * STOP_SENDING_ON_SHORT_SEND:
+ * Treat a short send as a blocked socket
+ * Might not always be a good idea, esp. if using something that's
+ * Edge triggered (ie, kqueue, epoll, etc)
+ */
+#undef STOP_SENDING_ON_SHORT_SEND
+
+#ifdef ALWAYS_SEND_DURING_SPLIT
+extern int currently_processing_netsplit;
+#endif
+
+static char sendbuf[2048];
+static char remotebuf[2048];
+static char selfbuf[256];
+static int  send_message(aClient *, char *, int, void*);
+
+#ifdef HAVE_ENCRYPTION_ON
+/*
+ * WARNING:
+ * Please be aware that if you are using both encryption
+ * and ziplinks, rc4buf in send.c MUST be the same size
+ * as zipOutBuf in zlink.c!
+ */
+static char rc4buf[16384];
+#endif
+
+static int  sentalong[MAXCONNECTIONS];
+static int  sent_serial;
+
+void init_send()
+{
+   memset(sentalong, 0, sizeof(int) * MAXCONNECTIONS);
+   sent_serial = 0;
+}
+
+/* This routine increments our serial number so it will
+ * be unique from anything in sentalong, no need for a memset
+ * except for every MAXINT calls - lucas
+ */
+
+/* This should work on any OS where an int is 32 bit, I hope.. */
+
+#define HIGHEST_SERIAL INT_MAX
+
+#define INC_SERIAL if(sent_serial == HIGHEST_SERIAL) \
+   { memset(sentalong, 0, sizeof(sentalong)); sent_serial = 0; } \
+   sent_serial++;
+
+
+/*
+ * dead_link
+ *
+ * somewhere along the lines of sending out, there was an error.
+ * we can't close it from the send loop, so mark it as dead
+ * and close it from the main loop.
+ *
+ * if this link is a server, tell routing people.
+ */
+
+static int dead_link(aClient *to, char *notice, int sockerr) 
+{
+    int errtmp = errno;  /* so we don't munge this later */
+    
+    to->sockerr = sockerr;
+    to->flags |= FLAGS_DEADSOCKET;
+    /*
+     * If because of BUFFERPOOL problem then clean dbuf's now so that
+     * notices don't hurt operators below.
+     */
+    SBufClear(&to->recvQ);
+    SBufClear(&to->sendQ);
+    /* Ok, if the link we're dropping is a server, send a routing
+     * notice..
+     */
+    if (IsServer(to) && !(to->flags & FLAGS_CLOSING))
+    {
+        char fbuf[512];
+
+        ircsprintf(fbuf, "from %s: %s", me.name, notice);
+        sendto_gnotice(fbuf, get_client_name(to, HIDEME), strerror(errtmp));
+        ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, notice);
+        sendto_serv_butone(to, fbuf, get_client_name(to, HIDEME),
+                           strerror(errtmp));
+    }  
+    return -1;
+}
+
+/*
+ * send_message 
+ * Internal utility which delivers one message buffer to the 
+ * socket. Takes care of the error handling and buffering, ifneeded.
+ */
+static int send_message(aClient *to, char *msg, int len, void* sbuf) 
+{
+    static int  SQinK;
+    int flag;
+    
+#ifdef DUMP_DEBUG
+    fprintf(dumpfp, "-> %s: %s\n", (to->name ? to->name : "*"), msg);
+#endif
+
+    if (to->from)
+        to = to->from;
+
+    flag = (!sbuf || ZipOut(to) || IsRC4OUT(to)) ? 1 : 0;
+
+    if (flag == 1)
+    {
+        if(IsServer(to) || IsNegoServer(to))
+        {
+            if(len>510) 
+            {
+                msg[511]='\n';
+                msg[512]='\0';
+                len=512;
+            }
+            else 
+            {
+                msg[len] = '\n';
+                msg[len+1] = '\0';
+                len++;
+            }   
+        }
+        else
+        {
+            if(len>509) 
+            {
+                msg[510]='\r';
+                msg[511]='\n';
+                msg[512]='\0';
+                len=512;
+            }
+            else 
+            {
+                msg[len] = '\r';
+                msg[len+1] = '\n';
+                msg[len+2] = '\0';
+                len+=2;
+            }   
+        }
+    }
+
+    if (IsMe(to)) 
+    {
+        strncpyzt(selfbuf, msg, sizeof(selfbuf));
+        sendto_ops("Trying to send to myself! [%s]", selfbuf);
+        return 0;
+    }
+   
+    if (IsDead(to))
+        return 0;
+
+    if (to->class && (SBufLength(&to->sendQ) > to->class->maxsendq))
+    {
+        /* this would be a duplicate notice, but it contains some useful 
+         * information thatwould be spamming the rest of the network.
+         * Kept in. - lucas
+         */
+        if (IsServer(to)) 
+            sendto_ops("Max SendQ limit exceeded for %s: %d > %d",
+                       get_client_name(to, HIDEME), SBufLength(&to->sendQ),
+                       to->class->maxsendq);
+        to->flags |= FLAGS_SENDQEX;
+        return dead_link(to, "Max Sendq exceeded for %s, closing link", 0);
+    }
+    
+    /*
+     * Update statistics. The following is slightly incorrect
+     * because it counts messages even if queued, but bytes only
+     * really sent. Queued bytes get updated in SendQueued.
+     */
+    to->sendM += 1;
+    me.sendM += 1;
+    if (to->lstn)
+        to->lstn->sendM += 1;
+
+    if(ZipOut(to))
+    {
+        int ldata = (to->flags & FLAGS_BURST);
+
+        msg = zip_output(to->serv->zip_out, msg, &len, 0, &ldata);
+        if(len == -1)
+        {
+            sendto_realops("Zipout error for %s: (%d) %s\n", to->name, ldata,
+                           msg);
+            return dead_link(to, "Zip output error for %s", IRCERR_ZIP);
+        }
+        
+        if(len == 0)
+            return 0;
+    }
+
+#ifdef HAVE_ENCRYPTION_ON
+    if(IsRC4OUT(to))
+    {
+        /* don't destroy the data in 'msg' */
+        rc4_process_stream_to_buf(to->serv->rc4_out, msg, rc4buf, len);
+        msg = rc4buf;
+    }
+#endif
+
+    if (!sbuf || flag)
+    {
+        if (sbuf_put(&to->sendQ, msg, len) < 0)
+            return dead_link(to, "Buffer allocation error for %s,"
+                                 " closing link", IRCERR_BUFALLOC);
+    }
+    else
+    {
+        if (sbuf_put_share(&to->sendQ, sbuf) < 0)
+            return dead_link(to, "Buffer allocation error for %s,"
+                                 " closing link", IRCERR_BUFALLOC);
+    }
+
+    /*
+     * This little bit is to stop the sendQ from growing too large
+     * when there is no need for it to. Thus we call send_queued()
+     * every time 2k has been added to the queue since the last
+     * non-fatal write. Also stops us from deliberately building a
+     * large sendQ and then trying to flood that link with data
+     * (possible during the net relinking done by servers with a large
+     * load).
+     */
+    /*
+     * Well, let's try every 4k for clients, and immediately for servers
+     * -Taner
+     */
+    /*
+     * Let's not waste time trying this on anyone who has a blocking socket.
+     * Also, let's send every 8k for servers, since there's lots of traffic
+     * there and we'd like to make it more efficient. - lucas
+     */
+
+    if(to->flags & FLAGS_BLOCKED)
+       return 0;
+
+#ifdef ALWAYS_SEND_DURING_SPLIT
+    if (currently_processing_netsplit)
+    {
+        send_queued(to);
+        return 0;
+    }
+#endif
+
+    SQinK = (SBufLength(&to->sendQ) >> 10);
+    if (IsServer(to)) 
+    {
+        if (SQinK > (to->lastsq + 8))
+            send_queued(to);
+    }
+    else 
+    {
+        if (SQinK > (to->lastsq + 4))
+            send_queued(to);
+    }
+    return 0;
+}
+
+
+/*
+ * send_queued 
+ * This function is called from the main select-loop (or whatever) 
+ * when there is a chance the some output would be possible. This 
+ * attempts to empty the send queue as far as possible...
+ */
+int send_queued(aClient *to)
+{
+    char       *msg;
+    int         len, rlen;
+    int more_data = 0; /* the hybrid approach.. */
+#ifdef WRITEV_IOV
+    struct iovec iov[WRITEV_IOV];
+#endif
+        
+    /*
+     * Once socket is marked dead, we cannot start writing to it,
+     * even if the error is removed...
+     * this should never happen.
+     */
+    if (IsDead(to)) 
+        return -1;
+
+    if(ZipOut(to) && zip_is_data_out(to->serv->zip_out))
+    {
+        if(SBufLength(&to->sendQ))
+            more_data = 1;
+        else
+        {
+            int ldata = (to->flags & FLAGS_BURST);
+
+            msg = zip_output(to->serv->zip_out, NULL, &len, 1, &ldata);
+            if(len == -1)
+            {
+                sendto_realops("Zipout error for %s: (%d) %s\n", to->name,
+                               ldata, msg);
+                return dead_link(to, "Zip output error for %s", IRCERR_ZIP);
+            }
+
+#ifdef HAVE_ENCRYPTION_ON
+            if(IsRC4OUT(to))
+                rc4_process_stream(to->serv->rc4_out, msg, len);
+#endif
+            /* silently stick this on the sendq... */
+            if (!sbuf_put(&to->sendQ, msg, len))
+                return dead_link(to, "Buffer allocation error for %s",
+                                 IRCERR_BUFALLOC);
+        }
+    }
+   
+    while (SBufLength(&to->sendQ) > 0) 
+    {
+#ifdef WRITEV_IOV
+        len = sbuf_mapiov(&to->sendQ, iov);
+        if ((rlen = deliver_it(to, iov, len)) < 0)
+#else
+        msg = sbuf_map(&to->sendQ, &len);
+        if ((rlen = deliver_it(to, msg, len)) < 0)
+#endif
+            return dead_link(to, "Write error to %s, closing link (%s)", errno);
+        sbuf_delete(&to->sendQ, rlen);
+        to->lastsq = (SBufLength(&to->sendQ) >> 10);
+
+#ifdef STOP_SENDING_ON_SHORT_SEND
+        if (rlen < len)
+        {
+            /* Treat this socket as blocking */
+            to->flags |= FLAGS_BLOCKED;
+            set_fd_flags(to->fd, FDF_WANTWRITE);
+#else
+        if (rlen == 0)
+        {
+            /* Socket is blocking... */
+#endif
+            break;
+        }
+
+        if(more_data && SBufLength(&to->sendQ) == 0)
+        {
+            int ldata = (to->flags & FLAGS_BURST);
+            
+            more_data = 0;
+            
+            msg = zip_output(to->serv->zip_out, NULL, &len, 1, &ldata);
+            if(len == -1)
+            {
+                sendto_realops("Zipout error for %s: (%d) %s\n", to->name,
+                               ldata, msg);
+                return dead_link(to, "Zip output error for %s", IRCERR_ZIP);
+            }
+            
+#ifdef HAVE_ENCRYPTION_ON
+            if(IsRC4OUT(to))
+                rc4_process_stream(to->serv->rc4_out, msg, len);
+#endif
+            /* silently stick this on the sendq... */
+            if (!sbuf_put(&to->sendQ, msg, len))
+                return dead_link(to, "Buffer allocation error for %s",
+                                 IRCERR_BUFALLOC);        
+        }
+    }
+    
+    if ((to->flags & FLAGS_SOBSENT) && IsBurst(to)
+         && SBufLength(&to->sendQ) < 20480) 
+    {
+        if (!(to->flags & FLAGS_BURST))
+        {
+            to->flags &= (~FLAGS_SOBSENT);
+            sendto_one(to, "BURST %d", SBufLength(&to->sendQ));
+        }
+    }
+    return (IsDead(to)) ? -1 : 0;
+}
+
+/* send message to single client */
+void sendto_one(aClient *to, char *pattern, ...) 
+{
+    va_list vl;
+    int len;            /* used for the length of the current message */
+    
+    va_start(vl, pattern);
+    len = ircvsprintf(sendbuf, pattern, vl);
+   
+    if (to->from)
+        to = to->from;
+    if (IsMe(to)) 
+    {
+        strncpyzt(selfbuf, sendbuf, sizeof(selfbuf));
+        sendto_ops("Trying to send [%s] to myself!", selfbuf);
+        return;
+    }
+    send_message(to, sendbuf, len, NULL);
+    va_end(vl);
+}
+
+/* send message to single client */
+void sendto_one_services(aClient *to, char *pattern, ...) 
+{
+    va_list vl;
+    int len;            /* used for the length of the current message */
+    
+    if(confopts & FLAGS_SERVHUB)
+        if(to && to->user && to->user->server && 
+                (!mycmp(to->user->server,Services_Name) || 
+                 !mycmp(to->user->server,Stats_Name)))
+            return;
+
+    va_start(vl, pattern);
+    len = ircvsprintf(sendbuf, pattern, vl);
+   
+    if (to->from)
+        to = to->from;
+    if (IsMe(to)) 
+    {
+        strncpyzt(selfbuf, sendbuf, sizeof(selfbuf));
+        sendto_ops("Trying to send [%s] to myself!", selfbuf);
+        return;
+    }
+    send_message(to, sendbuf, len, NULL);
+    va_end(vl);
+}
+
+void vsendto_one(aClient *to, char *pattern, va_list vl) 
+{
+    int len;            /* used for the length of the current message */
+   
+    len = ircvsprintf(sendbuf, pattern, vl);
+   
+    if (to->from)
+        to = to->from;
+    if (IsMe(to) && to->fd >= 0) 
+    {
+        strncpyzt(selfbuf, sendbuf, sizeof(selfbuf));
+        sendto_ops("Trying to send [%s] to myself!", selfbuf);
+        return;
+    }
+    send_message(to, sendbuf, len, NULL);
+}
+
+/* prefix_buffer
+ *
+ * take varargs and dump prefixed message into a buffer
+ * remote: 1 if client is remote, 0 if local
+ * from: the client sending the message
+ * prefix: the prefix as specified (parv[0] usually)
+ * buffer: the buffer to dump this into (NO BOUNDS CHECKING!)
+ * pattern: varargs pattern
+ * vl: varargs variable list with one arg taken already
+ */
+static inline int prefix_buffer(int remote, aClient *from, char *prefix,
+                                char *buffer, char *pattern, va_list vl)
+{
+    char *p;      /* temp pointer */
+    int msglen;   /* the length of the message we end up with */
+    int sidx = 1; /* start at offset 1 */
+
+    *buffer = ':';
+
+    if(!remote && IsPerson(from))
+    {
+        int flag = 0;
+        anUser *user = from->user;
+
+        for(p = from->name; *p; p++)
+                buffer[sidx++] = *p;
+
+        if (user)
+        {
+            if (*user->username) 
+            {
+                buffer[sidx++] = '!';
+                for(p = user->username; *p; p++)
+                    buffer[sidx++] = *p;
+            }
+            if (*user->host && !MyConnect(from)) 
+            {
+                buffer[sidx++] = '@';
+                for(p = user->host; *p; p++)
+                    buffer[sidx++] = *p;
+                flag = 1;
+            }
+        }
+        if (!flag && MyConnect(from) && *user->host) 
+        {
+            buffer[sidx++] = '@';
+            for(p = from->sockhost; *p; p++)
+                buffer[sidx++] = *p;
+        }
+    }
+    else
+    {
+            for(p = prefix; *p; p++)
+                buffer[sidx++] = *p;
+    }
+
+    msglen = ircvsprintf(&buffer[sidx], pattern + 3, vl);
+    msglen += sidx;
+
+    return msglen;
+}
+
+static inline int check_fake_direction(aClient *from, aClient *to)
+{
+    if (!MyClient(from) && IsPerson(to) && (to->from == from->from)) 
+    {
+        if (IsServer(from)) 
+        {
+            sendto_ops("Message to %s[%s] dropped from %s (Fake Direction)",
+                       to->name, to->from->name, from->name);
+            return -1;
+        }
+
+        sendto_ops("Ghosted: %s[%s@%s] from %s[%s@%s] (%s)", to->name,
+                   to->user->username, to->user->host, from->name,
+                   from->user->username, from->user->host, to->from->name);
+        sendto_serv_butone(NULL, ":%s KILL %s :%s (%s[%s@%s] Ghosted %s)",
+                           me.name, to->name, me.name, to->name,
+                           to->user->username, to->user->host, to->from->name);
+        
+        to->flags |= FLAGS_KILLED;
+        exit_client(NULL, to, &me, "Ghosted client");
+
+        if (IsPerson(from))
+            sendto_one(from, err_str(ERR_GHOSTEDCLIENT), me.name, from->name,
+                       to->name, to->user->username, to->user->host, to->from);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+void sendto_channel_butone(aClient *one, aClient *from, aChannel *chptr,
+                           char *pattern, ...) 
+{
+    chanMember *cm;
+    aClient *acptr;
+    int i;
+    int didlocal = 0, didremote = 0;
+    va_list vl;
+    char *pfix;
+    void *share_bufs[2] = { 0, 0 };
+   
+    va_start(vl, pattern);
+
+    pfix = va_arg(vl, char *);
+
+    INC_SERIAL
+    for (cm = chptr->members; cm; cm = cm->next) 
+    {
+        acptr = cm->cptr;
+        if (acptr->from == one)
+            continue; /* ...was the one I should skip */
+
+        if(confopts & FLAGS_SERVHUB)
+            if(acptr && acptr->user && acptr->user->server && 
+                    (!mycmp(acptr->user->server,Services_Name) || 
+                    !mycmp(acptr->user->server,Stats_Name)))
+                continue;
+
+        i = acptr->from->fd;
+        if (MyClient(acptr)) 
+        {
+            if(!didlocal)
+            {
+                didlocal = prefix_buffer(0, from, pfix, sendbuf, 
+                                         pattern, vl);
+                sbuf_begin_share(sendbuf, didlocal, &share_bufs[0]);
+            }
+            if(check_fake_direction(from, acptr))
+                    continue;
+            
+            send_message(acptr, sendbuf, didlocal, share_bufs[0]);
+            sentalong[i] = sent_serial;
+        }
+        else 
+        {
+            /*
+             * Now check whether a message has been sent to this remote
+             * link already
+             */
+            if(!didremote)
+            {
+                didremote = prefix_buffer(1, from, pfix, remotebuf, 
+                                          pattern, vl);
+                sbuf_begin_share(remotebuf, didremote, &share_bufs[1]);
+            }
+            
+            if(check_fake_direction(from, acptr))
+                    continue;
+            
+            if (sentalong[i] != sent_serial) 
+            {
+                send_message(acptr, remotebuf, didremote, share_bufs[1]);
+                sentalong[i] = sent_serial;
+            }
+        }
+    }
+    
+    sbuf_end_share(share_bufs, 2);    
+    va_end(vl);
+    return;
+}
+
+/*
+ * Like sendto_channel_butone, but sends to all servers but 'one'
+ * that have clients in this channel.
+ */
+void sendto_channel_remote_butone(aClient *one, aClient *from, aChannel *chptr,
+                                  char *pattern, ...) 
+{
+    chanMember *cm;
+    aClient *acptr;
+    int i;
+    int didremote = 0;
+    va_list vl;
+    char *pfix;
+    void *share_buf = NULL;
+   
+    va_start(vl, pattern);
+
+    pfix = va_arg(vl, char *);
+
+    INC_SERIAL
+    for (cm = chptr->members; cm; cm = cm->next) 
+    {
+        acptr = cm->cptr;
+        if (acptr->from == one)
+            continue; /* ...was the one I should skip */
+
+        if(confopts & FLAGS_SERVHUB)
+            if(acptr && acptr->user && acptr->user->server && 
+                    (!mycmp(acptr->user->server,Services_Name) || 
+                     !mycmp(acptr->user->server,Stats_Name)))
+                continue;
+
+        i = acptr->from->fd;
+        if (!MyClient(acptr)) 
+        {
+            /*
+             * Now check whether a message has been sent to this remote
+             * link already
+             */
+            if(!didremote)
+            {
+                didremote = prefix_buffer(1, from, pfix, remotebuf,
+                                          pattern, vl);
+                sbuf_begin_share(remotebuf, didremote, &share_buf);
+            }
+            
+            if(check_fake_direction(from, acptr))
+                    continue;
+            
+            if (sentalong[i] != sent_serial) 
+            {
+                send_message(acptr, remotebuf, didremote, share_buf);
+                sentalong[i] = sent_serial;
+            }
+        }
+    }
+    
+    sbuf_end_share(&share_buf, 1);
+    
+    va_end(vl);
+    return;
+}
+
+/*
+ * sendto_server_butone_services
+ * 
+ * Send a message to all connected servers except the client 'one', and do not send to services.dal.net.
+ */
+void sendto_serv_butone_services(aClient *one, char *pattern, ...) 
+{
+    aClient *cptr;
+    int k = 0;
+    fdlist send_fdlist;
+    va_list vl;
+    DLink *lp;
+    
+
+    va_start(vl, pattern);
+    for (lp = server_list; lp; lp = lp->next)
+    {
+        cptr = lp->value.cptr;
+        if((confopts & FLAGS_SERVHUB) && (!mycmp(cptr->name,Services_Name) 
+                || !mycmp(cptr->name,Stats_Name)))
+            continue;
+
+        if (one && cptr == one->from)
+            continue;
+        send_fdlist.entry[++k] = cptr->fd;
+    }
+    send_fdlist.last_entry = k;
+    if (k)
+        vsendto_fdlist(&send_fdlist, pattern, vl);
+    va_end(vl);
+    return;
+}
+
+#ifdef NOQUIT
+/*
+ * sendto_non_noquit_servs_butone
+ *
+ * Send a message to all non-noquit servs
+ */
+void sendto_non_noquit_servs_butone(aClient *one, char *pattern, ...)
+{
+    aClient *cptr;
+    int k = 0;
+    fdlist send_fdlist;
+    va_list vl;
+    DLink *lp;
+
+    va_start(vl, pattern);
+    for(lp = server_list; lp; lp = lp->next)
+    {
+        cptr = lp->value.cptr;
+
+        if (IsNoquit(cptr) || (one == cptr))
+            continue;
+
+        send_fdlist.entry[++k] = cptr->fd;
+    }
+    send_fdlist.last_entry = k;
+    if (k)
+        vsendto_fdlist(&send_fdlist, pattern, vl);
+    va_end(vl);
+    return;
+}
+#endif
+
+/*
+ * sendto_server_butone
+ * 
+ * Send a message to all connected servers except the client 'one'.
+ */
+void sendto_serv_butone(aClient *one, char *pattern, ...) 
+{
+    aClient *cptr;
+    int k = 0;
+    fdlist send_fdlist;
+    va_list vl;
+    DLink *lp;
+        
+    va_start(vl, pattern);
+    for(lp = server_list; lp; lp = lp->next)
+    {
+        cptr = lp->value.cptr;
+        if (one && cptr == one->from)
+            continue;
+        send_fdlist.entry[++k] = cptr->fd;
+    }
+    send_fdlist.last_entry = k;
+    if (k)
+        vsendto_fdlist(&send_fdlist, pattern, vl);
+    va_end(vl);
+    return;
+}
+
+/*
+ * sendto_common_channels()
+ * 
+ * Sends a message to all people (inclusing user) on local server who are
+ * in same channel with user.
+ */
+void sendto_common_channels(aClient *from, char *pattern, ...)
+{
+    Link *channels;
+    chanMember *users;
+    aClient *cptr;
+    va_list vl;
+    char *pfix;
+    int msglen = 0;
+    void *share_buf = NULL;
+
+    va_start(vl, pattern);
+
+    pfix = va_arg(vl, char *);
+
+    INC_SERIAL
+
+    if(from->fd >= 0)
+        sentalong[from->fd] = sent_serial;
+    
+    if (from->user)
+    {
+        for (channels = from->user->channel; channels; 
+             channels = channels->next)
+        {
+            for (users = channels->value.chptr->members; users; 
+                 users = users->next) 
+            {
+                cptr = users->cptr;
+        
+                if (!MyConnect(cptr) || sentalong[cptr->fd] == sent_serial)
+                        continue;
+            
+                sentalong[cptr->fd] = sent_serial;
+                if (!msglen)
+                {
+                    msglen = prefix_buffer(0, from, pfix, sendbuf,
+                                           pattern, vl);
+                    sbuf_begin_share(sendbuf, msglen, &share_buf);
+                }
+                if (check_fake_direction(from, cptr))
+                    continue;
+                send_message(cptr, sendbuf, msglen, share_buf);
+            }
+        }
+    }
+    
+    if(MyConnect(from))
+    {
+        if(!msglen)
+            msglen = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
+        /* send the share buf if others are using it too */
+        send_message(from, sendbuf, msglen, share_buf);
+    }
+    
+    sbuf_end_share(&share_buf, 1);
+
+    va_end(vl);
+    return;
+}
+
+/*
+ * send_quit_to_common_channels()
+ * 
+ * Sends a message to all people (inclusing user) on local server who are
+ * in same channel with user if the user can send to this channel.
+ */
+void send_quit_to_common_channels(aClient *from, char *reason)
+{
+    Link *channels;
+    chanMember *users;
+    aClient *cptr;
+    int msglen;
+    void *share_buf = NULL;
+
+    INC_SERIAL
+    
+    msglen=sprintf(sendbuf,":%s!%s@%s QUIT :%s", from->name,
+                   from->user->username,from->user->host, reason);      
+    sbuf_begin_share(sendbuf, msglen, &share_buf);
+   
+    if(from->fd >= 0)
+        sentalong[from->fd] = sent_serial;    
+    for (channels = from->user->channel; channels; channels = channels->next)
+    {
+        if (!can_send(from, channels->value.chptr, reason)) 
+        {
+            for (users = channels->value.chptr->members; users; 
+                 users = users->next) 
+            {
+                cptr = users->cptr;
+                if (!MyConnect(cptr) || sentalong[cptr->fd] == sent_serial)
+                    continue;
+                sentalong[cptr->fd] = sent_serial;
+                if (check_fake_direction(from, cptr))
+                    continue;
+                send_message(cptr, sendbuf, msglen, share_buf);
+            }
+        }
+    }
+    sbuf_end_share(&share_buf, 1);
+}
+
+/*
+ * send_part_to_common_channels()
+ * 
+ * Sends a message to all people (inclusing user) on local server who are
+ * in same channel with user if the user cannot send to the channel.
+ */
+void send_part_to_common_channels(aClient *from, char *reason)
+{
+    Link *channels;
+    chanMember *users;
+    aClient *cptr;
+    int msglen = 0;
+    void *share_buf = NULL;
+    
+    for (channels = from->user->channel; channels; channels = channels->next)
+    {
+        if (can_send(from, channels->value.chptr, reason)) 
+        {
+            msglen = sprintf(sendbuf,":%s!%s@%s PART %s",
+                             from->name,from->user->username,from->user->host,
+                             channels->value.chptr->chname);
+            sbuf_begin_share(sendbuf, msglen, &share_buf);
+
+            INC_SERIAL
+
+            if (from->fd >= 0)
+                sentalong[from->fd] = sent_serial;
+
+            for (users = channels->value.chptr->members; users; 
+                 users = users->next) 
+            {
+                cptr = users->cptr;
+              
+                if (!MyConnect(cptr) || sentalong[cptr->fd] == sent_serial)
+                    continue;
+                
+                sentalong[cptr->fd] = sent_serial;
+                if (check_fake_direction(from, cptr))
+                    continue;
+                send_message(cptr, sendbuf, msglen, share_buf);
+            }
+            sbuf_end_share(&share_buf, 1);
+        }
+    }
+}
+
+#ifdef FLUD
+void sendto_channel_butlocal(aClient *one, aClient *from, aChannel *chptr,
+                             char *pattern, ...)
+{
+    chanMember *cm;
+    aClient *acptr;
+    int i;
+    va_list vl;
+          
+    va_start(vl, pattern);
+
+    INC_SERIAL
+    
+    for (cm = chptr->members; cm; cm = cm->next) 
+    {
+        acptr = cm->cptr;
+        if (acptr->from == one)
+            continue;           /* ...was the one I should skip */
+        i = acptr->from->fd;
+        if (!MyFludConnect(acptr)) 
+        {
+            /*
+             * Now check whether a message has been sent to this remote
+             * link already
+             */
+            if (sentalong[i] != sent_serial) 
+            {
+                vsendto_prefix_one(acptr, from, pattern, vl);
+                sentalong[i] = sent_serial;
+            }
+        }
+    }
+    va_end(vl);
+    return;
+}
+#endif /* FLUD */
+
+/*
+ * sendto_channel_butserv
+ * 
+ * Send a message to all members of a channel that are connected to this
+ * server.
+ */
+void sendto_channel_butserv(aChannel *chptr, aClient *from, char *pattern, ...)
+{
+    chanMember  *cm;
+    aClient *acptr;
+    va_list vl;
+    int didlocal = 0;
+    char *pfix;
+    void *share_buf = NULL;
+
+    va_start(vl, pattern);
+    
+    pfix = va_arg(vl, char *);
+
+    for (cm = chptr->members; cm; cm = cm->next)
+    {
+        if (MyConnect(acptr = cm->cptr))
+        {
+            if(!didlocal)
+            {
+                didlocal = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
+                sbuf_begin_share(sendbuf, didlocal, &share_buf);
+            }
+            
+            if (check_fake_direction(from, acptr))
+                continue;
+
+            send_message(acptr, sendbuf, didlocal, share_buf);
+
+            /* vsendto_prefix_one(acptr, from, pattern, vl); */
+        }
+    }
+    sbuf_end_share(&share_buf, 1);
+    va_end(vl);
+}
+
+/*
+ * sendto_channel_butserv_me
+ * 
+ * Send a message to all members of a channel that are connected to this
+ * server. Possibly hide the origin, if it's a server, with me.name if certain paranoia is on.
+ */
+void sendto_channel_butserv_me(aChannel *chptr, aClient *from, char *pattern, ...)
+{
+    chanMember  *cm;
+    aClient *acptr;
+    va_list vl;
+    int didlocal = 0;
+    char *pfix;
+    void *share_buf = NULL;
+
+    va_start(vl, pattern);
+    
+    pfix = va_arg(vl, char *);
+
+#ifdef HIDE_SERVERMODE_ORIGINS
+    if(IsServer(from) && !IsULine(from))
+    {
+       from = &me;
+       pfix = me.name;
+    }
+#endif
+
+    for (cm = chptr->members; cm; cm = cm->next)
+    {
+        if (MyConnect(acptr = cm->cptr))
+        {
+            if (!didlocal)
+            {
+                didlocal = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
+                sbuf_begin_share(sendbuf, didlocal, &share_buf);
+            }
+            
+            if(check_fake_direction(from, acptr))
+                continue;
+
+            send_message(acptr, sendbuf, didlocal, share_buf);
+
+        }
+    }
+    sbuf_end_share(&share_buf, 1);
+    va_end(vl);
+}
+
+/*
+ * sendto_channelops_butserv
+ * 
+ * Send a message to all operators of a channel that are connected to this
+ * server.
+ */
+void sendto_channelops_butserv(aChannel *chptr, aClient *from, char *pattern, ...)
+{
+    chanMember  *cm;
+    aClient *acptr;
+    va_list vl;
+    int didlocal = 0;
+    char *pfix;
+    void *share_buf = NULL;
+
+    va_start(vl, pattern);
+    
+    pfix = va_arg(vl, char *);
+
+    for (cm = chptr->members; cm; cm = cm->next)
+    {
+        if (MyConnect(acptr = cm->cptr) && (cm->flags & CHFL_CHANOP))
+        {
+            if (!didlocal)
+            {
+                didlocal = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
+                sbuf_begin_share(sendbuf, didlocal, &share_buf);
+            }
+            
+            if (check_fake_direction(from, acptr))
+                continue;
+
+            send_message(acptr, sendbuf, didlocal, share_buf);
+        }
+    }
+    sbuf_end_share(&share_buf, 1);
+    va_end(vl);
+}
+
+/*
+ * * send a msg to all ppl on servers/hosts that match a specified mask *
+ * (used for enhanced PRIVMSGs) *
+ * 
+ * addition -- Armin, 8jun90 (gruner@informatik.tu-muenchen.de)
+ */
+static int match_it(aClient *one, char *mask, int what)
+{
+    if (what == MATCH_HOST)
+        return (match(mask, one->user->host) == 0);
+    else
+        return (match(mask, one->user->server) == 0);
+}
+
+/*
+ * sendto_match_butone
+ * 
+ * Send to all clients which match the mask in a way defined on 'what';
+ * either by user hostname or user servername.
+ */
+void sendto_match_butone(aClient *one, aClient *from, char *mask, int what, 
+                         char *pattern, ...)
+{
+    int     i;
+    aClient *cptr, *acptr;
+    char cansendlocal, cansendglobal;
+    va_list vl;
+        
+    va_start(vl, pattern);
+    if (MyConnect(from)) 
+    {
+        cansendlocal = (OPCanLNotice(from)) ? 1 : 0;
+        cansendglobal = (OPCanGNotice(from)) ? 1 : 0;
+    } 
+    else 
+        cansendlocal = cansendglobal = 1;
+    for (i = 0; i <= highest_fd; i++) 
+    {
+        if (!(cptr = local[i]))
+            continue;           /* that clients are not mine */
+        if (cptr == one)                /* must skip the origin !! */
+            continue;
+        if (IsServer(cptr)) 
+        {
+            if (!cansendglobal) continue;
+            for (acptr = client; acptr; acptr = acptr->next)
+                if (IsRegisteredUser(acptr)
+                    && match_it(acptr, mask, what)
+                    && acptr->from == cptr)
+                    break;
+            /*
+             * a person on that server matches the mask, so we * send *one*
+             * msg to that server ...
+             */
+            if (acptr == NULL)
+                continue;
+            /* ... but only if there *IS* a matching person */
+        }
+        /* my client, does he match ? */
+        else if (!cansendlocal || !(IsRegisteredUser(cptr) &&
+                                    match_it(cptr, mask, what)))
+            continue;
+        vsendto_prefix_one(cptr, from, pattern, vl);
+    }
+    va_end(vl);
+    return;
+}
+
+/*
+ * sendto_all_butone.
+ * 
+ * Send a message to all connections except 'one'. The basic wall type
+ * message generator.
+ */
+void sendto_all_butone(aClient *one, aClient *from, char *pattern, ...)
+{
+    int     i;
+    aClient *cptr;
+    va_list vl;
+        
+    va_start(vl, pattern);
+    for (i = 0; i <= highest_fd; i++)
+        if ((cptr = local[i]) && !IsMe(cptr) && one != cptr)
+            vsendto_prefix_one(cptr, from, pattern, vl);
+    va_end(vl);
+    return;
+}
+
+/*
+ * sendto_ops_lev
+ * 
+ * Send to *local* ops only at a certain level... 0 = normal +s 1 = client
+ * connect/disconnect   (+c) [IRCOPS ONLY] 2 = bot rejection
+ * (+r) 3 = server kills                      (+k)
+ */
+void sendto_ops_lev(int lev, char *pattern, ...)
+{
+    aClient *cptr;
+    int     i;
+    char        nbuf[1024];
+    va_list vl;
+    char *tmsg;
+
+#ifdef NICER_UMODENOTICE_SEPARATION
+    switch(lev)
+    {
+       case CCONN_LEV:
+          tmsg = "Client";
+          break;
+
+       case DEBUG_LEV:
+          tmsg = "Debug";
+          break;
+
+       case SPY_LEV:
+          tmsg = "Spy";
+          break;
+
+       case SPAM_LEV:
+          tmsg = "Spam";
+          break;
+
+       case FLOOD_LEV:
+          tmsg = "Flood";
+          break;
+
+       case DCCSEND_LEV:
+          tmsg = "DCCAllow";
+          break;
+
+       default:
+          tmsg = "Notice";
+    }
+#else
+    tmsg = "Notice";
+#endif
+        
+    va_start(vl,pattern);
+    for (i = 0; i <= highest_fd; i++)
+    if ((cptr = local[i]) && !IsServer(cptr) && !IsMe(cptr)) 
+    {
+        switch (lev) 
+        {
+            case CCONN_LEV:
+                if (!SendCConnNotice(cptr) || !IsAnOper(cptr))
+                    continue;
+                break;
+            case REJ_LEV:
+                if (!SendRejNotice(cptr) || !IsAnOper(cptr))
+                    continue;
+                break;
+            case SKILL_LEV:
+                if (!SendSkillNotice(cptr))
+                    continue;
+                break;
+            case USKILL_LEV:
+                if (!SendSUkillNotice(cptr) || !IsAnOper(cptr))
+                    continue;
+                break;
+            case SPY_LEV:
+                if (!SendSpyNotice(cptr) || !IsAnOper(cptr))
+                    continue;
+                break;
+            case DCCSEND_LEV:
+                if (!SendDCCNotice(cptr) || !IsAnOper(cptr))
+                    continue;
+                break;
+            case FLOOD_LEV:
+                if (!SendFloodNotice(cptr) || !IsAnOper(cptr))
+                    continue;
+                break;
+            case SPAM_LEV:
+                if (!SendSpamNotice(cptr) || !IsAnOper(cptr))
+                    continue;
+                break;
+            case DEBUG_LEV:
+                if (!SendDebugNotice(cptr) || !IsAnOper(cptr))
+                    continue;
+                break;
+                          
+            default:            /* this is stupid, but oh well */
+                if (!SendServNotice(cptr))
+                    continue;
+        }
+        ircsprintf(nbuf, ":%s NOTICE %s :*** %s -- ", me.name, 
+                   cptr->name, tmsg);
+        strncat(nbuf, pattern, sizeof(nbuf) - strlen(nbuf));
+        vsendto_one(cptr, nbuf, vl);
+    }
+    va_end(vl);
+    return;
+}                               
+
+/*
+ * sendto_ops
+ * 
+ * Send to *local* ops only.
+ */
+void sendto_ops(char *pattern, ...)
+{
+    va_list vl;
+        
+    va_start(vl, pattern);
+    vsendto_realops(pattern, vl);
+    va_end(vl);
+    return;
+}
+
+/*
+ * sendto_ops_butone 
+ * Send message to all operators. 
+ * one - client not to send message to 
+ * from- client which message is from *NEVER* NULL!!
+ */
+void sendto_ops_butone(aClient *one, aClient *from, char *pattern, ...)
+{
+    int     i;
+    aClient *cptr;
+    va_list vl;
+           
+    va_start(vl, pattern);
+
+    INC_SERIAL
+
+    for (cptr = client; cptr; cptr = cptr->next)
+    {
+        if (!SendWallops(cptr))
+            continue;
+        /*
+         * we want wallops if (MyClient(cptr) && !(IsServer(from) ||
+         * IsMe(from))) continue;
+         */
+        i = cptr->from->fd;     /* find connection oper is on */
+        if (sentalong[i] == sent_serial) /* sent message along it already ? */
+            continue;
+        if (cptr->from == one)
+            continue;           /* ...was the one I should skip */
+        sentalong[i] = sent_serial;
+        vsendto_prefix_one(cptr->from, from, pattern, vl);
+    }
+    va_end(vl);
+    return;
+}
+
+/*
+ * * sendto_wallops_butone *      Send message to all operators. * one
+ * - client not to send message to * from- client which message is from
+ * *NEVER* NULL!!
+ */
+void sendto_wallops_butone(aClient *one, aClient *from, char *pattern, ...)
+{
+    int     i;
+    aClient *cptr;
+    va_list vl;
+        
+    va_start(vl, pattern);
+    for(i=0;i<=highest_fd;i++)
+    {
+        if((cptr=local[i])!=NULL)
+        {
+            if(!(IsRegistered(cptr) && (SendWallops(cptr) ||
+                                        IsServer(cptr))) || cptr==one)
+                continue;
+            vsendto_prefix_one(cptr, from, pattern, vl);
+        }
+    }
+    va_end(vl);
+    return;
+}
+
+void send_globops(char *pattern, ...)
+{
+    aClient *cptr;
+    char nbuf[1024];
+    va_list vl;
+    DLink *lp;
+          
+    va_start(vl, pattern);
+    for (lp = oper_list; lp; lp = lp->next)
+    {
+        cptr = lp->value.cptr;
+        if (!SendGlobops(cptr) || !IsAnOper(cptr))
+            continue;
+
+        if (IsAnOper(cptr))
+        {
+            ircsprintf(nbuf, ":%s NOTICE %s :*** Global -- %s",
+                       me.name, cptr->name, pattern);
+            vsendto_one(cptr, nbuf, vl);
+        }
+    }
+    va_end(vl);
+    return;
+}
+
+void send_chatops(char *pattern, ...)
+{
+    aClient *cptr;
+    char nbuf[1024];
+    va_list vl;
+    DLink *lp;
+          
+    va_start(vl, pattern);
+    for (lp = oper_list; lp; lp = lp->next)
+    {
+        cptr = lp->value.cptr;
+        if (!SendChatops(cptr) || !IsAnOper(cptr))
+            continue;
+
+        if (IsAnOper(cptr))
+        {
+            ircsprintf(nbuf, ":%s NOTICE %s :*** ChatOps -- %s",
+                       me.name, cptr->name, pattern);
+            vsendto_one(cptr, nbuf, vl);
+        }
+    }
+    va_end(vl);
+    return;
+}
+
+/*
+ * to - destination client from - client which message is from
+ * 
+ * NOTE: NEITHER OF THESE SHOULD *EVER* BE NULL!! -avalon
+ */
+void sendto_prefix_one(aClient *to, aClient *from, char *pattern, ...)
+{
+    static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
+    static char temp[1024];
+    anUser *user;
+    char *idx;
+    char *par;
+    int flag = 0, sidx = 0;
+    va_list vl, vl2;
+
+    va_start(vl, pattern);
+    VA_COPY(vl2, vl);
+
+    par = va_arg(vl, char *);
+    /*
+     * Optimize by checking if (from && to) before everything 
+     * uhh, there's _always_ going to be a to!
+     */
+    if (from) 
+    {
+        if (!MyClient(from) && IsPerson(to) && (to->from == from->from)) 
+        {
+            if (IsServer(from)) 
+            {
+                ircvsprintf(temp, pattern, vl2);
+                sendto_ops("Send message (%s) to %s[%s] dropped from "
+                           "%s(Fake Dir)", temp, to->name, to->from->name,
+                           from->name);
+                va_end(vl);
+                return;
+            }
+            
+            sendto_ops("Ghosted: %s[%s@%s] from %s[%s@%s] (%s)", to->name,
+                       to->user->username, to->user->host, from->name,
+                       from->user->username, from->user->host, to->from->name);
+            sendto_serv_butone(NULL, ":%s KILL %s :%s (%s[%s@%s] Ghosted %s)",
+                               me.name, to->name, me.name, to->name,
+                               to->user->username, to->user->host,
+                               to->from->name);
+
+            to->flags |= FLAGS_KILLED;
+            exit_client(NULL, to, &me, "Ghosted client");
+            if (IsPerson(from))
+                sendto_one(from, err_str(ERR_GHOSTEDCLIENT), me.name,
+                           from->name, to->name, to->user->username,
+                           to->user->host, to->from);
+            va_end(vl);
+            return;
+        }
+
+        if (MyClient(to) && IsPerson(from) && !mycmp(par, from->name)) 
+        {
+            user = from->user;
+
+            for(idx = from->name; *idx; idx++)
+                sender[sidx++] = *idx;
+
+            if (user)
+            {
+                if (*user->username) 
+                {
+                    sender[sidx++] = '!';
+                    for(idx = user->username; *idx; idx++)
+                        sender[sidx++] = *idx;
+                }
+                if (*user->host && !MyConnect(from)) 
+                {
+                    sender[sidx++] = '@';
+                    for(idx = user->host; *idx; idx++)
+                        sender[sidx++] = *idx;
+                    flag = 1;
+                }
+            }
+
+            /*
+             * flag is used instead of index(sender, '@') for speed and
+             * also since username/nick may have had a '@' in them.
+             * -avalon
+             */
+
+            if (!flag && MyConnect(from) && *user->host) 
+            {
+                sender[sidx++] = '@';
+                for(idx = from->sockhost; *idx; idx++)
+                    sender[sidx++] = *idx;
+            }
+
+            sender[sidx] = '\0';
+            par = sender;
+        }
+    }
+
+    temp[0] = ':';
+    sidx = 1;
+
+    /*
+     * okay, we more or less know that our sendto_prefix crap is going 
+     * to be :%s <blah>, so it's easy to fix these lame problems...joy 
+     */
+
+    for(idx = par; *idx; idx++)
+        temp[sidx++] = *idx;
+    for(idx = (pattern + 3); *idx; idx++)
+        temp[sidx++] = *idx;
+
+    temp[sidx] = '\0'; 
+
+    vsendto_one(to, temp, vl);
+    va_end(vl);
+}
+
+/* this is an incredibly expensive function. 
+ * removed all strcat() calls. - lucas */
+void vsendto_prefix_one(aClient *to, aClient *from, char *pattern, va_list vl)
+{
+    static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
+    static char temp[1024];
+    anUser *user;
+    char *idx;
+    char *par;
+    int flag = 0, sidx = 0;
+    va_list vl2;
+    VA_COPY(vl2, vl);
+        
+    par = va_arg(vl2, char *);
+    /*
+     * Optimize by checking if (from && to) before everything 
+     * uhh, there's _always_ going to be a to!
+     */
+    if (from) 
+    {
+        if (!MyClient(from) && IsPerson(to) && (to->from == from->from)) 
+        {
+            if (IsServer(from)) 
+            {
+                ircvsprintf(temp, pattern, vl);
+                sendto_ops("Send message (%s) to %s[%s] dropped from "
+                           "%s(Fake Dir)", temp,
+                           to->name, to->from->name, from->name);
+                return;
+            }
+
+            sendto_ops("Ghosted: %s[%s@%s] from %s[%s@%s] (%s)", to->name,
+                       to->user->username, to->user->host, from->name,
+                       from->user->username, from->user->host, to->from->name);
+            sendto_serv_butone(NULL, ":%s KILL %s :%s (%s[%s@%s] Ghosted %s)",
+                               me.name, to->name, me.name, to->name,
+                               to->user->username, to->user->host,
+                               to->from->name);
+
+            to->flags |= FLAGS_KILLED;
+            exit_client(NULL, to, &me, "Ghosted client");
+            if (IsPerson(from))
+                sendto_one(from, err_str(ERR_GHOSTEDCLIENT), me.name,
+                           from->name, to->name, to->user->username,
+                           to->user->host, to->from);
+            return;
+        }
+
+        if (MyClient(to) && IsPerson(from) && !mycmp(par, from->name)) 
+        {
+            user = from->user;
+
+            for(idx = from->name; *idx; idx++)
+                sender[sidx++] = *idx;
+
+            if (user)
+            {
+                if (*user->username) 
+                {
+                    sender[sidx++] = '!';
+                    for(idx = user->username; *idx; idx++)
+                        sender[sidx++] = *idx;
+                }
+                if (*user->host && !MyConnect(from)) 
+                {
+                    sender[sidx++] = '@';
+                    for(idx = user->host; *idx; idx++)
+                        sender[sidx++] = *idx;
+                    flag = 1;
+                }
+            }
+
+            /*
+             * flag is used instead of index(sender, '@') for speed and
+             * also since username/nick may have had a '@' in them.
+             * -avalon
+             */
+
+            if (!flag && MyConnect(from) && *user->host) 
+            {
+                sender[sidx++] = '@';
+                for(idx = from->sockhost; *idx; idx++)
+                    sender[sidx++] = *idx;
+            }
+
+            sender[sidx] = '\0';
+            par = sender;
+
+        }
+    }
+
+    temp[0] = ':';
+    sidx = 1;
+
+    /* 
+     * okay, we more or less know that our sendto_prefix crap is 
+     * going to be :%s <blah>, so it's easy to fix these lame problems...joy
+     */
+
+    for(idx = par; *idx; idx++)
+        temp[sidx++] = *idx;
+    for(idx = (pattern + 3); *idx; idx++)
+        temp[sidx++] = *idx;
+
+    temp[sidx] = '\0'; 
+
+    vsendto_one(to, temp, vl2);
+}
+
+void sendto_fdlist(fdlist *listp, char *pattern, ...)
+{
+    int len, j, fd;
+    va_list vl;
+    void *share_buf = NULL;
+    
+    va_start(vl, pattern);
+    len = ircvsprintf(sendbuf, pattern, vl);
+    sbuf_begin_share(sendbuf, len, &share_buf);
+        
+    for (fd = listp->entry[j = 1]; j <= listp->last_entry;
+         fd = listp->entry[++j])
+        send_message(local[fd], sendbuf, len, share_buf);
+    sbuf_end_share(&share_buf, 1);
+    va_end(vl);
+}
+
+void vsendto_fdlist(fdlist *listp, char *pattern, va_list vl)
+{
+    int len, j, fd;
+    void *share_buf = NULL;
+    len = ircvsprintf(sendbuf, pattern, vl);
+    sbuf_begin_share(sendbuf, len, &share_buf);
+        
+    for (fd = listp->entry[j = 1]; j <= listp->last_entry;
+         fd = listp->entry[++j])
+        send_message(local[fd], sendbuf, len, share_buf);
+    sbuf_end_share(&share_buf, 1);
+}
+
+
+void vsendto_realops(char *pattern, va_list vl)
+{
+    aClient *cptr;
+    char nbuf[1024];
+    DLink *lp;
+
+    for (lp = oper_list; lp; lp = lp->next)
+    {
+        cptr = lp->value.cptr;
+        if (IsAnOper(cptr))
+        {
+            ircsprintf(nbuf, ":%s NOTICE %s :*** Notice -- %s",
+                       me.name, cptr->name, pattern);
+            vsendto_one(cptr, nbuf, vl);
+        }
+    }
+    return;
+}
+
+/*
+ * sendto_realops
+ * 
+ * Send to *local* ops only but NOT +s nonopers.
+ * If it's to local ops only and not +s nonopers, then SendServNotice is
+ * wrong. Changed to IsAnOper. -mjs
+ */
+void sendto_realops(char *pattern, ...)
+{
+    va_list vl;
+          
+    va_start(vl, pattern);
+    vsendto_realops(pattern, vl);
+    va_end(vl);
+}
+
+/*
+ * sendto_realops_lev
+ * 
+ * Send to *local* ops only but NOT +s nonopers at a certain level
+ */
+void sendto_realops_lev(int lev, char *pattern, ...)
+{
+    aClient *cptr;
+    char nbuf[1024];
+    va_list vl;
+    DLink *lp;
+    char *tmsg;
+
+    va_start(vl, pattern);
+
+#ifdef NICER_UMODENOTICE_SEPARATION
+    switch(lev)
+    {
+       case CCONN_LEV:
+          tmsg = "Client";
+          break;
+
+       case DEBUG_LEV:
+          tmsg = "Debug";
+          break;
+
+       case SPY_LEV:
+          tmsg = "Spy";
+          break;
+
+       case SPAM_LEV:
+          tmsg = "Spam";
+          break;
+
+       case FLOOD_LEV:
+          tmsg = "Flood";
+          break;
+
+       case DCCSEND_LEV:
+          tmsg = "DCCAllow";
+          break;
+
+       default:
+          tmsg = "Notice";
+    }
+#else
+    tmsg = "Notice";
+#endif
+
+    for (lp = oper_list; lp; lp = lp->next)
+    {
+        cptr = lp->value.cptr;
+        switch (lev)
+        {
+            case CCONN_LEV:
+                if (!SendCConnNotice(cptr))
+                    continue;
+                break;
+            case REJ_LEV:
+                if (!SendRejNotice(cptr))
+                    continue;
+                break;
+            case SKILL_LEV:
+                if (!SendSkillNotice(cptr))
+                    continue;
+                break;
+            case USKILL_LEV:
+                if (!SendSUkillNotice(cptr))
+                    continue;
+                break;
+            case SPY_LEV:
+                if (!SendSpyNotice(cptr))
+                    continue;
+                break;
+            case DCCSEND_LEV:
+                if (!SendDCCNotice(cptr))
+                    continue;
+                break;
+            case FLOOD_LEV:
+                if (!SendFloodNotice(cptr))
+                    continue;
+                break;
+            case SPAM_LEV:
+                if (!SendSpamNotice(cptr))
+                    continue;
+                break;
+            case DEBUG_LEV:
+                if (!SendDebugNotice(cptr))
+                    continue;
+                break;
+        }
+        ircsnprintf(nbuf, 1024, ":%s NOTICE %s :*** %s -- %s",
+                    me.name, cptr->name, tmsg, pattern);
+        vsendto_one(cptr, nbuf, vl);
+    }
+    va_end(vl);
+    return;
+}
+
+/*
+ * ts_warn
+ * Call sendto_ops, with some flood checking (at most 5 warnings 
+ * every 5 seconds)
+ */
+
+void ts_warn(char * pattern, ...)
+{
+    static ts_val last = 0;
+    static int  warnings = 0;
+    ts_val now;
+    va_list vl;
+        
+    va_start(vl, pattern);
+    /*
+     * if we're running with TS_WARNINGS enabled and someone does
+     * something silly like (remotely) connecting a nonTS server,
+     * we'll get a ton of warnings, so we make sure we don't send more
+     * than 5 every 5 seconds.  -orabidoo
+     */
+    now = time(NULL);
+    if (now - last < 5)
+    {
+        if (++warnings > 5)
+            return;
+    }
+    else
+    {
+        last = now;
+        warnings = 0;
+    }
+
+    vsendto_realops(pattern, vl);
+    va_end(vl);
+    return;
+}
+
+/*
+ * sendto_locops
+ */
+void sendto_locops(char *pattern, ...)
+{
+    aClient *cptr;
+    char nbuf[1024];
+    va_list vl;
+    DLink *lp;
+
+    va_start(vl, pattern);
+
+    for (lp = oper_list; lp; lp = lp->next)
+    {
+        cptr = lp->value.cptr;
+
+        if (SendGlobops(cptr))
+        {
+            ircsprintf(nbuf, ":%s NOTICE %s :*** LocOps -- %s",
+                       me.name, cptr->name, pattern);
+            vsendto_one(cptr, nbuf, vl);
+        }
+    }
+    va_end(vl);
+    return;
+}
+
+/* sendto_gnotice - send a routing notice to all local +n users. */
+void sendto_gnotice(char *pattern, ...)
+{
+    aClient *cptr;
+    char nbuf[1024];
+    va_list vl;
+    DLink *lp;
+        
+    va_start(vl, pattern);
+
+    for (lp = oper_list; lp; lp = lp->next)
+    {
+        cptr = lp->value.cptr;
+        if (SendRnotice(cptr)) 
+        {
+            ircsprintf(nbuf, ":%s NOTICE %s :*** Routing -- %s",
+                       me.name, cptr->name, pattern);
+            vsendto_one(cptr, nbuf, vl);
+        }
+    }
+    va_end(vl);
+    return;
+}
+
+/*
+ * sendto_channelops_butone
+ *   Send a message to all OPs in channel chptr that
+ *   are directly on this server and sends the message
+ *   on to the next server if it has any OPs.
+ */
+void sendto_channelops_butone(aClient *one, aClient *from, aChannel *chptr, 
+                              char *pattern, ...)
+{
+    chanMember   *cm;
+    aClient *acptr;
+    int     i;
+    va_list vl;
+        
+    va_start(vl, pattern);
+
+    INC_SERIAL
+    for (cm = chptr->members; cm; cm = cm->next)
+    {
+        acptr = cm->cptr;
+        if (acptr->from == one ||
+            !(cm->flags & CHFL_CHANOP))
+            continue;
+
+        if(confopts & FLAGS_SERVHUB)
+            if(acptr && acptr->user && acptr->user->server && 
+                (!mycmp(acptr->user->server,Services_Name) || 
+                !mycmp(acptr->user->server,Stats_Name)))
+            continue;
+
+        i = acptr->from->fd;
+        if (MyConnect(acptr) && IsRegisteredUser(acptr)) 
+        {
+            vsendto_prefix_one(acptr, from, pattern, vl);
+            sentalong[i] = sent_serial;
+        }
+        else
+        {
+            /*
+             * Now check whether a message has been sent to this
+             * remote link already 
+             */
+            if (sentalong[i] != sent_serial)
+            {
+                vsendto_prefix_one(acptr, from, pattern, vl);
+                
+                sentalong[i] = sent_serial;
+            }
+        }
+    }
+    va_end(vl);
+    return;
+}
+
+/*
+ * sendto_channelvoice_butone
+ *   Send a message to all voiced users in channel chptr that
+ *   are directly on this server and sends the message
+ *   on to the next server if it has any voiced users.
+ */
+void sendto_channelvoice_butone(aClient *one, aClient *from, aChannel *chptr, 
+                                char *pattern, ...)
+{
+    chanMember   *cm;
+    aClient *acptr;
+    int     i, didlocal = 0, didremote = 0;
+    va_list vl;
+    char *pfix;
+    void *share_buf[2] = { 0, 0 };
+        
+    va_start(vl, pattern);
+    pfix = va_arg(vl, char *);
+
+    INC_SERIAL
+    
+    for (cm = chptr->members; cm; cm = cm->next)
+    {
+        acptr = cm->cptr;
+        if (acptr->from == one || !(cm->flags & CHFL_VOICE))
+            continue;
+        if(confopts & FLAGS_SERVHUB)
+            if(acptr && acptr->user && acptr->user->server && 
+                (!mycmp(acptr->user->server,Services_Name) || 
+                 !mycmp(acptr->user->server,Stats_Name)))
+                continue;
+        i = acptr->from->fd;
+        if (MyConnect(acptr) && IsRegisteredUser(acptr))
+        {
+            if (!didlocal)
+            {
+                didlocal = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
+                sbuf_begin_share(sendbuf, didlocal, &share_buf[0]);
+            }
+            send_message(acptr, sendbuf, didlocal, share_buf[0]);
+            /* vsendto_prefix_one(acptr, from, pattern, vl); */
+            sentalong[i] = sent_serial;
+        }
+        else
+        {
+            /*
+             * Now check whether a message has been sent to this
+             * remote link already 
+             */
+            if (sentalong[i] != sent_serial)
+            {
+                if (!didremote)
+                {
+                    didremote = prefix_buffer(1, from, pfix, remotebuf, 
+                                              pattern, vl);
+                    sbuf_begin_share(remotebuf, didremote, &share_buf[1]);
+                }
+                send_message(acptr, remotebuf, didremote, share_buf[1]);
+                /* vsendto_prefix_one(acptr, from, pattern, vl); */
+                sentalong[i] = sent_serial;
+            }
+        }
+    }
+    sbuf_end_share(share_buf, 2);
+    va_end(vl);
+}
+
+/*
+ * sendto_channelvoiceops_butone
+ *   Send a message to all OPs or voiced users in channel chptr that
+ *   are directly on this server and sends the message
+ *   on to the next server if it has any OPs or voiced users.
+ */
+void sendto_channelvoiceops_butone(aClient *one, aClient *from, aChannel 
+                                   *chptr, char *pattern, ...)
+{
+    chanMember   *cm;
+    aClient *acptr;
+    int     i, didlocal = 0, didremote = 0;
+    char *pfix;
+    va_list vl;
+    void *share_buf[2] = { 0, 0 };
+        
+    va_start(vl, pattern);
+    pfix = va_arg(vl, char *);
+
+    INC_SERIAL
+
+    for (cm = chptr->members; cm; cm = cm->next)
+    {
+        acptr = cm->cptr;
+        if (acptr->from == one || !((cm->flags & CHFL_VOICE) ||
+                                    (cm->flags & CHFL_CHANOP)))
+            continue;
+
+        if(confopts & FLAGS_SERVHUB)
+            if(acptr && acptr->user && acptr->user->server && 
+                (!mycmp(acptr->user->server,Services_Name) || 
+                 !mycmp(acptr->user->server,Stats_Name)))
+                continue;
+
+        i = acptr->from->fd;
+        if (MyConnect(acptr) && IsRegisteredUser(acptr)) 
+        {
+            if (!didlocal)
+            {
+                didlocal = prefix_buffer(0, from, pfix, sendbuf, pattern, vl);
+                sbuf_begin_share(sendbuf, didlocal, &share_buf[0]);
+            }
+            send_message(acptr, sendbuf, didlocal, share_buf[0]);
+            /* vsendto_prefix_one(acptr, from, pattern, vl); */
+            sentalong[i] = sent_serial;
+        }
+        else /* remote link */
+        {
+            if (sentalong[i] != sent_serial) 
+            {
+                if (!didremote)
+                {
+                    didremote = prefix_buffer(1, from, pfix, remotebuf, 
+                                              pattern, vl);
+                    sbuf_begin_share(remotebuf, didremote, &share_buf[1]);
+                }
+                send_message(acptr, remotebuf, didremote, share_buf[1]);
+                /* vsendto_prefix_one(acptr, from, pattern, vl); */
+                sentalong[i] = sent_serial;
+            }
+        }
+    }
+    sbuf_end_share(share_buf, 2);
+}
+
+/*******************************************
+ * Flushing functions (empty queues)
+ *******************************************/
+
+/*
+ * flush_connections
+ * Empty only buffers for clients without FLAGS_BLOCKED
+ * dump_connections 
+ * Unintelligently try to empty all buffers.
+ */
+void flush_connections(int fd) 
+{
+    int     i;
+    aClient *cptr;
+    
+    if (fd == me.fd) 
+    {
+        for (i = highest_fd; i >= 0; i--)
+        {
+            if (!(cptr = local[i]))
+               continue;
+            if(!(cptr->flags & FLAGS_BLOCKED) &&
+                (SBufLength(&cptr->sendQ) > 0 ||
+                (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
+                send_queued(cptr);
+        }
+    }
+    else if (fd >= 0 && (cptr = local[fd]) &&
+             !(cptr->flags & FLAGS_BLOCKED) && 
+             (SBufLength(&cptr->sendQ) > 0 || 
+             (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
+        send_queued(cptr);
+}
+
+void dump_connections(int fd) 
+{
+    int     i;
+    aClient *cptr;
+    
+    if (fd == me.fd) 
+    {
+        for (i = highest_fd; i >= 0; i--)
+            if ((cptr = local[i]) && 
+                (SBufLength(&cptr->sendQ) > 0 || 
+                (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
+                send_queued(cptr);
+    }
+    else if (fd >= 0 && (cptr = local[fd]) && 
+        (SBufLength(&cptr->sendQ) > 0 || 
+        (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
+        send_queued(cptr);
+}
+
+/* flush an fdlist intelligently */
+void flush_fdlist_connections(fdlist *listp)
+{
+    int i, fd;
+    aClient *cptr;
+        
+    for (fd = listp->entry[i = 1]; i <= listp->last_entry;
+         fd = listp->entry[++i])
+        if ((cptr = local[fd]) && !(cptr->flags & FLAGS_BLOCKED) &&
+            (SBufLength(&cptr->sendQ) > 0 ||
+            (ZipOut(cptr) && zip_is_data_out(cptr->serv->zip_out))))
+            send_queued(cptr);
+}
diff --git a/src/socketengine_devpoll.c b/src/socketengine_devpoll.c
new file mode 100644 (file)
index 0000000..4565e42
--- /dev/null
@@ -0,0 +1,168 @@
+************************************************************************
+*   IRC - Internet Relay Chat, src/socketengine_devpoll.c
+*   Copyright (C) 2004 David Parton
+*
+* engine functions for the /dev/poll socket engine
+*
+*/
+
+/* $Id: socketengine_devpoll.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "fds.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/devpoll.h>
+#include <sys/poll.h>
+
+static int devpoll_id = -1, numfds = 0;
+
+void engine_init()
+{
+    devpoll_id = open("/dev/poll", O_RDWR);
+}
+
+void engine_add_fd(int fd)
+{
+    struct pollfd dev_fd;
+
+    if (numfds >= MAXCONNECTIONS)
+        abort();
+
+    dev_fd.events = 0;
+    dev_fd.revents = 0;
+    dev_fd.fd = fd;
+    if (write(devpoll_id, &dev_fd, sizeof(struct pollfd)) != sizeof(struct pollfd))
+        abort();
+
+    set_fd_internal(fd, 0);
+    ++numfds;
+}
+
+void engine_del_fd(int fd)
+{
+    struct pollfd dev_fd;
+
+    dev_fd.events = POLLREMOVE;
+    dev_fd.revents = 0;
+    dev_fd.fd = fd;
+    if (write(devpoll_id, &dev_fd, sizeof(struct pollfd)) != sizeof(struct pollfd))
+        abort();
+
+    --numfds;
+}
+
+void engine_change_fd_state(int fd, unsigned int stateplus)
+{
+    unsigned int events = 0;
+    struct pollfd dev_fd;
+
+    if (stateplus & FDF_WANTWRITE) events |= POLLOUT;
+    if (stateplus & FDF_WANTREAD) events |= POLLIN|POLLHUP|POLLERR;
+
+    dev_fd.events = events;
+    dev_fd.revents = 0;
+    dev_fd.fd = fd;
+
+    if (write(devpoll_id, &dev_fd, sizeof(struct pollfd)) != sizeof(struct pollfd))
+        abort();
+
+    set_fd_internal(fd, (void*)events);
+}
+
+#define ENGINE_MAX_EVENTS 512
+#define ENGINE_MAX_LOOPS (2 * (MAXCONNECTIONS / 512))
+
+int engine_read_message(time_t delay)
+{
+    struct pollfd events[ENGINE_MAX_EVENTS], *pevent;
+    struct dvpoll dopoll;
+    int nfds, i, numloops = 0, eventsfull;
+    unsigned int fdflags, fdevents;
+    int          fdtype;
+    void         *fdvalue;
+    aClient      *cptr;   
+    aListener    *lptr;
+
+    dopoll.dp_fds = events;
+    dopoll.dp_nfds = ENGINE_MAX_EVENTS;
+    dopoll.dp_timeout = delay;
+    do
+    {
+        nfds = ioctl(devpoll_id, DP_POLL, &dopoll);
+
+        if (nfds < 0)
+        {
+            if (errno == EINTR || errno == EAGAIN)
+                return -1; 
+
+            report_error("ioctl(devpoll): %s:%s", &me);
+            sleep(5);
+            return -1;
+        }
+        eventsfull = nfds == ENGINE_MAX_EVENTS;
+
+        if (delay || numloops) 
+            NOW = timeofday = time(NULL);
+        numloops++;
+
+        for (i = 0, pevent = events; i < nfds; i++, pevent++)
+        {
+            fdevents = (unsigned int)get_fd_internal(pevent->fd);
+            if (pevent->fd != -1)
+            {
+                int rr = (pevent->revents & (POLLIN|POLLHUP|POLLERR)) && (fdevents & (POLLIN|POLLHUP|POLLERR));
+                int rw = (pevent->revents & POLLOUT) && (fdevents & POLLOUT);
+
+                get_fd_info(pevent->fd, &fdtype, &fdflags, &fdvalue);
+
+                switch (fdtype)
+                {
+                case FDT_NONE:
+                    break;
+
+                case FDT_AUTH:
+                    cptr = (aClient*)fdvalue;
+                    if (rr) read_authports(cptr);
+                    if (rw && cptr->authfd >= 0) send_authports(cptr);
+                    check_client_fd(cptr);
+                    break;
+
+                case FDT_LISTENER:
+                    lptr = (aListener*)fdvalue;
+                    if (rr) accept_connection(lptr);
+                    break;
+
+                case FDT_RESOLVER:
+                    do_dns_async();
+                    break;
+
+                case FDT_CLIENT:
+                    cptr = (aClient*)fdvalue;
+                    readwrite_client(cptr, rr, rw);
+                    break;
+
+                case FDT_CALLBACKP:
+                    {
+                        struct fd_callbackp *fdcb = (struct fd_callbackp*)fdvalue;
+
+                        fdcb->rdf = rr;
+                        fdcb->wrf = rw;
+                        (*fdcb->callback)(fdcb);
+                        break;
+                    }
+
+                default:
+                    abort();
+                }
+            }
+        }
+    } while (eventsfull && numloops < ENGINE_MAX_LOOPS);
+
+    return 0;
+}
\ No newline at end of file
diff --git a/src/socketengine_epoll.c b/src/socketengine_epoll.c
new file mode 100644 (file)
index 0000000..5b3d5bc
--- /dev/null
@@ -0,0 +1,192 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/socketengine_epoll.c
+ *   Copyright (C) 2004 David Parton
+ *
+ * engine functions for the /dev/epoll socket engine
+ *
+ */
+ /* $Id: socketengine_epoll.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "fds.h"
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/epoll.h>
+
+#ifdef NEED_EPOLL_DEFS
+#include <asm/unistd.h>
+
+_syscall1(int, epoll_create, int, size)
+_syscall4(int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event*, event)
+_syscall4(int, epoll_wait, int, epfd, struct epoll_event*, pevents, int, maxevents, int, timeout)
+
+#endif
+
+static int epoll_id = -1, numfds = 0;
+static struct epoll_fd
+{
+    int       fd;
+    unsigned int events;
+} epoll_fds[MAXCONNECTIONS]; 
+
+
+void engine_init()
+{
+    epoll_id = epoll_create(MAXCONNECTIONS);
+    memset(epoll_fds, 0, sizeof(epoll_fds));
+}
+
+void engine_add_fd(int fd)
+{
+    struct epoll_event ev;
+    
+    if (numfds >= MAXCONNECTIONS)
+        abort();
+    
+    ev.events = 0;
+    ev.data.ptr = &epoll_fds[numfds];
+    if (epoll_ctl(epoll_id, EPOLL_CTL_ADD, fd, &ev) < 0)
+        abort();
+    
+    epoll_fds[numfds].fd = fd;
+    epoll_fds[numfds].events = 0;
+    set_fd_internal(fd, (void*)&epoll_fds[numfds]);
+    ++numfds;
+}
+
+void engine_del_fd(int fd)
+{
+    struct epoll_event ev;
+    struct epoll_fd    *epfd = (struct epoll_fd*)get_fd_internal(fd);
+    
+    if (epoll_ctl(epoll_id, EPOLL_CTL_DEL, fd, &ev) < 0)
+        abort();
+        
+    if (epfd - epoll_fds != numfds - 1)
+    {
+        *epfd = epoll_fds[numfds-1];
+        set_fd_internal(epfd->fd, (void*)epfd);
+        
+        /* update the epoll internal pointer as well */
+        ev.events = epfd->events;
+        ev.data.ptr = epfd;
+        if (epoll_ctl(epoll_id, EPOLL_CTL_MOD, epfd->fd, &ev) < 0)
+            abort();
+    }
+    
+    --numfds;
+}
+
+void engine_change_fd_state(int fd, unsigned int stateplus)
+{
+    struct epoll_event ev;
+    struct epoll_fd *epfd = (struct epoll_fd*)get_fd_internal(fd);
+    
+    ev.events = 0;
+    ev.data.ptr = epfd;
+    if (stateplus & FDF_WANTWRITE) ev.events |= EPOLLOUT;
+    if (stateplus & FDF_WANTREAD) ev.events |= EPOLLIN|EPOLLHUP|EPOLLERR;
+    
+    if (ev.events != epfd->events)
+    {
+        epfd->events = ev.events;
+        if (epoll_ctl(epoll_id, EPOLL_CTL_MOD, fd, &ev) < 0)
+            abort();
+    }
+}
+
+#define ENGINE_MAX_EVENTS 512
+#define ENGINE_MAX_LOOPS (2 * (MAXCONNECTIONS / 512))
+
+int engine_read_message(time_t delay)
+{
+    struct epoll_event events[ENGINE_MAX_EVENTS], *pevent;
+    struct epoll_fd* epfd;
+    int nfds, i, numloops = 0, eventsfull;
+    unsigned int fdflags;
+    int          fdtype;
+    void         *fdvalue;
+    aClient      *cptr;
+    aListener    *lptr;
+    
+    do
+    {
+        nfds = epoll_wait(epoll_id, events, ENGINE_MAX_EVENTS, delay * 1000);
+        
+        if (nfds == -1)
+        {
+            if (errno == EINTR || errno == EAGAIN)
+                return -1;
+                
+            report_error("epoll_wait: %s:%s", &me);
+            sleep(5);
+            return -1;
+        }
+        eventsfull = nfds == ENGINE_MAX_EVENTS;
+        
+        if (delay || numloops)
+            NOW = timeofday = time(NULL);
+        numloops++;
+        
+        for (i = 0, pevent = events; i < nfds; i++, pevent++)
+        {
+            epfd = pevent->data.ptr;
+            if (epfd->fd != -1)
+            {
+                int rr = (epfd->events & pevent->events) & (EPOLLIN|EPOLLHUP|EPOLLERR);
+                int rw = (epfd->events & pevent->events) & EPOLLOUT;
+                
+                get_fd_info(epfd->fd, &fdtype, &fdflags, &fdvalue);
+                
+                switch (fdtype)
+                {
+                case FDT_NONE:
+                    break;
+                    
+                case FDT_AUTH:
+                    cptr = (aClient*)fdvalue;
+                    if (rr) read_authports(cptr);
+                    if (rw && cptr->authfd >= 0) send_authports(cptr);  
+                    check_client_fd(cptr);
+                    break;
+                    
+                case FDT_LISTENER:
+                    lptr = (aListener*)fdvalue;
+                    if (rr) accept_connection(lptr);
+                    break;
+                    
+                case FDT_RESOLVER:
+                    do_dns_async();
+                    break;
+                    
+                case FDT_CLIENT:
+                    cptr = (aClient*)fdvalue;
+                    readwrite_client(cptr, rr, rw);
+                    break;
+                    
+                case FDT_CALLBACKP:
+                    {
+                        struct fd_callbackp *fdcb = (struct fd_callbackp*)fdvalue;
+                        
+                        fdcb->rdf = rr;
+                        fdcb->wrf = rw;
+                        (*fdcb->callback)(fdcb);
+                        break;
+                    }
+                    
+                default:
+                    abort();
+                }
+            }
+        }
+    } while (eventsfull && numloops < ENGINE_MAX_LOOPS);
+    
+    return 0;
+}
+
diff --git a/src/socketengine_kqueue.c b/src/socketengine_kqueue.c
new file mode 100644 (file)
index 0000000..f22ffe7
--- /dev/null
@@ -0,0 +1,289 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/socketengine_kqueue.c
+ *   Copyright (C) 2003 Lucas Madar
+ *
+ * engine functions for the kqueue() socket engine
+ *
+ */
+
+/* $Id: socketengine_kqueue.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "fds.h"
+
+#include <sys/event.h>
+#include <sys/time.h>
+
+#define MAX_EVENT_QUEUE 64
+
+static int kqueue_id = -1;
+static struct kevent eventQs[2][MAX_EVENT_QUEUE+1];
+static struct kevent *eventQ = eventQs[0];
+static int eventQi = 0;
+static int numEvents = 0;
+
+static void 
+kevent_add(struct kevent *e)
+{
+    if(kqueue_id == -1)
+        abort();
+
+    if(numEvents >= MAX_EVENT_QUEUE)
+    {
+        if(kevent(kqueue_id, eventQ, numEvents, NULL, 0, NULL) < 0)
+            sendto_realops_lev(DEBUG_LEV, "kevent() returned error: %s", 
+                               strerror(errno));
+        numEvents = 0;
+    }
+    memcpy(&eventQ[numEvents++], e, sizeof(struct kevent));
+}
+
+void 
+engine_init()
+{
+    kqueue_id = kqueue();
+    numEvents = 0;
+}
+
+void 
+engine_add_fd(int fd)
+{
+    struct kevent e;
+
+    e.ident = fd;
+    e.filter = EVFILT_READ;
+    e.flags = EV_ADD|EV_DISABLE;
+    e.fflags = 0;
+    e.data = 0;
+    e.udata = NULL;
+    kevent_add(&e);
+
+    e.ident = fd;
+    e.filter = EVFILT_WRITE;
+    e.flags = EV_ADD|EV_DISABLE;
+    e.fflags = 0;
+    e.data = 0;
+    e.udata = NULL;
+    kevent_add(&e);
+
+    set_fd_internal(fd, 0);
+}
+
+void 
+engine_del_fd(int fd)
+{
+/* we dont accually need to do this, as a close() clears the kevent
+ * filters and automagically removes itself from the queue.
+ * With the way we handle kevent() calls in kevent_add(), accually
+ * running these EV_DELETE routines causes bad file descriptor returns
+ * due to the fact that they could be close()'d before the kevent() is
+ * run.  --epi
+ */
+
+/********
+
+   struct kevent e;
+
+   e.ident = fd;
+   e.filter = EVFILT_READ;
+   e.flags = EV_DELETE;
+   e.fflags = 0;
+   e.data = 0;
+   e.udata = NULL;
+   kevent_add(&e);
+
+   e.ident = fd;
+   e.filter = EVFILT_WRITE;
+   e.flags = EV_DELETE;
+   e.fflags = 0;
+   e.data = 0;
+   e.udata = NULL;
+   kevent_add(&e);
+
+********/
+
+    /* But we should remove this fd from the change queue -- if it was closed
+     * and we have a change pending, kevent() will fail later.  What's worse
+     * is that when the queue is flushed due to being full, a kevent() failure
+     * may leave some changes unprocessed.  Reordering the change queue is not
+     * safe, hence the gymnastics below.    -Quension
+     */
+    int i, j;
+
+    if (!numEvents)
+        return;
+
+    /* optimal case: fd isn't in the change queue */
+    for (i = 0; i < numEvents; i++)
+        if (eventQ[i].ident == fd)
+            break;
+
+    /* second optimal case: fd is last, truncate the queue */
+    if (i == numEvents - 1)
+        numEvents--;
+
+    if (i == numEvents)
+        return;
+
+    /* swap array index, copy all fds before this one */
+    eventQi ^= 1;
+    memcpy(eventQs[eventQi], eventQ, sizeof(struct kevent) * i);
+
+    /* selectively copy remaining fds, skip bad one */
+    for (j = i++; i < numEvents; i++)
+        if (eventQ[i].ident != fd)
+            memcpy(&eventQs[eventQi][j++], &eventQ[i], sizeof(struct kevent));
+
+    /* swap active array */
+    numEvents = j;
+    eventQ = eventQs[eventQi];
+}
+
+void 
+engine_change_fd_state(int fd, unsigned int stateplus)
+{
+    unsigned int oldflags = (unsigned int) get_fd_internal(fd);
+    struct kevent e;
+
+    /* Something changed with our read state? */
+    if((oldflags ^ stateplus) & FDF_WANTREAD)
+    {
+        e.ident = fd;
+        e.filter = EVFILT_READ;
+        e.flags = EV_ADD|((stateplus & FDF_WANTREAD) ? EV_ENABLE : EV_DISABLE);
+        e.fflags = 0;
+        e.data = 0;
+        e.udata = 0;
+        kevent_add(&e);
+    }
+
+    /* Something changed with our write state? */
+    if((oldflags ^ stateplus) & FDF_WANTWRITE)
+    {
+        e.ident = fd;
+        e.filter = EVFILT_WRITE;
+        e.flags = EV_ADD|((stateplus & FDF_WANTWRITE) ? EV_ENABLE : EV_DISABLE);
+        e.fflags = 0;
+        e.data = 0;
+        e.udata = 0;
+        kevent_add(&e);
+    }
+
+    set_fd_internal(fd, (void *) stateplus);
+}
+
+#define ENGINE_MAX_EVENTS 512
+#define ENGINE_MAX_LOOPS (2 * (MAXCONNECTIONS / 512))
+
+int 
+engine_read_message(time_t delay)
+{
+    static struct kevent events[ENGINE_MAX_EVENTS];
+
+    int nevs, length, i, numloops, eventsfull;
+    unsigned int fdflags;
+    int fdtype;
+    void *fdvalue;
+    aClient *cptr;
+    aListener *lptr;
+    struct timespec wait;
+
+    numloops = 0;
+    wait.tv_sec = delay;
+    wait.tv_nsec = 0;
+
+    do
+    {
+        nevs = kevent(kqueue_id, eventQ, numEvents, events, 
+                      ENGINE_MAX_EVENTS, &wait);
+        numEvents = 0;
+
+        if(nevs == 0)
+            return 0;
+
+        if (nevs < 0)
+        {
+            if((errno == EINTR) || (errno == EAGAIN))
+                return -1;
+
+            report_error("kevent %s:%s", &me);
+            sleep(5);
+            return -1;
+        }
+
+        eventsfull = (nevs == ENGINE_MAX_EVENTS) ? 1 : 0;
+        if(delay || numloops)
+            NOW = timeofday = time(NULL);
+        numloops++;
+      
+        for(i = 0; i < nevs; i++)
+        {
+            int rr = 0, rw = 0;
+
+            if(events[i].flags & EV_ERROR)
+            {
+                errno = events[i].data;
+                /* this should be handled later i suppose */
+                continue;
+            }
+
+            get_fd_info(events[i].ident, &fdtype, &fdflags, &fdvalue);
+
+            if(events[i].filter == EVFILT_READ)
+                rr = 1;
+            else if(events[i].filter == EVFILT_WRITE)
+                rw = 1;
+
+            cptr = NULL;
+            length = -1;
+
+            switch(fdtype)
+            {
+                case FDT_NONE:
+                    break;
+
+                case FDT_AUTH:
+                    cptr = (aClient *) fdvalue;
+                    if (rr)
+                        read_authports(cptr);
+                    else if (rw && cptr->authfd >= 0)
+                        send_authports(cptr);
+                    check_client_fd(cptr);
+                    break;
+
+                case FDT_LISTENER:
+                    lptr = (aListener *) fdvalue;
+                    if(rr)
+                        accept_connection(lptr);
+                    break;
+
+                case FDT_RESOLVER:
+                    do_dns_async();
+                    break;
+
+                case FDT_CLIENT:
+                    cptr = (aClient *) fdvalue;
+                    readwrite_client(cptr, rr, rw);
+                    break;
+
+                case FDT_CALLBACKP:
+                {
+                    struct fd_callbackp *fdcb = (struct fd_callbackp *) fdvalue;
+
+                    fdcb->rdf = rr;
+                    fdcb->wrf = rw;
+                    (*fdcb->callback)(fdcb);
+                }
+                break;
+
+                default:
+                    abort(); /* unknown client type? bail! */
+            }
+        }
+    } while(eventsfull && (numloops < ENGINE_MAX_LOOPS));
+
+    return 0;
+}
diff --git a/src/socketengine_poll.c b/src/socketengine_poll.c
new file mode 100644 (file)
index 0000000..4dff01d
--- /dev/null
@@ -0,0 +1,169 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/socketengine_poll.c
+ *   Copyright (C) 2003 Lucas Madar
+ *
+ * engine functions for the poll() socket engine
+ *
+ */
+
+/* $Id: socketengine_poll.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "fds.h"
+
+#include <sys/poll.h>
+
+struct pollfd poll_fds[MAXCONNECTIONS];
+int last_pfd = -1;
+
+void engine_init()
+{
+}
+
+void engine_add_fd(int fd)
+{
+   struct pollfd *pfd = &poll_fds[++last_pfd];
+
+   /* sanity check */
+   if(last_pfd >= MAXCONNECTIONS)
+      abort();
+
+   set_fd_internal(fd, (void *) last_pfd);
+
+   pfd->fd = fd;
+   pfd->events = 0;
+   pfd->revents = 0;
+}
+
+void engine_del_fd(int fd)
+{
+   int arrayidx = (int) get_fd_internal(fd);
+
+   /* If it's at the end of the array, just chop it off */
+   if(arrayidx == last_pfd)
+   {
+      fdfprintf(stderr, "Removing %d[%d] from end of pollfds\n", last_pfd, fd);
+      last_pfd--;
+      return;
+   }
+
+   /* Otherwise, move the last array member to where the old one was */
+   fdfprintf(stderr, "Moving pfd %d[%d] to vacated spot %d[%d] -- now %d[%d]\n", 
+             last_pfd, poll_fds[last_pfd].fd, arrayidx, fd, last_pfd, fd);
+   memcpy(&poll_fds[arrayidx], &poll_fds[last_pfd], sizeof(struct pollfd));
+   last_pfd--;
+   set_fd_internal(poll_fds[arrayidx].fd, (void *) arrayidx);
+}
+
+void engine_change_fd_state(int fd, unsigned int stateplus)
+{
+   int arrayidx = (int) get_fd_internal(fd);
+   struct pollfd *pfd = &poll_fds[arrayidx];
+
+   pfd->events = 0;
+   if(stateplus & FDF_WANTREAD)
+      pfd->events |= POLLIN|POLLHUP|POLLERR;
+   if(stateplus & FDF_WANTWRITE)
+      pfd->events |= POLLOUT;
+}
+
+void engine_get_pollfds(struct pollfd **pfds, int *numpfds)
+{
+   *pfds = poll_fds;
+   *numpfds = (last_pfd + 1);
+}
+
+int engine_read_message(time_t delay)
+{
+   static struct pollfd poll_fdarray[MAXCONNECTIONS];
+
+   struct pollfd *pfd;
+   int nfds, nbr_pfds, length, i;
+   unsigned int fdflags;
+   int fdtype;
+   void *fdvalue;
+   aClient *cptr;
+   aListener *lptr;
+
+   engine_get_pollfds(&pfd, &nbr_pfds);
+   memcpy(poll_fdarray, pfd, sizeof(struct pollfd) * nbr_pfds);
+
+   nfds = poll(poll_fdarray, nbr_pfds, delay * 1000);
+   if (nfds == -1)
+   {
+      if(((errno == EINTR) || (errno == EAGAIN)))
+         return -1;
+      report_error("poll %s:%s", &me);
+      sleep(5);
+      return -1;
+   }
+
+   if(delay)
+      NOW = timeofday = time(NULL);
+
+   for (pfd = poll_fdarray, i = 0; nfds && (i < nbr_pfds); i++, pfd++) 
+   {
+      get_fd_info(pfd->fd, &fdtype, &fdflags, &fdvalue);
+
+      cptr = NULL;
+      length = -1;
+
+      if (nfds && pfd->revents)
+      {
+         int rr = pfd->revents & (POLLIN|POLLHUP|POLLERR);
+         int rw = pfd->revents & (POLLOUT);
+
+         fdfprintf(stderr, "fd %d: %s%s\n", pfd->fd, rr ? "read " : "", rw ? "write" : "");
+
+         nfds--;
+
+         switch(fdtype)
+         {
+            case FDT_NONE:
+               break;
+
+            case FDT_AUTH:
+               cptr = (aClient *) fdvalue;
+               if (rr)
+                  read_authports(cptr);
+               if (rw && cptr->authfd >= 0)
+                  send_authports(cptr);
+               check_client_fd(cptr);
+               break;
+
+            case FDT_LISTENER:
+               lptr = (aListener *) fdvalue;
+               if(rr)
+                  accept_connection(lptr);
+               break;
+
+            case FDT_RESOLVER:
+               do_dns_async();
+               break;
+
+            case FDT_CLIENT:
+               cptr = (aClient *) fdvalue;
+               readwrite_client(cptr, rr, rw);
+               break;
+
+            case FDT_CALLBACKP:
+               {
+                  struct fd_callbackp *fdcb = (struct fd_callbackp *) fdvalue;
+
+                  fdcb->rdf = rr;
+                  fdcb->wrf = rw;
+                  (*fdcb->callback)(fdcb);
+               }
+               break;
+
+            default:
+               abort(); /* unknown client type? bail! */
+         }
+      }
+   } /* end of for() loop for testing polled sockets */
+
+   return 0;
+}
diff --git a/src/socketengine_select.c b/src/socketengine_select.c
new file mode 100644 (file)
index 0000000..1d63c6e
--- /dev/null
@@ -0,0 +1,169 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/socketengine_select.c
+ *   Copyright (C) 2003 Lucas Madar
+ *
+ * engine functions for the select() socket engine
+ *
+ */
+
+/* $Id: socketengine_select.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "fds.h"
+
+static fd_set g_read_set, g_write_set;
+
+void engine_init()
+{
+   FD_ZERO(&g_read_set);
+   FD_ZERO(&g_write_set);
+}
+
+void engine_add_fd(int fd)
+{
+   set_fd_internal(fd, (void *) 0);
+}
+
+void engine_del_fd(int fd)
+{
+   FD_CLR(fd, &g_read_set);
+   FD_CLR(fd, &g_write_set);
+}
+
+void engine_change_fd_state(int fd, unsigned int stateplus)
+{
+   int prevstate = (int) get_fd_internal(fd);
+
+   if((stateplus & FDF_WANTREAD) && !(prevstate & FDF_WANTREAD))
+   { 
+      FD_SET(fd, &g_read_set);
+      prevstate |= FDF_WANTREAD;
+   }
+   else if(!(stateplus & FDF_WANTREAD) && (prevstate & FDF_WANTREAD))
+   {
+      FD_CLR(fd, &g_read_set);
+      prevstate &= ~(FDF_WANTREAD);
+   }
+
+   if((stateplus & FDF_WANTWRITE) && !(prevstate & FDF_WANTWRITE))
+   {
+      FD_SET(fd, &g_write_set);
+      prevstate |= FDF_WANTWRITE;
+   }
+   else if(!(stateplus & FDF_WANTWRITE) && (prevstate & FDF_WANTWRITE))
+   {
+      FD_CLR(fd, &g_write_set);
+      prevstate &= ~(FDF_WANTWRITE);
+   }
+
+   set_fd_internal(fd, (void *) prevstate);
+}
+
+static void engine_get_fdsets(fd_set *r, fd_set *w)
+{
+   memcpy(r, &g_read_set, sizeof(fd_set));
+   memcpy(w, &g_write_set, sizeof(fd_set));
+}
+
+int engine_read_message(time_t delay)
+{
+   fd_set read_set, write_set;
+   struct timeval wt;   
+   int nfds, length, i;
+   unsigned int fdflags;
+   int fdtype;
+   void *fdvalue;
+   aClient *cptr;
+   aListener *lptr;
+
+   engine_get_fdsets(&read_set, &write_set);
+
+   wt.tv_sec = delay;
+   wt.tv_usec = 0;
+
+   nfds = select(MAXCONNECTIONS, &read_set, &write_set, NULL, &wt);
+   if (nfds == -1)
+   {
+      if(((errno == EINTR) || (errno == EAGAIN)))
+         return -1;
+      report_error("select %s:%s", &me);
+      sleep(5);
+      return -1;
+   }
+   else if (nfds == 0)
+      return 0;
+
+   if(delay)
+      NOW = timeofday = time(NULL);
+
+   for (i = 0; i < MAXCONNECTIONS; i++) 
+   {
+      get_fd_info(i, &fdtype, &fdflags, &fdvalue);
+
+      cptr = NULL;
+      length = -1;
+
+      if (nfds)
+      {
+         int rr = FD_ISSET(i, &read_set);
+         int rw = FD_ISSET(i, &write_set);
+
+         if(rr || rw)
+            nfds--;
+         else
+            continue;
+
+         fdfprintf(stderr, "fd %d: %s%s\n", i, rr ? "read " : "", rw ? "write" : "");
+
+         switch(fdtype)
+         {
+            case FDT_NONE:
+               break;
+
+            case FDT_AUTH:
+               cptr = (aClient *) fdvalue;
+               if (rr)
+                  read_authports(cptr);
+               if (rw && cptr->authfd >= 0)
+                  send_authports(cptr);
+               check_client_fd(cptr);
+               break;
+
+            case FDT_LISTENER:
+               lptr = (aListener *) fdvalue;
+               if(rr)
+                  accept_connection(lptr);
+               break;
+
+            case FDT_RESOLVER:
+               do_dns_async();
+               break;
+
+            case FDT_CLIENT:
+               cptr = (aClient *) fdvalue;
+               readwrite_client(cptr, rr, rw);
+               break;
+
+            case FDT_CALLBACKP:
+               {
+                  struct fd_callbackp *fdcb = (struct fd_callbackp *) fdvalue;
+
+                  fdcb->rdf = rr;
+                  fdcb->wrf = rw;
+                  (*fdcb->callback)(fdcb);
+               }
+               break;
+
+            default:
+               abort(); /* unknown client type? bail! */
+         }
+      }
+      else
+         break; /* no more fds? break out of the loop */
+   } /* end of for() loop for testing selected sockets */
+
+   return 0;
+}
diff --git a/src/struct.c b/src/struct.c
new file mode 100644 (file)
index 0000000..ccca71a
--- /dev/null
@@ -0,0 +1,496 @@
+/* src/struct.c
+ * Copyright(c) 2003, Aaron Wiebe
+ * Bahamut development team
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: struct.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+/* This file contains quick and dirty functions for retriving information
+ * from structures. */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "channel.h"
+#include "throttle.h"
+#include "sbuf.h"
+
+#include "structfunc.h"     /* here for compiler prototype checks */
+
+aClient *ac_next(aClient *cptr)
+{
+       return cptr->next;
+}
+
+aClient *ac_prev(aClient *cptr)
+{
+       return cptr->prev;
+}
+
+anUser *ac_user(aClient *cptr)
+{
+       return cptr->user;
+}
+
+aServer *ac_server(aClient *cptr)
+{
+       return cptr->serv;
+}
+
+aWhowas *ac_whowas(aClient *cptr)
+{
+       return cptr->whowas;
+}
+
+aClient *ac_from(aClient *cptr)
+{
+       return cptr->from;
+}
+
+aClient *ac_uplink(aClient *cptr)
+{
+       return cptr->uplink;
+}
+
+time_t ac_lasttime(aClient *cptr)
+{
+       return cptr->lasttime;
+}
+
+time_t ac_firsttime(aClient *cptr)
+{
+       return cptr->firsttime;
+}
+
+time_t ac_since(aClient *cptr)
+{
+       return cptr->since;
+}
+
+ts_val ac_tsinfo(aClient *cptr)
+{
+       return cptr->tsinfo;
+}
+
+int ac_fd(aClient *cptr)
+{
+       return cptr->fd;
+}
+
+int ac_hopcount(aClient *cptr)
+{
+       return cptr->hopcount;
+}
+
+short ac_status(aClient *cptr)
+{
+       return cptr->status;
+}
+
+char ac_nicksent(aClient *cptr)
+{
+       return cptr->nicksent;
+}
+
+char *ac_name(aClient *cptr)
+{
+       return cptr->name;
+}
+
+char *ac_info(aClient *cptr)
+{
+       return cptr->info;
+}
+
+#ifdef FLUD
+Link *ac_fludees(aClient *cptr)
+{
+       return cptr->fludees;
+}
+#endif
+
+struct in_addr ac_ip(aClient *cptr)
+{
+       return cptr->ip;
+}
+
+char *ac_hostip(aClient *cptr)
+{
+       return cptr->hostip;
+}
+
+Link *ac_watch(aClient *cptr)
+{
+       return cptr->watch;
+}
+
+int ac_watches(aClient *cptr)
+{
+       return cptr->watches;
+}
+
+/*************************************
+ * local only stuff starts here 
+ *************************************/
+
+int ac_count(aClient *cptr)
+{
+       if(cptr->fd == -1)
+               abort();
+       return cptr->count;
+}
+
+#ifdef FLUD
+time_t ac_fludblock(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->fludblock;
+}
+
+struct fludbot *ac_fluders(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->fluders;
+}
+#endif
+#ifdef ANTI_SPAMBOT
+time_t ac_last_join_time(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->last_join_time;
+}
+
+time_t ac_last_leave_time(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->last_leave_time;
+}
+
+int ac_join_leave_count(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->join_leave_count;
+}
+
+int ac_oper_warn_count_down(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->oper_warn_count_down;
+}
+#endif
+
+char *ac_buffer(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->buffer;
+}
+
+short ac_lastsq(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->lastsq;
+}
+
+SBuf *ac_sendQ(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return &cptr->sendQ;
+}
+
+SBuf *ac_recvQ(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return &cptr->recvQ;
+}
+
+long ac_sendM(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->sendM;
+}
+
+long ac_sendK(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->sendK;
+}
+
+long ac_receiveM(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->receiveM;
+}
+
+long ac_receiveK(aClient *cptr)
+{
+       if(cptr->fd == -1)
+               abort();
+       return cptr->receiveK;
+}
+
+u_short ac_sendB(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->sendB;
+}
+
+u_short ac_receiveB(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->receiveB;
+}
+
+long ac_lastrecvM(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->lastrecvM;
+}
+
+int ac_priority(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->priority;
+}
+
+aListener *ac_lstn(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->lstn;
+}
+
+int ac_authfd(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->authfd;
+}
+
+char *ac_username(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->username;
+}
+
+unsigned short ac_port(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->port;
+}
+
+struct hostent *ac_hostp(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->hostp;
+}
+
+#ifdef ANTI_NICK_FLOOD
+time_t ac_last_nick_change(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->last_nick_change;
+}
+
+int ac_number_of_nick_changes(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->number_of_nick_changes;
+}
+#endif
+#ifdef NO_AWAY_FLUD
+time_t ac_alas(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->alas;
+}
+
+int ac_acount(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->acount;
+}
+
+#endif
+
+char *ac_sockhost(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->sockhost;
+}
+
+char *ac_passwd(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->passwd;
+}
+
+int ac_oflag(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->oflag;
+}
+
+int ac_sockerr(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->sockerr;
+}
+
+int ac_capabilities(aClient *cptr)
+{
+        if(cptr->fd == -1)
+                abort();
+        return cptr->capabilities;
+}
+
+/* channel functions */
+
+aChannel *ch_next(aChannel *chptr)
+{
+    return chptr->nextch;
+}
+
+aChannel *ch_prev(aChannel *chptr)
+{
+    return chptr->prevch;
+}
+
+aChannel *ch_hnext(aChannel *chptr)
+{
+    return chptr->hnextch;
+}
+
+int ch_hashv(aChannel *chptr)
+{
+    return chptr->hashv;
+}
+
+Mode ch_mode(aChannel *chptr)
+{
+    return chptr->mode;
+}
+
+char *ch_topic(aChannel *chptr)
+{
+    return chptr->topic;
+}
+
+char *ch_topic_nick(aChannel *chptr)
+{
+    return chptr->topic_nick;
+}
+
+time_t ch_topic_time(aChannel *chptr)
+{
+    return chptr->topic_time;
+}
+
+int ch_users(aChannel *chptr)
+{
+    return chptr->users;
+}
+
+chanMember *ch_members(aChannel *chptr)
+{
+    return chptr->members;
+}
+
+Link *ch_invites(aChannel *chptr)
+{
+    return chptr->invites;
+}
+
+aBan *ch_banlist(aChannel *chptr)
+{
+    return chptr->banlist;
+}
+
+#ifdef INVITE_LISTS
+anInvite *ch_invite_list(aChannel *chptr)
+{
+    return chptr->invite_list;
+}
+#endif
+
+#ifdef EXEMPT_LISTS
+aBanExempt *ch_banexempt_list(aChannel *chptr)
+{
+    return chptr->banexempt_list;
+}
+#endif
+
+ts_val ch_channelts(aChannel *chptr)
+{
+    return chptr->channelts;
+}
+
+#ifdef FLUD
+time_t ch_fludblock(aChannel *chptr)
+{
+    return chptr->fludblock;
+}
+
+struct fludbot *ch_fluders(aChannel *chptr)
+{
+    return chptr->fluders;
+}
+#endif
+
+char *ch_chname(aChannel *chptr)
+{
+    return chptr->chname;
+}
+
+int ch_join_start(aChannel *chptr)
+{
+    return chptr->join_start;
+}
+
+int ch_join_count(aChannel *chptr)
+{
+    return chptr->join_count;
+}
diff --git a/src/support.c b/src/support.c
new file mode 100644 (file)
index 0000000..5800c11
--- /dev/null
@@ -0,0 +1,291 @@
+
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/support.c
+ *   Copyright (C) 1990, 1991 Armin Gruner
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: support.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+
+#define FOREVER for(;;)
+
+extern int  errno;             /*
+                                * ...seems that errno.h doesn't define this
+                                * * everywhere 
+                                */
+extern void outofmemory();
+
+#if !defined( HAVE_STRTOKEN )
+/*
+ * *  strtoken.c --   walk through a string of tokens, using a set
+ * of separators 
+ * argv 9/90
+ * 
+ *      $Id: support.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $
+ */
+
+char *strtoken(char **save, char *str, char *fs)
+{
+    char       *pos = *save;   /* keep last position across calls */
+    char   *tmp;
+    
+    if (str)
+       pos = str;              /* new string scan */
+    
+    while (pos && *pos && strchr(fs, *pos) != NULL)
+       pos++;                  /* skip leading separators */
+    
+    if (!pos || !*pos)
+       return (pos = *save = NULL);    /* string contains only sep's */
+    
+    tmp = pos;                 /* now, keep position of the token */
+
+    while (*pos && strchr(fs, *pos) == NULL)
+       pos++;                  /* skip content of the token */
+
+    if (*pos)
+       *pos++ = '\0';          /* remove first sep after the token */
+    else
+       pos = NULL;             /* end of string */
+
+    *save = pos;
+    return (tmp);
+}
+#endif /* !HAVE_STRTOKEN */
+
+#if !defined( HAVE_STRTOK )
+/* NOT encouraged to use! */
+
+char *strtok(char *str, char *fs)
+{
+    static char *pos;
+    return strtoken(&pos, str, fs);
+}
+#endif /* !HAVE_STRTOK */
+
+#if !defined( HAVE_STRERROR )
+/*
+ * strerror - return an appropriate system error string to a given errno
+ * 
+ */
+
+char *strerror(int err_no)
+{
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+    extern char *sys_errlist[];        /* Sigh... hopefully on all systems */
+    extern int  sys_nerr;
+#endif
+    static char buff[40];
+    char       *errp;
+
+    errp = (err_no > sys_nerr ? (char *) NULL : sys_errlist[err_no]);
+
+    if (errp == (char *) NULL)
+    {
+       errp = buff;
+       (void) sprintf(errp, "Unknown Error %d", err_no);
+    }
+    return errp;
+}
+#endif /* !HAVE_STRERROR */
+
+/*
+ * inetntoa  --    changed name to remove collision possibility
+ * and so behaviour is gaurunteed to take a pointer arg.
+ *                       -avalon 23/11/92
+ * inet_ntoa -- returned the dotted notation of a given 
+ * internet number (some ULTRIX don't have this)
+ */
+
+char *inetntoa(char *in)
+{
+    static char buf[16];
+    u_char *s = (u_char *) in;
+    int     a, b, c, d;
+    
+    a = (int) *s++;
+    b = (int) *s++;
+    c = (int) *s++;
+    d = (int) *s++;
+    (void) ircsprintf(buf, "%d.%d.%d.%d", a, b, c, d);
+    
+    return buf;
+}
+
+#if !defined( HAVE_INET_NETOF )
+/* inet_netof --   return the net portion of an internet number */
+
+int inet_netof(struct in_addr in)
+{
+    int         addr = in.s_net;
+
+    if (addr & 0x80 == 0)
+       return ((int) in.s_net);
+
+    if (addr & 0x40 == 0)
+       return ((int) in.s_net * 256 + in.s_host);
+
+    return ((int) in.s_net * 256 + in.s_host * 256 + in.s_lh);
+}
+
+#endif /* !HAVE_INET_NETOF */
+
+void *MyMalloc(size_t x)
+{
+    void       *ret = malloc(x);
+
+    if (!ret)
+    {
+       outofmemory();
+    }
+    return ret;
+}
+
+void *MyRealloc(void *x, size_t y)
+{
+    void       *ret = realloc(x, y);
+
+    if (!ret)
+    {
+       outofmemory();
+    }
+    return ret;
+}
+
+/*
+ * read a string terminated by \r or \n in from a fd
+ * 
+ * Created: Sat Dec 12 06:29:58 EST 1992 by avalon 
+ * Returns: 
+ * 0 - EOF 
+ * -1 - error on read 
+ * >0 - number of bytes returned (<=num)
+ * After opening a fd, it is necessary to init dgets() by calling it as
+ * dgets(x,y,0); * to mark the buffer as being empty.
+ * 
+ * cleaned up by - Dianora aug 7 1997 *argh*
+ */
+int dgets(int fd, char *buf, int num)
+{
+    static char dgbuf[8192];
+    static char *head = dgbuf, *tail = dgbuf;
+    char *s, *t;
+    int n, nr;
+
+    /* Sanity checks. */
+    if (head == tail)
+       *head = '\0';
+
+    if (!num) 
+    {
+       head = tail = dgbuf;
+       *head = '\0';
+       return 0;
+    }
+
+    if (num > sizeof(dgbuf) - 1)
+       num = sizeof(dgbuf) - 1;
+
+    FOREVER
+    {
+       if (head > dgbuf)
+       {
+           for (nr = tail - head, s = head, t = dgbuf; nr > 0; nr--)
+               *t++ = *s++;
+           tail = t;
+           head = dgbuf;
+       }
+       /* check input buffer for EOL and if present return string. */
+       if (head < tail &&
+           ((s = strchr(head, '\n')) ||
+            (s = strchr(head, '\r'))) && s < tail)
+       {
+           n = MIN(s - head + 1, num); /* at least 1 byte */
+           memcpy(buf, head, n);
+           head += n;
+           if (head == tail)
+               head = tail = dgbuf;
+           return n;
+       }
+       
+       if (tail - head >= num) 
+       {      /* dgets buf is big enough */
+           n = num;
+           memcpy(buf, head, n);
+           head += n;
+           if (head == tail)
+               head = tail = dgbuf;
+           return n;
+       }
+       
+       n = sizeof(dgbuf) - (tail - dgbuf) - 1;
+       nr = read(fd, tail, n);
+       if (nr == -1)
+       {
+           head = tail = dgbuf;
+           return -1;
+       }
+       
+       if (!nr)
+       {
+           if (tail > head)
+           {
+               n = MIN(tail - head, num);
+               memcpy(buf, head, n);
+               head += n;
+               if (head == tail)
+                   head = tail = dgbuf;
+               return n;
+           }
+           head = tail = dgbuf;
+           return 0;
+       }
+       
+       tail += nr;
+       *tail = '\0';
+       
+       for (t = head; (s = strchr(t, '\n'));)
+       {
+           if ((s > head) && (s > dgbuf))
+           {
+               t = s - 1;
+               for (nr = 0; *t == '\\'; nr++)
+                   t--;
+               if (nr & 1)
+               {
+                   t = s + 1;
+                   s--;
+                   nr = tail - t;
+                   while (nr--)
+                       *s++ = *t++;
+                   tail -= 2;
+                   *tail = '\0';
+                   }
+               else
+                   s++;
+           }
+           else
+               s++;
+           t = s;
+       }
+       *tail = '\0';
+    }
+}
diff --git a/src/throttle.c b/src/throttle.c
new file mode 100644 (file)
index 0000000..c9d42d6
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2000, 2001 Chip Norkus
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The names of the maintainers, developers and contributors may not be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE MAINTAINER, DEVELOPERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE DEVELOPERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "res.h"
+#include "h.h"
+#include "numeric.h"
+#include "blalloc.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "queue.h"
+#include "throttle.h"
+
+BlockHeap *hashent_freelist;
+BlockHeap *throttle_freelist;
+
+/*******************************************************************************
+ * hash code here.  why isn't it in hash.c?  see the license. :)
+ ******************************************************************************/
+
+hashent *hashent_alloc()
+{
+   return BlockHeapALLOC(hashent_freelist, hashent);
+}
+
+void hashent_free(hashent *hp)
+{
+   BlockHeapFree(hashent_freelist, hp);
+}
+
+/* hash_table creation function.  given the user's paramters, allocate
+ * and empty a new hash table and return it. */
+hash_table *
+create_hash_table(int elems, size_t offset, size_t len, int flags, 
+                  int (*cmpfunc)(void *, void *)) 
+{
+    hash_table *htp = malloc(sizeof(hash_table));
+
+    htp->size = elems;
+    htp->keyoffset = offset;
+    htp->keylen = len;
+    htp->flags = flags;
+    htp->cmpfunc = cmpfunc;
+
+    htp->table = malloc(sizeof(hashent_list) * htp->size);
+    memset(htp->table, 0, sizeof(hashent_list) * htp->size);
+
+    return htp;
+}
+
+/* hash_table destroyer.  sweep through the given table and kill off every
+ * hashent */
+void 
+destroy_hash_table(hash_table *table) 
+{
+    hashent *hep;
+    int i;
+
+    for (i = 0;i < table->size;i++) 
+    {
+        while (!SLIST_EMPTY(&table->table[i])) 
+        {
+            hep = SLIST_FIRST(&table->table[i]);
+            SLIST_REMOVE_HEAD(&table->table[i], lp);
+            hashent_free(hep);
+        }
+    }
+    MyFree(table->table);
+    MyFree(table);
+}
+
+/* this is an expensive function.  it's not the sort of thing one should be
+ * calling a lot, however, in the right situations it can provide a lot of
+ * benefit */
+void 
+resize_hash_table(hash_table *table, int elems) 
+{
+    hashent_list *oldtable;
+    int oldsize, i;
+    hashent *hep;
+
+    /* preserve the old table, then create a new one.  */
+    oldtable = table->table;
+    oldsize = table->size;
+    table->size = elems;
+    table->table = malloc(sizeof(hashent_list) * table->size);
+    memset(table->table, 0, sizeof(hashent_list) * table->size);
+
+    /* now walk each bucket in the old table, pulling off individual entries
+     * and re-adding them to the table as we go */
+    for (i = 0;i < oldsize;i++) 
+    {
+        while (!SLIST_EMPTY(&oldtable[i])) 
+        {
+            hep = SLIST_FIRST(&oldtable[i]);
+            hash_insert(table, hep->ent);
+            SLIST_REMOVE_HEAD(&oldtable[i], lp);
+            hashent_free(hep);
+        }
+    }
+    MyFree(oldtable);
+}
+
+/* get the hash of a given key.  really only useful for insert/delete */
+unsigned int 
+hash_get_key_hash(hash_table *table, void *key, size_t offset) 
+{
+    char *rkey = (char *)key + offset;
+    int len = table->keylen;
+    unsigned int hash = 0;
+
+    if (!len)
+        len = strlen(rkey);
+    else if (table->flags & HASH_FL_STRING) 
+    {
+        len = strlen(rkey);
+        if (len > table->keylen)
+            len = table->keylen;
+    }
+    /* I borrowed this algorithm from perl5.  Kudos to Larry Wall & co. */
+    if (table->flags & HASH_FL_NOCASE)
+        while (len--)
+            hash = hash * 33 + ToLower(*rkey++);
+    else
+        while (len--)
+            hash = hash * 33 + *rkey++;
+
+    return hash % table->size;
+}
+
+/* add the given item onto the hash */
+int 
+hash_insert(hash_table *table, void *ent) 
+{
+    int hash = hash_get_key_hash(table, ent, table->keyoffset);
+    hashent *hep = hashent_alloc();
+
+    hep->ent = ent;
+    SLIST_INSERT_HEAD(&table->table[hash], hep, lp);
+
+    return 1;
+}
+
+/* delete the given item from the hash */
+int 
+hash_delete(hash_table *table, void *ent) 
+{
+    int hash = hash_get_key_hash(table, ent, table->keyoffset);
+    hashent *hep;
+
+    SLIST_FOREACH(hep, &table->table[hash], lp) 
+    {
+        if (hep->ent == ent)
+            break;
+    }
+    if (hep == NULL)
+        return 0;
+    SLIST_REMOVE(&table->table[hash], hep, hashent_t, lp);
+    hashent_free(hep);
+    return 1;
+}
+
+/* last, but not least, the find function.  given the table and the key to
+ * look for, it hashes the key, and then calls the compare function in the
+ * given table slice until it finds the item, or reaches the end of the
+ * list. */
+void *
+hash_find(hash_table *table, void *key) 
+{
+    int hash = hash_get_key_hash(table, key, 0);
+    hashent *hep;
+
+    SLIST_FOREACH(hep, &table->table[hash], lp) 
+    {
+        if (!table->cmpfunc(&((char *)hep->ent)[table->keyoffset], key))
+            return hep->ent;
+    }
+
+    return NULL; /* not found */
+}
+
+/*******************************************************************************
+ * actual throttle code here ;)
+ ******************************************************************************/
+
+LIST_HEAD(throttle_list_t, throttle_t) throttles;
+
+typedef struct throttle_t 
+{
+    char    addr[HOSTIPLEN + 1];    /* address of the throttle */
+    int     conns;                  /* number of connections seen from this
+                                       address. */
+    time_t  first;                  /* first time we saw this IP 
+                                     * in this stage */
+    time_t  last;                   /* last time we saw this IP */
+    time_t  zline_start;            /* time we placed a zline for this host,
+                                       or 0 if no zline */
+    int stage;                      /* how many times this host has been 
+                                     * z-lined */
+    int re_zlines;                  /* just a statistic -- how many times has 
+                                     * this host reconnected and had their 
+                                     * ban reset */
+
+    LIST_ENTRY(throttle_t) lp;
+} throttle;
+
+/* variables for the throttler */
+hash_table *throttle_hash;
+int throttle_tcount = THROTTLE_TRIGCOUNT;
+int throttle_ttime = THROTTLE_TRIGTIME;
+int throttle_rtime = THROTTLE_RECORDTIME;
+
+#ifdef THROTTLE_ENABLE
+int throttle_enable = 1;
+#else
+int throttle_enable = 0;
+#endif
+
+int numthrottles = 0; /* number of throttles in existence */
+
+#ifdef THROTTLE_ENABLE
+void throttle_init(void) 
+{
+    hashent_freelist = BlockHeapCreate(sizeof(hashent), 1024);
+    throttle_freelist = BlockHeapCreate(sizeof(throttle), 1024);
+    /* create the throttle hash. */
+    throttle_hash = create_hash_table(THROTTLE_HASHSIZE,
+            offsetof(throttle, addr), HOSTIPLEN,
+            HASH_FL_STRING, (int (*)(void *, void *))strcmp);
+}
+
+throttle *throttle_alloc()
+{
+   return BlockHeapALLOC(throttle_freelist, throttle);
+}
+
+void throttle_free(throttle *tp)
+{
+   BlockHeapFree(throttle_freelist, tp);
+}
+
+/* returns the zline time, in seconds */
+static int 
+throttle_get_zline_time(int stage)
+{
+   switch(stage)
+   {
+      case -1: 
+         return 0; /* no throttle */
+
+      case 0:
+         return 120; /* 2 minutes */
+
+      case 1:
+         return 300; /* 5 minutes */
+
+      case 2:
+         return 900; /* 15 minutes */
+
+      case 3:
+         return 1800; /* a half hour */
+
+      default:
+         return 3600; /* an hour */
+   }
+  
+   return 0; /* dumb compiler */
+}
+
+void 
+throttle_remove(char *host)
+{
+    throttle *tp = hash_find(throttle_hash, host);
+
+    if(tp)
+    {
+        LIST_REMOVE(tp, lp);
+        hash_delete(throttle_hash, tp);
+        throttle_free(tp);
+        numthrottles--;
+    }
+}
+
+void 
+throttle_force(char *host)
+{
+    throttle *tp = hash_find(throttle_hash, host);
+
+    if (tp == NULL) 
+    {
+        /* we haven't seen this one before, create a new throttle and add it to
+         * the hash.  XXX: blockheap code should be used, but the blockheap
+         * allocator available in ircd is broken beyond repair as far as I'm
+         * concerned. -wd */
+        tp = throttle_alloc();;
+        strcpy(tp->addr, host);
+
+        tp->stage = -1; /* no zline stage yet */
+        tp->zline_start = 0;
+        tp->conns = 0;
+        tp->first = NOW;
+        tp->re_zlines = 0;
+
+        hash_insert(throttle_hash, tp);
+        LIST_INSERT_HEAD(&throttles, tp, lp);
+        numthrottles++;
+    } 
+
+    /* now force them to be autothrottled if they reconnect. */
+    tp->conns = -1;
+    tp->last = tp->first = NOW;
+}
+
+/* fd is -1 for remote signons */
+int 
+throttle_check(char *host, int fd, time_t sotime) 
+{
+    throttle *tp = hash_find(throttle_hash, host);
+
+    if (!throttle_enable)
+        return 1; /* always successful */
+
+    /* If this is an old remote signon, just ignore it */
+    if(fd == -1 && (NOW - sotime > throttle_ttime))
+       return 1;
+
+    /* If this user is signing on 'in the future', we need to 
+       fix that. Someone has a bad remote TS, perhaps we should complain */
+    if(sotime > NOW)
+       sotime = NOW;
+
+    if (tp == NULL) 
+    {
+        /* we haven't seen this one before, create a new throttle and add it to
+         * the hash.  XXX: blockheap code should be used, but the blockheap
+         * allocator available in ircd is broken beyond repair as far as I'm
+         * concerned. -wd */
+        tp = throttle_alloc();;
+        strcpy(tp->addr, host);
+
+        tp->stage = -1; /* no zline stage yet */
+        tp->zline_start = 0;
+        tp->conns = 0;
+        tp->first = sotime;
+        tp->re_zlines = 0;
+
+        hash_insert(throttle_hash, tp);
+        LIST_INSERT_HEAD(&throttles, tp, lp);
+        numthrottles++;
+    } 
+    else if(tp->zline_start)
+    {
+       time_t zlength = throttle_get_zline_time(tp->stage);
+
+       /* If they're zlined, drop them */
+       /* Also, reset the zline counter */
+       if(sotime - tp->zline_start < zlength)
+       {
+          /* don't reset throttle time for new remote signons */
+          if(fd == -1)
+             return 0;
+          /* 
+           * Reset the z-line period to start now
+           * Mean, but should get the bots and help the humans
+           */
+          tp->re_zlines++;
+          tp->zline_start = sotime;
+          return 0;
+       }
+
+       /* may look redundant, but it fixes it if 
+          someone sets throttle_ttime to something insane */
+       tp->conns = 0;
+       tp->first = sotime;
+       tp->zline_start = 0;
+    }
+
+    /* got a throttle, up the conns */
+    if(tp->conns >= 0)
+       tp->conns++;
+    tp->last = sotime;
+
+    /* check the time bits, if they exceeded the throttle timeout, we should
+     * actually remove this structure from the hash and free it and create a
+     * new one, except that would be preposterously expensive, so we just
+     * re-set variables ;) -wd */
+    if (sotime - tp->first > throttle_ttime) 
+    {
+        tp->conns = 1;
+        tp->first = sotime;
+
+        /* we can probably gaurantee they aren't going to be throttled, return
+         * success */
+        return 1;
+    }
+
+    if (tp->conns == -1)
+    {
+        /* This is a forced throttle, drop 'em! */
+        return 0;
+    }
+
+    if (tp->conns >= throttle_tcount) 
+    {
+        /* mark them as z:lined (we do not actually add a Z:line as this would
+         * be wasteful) and let local +c ops know about this */
+        if (fd != -1) 
+        {
+            char errbufr[512];
+            int zlength, elength;
+
+            tp->stage++;
+            zlength = throttle_get_zline_time(tp->stage);
+
+            /* let +c ops know */
+            sendto_realops_lev(REJ_LEV, "throttled connections from %s (%d in"
+                               " %d seconds) for %d minutes (offense %d)",
+                               tp->addr, tp->conns, sotime - tp->first, 
+                               zlength / 60, tp->stage + 1);
+
+            elength = ircsnprintf(errbufr, 512, ":%s NOTICE ZUSR :You have"
+                                  " been throttled for %d minutes for too"
+                                  " many connections in a short period of time."
+                                  " Further connections in this period will"
+                                  " reset your throttle and you will have to"
+                                  " wait longer.\r\n", me.name, zlength / 60);
+            send(fd, errbufr, elength, 0);
+
+            if(throttle_get_zline_time(tp->stage+1) != zlength)
+            {
+                elength = ircsnprintf(errbufr, 512, ":%s NOTICE ZUSR :When you"
+                                      " return, if you are throttled again, "
+                                      "your throttle will last longer.\r\n", 
+                                      me.name);
+                send(fd, errbufr, elength, 0);
+            }
+
+            /* We steal this message from undernet, because mIRC detects it 
+             * and doesn't try to autoreconnect */
+            elength = ircsnprintf(errbufr, 512, "ERROR :Your host is trying "
+                                  "to (re)connect too fast -- throttled.\r\n",
+                                  tp->addr);
+            send(fd, errbufr, elength, 0);
+
+            tp->zline_start = sotime;
+        } 
+        else 
+        {
+            /* it might be desireable at some point to let people know about
+             * these problems.  for now, however, don't. */
+        }
+        return 0; /* drop 'em */
+    }
+    return 1; /* they're okay. */
+}
+                
+/* walk through our list of throttles, expire any as necessary.  in the case of
+ * Z:lines, expire them at the end of the Z:line timeout period. */
+/* Expire at the end of the zline timeout period plus throttle_rtime */
+void 
+throttle_timer(time_t now) 
+{
+    throttle *tp, *tp2;
+    time_t zlength;
+
+    if (!throttle_enable)
+        return;
+
+    tp = LIST_FIRST(&throttles);
+    while (tp != NULL)
+    {
+        zlength = throttle_get_zline_time(tp->stage);
+        tp2=LIST_NEXT(tp, lp);
+        if ((now == 0) || (tp->zline_start && 
+            (now - tp->zline_start) >= (zlength + throttle_rtime)) ||
+            (!tp->zline_start && (now - tp->last) >= throttle_rtime)) 
+        {
+            /* delete this item */
+            LIST_REMOVE(tp, lp);
+            hash_delete(throttle_hash, tp);
+            throttle_free(tp);
+            numthrottles--;
+        }
+        tp=tp2;
+    }
+}
+
+void throttle_rehash(void) 
+{
+    throttle_timer(0);
+}
+
+void throttle_resize(int size) 
+{
+    resize_hash_table(throttle_hash, size);
+}
+
+void throttle_stats(aClient *cptr, char *name) 
+{
+    int pending = 0, bans = 0;
+    throttle *tp;
+    unsigned int tcnt, tsz, hcnt, hsz;
+
+    tcnt = throttle_freelist->blocksAllocated * 
+           throttle_freelist->elemsPerBlock;
+    tsz = tcnt * throttle_freelist->elemSize;
+
+    hcnt = hashent_freelist->blocksAllocated * 
+           hashent_freelist->elemsPerBlock;
+    hsz = hcnt * hashent_freelist->elemSize;
+
+    sendto_one(cptr, ":%s %d %s :throttles: %d", me.name, RPL_STATSDEBUG, name,
+            numthrottles);
+    sendto_one(cptr, ":%s %d %s :alloc memory: %d throttles (%d bytes), "
+            "%d hashents (%d bytes)", me.name, RPL_STATSDEBUG, name,
+            tcnt, tsz, hcnt, hsz);            
+    sendto_one(cptr, ":%s %d %s :throttle hash table size: %d", me.name,
+            RPL_STATSDEBUG, name, throttle_hash->size);
+
+    /* now count bans/pending */
+    LIST_FOREACH(tp, &throttles, lp) 
+    {
+        if (tp->zline_start)
+            bans++;
+        else
+            pending++;
+    }
+    sendto_one(cptr, ":%s %d %s :throttles pending=%d bans=%d", me.name,
+            RPL_STATSDEBUG, name, pending, bans);
+    LIST_FOREACH(tp, &throttles, lp) 
+    {
+        int ztime = throttle_get_zline_time(tp->stage);
+
+        if (tp->zline_start && tp->zline_start + ztime > NOW)
+            sendto_one(cptr, ":%s %d %s :throttled: %s [stage %d, %d secs"
+                             " remain, %d futile retries]", me.name,
+                            RPL_STATSDEBUG, name, tp->addr, tp->stage, 
+                            (tp->zline_start + ztime) - NOW, tp->re_zlines);
+    }
+}
+
+#else
+/* ignore this -- required for drone modules and the like */
+void throttle_force(char *host) {}
+#endif
+/* vi:set ts=8 sts=4 sw=4 tw=79: */
diff --git a/src/userban.c b/src/userban.c
new file mode 100644 (file)
index 0000000..c5ebc8f
--- /dev/null
@@ -0,0 +1,1755 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/userban.c
+ *   Copyright (C) 2002 Lucas Madar and
+ *                      the DALnet coding team
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: userban.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "inet.h"
+#include "h.h"
+#include "userban.h"
+#include "queue.h"
+
+#define HASH_SIZE (32749)    /* largest prime < 32768 */
+
+LIST_HEAD(banlist_t, userBanEntry);
+typedef struct banlist_t ban_list;
+
+typedef struct userBanEntry {
+   struct userBan *ban;
+   LIST_ENTRY(userBanEntry) lp;
+} uBanEnt;
+
+typedef struct _abanlist {
+   ban_list wild_list;
+
+   int numbuckets;
+   ban_list *hash_list;
+} aBanList;
+
+typedef struct userBan auserBan;
+
+ban_list CIDR4BIG_bans = LIST_HEAD_INITIALIZER(CIDR4BIG_bans);
+ban_list **CIDR4_bans; 
+
+aBanList host_bans;
+aBanList ip_bans;
+
+aBanList gcos_bans;
+aBanList nick_bans;
+aBanList chan_bans;
+
+struct userBan *userban_alloc();
+struct simBan *simban_alloc();
+uBanEnt *ubanent_alloc();
+void ubanent_free(uBanEnt *);
+void userban_free(struct userBan *);
+void simban_free(struct simBan *);
+unsigned int host_hash(char *n);
+unsigned int ip_hash(char *n);
+
+unsigned int cidr_to_netmask(unsigned int cidr)
+{
+   if (cidr == 0)
+      return 0;
+
+   return (0xFFFFFFFF - (1 << (32 - cidr)) + 1);
+}
+
+unsigned int netmask_to_cidr(unsigned int mask) 
+{
+   int tmp = 0;
+
+   while (!(mask & (1 << tmp)) && tmp < 32) 
+      tmp++;
+
+   return (32 - tmp); 
+}
+
+/* userban (akill/kline) functions */
+
+void add_hostbased_userban(struct userBan *b)
+{
+   uBanEnt *bl;
+
+   bl = ubanent_alloc();
+   bl->ban = b;
+   b->internal_ent = (void *) bl;
+
+   if(b->flags & UBAN_CIDR4BIG)
+   {
+      LIST_INSERT_HEAD(&CIDR4BIG_bans, bl, lp);
+      return;
+   }
+
+   if(b->flags & UBAN_CIDR4)
+   {
+      unsigned char *s = (unsigned char *) &bl->ban->cidr4ip;
+      int a, b;
+
+      a = (int) *s++;
+      b = (int) *s;
+
+      LIST_INSERT_HEAD(&CIDR4_bans[a][b], bl, lp);
+      return;
+   }
+
+   if(b->flags & UBAN_IP)
+   {
+      if(b->flags & UBAN_WILD)
+      {
+         LIST_INSERT_HEAD(&ip_bans.wild_list, bl, lp);
+      }
+      else
+      {
+         unsigned int hv = ip_hash(b->h) % HASH_SIZE;
+
+         LIST_INSERT_HEAD(&ip_bans.hash_list[hv], bl, lp);
+      }
+
+      return;
+   }
+
+   if(b->flags & UBAN_HOST)
+   {
+      if(b->flags & UBAN_WILD)
+      {
+         LIST_INSERT_HEAD(&host_bans.wild_list, bl, lp);
+      }
+      else
+      {
+         unsigned int hv = host_hash(b->h) % HASH_SIZE;
+
+         LIST_INSERT_HEAD(&host_bans.hash_list[hv], bl, lp);
+      }
+
+      return;
+   }
+
+   /* unreachable code */
+   abort();
+}
+
+void remove_userban(struct userBan *b)
+{
+   uBanEnt *bl = (uBanEnt *) b->internal_ent;
+
+   LIST_REMOVE(bl, lp);
+
+   ubanent_free(bl);
+
+   return;
+}
+
+/*
+ * user_match_ban -- be sure to call only for fully-initialized users
+ * returns 0 on no match, 1 otherwise 
+ */
+int user_match_ban(aClient *cptr, struct userBan *ban)
+{
+   /* first match the 'user' portion */
+
+   if((!(ban->flags & UBAN_WILDUSER)) && match(ban->u, cptr->user->username)) 
+      return 0;
+
+   if(ban->flags & UBAN_IP)
+   {
+      char iptmp[HOSTIPLEN + 1];
+
+      strncpyzt(iptmp, inetntoa((char *)&cptr->ip), HOSTIPLEN + 1);
+      if(ban->flags & UBAN_WILD)
+      {
+         if(match(ban->h, iptmp) == 0)
+            return 1;
+      }
+      else
+      {
+         if(mycmp(ban->h, iptmp) == 0)
+            return 1;
+      }
+      return 0;
+   }
+
+   if(ban->flags & UBAN_HOST)
+   {
+      if(ban->flags & UBAN_WILD)
+      {
+         if((ban->flags & UBAN_WILDHOST) || match(ban->h, cptr->user->host) == 0)
+            return 1;
+      }
+      else
+      {
+         if(mycmp(ban->h, cptr->user->host) == 0)
+            return 1;
+      }
+      return 0;
+   }
+
+   if(ban->flags & (UBAN_CIDR4|UBAN_CIDR4BIG))
+   {
+      if((cptr->ip.s_addr & ban->cidr4mask) == ban->cidr4ip)
+         return 1;
+      return 0;
+   }
+
+   return 0;
+}
+
+struct userBan *check_userbanned(aClient *cptr, unsigned int yflags, unsigned int nflags)
+{
+   char iptmp[HOSTIPLEN + 1];
+   uBanEnt *bl;
+
+   strncpyzt(iptmp, inetntoa((char *)&cptr->ip), HOSTIPLEN + 1);
+
+   if(yflags & UBAN_IP)
+   {
+      unsigned int hv = ip_hash(iptmp) % HASH_SIZE;
+
+      LIST_FOREACH(bl, &ip_bans.hash_list[hv], lp) 
+      {
+         if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
+            continue;
+
+         if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
+             ((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
+            continue;
+
+         if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username)) 
+            continue;
+
+         if(mycmp(bl->ban->h, iptmp) == 0)
+            return bl->ban;
+      }
+
+      LIST_FOREACH(bl, &ip_bans.wild_list, lp) 
+      {
+         if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
+            continue;
+
+         if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
+             ((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
+            continue;
+
+         if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username)) 
+            continue;
+
+         if(match(bl->ban->h, iptmp) == 0)
+            return bl->ban;
+      }
+   }
+
+   if(yflags & UBAN_CIDR4)
+   {
+      unsigned char *s = (unsigned char *) &cptr->ip.s_addr;
+      int a, b;
+
+      a = (int) *s++;
+      b = (int) *s;
+
+      LIST_FOREACH(bl, &CIDR4_bans[a][b], lp) 
+      {
+         if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
+            continue;
+
+         if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
+             ((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
+            continue;
+
+         if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username)) 
+            continue;
+
+         if((cptr->ip.s_addr & bl->ban->cidr4mask) == bl->ban->cidr4ip)
+            return bl->ban;
+      }
+
+      LIST_FOREACH(bl, &CIDR4BIG_bans, lp) 
+      {
+         if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
+            continue;
+
+         if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
+             ((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
+            continue;
+
+         if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username)) 
+            continue;
+
+         if((cptr->ip.s_addr & bl->ban->cidr4mask) == bl->ban->cidr4ip)
+            return bl->ban;
+      }
+   }
+
+   if(yflags & UBAN_HOST)
+   {
+      unsigned int hv = host_hash(cptr->user->host) % HASH_SIZE;
+
+      LIST_FOREACH(bl, &host_bans.hash_list[hv], lp) 
+      {
+         if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
+            continue;
+
+         if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
+             ((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
+            continue;
+
+         if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username)) 
+            continue;
+
+         if(mycmp(bl->ban->h, cptr->user->host) == 0)
+            return bl->ban;
+      }
+
+      LIST_FOREACH(bl, &host_bans.wild_list, lp) 
+      {
+         if((bl->ban->flags & UBAN_TEMPORARY) && bl->ban->timeset + bl->ban->duration <= NOW)
+            continue;
+
+         if( ((yflags & UBAN_WILDUSER) && !(bl->ban->flags & UBAN_WILDUSER)) ||
+             ((nflags & UBAN_WILDUSER) && (bl->ban->flags & UBAN_WILDUSER)))
+            continue;
+
+         if((!(bl->ban->flags & UBAN_WILDUSER)) && match(bl->ban->u, cptr->user->username)) 
+            continue;
+
+         if((bl->ban->flags & UBAN_WILDHOST) || match(bl->ban->h, cptr->user->host) == 0)
+            return bl->ban;
+      }
+   }
+   return NULL;
+}
+
+struct userBan *find_userban_exact(struct userBan *borig, unsigned int careflags)
+{
+   uBanEnt *bl;
+
+   if(borig->flags & UBAN_CIDR4BIG)
+   {
+      LIST_FOREACH(bl, &CIDR4BIG_bans, lp) {
+         /* must have same wilduser, etc setting */
+         if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
+            continue;
+
+         /* user fields do not match? */
+         if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
+            continue;
+
+         if(!((borig->cidr4ip == bl->ban->cidr4ip) && (borig->cidr4mask == bl->ban->cidr4mask)))
+            continue;
+
+         return bl->ban;
+      }
+
+      return NULL;
+   }
+
+   if(borig->flags & UBAN_CIDR4)
+   {
+      unsigned char *s = (unsigned char *) &borig->cidr4ip;
+      int a, b;
+
+      a = (int) *s++;
+      b = (int) *s;
+
+      LIST_FOREACH(bl, &CIDR4_bans[a][b], lp) {
+         if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
+            continue;
+
+         if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
+            continue;
+
+         if(!((borig->cidr4ip == bl->ban->cidr4ip) && (borig->cidr4mask == bl->ban->cidr4mask)))
+            continue;
+
+         return bl->ban;
+      }
+
+      return NULL;
+   }
+
+   if(borig->flags & UBAN_IP)
+   {
+      if(borig->flags & UBAN_WILD)
+      {
+         LIST_FOREACH(bl, &ip_bans.wild_list, lp) {
+            if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
+               continue;
+
+            if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
+               continue;
+
+            if(mycmp(borig->h, bl->ban->h))
+               continue;
+
+            return bl->ban;
+         }
+      }
+      else
+      {
+         unsigned int hv = ip_hash(borig->h) % HASH_SIZE;
+
+         LIST_FOREACH(bl, &ip_bans.hash_list[hv], lp) {
+            if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
+               continue;
+
+            if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
+               continue;
+
+            if(mycmp(borig->h, bl->ban->h))
+               continue;
+
+            return bl->ban;
+         }
+      }
+
+      return NULL;
+   }
+
+   if(borig->flags & UBAN_HOST)
+   {
+      if(borig->flags & UBAN_WILD)
+      {
+         LIST_FOREACH(bl, &host_bans.wild_list, lp) {
+            if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
+               continue;
+
+            if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
+               continue;
+
+            if(mycmp(borig->h, bl->ban->h))
+               continue;
+
+            return bl->ban;
+         }
+      }
+      else
+      {
+         unsigned int hv = host_hash(borig->h) % HASH_SIZE;
+
+         LIST_FOREACH(bl, &host_bans.hash_list[hv], lp) {
+            if((bl->ban->flags ^ borig->flags) & (UBAN_WILDUSER|careflags))
+               continue;
+
+            if(!(borig->flags & UBAN_WILDUSER) && mycmp(borig->u, bl->ban->u))
+               continue;
+
+            if(mycmp(borig->h, bl->ban->h))
+               continue;
+
+            return bl->ban;
+         }
+      }
+
+      return NULL;
+   }
+
+   /* unreachable code */
+   abort();
+}
+
+static inline void expire_list(uBanEnt *bl)
+{
+   uBanEnt *bln;
+   struct userBan *ban;
+
+   while(bl)
+   {
+      bln = LIST_NEXT(bl, lp);
+      ban = bl->ban;
+
+      if((ban->flags & UBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+      {
+         remove_userban(ban);
+         userban_free(ban);
+      }
+      bl = bln;
+   }
+}
+
+static inline void remove_list_match_flags(uBanEnt *bl, unsigned int flags, unsigned int nflags)
+{
+   uBanEnt *bln;
+   struct userBan *ban;
+
+   while(bl)
+   {
+      bln = LIST_NEXT(bl, lp);
+      ban = bl->ban;
+
+      if((flags == 0 && nflags == 0) || (((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0)))
+      {
+         remove_userban(ban);
+         userban_free(ban);
+      }
+      bl = bln;
+   }
+}
+
+static inline void report_list_match_flags(aClient *cptr, uBanEnt *bl, unsigned int flags, unsigned int nflags, char rchar)
+{
+   struct userBan *ban;
+   char kset[8];
+   char host[128];
+
+   while(bl)
+   {
+      ban = bl->ban;
+
+      if((flags == 0 && nflags == 0) || (((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0)))
+      {
+         if(ban->flags & UBAN_LOCAL)
+         {
+            if(ban->flags & UBAN_TEMPORARY)
+               kset[0] = 'k';
+            else
+               kset[0] = 'K';
+         }
+         else
+         {
+            kset[0] = 'a';
+         }
+         kset[1] = rchar;
+         kset[2] = '\0';
+
+         if(ban->flags & (UBAN_CIDR4|UBAN_CIDR4BIG))
+            snprintf(host, 128, "%s/%d", inetntoa((char *)&ban->cidr4ip), netmask_to_cidr(ntohl(ban->cidr4mask)));
+         else
+            strcpy(host, ban->h);
+
+         sendto_one(cptr, rpl_str(RPL_STATSKLINE), me.name,
+                    cptr->name, kset, host,
+                    (ban->flags & UBAN_WILDUSER) ? "*" : ban->u, 
+                    (ban->flags & UBAN_TEMPORARY) ? (((ban->timeset + ban->duration) - NOW) / 60) : -1, 
+                    (ban->reason) ? ban->reason : "No reason");
+      }
+
+      bl = LIST_NEXT(bl, lp);
+   }
+}
+
+void expire_userbans()
+{
+   uBanEnt *bl;
+   int a, b;
+
+   bl = LIST_FIRST(&CIDR4BIG_bans);
+   expire_list(bl);
+
+   for(a = 0; a < 256; a++)
+   {
+     for(b = 0; b < 256; b++)
+     {
+        bl = LIST_FIRST(&CIDR4_bans[a][b]);
+        expire_list(bl);
+     }
+   }
+
+   bl = LIST_FIRST(&host_bans.wild_list);
+   expire_list(bl);
+   bl = LIST_FIRST(&ip_bans.wild_list);
+   expire_list(bl);
+
+   for(a = 0; a < HASH_SIZE; a++)
+   {
+      bl = LIST_FIRST(&host_bans.hash_list[a]);
+      expire_list(bl);
+      bl = LIST_FIRST(&ip_bans.hash_list[a]);
+      expire_list(bl);
+   }
+}
+
+void remove_userbans_match_flags(unsigned int flags, unsigned int nflags)
+{
+   uBanEnt *bl;
+   int a, b;
+
+   bl = LIST_FIRST(&CIDR4BIG_bans);
+   remove_list_match_flags(bl, flags, nflags);
+
+   for(a = 0; a < 256; a++)
+   {
+     for(b = 0; b < 256; b++)
+     {
+        bl = LIST_FIRST(&CIDR4_bans[a][b]);
+        remove_list_match_flags(bl, flags, nflags);
+     }
+   }
+
+   bl = LIST_FIRST(&host_bans.wild_list);
+   remove_list_match_flags(bl, flags, nflags);
+   bl = LIST_FIRST(&ip_bans.wild_list);
+   remove_list_match_flags(bl, flags, nflags);
+
+   for(a = 0; a < HASH_SIZE; a++)
+   {
+      bl = LIST_FIRST(&host_bans.hash_list[a]);
+      remove_list_match_flags(bl, flags, nflags);
+      bl = LIST_FIRST(&ip_bans.hash_list[a]);
+      remove_list_match_flags(bl, flags, nflags);
+   }
+}
+
+void report_userbans_match_flags(aClient *cptr, unsigned int flags, unsigned int nflags)
+{
+   uBanEnt *bl;
+   int a, b;
+
+   bl = LIST_FIRST(&CIDR4BIG_bans);
+   report_list_match_flags(cptr, bl, flags, nflags, 'C');
+
+   for(a = 0; a < 256; a++)
+   {
+     for(b = 0; b < 256; b++)
+     {
+        bl = LIST_FIRST(&CIDR4_bans[a][b]);
+        report_list_match_flags(cptr, bl, flags, nflags, 'c');
+     }
+   }
+
+   bl = LIST_FIRST(&host_bans.wild_list);
+   report_list_match_flags(cptr, bl, flags, nflags, 'h');
+   bl = LIST_FIRST(&ip_bans.wild_list);
+   report_list_match_flags(cptr, bl, flags, nflags, 'i');
+
+   for(a = 0; a < HASH_SIZE; a++)
+   {
+      bl = LIST_FIRST(&host_bans.hash_list[a]);
+      report_list_match_flags(cptr, bl, flags, nflags, 'H');
+      bl = LIST_FIRST(&ip_bans.hash_list[a]);
+      report_list_match_flags(cptr, bl, flags, nflags, 'I');
+   }
+}
+
+char *get_userban_host(struct userBan *ban, char *buf, int buflen)
+{
+   *buf = '\0';
+
+   if(ban->flags & (UBAN_CIDR4|UBAN_CIDR4BIG))
+      snprintf(buf, buflen, "%s/%d", inetntoa((char *)&ban->cidr4ip), netmask_to_cidr(ntohl(ban->cidr4mask)));
+   else
+      snprintf(buf, buflen, "%s", ban->h);
+
+   return buf;
+}
+
+/*
+ * Fills in the following fields
+ * of a userban structure, or returns NULL if invalid stuff is passed.
+ *  - flags, u, h, cidr4ip, cidr4mask
+ */
+struct userBan *make_hostbased_ban(char *user, char *phost)
+{
+   char host[512];
+   unsigned int flags = 0, c4h = 0, c4m = 0;
+   int numcount, othercount, wildcount, dotcount, slashcount;
+   char *tmp;
+   struct userBan *b;
+
+   strncpy(host, phost, 512);
+
+   numcount = othercount = wildcount = dotcount = slashcount = 0;
+
+   for(tmp = host; *tmp; tmp++)
+   {
+      switch(*tmp)
+      {
+         case '0':
+         case '1':
+         case '2':
+         case '3':
+         case '4':
+         case '5':
+         case '6':
+         case '7':
+         case '8':
+         case '9':
+            numcount++;
+            break;
+
+         case '*':
+         case '?':
+            wildcount++;
+            break;
+
+         case '.':
+            dotcount++;
+            break;
+
+         case '/':
+            slashcount++;
+            break;
+
+         default:
+            othercount++;
+            break;
+      }      
+   }
+
+   if(wildcount && !numcount && !othercount)
+   {
+      if(!user || !*user || mycmp(user, "*") == 0)
+         return NULL; /* all wildcards? aagh! */
+
+      flags = (UBAN_HOST|UBAN_WILD);
+
+      if(mycmp(host, "*.*") == 0 || mycmp(host, "*") == 0)
+         flags |= UBAN_WILDHOST;
+
+      goto success;
+   }
+
+   /* everything must have a dot. never more than one slash. */
+   if(dotcount == 0 || slashcount > 1)
+      return NULL;
+
+   /* wildcarded IP address? -- can we convert it to a CIDR? */
+   if(wildcount && numcount && !othercount)
+   {
+      char octet[4][8];
+      int i1, i2;
+      int gotwild;
+
+      if(slashcount)
+         return NULL; /* slashes and wildcards? */
+
+      /* I see... more than 3 dots? */
+      if(dotcount > 3)
+         return NULL;
+
+      i1 = i2 = 0;
+
+      /* separate this thing into dotcount octets. */
+      for(tmp = host; *tmp; tmp++)
+      {
+         if(*tmp == '.')
+         {
+            octet[i1][i2] = '\0';
+            i2 = 0;
+            i1++;
+            continue;
+         }
+         if(i2 < 6)
+         {
+            octet[i1][i2++] = *tmp;
+         }
+      }
+      octet[i1][i2] = '\0';
+
+      /* verify that each octet is all numbers or just a '*' */
+      /* bans that match 123.123.123.1?? are still valid, just not convertable to a CIDR */
+
+      for(gotwild = i1 = 0; i1 <= dotcount; i1++)
+      {
+         if(strcmp(octet[i1], "*") == 0)
+         {
+            gotwild++;
+            continue;
+         }
+
+         /* ban in the format of 1.2.*.4 */
+         if(gotwild)
+         {
+            flags = (UBAN_IP|UBAN_WILD);
+            goto success;
+         }
+
+         for(i2 = 0; octet[i1][i2]; i2++)
+         {
+             switch(octet[i1][i2])
+             {
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                   break;
+
+                default:
+                   flags = (UBAN_IP|UBAN_WILD);
+                   goto success;
+             }
+         }
+      }
+
+      if(octet[0][0] == '*')
+         return NULL; /* the first octet is a wildcard? what the hell? */
+
+      if(octet[1][0] == '*')
+      {
+         sprintf(host, "%s.0.0.0/8", octet[0]);
+         goto cidrforce;
+      }
+      else if(dotcount >= 2 && octet[2][0] == '*')
+      {
+         sprintf(host, "%s.%s.0.0/16", octet[0], octet[1]);
+         goto cidrforce;
+      }
+      else if(dotcount >= 3 && octet[3][0] == '*')
+      {
+         sprintf(host, "%s.%s.%s.0/24", octet[0], octet[1], octet[2]);
+         goto cidrforce;
+      }
+
+      return NULL; /* we should never get here. If we do, something is wrong. */
+   }
+
+   /* CIDR IP4 address? */
+   if(!wildcount && numcount && !othercount && slashcount)
+   {
+      int sval;
+      char *sep, *err;
+      struct in_addr ia, na;
+
+cidrforce:
+      sep = strchr(host, '/'); /* guaranteed to be here because slashcount */
+      *sep = '\0';
+      sep++;
+      if((ia.s_addr = inet_addr(host)) == 0xFFFFFFFF) /* invalid ip4 address! */
+         return NULL;
+
+      /* is there a problem with the / mask? */
+      sval = strtol(sep, &err, 10);
+      if(*err != '\0')
+         return NULL;
+
+      if(sval < 0 || sval > 32)
+         return NULL;
+
+      na.s_addr = htonl(cidr_to_netmask(sval));
+      ia.s_addr &= na.s_addr;
+
+      c4h = ia.s_addr;
+      c4m = na.s_addr;
+      
+      flags = (sval < 16) ? UBAN_CIDR4BIG : UBAN_CIDR4;
+      goto success;
+   }
+
+   if(slashcount)
+      return NULL;
+   if(!othercount)
+   {
+      flags = (UBAN_IP | (wildcount ? UBAN_WILD : 0));
+      goto success;
+   }
+
+   flags = (UBAN_HOST | (wildcount ? UBAN_WILD : 0));
+
+success:
+   b = userban_alloc();
+   if(!b)
+      return NULL;
+
+   b->reason = NULL;
+
+   if(flags & (UBAN_CIDR4BIG|UBAN_CIDR4))
+   {
+      b->cidr4ip = c4h;
+      b->cidr4mask = c4m;
+      b->h = NULL;
+   }
+   else
+   {
+      b->cidr4ip = b->cidr4mask = 0;
+      b->h = (char *)MyMalloc(strlen(host) + 1);
+      strcpy(b->h, host);
+   }
+
+   if(!user || !*user || mycmp(user, "*") == 0)
+   {
+      flags |= UBAN_WILDUSER;
+      b->u = NULL;
+   }
+   else
+   {
+      b->u = (char *)MyMalloc(strlen(user) + 1);
+      strcpy(b->u, user);
+   }
+
+   b->flags = flags;
+
+   return b;   
+}
+
+/* simban (simple ban) functions */
+
+/*
+ * make_simpleban does only simple sanity checking.
+ * You must pass it one of each of the following flags in 'flags', or'd together:
+ * SBAN_GCOS (gline) or SBAN_NICK (qline) or SBAN_CHAN (channel qline)
+ * SBAN_LOCAL or SBAN_NETWORK (self-explanatory)
+ */
+struct simBan *make_simpleban(unsigned int flags, char *mask) 
+{
+   char *tmp;
+   struct simBan *b;
+   int wildcount = 0, othercount = 0;
+
+   for(tmp = mask; *tmp; tmp++)
+   {
+      switch(*tmp)
+      {
+         case '*':
+         case '?':
+            wildcount++;
+            break;
+
+         default:
+            othercount++;
+            break;
+      }
+   }
+
+   if((flags & (SBAN_NETWORK|SBAN_LOCAL)) == 0)
+      return NULL;
+
+   if((flags & (SBAN_NICK|SBAN_GCOS|SBAN_CHAN)) == 0)
+      return NULL;
+
+   if(othercount == 0)
+      return NULL; /* No bans consisting only of wildcards */
+
+   if(wildcount)
+      flags |= SBAN_WILD;
+
+   b = simban_alloc();
+   if(!b) 
+      return NULL;
+
+   b->reason = NULL;
+   b->mask = (char *) MyMalloc(strlen(mask) + 1);
+   strcpy(b->mask, mask);
+   b->flags = flags;
+
+   return b;
+}
+
+void add_simban(struct simBan *b)
+{
+   uBanEnt *bl;
+   aBanList *banlist;
+   ban_list *thelist;
+
+   bl = ubanent_alloc();
+   bl->ban = (struct userBan *) b;
+   b->internal_ent = (void *) bl;
+
+   if(b->flags & SBAN_NICK)
+      banlist = &nick_bans;
+   else if(b->flags & SBAN_CHAN)
+      banlist = &chan_bans;
+   else if(b->flags & SBAN_GCOS)
+      banlist = &gcos_bans;
+   else
+      abort(); /* ack! */
+
+   if(b->flags & SBAN_WILD)
+   {
+      thelist = &banlist->wild_list;
+   }
+   else
+   {
+      unsigned int hv = host_hash(b->mask) % HASH_SIZE;
+
+      thelist = &banlist->hash_list[hv];
+   }
+
+   LIST_INSERT_HEAD(thelist, bl, lp);
+}
+
+void remove_simban(struct simBan *b)
+{
+   uBanEnt *bl = (uBanEnt *) b->internal_ent;
+
+   LIST_REMOVE(bl, lp);
+
+   ubanent_free(bl);
+
+   return;
+}
+
+struct simBan *find_simban_exact(struct simBan *borig)
+{
+   uBanEnt *bl;
+   struct simBan *ban;
+   aBanList *banlist;
+   ban_list *thelist;
+
+   if(borig->flags & SBAN_NICK)
+      banlist = &nick_bans;
+   else if(borig->flags & SBAN_CHAN)
+      banlist = &chan_bans;
+   else if(borig->flags & SBAN_GCOS)
+      banlist = &gcos_bans;
+   else
+      return NULL;
+
+   if(borig->flags & SBAN_WILD)
+   {
+      thelist = &banlist->wild_list;
+   }
+   else
+   {
+      unsigned int hv = host_hash(borig->mask) % HASH_SIZE;
+
+      thelist = &banlist->hash_list[hv];
+   }
+   LIST_FOREACH(bl, thelist, lp) 
+   {
+      ban = (struct simBan *) bl->ban;
+
+      if(ban->flags != borig->flags)
+         continue;
+
+      if(mycmp(ban->mask, borig->mask))
+         continue;
+
+      return ban;
+   }
+
+   return NULL;
+}
+
+/* does cptr match the ban specified in b? 
+ * return: 0 = no
+ */
+int user_match_simban(aClient *cptr, struct simBan *b)
+{
+   char *userinfo;
+   int (*chkfnc)(char *, char *);
+
+   if(b->flags & SBAN_NICK)
+      userinfo = cptr->name;
+   else if(b->flags & SBAN_CHAN)
+      return 0; /* not applicable */
+   else if(b->flags & SBAN_GCOS)
+      userinfo = cptr->info;
+   else
+      abort(); /* aagh! */
+
+   if(b->flags & SBAN_WILD)
+      chkfnc = match;
+   else
+      chkfnc = mycmp;
+   if(chkfnc(b->mask, userinfo) == 0)
+      return 1;
+
+   return 0;
+}
+
+struct simBan *check_mask_simbanned(char *mask, unsigned int flags)
+{
+   uBanEnt *bl;
+   struct simBan *ban;
+   aBanList *banlist;
+   ban_list *thelist;
+   int (*chkfnc)(char *, char *);
+   unsigned int hv;
+
+   if(flags & SBAN_NICK)
+      banlist = &nick_bans;
+   else if(flags & SBAN_CHAN)
+      banlist = &chan_bans;
+   else if(flags & SBAN_GCOS)
+      banlist = &gcos_bans;
+   else
+      abort(); /* aagh! */
+
+   hv = host_hash(mask) % HASH_SIZE;
+   thelist = &banlist->hash_list[hv];
+   chkfnc = mycmp;
+   LIST_FOREACH(bl, thelist, lp) 
+   {
+      ban = (struct simBan *) bl->ban;
+
+      if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+         continue;
+
+      if(chkfnc(ban->mask, mask))
+         continue;
+
+      return ban;
+   }
+
+   thelist = &banlist->wild_list;
+   chkfnc = match;
+   LIST_FOREACH(bl, thelist, lp) 
+   {
+      ban = (struct simBan *) bl->ban;
+
+      if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+         continue;
+
+      if(chkfnc(ban->mask, mask))
+         continue;
+
+      return ban;
+   }
+
+   return NULL;
+}
+
+void report_simbans_match_flags(aClient *cptr, unsigned int flags, unsigned int nflags)
+{
+   uBanEnt *bl;
+   struct simBan *ban;
+   aBanList *banlist;
+   ban_list *thelist;
+   unsigned int hv;
+   char sbuf[16];
+   int slen;
+
+   if(flags & SBAN_NICK)
+      banlist = &nick_bans;
+   else if(flags & SBAN_CHAN)
+      banlist = &chan_bans;
+   else if(flags & SBAN_GCOS)
+      banlist = &gcos_bans;
+   else
+      abort(); /* aagh! */
+
+   for(hv = 0; hv < HASH_SIZE; hv++)
+   {
+      thelist = &banlist->hash_list[hv];
+      LIST_FOREACH(bl, thelist, lp)
+      {
+         ban = (struct simBan *) bl->ban;
+
+         if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+            continue;
+
+         if(((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0))
+         {
+            int rpl = RPL_STATSQLINE;
+            slen = 0;
+            if(flags & SBAN_NICK)
+            {
+               sbuf[slen++] = (flags & SBAN_LOCAL) ? 'Q' : 'q';
+               sbuf[slen++] = 'n';
+            }
+            else if(flags & SBAN_CHAN)
+            {
+               sbuf[slen++] = (flags & SBAN_LOCAL) ? 'Q' : 'q';
+               sbuf[slen++] = 'c';
+            }
+            else if(flags & SBAN_GCOS)
+            {
+               rpl = RPL_STATSGLINE;
+               sbuf[slen++] = (flags & SBAN_LOCAL) ? 'G' : 'g';
+            }
+            sbuf[slen] = '\0';
+
+            sendto_one(cptr, rpl_str(rpl), me.name, cptr->name,
+                       sbuf, 
+                       ban->mask, 
+                       (ban->flags & SBAN_TEMPORARY) ? (((ban->timeset + ban->duration) - NOW) / 60) : -1,
+                       ban->reason ? ban->reason : "No Reason");
+         }
+      }
+   }
+
+   thelist = &banlist->wild_list;
+   LIST_FOREACH(bl, thelist, lp)
+   {
+      ban = (struct simBan *) bl->ban;
+
+      if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+         continue;
+
+      if(((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0))
+      {
+         int rpl = RPL_STATSQLINE;
+         slen = 0;
+         if(flags & SBAN_NICK)
+         {
+            sbuf[slen++] = (flags & SBAN_LOCAL) ? 'Q' : 'q';
+            sbuf[slen++] = 'n';
+         }
+         else if(flags & SBAN_CHAN)
+         {
+            sbuf[slen++] = (flags & SBAN_LOCAL) ? 'Q' : 'q';
+            sbuf[slen++] = 'c';
+         }
+         else if(flags & SBAN_GCOS)
+         {
+            rpl = RPL_STATSGLINE;
+            sbuf[slen++] = (flags & SBAN_LOCAL) ? 'G' : 'g';
+         }
+         sbuf[slen++] = 'w';
+         sbuf[slen] = '\0';
+
+         sendto_one(cptr, rpl_str(rpl), me.name, cptr->name,
+                    sbuf, 
+                    ban->mask, 
+                    (ban->flags & SBAN_TEMPORARY) ? (((ban->timeset + ban->duration) - NOW) / 60) : -1,
+                    ban->reason ? ban->reason : "No Reason");
+      }
+   }   
+}
+
+
+void remove_simbans_match_flags(unsigned int flags, unsigned int nflags)
+{
+   uBanEnt *bl;
+   struct simBan *ban;
+   aBanList *banlist;
+   ban_list *thelist;
+   unsigned int hv;
+
+   if(flags & SBAN_NICK)
+      banlist = &nick_bans;
+   else if(flags & SBAN_CHAN)
+      banlist = &chan_bans;
+   else if(flags & SBAN_GCOS)
+      banlist = &gcos_bans;
+   else
+      abort(); /* aagh! */
+
+   for(hv = 0; hv < HASH_SIZE; hv++)
+   {
+      thelist = &banlist->hash_list[hv];
+      LIST_FOREACH(bl, thelist, lp)
+      {
+         ban = (struct simBan *) bl->ban;
+
+         if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+            continue;
+
+         if(((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0))
+         {
+            /* Kludge it out! */
+            ban->flags |= SBAN_TEMPORARY;
+            ban->timeset = NOW - 5;
+            ban->duration = 1;
+         }
+      }
+   }
+
+   thelist = &banlist->wild_list;
+   LIST_FOREACH(bl, thelist, lp)
+   {
+      ban = (struct simBan *) bl->ban;
+
+      if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+         continue;
+
+      if(((ban->flags & flags) == flags) && ((ban->flags & nflags) == 0))
+      {
+         /* Kludge it out! */
+         ban->flags |= SBAN_TEMPORARY;
+         ban->timeset = NOW - 5;
+         ban->duration = 1;
+      }
+   }   
+}
+
+void send_simbans(aClient *cptr, unsigned int flags)
+{
+   uBanEnt *bl;
+   struct simBan *ban;
+   aBanList *banlist;
+   ban_list *thelist;
+   unsigned int hv;
+
+   if(flags & SBAN_NICK)
+      banlist = &nick_bans;
+   else if(flags & SBAN_CHAN)
+      banlist = &chan_bans;
+   else if(flags & SBAN_GCOS)
+      banlist = &gcos_bans;
+   else
+      abort(); /* aagh! */
+
+   for(hv = 0; hv < HASH_SIZE; hv++)
+   {
+      thelist = &banlist->hash_list[hv];
+      LIST_FOREACH(bl, thelist, lp)
+      {
+         ban = (struct simBan *) bl->ban;
+
+         if(ban->flags & SBAN_TEMPORARY)
+            continue;
+
+         if((ban->flags & flags) == flags)
+         {
+            if(ban->flags & SBAN_GCOS)
+               sendto_one(cptr, ":%s SGLINE %d :%s:%s", me.name, strlen(ban->mask),
+                          ban->mask, ban->reason);
+            else
+               sendto_one(cptr, ":%s SQLINE %s :%s", me.name,
+                          ban->mask, ban->reason);
+         }
+      }
+   }
+
+   thelist = &banlist->wild_list;
+   LIST_FOREACH(bl, thelist, lp)
+   {
+      ban = (struct simBan *) bl->ban;
+
+      if(ban->flags & SBAN_TEMPORARY)
+         continue;
+
+      if((ban->flags & flags) == flags)
+      {
+         if(ban->flags & SBAN_GCOS)
+            sendto_one(cptr, ":%s SGLINE %d :%s:%s", me.name, strlen(ban->mask),
+                       ban->mask, ban->reason);
+         else
+            sendto_one(cptr, ":%s SQLINE %s :%s", me.name,
+                       ban->mask, ban->reason);
+      }
+   }   
+}
+
+void remove_simbans_match_mask(unsigned int flags, char *mask, int wild)
+{
+   uBanEnt *bl;
+   struct simBan *ban;
+   aBanList *banlist;
+   ban_list *thelist;
+   unsigned int hv;
+   int (*chkfnc)(char *, char *);
+
+   chkfnc = wild ? match : mycmp;
+
+   if(flags & SBAN_NICK)
+      banlist = &nick_bans;
+   else if(flags & SBAN_CHAN)
+      banlist = &chan_bans;
+   else if(flags & SBAN_GCOS)
+      banlist = &gcos_bans;
+   else
+      abort(); /* aagh! */
+
+   for(hv = 0; hv < HASH_SIZE; hv++)
+   {
+      thelist = &banlist->hash_list[hv];
+      LIST_FOREACH(bl, thelist, lp)
+      {
+         ban = (struct simBan *) bl->ban;
+
+         if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+            continue;
+
+         if((ban->flags & flags) != flags)
+            continue;
+
+         if(chkfnc(mask, ban->mask) == 0)
+         {
+            /* Kludge it out! */
+            ban->flags |= SBAN_TEMPORARY;
+            ban->timeset = NOW - 5;
+            ban->duration = 1;
+         }
+      }
+   }
+
+   thelist = &banlist->wild_list;
+   LIST_FOREACH(bl, thelist, lp)
+   {
+      ban = (struct simBan *) bl->ban;
+
+      if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+         continue;
+
+      if((ban->flags & flags) != flags)
+         continue;
+
+      if(chkfnc(mask, ban->mask) == 0)
+      {
+         /* Kludge it out! */
+         ban->flags |= SBAN_TEMPORARY;
+         ban->timeset = NOW - 5;
+         ban->duration = 1;
+      }
+   }   
+}
+
+static inline void expire_simlist(uBanEnt *bl)
+{
+   uBanEnt *bln;
+   struct simBan *ban;
+
+   while(bl)
+   {
+      bln = LIST_NEXT(bl, lp);
+      ban = (struct simBan *)bl->ban;
+
+      if((ban->flags & SBAN_TEMPORARY) && ban->timeset + ban->duration <= NOW)
+      {
+         remove_simban(ban);
+         simban_free(ban);
+      }
+      bl = bln;
+   }
+}
+
+void expire_simbans()
+{
+   uBanEnt *bl;
+   int a;
+
+   bl = LIST_FIRST(&nick_bans.wild_list);
+   expire_simlist(bl);
+   bl = LIST_FIRST(&chan_bans.wild_list);
+   expire_simlist(bl);
+   bl = LIST_FIRST(&gcos_bans.wild_list);
+   expire_simlist(bl);
+
+   for(a = 0; a < HASH_SIZE; a++)
+   {
+      bl = LIST_FIRST(&nick_bans.hash_list[a]);
+      expire_simlist(bl);
+      bl = LIST_FIRST(&chan_bans.hash_list[a]);
+      expire_simlist(bl);
+      bl = LIST_FIRST(&gcos_bans.hash_list[a]);
+      expire_simlist(bl);
+   }
+}
+
+/* Hash and init functions */
+
+unsigned int ip_hash(char *n)
+{
+   unsigned int hv = 0;
+
+   while(*n)
+   {
+      hv = hv * 33 + tolowertab[(unsigned char) *n++];
+   }
+
+   return hv;
+}
+
+unsigned int host_hash(char *n)
+{
+   unsigned int hv = 0;
+
+   while(*n)
+   {
+      if(*n != '.') 
+      {
+         hv <<= 5;
+         hv |= ((touppertab[(unsigned char) *n]) - 65) & 0xFF;
+      }
+      n++;
+   }
+
+   return hv;
+}
+
+void init_banlist(aBanList *a, int numbuckets)
+{
+   memset(a, 0, sizeof(aBanList));
+   a->numbuckets = numbuckets;
+   a->hash_list = (ban_list *) MyMalloc(numbuckets * sizeof(ban_list));
+   memset(a->hash_list, 0, numbuckets * sizeof(ban_list));
+}
+
+void init_userban()
+{
+   int i;
+
+   CIDR4_bans = (ban_list **) MyMalloc(256 * sizeof(ban_list *));
+   for(i = 0; i < 256; i++)
+   {
+      CIDR4_bans[i] = (ban_list *) MyMalloc(256 * sizeof(ban_list));
+      memset(CIDR4_bans[i], 0, 256 * sizeof(ban_list));
+   }
+
+   init_banlist(&host_bans, HASH_SIZE);
+   init_banlist(&ip_bans, HASH_SIZE);
+
+   init_banlist(&gcos_bans, HASH_SIZE);
+   init_banlist(&nick_bans, HASH_SIZE);
+   init_banlist(&chan_bans, HASH_SIZE);
+}
+
+unsigned int userban_count = 0, ubanent_count = 0, simban_count = 0;
+
+struct userBan *userban_alloc()
+{
+   struct userBan *b;
+
+   b = (struct userBan *) MyMalloc(sizeof(struct userBan));
+   if(b)
+   {
+      memset(b, 0, sizeof(struct userBan));
+      userban_count++;
+   }
+   return b;
+}
+
+void userban_free(struct userBan *b)
+{
+   if(b->u)
+      MyFree(b->u);
+
+   if(b->h)
+      MyFree(b->h);
+
+   if(b->reason)
+      MyFree(b->reason);
+
+   userban_count--;
+   MyFree(b);
+}
+
+uBanEnt *ubanent_alloc()
+{
+   uBanEnt *b;
+
+   b = (uBanEnt *) MyMalloc(sizeof(uBanEnt));
+   if(b)
+   {
+      memset(b, 0, sizeof(uBanEnt));
+      ubanent_count++;
+   }
+   return b;
+}
+
+void ubanent_free(uBanEnt *b)
+{
+   ubanent_count--;
+   MyFree(b);
+}
+
+struct simBan *simban_alloc()
+{
+   struct simBan *b;
+
+   b = (struct simBan *) MyMalloc(sizeof(struct simBan));
+   if(b)
+   {
+      memset(b, 0, sizeof(struct simBan));
+      simban_count++;
+   }
+   return b;
+}
+
+void simban_free(struct simBan *b)
+{
+   if(b->mask)
+      MyFree(b->mask);
+
+   if(b->reason)
+      MyFree(b->reason);
+
+   simban_count--;
+   MyFree(b);
+}
+
+int count_simlist(uBanEnt *bl, int *mem)
+{
+   uBanEnt *bln;
+   struct simBan *ban;
+   int umem = 0, ucnt = 0;
+
+   while(bl)
+   {
+      bln = LIST_NEXT(bl, lp);
+      ban = (struct simBan *)bl->ban;
+
+      ucnt++;
+      umem += sizeof(struct simBan);
+      if(ban->mask)
+         umem += (strlen(ban->mask) + 1);
+
+      if(ban->reason)
+         umem += (strlen(ban->reason) + 1);
+
+      bl = bln;
+   }
+
+   if(mem)
+      *mem = umem;
+
+   return ucnt;
+}
+
+int count_list(uBanEnt *bl, int *mem)
+{
+   uBanEnt *bln;
+   struct userBan *ban;
+   int umem = 0, ucnt = 0;
+
+   while(bl)
+   {
+      bln = LIST_NEXT(bl, lp);
+      ban = bl->ban;
+
+      ucnt++;
+      umem += sizeof(struct userBan);
+      if(ban->u)
+         umem += (strlen(ban->u) + 1);
+      if(ban->h)
+         umem += (strlen(ban->h) + 1);
+      if(ban->reason)
+         umem += (strlen(ban->reason) + 1);
+
+      bl = LIST_NEXT(bl, lp);
+   }
+
+   if(mem)
+      *mem = umem;
+
+   return ucnt;
+}
+
+int count_userbans(aClient *cptr)
+{
+   uBanEnt *bl;
+   int a, b;
+   int ic[16], im[16];
+   int ict = 0, imt = 0;
+
+   memset(ic, 0, sizeof(int) * 16);
+   memset(im, 0, sizeof(int) * 16);
+
+   bl = LIST_FIRST(&CIDR4BIG_bans);
+   ic[0] = count_list(bl, &im[0]);
+
+   for(a = 0; a < 256; a++)
+   {
+     for(b = 0; b < 256; b++)
+     {
+        int tmpim;
+
+        bl = LIST_FIRST(&CIDR4_bans[a][b]);
+        ic[1] += count_list(bl, &tmpim);
+        im[1] += tmpim;
+     }
+   }
+
+   bl = LIST_FIRST(&host_bans.wild_list);
+   ic[2] = count_list(bl, &im[2]);
+   bl = LIST_FIRST(&ip_bans.wild_list);
+   ic[3] = count_list(bl, &im[3]);
+
+   for(a = 0; a < HASH_SIZE; a++)
+   {
+      int tmpim;
+
+      bl = LIST_FIRST(&host_bans.hash_list[a]);
+      ic[4] += count_list(bl, &tmpim);
+      im[4] += tmpim;
+
+      bl = LIST_FIRST(&ip_bans.hash_list[a]);
+      ic[5] += count_list(bl, &tmpim);
+      im[5] += tmpim;
+   }
+
+   for(a = 0; a < 16; a++)
+   {
+      ict += ic[a];
+      imt += im[a];
+   }
+
+   sendto_one(cptr, ":%s %d %s :UserBans %d(%d) UserBanEnts %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ict, imt, ubanent_count,
+              ubanent_count * sizeof(uBanEnt));
+
+   sendto_one(cptr, ":%s %d %s :  CIDR4BIG %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[0], im[0]);
+   sendto_one(cptr, ":%s %d %s :  CIDR4 %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[1], im[1]);
+   sendto_one(cptr, ":%s %d %s :  Host %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[4], im[4]);
+   sendto_one(cptr, ":%s %d %s :  Host wild %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[2], im[2]);
+   sendto_one(cptr, ":%s %d %s :  IP %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[5], im[5]);
+   sendto_one(cptr, ":%s %d %s :  IP wild %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[3], im[3]);
+
+   return imt + (ubanent_count * sizeof(uBanEnt));
+}
+
+int count_simbans(aClient *cptr)
+{
+   uBanEnt *bl;
+   int a;
+   int ic[16], im[16];
+   int ict = 0, imt = 0;
+
+   memset(ic, 0, sizeof(int) * 16);
+   memset(im, 0, sizeof(int) * 16);
+
+   bl = LIST_FIRST(&nick_bans.wild_list);
+   ic[1] = count_simlist(bl, &im[1]);
+   bl = LIST_FIRST(&chan_bans.wild_list);
+   ic[3] = count_simlist(bl, &im[3]);
+   bl = LIST_FIRST(&gcos_bans.wild_list);
+   ic[5] = count_simlist(bl, &im[5]);
+
+   for(a = 0; a < HASH_SIZE; a++)
+   {
+      int tmpim;
+
+      bl = LIST_FIRST(&nick_bans.hash_list[a]);
+      ic[0] += count_list(bl, &tmpim);
+      im[0] += tmpim;
+
+      bl = LIST_FIRST(&chan_bans.hash_list[a]);
+      ic[2] += count_list(bl, &tmpim);
+      im[2] += tmpim;
+
+      bl = LIST_FIRST(&gcos_bans.hash_list[a]);
+      ic[4] += count_list(bl, &tmpim);
+      im[4] += tmpim;
+   }
+
+   for(a = 0; a < 16; a++)
+   {
+      ict += ic[a];
+      imt += im[a];
+   }
+
+   sendto_one(cptr, ":%s %d %s :SimBans %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ict, imt);
+
+   sendto_one(cptr, ":%s %d %s :  Nick %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[0], im[0]);
+   sendto_one(cptr, ":%s %d %s :  Nick wild %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[1], im[1]);
+   sendto_one(cptr, ":%s %d %s :  Chan %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[2], im[2]);
+   sendto_one(cptr, ":%s %d %s :  Chan wild %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[3], im[3]);
+   sendto_one(cptr, ":%s %d %s :  GCOS %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[4], im[4]);
+   sendto_one(cptr, ":%s %d %s :  GCOS wild %d(%d)",
+              me.name, RPL_STATSDEBUG, cptr->name, ic[5], im[5]);
+
+   return imt;
+}
+
diff --git a/src/version.c.SH b/src/version.c.SH
new file mode 100755 (executable)
index 0000000..b83d923
--- /dev/null
@@ -0,0 +1,124 @@
+#!/bin/sh
+spitshell=cat
+package=IRC
+
+echo "Extracting $package/src/version.c..."
+
+if test -r version.c.last
+then
+       generation=`sed -n 's/^char \*generation = \"\(.*\)\";/\1/p' < version.c.last`
+if test ! "$generation" ; then generation=0; fi
+else
+       generation=0
+fi
+
+generation=`expr $generation + 1`
+
+uname=`uname -a`
+
+creation=`date | \
+awk '{if (NF == 6) \
+{ print $1 " "  $2 " " $3 " "  $6 " at " $4 " " $5 } \
+else \
+{ print $1 " "  $2 " " $3 " " $7 " at " $4 " " $5 " " $6 }}'`
+
+$spitshell >version.c <<!SUB!THIS!
+/*
+ *   IRC - Internet Relay Chat, src/version.c
+ *   Copyright (C) 1990 Chelsea Ashley Dyerman
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file is generated by version.c.SH. Any changes made will go away.
+ */
+
+#include "struct.h"
+#include "patchlevel.h"
+
+char *generation = "$generation";
+char *creation = "$creation";
+char version[128];
+
+char *infotext[] =
+    {
+        "$package --",
+        "Based on the original code written by Jarkko Oikarinen",
+        "Copyright 1988, 1989, 1990, 1991 University of Oulu, Computing Center",
+        "",
+        "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 1, or",
+        "(at your option) any later version.",
+        "",
+     "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.",
+     "",
+        "Send bug reports to dalnet-src@dal.net",
+        "",
+        "The following people currently compose the bahamut team and",
+     "actively maintain the bahamut codebase:",
+        "epiphani            Aaron Wiebe         epiphani@dal.net",
+     "Quension            Trevor Talbot       quension@dal.net",
+     "sedition            David Parton        sedition@dal.net",
+        "",
+        "The following people have contributed code or support",
+        "to the bahamut team:",
+        "aClient             Diane Bruce         db@koruna.varner.com",
+        "Dakal               David Knepper       dakal@dal.net",
+        "Dalvenjah           Sven Nielsen        dalvenjah@dal.net",
+        "MSofty              Mark Salerno        msofty@dal.net",
+     "Raistlin_Majere     Jason Slagle        raistlin@bahamut.net",
+        "Rakarra             Ian Westcott        rakarra@dal.net",
+     "lucas               Lucas Madar         lucas@dal.net",
+        "srd                 Sean McKay          srd@powertrip.net",
+        "White_Dragon        Chip Norkus         wd@dal.net",
+     "xPsycho             Ryan Smith          xpsycho@dal.net",
+        "",
+        "The following people have contributed ideas or testing to the",
+        "bahamut project:",
+        "driz                David Friedman      driz@dal.net",
+        "drone               Mike Wilson         drone@dal.net",
+        "habit               Melinda Thompson    habit@dal.net",
+        "Kevin               Kevin Turner        kevin@dal.net",
+        "",
+        "",
+        "Developers and Contributors for previous versions:",
+        "Chris A. Bongaarts  Chris Behrens       Diane Bruce",
+        "Johannes Erdfelt    Roger Espel Llima   Jon Lusky",
+        "Michael Pearce      Brian Kraemer       Joan Touzet",
+        "Chris Portman       Keith Fralick       Sam Noble",
+        "Taner Halicioglu    Chris Behrens       Darren Reed",
+        "Markku Savela       Greg Lindahl        Jarkko Oikarinen",
+        "Armin Gruner        Matthew Green       Chuck Kane",
+        "Matt Lyle           Vesa Ruokonen       Nicolas PIOCH",
+        "Stellan Klebom      Dan Goodwin         Mike Bolotski",
+        "Ian Frechette       Markku Jarvinen     Kimmo Suominen",
+        "Jeff Trim           Vijay Subramaniam   Karl Kleinpaste",
+        "Bill Wisner         Tom Davis           Hugo Calendar",
+        "Tom Hopkins         Stephen van den Berg",
+        "Bo Adler            Michael Sandrof     Jon Solomon",
+        "Jan Peterson        Helen Rose          Paul Graham",
+        "",
+        "Thanks also goes to those persons not mentioned here who have added",
+        "their advice, opinions, and code to IRC.",
+        "Thanks also to those who provide the kind sys admins who let me and",
+        "others continue to develop IRC.",
+        "",
+        0,
+};
+!SUB!THIS!
diff --git a/src/whowas.c b/src/whowas.c
new file mode 100644 (file)
index 0000000..b6ff658
--- /dev/null
@@ -0,0 +1,221 @@
+/************************************************************************
+*   IRC - Internet Relay Chat, src/whowas.c
+*   Copyright (C) 1990 Markku Savela
+*
+*   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* $Id: whowas.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "h.h"
+
+/* externally defined functions */
+unsigned int hash_whowas_name(char *); /* defined in hash.c */
+/* internally defined function */
+static void add_whowas_to_clist(aWhowas **, aWhowas *);
+static void del_whowas_from_clist(aWhowas **, aWhowas *);
+static void add_whowas_to_list(aWhowas **, aWhowas *);
+static void del_whowas_from_list(aWhowas **, aWhowas *);
+
+aWhowas     WHOWAS[NICKNAMEHISTORYLENGTH];
+aWhowas    *WHOWASHASH[WW_MAX];
+
+int         whowas_next = 0;
+
+void add_history(aClient *cptr, int online)
+{
+    aWhowas    *new;
+    
+    new = &WHOWAS[whowas_next];
+
+    if (new->hashv != -1) 
+    {
+       if (new->online)
+           del_whowas_from_clist(&(new->online->whowas), new);
+       del_whowas_from_list(&WHOWASHASH[new->hashv], new);
+    }
+    new->hashv = hash_whowas_name(cptr->name);
+    new->logoff = NOW;
+    strncpyzt(new->name, cptr->name, NICKLEN + 1);
+    strncpyzt(new->username, cptr->user->username, USERLEN + 1);
+    strncpyzt(new->hostname, cptr->user->host, HOSTLEN);
+    strncpyzt(new->realname, cptr->info, REALLEN);
+    /*
+     * Its not string copied, a pointer to the scache hash is copied
+     * -Dianora
+     */
+    new->servername = cptr->user->server;
+    new->umode = cptr->umode;
+
+    if (online) 
+    {
+       new->online = cptr;
+       add_whowas_to_clist(&(cptr->whowas), new);
+    }
+    else
+       new->online = NULL;
+    add_whowas_to_list(&WHOWASHASH[new->hashv], new);
+    whowas_next++;
+    if (whowas_next == NICKNAMEHISTORYLENGTH)
+       whowas_next = 0;
+}
+
+void off_history(aClient *cptr)
+{
+    aWhowas    *temp, *next;
+    
+    for (temp = cptr->whowas; temp; temp = next) 
+    {
+       next = temp->cnext;
+       temp->online = NULL;
+       del_whowas_from_clist(&(cptr->whowas), temp);
+    }
+}
+
+aClient *get_history(char *nick, time_t timelimit)
+{
+    aWhowas    *temp;
+    int         blah;
+
+    timelimit = NOW - timelimit;
+    blah = hash_whowas_name(nick);
+    temp = WHOWASHASH[blah];
+    for (; temp; temp = temp->next)
+    {
+       if (mycmp(nick, temp->name))
+           continue;
+       if (temp->logoff < timelimit)
+           continue;
+       return temp->online;
+    }
+    return NULL;
+}
+
+/*
+ * m_whowas 
+ * parv[0] = sender prefix
+ * parv[1] = nickname queried
+ */
+int m_whowas(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+    aWhowas *temp;
+    int cur = 0;
+    int         max = -1, found = 0;
+    char       *p, *nick, *s;
+
+    if (parc < 2) 
+    {
+       sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN),
+                  me.name, parv[0]);
+       return 0;
+    }
+    if (parc > 2)
+       max = atoi(parv[2]);
+    if (parc > 3)
+       if (hunt_server(cptr, sptr, ":%s WHOWAS %s %s :%s", 3, parc, parv))
+           return 0;
+
+    parv[1] = canonize(parv[1]);
+    if (!MyConnect(sptr) && (max > 20))
+       max = 20;
+    for (s = parv[1]; (nick = strtoken(&p, s, ",")); s = NULL) 
+    {
+       temp = WHOWASHASH[hash_whowas_name(nick)];
+       found = 0;
+       for (; temp; temp = temp->next) 
+       {
+           if (!mycmp(nick, temp->name)) 
+           {
+               sendto_one(sptr, rpl_str(RPL_WHOWASUSER),
+                          me.name, parv[0], temp->name,
+                          temp->username,
+                          temp->hostname,
+                          temp->realname);
+               if((temp->umode & UMODE_I) && !IsAnOper(sptr))
+                   sendto_one(sptr, rpl_str(RPL_WHOISSERVER),
+                              me.name, parv[0], temp->name,
+                              HIDDEN_SERVER_NAME, myctime(temp->logoff));
+               else
+                   sendto_one(sptr, rpl_str(RPL_WHOISSERVER),
+                              me.name, parv[0], temp->name,
+                              temp->servername, myctime(temp->logoff));
+               cur++;
+               found++;
+           }
+           if (max > 0 && cur >= max)
+               break;
+       }
+       if (!found)
+           sendto_one(sptr, err_str(ERR_WASNOSUCHNICK),
+                      me.name, parv[0], nick);
+       if (p)
+           p[-1] = ',';
+    }
+    sendto_one(sptr, rpl_str(RPL_ENDOFWHOWAS), me.name, parv[0], parv[1]);
+    return 0;
+}
+
+void initwhowas()
+{
+    int i;
+
+    for (i = 0; i < NICKNAMEHISTORYLENGTH; i++)
+    {
+       memset((char *) &WHOWAS[i], '\0', sizeof(aWhowas));
+       WHOWAS[i].hashv = -1;
+    }
+    for (i = 0; i < WW_MAX; i++)
+       WHOWASHASH[i] = NULL;
+}
+
+static void add_whowas_to_clist(aWhowas ** bucket, aWhowas * whowas)
+{
+    whowas->cprev = NULL;
+    if ((whowas->cnext = *bucket) != NULL)
+       whowas->cnext->cprev = whowas;
+    *bucket = whowas;
+}
+
+static void del_whowas_from_clist(aWhowas ** bucket, aWhowas * whowas)
+{
+    if (whowas->cprev)
+       whowas->cprev->cnext = whowas->cnext;
+    else
+       *bucket = whowas->cnext;
+    if (whowas->cnext)
+       whowas->cnext->cprev = whowas->cprev;
+}
+
+static void add_whowas_to_list(aWhowas ** bucket, aWhowas * whowas)
+{
+    whowas->prev = NULL;
+    if ((whowas->next = *bucket) != NULL)
+       whowas->next->prev = whowas;
+    *bucket = whowas;
+}
+
+static void del_whowas_from_list(aWhowas ** bucket, aWhowas * whowas)
+{
+    if (whowas->prev)
+       whowas->prev->next = whowas->next;
+    else
+       *bucket = whowas->next;
+    if (whowas->next)
+       whowas->next->prev = whowas->prev;
+}
diff --git a/src/zlink.c b/src/zlink.c
new file mode 100644 (file)
index 0000000..b2bbc51
--- /dev/null
@@ -0,0 +1,278 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, src/zlink.c
+ *   Copyright (C) 2000 Lucas Madar
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: zlink.c,v 1.1 2005/01/12 07:44:57 mmondor Exp $ */
+
+/*
+ * This streaming ircd zlib implementation was
+ * inspired mostly by dianora's example in hybrid-6
+ * - lucas
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "zlib.h"
+
+#define COMPRESSION_LEVEL      3       /* 0 to 9, 0 = none */
+#define ZIP_MIN_BLOCK          1024    /* smallest block to compress */
+#define ZIP_MAX_BLOCK          8192    /* largest block to compress */
+
+/*
+ * This shouldn't be necessary.
+ * The outbuf should never be larger than 
+ * the maximum block.. should it?
+ * I'll account for any weirdness in zlib.
+ *
+ * WARNING:
+ * Please be aware that if you are using both encryption
+ * and ziplinks, rc4buf in send.c MUST be the same size
+ * as zipOutBuf in zlink.c!
+ */
+#define zipOutBufSize (ZIP_MAX_BLOCK * 2)
+static char zipOutBuf[zipOutBufSize];
+
+/*
+ * 64k overflowed every now and then.
+ * This isn't that important, an overflow is 
+ * non-fatal, but causes more calls to deflate()
+ * 96k seems to overflow a lot now.
+ */
+#define zipInBufSize (131072) /* 128K */
+static char zipInBuf[zipInBufSize];
+
+/* opaque "out" data structure */
+struct zipped_link_out 
+{
+    z_stream    stream;             /* zip stream data */
+    char        buf[ZIP_MAX_BLOCK]; /* zipped buffer */
+    int         bufsize;            /* size of inbuf content */
+};
+
+/* opaque "in" data structure */
+struct zipped_link_in 
+{
+    z_stream    stream;             /* zip stream data */
+};
+
+/* returns a pointer to a setup opaque input session */
+void *zip_create_input_session()
+{
+    struct zipped_link_in *zip;
+
+    zip = (struct zipped_link_in *) malloc(sizeof(struct zipped_link_in));
+
+    memset(zip, 0, sizeof(struct zipped_link_in));
+
+    zip->stream.zalloc = NULL;
+    zip->stream.zfree = NULL;
+    zip->stream.data_type = Z_ASCII;
+
+    if(inflateInit(&zip->stream) != Z_OK)
+       return NULL;
+
+    return (void *) zip;
+}
+
+/* returns a pointer to an opaque output session */
+void *zip_create_output_session()
+{
+    struct zipped_link_out *zip;
+
+    zip = (struct zipped_link_out *) malloc(sizeof(struct zipped_link_out));
+
+    memset(zip, 0, sizeof(struct zipped_link_out));
+
+    zip->stream.zalloc = NULL;
+    zip->stream.zfree = NULL;
+    zip->stream.data_type = Z_ASCII;
+
+    if(deflateInit(&zip->stream, COMPRESSION_LEVEL) != Z_OK)
+       return NULL;
+
+    return (void *) zip;
+}
+
+/*
+ * zip_input()
+ *
+ * session - opaque in-session pointer
+ * buffer - compressed buffer
+ * len - length of buffer (will change)
+ * err - numeric error if length is -1 on return
+ * nbuf - set if this function needs to be called again
+ * nbuflen - if nbuf is set, length to call with again.
+ *  -- nbuf, if set, should call zip_input when done processing
+ *     first return, with buffer set to nbuf.
+ * returns:
+ * len > -1:
+ *   compressed data
+ * len == -1:
+ *   error message
+ */
+char *zip_input(void *session, char *buffer, int *len, int *err,
+               char **nbuf, int *nbuflen)
+{
+    struct zipped_link_in *z = (struct zipped_link_in *) session;
+    z_stream *zin = &z->stream;
+    int ret;
+
+    *nbuf = NULL;
+    *err = 0;
+
+    zin->next_in = buffer;
+    zin->avail_in = *len;
+    zin->next_out = zipInBuf;
+    zin->avail_out = zipInBufSize;   
+
+    ret = inflate(zin, Z_SYNC_FLUSH);
+
+    switch(ret)
+    {
+    case Z_OK:
+        if(zin->avail_in) /* grrr, didn't take all the input */
+        {
+           if(zin->avail_out != 0) /* but there was still output left??? */
+           {
+               *len = -1;
+               return zin->msg ? zin->msg : "????";
+           }
+           *nbuf = zin->next_in;
+           *nbuflen = zin->avail_in;
+           *len = zipInBufSize - zin->avail_out;
+           return zipInBuf;
+        }
+        else
+        {
+           *len = zipInBufSize - zin->avail_out;
+           return zipInBuf;
+        }
+
+    default:
+       *len = -1;
+       *err = ret;
+       return zin->msg ? zin->msg : "????";
+    }
+}
+
+/* returns the amount of data waiting in the outgoing buffer */
+int zip_is_data_out(void *session)
+{
+    struct zipped_link_out *z = (struct zipped_link_out *) session;
+
+    return z->bufsize;
+}
+
+/*
+ * zip_output():
+ * session is opaque session pointer.
+ * buffer is buffer to compress.
+ * len is length of buffer, will change.
+ * forceflush forces inflate to return a buffer, even if it has
+ * not optimally compressed something.
+ * Largedata should be nonzero during a split.
+ * largedata is also an error number, it is set if len is -1.
+ * if len is -1, returns null terminated error string.
+ */
+char *zip_output(void *session, char *buffer, int *len,
+                int forceflush, int *largedata)
+{
+    struct zipped_link_out *z = (struct zipped_link_out *) session;
+    z_stream *zout = &z->stream;
+    int ret;
+
+    if(buffer)
+    {
+       memcpy(z->buf + z->bufsize, buffer, *len);
+       z->bufsize += *len;
+    }
+
+    if( !forceflush && ((z->bufsize < ZIP_MIN_BLOCK) || 
+                       (largedata && (z->bufsize < (ZIP_MAX_BLOCK - 512)))))
+    {
+       *len = 0;
+       return NULL;
+    }
+    
+    zout->next_in = z->buf;
+    zout->avail_in = z->bufsize;
+    zout->next_out = zipOutBuf;
+    zout->avail_out = zipOutBufSize;
+   
+    /*
+     * We do our own internal buffering,
+     * so flush all the time.
+     */
+    ret = deflate(zout, Z_SYNC_FLUSH);
+
+    if(ret == Z_OK)
+    {
+       z->bufsize = 0;
+       *len = zipOutBufSize - zout->avail_out;
+       return zipOutBuf;
+    }   
+
+    *len = -1;
+    *largedata = ret;
+    return zout->msg ? zout->msg : "???";
+}
+
+/* if *insiz is zero, there are no stats available for this session. */
+void zip_out_get_stats(void *session, unsigned long *insiz,
+                      unsigned long *outsiz, double *ratio)
+{
+    struct zipped_link_out *z = (struct zipped_link_out *) session;
+    
+    *insiz = z->stream.total_in;
+    *outsiz = z->stream.total_out;
+
+    if(*insiz)
+       *ratio = ((100.0 * (double)z->stream.total_out) /
+                 (double) z->stream.total_in);
+}
+
+void zip_in_get_stats(void *session, unsigned long *insiz, 
+                     unsigned long *outsiz, double *ratio)
+{
+   struct zipped_link_in *z = (struct zipped_link_in *) session;
+
+   *insiz = z->stream.total_in;
+   *outsiz = z->stream.total_out;
+
+   if(*outsiz)
+       *ratio = ((100.0 * (double)z->stream.total_in) / 
+                 (double) z->stream.total_out);
+}
+
+void zip_destroy_output_session(void *session)
+{
+    struct zipped_link_out *z = (struct zipped_link_out *) session;
+
+    deflateEnd(&z->stream);
+    free(session);
+}
+
+void zip_destroy_input_session(void *session)
+{
+    struct zipped_link_in *z = (struct zipped_link_in *) session;
+
+    inflateEnd(&z->stream);
+    free(session);
+}
+
diff --git a/tools/Makefile.in b/tools/Makefile.in
new file mode 100644 (file)
index 0000000..2c9ca69
--- /dev/null
@@ -0,0 +1,45 @@
+CC=@CC@
+RM=@RM@
+MV=@MV@
+IRCDLIBS=@LIBS@
+INSTALL=@INSTALL@
+INSTALL_BIN=@INSTALL_PROGRAM@
+INSTALL_DIR=@INSTALL_DIR@
+INCLUDEDIR=-I../include
+
+mkpasswd_SOURCES = mkpasswd.c
+mkpasswd_OBJECTS = mkpasswd.o
+convert_conf_SOURCES = convert_conf.c
+convert_conf_OBJECTS = convert_conf.o ../src/match.o
+PROGRAMS = mkpasswd convert_conf
+
+all_OBJECTS = $(mkpasswd_OBJECTS) $(convert_conf_OBJECTS)
+
+all: mkpasswd convert_conf
+
+build: all
+
+mkpasswd: $(mkpasswd_OBJECTS)
+       $(CC) -o mkpasswd $(mkpasswd_OBJECTS) $(IRCDLIBS)
+
+convert_conf: $(convert_conf_OBJECTS)
+       $(CC) -o convert_conf $(convert_conf_OBJECTS) $(IRCDLIBS)
+
+clean:
+       $(RM) -f $(all_OBJECTS) mkpasswd convert_conf *~ core
+
+distclean: clean
+       $(RM) -f Makefile
+
+.c.o:
+       $(CC) $(CFLAGS) $(INCLUDEDIR) -c $<
+
+install:
+       @for i in $(PROGRAMS); do \
+               if test -f $(INSTALL_DIR)/$$i; then \
+                       echo $(MV) $(INSTALL_DIR)/$$i $(INSTALL_DIR)/$$i.old; \
+                       $(MV) $(INSTALL_DIR)/$$i $(INSTALL_DIR)/$$i.old; \
+               fi; \
+               echo $(INSTALL_BIN) $$i $(INSTALL_DIR); \
+               $(INSTALL_BIN) $$i $(INSTALL_DIR); \
+       done
diff --git a/tools/convert_conf.c b/tools/convert_conf.c
new file mode 100644 (file)
index 0000000..f925b20
--- /dev/null
@@ -0,0 +1,1330 @@
+/* tools/convert_conf.c
+ * Copyright (c) 2004, Aaron Wiebe
+ *              and The Bahamut Development Team
+ *
+ *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* $Id: convert_conf.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "inet.h"
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+/* the majority of this is just cut and paste from ircd */
+
+#define MyMalloc(x) malloc(x)
+#define SBAN_LOCAL     0x001
+#define SBAN_NETWORK   0x002
+#define SBAN_NICK      0x004   /* sban on the nick field */
+#define SBAN_GCOS      0x008   /* sban on the gcos field */
+#define SBAN_CHAN      0x010   /* sban on the chname field */
+#define SBAN_WILD      0x020   /* sban mask contains wildcards */
+#define SBAN_TEMPORARY 0x040   /* sban is temporary */
+
+#define MAX_USERVERS 32
+
+aConnect   *connects  = ((aConnect *) NULL);    /* connects, C/N pairs  */
+aAllow     *allows    = ((aAllow *) NULL);  /* allows  - I lines    */
+Conf_Me    *MeLine    = ((Conf_Me *) NULL); /* meline - only one    */
+aOper      *opers     = ((aOper *) NULL);   /* opers - Olines   */
+aPort      *ports     = ((aPort *) NULL);   /* ports - P/M lines    */
+aClass     *classes = NULL;
+char       *uservers[MAX_USERVERS];
+
+typedef struct _asimban SimBan;
+typedef struct _ahostban HostBan;
+
+struct _asimban
+{
+    int type;
+    char *reason;
+    char *target;
+    SimBan *next;
+};
+
+struct _ahostban
+{
+    char *username;
+    char *reason;
+    char *target;
+    HostBan *next;
+};
+
+SimBan *sbans = NULL;
+HostBan *hbans = NULL;
+int  do_tline = 0;
+char ProxyMonHost[HOSTLEN+1];
+char ProxyMonURL[TOPICLEN+1];
+
+
+int dgets(int fd, char *buf, int num)
+{
+    static char dgbuf[8192];
+    static char *head = dgbuf, *tail = dgbuf;
+    char *s, *t;
+    int n, nr;
+
+    /* Sanity checks. */
+    if (head == tail)
+    *head = '\0';
+
+    if (!num)
+    {
+    head = tail = dgbuf;
+    *head = '\0';
+    return 0;
+    }
+
+    if (num > sizeof(dgbuf) - 1)
+    num = sizeof(dgbuf) - 1;
+
+    FOREVER
+    {
+    if (head > dgbuf)
+    {
+        for (nr = tail - head, s = head, t = dgbuf; nr > 0; nr--)
+        *t++ = *s++;
+        tail = t;
+        head = dgbuf;
+    }
+    /* check input buffer for EOL and if present return string. */
+    if (head < tail &&
+        ((s = strchr(head, '\n')) ||
+         (s = strchr(head, '\r'))) && s < tail)
+    {
+        n = MIN(s - head + 1, num); /* at least 1 byte */
+        memcpy(buf, head, n);
+        head += n;
+        if (head == tail)
+        head = tail = dgbuf;
+        return n;
+    }
+
+
+    if (tail - head >= num)
+    {      /* dgets buf is big enough */
+        n = num;
+        memcpy(buf, head, n);
+        head += n;
+        if (head == tail)
+        head = tail = dgbuf;
+        return n;
+    }
+
+    n = sizeof(dgbuf) - (tail - dgbuf) - 1;
+    nr = read(fd, tail, n);
+    if (nr == -1)
+    {
+        head = tail = dgbuf;
+        return -1;
+    }
+
+    if (!nr)
+    {
+        if (tail > head)
+        {
+        n = MIN(tail - head, num);
+        memcpy(buf, head, n);
+        head += n;
+        if (head == tail)
+            head = tail = dgbuf;
+        return n;
+        }
+        head = tail = dgbuf;
+        return 0;
+    }
+
+    tail += nr;
+    *tail = '\0';
+
+    for (t = head; (s = strchr(t, '\n'));)
+    {
+        if ((s > head) && (s > dgbuf))
+        {
+        t = s - 1;
+        for (nr = 0; *t == '\\'; nr++)
+            t--;
+        if (nr & 1)
+        {
+            t = s + 1;
+            s--;
+            nr = tail - t;
+            while (nr--)
+            *s++ = *t++;
+            tail -= 2;
+            *tail = '\0';
+            }
+        else
+            s++;
+        }
+        else
+        s++;
+        t = s;
+    }
+    *tail = '\0';
+    }
+}
+
+
+char *getfield(char *newline)
+{
+    static char *line = (char *) NULL;
+    char       *end, *field;
+
+    if (newline)
+    line = newline;
+
+    if (line == (char *) NULL)
+    return ((char *) NULL);
+
+    field = line;
+    if ((end = strchr(line, ':')) == NULL)
+    {
+    line = (char *) NULL;
+    if ((end = strchr(field, '\n')) == (char *) NULL)
+        end = field + strlen(field);
+    }
+    else
+    line = end + 1;
+    *end = '\0';
+    return (field);
+}
+
+
+aClass *make_class()
+{
+    aClass *tmp;
+
+    tmp = (aClass *) MyMalloc(sizeof(aClass));
+    return tmp;
+}
+
+void free_class(tmp)
+    aClass *tmp;
+{
+    MyFree(tmp);
+}
+
+aOper *make_oper()
+{
+    aOper *i;
+    i = (struct Conf_Oper *) MyMalloc(sizeof(aOper));
+    memset((char *) i, '\0', sizeof(aOper));
+    return i;
+}
+
+SimBan *make_simban()
+{
+    SimBan *i;
+    i = (struct _asimban *) MyMalloc(sizeof(SimBan));
+    memset((char *) i, '\0', sizeof(SimBan));
+    return i;
+}
+
+HostBan *make_hostban()
+{
+    HostBan *i;
+    i = (struct _ahostban *) MyMalloc(sizeof(HostBan));
+    memset((char *) i, '\0', sizeof(HostBan));
+    return i;
+}
+
+aConnect *make_connect()
+{
+    aConnect *i;
+    i = (struct Conf_Connect *) MyMalloc(sizeof(aConnect));
+    memset((char *) i, '\0', sizeof(aConnect));
+    return i;
+}
+
+aAllow *make_allow()
+{
+    aAllow *i;
+    i = (struct Conf_Allow *) MyMalloc(sizeof(aAllow));
+    memset((char *) i, '\0', sizeof(aAllow));
+    return i;
+}
+
+aPort *make_port()
+{
+    aPort *i;
+    i = (struct Conf_Port *) MyMalloc(sizeof(aPort));
+    memset((char *) i, '\0', sizeof(aPort));
+    return i;
+}
+
+Conf_Me *make_me()
+{
+    Conf_Me *i;
+    i = (struct Conf_Me *) MyMalloc(sizeof(Conf_Me));
+    memset((char *) i, '\0', sizeof(Conf_Me));
+    return i;
+}
+
+/* find the appropriate conf and return it */
+
+aConnect *
+find_aConnect(char *name)
+{
+    aConnect *tmp;
+    for(tmp = connects; tmp; tmp = tmp->next)
+        if(!match(name, tmp->name))
+            break;
+    return tmp;
+}
+
+aPort *
+find_port(int port)
+{
+    aPort *tmp;
+    for(tmp = ports; tmp; tmp = tmp->next)
+        if(tmp->port == port)
+            break;
+    return tmp;
+}
+
+aConnect *
+find_aConnect_match(char *name, char *username, char *host)
+{
+    aConnect *aconn;
+    char userhost[USERLEN + HOSTLEN + 3];
+
+    (void) sprintf(userhost, "%s@%s", username, host);
+
+    for(aconn = connects; aconn; aconn = aconn->next)
+        if(!mycmp(name, aconn->name) && !match(userhost, aconn->host))
+            break;
+    return aconn;
+}
+
+char *
+find_aUserver(char *name)
+{
+    int i;
+    
+    for (i = 0; uservers[i]; ++i)
+        if (mycmp(name, uservers[i]) == 0)
+            break;
+    return uservers[i];
+}
+
+aOper *
+find_oper(char *name, char *username, char *sockhost, char *hostip)
+{
+    aOper *aoper;
+    char userhost[USERLEN + HOSTLEN + 3];
+    char userip[USERLEN + HOSTLEN + 3];
+    int i;
+
+    /* sockhost OR hostip must match our host field */
+
+
+    (void) sprintf(userhost, "%s@%s", username, sockhost);
+    (void) sprintf(userip, "%s@%s", username, sockhost);
+
+    for(aoper = opers; aoper; aoper = aoper->next)
+    {
+        for (i = 0; aoper->hosts[i]; ++i)
+        {
+            if(!(mycmp(name, aoper->nick) && (match(userhost, aoper->hosts[i]) ||
+                 match(userip, aoper->hosts[i]))))
+                break;
+        }
+        if (aoper->hosts[i]) break;
+    }
+    return aoper;
+}
+
+aOper *
+find_oper_byname(char *name)
+{
+    aOper *aoper;
+    for(aoper = opers; aoper; aoper = aoper->next)
+        if(!mycmp(name, aoper->nick))
+            break;
+    return aoper;
+}
+
+aClass *
+find_class(char *name)
+{
+    aClass *tmp;
+    for(tmp = classes; tmp; tmp = tmp->next)
+        if(!mycmp(name, tmp->name))
+            break;
+    return tmp;
+}
+
+
+/* confadd_ functions
+ * add a config item
+ * Feb.15/04 -epi
+ */
+static int oper_access[] =
+{
+    ~(OFLAG_ADMIN|OFLAG_SADMIN|OFLAG_ZLINE|OFLAG_ADMIN), '*',
+    OFLAG_LOCAL,   'o',
+    OFLAG_GLOBAL,  'O',
+    OFLAG_REHASH,  'r',
+    OFLAG_DIE,     'D',
+    OFLAG_RESTART, 'R',
+    OFLAG_GLOBOP,  'h',
+    OFLAG_WALLOP,  'w',
+    OFLAG_LOCOP,   'l',
+    OFLAG_LROUTE,  'c',
+    OFLAG_GROUTE,  'C',
+    OFLAG_LKILL,   'k',
+    OFLAG_GKILL,   'K',
+    OFLAG_KLINE,   'b',
+    OFLAG_UNKLINE, 'B',
+    OFLAG_LNOTICE, 'n',
+    OFLAG_GNOTICE, 'N',
+    OFLAG_ADMIN,   'A',
+    OFLAG_SADMIN,  'a',
+    OFLAG_UMODEc,  'u',
+    OFLAG_UMODEf,  'f',
+    OFLAG_ZLINE,   'z',
+    OFLAG_UMODEF,  'F',
+    0, 0 };
+
+
+void
+confadd_oper(char *name, char *host, char *passwd, char *flags, char *class)
+{
+    aOper *x;
+    int new, hostidx;
+    
+    if (!strchr(host, '@') && *host != '/')
+    {
+        char       *newhost;
+        int         len = 3;
+        len += strlen(host);
+        newhost = (char *) MyMalloc(len);
+        (void) sprintf(newhost, "*@%s", host);
+        host = newhost;
+    }
+
+
+    if((x = find_oper_byname(name)))
+    {
+        new = 0;
+        for (hostidx = 0; x->hosts[hostidx]; ++hostidx)
+        {
+            if (mycmp(x->hosts[hostidx], host) == 0)
+                break;
+        }
+        if (x->hosts[hostidx] == NULL)
+        {
+            DupString(x->hosts[hostidx], host);
+            x->hosts[hostidx+1] = NULL;
+        }
+    }
+    else
+    {
+        x = make_oper();
+        DupString(x->nick, name);
+        DupString(x->passwd, passwd);
+        DupString(x->hosts[0], host);
+        x->hosts[1] = NULL;
+        new = 1;
+    }
+    x->legal = 1;
+    /* this kludge is just to get the string across.. *shrug* */
+    DupString(x->class_name, flags);
+    if(class)
+        x->class = find_class(class);
+    if(new)
+    {
+        x->next = opers;
+        opers = x;
+    }
+    return;
+}
+
+static int server_info[] =
+{
+    CONN_ZIP, 'Z',
+    CONN_DKEY, 'E',
+    CONN_HUB, 'H',
+    0, 0
+};
+
+void
+confadd_connect(char *name, char *host, char *apasswd, char *cpasswd,
+                int port, char *flags, char *source, char *class)
+{
+    aConnect *x;
+    int *i, flag, new = 0;
+    char *m = "*";
+
+    if(!(x = find_aConnect(name)))
+    {
+        x = make_connect();
+        DupString(x->name, name);
+        x->port = 0;
+        new = 1;
+    }
+    x->legal = 1;
+    if(host)
+    {
+        MyFree(x->host);
+        DupString(x->host, host);
+        if (!strchr(x->host, '@') && *x->host != '/')
+        {
+            char       *newhost;
+            int         len = 3;
+            len += strlen(x->host);
+            newhost = (char *) MyMalloc(len);
+            (void) sprintf(newhost, "*@%s", x->host);
+            MyFree(x->host);
+            x->host = newhost;
+        }
+    }
+    if(class)
+        x->class = find_class(class);
+    if(port)
+        x->port = port;
+    if(apasswd)
+    {
+        MyFree(x->apasswd);
+        DupString(x->apasswd, apasswd);
+    }
+    if(cpasswd)
+    {
+        MyFree(x->cpasswd);
+        DupString(x->cpasswd, cpasswd);
+    }
+    if(flags)
+    {
+        x->flags = 0;
+        for (m=(*flags) ? flags : m; *m; m++)
+        {
+            for (i=server_info; (flag = *i); i+=2)
+            if (*m==(char)(*(i+1)))
+            {
+                x->flags |= flag;
+                break;
+            }
+        }
+    }
+    if(source)
+    {
+        int dots;
+        char *n = source;
+        while(*n)
+        {
+            if(*n == '.')
+                dots++;
+            n++;
+        }
+        if(dots == 3)       /* make sure we dont add a class, since that
+                             * worked in the old method */
+        {
+            MyFree(x->source);
+            DupString(x->source, source);
+        }
+    }
+    if(new)
+    {
+        x->next = connects;
+        connects = x;
+    }
+    return;
+}
+
+void
+confadd_allow(char *ipmask, char *passwd, char *hostmask, int port, char *class)
+{
+    aAllow *x;
+    /* Currently, Allows are the only config types without
+     * easy identifiers - so we dont worry about duplicate types.
+     * this will change once a new config parser is written
+     * -epi
+     */
+
+    x = make_allow();
+    if(ipmask)
+        DupString(x->ipmask, ipmask);
+    if(passwd && passwd != "")
+        DupString(x->passwd, passwd);
+    if(hostmask)
+        DupString(x->hostmask, hostmask);
+    if(port)
+        x->port = port;
+    else
+        x->port = 0;
+    if(class)
+        x->class = find_class(class);
+    if(strchr(x->ipmask, '@'))
+        x->flags |= CONF_FLAGS_I_HOST_HAS_AT;
+    if(strchr(x->hostmask, '@'))
+        x->flags |= CONF_FLAGS_I_NAME_HAS_AT;
+#if (RIDICULOUS_PARANOIA_LEVEL>=1)
+    if(myncmp(x->passwd, "oper", 4) == 0)
+    {
+        if((x->passwd[4] == '.') || (x->passwd[4] == '\0'))
+        {
+            char *tmpd = x->passwd;
+            char *tmp = x->passwd + 4;
+
+            x->flags |= CONF_FLAGS_I_OPERPORT;
+            if(*tmp)
+                tmp++;
+            DupString(x->passwd, tmp);
+            MyFree(tmpd);
+        }
+    }
+#endif
+    /* would help if we added it to our list, eh */
+    x->next = allows;
+    allows = x;
+    return;
+}
+
+void
+confadd_port(int port, char *allow, char *address)
+{
+    aPort *x;
+    int    new;
+
+    if(!(port > 0))
+        return;
+
+    if((x = find_port(port)))
+    {
+        MyFree(x->allow);
+        MyFree(x->address);
+        x->legal = 1;
+        new = 0;
+    }
+    else
+    {
+        x = make_port();
+        x->port = port;
+        new = 1;
+    }
+    if(allow)
+        DupString(x->allow, allow);
+    if(address)
+        DupString(x->address, address);
+    if(new)
+    {
+        x->next = ports;
+        ports = x;
+    }
+    return;
+}
+
+void
+confadd_me(char *servername, char *info, char *dpass, char *rpass,
+            char *aline1, char *aline2, char *aline3)
+{
+    if(!MeLine)
+        MeLine = make_me();
+    if(servername)
+    {
+        DupString(MeLine->servername, servername);
+    }
+    if(info)
+    {
+        MyFree(MeLine->info);
+        DupString(MeLine->info, info);
+    }
+    if(aline1)
+    {
+        MyFree(MeLine->admin[0]);
+        DupString(MeLine->admin[0], aline1);
+    }
+    if(aline2)
+    {
+        MyFree(MeLine->admin[1]);
+        DupString(MeLine->admin[1], aline2);
+    }
+    if(aline3)
+    {
+        MyFree(MeLine->admin[2]);
+        DupString(MeLine->admin[2], aline3);
+    }
+    if(dpass)
+    {
+        MyFree(MeLine->diepass);
+        DupString(MeLine->diepass, dpass);
+    }
+    if(rpass)
+    {
+        MyFree(MeLine->restartpass);
+        DupString(MeLine->restartpass, rpass);
+    }
+    return;
+}
+
+void
+confadd_class(char *name, int ping, int connfreq, int maxlinks, long sendq)
+{
+    aClass *x;
+    int new = 0;
+
+    if(!(x = find_class(name)))
+    {
+        x = make_class();
+        DupString(x->name, name);
+        new = 1;
+    }
+    x->pingfreq = ping;
+    x->connfreq = connfreq;
+    x->maxlinks = maxlinks;
+    x->maxsendq = (sendq > 0) ? sendq : MAXSENDQLENGTH;
+    if(new)
+    {
+        x->next = classes;
+        classes = x;
+    }
+    return;
+}
+
+void
+confadd_simban(int flags, char *mask, char *reason)
+{
+    SimBan *x;
+    x = make_simban();
+    x->type = flags;
+    DupString(x->target, mask);
+    if(reason)
+        DupString(x->reason, reason);
+    x->next = sbans;
+    sbans = x;
+    return;
+}
+    
+void
+confadd_hostban(char *username, char *mask, char *reason)
+{
+    HostBan *x;
+    x = make_hostban();
+    if(username)
+        DupString(x->username, username);
+    if(mask)
+       DupString(x->target, mask);
+       if(reason)
+       DupString(x->reason, reason);
+    x->next = hbans;
+    hbans = x;
+    return;
+}
+
+void
+confadd_uline(char *host)
+{
+    int i;
+    
+    if (find_aUserver(host) != NULL)
+        return;
+    
+    for (i = 0; uservers[i]; ++i);
+    
+    DupString(uservers[i], host);
+    uservers[i+1] = NULL;
+}
+    
+
+/*
+ * openconf
+ *
+ * returns -1 on any error or else the fd opened from which to read the
+ * configuration file from.  This may either be the file direct or one
+ * end of a pipe from m4.
+ */
+int openconf(char *filename)
+{
+    return open(filename, O_RDONLY);
+}
+
+/*
+ * initconf()
+ *    Read configuration file.
+ *
+ * - file descriptor pointing to config file to use returns -1,
+ * if file cannot be opened, 0 if file opened
+ * almost completely rewritten when killing aConfItem, feb04 -epi
+ */
+
+#define MAXCONFLINKS 150
+
+int
+initconf(int opt, int fd, aClient *rehasher)
+{
+    static char quotes[9][2] =
+    {
+    {'b', '\b'},
+    {'f', '\f'},
+    {'n', '\n'},
+    {'r', '\r'},
+    {'t', '\t'},
+    {'v', '\v'},
+    {'\\', '\\'},
+    {0, 0}
+    };
+
+    char       *tmp, *s;
+    int         i;
+    char        line[512], c[80];
+
+    /* temp variables just til we complete the rest of the
+     * switch to separate conf structures.  if this is still
+     * here in 2006, find me and beat me up.  -epi
+     * there shouldnt be more than 5 fields per line
+     */
+
+    int     t_status;
+    char    *t_host;
+    char    *t_passwd;
+    char    *t_name;
+    char    *t_flags;
+    char    *t_class;
+
+
+    (void) dgets(-1, NULL, 0);  /* make sure buffer is at empty pos  */
+
+    while ((i = dgets(fd, line, sizeof(line) - 1)) > 0)
+    {
+        line[i] = '\0';
+        if ((tmp = (char *) strchr(line, '\n')))
+            *tmp = '\0';
+        else
+            while (dgets(fd, c, sizeof(c) - 1) > 0)
+                if ((tmp = (char *) strchr(c, '\n')))
+                {
+                    *tmp = '\0';
+                    break;
+                }
+
+        /* Do quoting of characters detection. */
+
+        for (tmp = line; *tmp; tmp++)
+        {
+            if (*tmp == '\\')
+            {
+                for (i = 0; quotes[i][0]; i++)
+                    if (quotes[i][0] == *(tmp + 1))
+                    {
+                        *tmp = quotes[i][1];
+                        break;
+                    }
+                if (!quotes[i][0])
+                    *tmp = *(tmp + 1);
+                if (!*(tmp + 1))
+                    break;
+                else
+                    for (s = tmp; (*s = *(s + 1)); s++);
+            }
+        }
+
+        if (!*line || line[0] == '#' || line[0] == '\n' ||
+                line[0] == ' ' || line[0] == '\t')
+            continue;
+
+        /* Could we test if it's conf line at all?        -Vesa */
+
+        if (line[1] != ':')
+        {
+            printf("\nBad config line: \"%s\" - Ignored\n", line);
+            continue;
+        }
+
+        tmp = getfield(line);
+        if (!tmp)
+            continue;
+        switch (*tmp)
+        {
+            case 'A':
+            case 'a':       /* Administrative info */
+                t_status = CONF_ADMIN;
+                break;
+
+            case 'C':       /* Server I should try to connect */
+            case 'c':
+                t_status = CONF_CONNECT_SERVER;
+                break;
+
+            case 'G':       /* restricted gcos */
+            case 'g':
+                t_status = CONF_GCOS;
+                break;
+
+            case 'H':       /* Hub server line */
+            case 'h':
+                t_status = CONF_HUB;
+                break;
+
+            case 'i':       /* to connect me */
+            case 'I':
+                t_status = CONF_CLIENT;
+                break;
+            case 'K':       /* the infamous klines */
+            case 'k':
+                t_status = CONF_KILL;
+                break;
+
+            /*
+             * Me. Host field is name used for this host
+             * and port number is the number of the port
+             */
+            case 'M':
+            case 'm':
+                t_status = CONF_ME;
+                break;
+
+            case 'N':
+            case 'n':
+
+            /* Server where I should NOT try to
+             * connect in case of lp failures
+             * but which tries to connect ME
+             */
+                t_status = CONF_NOCONNECT_SERVER;
+                break;
+
+            case 'O':       /* Operator line */
+            case 'o':
+                t_status = CONF_OPERATOR;
+                break;
+
+            case 'P':       /* listen port line */
+            case 'p':
+                t_status = CONF_LISTEN_PORT;
+                break;
+
+            case 'Q':       /* restricted nicknames */
+            case 'q':
+                t_status = CONF_QUARANTINE;
+                break;
+
+            case 'T':
+            case 't':
+                t_status = CONF_MONINFO;
+                break;
+
+            case 'U':       /* Ultimate Servers (aka God) */
+            case 'u':
+                t_status = CONF_ULINE;
+                break;
+
+            case 'X':       /* die/restart pass line */
+            case 'x':
+                t_status = CONF_DRPASS;
+                break;
+
+            case 'Y':       /* Class line */
+            case 'y':
+                t_status = CONF_CLASS;
+                break;
+
+            default:
+                t_status = CONF_ILLEGAL;
+                printf("// Bad config line: \"%s\" - Ignored\n", line);
+                break;
+        }
+
+        if(t_status & CONF_ILLEGAL) /* skip this line */
+            continue;
+
+        t_host = getfield(NULL);
+        t_passwd = getfield(NULL);
+        t_name = getfield(NULL);
+        t_flags = getfield(NULL);
+        t_class = getfield(NULL);
+
+        /* from this point, every configuration line
+         * is taken care of within its own if statement.
+         * Everything should be contained. -epi
+         */
+
+        if(t_status & CONF_MONINFO)
+        {
+            if(t_host && t_host[0] != '\0')
+                strncpyzt(ProxyMonHost, t_host, sizeof(ProxyMonHost));
+            strcpy(ProxyMonURL, "http://");
+            if(t_passwd && t_passwd[0] != '\0')
+                strncpyzt(ProxyMonURL, t_passwd, sizeof(ProxyMonURL));
+            do_tline = 1;
+            continue;
+        }
+        if(t_status & CONF_ADMIN)
+        {
+            confadd_me(0,0,0,0, t_host, t_passwd, t_name);
+            continue;
+        }
+        if (t_status & CONF_OPS)
+        {
+            confadd_oper(t_name, t_host, t_passwd, t_flags, t_class);
+            continue;
+        }
+        if(t_status & CONF_NOCONNECT_SERVER)
+        {
+            confadd_connect(t_name, t_host, t_passwd, 0, 0, t_flags, 0,
+                            t_class);
+            continue;
+        }
+        if (t_status & CONF_CONNECT_SERVER)
+        {
+            confadd_connect(t_name, t_host, 0, t_passwd, atoi(t_flags), 0,
+                            t_class, 0);
+            continue;
+        }
+        if(t_status & CONF_HUB)
+        {
+            aConnect *x = find_aConnect(t_name);
+            if(!x)
+                confadd_connect(t_name, 0, 0, 0, 0, t_flags, 0, 0);
+            else
+                x->flags |= CONN_HUB;
+            continue;
+        }
+        if (t_status & CONF_CLASS)
+        {
+            confadd_class(t_host, atoi(t_passwd), atoi(t_name),
+                            atoi(t_flags), atoi(t_class));
+            continue;
+        }
+
+        if (t_status & CONF_CLIENT)
+        {
+            confadd_allow(t_host, t_passwd, t_name, atoi(t_flags), t_class);
+            continue;
+        }
+        if(t_status & CONF_LISTEN_PORT)
+        {
+            confadd_port(atoi(t_flags), t_host, t_passwd);
+            continue;
+        }
+
+        /*
+         * Own port and name cannot be changed after the startup.  (or
+         * could be allowed, but only if all links are closed  first).
+         * Configuration info does not override the name and port  if
+         * previously defined. Note, that "info"-field can be changed
+         * by "/rehash". Can't change vhost mode/address either
+         */
+        if (t_status == CONF_ME)
+        {
+            confadd_me(t_host, t_name, 0, 0, 0, 0, 0);
+            confadd_port(atoi(t_flags), NULL, t_passwd);
+            continue;
+        }
+
+#ifdef WINGATE_NOTICE
+        if (t_status == CONF_MONINFO)
+        {
+            if(!t_host || t_host[0] == '\0')
+                strncpyzt(ProxyMonHost, MONITOR_HOST, sizeof(ProxyMonHost));
+            else
+                strncpyzt(ProxyMonHost, t_host, sizeof(ProxyMonHost));
+
+            strcpy(ProxyMonURL, "http://");
+
+            if(!t_passwd || t_passwd[0] == '\0')
+                strncpyzt((ProxyMonURL + 7), DEFAULT_PROXY_INFO_URL,
+                          sizeof(ProxyMonURL) - 7);
+            else
+                strncpyzt((ProxyMonURL + 7), t_passwd, sizeof(ProxyMonURL) - 7);
+            continue;
+        }
+#endif
+
+        if (t_status & CONF_QUARANTINE)
+        {
+            int flags;
+            char *sb_m, *sb_r;
+
+            if(BadPtr(t_name))
+                continue;
+
+            if(t_name[0] == '#')
+            {
+                flags |= SBAN_CHAN;
+                sb_r = BadPtr(t_passwd) ? "Reserved Channel" : t_passwd;
+            }
+            else
+            {
+                flags |= SBAN_NICK;
+                sb_r = BadPtr(t_passwd) ? "Reserved Nickname" : t_passwd;
+            }
+
+            sb_m = t_name;
+
+            confadd_simban(flags, sb_m, sb_r);
+
+            continue;
+        }
+
+        if (t_status & CONF_GCOS)
+        {
+            unsigned int flags;
+            char *sb_m, *sb_r;
+
+            if(BadPtr(t_name))
+                continue;
+
+            flags = SBAN_LOCAL|SBAN_GCOS;
+            sb_r = BadPtr(t_passwd) ? "Bad GCOS" : t_passwd;
+
+            sb_m = t_name;
+
+            confadd_simban(flags, sb_m, sb_r);
+                continue;
+        }
+
+        if (t_status & CONF_KILL)
+        {
+            char *ub_u, *ub_r;
+
+            if(BadPtr(t_host))
+                continue;
+
+            ub_u = BadPtr(t_name) ? "*" : t_name;
+            ub_r = BadPtr(t_passwd) ? "<No Reason>" : t_passwd;
+
+            confadd_hostban(ub_u, t_host, ub_r);
+
+            continue;
+        }
+        if (t_status & CONF_ULINE)
+        {
+            confadd_uline(t_host);
+            continue;
+        }
+        if(t_status & CONF_DRPASS)
+        {
+            confadd_me(0,0, t_host, t_passwd, 0, 0, 0);
+            continue;
+        }
+        /* oh shit! */
+        printf("Error parsing config file!\n");
+        printf("line: %s", line);
+        exit(-1);
+    }
+    (void) dgets(-1, NULL, 0);  /* make sure buffer is at empty pos */
+    (void) close(fd);
+    return 0;
+}
+
+void
+printconf()
+{
+    /* print off the new config file */
+    aClass *class;
+    aAllow *allow;
+    aOper  *aoper;
+    aConnect *aconn;
+    aPort   *aport;
+    SimBan  *sban;
+    HostBan *hban;
+
+    printf("/* Generated by Bahamut's convert_conf */ \n");
+    printf("\nglobal {\n");
+    printf("    name \"%s\";\n", MeLine->servername);
+    printf("    info \"%s\";\n", MeLine->info);
+    printf("    admin {\n");
+    if(MeLine->admin[0] && strcmp(MeLine->admin[0], ""))
+        printf("        \"%s\";\n", MeLine->admin[0]);
+    if(MeLine->admin[1] && strcmp(MeLine->admin[1], ""))
+        printf("        \"%s\";\n", MeLine->admin[1]);
+    if(MeLine->admin[2] && strcmp(MeLine->admin[2], ""))
+        printf("        \"%s\";\n", MeLine->admin[2]);
+    printf("    };\n");
+    if(MeLine->diepass && MeLine->diepass != "")
+        printf("    dpass \"%s\";\n", MeLine->diepass);
+    if(MeLine->restartpass && MeLine->restartpass != "")
+        printf("    rpass \"%s\";\n", MeLine->restartpass);
+    printf("};\n\n");
+    printf("/* Option Definitions */\n\n");
+    printf("options {\n");
+    printf("    servtype client;  //  MAKE SURE THIS IS CORRECT\n");
+    printf("    network_kline \"kline@dal.net\";\n");
+    printf("    local_kline \"poorly@configure.server\";\n");
+    if(do_tline)
+    {
+        printf("    wgmonhost \"%s\";\n", ProxyMonHost);
+        printf("    wgmonurl \"%s\";\n", ProxyMonURL);
+    }
+    printf("/********* other options worth reviewing *********/\n");
+    printf("#    maxchannels 10;\n");
+    printf("#    short_motd;\n");
+    printf("#    crypt_oper_pass;\n");
+    printf("#    staff_address staff.dalnet;\n");
+    printf("};\n\n");
+    printf("/* Class Definitions */\n\n");
+    for(class = classes; class; class = class->next)
+    {
+        printf("class {\n");
+        printf("    name %s;\n", class->name);
+        printf("    pingfreq %d;\n", class->pingfreq);
+        printf("    maxusers %d;\n", class->maxlinks);
+        if(class->connfreq > 0)
+            printf("    connfreq %d;\n", class->connfreq);
+        printf("    maxsendq %d;\n", class->maxsendq);
+        printf("};\n\n");
+    }
+    printf("/* Allow definitions */\n\n");
+    for(allow = allows; allow; allow = allow->next)
+    {
+        printf("allow {\n");
+        printf("    ipmask \"%s\";\n", allow->ipmask);
+        printf("    host \"%s\";\n", allow->hostmask);
+        if(allow->passwd && strcmp(allow->passwd, ""))
+            printf("    passwd \"%s\";\n", allow->passwd);
+        if(allow->port != 0)
+            printf("    port %d;\n", allow->port);
+        if(allow->class)
+            printf("    class \"%s\";\n", allow->class->name);
+        else
+            printf("    /** YOU SHOULD HAVE A CLASS HERE **/\n");
+        printf("};\n\n");
+    }
+    printf("/* Oper definitions */\n\n");
+    for(aoper = opers; aoper; aoper = aoper->next)
+    {
+        int i;
+        printf("oper {\n");
+        printf("    name \"%s\";\n", aoper->nick);
+        for (i = 0; aoper->hosts[i]; ++i)
+            printf("    host \"%s\";\n", aoper->hosts[i]);
+        printf("    passwd \"%s\";\n", aoper->passwd);
+        printf("    access %s;\n", aoper->class_name); /* kludge! */
+        if(aoper->class)
+            printf("    class \"%s\";\n", aoper->class->name);
+        else
+            printf("    /** YOU SHOULD HAVE A CLASS HERE **/\n");
+        printf("};\n\n");
+    }
+    printf("/* Connection Definitions */\n\n");
+    for(aconn = connects; aconn; aconn = aconn->next)
+    {
+        if(!aconn->host || aconn->host == "")
+            continue;
+        printf("connect {\n", aconn->name);
+        printf("    name \"%s\";\n", aconn->name);
+        printf("    host \"%s\";\n", aconn->host);
+        printf("    apasswd \"%s\";\n", aconn->apasswd);
+        printf("    cpasswd \"%s\";\n", aconn->cpasswd);
+        if(aconn->port > 0)
+            printf("    port %d;\n", aconn->port);
+        if(aconn->source && aconn->source != "")
+            printf("    bind \"%s\";\n", aconn->source);
+        if(aconn->flags != 0)
+            printf("    flags ");
+        if(aconn->flags & CONN_ZIP)
+            printf("Z");
+        if(aconn->flags & CONN_DKEY)
+            printf("E");
+        if(aconn->flags & CONN_HUB)
+            printf("H");
+        if(aconn->flags != 0)
+            printf(";\n");
+        if(aconn->class)
+            printf("    class \"%s\";\n", aconn->class->name);
+        else
+            printf("    /** YOU SHOULD HAVE A CLASS HERE **/\n");
+        printf("};\n\n");
+    }
+    if (uservers[0])
+    {
+        int i;
+        printf("/* Superservers */\n\n");
+        printf("super {\n");
+        for (i = 0; uservers[i]; ++i)
+        {
+            if (i != 0) printf(";\n");
+            printf("    %s", uservers[i]);
+        }
+        printf(";\n};\n\n");
+    }
+    printf("/* port configurations */\n\n");
+    for(aport = ports; aport; aport = aport->next)
+    {
+        printf("port {\n");
+        printf("    port %d;\n", aport->port);
+        if(aport->allow && strcmp(aport->allow, "") && aport->allow != "*")
+            printf("    ipmask \"%s\";\n", aport->allow);
+        if(aport->address && strcmp(aport->address, "") && aport->address != "*")
+            printf("    bind \"%s\";\n", aport->address);
+        printf("};\n\n");
+    }
+    if(sbans)
+        printf("/* Quarantines */\n\n");
+    for(sban = sbans; sban; sban = sban->next)
+    {
+        printf("restrict {\n");
+        printf("    type ");
+        if(sban->type & SBAN_NICK)
+            printf("NICK;\n");
+        else if(sban->type & SBAN_GCOS)
+            printf("GCOS;\n");
+        else if(sban->type & SBAN_CHAN)
+            printf("CHAN;\n");
+        else
+        {
+            printf("\n\n\nPROPLEM READING QLINE TYPE \n\n");
+            exit(-1);
+        }
+        printf("    mask \"%s\";\n", sban->target);
+        printf("    reason \"%s\";\n", sban->reason);
+        printf("};\n\n");
+    }
+    if(hbans)
+        printf("/* kill definitions */\n\n");
+    for(hban = hbans; hban; hban = hban->next)
+    {
+        printf("kill {\n");
+        printf("    mask \"%s@%s\";\n", hban->username ? hban->username : "*",
+                                        hban->target);
+        printf("    reason \"%s\";\n", hban->reason);
+        printf("};\n\n");
+    }
+    return;
+}
+    
+int main(int argc, char *argv[])
+{
+    char *file;
+    int   fd;
+
+    if(argc != 2)
+    {
+        printf("Useage:  ./convert_conf ircd.conf > ircd.conf.new\n");
+        exit(0);
+    }
+    file = argv[1];
+    
+    if((fd = openconf(file)) == -1)
+    {
+        printf("Cannot locate file %s\n", file);
+        exit(-1);
+    }
+    (void) initconf(0, fd, 0);
+    
+    /* so far so good */
+
+    printconf();
+
+    exit(1);
+}
diff --git a/tools/mkpasswd.c b/tools/mkpasswd.c
new file mode 100644 (file)
index 0000000..d316091
--- /dev/null
@@ -0,0 +1,45 @@
+/* simple password generator by Nelson Minar (minar@reed.edu)
+ * copyright 1991, all rights reserved.
+ * You can use this code as long as my name stays with it.
+ */
+
+#include "sys.h"
+#include <time.h>
+
+#ifndef lint
+/* static char *rcs_version = "$Id: mkpasswd.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $"; */
+#endif
+
+extern char *getpass();
+extern char *crypt();
+/* extern long random(); */
+/* extern int srandom(unsigned); */
+
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+  static char saltChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
+  char salt[3];
+  char * plaintext;
+
+  if (argc < 2) {
+    srandom(time(0));          /* may not be the BEST salt, but its close */
+    salt[0] = saltChars[random() % 64];
+    salt[1] = saltChars[random() % 64];
+    salt[2] = 0;
+  }
+  else {
+    salt[0] = argv[1][0];
+    salt[1] = argv[1][1];
+    salt[2] = '\0';
+    if ((strchr(saltChars, salt[0]) == NULL) || (strchr(saltChars, salt[1]) == NULL))
+      fprintf(stderr, "illegal salt %s\n", salt), exit(1);
+  }
+
+  plaintext = getpass("plaintext: ");
+
+  printf("%s\n", crypt(plaintext, salt));
+  return 0;
+}
+
diff --git a/zlib/ChangeLog b/zlib/ChangeLog
new file mode 100644 (file)
index 0000000..c4c4486
--- /dev/null
@@ -0,0 +1,722 @@
+
+                ChangeLog file for zlib
+
+Changes in 1.2.1 (17 November 2003)
+- Remove a tab in contrib/gzappend/gzappend.c
+- Update some interfaces in contrib for new zlib functions
+- Update zlib version number in some contrib entries
+- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta]
+- Support shared libraries on Hurd and KFreeBSD [Brown]
+- Fix error in NO_DIVIDE option of adler32.c
+
+Changes in 1.2.0.8 (4 November 2003)
+- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas
+- Add experimental NO_DIVIDE #define in adler32.c
+    - Possibly faster on some processors (let me know if it is)
+- Correct Z_BLOCK to not return on first inflate call if no wrap
+- Fix strm->data_type on inflate() return to correctly indicate EOB
+- Add deflatePrime() function for appending in the middle of a byte
+- Add contrib/gzappend for an example of appending to a stream
+- Update win32/DLL_FAQ.txt [Truta]
+- Delete Turbo C comment in README [Truta]
+- Improve some indentation in zconf.h [Truta]
+- Fix infinite loop on bad input in configure script [Church]
+- Fix gzeof() for concatenated gzip files [Johnson]
+- Add example to contrib/visual-basic.txt [Michael B.]
+- Add -p to mkdir's in Makefile.in [vda]
+- Fix configure to properly detect presence or lack of printf functions
+- Add AS400 support [Monnerat]
+- Add a little Cygwin support [Wilson]
+
+Changes in 1.2.0.7 (21 September 2003)
+- Correct some debug formats in contrib/infback9
+- Cast a type in a debug statement in trees.c
+- Change search and replace delimiter in configure from % to # [Beebe]
+- Update contrib/untgz to 0.2 with various fixes [Truta]
+- Add build support for Amiga [Nikl]
+- Remove some directories in old that have been updated to 1.2
+- Add dylib building for Mac OS X in configure and Makefile.in
+- Remove old distribution stuff from Makefile
+- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X
+- Update links in README
+
+Changes in 1.2.0.6 (13 September 2003)
+- Minor FAQ updates
+- Update contrib/minizip to 1.00 [Vollant]
+- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta]
+- Update POSTINC comment for 68060 [Nikl]
+- Add contrib/infback9 with deflate64 decoding (unsupported)
+- For MVS define NO_vsnprintf and undefine FAR [van Burik]
+- Add pragma for fdopen on MVS [van Burik]
+
+Changes in 1.2.0.5 (8 September 2003)
+- Add OF to inflateBackEnd() declaration in zlib.h
+- Remember start when using gzdopen in the middle of a file
+- Use internal off_t counters in gz* functions to properly handle seeks
+- Perform more rigorous check for distance-too-far in inffast.c
+- Add Z_BLOCK flush option to return from inflate at block boundary
+- Set strm->data_type on return from inflate
+    - Indicate bits unused, if at block boundary, and if in last block
+- Replace size_t with ptrdiff_t in crc32.c, and check for correct size
+- Add condition so old NO_DEFLATE define still works for compatibility
+- FAQ update regarding the Windows DLL [Truta]
+- INDEX update: add qnx entry, remove aix entry [Truta]
+- Install zlib.3 into mandir [Wilson]
+- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta]
+- Adapt the zlib interface to the new DLL convention guidelines [Truta]
+- Introduce ZLIB_WINAPI macro to allow the export of functions using
+  the WINAPI calling convention, for Visual Basic [Vollant, Truta]
+- Update msdos and win32 scripts and makefiles [Truta]
+- Export symbols by name, not by ordinal, in win32/zlib.def [Truta]
+- Add contrib/ada [Anisimkov]
+- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta]
+- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant]
+- Add contrib/masm686 [Truta]
+- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm
+  [Truta, Vollant]
+- Update contrib/delphi; rename to contrib/pascal; add example [Truta]
+- Remove contrib/delphi2; add a new contrib/delphi [Truta]
+- Avoid inclusion of the nonstandard <memory.h> in contrib/iostream,
+  and fix some method prototypes [Truta]
+- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip
+  [Truta]
+- Avoid the use of backslash (\) in contrib/minizip [Vollant]
+- Fix file time handling in contrib/untgz; update makefiles [Truta]
+- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines
+  [Vollant]
+- Remove contrib/vstudio/vc15_16 [Vollant]
+- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta]
+- Update README.contrib [Truta]
+- Invert the assignment order of match_head and s->prev[...] in
+  INSERT_STRING [Truta]
+- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings
+  [Truta]
+- Compare function pointers with 0, not with NULL or Z_NULL [Truta]
+- Fix prototype of syncsearch in inflate.c [Truta]
+- Introduce ASMINF macro to be enabled when using an ASM implementation
+  of inflate_fast [Truta]
+- Change NO_DEFLATE to NO_GZCOMPRESS [Truta]
+- Modify test_gzio in example.c to take a single file name as a
+  parameter [Truta]
+- Exit the example.c program if gzopen fails [Truta]
+- Add type casts around strlen in example.c [Truta]
+- Remove casting to sizeof in minigzip.c; give a proper type
+  to the variable compared with SUFFIX_LEN [Truta]
+- Update definitions of STDC and STDC99 in zconf.h [Truta]
+- Synchronize zconf.h with the new Windows DLL interface [Truta]
+- Use SYS16BIT instead of __32BIT__ to distinguish between
+  16- and 32-bit platforms [Truta]
+- Use far memory allocators in small 16-bit memory models for
+  Turbo C [Truta]
+- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in
+  zlibCompileFlags [Truta]
+- Cygwin has vsnprintf [Wilson]
+- In Windows16, OS_CODE is 0, as in MSDOS [Truta]
+- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson]
+
+Changes in 1.2.0.4 (10 August 2003)
+- Minor FAQ updates
+- Be more strict when checking inflateInit2's windowBits parameter
+- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well
+- Add gzip wrapper option to deflateInit2 using windowBits
+- Add updated QNX rule in configure and qnx directory [Bonnefoy]
+- Make inflate distance-too-far checks more rigorous
+- Clean up FAR usage in inflate
+- Add casting to sizeof() in gzio.c and minigzip.c
+
+Changes in 1.2.0.3 (19 July 2003)
+- Fix silly error in gzungetc() implementation [Vollant]
+- Update contrib/minizip and contrib/vstudio [Vollant]
+- Fix printf format in example.c
+- Correct cdecl support in zconf.in.h [Anisimkov]
+- Minor FAQ updates
+
+Changes in 1.2.0.2 (13 July 2003)
+- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons
+- Attempt to avoid warnings in crc32.c for pointer-int conversion
+- Add AIX to configure, remove aix directory [Bakker]
+- Add some casts to minigzip.c
+- Improve checking after insecure sprintf() or vsprintf() calls
+- Remove #elif's from crc32.c
+- Change leave label to inf_leave in inflate.c and infback.c to avoid
+  library conflicts
+- Remove inflate gzip decoding by default--only enable gzip decoding by
+  special request for stricter backward compatibility
+- Add zlibCompileFlags() function to return compilation information
+- More typecasting in deflate.c to avoid warnings
+- Remove leading underscore from _Capital #defines [Truta]
+- Fix configure to link shared library when testing
+- Add some Windows CE target adjustments [Mai]
+- Remove #define ZLIB_DLL in zconf.h [Vollant]
+- Add zlib.3 [Rodgers]
+- Update RFC URL in deflate.c and algorithm.txt [Mai]
+- Add zlib_dll_FAQ.txt to contrib [Truta]
+- Add UL to some constants [Truta]
+- Update minizip and vstudio [Vollant]
+- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h
+- Expand use of NO_DUMMY_DECL to avoid all dummy structures
+- Added iostream3 to contrib [Schwardt]
+- Replace rewind() with fseek() for WinCE [Truta]
+- Improve setting of zlib format compression level flags
+    - Report 0 for huffman and rle strategies and for level == 0 or 1
+    - Report 2 only for level == 6
+- Only deal with 64K limit when necessary at compile time [Truta]
+- Allow TOO_FAR check to be turned off at compile time [Truta]
+- Add gzclearerr() function [Souza]
+- Add gzungetc() function
+
+Changes in 1.2.0.1 (17 March 2003)
+- Add Z_RLE strategy for run-length encoding [Truta]
+    - When Z_RLE requested, restrict matches to distance one
+    - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE
+- Correct FASTEST compilation to allow level == 0
+- Clean up what gets compiled for FASTEST
+- Incorporate changes to zconf.in.h [Vollant]
+    - Refine detection of Turbo C need for dummy returns
+    - Refine ZLIB_DLL compilation
+    - Include additional header file on VMS for off_t typedef
+- Try to use _vsnprintf where it supplants vsprintf [Vollant]
+- Add some casts in inffast.c
+- Enchance comments in zlib.h on what happens if gzprintf() tries to
+  write more than 4095 bytes before compression
+- Remove unused state from inflateBackEnd()
+- Remove exit(0) from minigzip.c, example.c
+- Get rid of all those darn tabs
+- Add "check" target to Makefile.in that does the same thing as "test"
+- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in
+- Update contrib/inflate86 [Anderson]
+- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant]
+- Add msdos and win32 directories with makefiles [Truta]
+- More additions and improvements to the FAQ
+
+Changes in 1.2.0 (9 March 2003)
+- New and improved inflate code
+    - About 20% faster
+    - Does not allocate 32K window unless and until needed
+    - Automatically detects and decompresses gzip streams
+    - Raw inflate no longer needs an extra dummy byte at end
+    - Added inflateBack functions using a callback interface--even faster
+      than inflate, useful for file utilities (gzip, zip)
+    - Added inflateCopy() function to record state for random access on
+      externally generated deflate streams (e.g. in gzip files)
+    - More readable code (I hope)
+- New and improved crc32()
+    - About 50% faster, thanks to suggestions from Rodney Brown
+- Add deflateBound() and compressBound() functions
+- Fix memory leak in deflateInit2()
+- Permit setting dictionary for raw deflate (for parallel deflate)
+- Fix const declaration for gzwrite()
+- Check for some malloc() failures in gzio.c
+- Fix bug in gzopen() on single-byte file 0x1f
+- Fix bug in gzread() on concatenated file with 0x1f at end of buffer
+  and next buffer doesn't start with 0x8b
+- Fix uncompress() to return Z_DATA_ERROR on truncated input
+- Free memory at end of example.c
+- Remove MAX #define in trees.c (conflicted with some libraries)
+- Fix static const's in deflate.c, gzio.c, and zutil.[ch]
+- Declare malloc() and free() in gzio.c if STDC not defined
+- Use malloc() instead of calloc() in zutil.c if int big enough
+- Define STDC for AIX
+- Add aix/ with approach for compiling shared library on AIX
+- Add HP-UX support for shared libraries in configure
+- Add OpenUNIX support for shared libraries in configure
+- Use $cc instead of gcc to build shared library
+- Make prefix directory if needed when installing
+- Correct Macintosh avoidance of typedef Byte in zconf.h
+- Correct Turbo C memory allocation when under Linux
+- Use libz.a instead of -lz in Makefile (assure use of compiled library)
+- Update configure to check for snprintf or vsnprintf functions and their
+  return value, warn during make if using an insecure function
+- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that
+  is lost when library is used--resolution is to build new zconf.h
+- Documentation improvements (in zlib.h):
+    - Document raw deflate and inflate
+    - Update RFCs URL
+    - Point out that zlib and gzip formats are different
+    - Note that Z_BUF_ERROR is not fatal
+    - Document string limit for gzprintf() and possible buffer overflow
+    - Note requirement on avail_out when flushing
+    - Note permitted values of flush parameter of inflate()
+- Add some FAQs (and even answers) to the FAQ
+- Add contrib/inflate86/ for x86 faster inflate
+- Add contrib/blast/ for PKWare Data Compression Library decompression
+- Add contrib/puff/ simple inflate for deflate format description
+
+Changes in 1.1.4 (11 March 2002)
+- ZFREE was repeated on same allocation on some error conditions.
+  This creates a security problem described in
+  http://www.zlib.org/advisory-2002-03-11.txt
+- Returned incorrect error (Z_MEM_ERROR) on some invalid data
+- Avoid accesses before window for invalid distances with inflate window
+  less than 32K.
+- force windowBits > 8 to avoid a bug in the encoder for a window size
+  of 256 bytes. (A complete fix will be available in 1.1.5).
+
+Changes in 1.1.3 (9 July 1998)
+- fix "an inflate input buffer bug that shows up on rare but persistent
+  occasions" (Mark)
+- fix gzread and gztell for concatenated .gz files (Didier Le Botlan)
+- fix gzseek(..., SEEK_SET) in write mode
+- fix crc check after a gzeek (Frank Faubert)
+- fix miniunzip when the last entry in a zip file is itself a zip file
+  (J Lillge)
+- add contrib/asm586 and contrib/asm686 (Brian Raiter)
+  See http://www.muppetlabs.com/~breadbox/software/assembly.html
+- add support for Delphi 3 in contrib/delphi (Bob Dellaca)
+- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti)
+- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren)
+- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks)
+- added a FAQ file
+
+- Support gzdopen on Mac with Metrowerks (Jason Linhart)
+- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart)
+- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young)
+- avoid some warnings with Borland C (Tom Tanner)
+- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant)
+- emulate utime() for WIN32 in contrib/untgz  (Gilles Vollant)
+- allow several arguments to configure (Tim Mooney, Frodo Looijaard)
+- use libdir and includedir in Makefile.in (Tim Mooney)
+- support shared libraries on OSF1 V4 (Tim Mooney)
+- remove so_locations in "make clean"  (Tim Mooney)
+- fix maketree.c compilation error (Glenn, Mark)
+- Python interface to zlib now in Python 1.5 (Jeremy Hylton)
+- new Makefile.riscos (Rich Walker)
+- initialize static descriptors in trees.c for embedded targets (Nick Smith)
+- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith)
+- add the OS/2 files in Makefile.in too (Andrew Zabolotny)
+- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane)
+- fix maketree.c to allow clean compilation of inffixed.h (Mark)
+- fix parameter check in deflateCopy (Gunther Nikl)
+- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler)
+- Many portability patches by Christian Spieler:
+  . zutil.c, zutil.h: added "const" for zmem*
+  . Make_vms.com: fixed some typos
+  . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists
+  . msdos/Makefile.msc: remove "default rtl link library" info from obj files
+  . msdos/Makefile.*: use model-dependent name for the built zlib library
+  . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc:
+     new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT)
+- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane)
+- replace __far with _far for better portability (Christian Spieler, Tom Lane)
+- fix test for errno.h in configure (Tim Newsham)
+
+Changes in 1.1.2 (19 March 98)
+- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant)
+  See http://www.winimage.com/zLibDll/unzip.html
+- preinitialize the inflate tables for fixed codes, to make the code
+  completely thread safe (Mark)
+- some simplifications and slight speed-up to the inflate code (Mark)
+- fix gzeof on non-compressed files (Allan Schrum)
+- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs)
+- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn)
+- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny)
+- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori)
+- do not wrap extern "C" around system includes (Tom Lane)
+- mention zlib binding for TCL in README (Andreas Kupries)
+- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert)
+- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson)
+- allow "configure --prefix $HOME" (Tim Mooney)
+- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson)
+- move Makefile.sas to amiga/Makefile.sas
+
+Changes in 1.1.1 (27 Feb 98)
+- fix macros _tr_tally_* in deflate.h for debug mode  (Glenn Randers-Pehrson)
+- remove block truncation heuristic which had very marginal effect for zlib
+  (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the
+  compression ratio on some files. This also allows inlining _tr_tally for
+  matches in deflate_slow.
+- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier)
+
+Changes in 1.1.0 (24 Feb 98)
+- do not return STREAM_END prematurely in inflate (John Bowler)
+- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler)
+- compile with -DFASTEST to get compression code optimized for speed only
+- in minigzip, try mmap'ing the input file first (Miguel Albrecht)
+- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain
+  on Sun but significant on HP)
+
+- add a pointer to experimental unzip library in README (Gilles Vollant)
+- initialize variable gcc in configure (Chris Herborth)
+
+Changes in 1.0.9 (17 Feb 1998)
+- added gzputs and gzgets functions
+- do not clear eof flag in gzseek (Mark Diekhans)
+- fix gzseek for files in transparent mode (Mark Diekhans)
+- do not assume that vsprintf returns the number of bytes written (Jens Krinke)
+- replace EXPORT with ZEXPORT to avoid conflict with other programs
+- added compress2 in zconf.h, zlib.def, zlib.dnt
+- new asm code from Gilles Vollant in contrib/asm386
+- simplify the inflate code (Mark):
+ . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new()
+ . ZALLOC the length list in inflate_trees_fixed() instead of using stack
+ . ZALLOC the value area for huft_build() instead of using stack
+ . Simplify Z_FINISH check in inflate()
+
+- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8
+- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi)
+- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with
+  the declaration of FAR (Gilles VOllant)
+- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann)
+- read_buf buf parameter of type Bytef* instead of charf*
+- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout)
+- do not redeclare unlink in minigzip.c for WIN32 (John Bowler)
+- fix check for presence of directories in "make install" (Ian Willis)
+
+Changes in 1.0.8 (27 Jan 1998)
+- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant)
+- fix gzgetc and gzputc for big endian systems (Markus Oberhumer)
+- added compress2() to allow setting the compression level
+- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong)
+- use constant arrays for the static trees in trees.c instead of computing
+  them at run time (thanks to Ken Raeburn for this suggestion). To create
+  trees.h, compile with GEN_TREES_H and run "make test".
+- check return code of example in "make test" and display result
+- pass minigzip command line options to file_compress
+- simplifying code of inflateSync to avoid gcc 2.8 bug
+
+- support CC="gcc -Wall" in configure -s (QingLong)
+- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn)
+- fix test for shared library support to avoid compiler warnings
+- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant)
+- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit)
+- do not use fdopen for Metrowerks on Mac (Brad Pettit))
+- add checks for gzputc and gzputc in example.c
+- avoid warnings in gzio.c and deflate.c (Andreas Kleinert)
+- use const for the CRC table (Ken Raeburn)
+- fixed "make uninstall" for shared libraries
+- use Tracev instead of Trace in infblock.c
+- in example.c use correct compressed length for test_sync
+- suppress +vnocompatwarnings in configure for HPUX (not always supported)
+
+Changes in 1.0.7 (20 Jan 1998)
+- fix gzseek which was broken in write mode
+- return error for gzseek to negative absolute position
+- fix configure for Linux (Chun-Chung Chen)
+- increase stack space for MSC (Tim Wegner)
+- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant)
+- define EXPORTVA for gzprintf (Gilles Vollant)
+- added man page zlib.3 (Rick Rodgers)
+- for contrib/untgz, fix makedir() and improve Makefile
+
+- check gzseek in write mode in example.c
+- allocate extra buffer for seeks only if gzseek is actually called
+- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant)
+- add inflateSyncPoint in zconf.h
+- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def
+
+Changes in 1.0.6 (19 Jan 1998)
+- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and
+  gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code)
+- Fix a deflate bug occuring only with compression level 0 (thanks to
+  Andy Buckler for finding this one).
+- In minigzip, pass transparently also the first byte for .Z files.
+- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress()
+- check Z_FINISH in inflate (thanks to Marc Schluper)
+- Implement deflateCopy (thanks to Adam Costello)
+- make static libraries by default in configure, add --shared option.
+- move MSDOS or Windows specific files to directory msdos
+- suppress the notion of partial flush to simplify the interface
+  (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4)
+- suppress history buffer provided by application to simplify the interface
+  (this feature was not implemented anyway in 1.0.4)
+- next_in and avail_in must be initialized before calling inflateInit or
+  inflateInit2
+- add EXPORT in all exported functions (for Windows DLL)
+- added Makefile.nt (thanks to Stephen Williams)
+- added the unsupported "contrib" directory:
+   contrib/asm386/ by Gilles Vollant <info@winimage.com>
+        386 asm code replacing longest_match().
+   contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu>
+        A C++ I/O streams interface to the zlib gz* functions
+   contrib/iostream2/  by Tyge Løvset <Tyge.Lovset@cmr.no>
+        Another C++ I/O streams interface
+   contrib/untgz/  by "Pedro A. Aranda Guti\irrez" <paag@tid.es>
+        A very simple tar.gz file extractor using zlib
+   contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl>
+        How to use compress(), uncompress() and the gz* functions from VB.
+- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression
+  level) in minigzip (thanks to Tom Lane)
+
+- use const for rommable constants in deflate
+- added test for gzseek and gztell in example.c
+- add undocumented function inflateSyncPoint() (hack for Paul Mackerras)
+- add undocumented function zError to convert error code to string
+  (for Tim Smithers)
+- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code.
+- Use default memcpy for Symantec MSDOS compiler.
+- Add EXPORT keyword for check_func (needed for Windows DLL)
+- add current directory to LD_LIBRARY_PATH for "make test"
+- create also a link for libz.so.1
+- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura)
+- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX)
+- added -soname for Linux in configure (Chun-Chung Chen,
+- assign numbers to the exported functions in zlib.def (for Windows DLL)
+- add advice in zlib.h for best usage of deflateSetDictionary
+- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn)
+- allow compilation with ANSI keywords only enabled for TurboC in large model
+- avoid "versionString"[0] (Borland bug)
+- add NEED_DUMMY_RETURN for Borland
+- use variable z_verbose for tracing in debug mode (L. Peter Deutsch).
+- allow compilation with CC
+- defined STDC for OS/2 (David Charlap)
+- limit external names to 8 chars for MVS (Thomas Lund)
+- in minigzip.c, use static buffers only for 16-bit systems
+- fix suffix check for "minigzip -d foo.gz"
+- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee)
+- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau)
+- added makelcc.bat for lcc-win32 (Tom St Denis)
+- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe)
+- Avoid expanded $Id: ChangeLog,v 1.1 2005/01/12 07:44:59 mmondor Exp $. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion.
+- check for unistd.h in configure (for off_t)
+- remove useless check parameter in inflate_blocks_free
+- avoid useless assignment of s->check to itself in inflate_blocks_new
+- do not flush twice in gzclose (thanks to Ken Raeburn)
+- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h
+- use NO_ERRNO_H instead of enumeration of operating systems with errno.h
+- work around buggy fclose on pipes for HP/UX
+- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson)
+- fix configure if CC is already equal to gcc
+
+Changes in 1.0.5 (3 Jan 98)
+- Fix inflate to terminate gracefully when fed corrupted or invalid data
+- Use const for rommable constants in inflate
+- Eliminate memory leaks on error conditions in inflate
+- Removed some vestigial code in inflate
+- Update web address in README
+
+Changes in 1.0.4 (24 Jul 96)
+- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF
+  bit, so the decompressor could decompress all the correct data but went
+  on to attempt decompressing extra garbage data. This affected minigzip too.
+- zlibVersion and gzerror return const char* (needed for DLL)
+- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno)
+- use z_error only for DEBUG (avoid problem with DLLs)
+
+Changes in 1.0.3 (2 Jul 96)
+- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS
+  small and medium models; this makes the library incompatible with previous
+  versions for these models. (No effect in large model or on other systems.)
+- return OK instead of BUF_ERROR if previous deflate call returned with
+  avail_out as zero but there is nothing to do
+- added memcmp for non STDC compilers
+- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly)
+- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO)
+- better check for 16-bit mode MSC (avoids problem with Symantec)
+
+Changes in 1.0.2 (23 May 96)
+- added Windows DLL support
+- added a function zlibVersion (for the DLL support)
+- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model)
+- Bytef is define's instead of typedef'd only for Borland C
+- avoid reading uninitialized memory in example.c
+- mention in README that the zlib format is now RFC1950
+- updated Makefile.dj2
+- added algorithm.doc
+
+Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion]
+- fix array overlay in deflate.c which sometimes caused bad compressed data
+- fix inflate bug with empty stored block
+- fix MSDOS medium model which was broken in 0.99
+- fix deflateParams() which could generated bad compressed data.
+- Bytef is define'd instead of typedef'ed (work around Borland bug)
+- added an INDEX file
+- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32),
+  Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas)
+- speed up adler32 for modern machines without auto-increment
+- added -ansi for IRIX in configure
+- static_init_done in trees.c is an int
+- define unlink as delete for VMS
+- fix configure for QNX
+- add configure branch for SCO and HPUX
+- avoid many warnings (unused variables, dead assignments, etc...)
+- no fdopen for BeOS
+- fix the Watcom fix for 32 bit mode (define FAR as empty)
+- removed redefinition of Byte for MKWERKS
+- work around an MWKERKS bug (incorrect merge of all .h files)
+
+Changes in 0.99 (27 Jan 96)
+- allow preset dictionary shared between compressor and decompressor
+- allow compression level 0 (no compression)
+- add deflateParams in zlib.h: allow dynamic change of compression level
+  and compression strategy.
+- test large buffers and deflateParams in example.c
+- add optional "configure" to build zlib as a shared library
+- suppress Makefile.qnx, use configure instead
+- fixed deflate for 64-bit systems (detected on Cray)
+- fixed inflate_blocks for 64-bit systems (detected on Alpha)
+- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2)
+- always return Z_BUF_ERROR when deflate() has nothing to do
+- deflateInit and inflateInit are now macros to allow version checking
+- prefix all global functions and types with z_ with -DZ_PREFIX
+- make falloc completely reentrant (inftrees.c)
+- fixed very unlikely race condition in ct_static_init
+- free in reverse order of allocation to help memory manager
+- use zlib-1.0/* instead of zlib/* inside the tar.gz
+- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith
+  -Wconversion -Wstrict-prototypes -Wmissing-prototypes"
+- allow gzread on concatenated .gz files
+- deflateEnd now returns Z_DATA_ERROR if it was premature
+- deflate is finally (?) fully deterministic (no matches beyond end of input)
+- Document Z_SYNC_FLUSH
+- add uninstall in Makefile
+- Check for __cpluplus in zlib.h
+- Better test in ct_align for partial flush
+- avoid harmless warnings for Borland C++
+- initialize hash_head in deflate.c
+- avoid warning on fdopen (gzio.c) for HP cc -Aa
+- include stdlib.h for STDC compilers
+- include errno.h for Cray
+- ignore error if ranlib doesn't exist
+- call ranlib twice for NeXTSTEP
+- use exec_prefix instead of prefix for libz.a
+- renamed ct_* as _tr_* to avoid conflict with applications
+- clear z->msg in inflateInit2 before any error return
+- initialize opaque in example.c, gzio.c, deflate.c and inflate.c
+- fixed typo in zconf.h (_GNUC__ => __GNUC__)
+- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode)
+- fix typo in Make_vms.com (f$trnlnm -> f$getsyi)
+- in fcalloc, normalize pointer if size > 65520 bytes
+- don't use special fcalloc for 32 bit Borland C++
+- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc...
+- use Z_BINARY instead of BINARY
+- document that gzclose after gzdopen will close the file
+- allow "a" as mode in gzopen.
+- fix error checking in gzread
+- allow skipping .gz extra-field on pipes
+- added reference to Perl interface in README
+- put the crc table in FAR data (I dislike more and more the medium model :)
+- added get_crc_table
+- added a dimension to all arrays (Borland C can't count).
+- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast
+- guard against multiple inclusion of *.h (for precompiled header on Mac)
+- Watcom C pretends to be Microsoft C small model even in 32 bit mode.
+- don't use unsized arrays to avoid silly warnings by Visual C++:
+     warning C4746: 'inflate_mask' : unsized array treated as  '__far'
+     (what's wrong with far data in far model?).
+- define enum out of inflate_blocks_state to allow compilation with C++
+
+Changes in 0.95 (16 Aug 95)
+- fix MSDOS small and medium model (now easier to adapt to any compiler)
+- inlined send_bits
+- fix the final (:-) bug for deflate with flush (output was correct but
+  not completely flushed in rare occasions).
+- default window size is same for compression and decompression
+  (it's now sufficient to set MAX_WBITS in zconf.h).
+- voidp -> voidpf and voidnp -> voidp (for consistency with other
+  typedefs and because voidnp was not near in large model).
+
+Changes in 0.94 (13 Aug 95)
+- support MSDOS medium model
+- fix deflate with flush (could sometimes generate bad output)
+- fix deflateReset (zlib header was incorrectly suppressed)
+- added support for VMS
+- allow a compression level in gzopen()
+- gzflush now calls fflush
+- For deflate with flush, flush even if no more input is provided.
+- rename libgz.a as libz.a
+- avoid complex expression in infcodes.c triggering Turbo C bug
+- work around a problem with gcc on Alpha (in INSERT_STRING)
+- don't use inline functions (problem with some gcc versions)
+- allow renaming of Byte, uInt, etc... with #define.
+- avoid warning about (unused) pointer before start of array in deflate.c
+- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c
+- avoid reserved word 'new' in trees.c
+
+Changes in 0.93 (25 June 95)
+- temporarily disable inline functions
+- make deflate deterministic
+- give enough lookahead for PARTIAL_FLUSH
+- Set binary mode for stdin/stdout in minigzip.c for OS/2
+- don't even use signed char in inflate (not portable enough)
+- fix inflate memory leak for segmented architectures
+
+Changes in 0.92 (3 May 95)
+- don't assume that char is signed (problem on SGI)
+- Clear bit buffer when starting a stored block
+- no memcpy on Pyramid
+- suppressed inftest.c
+- optimized fill_window, put longest_match inline for gcc
+- optimized inflate on stored blocks.
+- untabify all sources to simplify patches
+
+Changes in 0.91 (2 May 95)
+- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h
+- Document the memory requirements in zconf.h
+- added "make install"
+- fix sync search logic in inflateSync
+- deflate(Z_FULL_FLUSH) now works even if output buffer too short
+- after inflateSync, don't scare people with just "lo world"
+- added support for DJGPP
+
+Changes in 0.9 (1 May 95)
+- don't assume that zalloc clears the allocated memory (the TurboC bug
+  was Mark's bug after all :)
+- let again gzread copy uncompressed data unchanged (was working in 0.71)
+- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented
+- added a test of inflateSync in example.c
+- moved MAX_WBITS to zconf.h because users might want to change that.
+- document explicitly that zalloc(64K) on MSDOS must return a normalized
+  pointer (zero offset)
+- added Makefiles for Microsoft C, Turbo C, Borland C++
+- faster crc32()
+
+Changes in 0.8 (29 April 95)
+- added fast inflate (inffast.c)
+- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this
+  is incompatible with previous versions of zlib which returned Z_OK.
+- work around a TurboC compiler bug (bad code for b << 0, see infutil.h)
+  (actually that was not a compiler bug, see 0.81 above)
+- gzread no longer reads one extra byte in certain cases
+- In gzio destroy(), don't reference a freed structure
+- avoid many warnings for MSDOS
+- avoid the ERROR symbol which is used by MS Windows
+
+Changes in 0.71 (14 April 95)
+- Fixed more MSDOS compilation problems :( There is still a bug with
+  TurboC large model.
+
+Changes in 0.7 (14 April 95)
+- Added full inflate support.
+- Simplified the crc32() interface. The pre- and post-conditioning
+  (one's complement) is now done inside crc32(). WARNING: this is
+  incompatible with previous versions; see zlib.h for the new usage.
+
+Changes in 0.61 (12 April 95)
+- workaround for a bug in TurboC. example and minigzip now work on MSDOS.
+
+Changes in 0.6 (11 April 95)
+- added minigzip.c
+- added gzdopen to reopen a file descriptor as gzFile
+- added transparent reading of non-gziped files in gzread.
+- fixed bug in gzread (don't read crc as data)
+- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose).
+- don't allocate big arrays in the stack (for MSDOS)
+- fix some MSDOS compilation problems
+
+Changes in 0.5:
+- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but
+  not yet Z_FULL_FLUSH.
+- support decompression but only in a single step (forced Z_FINISH)
+- added opaque object for zalloc and zfree.
+- added deflateReset and inflateReset
+- added a variable zlib_version for consistency checking.
+- renamed the 'filter' parameter of deflateInit2 as 'strategy'.
+  Added Z_FILTERED and Z_HUFFMAN_ONLY constants.
+
+Changes in 0.4:
+- avoid "zip" everywhere, use zlib instead of ziplib.
+- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush
+  if compression method == 8.
+- added adler32 and crc32
+- renamed deflateOptions as deflateInit2, call one or the other but not both
+- added the method parameter for deflateInit2.
+- added inflateInit2
+- simplied considerably deflateInit and inflateInit by not supporting
+  user-provided history buffer. This is supported only in deflateInit2
+  and inflateInit2.
+
+Changes in 0.3:
+- prefix all macro names with Z_
+- use Z_FINISH instead of deflateEnd to finish compression.
+- added Z_HUFFMAN_ONLY
+- added gzerror()
diff --git a/zlib/FAQ b/zlib/FAQ
new file mode 100644 (file)
index 0000000..7115ec3
--- /dev/null
+++ b/zlib/FAQ
@@ -0,0 +1,315 @@
+
+                Frequently Asked Questions about zlib
+
+
+If your question is not there, please check the zlib home page
+http://www.zlib.org which may have more recent information.
+The lastest zlib FAQ is at http://www.gzip.org/zlib/zlib_faq.html
+
+
+ 1. Is zlib Y2K-compliant?
+
+    Yes. zlib doesn't handle dates.
+
+ 2. Where can I get a Windows DLL version?
+
+    The zlib sources can be compiled without change to produce a DLL.
+    See the file win32/DLL_FAQ.txt in the zlib distribution.
+    Pointers to the precompiled DLL are found in the zlib web site at
+    http://www.zlib.org.
+
+ 3. Where can I get a Visual Basic interface to zlib?
+
+    See
+        * http://www.winimage.com/zLibDll/
+        * http://www.dogma.net/markn/articles/zlibtool/zlibtool.htm
+        * contrib/visual-basic.txt in the zlib distribution
+
+ 4. compress() returns Z_BUF_ERROR
+
+    Make sure that before the call of compress, the length of the compressed
+    buffer is equal to the total size of the compressed buffer and not
+    zero. For Visual Basic, check that this parameter is passed by reference
+    ("as any"), not by value ("as long").
+
+ 5. deflate() or inflate() returns Z_BUF_ERROR
+
+    Before making the call, make sure that avail_in and avail_out are not
+    zero. When setting the parameter flush equal to Z_FINISH, also make sure
+    that avail_out is big enough to allow processing all pending input.
+    Note that a Z_BUF_ERROR is not fatal--another call to deflate() or
+    inflate() can be made with more input or output space. A Z_BUF_ERROR
+    may in fact be unavoidable depending on how the functions are used, since
+    it is not possible to tell whether or not there is more output pending
+    when strm.avail_out returns with zero.
+
+ 6. Where's the zlib documentation (man pages, etc.)?
+
+    It's in zlib.h for the moment, and Francis S. Lin has converted it to a
+    web page zlib.html. Volunteers to transform this to Unix-style man pages,
+    please contact Jean-loup Gailly (jloup@gzip.org). Examples of zlib usage
+    are in the files example.c and minigzip.c.
+
+ 7. Why don't you use GNU autoconf or libtool or ...?
+
+    Because we would like to keep zlib as a very small and simple
+    package. zlib is rather portable and doesn't need much configuration.
+
+ 8. I found a bug in zlib.
+
+    Most of the time, such problems are due to an incorrect usage of
+    zlib. Please try to reproduce the problem with a small program and send
+    the corresponding source to us at zlib@gzip.org . Do not send
+    multi-megabyte data files without prior agreement.
+
+ 9. Why do I get "undefined reference to gzputc"?
+
+    If "make test" produces something like
+
+       example.o(.text+0x154): undefined reference to `gzputc'
+
+    check that you don't have old files libz.* in /usr/lib, /usr/local/lib or
+    /usr/X11R6/lib. Remove any old versions, then do "make install".
+
+10. I need a Delphi interface to zlib.
+
+    See the contrib/delphi directory in the zlib distribution.
+
+11. Can zlib handle .zip archives?
+
+    See the directory contrib/minizip in the zlib distribution.
+
+12. Can zlib handle .Z files?
+
+    No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt
+    the code of uncompress on your own.
+
+13. How can I make a Unix shared library?
+
+    make clean
+    ./configure -s
+    make
+
+14. How do I install a shared zlib library on Unix?
+
+    make install
+
+    However, many flavors of Unix come with a shared zlib already installed.
+    Before going to the trouble of compiling a shared version of zlib and
+    trying to install it, you may want to check if it's already there! If you
+    can #include <zlib.h>, it's there. The -lz option will probably link to it.
+
+15. I have a question about OttoPDF
+
+    We are not the authors of OttoPDF. The real author is on the OttoPDF web
+    site Joel Hainley jhainley@myndkryme.com.
+
+16. Why does gzip give an error on a file I make with compress/deflate?
+
+    The compress and deflate functions produce data in the zlib format, which
+    is different and incompatible with the gzip format. The gz* functions in
+    zlib on the other hand use the gzip format. Both the zlib and gzip
+    formats use the same compressed data format internally, but have different
+    headers and trailers around the compressed data.
+
+17. Ok, so why are there two different formats?
+
+    The gzip format was designed to retain the directory information about
+    a single file, such as the name and last modification date. The zlib
+    format on the other hand was designed for in-memory and communication
+    channel applications, and has a much more compact header and trailer and
+    uses a faster integrity check than gzip.
+
+18. Well that's nice, but how do I make a gzip file in memory?
+
+    You can request that deflate write the gzip format instead of the zlib
+    format using deflateInit2(). You can also request that inflate decode
+    the gzip format using inflateInit2(). Read zlib.h for more details.
+
+    Note that you cannot specify special gzip header contents (e.g. a file
+    name or modification date), nor will inflate tell you what was in the
+    gzip header. If you need to customize the header or see what's in it,
+    you can use the raw deflate and inflate operations and the crc32()
+    function and roll your own gzip encoding and decoding. Read the gzip
+    RFC 1952 for details of the header and trailer format.
+
+19. Is zlib thread-safe?
+
+    Yes. However any library routines that zlib uses and any application-
+    provided memory allocation routines must also be thread-safe. zlib's gz*
+    functions use stdio library routines, and most of zlib's functions use the
+    library memory allocation routines by default. zlib's Init functions allow
+    for the application to provide custom memory allocation routines.
+
+    Of course, you should only operate on any given zlib or gzip stream from a
+    single thread at a time.
+
+20. Can I use zlib in my commercial application?
+
+    Yes. Please read the license in zlib.h.
+
+21. Is zlib under the GNU license?
+
+    No. Please read the license in zlib.h.
+
+22. The license says that altered source versions must be "plainly marked". So
+    what exactly do I need to do to meet that requirement?
+
+    You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In
+    particular, the final version number needs to be changed to "f", and an
+    identification string should be appended to ZLIB_VERSION. Version numbers
+    x.x.x.f are reserved for modifications to zlib by others than the zlib
+    maintainers. For example, if the version of the base zlib you are altering
+    is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and
+    ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also
+    update the version strings in deflate.c and inftrees.c.
+
+    For altered source distributions, you should also note the origin and
+    nature of the changes in zlib.h, as well as in ChangeLog and README, along
+    with the dates of the alterations. The origin should include at least your
+    name (or your company's name), and an email address to contact for help or
+    issues with the library.
+
+    Note that distributing a compiled zlib library along with zlib.h and
+    zconf.h is also a source distribution, and so you should change
+    ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes
+    in zlib.h as you would for a full source distribution.
+
+23. Will zlib work on a big-endian or little-endian architecture, and can I
+    exchange compressed data between them?
+
+    Yes and yes.
+
+24. Will zlib work on a 64-bit machine?
+
+    It should. It has been tested on 64-bit machines, and has no dependence
+    on any data types being limited to 32-bits in length. If you have any
+    difficulties, please provide a complete problem report to zlib@gzip.org
+
+25. Will zlib decompress data from the PKWare Data Compression Library?
+
+    No. The PKWare DCL uses a completely different compressed data format
+    than does PKZIP and zlib. However, you can look in zlib's contrib/blast
+    directory for a possible solution to your problem.
+
+26. Can I access data randomly in a compressed stream?
+
+    No, not without some preparation. If when compressing you periodically
+    use Z_FULL_FLUSH, carefully write all the pending data at those points,
+    and keep an index of those locations, then you can start decompression
+    at those points. You have to be careful to not use Z_FULL_FLUSH too
+    often, since it can significantly degrade compression.
+
+27. Does zlib work on MVS, OS/390, CICS, etc.?
+
+    We don't know for sure. We have heard occasional reports of success on
+    these systems. If you do use it on one of these, please provide us with
+    a report, instructions, and patches that we can reference when we get
+    these questions. Thanks.
+
+28. Is there some simpler, easier to read version of inflate I can look at
+    to understand the deflate format?
+
+    First off, you should read RFC 1951. Second, yes. Look in zlib's
+    contrib/puff directory.
+
+29. Does zlib infringe on any patents?
+
+    As far as we know, no. In fact, that was originally the whole point behind
+    zlib. Look here for some more information:
+
+    http://www.gzip.org/#faq11
+
+30. Can zlib work with greater than 4 GB of data?
+
+    Yes. inflate() and deflate() will process any amount of data correctly.
+    Each call of inflate() or deflate() is limited to input and output chunks
+    of the maximum value that can be stored in the compiler's "unsigned int"
+    type, but there is no limit to the number of chunks. Note however that the
+    strm.total_in and strm_total_out counters may be limited to 4 GB. These
+    counters are provided as a convenience and are not used internally by
+    inflate() or deflate(). The application can easily set up its own counters
+    updated after each call of inflate() or deflate() to count beyond 4 GB.
+    compress() and uncompress() may be limited to 4 GB, since they operate in a
+    single call. gzseek() and gztell() may be limited to 4 GB depending on how
+    zlib is compiled. See the zlibCompileFlags() function in zlib.h.
+
+    The word "may" appears several times above since there is a 4 GB limit
+    only if the compiler's "long" type is 32 bits. If the compiler's "long"
+    type is 64 bits, then the limit is 16 exabytes.
+
+31. Does zlib have any security vulnerabilities?
+
+    The only one that we are aware of is potentially in gzprintf(). If zlib
+    is compiled to use sprintf() or vsprintf(), then there is no protection
+    against a buffer overflow of a 4K string space, other than the caller of
+    gzprintf() assuring that the output will not exceed 4K. On the other
+    hand, if zlib is compiled to use snprintf() or vsnprintf(), which should
+    normally be the case, then there is no vulnerability. The ./configure
+    script will display warnings if an insecure variation of sprintf() will
+    be used by gzprintf(). Also the zlibCompileFlags() function will return
+    information on what variant of sprintf() is used by gzprintf().
+
+    If you don't have snprintf() or vsnprintf() and would like one, you can
+    find a portable implementation here:
+
+        http://www.ijs.si/software/snprintf/
+
+    Note that you should be using the most recent version of zlib. Versions
+    1.1.3 and before were subject to a double-free vulnerability.
+
+32. Is there a Java version of zlib?
+
+    Probably what you want is to use zlib in Java. zlib is already included
+    as part of the Java SDK in the java.util.zip package. If you really want
+    a version of zlib written in the Java language, look on the zlib home
+    page for links: http://www.zlib.org/
+
+33. I get this or that compiler or source-code scanner warning when I crank it
+    up to maximally-pendantic. Can't you guys write proper code?
+
+    Many years ago, we gave up attempting to avoid warnings on every compiler
+    in the universe. It just got to be a waste of time, and some compilers
+    were downright silly. So now, we simply make sure that the code always
+    works.
+
+34. Will zlib read the (insert any ancient or arcane format here) compressed
+    data format?
+
+    Probably not. Look in the comp.compression FAQ for pointers to various
+    formats and associated software.
+
+35. How can I encrypt/decrypt zip files with zlib?
+
+    zlib doesn't support encryption. The original PKZIP encryption is very weak
+    and can be broken with freely available programs. To get strong encryption,
+    use gpg ( http://www.gnupg.org/ ) which already includes zlib compression.
+    For PKZIP compatible "encryption", look at http://www.info-zip.org/
+
+36. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
+
+    "gzip" is the gzip format, and "deflate" is the zlib format. They should
+    probably have called the second one "zlib" instead to avoid confusion
+    with the raw deflate compressed data format. While the HTTP 1.1 RFC 2616
+    correctly points to the zlib specification in RFC 1950 for the "deflate"
+    transfer encoding, there have been reports of servers and browsers that
+    incorrectly produce or expect raw deflate data per the deflate
+    specficiation in RFC 1951, most notably Microsoft. So even though the
+    "deflate" transfer encoding using the zlib format would be the more
+    efficient approach (and in fact exactly what the zlib format was designed
+    for), using the "gzip" transfer encoding is probably more reliable due to
+    an unfortunate choice of name on the part of the HTTP 1.1 authors.
+
+    Bottom line: use the gzip format for HTTP 1.1 encoding.
+
+37. Does zlib support the new "Deflate64" format introduced by PKWare?
+
+    No. PKWare has apparently decided to keep that format proprietary, since
+    they have not documented it as they have previous compression formats.
+    In any case, the compression improvements are so modest compared to other
+    more modern approaches, that it's not worth the effort to implement.
+
+38. Can you please sign these lengthy legal documents and fax them back to us
+    so that we can use your software in our product?
+
+    No. Go away. Shoo.
diff --git a/zlib/INDEX b/zlib/INDEX
new file mode 100644 (file)
index 0000000..a9de784
--- /dev/null
@@ -0,0 +1,48 @@
+ChangeLog       history of changes
+FAQ             Frequently Asked Questions about zlib
+INDEX           this file
+Makefile        makefile for Unix (generated by configure)
+Makefile.in     makefile for Unix (template for configure)
+README          guess what
+algorithm.txt   description of the (de)compression algorithm
+configure       configure script for Unix
+zconf.in.h      template for zconf.h (used by configure)
+
+msdos/          makefiles for MSDOS
+old/            makefiles for various architectures and zlib documentation
+                files that have not yet been updated for zlib 1.2.x
+qnx/            makefiles for QNX
+win32/          makefiles for Windows
+
+                zlib public header files (must be kept):
+zconf.h
+zlib.h
+
+                private source files used to build the zlib library:
+adler32.c
+compress.c
+crc32.c
+crc32.h
+deflate.c
+deflate.h
+gzio.c
+infback.c
+inffast.c
+inffast.h
+inffixed.h
+inflate.c
+inflate.h
+inftrees.c
+inftrees.h
+trees.c
+trees.h
+uncompr.c
+zutil.c
+zutil.h
+
+                source files for sample programs:
+example.c
+minigzip.c
+
+                unsupported contribution by third parties
+See contrib/README.contrib
diff --git a/zlib/Makefile.in b/zlib/Makefile.in
new file mode 100644 (file)
index 0000000..796a6d8
--- /dev/null
@@ -0,0 +1,156 @@
+# Makefile for zlib
+# Copyright (C) 1995-2003 Jean-loup Gailly.
+# For conditions of distribution and use, see copyright notice in zlib.h
+
+# To compile and test, type:
+#    ./configure; make test
+# The call of configure is optional if you don't have special requirements
+# If you wish to build zlib as a shared library, use: ./configure -s
+
+# To use the asm code, type:
+#    cp contrib/asm?86/match.S ./match.S
+#    make LOC=-DASMV OBJA=match.o
+
+# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type:
+#    make install
+# To install in $HOME instead of /usr/local, use:
+#    make install prefix=$HOME
+
+CC=cc
+
+CFLAGS=-O
+#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7
+#CFLAGS=-g -DDEBUG
+#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \
+#           -Wstrict-prototypes -Wmissing-prototypes
+
+LDFLAGS=libz.a
+LDSHARED=$(CC)
+CPP=$(CC) -E
+
+LIBS=libz.a
+SHAREDLIB=libz.so
+SHAREDLIBV=libz.so.1.2.1
+SHAREDLIBM=libz.so.1
+
+AR=ar rc
+RANLIB=ranlib
+TAR=tar
+SHELL=/bin/sh
+EXE=
+
+prefix = /usr/local
+exec_prefix = ${prefix}
+libdir = ${exec_prefix}/lib
+includedir = ${prefix}/include
+mandir = ${prefix}/share/man
+man3dir = ${mandir}/man3
+
+OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \
+       zutil.o inflate.o infback.o inftrees.o inffast.o
+
+OBJA =
+# to use the asm code: make OBJA=match.o
+
+TEST_OBJS = example.o minigzip.o
+
+all: example$(EXE) minigzip$(EXE)
+
+build: all
+
+check: test
+test: all
+       @LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \
+       echo hello world | ./minigzip | ./minigzip -d || \
+         echo '                *** minigzip test FAILED ***' ; \
+       if ./example; then \
+         echo '                *** zlib test OK ***'; \
+       else \
+         echo '                *** zlib test FAILED ***'; \
+       fi
+
+libz.a: $(OBJS) $(OBJA)
+       $(AR) $@ $(OBJS) $(OBJA)
+       -@ ($(RANLIB) $@ || true) >/dev/null 2>&1
+
+match.o: match.S
+       $(CPP) match.S > _match.s
+       $(CC) -c _match.s
+       mv _match.o match.o
+       rm -f _match.s
+
+$(SHAREDLIBV): $(OBJS)
+       $(LDSHARED) -o $@ $(OBJS)
+       rm -f $(SHAREDLIB) $(SHAREDLIBM)
+       ln -s $@ $(SHAREDLIB)
+       ln -s $@ $(SHAREDLIBM)
+
+example$(EXE): example.o $(LIBS)
+       $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS)
+
+minigzip$(EXE): minigzip.o $(LIBS)
+       $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS)
+
+install: $(LIBS)
+       -@if [ ! -d $(exec_prefix) ]; then mkdir -p $(exec_prefix); fi
+       -@if [ ! -d $(includedir)  ]; then mkdir -p $(includedir); fi
+       -@if [ ! -d $(libdir)      ]; then mkdir -p $(libdir); fi
+       -@if [ ! -d $(man3dir)     ]; then mkdir -p $(man3dir); fi
+       cp zlib.h zconf.h $(includedir)
+       chmod 644 $(includedir)/zlib.h $(includedir)/zconf.h
+       cp $(LIBS) $(libdir)
+       cd $(libdir); chmod 755 $(LIBS)
+       -@(cd $(libdir); $(RANLIB) libz.a || true) >/dev/null 2>&1
+       cd $(libdir); if test -f $(SHAREDLIBV); then \
+         rm -f $(SHAREDLIB) $(SHAREDLIBM); \
+         ln -s $(SHAREDLIBV) $(SHAREDLIB); \
+         ln -s $(SHAREDLIBV) $(SHAREDLIBM); \
+         (ldconfig || true)  >/dev/null 2>&1; \
+       fi
+       cp zlib.3 $(man3dir)
+       chmod 644 $(man3dir)/zlib.3
+# The ranlib in install is needed on NeXTSTEP which checks file times
+# ldconfig is for Linux
+
+uninstall:
+       cd $(includedir); \
+       cd $(libdir); rm -f libz.a; \
+       if test -f $(SHAREDLIBV); then \
+         rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \
+       fi
+       cd $(man3dir); rm -f zlib.3
+
+mostlyclean: clean
+clean:
+       rm -f *.o *~ example$(EXE) minigzip$(EXE) \
+          libz.* foo.gz so_locations \
+          _match.s maketree contrib/infback9/*.o
+
+maintainer-clean: distclean
+distclean: clean
+       cp -p Makefile.in Makefile
+       cp -p zconf.in.h zconf.h
+       rm -f .DS_Store
+
+tags:
+       etags *.[ch]
+
+depend:
+       makedepend -- $(CFLAGS) -- *.[ch]
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+adler32.o: zlib.h zconf.h
+compress.o: zlib.h zconf.h
+crc32.o: crc32.h zlib.h zconf.h
+deflate.o: deflate.h zutil.h zlib.h zconf.h
+example.o: zlib.h zconf.h
+gzio.o: zutil.h zlib.h zconf.h
+inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
+inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
+infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h
+inftrees.o: zutil.h zlib.h zconf.h inftrees.h
+minigzip.o: zlib.h zconf.h
+trees.o: deflate.h zutil.h zlib.h zconf.h trees.h
+uncompr.o: zlib.h zconf.h
+zutil.o: zutil.h zlib.h zconf.h
diff --git a/zlib/README b/zlib/README
new file mode 100644 (file)
index 0000000..0f12054
--- /dev/null
@@ -0,0 +1,126 @@
+ZLIB DATA COMPRESSION LIBRARY
+
+zlib 1.2.1 is a general purpose data compression library.  All the code is
+thread safe.  The data format used by the zlib library is described by RFCs
+(Request for Comments) 1950 to 1952 in the files
+http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format)
+and rfc1952.txt (gzip format). These documents are also available in other
+formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+
+All functions of the compression library are documented in the file zlib.h
+(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example
+of the library is given in the file example.c which also tests that the library
+is working correctly. Another example is given in the file minigzip.c. The
+compression library itself is composed of all source files except example.c and
+minigzip.c.
+
+To compile all files and run the test program, follow the instructions given at
+the top of Makefile. In short "make test; make install" should work for most
+machines. For Unix: "./configure; make test; make install" For MSDOS, use one
+of the special makefiles such as Makefile.msc. For VMS, use Make_vms.com or
+descrip.mms.
+
+Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant
+<info@winimage.com> for the Windows DLL version. The zlib home page is
+http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem,
+please check this site to verify that you have the latest version of zlib;
+otherwise get the latest version and check whether the problem still exists or
+not.
+
+PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking
+for help.
+
+Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997
+issue of  Dr. Dobb's Journal; a copy of the article is available in
+http://dogma.net/markn/articles/zlibtool/zlibtool.htm
+
+The changes made in version 1.2.1 are documented in the file ChangeLog.
+
+Unsupported third party contributions are provided in directory "contrib".
+
+A Java implementation of zlib is available in the Java Development Kit
+http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html
+See the zlib home page http://www.zlib.org for details.
+
+A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is in the
+CPAN (Comprehensive Perl Archive Network) sites
+http://www.cpan.org/modules/by-module/Compress/
+
+A Python interface to zlib written by A.M. Kuchling <amk@magnet.com> is
+available in Python 1.5 and later versions, see
+http://www.python.org/doc/lib/module-zlib.html
+
+A zlib binding for TCL written by Andreas Kupries <a.kupries@westend.com> is
+availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html
+
+An experimental package to read and write files in .zip format, written on top
+of zlib by Gilles Vollant <info@winimage.com>, is available in the
+contrib/minizip directory of zlib.
+
+
+Notes for some targets:
+
+- For Windows DLL versions, please see win32/DLL_FAQ.txt
+
+- For 64-bit Irix, deflate.c must be compiled without any optimization. With
+  -O, one libpng test fails. The test works in 32 bit mode (with the -n32
+  compiler flag). The compiler bug has been reported to SGI.
+
+- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works
+  when compiled with cc.
+
+- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is
+  necessary to get gzprintf working correctly. This is done by configure.
+
+- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with
+  other compilers. Use "make test" to check your compiler.
+
+- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers.
+
+- For PalmOs, see http://palmzlib.sourceforge.net/
+
+- When building a shared, i.e. dynamic library on Mac OS X, the library must be
+  installed before testing (do "make install" before "make test"), since the
+  library location is specified in the library.
+
+
+Acknowledgments:
+
+  The deflate format used by zlib was defined by Phil Katz. The deflate
+  and zlib specifications were written by L. Peter Deutsch. Thanks to all the
+  people who reported problems and suggested various improvements in zlib;
+  they are too numerous to cite here.
+
+Copyright notice:
+
+ (C) 1995-2003 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+If you use the zlib library in a product, we would appreciate *not*
+receiving lengthy legal documents to sign. The sources are provided
+for free but without warranty of any kind.  The library has been
+entirely written by Jean-loup Gailly and Mark Adler; it does not
+include third-party code.
+
+If you redistribute modified sources, we would appreciate that you include
+in the file ChangeLog history information documenting your changes. Please
+read the FAQ for more information on the distribution of modified source
+versions.
diff --git a/zlib/adler32.c b/zlib/adler32.c
new file mode 100644 (file)
index 0000000..18cd890
--- /dev/null
@@ -0,0 +1,74 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: adler32.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#define BASE 65521UL    /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i)  {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf)   DO8(buf,0); DO8(buf,8);
+
+#ifdef NO_DIVIDE
+#  define MOD(a) \
+    do { \
+        if (a >= (BASE << 16)) a -= (BASE << 16); \
+        if (a >= (BASE << 15)) a -= (BASE << 15); \
+        if (a >= (BASE << 14)) a -= (BASE << 14); \
+        if (a >= (BASE << 13)) a -= (BASE << 13); \
+        if (a >= (BASE << 12)) a -= (BASE << 12); \
+        if (a >= (BASE << 11)) a -= (BASE << 11); \
+        if (a >= (BASE << 10)) a -= (BASE << 10); \
+        if (a >= (BASE << 9)) a -= (BASE << 9); \
+        if (a >= (BASE << 8)) a -= (BASE << 8); \
+        if (a >= (BASE << 7)) a -= (BASE << 7); \
+        if (a >= (BASE << 6)) a -= (BASE << 6); \
+        if (a >= (BASE << 5)) a -= (BASE << 5); \
+        if (a >= (BASE << 4)) a -= (BASE << 4); \
+        if (a >= (BASE << 3)) a -= (BASE << 3); \
+        if (a >= (BASE << 2)) a -= (BASE << 2); \
+        if (a >= (BASE << 1)) a -= (BASE << 1); \
+        if (a >= BASE) a -= BASE; \
+    } while (0)
+#else
+#  define MOD(a) a %= BASE
+#endif
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+    uLong adler;
+    const Bytef *buf;
+    uInt len;
+{
+    unsigned long s1 = adler & 0xffff;
+    unsigned long s2 = (adler >> 16) & 0xffff;
+    int k;
+
+    if (buf == Z_NULL) return 1L;
+
+    while (len > 0) {
+        k = len < NMAX ? (int)len : NMAX;
+        len -= k;
+        while (k >= 16) {
+            DO16(buf);
+            buf += 16;
+            k -= 16;
+        }
+        if (k != 0) do {
+            s1 += *buf++;
+            s2 += s1;
+        } while (--k);
+        MOD(s1);
+        MOD(s2);
+    }
+    return (s2 << 16) | s1;
+}
diff --git a/zlib/algorithm.txt b/zlib/algorithm.txt
new file mode 100644 (file)
index 0000000..b022dde
--- /dev/null
@@ -0,0 +1,209 @@
+1. Compression algorithm (deflate)
+
+The deflation algorithm used by gzip (also zip and zlib) is a variation of
+LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in
+the input data.  The second occurrence of a string is replaced by a
+pointer to the previous string, in the form of a pair (distance,
+length).  Distances are limited to 32K bytes, and lengths are limited
+to 258 bytes. When a string does not occur anywhere in the previous
+32K bytes, it is emitted as a sequence of literal bytes.  (In this
+description, `string' must be taken as an arbitrary sequence of bytes,
+and is not restricted to printable characters.)
+
+Literals or match lengths are compressed with one Huffman tree, and
+match distances are compressed with another tree. The trees are stored
+in a compact form at the start of each block. The blocks can have any
+size (except that the compressed data for one block must fit in
+available memory). A block is terminated when deflate() determines that
+it would be useful to start another block with fresh trees. (This is
+somewhat similar to the behavior of LZW-based _compress_.)
+
+Duplicated strings are found using a hash table. All input strings of
+length 3 are inserted in the hash table. A hash index is computed for
+the next 3 bytes. If the hash chain for this index is not empty, all
+strings in the chain are compared with the current input string, and
+the longest match is selected.
+
+The hash chains are searched starting with the most recent strings, to
+favor small distances and thus take advantage of the Huffman encoding.
+The hash chains are singly linked. There are no deletions from the
+hash chains, the algorithm simply discards matches that are too old.
+
+To avoid a worst-case situation, very long hash chains are arbitrarily
+truncated at a certain length, determined by a runtime option (level
+parameter of deflateInit). So deflate() does not always find the longest
+possible match but generally finds a match which is long enough.
+
+deflate() also defers the selection of matches with a lazy evaluation
+mechanism. After a match of length N has been found, deflate() searches for
+a longer match at the next input byte. If a longer match is found, the
+previous match is truncated to a length of one (thus producing a single
+literal byte) and the process of lazy evaluation begins again. Otherwise,
+the original match is kept, and the next match search is attempted only N
+steps later.
+
+The lazy match evaluation is also subject to a runtime parameter. If
+the current match is long enough, deflate() reduces the search for a longer
+match, thus speeding up the whole process. If compression ratio is more
+important than speed, deflate() attempts a complete second search even if
+the first match is already long enough.
+
+The lazy match evaluation is not performed for the fastest compression
+modes (level parameter 1 to 3). For these fast modes, new strings
+are inserted in the hash table only when no match was found, or
+when the match is not too long. This degrades the compression ratio
+but saves time since there are both fewer insertions and fewer searches.
+
+
+2. Decompression algorithm (inflate)
+
+2.1 Introduction
+
+The key question is how to represent a Huffman code (or any prefix code) so
+that you can decode fast.  The most important characteristic is that shorter
+codes are much more common than longer codes, so pay attention to decoding the
+short codes fast, and let the long codes take longer to decode.
+
+inflate() sets up a first level table that covers some number of bits of
+input less than the length of longest code.  It gets that many bits from the
+stream, and looks it up in the table.  The table will tell if the next
+code is that many bits or less and how many, and if it is, it will tell
+the value, else it will point to the next level table for which inflate()
+grabs more bits and tries to decode a longer code.
+
+How many bits to make the first lookup is a tradeoff between the time it
+takes to decode and the time it takes to build the table.  If building the
+table took no time (and if you had infinite memory), then there would only
+be a first level table to cover all the way to the longest code.  However,
+building the table ends up taking a lot longer for more bits since short
+codes are replicated many times in such a table.  What inflate() does is
+simply to make the number of bits in the first table a variable, and  then
+to set that variable for the maximum speed.
+
+For inflate, which has 286 possible codes for the literal/length tree, the size
+of the first table is nine bits.  Also the distance trees have 30 possible
+values, and the size of the first table is six bits.  Note that for each of
+those cases, the table ended up one bit longer than the ``average'' code
+length, i.e. the code length of an approximately flat code which would be a
+little more than eight bits for 286 symbols and a little less than five bits
+for 30 symbols.
+
+
+2.2 More details on the inflate table lookup
+
+Ok, you want to know what this cleverly obfuscated inflate tree actually
+looks like.  You are correct that it's not a Huffman tree.  It is simply a
+lookup table for the first, let's say, nine bits of a Huffman symbol.  The
+symbol could be as short as one bit or as long as 15 bits.  If a particular
+symbol is shorter than nine bits, then that symbol's translation is duplicated
+in all those entries that start with that symbol's bits.  For example, if the
+symbol is four bits, then it's duplicated 32 times in a nine-bit table.  If a
+symbol is nine bits long, it appears in the table once.
+
+If the symbol is longer than nine bits, then that entry in the table points
+to another similar table for the remaining bits.  Again, there are duplicated
+entries as needed.  The idea is that most of the time the symbol will be short
+and there will only be one table look up.  (That's whole idea behind data
+compression in the first place.)  For the less frequent long symbols, there
+will be two lookups.  If you had a compression method with really long
+symbols, you could have as many levels of lookups as is efficient.  For
+inflate, two is enough.
+
+So a table entry either points to another table (in which case nine bits in
+the above example are gobbled), or it contains the translation for the symbol
+and the number of bits to gobble.  Then you start again with the next
+ungobbled bit.
+
+You may wonder: why not just have one lookup table for how ever many bits the
+longest symbol is?  The reason is that if you do that, you end up spending
+more time filling in duplicate symbol entries than you do actually decoding.
+At least for deflate's output that generates new trees every several 10's of
+kbytes.  You can imagine that filling in a 2^15 entry table for a 15-bit code
+would take too long if you're only decoding several thousand symbols.  At the
+other extreme, you could make a new table for every bit in the code.  In fact,
+that's essentially a Huffman tree.  But then you spend two much time
+traversing the tree while decoding, even for short symbols.
+
+So the number of bits for the first lookup table is a trade of the time to
+fill out the table vs. the time spent looking at the second level and above of
+the table.
+
+Here is an example, scaled down:
+
+The code being decoded, with 10 symbols, from 1 to 6 bits long:
+
+A: 0
+B: 10
+C: 1100
+D: 11010
+E: 11011
+F: 11100
+G: 11101
+H: 11110
+I: 111110
+J: 111111
+
+Let's make the first table three bits long (eight entries):
+
+000: A,1
+001: A,1
+010: A,1
+011: A,1
+100: B,2
+101: B,2
+110: -> table X (gobble 3 bits)
+111: -> table Y (gobble 3 bits)
+
+Each entry is what the bits decode as and how many bits that is, i.e. how
+many bits to gobble.  Or the entry points to another table, with the number of
+bits to gobble implicit in the size of the table.
+
+Table X is two bits long since the longest code starting with 110 is five bits
+long:
+
+00: C,1
+01: C,1
+10: D,2
+11: E,2
+
+Table Y is three bits long since the longest code starting with 111 is six
+bits long:
+
+000: F,2
+001: F,2
+010: G,2
+011: G,2
+100: H,2
+101: H,2
+110: I,3
+111: J,3
+
+So what we have here are three tables with a total of 20 entries that had to
+be constructed.  That's compared to 64 entries for a single table.  Or
+compared to 16 entries for a Huffman tree (six two entry tables and one four
+entry table).  Assuming that the code ideally represents the probability of
+the symbols, it takes on the average 1.25 lookups per symbol.  That's compared
+to one lookup for the single table, or 1.66 lookups per symbol for the
+Huffman tree.
+
+There, I think that gives you a picture of what's going on.  For inflate, the
+meaning of a particular symbol is often more than just a letter.  It can be a
+byte (a "literal"), or it can be either a length or a distance which
+indicates a base value and a number of bits to fetch after the code that is
+added to the base value.  Or it might be the special end-of-block code.  The
+data structures created in inftrees.c try to encode all that information
+compactly in the tables.
+
+
+Jean-loup Gailly        Mark Adler
+jloup@gzip.org          madler@alumni.caltech.edu
+
+
+References:
+
+[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data
+Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3,
+pp. 337-343.
+
+``DEFLATE Compressed Data Format Specification'' available in
+http://www.ietf.org/rfc/rfc1951.txt
diff --git a/zlib/compress.c b/zlib/compress.c
new file mode 100644 (file)
index 0000000..64f2a65
--- /dev/null
@@ -0,0 +1,79 @@
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: compress.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+     Compresses the source buffer into the destination buffer. The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer. Upon entry, destLen is the total size of the
+   destination buffer, which must be at least 0.1% larger than sourceLen plus
+   12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+    int level;
+{
+    z_stream stream;
+    int err;
+
+    stream.next_in = (Bytef*)source;
+    stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+    /* Check for source > 64K on 16-bit machine: */
+    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+    stream.next_out = dest;
+    stream.avail_out = (uInt)*destLen;
+    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+    stream.opaque = (voidpf)0;
+
+    err = deflateInit(&stream, level);
+    if (err != Z_OK) return err;
+
+    err = deflate(&stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        deflateEnd(&stream);
+        return err == Z_OK ? Z_BUF_ERROR : err;
+    }
+    *destLen = stream.total_out;
+
+    err = deflateEnd(&stream);
+    return err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+{
+    return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
+
+/* ===========================================================================
+     If the default memLevel or windowBits for deflateInit() is changed, then
+   this function needs to be updated.
+ */
+uLong ZEXPORT compressBound (sourceLen)
+    uLong sourceLen;
+{
+    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11;
+}
diff --git a/zlib/configure b/zlib/configure
new file mode 100755 (executable)
index 0000000..d698479
--- /dev/null
@@ -0,0 +1,445 @@
+#!/bin/sh
+# configure script for zlib. This script is needed only if
+# you wish to build a shared library and your system supports them,
+# of if you need special compiler, flags or install directory.
+# Otherwise, you can just use directly "make test; make install"
+#
+# To create a shared library, use "configure --shared"; by default a static
+# library is created. If the primitive shared library support provided here
+# does not work, use ftp://prep.ai.mit.edu/pub/gnu/libtool-*.tar.gz
+#
+# To impose specific compiler or flags or install directory, use for example:
+#    prefix=$HOME CC=cc CFLAGS="-O4" ./configure
+# or for csh/tcsh users:
+#    (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure)
+# LDSHARED is the command to be used to create a shared library
+
+# Incorrect settings of CC or CFLAGS may prevent creating a shared library.
+# If you have problems, try without defining CC and CFLAGS before reporting
+# an error.
+
+LIBS=libz.a
+LDFLAGS="-L. ${LIBS}"
+VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`
+VER2=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\)\\..*/\1/p' < zlib.h`
+VER1=`sed -n -e '/VERSION "/s/.*"\([0-9]*\)\\..*/\1/p' < zlib.h`
+AR=${AR-"ar rc"}
+RANLIB=${RANLIB-"ranlib"}
+prefix=${prefix-/usr/local}
+exec_prefix=${exec_prefix-'${prefix}'}
+libdir=${libdir-'${exec_prefix}/lib'}
+includedir=${includedir-'${prefix}/include'}
+mandir=${mandir-'${prefix}/share/man'}
+shared_ext='.so'
+shared=0
+gcc=0
+old_cc="$CC"
+old_cflags="$CFLAGS"
+
+while test $# -ge 1
+do
+case "$1" in
+    -h* | --h*)
+      echo 'usage:'
+      echo '  configure [--shared] [--prefix=PREFIX]  [--exec_prefix=EXPREFIX]'
+      echo '     [--libdir=LIBDIR] [--includedir=INCLUDEDIR]'
+        exit 0;;
+    -p*=* | --p*=*) prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+    -e*=* | --e*=*) exec_prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+    -l*=* | --libdir=*) libdir=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+    -i*=* | --includedir=*) includedir=`echo $1 | sed 's/[-a-z_]*=//'`;shift;;
+    -p* | --p*) prefix="$2"; shift; shift;;
+    -e* | --e*) exec_prefix="$2"; shift; shift;;
+    -l* | --l*) libdir="$2"; shift; shift;;
+    -i* | --i*) includedir="$2"; shift; shift;;
+    -s* | --s*) shared=1; shift;;
+    *) echo "unknown option: $1"; echo "$0 --help for help"; exit 1;;
+    esac
+done
+
+test=ztest$$
+cat > $test.c <<EOF
+extern int getchar();
+int hello() {return getchar();}
+EOF
+
+test -z "$CC" && echo Checking for gcc...
+cc=${CC-gcc}
+cflags=${CFLAGS-"-O3"}
+# to force the asm version use: CFLAGS="-O3 -DASMV" ./configure
+case "$cc" in
+  *gcc*) gcc=1;;
+esac
+
+if test "$gcc" -eq 1 && ($cc -c $cflags $test.c) 2>/dev/null; then
+  CC="$cc"
+  SFLAGS=${CFLAGS-"-fPIC -O3"}
+  CFLAGS="$cflags"
+  case `(uname -s || echo unknown) 2>/dev/null` in
+  Linux | linux | GNU | GNU/*) LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1"};;
+  CYGWIN* | Cygwin* | cygwin* )
+             EXE='.exe';;
+  QNX*)  # This is for QNX6. I suppose that the QNX rule below is for QNX2,QNX4
+         # (alain.bonnefoy@icbt.com)
+                 LDSHARED=${LDSHARED-"$cc -shared -Wl,-hlibz.so.1"};;
+  HP-UX*)        LDSHARED=${LDSHARED-"$cc -shared $SFLAGS"}
+                 shared_ext='.sl'
+                 SHAREDLIB='libz.sl';;
+  Darwin*)   shared_ext='.dylib'
+             SHAREDLIB=libz$shared_ext
+             SHAREDLIBV=libz.$VER$shared_ext
+             SHAREDLIBM=libz.$VER1$shared_ext
+             LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name /usr/lib/$SHAREDLIBV -compatibility_version $VER2 -current_version $VER"}
+             libdir='/usr/lib'
+             includedir='/usr/include';;
+  *)             LDSHARED=${LDSHARED-"$cc -shared"};;
+  esac
+else
+  # find system name and corresponding cc options
+  CC=${CC-cc}
+  case `(uname -sr || echo unknown) 2>/dev/null` in
+  HP-UX*)    SFLAGS=${CFLAGS-"-O +z"}
+             CFLAGS=${CFLAGS-"-O"}
+#            LDSHARED=${LDSHARED-"ld -b +vnocompatwarnings"}
+             LDSHARED=${LDSHARED-"ld -b"}
+             shared_ext='.sl'
+             SHAREDLIB='libz.sl';;
+  IRIX*)     SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."}
+             CFLAGS=${CFLAGS-"-ansi -O2"}
+             LDSHARED=${LDSHARED-"cc -shared"};;
+  OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"}
+             CFLAGS=${CFLAGS-"-O -std1"}
+             LDSHARED=${LDSHARED-"cc -shared  -Wl,-soname,libz.so -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"};;
+  OSF1*)     SFLAGS=${CFLAGS-"-O -std1"}
+             CFLAGS=${CFLAGS-"-O -std1"}
+             LDSHARED=${LDSHARED-"cc -shared"};;
+  QNX*)      SFLAGS=${CFLAGS-"-4 -O"}
+             CFLAGS=${CFLAGS-"-4 -O"}
+             LDSHARED=${LDSHARED-"cc"}
+             RANLIB=${RANLIB-"true"}
+             AR="cc -A";;
+  SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "}
+             CFLAGS=${CFLAGS-"-O3"}
+             LDSHARED=${LDSHARED-"cc -dy -KPIC -G"};;
+  SunOS\ 5*) SFLAGS=${CFLAGS-"-fast -xcg89 -KPIC -R."}
+             CFLAGS=${CFLAGS-"-fast -xcg89"}
+             LDSHARED=${LDSHARED-"cc -G"};;
+  SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"}
+             CFLAGS=${CFLAGS-"-O2"}
+             LDSHARED=${LDSHARED-"ld"};;
+  UNIX_System_V\ 4.2.0)
+             SFLAGS=${CFLAGS-"-KPIC -O"}
+             CFLAGS=${CFLAGS-"-O"}
+             LDSHARED=${LDSHARED-"cc -G"};;
+  UNIX_SV\ 4.2MP)
+             SFLAGS=${CFLAGS-"-Kconform_pic -O"}
+             CFLAGS=${CFLAGS-"-O"}
+             LDSHARED=${LDSHARED-"cc -G"};;
+  OpenUNIX\ 5)
+             SFLAGS=${CFLAGS-"-KPIC -O"}
+             CFLAGS=${CFLAGS-"-O"}
+             LDSHARED=${LDSHARED-"cc -G"};;
+  AIX*)  # Courtesy of dbakker@arrayasolutions.com
+             SFLAGS=${CFLAGS-"-O -qmaxmem=8192"}
+             CFLAGS=${CFLAGS-"-O -qmaxmem=8192"}
+             LDSHARED=${LDSHARED-"xlc -G"};;
+  # send working options for other systems to support@gzip.org
+  *)         SFLAGS=${CFLAGS-"-O"}
+             CFLAGS=${CFLAGS-"-O"}
+             LDSHARED=${LDSHARED-"cc -shared"};;
+  esac
+fi
+
+SHAREDLIB=${SHAREDLIB-"libz$shared_ext"}
+SHAREDLIBV=${SHAREDLIBV-"libz$shared_ext.$VER"}
+SHAREDLIBM=${SHAREDLIBM-"libz$shared_ext.$VER1"}
+
+if test $shared -eq 1; then
+  echo Checking for shared library support...
+  # we must test in two steps (cc then ld), required at least on SunOS 4.x
+  if test "`($CC -c $SFLAGS $test.c) 2>&1`" = "" &&
+     test "`($LDSHARED -o $test$shared_ext $test.o) 2>&1`" = ""; then
+    CFLAGS="$SFLAGS"
+    LIBS="$SHAREDLIBV"
+    echo Building shared library $SHAREDLIBV with $CC.
+  elif test -z "$old_cc" -a -z "$old_cflags"; then
+    echo No shared library support.
+    shared=0;
+  else
+    echo 'No shared library support; try without defining CC and CFLAGS'
+    shared=0;
+  fi
+fi
+if test $shared -eq 0; then
+  LDSHARED="$CC"
+  echo Building static library $LIBS version $VER with $CC.
+else
+  LDFLAGS="-L. ${SHAREDLIBV}"
+fi
+
+cat > $test.c <<EOF
+#include <unistd.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  sed < zconf.in.h "/HAVE_UNISTD_H/s%0%1%" > zconf.h
+  echo "Checking for unistd.h... Yes."
+else
+  cp -p zconf.in.h zconf.h
+  echo "Checking for unistd.h... No."
+fi
+
+cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+#include "zconf.h"
+
+int main()
+{
+#ifndef STDC
+  choke me
+#endif
+
+  return 0;
+}
+EOF
+
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  echo "Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf()"
+
+  cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...)
+{
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+  return 0;
+}
+
+int main()
+{
+  return (mytest("Hello%d\n", 1));
+}
+EOF
+
+  if test "`($CC $CFLAGS -o $test $test.c) 2>&1`" = ""; then
+    echo "Checking for vsnprintf() in stdio.h... Yes."
+
+    cat >$test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...)
+{
+  int n;
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  n = vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+  return n;
+}
+
+int main()
+{
+  return (mytest("Hello%d\n", 1));
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      echo "Checking for return value of vsnprintf()... Yes."
+    else
+      CFLAGS="$CFLAGS -DHAS_vsnprintf_void"
+      echo "Checking for return value of vsnprintf()... No."
+      echo "  WARNING: apparently vsnprintf() does not return a value. zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities."
+    fi
+  else
+    CFLAGS="$CFLAGS -DNO_vsnprintf"
+    echo "Checking for vsnprintf() in stdio.h... No."
+    echo "  WARNING: vsnprintf() not found, falling back to vsprintf(). zlib"
+    echo "  can build but will be open to possible buffer-overflow security"
+    echo "  vulnerabilities."
+
+    cat >$test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...)
+{
+  int n;
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  n = vsprintf(buf, fmt, ap);
+  va_end(ap);
+  return n;
+}
+
+int main()
+{
+  return (mytest("Hello%d\n", 1));
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      echo "Checking for return value of vsprintf()... Yes."
+    else
+      CFLAGS="$CFLAGS -DHAS_vsprintf_void"
+      echo "Checking for return value of vsprintf()... No."
+      echo "  WARNING: apparently vsprintf() does not return a value. zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities."
+    fi
+  fi
+else
+  echo "Checking whether to use vs[n]printf() or s[n]printf()... using s[n]printf()"
+
+  cat >$test.c <<EOF
+#include <stdio.h>
+
+int mytest()
+{
+  char buf[20];
+
+  snprintf(buf, sizeof(buf), "%s", "foo");
+  return 0;
+}
+
+int main()
+{
+  return (mytest());
+}
+EOF
+
+  if test "`($CC $CFLAGS -o $test $test.c) 2>&1`" = ""; then
+    echo "Checking for snprintf() in stdio.h... Yes."
+
+    cat >$test.c <<EOF
+#include <stdio.h>
+
+int mytest()
+{
+  char buf[20];
+
+  return snprintf(buf, sizeof(buf), "%s", "foo");
+}
+
+int main()
+{
+  return (mytest());
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      echo "Checking for return value of snprintf()... Yes."
+    else
+      CFLAGS="$CFLAGS -DHAS_snprintf_void"
+      echo "Checking for return value of snprintf()... No."
+      echo "  WARNING: apparently snprintf() does not return a value. zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities."
+    fi
+  else
+    CFLAGS="$CFLAGS -DNO_snprintf"
+    echo "Checking for snprintf() in stdio.h... No."
+    echo "  WARNING: snprintf() not found, falling back to sprintf(). zlib"
+    echo "  can build but will be open to possible buffer-overflow security"
+    echo "  vulnerabilities."
+
+    cat >$test.c <<EOF
+#include <stdio.h>
+
+int mytest()
+{
+  char buf[20];
+
+  return sprintf(buf, "%s", "foo");
+}
+
+int main()
+{
+  return (mytest());
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      echo "Checking for return value of sprintf()... Yes."
+    else
+      CFLAGS="$CFLAGS -DHAS_sprintf_void"
+      echo "Checking for return value of sprintf()... No."
+      echo "  WARNING: apparently sprintf() does not return a value. zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities."
+    fi
+  fi
+fi
+
+cat >$test.c <<EOF
+#include <errno.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  echo "Checking for errno.h... Yes."
+else
+  echo "Checking for errno.h... No."
+  CFLAGS="$CFLAGS -DNO_ERRNO_H"
+fi
+
+cat > $test.c <<EOF
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+caddr_t hello() {
+  return mmap((caddr_t)0, (off_t)0, PROT_READ, MAP_SHARED, 0, (off_t)0);
+}
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  CFLAGS="$CFLAGS -DUSE_MMAP"
+  echo Checking for mmap support... Yes.
+else
+  echo Checking for mmap support... No.
+fi
+
+CPP=${CPP-"$CC -E"}
+case $CFLAGS in
+  *ASMV*)
+    if test "`nm $test.o | grep _hello`" = ""; then
+      CPP="$CPP -DNO_UNDERLINE"
+      echo Checking for underline in external names... No.
+    else
+      echo Checking for underline in external names... Yes.
+    fi;;
+esac
+
+rm -f $test.[co] $test $test$shared_ext
+
+# udpate Makefile
+sed < Makefile.in "
+/^CC *=/s#=.*#=$CC#
+/^CFLAGS *=/s#=.*#=$CFLAGS#
+/^CPP *=/s#=.*#=$CPP#
+/^LDSHARED *=/s#=.*#=$LDSHARED#
+/^LIBS *=/s#=.*#=$LIBS#
+/^SHAREDLIB *=/s#=.*#=$SHAREDLIB#
+/^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV#
+/^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM#
+/^AR *=/s#=.*#=$AR#
+/^RANLIB *=/s#=.*#=$RANLIB#
+/^EXE *=/s#=.*#=$EXE#
+/^prefix *=/s#=.*#=$prefix#
+/^exec_prefix *=/s#=.*#=$exec_prefix#
+/^libdir *=/s#=.*#=$libdir#
+/^includedir *=/s#=.*#=$includedir#
+/^mandir *=/s#=.*#=$mandir#
+/^LDFLAGS *=/s#=.*#=$LDFLAGS#
+" > Makefile
diff --git a/zlib/crc32.c b/zlib/crc32.c
new file mode 100644 (file)
index 0000000..bb305b9
--- /dev/null
@@ -0,0 +1,311 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
+ * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
+ * tables for updating the shift register in one step with three exclusive-ors
+ * instead of four steps with four exclusive-ors.  This results about a factor
+ * of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
+ */
+
+/* @(#) $Id: crc32.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#ifdef MAKECRCH
+#  include <stdio.h>
+#  ifndef DYNAMIC_CRC_TABLE
+#    define DYNAMIC_CRC_TABLE
+#  endif /* !DYNAMIC_CRC_TABLE */
+#endif /* MAKECRCH */
+
+#include "zutil.h"      /* for STDC and FAR definitions */
+
+#define local static
+
+/* Find a four-byte integer type for crc32_little() and crc32_big(). */
+#ifndef NOBYFOUR
+#  ifdef STDC           /* need ANSI C limits.h to determine sizes */
+#    include <limits.h>
+#    define BYFOUR
+#    if (UINT_MAX == 0xffffffffUL)
+       typedef unsigned int u4;
+#    else
+#      if (ULONG_MAX == 0xffffffffUL)
+         typedef unsigned long u4;
+#      else
+#        if (USHRT_MAX == 0xffffffffUL)
+           typedef unsigned short u4;
+#        else
+#          undef BYFOUR     /* can't find a four-byte integer type! */
+#        endif
+#      endif
+#    endif
+#  endif /* STDC */
+#endif /* !NOBYFOUR */
+
+/* Definitions for doing the crc four data bytes at a time. */
+#ifdef BYFOUR
+#  define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \
+                (((w)&0xff00)<<8)+(((w)&0xff)<<24))
+   local unsigned long crc32_little OF((unsigned long,
+                        const unsigned char FAR *, unsigned));
+   local unsigned long crc32_big OF((unsigned long,
+                        const unsigned char FAR *, unsigned));
+#  define TBLS 8
+#else
+#  define TBLS 1
+#endif /* BYFOUR */
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local int crc_table_empty = 1;
+local unsigned long FAR crc_table[TBLS][256];
+local void make_crc_table OF((void));
+#ifdef MAKECRCH
+   local void write_table OF((FILE *, const unsigned long FAR *));
+#endif /* MAKECRCH */
+
+/*
+  Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
+  x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+  Polynomials over GF(2) are represented in binary, one bit per coefficient,
+  with the lowest powers in the most significant bit.  Then adding polynomials
+  is just exclusive-or, and multiplying a polynomial by x is a right shift by
+  one.  If we call the above polynomial p, and represent a byte as the
+  polynomial q, also with the lowest power in the most significant bit (so the
+  byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+  where a mod b means the remainder after dividing a by b.
+
+  This calculation is done using the shift-register method of multiplying and
+  taking the remainder.  The register is initialized to zero, and for each
+  incoming bit, x^32 is added mod p to the register if the bit is a one (where
+  x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+  x (which is shifting right by one and adding x^32 mod p if the bit shifted
+  out is a one).  We start with the highest power (least significant bit) of
+  q and repeat for all eight bits of q.
+
+  The first table is simply the CRC of all possible eight bit values.  This is
+  all the information needed to generate CRCs on data a byte at a time for all
+  combinations of CRC register values and incoming bytes.  The remaining tables
+  allow for word-at-a-time CRC calculation for both big-endian and little-
+  endian machines, where a word is four bytes.
+*/
+local void make_crc_table()
+{
+    unsigned long c;
+    int n, k;
+    unsigned long poly;            /* polynomial exclusive-or pattern */
+    /* terms of polynomial defining this crc (except x^32): */
+    static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+    /* make exclusive-or pattern from polynomial (0xedb88320UL) */
+    poly = 0UL;
+    for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++)
+        poly |= 1UL << (31 - p[n]);
+
+    /* generate a crc for every 8-bit value */
+    for (n = 0; n < 256; n++) {
+        c = (unsigned long)n;
+        for (k = 0; k < 8; k++)
+            c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+        crc_table[0][n] = c;
+    }
+
+#ifdef BYFOUR
+    /* generate crc for each value followed by one, two, and three zeros, and
+       then the byte reversal of those as well as the first table */
+    for (n = 0; n < 256; n++) {
+        c = crc_table[0][n];
+        crc_table[4][n] = REV(c);
+        for (k = 1; k < 4; k++) {
+            c = crc_table[0][c & 0xff] ^ (c >> 8);
+            crc_table[k][n] = c;
+            crc_table[k + 4][n] = REV(c);
+        }
+    }
+#endif /* BYFOUR */
+
+  crc_table_empty = 0;
+
+#ifdef MAKECRCH
+    /* write out CRC tables to crc32.h */
+    {
+        FILE *out;
+
+        out = fopen("crc32.h", "w");
+        if (out == NULL) return;
+        fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
+        fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
+        fprintf(out, "local const unsigned long FAR ");
+        fprintf(out, "crc_table[TBLS][256] =\n{\n  {\n");
+        write_table(out, crc_table[0]);
+#  ifdef BYFOUR
+        fprintf(out, "#ifdef BYFOUR\n");
+        for (k = 1; k < 8; k++) {
+            fprintf(out, "  },\n  {\n");
+            write_table(out, crc_table[k]);
+        }
+        fprintf(out, "#endif\n");
+#  endif /* BYFOUR */
+        fprintf(out, "  }\n};\n");
+        fclose(out);
+    }
+#endif /* MAKECRCH */
+}
+
+#ifdef MAKECRCH
+local void write_table(out, table)
+    FILE *out;
+    const unsigned long FAR *table;
+{
+    int n;
+
+    for (n = 0; n < 256; n++)
+        fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : "    ", table[n],
+                n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
+}
+#endif /* MAKECRCH */
+
+#else /* !DYNAMIC_CRC_TABLE */
+/* ========================================================================
+ * Tables of CRC-32s of all single-byte values, made by make_crc_table().
+ */
+#include "crc32.h"
+#endif /* DYNAMIC_CRC_TABLE */
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const unsigned long FAR * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+  if (crc_table_empty) make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+  return (const unsigned long FAR *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
+#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
+
+/* ========================================================================= */
+unsigned long ZEXPORT crc32(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    unsigned len;
+{
+    if (buf == Z_NULL) return 0UL;
+
+#ifdef DYNAMIC_CRC_TABLE
+    if (crc_table_empty)
+        make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+
+#ifdef BYFOUR
+    if (sizeof(void *) == sizeof(ptrdiff_t)) {
+        u4 endian;
+
+        endian = 1;
+        if (*((unsigned char *)(&endian)))
+            return crc32_little(crc, buf, len);
+        else
+            return crc32_big(crc, buf, len);
+    }
+#endif /* BYFOUR */
+    crc = crc ^ 0xffffffffUL;
+    while (len >= 8) {
+        DO8;
+        len -= 8;
+    }
+    if (len) do {
+        DO1;
+    } while (--len);
+    return crc ^ 0xffffffffUL;
+}
+
+#ifdef BYFOUR
+
+/* ========================================================================= */
+#define DOLIT4 c ^= *buf4++; \
+        c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
+            crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
+#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
+
+/* ========================================================================= */
+local unsigned long crc32_little(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    unsigned len;
+{
+    register u4 c;
+    register const u4 FAR *buf4;
+
+    c = (u4)crc;
+    c = ~c;
+    while (len && ((ptrdiff_t)buf & 3)) {
+        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+        len--;
+    }
+
+    buf4 = (const u4 FAR *)buf;
+    while (len >= 32) {
+        DOLIT32;
+        len -= 32;
+    }
+    while (len >= 4) {
+        DOLIT4;
+        len -= 4;
+    }
+    buf = (const unsigned char FAR *)buf4;
+
+    if (len) do {
+        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+    } while (--len);
+    c = ~c;
+    return (unsigned long)c;
+}
+
+/* ========================================================================= */
+#define DOBIG4 c ^= *++buf4; \
+        c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
+            crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
+#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
+
+/* ========================================================================= */
+local unsigned long crc32_big(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    unsigned len;
+{
+    register u4 c;
+    register const u4 FAR *buf4;
+
+    c = REV((u4)crc);
+    c = ~c;
+    while (len && ((ptrdiff_t)buf & 3)) {
+        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+        len--;
+    }
+
+    buf4 = (const u4 FAR *)buf;
+    buf4--;
+    while (len >= 32) {
+        DOBIG32;
+        len -= 32;
+    }
+    while (len >= 4) {
+        DOBIG4;
+        len -= 4;
+    }
+    buf4++;
+    buf = (const unsigned char FAR *)buf4;
+
+    if (len) do {
+        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+    } while (--len);
+    c = ~c;
+    return (unsigned long)(REV(c));
+}
+
+#endif /* BYFOUR */
diff --git a/zlib/crc32.h b/zlib/crc32.h
new file mode 100644 (file)
index 0000000..8053b61
--- /dev/null
@@ -0,0 +1,441 @@
+/* crc32.h -- tables for rapid CRC calculation
+ * Generated automatically by crc32.c
+ */
+
+local const unsigned long FAR crc_table[TBLS][256] =
+{
+  {
+    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+    0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+    0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+    0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+    0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+    0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+    0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+    0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+    0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+    0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+    0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+    0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+    0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+    0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+    0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+    0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+    0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+    0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+    0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+    0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+    0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+    0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+    0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+    0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+    0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+    0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+    0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+    0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+    0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+    0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+    0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+    0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+    0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+    0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+    0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+    0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+    0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+    0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+    0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+    0x2d02ef8dUL
+#ifdef BYFOUR
+  },
+  {
+    0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+    0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+    0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+    0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+    0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+    0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+    0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+    0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+    0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+    0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+    0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+    0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+    0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+    0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+    0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+    0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+    0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+    0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+    0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+    0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+    0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+    0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+    0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+    0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+    0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+    0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+    0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+    0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+    0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+    0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+    0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+    0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+    0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+    0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+    0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+    0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+    0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+    0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+    0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+    0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+    0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+    0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+    0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+    0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+    0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+    0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+    0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+    0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+    0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+    0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+    0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+    0x9324fd72UL
+  },
+  {
+    0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+    0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+    0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+    0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+    0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+    0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+    0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+    0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+    0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+    0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+    0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+    0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+    0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+    0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+    0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+    0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+    0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+    0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+    0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+    0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+    0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+    0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+    0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+    0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+    0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+    0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+    0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+    0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+    0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+    0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+    0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+    0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+    0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+    0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+    0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+    0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+    0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+    0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+    0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+    0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+    0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+    0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+    0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+    0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+    0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+    0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+    0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+    0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+    0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+    0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+    0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+    0xbe9834edUL
+  },
+  {
+    0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+    0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+    0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+    0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+    0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+    0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+    0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+    0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+    0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+    0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+    0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+    0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+    0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+    0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+    0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+    0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+    0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+    0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+    0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+    0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+    0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+    0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+    0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+    0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+    0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+    0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+    0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+    0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+    0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+    0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+    0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+    0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+    0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+    0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+    0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+    0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+    0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+    0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+    0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+    0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+    0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+    0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+    0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+    0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+    0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+    0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+    0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+    0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+    0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+    0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+    0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+    0xde0506f1UL
+  },
+  {
+    0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+    0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+    0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+    0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+    0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+    0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+    0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+    0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+    0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+    0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+    0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+    0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+    0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+    0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+    0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+    0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+    0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+    0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+    0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+    0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+    0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+    0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+    0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+    0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+    0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+    0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+    0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+    0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+    0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+    0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+    0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+    0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+    0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+    0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+    0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+    0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+    0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+    0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+    0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+    0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+    0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+    0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+    0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+    0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+    0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+    0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+    0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+    0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+    0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+    0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+    0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+    0x8def022dUL
+  },
+  {
+    0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+    0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+    0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+    0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+    0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+    0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+    0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+    0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+    0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+    0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+    0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+    0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+    0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+    0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+    0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+    0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+    0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+    0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+    0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+    0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+    0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+    0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+    0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+    0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+    0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+    0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+    0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+    0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+    0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+    0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+    0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+    0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+    0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+    0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+    0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+    0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+    0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+    0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+    0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+    0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+    0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+    0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+    0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+    0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+    0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+    0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+    0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+    0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+    0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+    0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+    0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+    0x72fd2493UL
+  },
+  {
+    0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+    0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+    0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+    0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+    0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+    0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+    0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+    0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+    0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+    0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+    0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+    0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+    0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+    0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+    0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+    0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+    0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+    0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+    0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+    0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+    0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+    0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+    0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+    0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+    0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+    0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+    0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+    0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+    0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+    0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+    0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+    0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+    0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+    0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+    0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+    0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+    0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+    0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+    0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+    0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+    0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+    0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+    0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+    0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+    0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+    0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+    0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+    0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+    0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+    0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+    0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+    0xed3498beUL
+  },
+  {
+    0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+    0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+    0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+    0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+    0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+    0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+    0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+    0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+    0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+    0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+    0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+    0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+    0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+    0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+    0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+    0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+    0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+    0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+    0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+    0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+    0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+    0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+    0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+    0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+    0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+    0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+    0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+    0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+    0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+    0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+    0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+    0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+    0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+    0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+    0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+    0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+    0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+    0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+    0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+    0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+    0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+    0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+    0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+    0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+    0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+    0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+    0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+    0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+    0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+    0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+    0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+    0xf10605deUL
+#endif
+  }
+};
diff --git a/zlib/deflate.c b/zlib/deflate.c
new file mode 100644 (file)
index 0000000..99214e3
--- /dev/null
@@ -0,0 +1,1502 @@
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ *  ALGORITHM
+ *
+ *      The "deflation" process depends on being able to identify portions
+ *      of the input text which are identical to earlier input (within a
+ *      sliding window trailing behind the input currently being processed).
+ *
+ *      The most straightforward technique turns out to be the fastest for
+ *      most input files: try all possible matches and select the longest.
+ *      The key feature of this algorithm is that insertions into the string
+ *      dictionary are very simple and thus fast, and deletions are avoided
+ *      completely. Insertions are performed at each input character, whereas
+ *      string matches are performed only when the previous match ends. So it
+ *      is preferable to spend more time in matches to allow very fast string
+ *      insertions and avoid deletions. The matching algorithm for small
+ *      strings is inspired from that of Rabin & Karp. A brute force approach
+ *      is used to find longer strings when a small match has been found.
+ *      A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ *      (by Leonid Broukhis).
+ *         A previous version of this file used a more sophisticated algorithm
+ *      (by Fiala and Greene) which is guaranteed to run in linear amortized
+ *      time, but has a larger average cost, uses more memory and is patented.
+ *      However the F&G algorithm may be faster for some highly redundant
+ *      files if the parameter max_chain_length (described below) is too large.
+ *
+ *  ACKNOWLEDGEMENTS
+ *
+ *      The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ *      I found it in 'freeze' written by Leonid Broukhis.
+ *      Thanks to many people for bug reports and testing.
+ *
+ *  REFERENCES
+ *
+ *      Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ *      Available in http://www.ietf.org/rfc/rfc1951.txt
+ *
+ *      A description of the Rabin and Karp algorithm is given in the book
+ *         "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ *      Fiala,E.R., and Greene,D.H.
+ *         Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $Id: deflate.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#include "deflate.h"
+
+const char deflate_copyright[] =
+   " deflate 1.2.1 Copyright 1995-2003 Jean-loup Gailly ";
+/*
+  If you use the zlib library in a product, an acknowledgment is welcome
+  in the documentation of your product. If for some reason you cannot
+  include such an acknowledgment, I would appreciate that you keep this
+  copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ *  Function prototypes.
+ */
+typedef enum {
+    need_more,      /* block not completed, need more input or more output */
+    block_done,     /* block flush performed */
+    finish_started, /* finish started, need only more output at next deflate */
+    finish_done     /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window    OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast   OF((deflate_state *s, int flush));
+#ifndef FASTEST
+local block_state deflate_slow   OF((deflate_state *s, int flush));
+#endif
+local void lm_init        OF((deflate_state *s));
+local void putShortMSB    OF((deflate_state *s, uInt b));
+local void flush_pending  OF((z_streamp strm));
+local int read_buf        OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifndef FASTEST
+#ifdef ASMV
+      void match_init OF((void)); /* asm code initialization */
+      uInt longest_match  OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match  OF((deflate_state *s, IPos cur_match));
+#endif
+#endif
+local uInt longest_match_fast OF((deflate_state *s, IPos cur_match));
+
+#ifdef DEBUG
+local  void check_match OF((deflate_state *s, IPos start, IPos match,
+                            int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+#  define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+   ush good_length; /* reduce lazy search above this match length */
+   ush max_lazy;    /* do not perform lazy search above this match length */
+   ush nice_length; /* quit search above this match length */
+   ush max_chain;
+   compress_func func;
+} config;
+
+#ifdef FASTEST
+local const config configuration_table[2] = {
+/*      good lazy nice chain */
+/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
+/* 1 */ {4,    4,  8,    4, deflate_fast}}; /* max speed, no lazy matches */
+#else
+local const config configuration_table[10] = {
+/*      good lazy nice chain */
+/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
+/* 1 */ {4,    4,  8,    4, deflate_fast}, /* max speed, no lazy matches */
+/* 2 */ {4,    5, 16,    8, deflate_fast},
+/* 3 */ {4,    6, 32,   32, deflate_fast},
+
+/* 4 */ {4,    4, 16,   16, deflate_slow},  /* lazy matches */
+/* 5 */ {8,   16, 32,   32, deflate_slow},
+/* 6 */ {8,   16, 128, 128, deflate_slow},
+/* 7 */ {8,   32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
+#endif
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+#ifndef NO_DUMMY_DECL
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+#endif
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN  assertion: all calls to to UPDATE_HASH are made with consecutive
+ *    input characters, so that a running hash key can be computed from the
+ *    previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN  assertion: all calls to to INSERT_STRING are made with consecutive
+ *    input characters and the first MIN_MATCH bytes of str are valid
+ *    (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+    match_head = s->head[s->ins_h], \
+    s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+    match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
+    s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+    s->head[s->hash_size-1] = NIL; \
+    zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+    z_streamp strm;
+    int level;
+    const char *version;
+    int stream_size;
+{
+    return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+                         Z_DEFAULT_STRATEGY, version, stream_size);
+    /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+                  version, stream_size)
+    z_streamp strm;
+    int  level;
+    int  method;
+    int  windowBits;
+    int  memLevel;
+    int  strategy;
+    const char *version;
+    int stream_size;
+{
+    deflate_state *s;
+    int wrap = 1;
+    static const char my_version[] = ZLIB_VERSION;
+
+    ushf *overlay;
+    /* We overlay pending_buf and d_buf+l_buf. This works since the average
+     * output size for (length,distance) codes is <= 24 bits.
+     */
+
+    if (version == Z_NULL || version[0] != my_version[0] ||
+        stream_size != sizeof(z_stream)) {
+        return Z_VERSION_ERROR;
+    }
+    if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+    strm->msg = Z_NULL;
+    if (strm->zalloc == (alloc_func)0) {
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+    }
+    if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+
+#ifdef FASTEST
+    if (level != 0) level = 1;
+#else
+    if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+
+    if (windowBits < 0) { /* suppress zlib wrapper */
+        wrap = 0;
+        windowBits = -windowBits;
+    }
+#ifdef GZIP
+    else if (windowBits > 15) {
+        wrap = 2;       /* write gzip wrapper instead */
+        windowBits -= 16;
+    }
+#endif
+    if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+        windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+        strategy < 0 || strategy > Z_RLE) {
+        return Z_STREAM_ERROR;
+    }
+    if (windowBits == 8) windowBits = 9;  /* until 256-byte window bug fixed */
+    s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+    if (s == Z_NULL) return Z_MEM_ERROR;
+    strm->state = (struct internal_state FAR *)s;
+    s->strm = strm;
+
+    s->wrap = wrap;
+    s->w_bits = windowBits;
+    s->w_size = 1 << s->w_bits;
+    s->w_mask = s->w_size - 1;
+
+    s->hash_bits = memLevel + 7;
+    s->hash_size = 1 << s->hash_bits;
+    s->hash_mask = s->hash_size - 1;
+    s->hash_shift =  ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+    s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+    s->prev   = (Posf *)  ZALLOC(strm, s->w_size, sizeof(Pos));
+    s->head   = (Posf *)  ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+    s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+    overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+    s->pending_buf = (uchf *) overlay;
+    s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+    if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+        s->pending_buf == Z_NULL) {
+        s->status = FINISH_STATE;
+        strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+        deflateEnd (strm);
+        return Z_MEM_ERROR;
+    }
+    s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+    s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+    s->level = level;
+    s->strategy = strategy;
+    s->method = (Byte)method;
+
+    return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+    z_streamp strm;
+    const Bytef *dictionary;
+    uInt  dictLength;
+{
+    deflate_state *s;
+    uInt length = dictLength;
+    uInt n;
+    IPos hash_head = 0;
+
+    if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
+        strm->state->wrap == 2 ||
+        (strm->state->wrap == 1 && strm->state->status != INIT_STATE))
+        return Z_STREAM_ERROR;
+
+    s = strm->state;
+    if (s->wrap)
+        strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+    if (length < MIN_MATCH) return Z_OK;
+    if (length > MAX_DIST(s)) {
+        length = MAX_DIST(s);
+#ifndef USE_DICT_HEAD
+        dictionary += dictLength - length; /* use the tail of the dictionary */
+#endif
+    }
+    zmemcpy(s->window, dictionary, length);
+    s->strstart = length;
+    s->block_start = (long)length;
+
+    /* Insert all strings in the hash table (except for the last two bytes).
+     * s->lookahead stays null, so s->ins_h will be recomputed at the next
+     * call of fill_window.
+     */
+    s->ins_h = s->window[0];
+    UPDATE_HASH(s, s->ins_h, s->window[1]);
+    for (n = 0; n <= length - MIN_MATCH; n++) {
+        INSERT_STRING(s, n, hash_head);
+    }
+    if (hash_head) hash_head = 0;  /* to make compiler happy */
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+    z_streamp strm;
+{
+    deflate_state *s;
+
+    if (strm == Z_NULL || strm->state == Z_NULL ||
+        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) {
+        return Z_STREAM_ERROR;
+    }
+
+    strm->total_in = strm->total_out = 0;
+    strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+    strm->data_type = Z_UNKNOWN;
+
+    s = (deflate_state *)strm->state;
+    s->pending = 0;
+    s->pending_out = s->pending_buf;
+
+    if (s->wrap < 0) {
+        s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
+    }
+    s->status = s->wrap ? INIT_STATE : BUSY_STATE;
+    strm->adler =
+#ifdef GZIP
+        s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
+#endif
+        adler32(0L, Z_NULL, 0);
+    s->last_flush = Z_NO_FLUSH;
+
+    _tr_init(s);
+    lm_init(s);
+
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePrime (strm, bits, value)
+    z_streamp strm;
+    int bits;
+    int value;
+{
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    strm->state->bi_valid = bits;
+    strm->state->bi_buf = (ush)(value & ((1 << bits) - 1));
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+    z_streamp strm;
+    int level;
+    int strategy;
+{
+    deflate_state *s;
+    compress_func func;
+    int err = Z_OK;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    s = strm->state;
+
+#ifdef FASTEST
+    if (level != 0) level = 1;
+#else
+    if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+    if (level < 0 || level > 9 || strategy < 0 || strategy > Z_RLE) {
+        return Z_STREAM_ERROR;
+    }
+    func = configuration_table[s->level].func;
+
+    if (func != configuration_table[level].func && strm->total_in != 0) {
+        /* Flush the last buffer: */
+        err = deflate(strm, Z_PARTIAL_FLUSH);
+    }
+    if (s->level != level) {
+        s->level = level;
+        s->max_lazy_match   = configuration_table[level].max_lazy;
+        s->good_match       = configuration_table[level].good_length;
+        s->nice_match       = configuration_table[level].nice_length;
+        s->max_chain_length = configuration_table[level].max_chain;
+    }
+    s->strategy = strategy;
+    return err;
+}
+
+/* =========================================================================
+ * For the default windowBits of 15 and memLevel of 8, this function returns
+ * a close to exact, as well as small, upper bound on the compressed size.
+ * They are coded as constants here for a reason--if the #define's are
+ * changed, then this function needs to be changed as well.  The return
+ * value for 15 and 8 only works for those exact settings.
+ *
+ * For any setting other than those defaults for windowBits and memLevel,
+ * the value returned is a conservative worst case for the maximum expansion
+ * resulting from using fixed blocks instead of stored blocks, which deflate
+ * can emit on compressed data for some combinations of the parameters.
+ *
+ * This function could be more sophisticated to provide closer upper bounds
+ * for every combination of windowBits and memLevel, as well as wrap.
+ * But even the conservative upper bound of about 14% expansion does not
+ * seem onerous for output buffer allocation.
+ */
+uLong ZEXPORT deflateBound(strm, sourceLen)
+    z_streamp strm;
+    uLong sourceLen;
+{
+    deflate_state *s;
+    uLong destLen;
+
+    /* conservative upper bound */
+    destLen = sourceLen +
+              ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11;
+
+    /* if can't get parameters, return conservative bound */
+    if (strm == Z_NULL || strm->state == Z_NULL)
+        return destLen;
+
+    /* if not default parameters, return conservative bound */
+    s = strm->state;
+    if (s->w_bits != 15 || s->hash_bits != 8 + 7)
+        return destLen;
+
+    /* default settings: return tight bound for that case */
+    return compressBound(sourceLen);
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+    deflate_state *s;
+    uInt b;
+{
+    put_byte(s, (Byte)(b >> 8));
+    put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+    z_streamp strm;
+{
+    unsigned len = strm->state->pending;
+
+    if (len > strm->avail_out) len = strm->avail_out;
+    if (len == 0) return;
+
+    zmemcpy(strm->next_out, strm->state->pending_out, len);
+    strm->next_out  += len;
+    strm->state->pending_out  += len;
+    strm->total_out += len;
+    strm->avail_out  -= len;
+    strm->state->pending -= len;
+    if (strm->state->pending == 0) {
+        strm->state->pending_out = strm->state->pending_buf;
+    }
+}
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+    z_streamp strm;
+    int flush;
+{
+    int old_flush; /* value of flush param for previous deflate call */
+    deflate_state *s;
+
+    if (strm == Z_NULL || strm->state == Z_NULL ||
+        flush > Z_FINISH || flush < 0) {
+        return Z_STREAM_ERROR;
+    }
+    s = strm->state;
+
+    if (strm->next_out == Z_NULL ||
+        (strm->next_in == Z_NULL && strm->avail_in != 0) ||
+        (s->status == FINISH_STATE && flush != Z_FINISH)) {
+        ERR_RETURN(strm, Z_STREAM_ERROR);
+    }
+    if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+    s->strm = strm; /* just in case */
+    old_flush = s->last_flush;
+    s->last_flush = flush;
+
+    /* Write the header */
+    if (s->status == INIT_STATE) {
+#ifdef GZIP
+        if (s->wrap == 2) {
+            put_byte(s, 31);
+            put_byte(s, 139);
+            put_byte(s, 8);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, 0);
+            put_byte(s, s->level == 9 ? 2 :
+                        (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+                         4 : 0));
+            put_byte(s, 255);
+            s->status = BUSY_STATE;
+            strm->adler = crc32(0L, Z_NULL, 0);
+        }
+        else
+#endif
+        {
+            uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+            uInt level_flags;
+
+            if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
+                level_flags = 0;
+            else if (s->level < 6)
+                level_flags = 1;
+            else if (s->level == 6)
+                level_flags = 2;
+            else
+                level_flags = 3;
+            header |= (level_flags << 6);
+            if (s->strstart != 0) header |= PRESET_DICT;
+            header += 31 - (header % 31);
+
+            s->status = BUSY_STATE;
+            putShortMSB(s, header);
+
+            /* Save the adler32 of the preset dictionary: */
+            if (s->strstart != 0) {
+                putShortMSB(s, (uInt)(strm->adler >> 16));
+                putShortMSB(s, (uInt)(strm->adler & 0xffff));
+            }
+            strm->adler = adler32(0L, Z_NULL, 0);
+        }
+    }
+
+    /* Flush as much pending output as possible */
+    if (s->pending != 0) {
+        flush_pending(strm);
+        if (strm->avail_out == 0) {
+            /* Since avail_out is 0, deflate will be called again with
+             * more output space, but possibly with both pending and
+             * avail_in equal to zero. There won't be anything to do,
+             * but this is not an error situation so make sure we
+             * return OK instead of BUF_ERROR at next call of deflate:
+             */
+            s->last_flush = -1;
+            return Z_OK;
+        }
+
+    /* Make sure there is something to do and avoid duplicate consecutive
+     * flushes. For repeated and useless calls with Z_FINISH, we keep
+     * returning Z_STREAM_END instead of Z_BUF_ERROR.
+     */
+    } else if (strm->avail_in == 0 && flush <= old_flush &&
+               flush != Z_FINISH) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
+    /* User must not provide more input after the first FINISH: */
+    if (s->status == FINISH_STATE && strm->avail_in != 0) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
+    /* Start a new block or continue the current one.
+     */
+    if (strm->avail_in != 0 || s->lookahead != 0 ||
+        (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+        block_state bstate;
+
+        bstate = (*(configuration_table[s->level].func))(s, flush);
+
+        if (bstate == finish_started || bstate == finish_done) {
+            s->status = FINISH_STATE;
+        }
+        if (bstate == need_more || bstate == finish_started) {
+            if (strm->avail_out == 0) {
+                s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+            }
+            return Z_OK;
+            /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+             * of deflate should use the same flush parameter to make sure
+             * that the flush is complete. So we don't have to output an
+             * empty block here, this will be done at next call. This also
+             * ensures that for a very small output buffer, we emit at most
+             * one empty block.
+             */
+        }
+        if (bstate == block_done) {
+            if (flush == Z_PARTIAL_FLUSH) {
+                _tr_align(s);
+            } else { /* FULL_FLUSH or SYNC_FLUSH */
+                _tr_stored_block(s, (char*)0, 0L, 0);
+                /* For a full flush, this empty block will be recognized
+                 * as a special marker by inflate_sync().
+                 */
+                if (flush == Z_FULL_FLUSH) {
+                    CLEAR_HASH(s);             /* forget history */
+                }
+            }
+            flush_pending(strm);
+            if (strm->avail_out == 0) {
+              s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+              return Z_OK;
+            }
+        }
+    }
+    Assert(strm->avail_out > 0, "bug2");
+
+    if (flush != Z_FINISH) return Z_OK;
+    if (s->wrap <= 0) return Z_STREAM_END;
+
+    /* Write the trailer */
+#ifdef GZIP
+    if (s->wrap == 2) {
+        put_byte(s, (Byte)(strm->adler & 0xff));
+        put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+        put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
+        put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
+        put_byte(s, (Byte)(strm->total_in & 0xff));
+        put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
+        put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
+        put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
+    }
+    else
+#endif
+    {
+        putShortMSB(s, (uInt)(strm->adler >> 16));
+        putShortMSB(s, (uInt)(strm->adler & 0xffff));
+    }
+    flush_pending(strm);
+    /* If avail_out is zero, the application will call deflate again
+     * to flush the rest.
+     */
+    if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
+    return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+    z_streamp strm;
+{
+    int status;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+
+    status = strm->state->status;
+    if (status != INIT_STATE && status != BUSY_STATE &&
+        status != FINISH_STATE) {
+      return Z_STREAM_ERROR;
+    }
+
+    /* Deallocate in reverse order of allocations: */
+    TRY_FREE(strm, strm->state->pending_buf);
+    TRY_FREE(strm, strm->state->head);
+    TRY_FREE(strm, strm->state->prev);
+    TRY_FREE(strm, strm->state->window);
+
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+
+    return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+    z_streamp dest;
+    z_streamp source;
+{
+#ifdef MAXSEG_64K
+    return Z_STREAM_ERROR;
+#else
+    deflate_state *ds;
+    deflate_state *ss;
+    ushf *overlay;
+
+
+    if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
+        return Z_STREAM_ERROR;
+    }
+
+    ss = source->state;
+
+    *dest = *source;
+
+    ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+    if (ds == Z_NULL) return Z_MEM_ERROR;
+    dest->state = (struct internal_state FAR *) ds;
+    *ds = *ss;
+    ds->strm = dest;
+
+    ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+    ds->prev   = (Posf *)  ZALLOC(dest, ds->w_size, sizeof(Pos));
+    ds->head   = (Posf *)  ZALLOC(dest, ds->hash_size, sizeof(Pos));
+    overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+    ds->pending_buf = (uchf *) overlay;
+
+    if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+        ds->pending_buf == Z_NULL) {
+        deflateEnd (dest);
+        return Z_MEM_ERROR;
+    }
+    /* following zmemcpy do not work for 16-bit MSDOS */
+    zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+    zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+    zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+    zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+    ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+    ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+    ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+    ds->l_desc.dyn_tree = ds->dyn_ltree;
+    ds->d_desc.dyn_tree = ds->dyn_dtree;
+    ds->bl_desc.dyn_tree = ds->bl_tree;
+
+    return Z_OK;
+#endif /* MAXSEG_64K */
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read.  All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+    z_streamp strm;
+    Bytef *buf;
+    unsigned size;
+{
+    unsigned len = strm->avail_in;
+
+    if (len > size) len = size;
+    if (len == 0) return 0;
+
+    strm->avail_in  -= len;
+
+    if (strm->state->wrap == 1) {
+        strm->adler = adler32(strm->adler, strm->next_in, len);
+    }
+#ifdef GZIP
+    else if (strm->state->wrap == 2) {
+        strm->adler = crc32(strm->adler, strm->next_in, len);
+    }
+#endif
+    zmemcpy(buf, strm->next_in, len);
+    strm->next_in  += len;
+    strm->total_in += len;
+
+    return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+    deflate_state *s;
+{
+    s->window_size = (ulg)2L*s->w_size;
+
+    CLEAR_HASH(s);
+
+    /* Set the default configuration parameters:
+     */
+    s->max_lazy_match   = configuration_table[s->level].max_lazy;
+    s->good_match       = configuration_table[s->level].good_length;
+    s->nice_match       = configuration_table[s->level].nice_length;
+    s->max_chain_length = configuration_table[s->level].max_chain;
+
+    s->strstart = 0;
+    s->block_start = 0L;
+    s->lookahead = 0;
+    s->match_length = s->prev_length = MIN_MATCH-1;
+    s->match_available = 0;
+    s->ins_h = 0;
+#ifdef ASMV
+    match_init(); /* initialize the asm code */
+#endif
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+    deflate_state *s;
+    IPos cur_match;                             /* current match */
+{
+    unsigned chain_length = s->max_chain_length;/* max hash chain length */
+    register Bytef *scan = s->window + s->strstart; /* current string */
+    register Bytef *match;                       /* matched string */
+    register int len;                           /* length of current match */
+    int best_len = s->prev_length;              /* best match length so far */
+    int nice_match = s->nice_match;             /* stop if match long enough */
+    IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+        s->strstart - (IPos)MAX_DIST(s) : NIL;
+    /* Stop when cur_match becomes <= limit. To simplify the code,
+     * we prevent matches with the string of window index 0.
+     */
+    Posf *prev = s->prev;
+    uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+    /* Compare two bytes at a time. Note: this is not always beneficial.
+     * Try with and without -DUNALIGNED_OK to check.
+     */
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+    register ush scan_start = *(ushf*)scan;
+    register ush scan_end   = *(ushf*)(scan+best_len-1);
+#else
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+    register Byte scan_end1  = scan[best_len-1];
+    register Byte scan_end   = scan[best_len];
+#endif
+
+    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+     * It is easy to get rid of this optimization if necessary.
+     */
+    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+    /* Do not waste too much time if we already have a good match: */
+    if (s->prev_length >= s->good_match) {
+        chain_length >>= 2;
+    }
+    /* Do not look for matches beyond the end of the input. This is necessary
+     * to make deflate deterministic.
+     */
+    if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+    do {
+        Assert(cur_match < s->strstart, "no future");
+        match = s->window + cur_match;
+
+        /* Skip to next match if the match length cannot increase
+         * or if the match length is less than 2:
+         */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+        /* This code assumes sizeof(unsigned short) == 2. Do not use
+         * UNALIGNED_OK if your compiler uses a different size.
+         */
+        if (*(ushf*)(match+best_len-1) != scan_end ||
+            *(ushf*)match != scan_start) continue;
+
+        /* It is not necessary to compare scan[2] and match[2] since they are
+         * always equal when the other bytes match, given that the hash keys
+         * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+         * strstart+3, +5, ... up to strstart+257. We check for insufficient
+         * lookahead only every 4th comparison; the 128th check will be made
+         * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+         * necessary to put more guard bytes at the end of the window, or
+         * to check more often for insufficient lookahead.
+         */
+        Assert(scan[2] == match[2], "scan[2]?");
+        scan++, match++;
+        do {
+        } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 scan < strend);
+        /* The funny "do {}" generates better code on most compilers */
+
+        /* Here, scan <= window+strstart+257 */
+        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+        if (*scan == *match) scan++;
+
+        len = (MAX_MATCH - 1) - (int)(strend-scan);
+        scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+        if (match[best_len]   != scan_end  ||
+            match[best_len-1] != scan_end1 ||
+            *match            != *scan     ||
+            *++match          != scan[1])      continue;
+
+        /* The check at best_len-1 can be removed because it will be made
+         * again later. (This heuristic is not always a win.)
+         * It is not necessary to compare scan[2] and match[2] since they
+         * are always equal when the other bytes match, given that
+         * the hash keys are equal and that HASH_BITS >= 8.
+         */
+        scan += 2, match++;
+        Assert(*scan == *match, "match[2]?");
+
+        /* We check for insufficient lookahead only every 8th comparison;
+         * the 256th check will be made at strstart+258.
+         */
+        do {
+        } while (*++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 scan < strend);
+
+        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+        len = MAX_MATCH - (int)(strend - scan);
+        scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+        if (len > best_len) {
+            s->match_start = cur_match;
+            best_len = len;
+            if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+            scan_end = *(ushf*)(scan+best_len-1);
+#else
+            scan_end1  = scan[best_len-1];
+            scan_end   = scan[best_len];
+#endif
+        }
+    } while ((cur_match = prev[cur_match & wmask]) > limit
+             && --chain_length != 0);
+
+    if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+    return s->lookahead;
+}
+#endif /* ASMV */
+#endif /* FASTEST */
+
+/* ---------------------------------------------------------------------------
+ * Optimized version for level == 1 or strategy == Z_RLE only
+ */
+local uInt longest_match_fast(s, cur_match)
+    deflate_state *s;
+    IPos cur_match;                             /* current match */
+{
+    register Bytef *scan = s->window + s->strstart; /* current string */
+    register Bytef *match;                       /* matched string */
+    register int len;                           /* length of current match */
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+     * It is easy to get rid of this optimization if necessary.
+     */
+    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+    Assert(cur_match < s->strstart, "no future");
+
+    match = s->window + cur_match;
+
+    /* Return failure if the match length is less than 2:
+     */
+    if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+    /* The check at best_len-1 can be removed because it will be made
+     * again later. (This heuristic is not always a win.)
+     * It is not necessary to compare scan[2] and match[2] since they
+     * are always equal when the other bytes match, given that
+     * the hash keys are equal and that HASH_BITS >= 8.
+     */
+    scan += 2, match += 2;
+    Assert(*scan == *match, "match[2]?");
+
+    /* We check for insufficient lookahead only every 8th comparison;
+     * the 256th check will be made at strstart+258.
+     */
+    do {
+    } while (*++scan == *++match && *++scan == *++match &&
+             *++scan == *++match && *++scan == *++match &&
+             *++scan == *++match && *++scan == *++match &&
+             *++scan == *++match && *++scan == *++match &&
+             scan < strend);
+
+    Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+    len = MAX_MATCH - (int)(strend - scan);
+
+    if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+    s->match_start = cur_match;
+    return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
+}
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+    deflate_state *s;
+    IPos start, match;
+    int length;
+{
+    /* check that the match is indeed a match */
+    if (zmemcmp(s->window + match,
+                s->window + start, length) != EQUAL) {
+        fprintf(stderr, " start %u, match %u, length %d\n",
+                start, match, length);
+        do {
+            fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+        } while (--length != 0);
+        z_error("invalid match");
+    }
+    if (z_verbose > 1) {
+        fprintf(stderr,"\\[%d,%d]", start-match, length);
+        do { putc(s->window[start++], stderr); } while (--length != 0);
+    }
+}
+#else
+#  define check_match(s, start, match, length)
+#endif /* DEBUG */
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ *    At least one byte has been read, or avail_in == 0; reads are
+ *    performed for at least two bytes (required for the zip translate_eol
+ *    option -- not supported here).
+ */
+local void fill_window(s)
+    deflate_state *s;
+{
+    register unsigned n, m;
+    register Posf *p;
+    unsigned more;    /* Amount of free space at the end of the window. */
+    uInt wsize = s->w_size;
+
+    do {
+        more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+        /* Deal with !@#$% 64K limit: */
+        if (sizeof(int) <= 2) {
+            if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+                more = wsize;
+
+            } else if (more == (unsigned)(-1)) {
+                /* Very unlikely, but possible on 16 bit machine if
+                 * strstart == 0 && lookahead == 1 (input done a byte at time)
+                 */
+                more--;
+            }
+        }
+
+        /* If the window is almost full and there is insufficient lookahead,
+         * move the upper half to the lower one to make room in the upper half.
+         */
+        if (s->strstart >= wsize+MAX_DIST(s)) {
+
+            zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
+            s->match_start -= wsize;
+            s->strstart    -= wsize; /* we now have strstart >= MAX_DIST */
+            s->block_start -= (long) wsize;
+
+            /* Slide the hash table (could be avoided with 32 bit values
+               at the expense of memory usage). We slide even when level == 0
+               to keep the hash table consistent if we switch back to level > 0
+               later. (Using level 0 permanently is not an optimal usage of
+               zlib, so we don't care about this pathological case.)
+             */
+            n = s->hash_size;
+            p = &s->head[n];
+            do {
+                m = *--p;
+                *p = (Pos)(m >= wsize ? m-wsize : NIL);
+            } while (--n);
+
+            n = wsize;
+#ifndef FASTEST
+            p = &s->prev[n];
+            do {
+                m = *--p;
+                *p = (Pos)(m >= wsize ? m-wsize : NIL);
+                /* If n is not on any hash chain, prev[n] is garbage but
+                 * its value will never be used.
+                 */
+            } while (--n);
+#endif
+            more += wsize;
+        }
+        if (s->strm->avail_in == 0) return;
+
+        /* If there was no sliding:
+         *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+         *    more == window_size - lookahead - strstart
+         * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+         * => more >= window_size - 2*WSIZE + 2
+         * In the BIG_MEM or MMAP case (not yet supported),
+         *   window_size == input_size + MIN_LOOKAHEAD  &&
+         *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+         * Otherwise, window_size == 2*WSIZE so more >= 2.
+         * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+         */
+        Assert(more >= 2, "more < 2");
+
+        n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+        s->lookahead += n;
+
+        /* Initialize the hash value now that we have some input: */
+        if (s->lookahead >= MIN_MATCH) {
+            s->ins_h = s->window[s->strstart];
+            UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+            Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+        }
+        /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+         * but this is not important since only literal bytes will be emitted.
+         */
+
+    } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, eof) { \
+   _tr_flush_block(s, (s->block_start >= 0L ? \
+                   (charf *)&s->window[(unsigned)s->block_start] : \
+                   (charf *)Z_NULL), \
+                (ulg)((long)s->strstart - s->block_start), \
+                (eof)); \
+   s->block_start = s->strstart; \
+   flush_pending(s->strm); \
+   Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, eof) { \
+   FLUSH_BLOCK_ONLY(s, eof); \
+   if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+     * to pending_buf_size, and each stored block has a 5 byte header:
+     */
+    ulg max_block_size = 0xffff;
+    ulg max_start;
+
+    if (max_block_size > s->pending_buf_size - 5) {
+        max_block_size = s->pending_buf_size - 5;
+    }
+
+    /* Copy as much as possible from input to output: */
+    for (;;) {
+        /* Fill the window as much as possible: */
+        if (s->lookahead <= 1) {
+
+            Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+                   s->block_start >= (long)s->w_size, "slide too late");
+
+            fill_window(s);
+            if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+        Assert(s->block_start >= 0L, "block gone");
+
+        s->strstart += s->lookahead;
+        s->lookahead = 0;
+
+        /* Emit a stored block if pending_buf will be full: */
+        max_start = s->block_start + max_block_size;
+        if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+            /* strstart == 0 is possible when wraparound on 16-bit machine */
+            s->lookahead = (uInt)(s->strstart - max_start);
+            s->strstart = (uInt)max_start;
+            FLUSH_BLOCK(s, 0);
+        }
+        /* Flush if we may have to slide, otherwise block_start may become
+         * negative and the data will be gone:
+         */
+        if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+            FLUSH_BLOCK(s, 0);
+        }
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    IPos hash_head = NIL; /* head of the hash chain */
+    int bflush;           /* set if current block must be flushed */
+
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the next match, plus MIN_MATCH bytes to insert the
+         * string following the next match.
+         */
+        if (s->lookahead < MIN_LOOKAHEAD) {
+            fill_window(s);
+            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+                return need_more;
+            }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* Insert the string window[strstart .. strstart+2] in the
+         * dictionary, and set hash_head to the head of the hash chain:
+         */
+        if (s->lookahead >= MIN_MATCH) {
+            INSERT_STRING(s, s->strstart, hash_head);
+        }
+
+        /* Find the longest match, discarding those <= prev_length.
+         * At this point we have always match_length < MIN_MATCH
+         */
+        if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+            /* To simplify the code, we prevent matches with the string
+             * of window index 0 (in particular we have to avoid a match
+             * of the string with itself at the start of the input file).
+             */
+#ifdef FASTEST
+            if ((s->strategy < Z_HUFFMAN_ONLY) ||
+                (s->strategy == Z_RLE && s->strstart - hash_head == 1)) {
+                s->match_length = longest_match_fast (s, hash_head);
+            }
+#else
+            if (s->strategy < Z_HUFFMAN_ONLY) {
+                s->match_length = longest_match (s, hash_head);
+            } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+                s->match_length = longest_match_fast (s, hash_head);
+            }
+#endif
+            /* longest_match() or longest_match_fast() sets match_start */
+        }
+        if (s->match_length >= MIN_MATCH) {
+            check_match(s, s->strstart, s->match_start, s->match_length);
+
+            _tr_tally_dist(s, s->strstart - s->match_start,
+                           s->match_length - MIN_MATCH, bflush);
+
+            s->lookahead -= s->match_length;
+
+            /* Insert new strings in the hash table only if the match length
+             * is not too large. This saves time but degrades compression.
+             */
+#ifndef FASTEST
+            if (s->match_length <= s->max_insert_length &&
+                s->lookahead >= MIN_MATCH) {
+                s->match_length--; /* string at strstart already in table */
+                do {
+                    s->strstart++;
+                    INSERT_STRING(s, s->strstart, hash_head);
+                    /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+                     * always MIN_MATCH bytes ahead.
+                     */
+                } while (--s->match_length != 0);
+                s->strstart++;
+            } else
+#endif
+            {
+                s->strstart += s->match_length;
+                s->match_length = 0;
+                s->ins_h = s->window[s->strstart];
+                UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+                Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+                /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+                 * matter since it will be recomputed at next deflate call.
+                 */
+            }
+        } else {
+            /* No match, output a literal byte */
+            Tracevv((stderr,"%c", s->window[s->strstart]));
+            _tr_tally_lit (s, s->window[s->strstart], bflush);
+            s->lookahead--;
+            s->strstart++;
+        }
+        if (bflush) FLUSH_BLOCK(s, 0);
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    IPos hash_head = NIL;    /* head of hash chain */
+    int bflush;              /* set if current block must be flushed */
+
+    /* Process the input block. */
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the next match, plus MIN_MATCH bytes to insert the
+         * string following the next match.
+         */
+        if (s->lookahead < MIN_LOOKAHEAD) {
+            fill_window(s);
+            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+                return need_more;
+            }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* Insert the string window[strstart .. strstart+2] in the
+         * dictionary, and set hash_head to the head of the hash chain:
+         */
+        if (s->lookahead >= MIN_MATCH) {
+            INSERT_STRING(s, s->strstart, hash_head);
+        }
+
+        /* Find the longest match, discarding those <= prev_length.
+         */
+        s->prev_length = s->match_length, s->prev_match = s->match_start;
+        s->match_length = MIN_MATCH-1;
+
+        if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+            s->strstart - hash_head <= MAX_DIST(s)) {
+            /* To simplify the code, we prevent matches with the string
+             * of window index 0 (in particular we have to avoid a match
+             * of the string with itself at the start of the input file).
+             */
+            if (s->strategy < Z_HUFFMAN_ONLY) {
+                s->match_length = longest_match (s, hash_head);
+            } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+                s->match_length = longest_match_fast (s, hash_head);
+            }
+            /* longest_match() or longest_match_fast() sets match_start */
+
+            if (s->match_length <= 5 && (s->strategy == Z_FILTERED
+#if TOO_FAR <= 32767
+                || (s->match_length == MIN_MATCH &&
+                    s->strstart - s->match_start > TOO_FAR)
+#endif
+                )) {
+
+                /* If prev_match is also MIN_MATCH, match_start is garbage
+                 * but we will ignore the current match anyway.
+                 */
+                s->match_length = MIN_MATCH-1;
+            }
+        }
+        /* If there was a match at the previous step and the current
+         * match is not better, output the previous match:
+         */
+        if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+            uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+            /* Do not insert strings in hash table beyond this. */
+
+            check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+            _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+                           s->prev_length - MIN_MATCH, bflush);
+
+            /* Insert in hash table all strings up to the end of the match.
+             * strstart-1 and strstart are already inserted. If there is not
+             * enough lookahead, the last two strings are not inserted in
+             * the hash table.
+             */
+            s->lookahead -= s->prev_length-1;
+            s->prev_length -= 2;
+            do {
+                if (++s->strstart <= max_insert) {
+                    INSERT_STRING(s, s->strstart, hash_head);
+                }
+            } while (--s->prev_length != 0);
+            s->match_available = 0;
+            s->match_length = MIN_MATCH-1;
+            s->strstart++;
+
+            if (bflush) FLUSH_BLOCK(s, 0);
+
+        } else if (s->match_available) {
+            /* If there was no match at the previous position, output a
+             * single literal. If there was a match but the current match
+             * is longer, truncate the previous match to a single literal.
+             */
+            Tracevv((stderr,"%c", s->window[s->strstart-1]));
+            _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+            if (bflush) {
+                FLUSH_BLOCK_ONLY(s, 0);
+            }
+            s->strstart++;
+            s->lookahead--;
+            if (s->strm->avail_out == 0) return need_more;
+        } else {
+            /* There is no previous match to compare with, wait for
+             * the next step to decide.
+             */
+            s->match_available = 1;
+            s->strstart++;
+            s->lookahead--;
+        }
+    }
+    Assert (flush != Z_NO_FLUSH, "no flush?");
+    if (s->match_available) {
+        Tracevv((stderr,"%c", s->window[s->strstart-1]));
+        _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+        s->match_available = 0;
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
+#endif /* FASTEST */
diff --git a/zlib/deflate.h b/zlib/deflate.h
new file mode 100644 (file)
index 0000000..d4f44e4
--- /dev/null
@@ -0,0 +1,326 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2002 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id: deflate.h,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#ifndef DEFLATE_H
+#define DEFLATE_H
+
+#include "zutil.h"
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+   trailer creation by deflate().  NO_GZIP would be used to avoid linking in
+   the crc code when it is not needed.  For shared libraries, gzip encoding
+   should be left enabled. */
+#ifndef NO_GZIP
+#  define GZIP
+#endif
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS  256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES   30
+/* number of distance codes */
+
+#define BL_CODES  19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE    42
+#define BUSY_STATE   113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+    union {
+        ush  freq;       /* frequency count */
+        ush  code;       /* bit string */
+    } fc;
+    union {
+        ush  dad;        /* father node in Huffman tree */
+        ush  len;        /* length of bit string */
+    } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad  dl.dad
+#define Len  dl.len
+
+typedef struct static_tree_desc_s  static_tree_desc;
+
+typedef struct tree_desc_s {
+    ct_data *dyn_tree;           /* the dynamic tree */
+    int     max_code;            /* largest code with non zero frequency */
+    static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+    z_streamp strm;      /* pointer back to this zlib stream */
+    int   status;        /* as the name implies */
+    Bytef *pending_buf;  /* output still pending */
+    ulg   pending_buf_size; /* size of pending_buf */
+    Bytef *pending_out;  /* next pending byte to output to the stream */
+    int   pending;       /* nb of bytes in the pending buffer */
+    int   wrap;          /* bit 0 true for zlib, bit 1 true for gzip */
+    Byte  data_type;     /* UNKNOWN, BINARY or ASCII */
+    Byte  method;        /* STORED (for zip only) or DEFLATED */
+    int   last_flush;    /* value of flush param for previous deflate call */
+
+                /* used by deflate.c: */
+
+    uInt  w_size;        /* LZ77 window size (32K by default) */
+    uInt  w_bits;        /* log2(w_size)  (8..16) */
+    uInt  w_mask;        /* w_size - 1 */
+
+    Bytef *window;
+    /* Sliding window. Input bytes are read into the second half of the window,
+     * and move to the first half later to keep a dictionary of at least wSize
+     * bytes. With this organization, matches are limited to a distance of
+     * wSize-MAX_MATCH bytes, but this ensures that IO is always
+     * performed with a length multiple of the block size. Also, it limits
+     * the window size to 64K, which is quite useful on MSDOS.
+     * To do: use the user input buffer as sliding window.
+     */
+
+    ulg window_size;
+    /* Actual size of window: 2*wSize, except when the user input buffer
+     * is directly used as sliding window.
+     */
+
+    Posf *prev;
+    /* Link to older string with same hash index. To limit the size of this
+     * array to 64K, this link is maintained only for the last 32K strings.
+     * An index in this array is thus a window index modulo 32K.
+     */
+
+    Posf *head; /* Heads of the hash chains or NIL. */
+
+    uInt  ins_h;          /* hash index of string to be inserted */
+    uInt  hash_size;      /* number of elements in hash table */
+    uInt  hash_bits;      /* log2(hash_size) */
+    uInt  hash_mask;      /* hash_size-1 */
+
+    uInt  hash_shift;
+    /* Number of bits by which ins_h must be shifted at each input
+     * step. It must be such that after MIN_MATCH steps, the oldest
+     * byte no longer takes part in the hash key, that is:
+     *   hash_shift * MIN_MATCH >= hash_bits
+     */
+
+    long block_start;
+    /* Window position at the beginning of the current output block. Gets
+     * negative when the window is moved backwards.
+     */
+
+    uInt match_length;           /* length of best match */
+    IPos prev_match;             /* previous match */
+    int match_available;         /* set if previous match exists */
+    uInt strstart;               /* start of string to insert */
+    uInt match_start;            /* start of matching string */
+    uInt lookahead;              /* number of valid bytes ahead in window */
+
+    uInt prev_length;
+    /* Length of the best match at previous step. Matches not greater than this
+     * are discarded. This is used in the lazy match evaluation.
+     */
+
+    uInt max_chain_length;
+    /* To speed up deflation, hash chains are never searched beyond this
+     * length.  A higher limit improves compression ratio but degrades the
+     * speed.
+     */
+
+    uInt max_lazy_match;
+    /* Attempt to find a better match only when the current match is strictly
+     * smaller than this value. This mechanism is used only for compression
+     * levels >= 4.
+     */
+#   define max_insert_length  max_lazy_match
+    /* Insert new strings in the hash table only if the match length is not
+     * greater than this length. This saves time but degrades compression.
+     * max_insert_length is used only for compression levels <= 3.
+     */
+
+    int level;    /* compression level (1..9) */
+    int strategy; /* favor or force Huffman coding*/
+
+    uInt good_match;
+    /* Use a faster search when the previous match is longer than this */
+
+    int nice_match; /* Stop searching when current match exceeds this */
+
+                /* used by trees.c: */
+    /* Didn't use ct_data typedef below to supress compiler warning */
+    struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
+    struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+    struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */
+
+    struct tree_desc_s l_desc;               /* desc. for literal tree */
+    struct tree_desc_s d_desc;               /* desc. for distance tree */
+    struct tree_desc_s bl_desc;              /* desc. for bit length tree */
+
+    ush bl_count[MAX_BITS+1];
+    /* number of codes at each bit length for an optimal tree */
+
+    int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
+    int heap_len;               /* number of elements in the heap */
+    int heap_max;               /* element of largest frequency */
+    /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+     * The same heap array is used to build all trees.
+     */
+
+    uch depth[2*L_CODES+1];
+    /* Depth of each subtree used as tie breaker for trees of equal frequency
+     */
+
+    uchf *l_buf;          /* buffer for literals or lengths */
+
+    uInt  lit_bufsize;
+    /* Size of match buffer for literals/lengths.  There are 4 reasons for
+     * limiting lit_bufsize to 64K:
+     *   - frequencies can be kept in 16 bit counters
+     *   - if compression is not successful for the first block, all input
+     *     data is still in the window so we can still emit a stored block even
+     *     when input comes from standard input.  (This can also be done for
+     *     all blocks if lit_bufsize is not greater than 32K.)
+     *   - if compression is not successful for a file smaller than 64K, we can
+     *     even emit a stored file instead of a stored block (saving 5 bytes).
+     *     This is applicable only for zip (not gzip or zlib).
+     *   - creating new Huffman trees less frequently may not provide fast
+     *     adaptation to changes in the input data statistics. (Take for
+     *     example a binary file with poorly compressible code followed by
+     *     a highly compressible string table.) Smaller buffer sizes give
+     *     fast adaptation but have of course the overhead of transmitting
+     *     trees more frequently.
+     *   - I can't count above 4
+     */
+
+    uInt last_lit;      /* running index in l_buf */
+
+    ushf *d_buf;
+    /* Buffer for distances. To simplify the code, d_buf and l_buf have
+     * the same number of elements. To use different lengths, an extra flag
+     * array would be necessary.
+     */
+
+    ulg opt_len;        /* bit length of current block with optimal trees */
+    ulg static_len;     /* bit length of current block with static trees */
+    uInt matches;       /* number of string matches in current block */
+    int last_eob_len;   /* bit length of EOB code for last block */
+
+#ifdef DEBUG
+    ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+    ulg bits_sent;      /* bit length of compressed data sent mod 2^32 */
+#endif
+
+    ush bi_buf;
+    /* Output buffer. bits are inserted starting at the bottom (least
+     * significant bits).
+     */
+    int bi_valid;
+    /* Number of valid bits in bi_buf.  All bits above the last valid bit
+     * are always zero.
+     */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s)  ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+        /* in trees.c */
+void _tr_init         OF((deflate_state *s));
+int  _tr_tally        OF((deflate_state *s, unsigned dist, unsigned lc));
+void _tr_flush_block  OF((deflate_state *s, charf *buf, ulg stored_len,
+                          int eof));
+void _tr_align        OF((deflate_state *s));
+void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+                          int eof));
+
+#define d_code(dist) \
+   ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+  extern uch _length_code[];
+  extern uch _dist_code[];
+#else
+  extern const uch _length_code[];
+  extern const uch _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+  { uch cc = (c); \
+    s->d_buf[s->last_lit] = 0; \
+    s->l_buf[s->last_lit++] = cc; \
+    s->dyn_ltree[cc].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+   }
+# define _tr_tally_dist(s, distance, length, flush) \
+  { uch len = (length); \
+    ush dist = (distance); \
+    s->d_buf[s->last_lit] = dist; \
+    s->l_buf[s->last_lit++] = len; \
+    dist--; \
+    s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+    s->dyn_dtree[d_code(dist)].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+  }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+              flush = _tr_tally(s, distance, length)
+#endif
+
+#endif /* DEFLATE_H */
diff --git a/zlib/example.c b/zlib/example.c
new file mode 100644 (file)
index 0000000..6b62ba3
--- /dev/null
@@ -0,0 +1,567 @@
+/* example.c -- usage example of the zlib compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: example.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+#  include <string.h>
+#  include <stdlib.h>
+#else
+   extern void exit  OF((int));
+#endif
+
+#if defined(VMS) || defined(RISCOS)
+#  define TESTFILE "foo-gz"
+#else
+#  define TESTFILE "foo.gz"
+#endif
+
+#define CHECK_ERR(err, msg) { \
+    if (err != Z_OK) { \
+        fprintf(stderr, "%s error: %d\n", msg, err); \
+        exit(1); \
+    } \
+}
+
+const char hello[] = "hello, hello!";
+/* "hello world" would be more standard, but the repeated "hello"
+ * stresses the compression code better, sorry...
+ */
+
+const char dictionary[] = "hello";
+uLong dictId; /* Adler32 value of the dictionary */
+
+void test_compress      OF((Byte *compr, uLong comprLen,
+                            Byte *uncompr, uLong uncomprLen));
+void test_gzio          OF((const char *fname,
+                            Byte *uncompr, uLong uncomprLen));
+void test_deflate       OF((Byte *compr, uLong comprLen));
+void test_inflate       OF((Byte *compr, uLong comprLen,
+                            Byte *uncompr, uLong uncomprLen));
+void test_large_deflate OF((Byte *compr, uLong comprLen,
+                            Byte *uncompr, uLong uncomprLen));
+void test_large_inflate OF((Byte *compr, uLong comprLen,
+                            Byte *uncompr, uLong uncomprLen));
+void test_flush         OF((Byte *compr, uLong *comprLen));
+void test_sync          OF((Byte *compr, uLong comprLen,
+                            Byte *uncompr, uLong uncomprLen));
+void test_dict_deflate  OF((Byte *compr, uLong comprLen));
+void test_dict_inflate  OF((Byte *compr, uLong comprLen,
+                            Byte *uncompr, uLong uncomprLen));
+int  main               OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Test compress() and uncompress()
+ */
+void test_compress(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    uLong len = (uLong)strlen(hello)+1;
+
+    err = compress(compr, &comprLen, (const Bytef*)hello, len);
+    CHECK_ERR(err, "compress");
+
+    strcpy((char*)uncompr, "garbage");
+
+    err = uncompress(uncompr, &uncomprLen, compr, comprLen);
+    CHECK_ERR(err, "uncompress");
+
+    if (strcmp((char*)uncompr, hello)) {
+        fprintf(stderr, "bad uncompress\n");
+        exit(1);
+    } else {
+        printf("uncompress(): %s\n", (char *)uncompr);
+    }
+}
+
+/* ===========================================================================
+ * Test read/write of .gz files
+ */
+void test_gzio(fname, uncompr, uncomprLen)
+    const char *fname; /* compressed file name */
+    Byte *uncompr;
+    uLong uncomprLen;
+{
+#ifdef NO_GZCOMPRESS
+    fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
+#else
+    int err;
+    int len = (int)strlen(hello)+1;
+    gzFile file;
+    z_off_t pos;
+
+    file = gzopen(fname, "wb");
+    if (file == NULL) {
+        fprintf(stderr, "gzopen error\n");
+        exit(1);
+    }
+    gzputc(file, 'h');
+    if (gzputs(file, "ello") != 4) {
+        fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err));
+        exit(1);
+    }
+    if (gzprintf(file, ", %s!", "hello") != 8) {
+        fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err));
+        exit(1);
+    }
+    gzseek(file, 1L, SEEK_CUR); /* add one zero byte */
+    gzclose(file);
+
+    file = gzopen(fname, "rb");
+    if (file == NULL) {
+        fprintf(stderr, "gzopen error\n");
+        exit(1);
+    }
+    strcpy((char*)uncompr, "garbage");
+
+    if (gzread(file, uncompr, (unsigned)uncomprLen) != len) {
+        fprintf(stderr, "gzread err: %s\n", gzerror(file, &err));
+        exit(1);
+    }
+    if (strcmp((char*)uncompr, hello)) {
+        fprintf(stderr, "bad gzread: %s\n", (char*)uncompr);
+        exit(1);
+    } else {
+        printf("gzread(): %s\n", (char*)uncompr);
+    }
+
+    pos = gzseek(file, -8L, SEEK_CUR);
+    if (pos != 6 || gztell(file) != pos) {
+        fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n",
+                (long)pos, (long)gztell(file));
+        exit(1);
+    }
+
+    if (gzgetc(file) != ' ') {
+        fprintf(stderr, "gzgetc error\n");
+        exit(1);
+    }
+
+    if (gzungetc(' ', file) != ' ') {
+        fprintf(stderr, "gzungetc error\n");
+        exit(1);
+    }
+
+    gzgets(file, (char*)uncompr, (int)uncomprLen);
+    if (strlen((char*)uncompr) != 7) { /* " hello!" */
+        fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err));
+        exit(1);
+    }
+    if (strcmp((char*)uncompr, hello + 6)) {
+        fprintf(stderr, "bad gzgets after gzseek\n");
+        exit(1);
+    } else {
+        printf("gzgets() after gzseek: %s\n", (char*)uncompr);
+    }
+
+    gzclose(file);
+#endif
+}
+
+/* ===========================================================================
+ * Test deflate() with small buffers
+ */
+void test_deflate(compr, comprLen)
+    Byte *compr;
+    uLong comprLen;
+{
+    z_stream c_stream; /* compression stream */
+    int err;
+    uLong len = (uLong)strlen(hello)+1;
+
+    c_stream.zalloc = (alloc_func)0;
+    c_stream.zfree = (free_func)0;
+    c_stream.opaque = (voidpf)0;
+
+    err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+    CHECK_ERR(err, "deflateInit");
+
+    c_stream.next_in  = (Bytef*)hello;
+    c_stream.next_out = compr;
+
+    while (c_stream.total_in != len && c_stream.total_out < comprLen) {
+        c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */
+        err = deflate(&c_stream, Z_NO_FLUSH);
+        CHECK_ERR(err, "deflate");
+    }
+    /* Finish the stream, still forcing small buffers: */
+    for (;;) {
+        c_stream.avail_out = 1;
+        err = deflate(&c_stream, Z_FINISH);
+        if (err == Z_STREAM_END) break;
+        CHECK_ERR(err, "deflate");
+    }
+
+    err = deflateEnd(&c_stream);
+    CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with small buffers
+ */
+void test_inflate(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    z_stream d_stream; /* decompression stream */
+
+    strcpy((char*)uncompr, "garbage");
+
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+
+    d_stream.next_in  = compr;
+    d_stream.avail_in = 0;
+    d_stream.next_out = uncompr;
+
+    err = inflateInit(&d_stream);
+    CHECK_ERR(err, "inflateInit");
+
+    while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
+        d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */
+        err = inflate(&d_stream, Z_NO_FLUSH);
+        if (err == Z_STREAM_END) break;
+        CHECK_ERR(err, "inflate");
+    }
+
+    err = inflateEnd(&d_stream);
+    CHECK_ERR(err, "inflateEnd");
+
+    if (strcmp((char*)uncompr, hello)) {
+        fprintf(stderr, "bad inflate\n");
+        exit(1);
+    } else {
+        printf("inflate(): %s\n", (char *)uncompr);
+    }
+}
+
+/* ===========================================================================
+ * Test deflate() with large buffers and dynamic change of compression level
+ */
+void test_large_deflate(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    z_stream c_stream; /* compression stream */
+    int err;
+
+    c_stream.zalloc = (alloc_func)0;
+    c_stream.zfree = (free_func)0;
+    c_stream.opaque = (voidpf)0;
+
+    err = deflateInit(&c_stream, Z_BEST_SPEED);
+    CHECK_ERR(err, "deflateInit");
+
+    c_stream.next_out = compr;
+    c_stream.avail_out = (uInt)comprLen;
+
+    /* At this point, uncompr is still mostly zeroes, so it should compress
+     * very well:
+     */
+    c_stream.next_in = uncompr;
+    c_stream.avail_in = (uInt)uncomprLen;
+    err = deflate(&c_stream, Z_NO_FLUSH);
+    CHECK_ERR(err, "deflate");
+    if (c_stream.avail_in != 0) {
+        fprintf(stderr, "deflate not greedy\n");
+        exit(1);
+    }
+
+    /* Feed in already compressed data and switch to no compression: */
+    deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY);
+    c_stream.next_in = compr;
+    c_stream.avail_in = (uInt)comprLen/2;
+    err = deflate(&c_stream, Z_NO_FLUSH);
+    CHECK_ERR(err, "deflate");
+
+    /* Switch back to compressing mode: */
+    deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED);
+    c_stream.next_in = uncompr;
+    c_stream.avail_in = (uInt)uncomprLen;
+    err = deflate(&c_stream, Z_NO_FLUSH);
+    CHECK_ERR(err, "deflate");
+
+    err = deflate(&c_stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        fprintf(stderr, "deflate should report Z_STREAM_END\n");
+        exit(1);
+    }
+    err = deflateEnd(&c_stream);
+    CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with large buffers
+ */
+void test_large_inflate(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    z_stream d_stream; /* decompression stream */
+
+    strcpy((char*)uncompr, "garbage");
+
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+
+    d_stream.next_in  = compr;
+    d_stream.avail_in = (uInt)comprLen;
+
+    err = inflateInit(&d_stream);
+    CHECK_ERR(err, "inflateInit");
+
+    for (;;) {
+        d_stream.next_out = uncompr;            /* discard the output */
+        d_stream.avail_out = (uInt)uncomprLen;
+        err = inflate(&d_stream, Z_NO_FLUSH);
+        if (err == Z_STREAM_END) break;
+        CHECK_ERR(err, "large inflate");
+    }
+
+    err = inflateEnd(&d_stream);
+    CHECK_ERR(err, "inflateEnd");
+
+    if (d_stream.total_out != 2*uncomprLen + comprLen/2) {
+        fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out);
+        exit(1);
+    } else {
+        printf("large_inflate(): OK\n");
+    }
+}
+
+/* ===========================================================================
+ * Test deflate() with full flush
+ */
+void test_flush(compr, comprLen)
+    Byte *compr;
+    uLong *comprLen;
+{
+    z_stream c_stream; /* compression stream */
+    int err;
+    uInt len = (uInt)strlen(hello)+1;
+
+    c_stream.zalloc = (alloc_func)0;
+    c_stream.zfree = (free_func)0;
+    c_stream.opaque = (voidpf)0;
+
+    err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION);
+    CHECK_ERR(err, "deflateInit");
+
+    c_stream.next_in  = (Bytef*)hello;
+    c_stream.next_out = compr;
+    c_stream.avail_in = 3;
+    c_stream.avail_out = (uInt)*comprLen;
+    err = deflate(&c_stream, Z_FULL_FLUSH);
+    CHECK_ERR(err, "deflate");
+
+    compr[3]++; /* force an error in first compressed block */
+    c_stream.avail_in = len - 3;
+
+    err = deflate(&c_stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        CHECK_ERR(err, "deflate");
+    }
+    err = deflateEnd(&c_stream);
+    CHECK_ERR(err, "deflateEnd");
+
+    *comprLen = c_stream.total_out;
+}
+
+/* ===========================================================================
+ * Test inflateSync()
+ */
+void test_sync(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    z_stream d_stream; /* decompression stream */
+
+    strcpy((char*)uncompr, "garbage");
+
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+
+    d_stream.next_in  = compr;
+    d_stream.avail_in = 2; /* just read the zlib header */
+
+    err = inflateInit(&d_stream);
+    CHECK_ERR(err, "inflateInit");
+
+    d_stream.next_out = uncompr;
+    d_stream.avail_out = (uInt)uncomprLen;
+
+    inflate(&d_stream, Z_NO_FLUSH);
+    CHECK_ERR(err, "inflate");
+
+    d_stream.avail_in = (uInt)comprLen-2;   /* read all compressed data */
+    err = inflateSync(&d_stream);           /* but skip the damaged part */
+    CHECK_ERR(err, "inflateSync");
+
+    err = inflate(&d_stream, Z_FINISH);
+    if (err != Z_DATA_ERROR) {
+        fprintf(stderr, "inflate should report DATA_ERROR\n");
+        /* Because of incorrect adler32 */
+        exit(1);
+    }
+    err = inflateEnd(&d_stream);
+    CHECK_ERR(err, "inflateEnd");
+
+    printf("after inflateSync(): hel%s\n", (char *)uncompr);
+}
+
+/* ===========================================================================
+ * Test deflate() with preset dictionary
+ */
+void test_dict_deflate(compr, comprLen)
+    Byte *compr;
+    uLong comprLen;
+{
+    z_stream c_stream; /* compression stream */
+    int err;
+
+    c_stream.zalloc = (alloc_func)0;
+    c_stream.zfree = (free_func)0;
+    c_stream.opaque = (voidpf)0;
+
+    err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
+    CHECK_ERR(err, "deflateInit");
+
+    err = deflateSetDictionary(&c_stream,
+                               (const Bytef*)dictionary, sizeof(dictionary));
+    CHECK_ERR(err, "deflateSetDictionary");
+
+    dictId = c_stream.adler;
+    c_stream.next_out = compr;
+    c_stream.avail_out = (uInt)comprLen;
+
+    c_stream.next_in = (Bytef*)hello;
+    c_stream.avail_in = (uInt)strlen(hello)+1;
+
+    err = deflate(&c_stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        fprintf(stderr, "deflate should report Z_STREAM_END\n");
+        exit(1);
+    }
+    err = deflateEnd(&c_stream);
+    CHECK_ERR(err, "deflateEnd");
+}
+
+/* ===========================================================================
+ * Test inflate() with a preset dictionary
+ */
+void test_dict_inflate(compr, comprLen, uncompr, uncomprLen)
+    Byte *compr, *uncompr;
+    uLong comprLen, uncomprLen;
+{
+    int err;
+    z_stream d_stream; /* decompression stream */
+
+    strcpy((char*)uncompr, "garbage");
+
+    d_stream.zalloc = (alloc_func)0;
+    d_stream.zfree = (free_func)0;
+    d_stream.opaque = (voidpf)0;
+
+    d_stream.next_in  = compr;
+    d_stream.avail_in = (uInt)comprLen;
+
+    err = inflateInit(&d_stream);
+    CHECK_ERR(err, "inflateInit");
+
+    d_stream.next_out = uncompr;
+    d_stream.avail_out = (uInt)uncomprLen;
+
+    for (;;) {
+        err = inflate(&d_stream, Z_NO_FLUSH);
+        if (err == Z_STREAM_END) break;
+        if (err == Z_NEED_DICT) {
+            if (d_stream.adler != dictId) {
+                fprintf(stderr, "unexpected dictionary");
+                exit(1);
+            }
+            err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary,
+                                       sizeof(dictionary));
+        }
+        CHECK_ERR(err, "inflate with dict");
+    }
+
+    err = inflateEnd(&d_stream);
+    CHECK_ERR(err, "inflateEnd");
+
+    if (strcmp((char*)uncompr, hello)) {
+        fprintf(stderr, "bad inflate with dict\n");
+        exit(1);
+    } else {
+        printf("inflate with dictionary: %s\n", (char *)uncompr);
+    }
+}
+
+/* ===========================================================================
+ * Usage:  example [output.gz  [input.gz]]
+ */
+
+int main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    Byte *compr, *uncompr;
+    uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */
+    uLong uncomprLen = comprLen;
+    static const char* myVersion = ZLIB_VERSION;
+
+    if (zlibVersion()[0] != myVersion[0]) {
+        fprintf(stderr, "incompatible zlib version\n");
+        exit(1);
+
+    } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) {
+        fprintf(stderr, "warning: different zlib version\n");
+    }
+
+    printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n",
+            ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags());
+
+    compr    = (Byte*)calloc((uInt)comprLen, 1);
+    uncompr  = (Byte*)calloc((uInt)uncomprLen, 1);
+    /* compr and uncompr are cleared to avoid reading uninitialized
+     * data and to ensure that uncompr compresses well.
+     */
+    if (compr == Z_NULL || uncompr == Z_NULL) {
+        printf("out of memory\n");
+        exit(1);
+    }
+    test_compress(compr, comprLen, uncompr, uncomprLen);
+
+    test_gzio((argc > 1 ? argv[1] : TESTFILE),
+              uncompr, uncomprLen);
+
+    test_deflate(compr, comprLen);
+    test_inflate(compr, comprLen, uncompr, uncomprLen);
+
+    test_large_deflate(compr, comprLen, uncompr, uncomprLen);
+    test_large_inflate(compr, comprLen, uncompr, uncomprLen);
+
+    test_flush(compr, &comprLen);
+    test_sync(compr, comprLen, uncompr, uncomprLen);
+    comprLen = uncomprLen;
+
+    test_dict_deflate(compr, comprLen);
+    test_dict_inflate(compr, comprLen, uncompr, uncomprLen);
+
+    free(compr);
+    free(uncompr);
+
+    return 0;
+}
diff --git a/zlib/gzio.c b/zlib/gzio.c
new file mode 100644 (file)
index 0000000..1a0db7d
--- /dev/null
@@ -0,0 +1,1005 @@
+/* gzio.c -- IO on .gz files
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Compile this file with -DNO_GZCOMPRESS to avoid the compression code.
+ */
+
+/* @(#) $Id: gzio.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#include <stdio.h>
+
+#include "zutil.h"
+
+#ifdef NO_DEFLATE       /* for compatiblity with old definition */
+#  define NO_GZCOMPRESS
+#endif
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef Z_BUFSIZE
+#  ifdef MAXSEG_64K
+#    define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
+#  else
+#    define Z_BUFSIZE 16384
+#  endif
+#endif
+#ifndef Z_PRINTF_BUFSIZE
+#  define Z_PRINTF_BUFSIZE 4096
+#endif
+
+#ifdef __MVS__
+#  pragma map (fdopen , "\174\174FDOPEN")
+   FILE *fdopen(int, const char *);
+#endif
+
+#ifndef STDC
+extern voidp  malloc OF((uInt size));
+extern void   free   OF((voidpf ptr));
+#endif
+
+#define ALLOC(size) malloc(size)
+#define TRYFREE(p) {if (p) free(p);}
+
+static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define RESERVED     0xE0 /* bits 5..7: reserved */
+
+typedef struct gz_stream {
+    z_stream stream;
+    int      z_err;   /* error code for last stream operation */
+    int      z_eof;   /* set if end of input file */
+    FILE     *file;   /* .gz file */
+    Byte     *inbuf;  /* input buffer */
+    Byte     *outbuf; /* output buffer */
+    uLong    crc;     /* crc32 of uncompressed data */
+    char     *msg;    /* error message */
+    char     *path;   /* path name for debugging only */
+    int      transparent; /* 1 if input file is not a .gz file */
+    char     mode;    /* 'w' or 'r' */
+    z_off_t  start;   /* start of compressed data in file (header skipped) */
+    z_off_t  in;      /* bytes into deflate or inflate */
+    z_off_t  out;     /* bytes out of deflate or inflate */
+    int      back;    /* one character push-back */
+    int      last;    /* true if push-back is last character */
+} gz_stream;
+
+
+local gzFile gz_open      OF((const char *path, const char *mode, int  fd));
+local int do_flush        OF((gzFile file, int flush));
+local int    get_byte     OF((gz_stream *s));
+local void   check_header OF((gz_stream *s));
+local int    destroy      OF((gz_stream *s));
+local void   putLong      OF((FILE *file, uLong x));
+local uLong  getLong      OF((gz_stream *s));
+
+/* ===========================================================================
+     Opens a gzip (.gz) file for reading or writing. The mode parameter
+   is as in fopen ("rb" or "wb"). The file is given either by file descriptor
+   or path name (if fd == -1).
+     gz_open returns NULL if the file could not be opened or if there was
+   insufficient memory to allocate the (de)compression state; errno
+   can be checked to distinguish the two cases (if errno is zero, the
+   zlib error is Z_MEM_ERROR).
+*/
+local gzFile gz_open (path, mode, fd)
+    const char *path;
+    const char *mode;
+    int  fd;
+{
+    int err;
+    int level = Z_DEFAULT_COMPRESSION; /* compression level */
+    int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
+    char *p = (char*)mode;
+    gz_stream *s;
+    char fmode[80]; /* copy of mode, without the compression level */
+    char *m = fmode;
+
+    if (!path || !mode) return Z_NULL;
+
+    s = (gz_stream *)ALLOC(sizeof(gz_stream));
+    if (!s) return Z_NULL;
+
+    s->stream.zalloc = (alloc_func)0;
+    s->stream.zfree = (free_func)0;
+    s->stream.opaque = (voidpf)0;
+    s->stream.next_in = s->inbuf = Z_NULL;
+    s->stream.next_out = s->outbuf = Z_NULL;
+    s->stream.avail_in = s->stream.avail_out = 0;
+    s->file = NULL;
+    s->z_err = Z_OK;
+    s->z_eof = 0;
+    s->in = 0;
+    s->out = 0;
+    s->back = EOF;
+    s->crc = crc32(0L, Z_NULL, 0);
+    s->msg = NULL;
+    s->transparent = 0;
+
+    s->path = (char*)ALLOC(strlen(path)+1);
+    if (s->path == NULL) {
+        return destroy(s), (gzFile)Z_NULL;
+    }
+    strcpy(s->path, path); /* do this early for debugging */
+
+    s->mode = '\0';
+    do {
+        if (*p == 'r') s->mode = 'r';
+        if (*p == 'w' || *p == 'a') s->mode = 'w';
+        if (*p >= '0' && *p <= '9') {
+            level = *p - '0';
+        } else if (*p == 'f') {
+          strategy = Z_FILTERED;
+        } else if (*p == 'h') {
+          strategy = Z_HUFFMAN_ONLY;
+        } else if (*p == 'R') {
+          strategy = Z_RLE;
+        } else {
+            *m++ = *p; /* copy the mode */
+        }
+    } while (*p++ && m != fmode + sizeof(fmode));
+    if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
+
+    if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+        err = Z_STREAM_ERROR;
+#else
+        err = deflateInit2(&(s->stream), level,
+                           Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
+        /* windowBits is passed < 0 to suppress zlib header */
+
+        s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+#endif
+        if (err != Z_OK || s->outbuf == Z_NULL) {
+            return destroy(s), (gzFile)Z_NULL;
+        }
+    } else {
+        s->stream.next_in  = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
+
+        err = inflateInit2(&(s->stream), -MAX_WBITS);
+        /* windowBits is passed < 0 to tell that there is no zlib header.
+         * Note that in this case inflate *requires* an extra "dummy" byte
+         * after the compressed stream in order to complete decompression and
+         * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+         * present after the compressed stream.
+         */
+        if (err != Z_OK || s->inbuf == Z_NULL) {
+            return destroy(s), (gzFile)Z_NULL;
+        }
+    }
+    s->stream.avail_out = Z_BUFSIZE;
+
+    errno = 0;
+    s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode);
+
+    if (s->file == NULL) {
+        return destroy(s), (gzFile)Z_NULL;
+    }
+    if (s->mode == 'w') {
+        /* Write a very simple .gz header:
+         */
+        fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
+             Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
+        s->start = 10L;
+        /* We use 10L instead of ftell(s->file) to because ftell causes an
+         * fflush on some systems. This version of the library doesn't use
+         * start anyway in write mode, so this initialization is not
+         * necessary.
+         */
+    } else {
+        check_header(s); /* skip the .gz header */
+        s->start = ftell(s->file) - s->stream.avail_in;
+    }
+
+    return (gzFile)s;
+}
+
+/* ===========================================================================
+     Opens a gzip (.gz) file for reading or writing.
+*/
+gzFile ZEXPORT gzopen (path, mode)
+    const char *path;
+    const char *mode;
+{
+    return gz_open (path, mode, -1);
+}
+
+/* ===========================================================================
+     Associate a gzFile with the file descriptor fd. fd is not dup'ed here
+   to mimic the behavio(u)r of fdopen.
+*/
+gzFile ZEXPORT gzdopen (fd, mode)
+    int fd;
+    const char *mode;
+{
+    char name[20];
+
+    if (fd < 0) return (gzFile)Z_NULL;
+    sprintf(name, "<fd:%d>", fd); /* for debugging */
+
+    return gz_open (name, mode, fd);
+}
+
+/* ===========================================================================
+ * Update the compression level and strategy
+ */
+int ZEXPORT gzsetparams (file, level, strategy)
+    gzFile file;
+    int level;
+    int strategy;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+    /* Make room to allow flushing */
+    if (s->stream.avail_out == 0) {
+
+        s->stream.next_out = s->outbuf;
+        if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+            s->z_err = Z_ERRNO;
+        }
+        s->stream.avail_out = Z_BUFSIZE;
+    }
+
+    return deflateParams (&(s->stream), level, strategy);
+}
+
+/* ===========================================================================
+     Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+   for end of file.
+   IN assertion: the stream s has been sucessfully opened for reading.
+*/
+local int get_byte(s)
+    gz_stream *s;
+{
+    if (s->z_eof) return EOF;
+    if (s->stream.avail_in == 0) {
+        errno = 0;
+        s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+        if (s->stream.avail_in == 0) {
+            s->z_eof = 1;
+            if (ferror(s->file)) s->z_err = Z_ERRNO;
+            return EOF;
+        }
+        s->stream.next_in = s->inbuf;
+    }
+    s->stream.avail_in--;
+    return *(s->stream.next_in)++;
+}
+
+/* ===========================================================================
+      Check the gzip header of a gz_stream opened for reading. Set the stream
+    mode to transparent if the gzip magic header is not present; set s->err
+    to Z_DATA_ERROR if the magic header is present but the rest of the header
+    is incorrect.
+    IN assertion: the stream s has already been created sucessfully;
+       s->stream.avail_in is zero for the first time, but may be non-zero
+       for concatenated .gz files.
+*/
+local void check_header(s)
+    gz_stream *s;
+{
+    int method; /* method byte */
+    int flags;  /* flags byte */
+    uInt len;
+    int c;
+
+    /* Assure two bytes in the buffer so we can peek ahead -- handle case
+       where first byte of header is at the end of the buffer after the last
+       gzip segment */
+    len = s->stream.avail_in;
+    if (len < 2) {
+        if (len) s->inbuf[0] = s->stream.next_in[0];
+        errno = 0;
+        len = fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file);
+        if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO;
+        s->stream.avail_in += len;
+        s->stream.next_in = s->inbuf;
+        if (s->stream.avail_in < 2) {
+            s->transparent = s->stream.avail_in;
+            return;
+        }
+    }
+
+    /* Peek ahead to check the gzip magic header */
+    if (s->stream.next_in[0] != gz_magic[0] ||
+        s->stream.next_in[1] != gz_magic[1]) {
+        s->transparent = 1;
+        return;
+    }
+    s->stream.avail_in -= 2;
+    s->stream.next_in += 2;
+
+    /* Check the rest of the gzip header */
+    method = get_byte(s);
+    flags = get_byte(s);
+    if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+        s->z_err = Z_DATA_ERROR;
+        return;
+    }
+
+    /* Discard time, xflags and OS code: */
+    for (len = 0; len < 6; len++) (void)get_byte(s);
+
+    if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+        len  =  (uInt)get_byte(s);
+        len += ((uInt)get_byte(s))<<8;
+        /* len is garbage if EOF but the loop below will quit anyway */
+        while (len-- != 0 && get_byte(s) != EOF) ;
+    }
+    if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+        while ((c = get_byte(s)) != 0 && c != EOF) ;
+    }
+    if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
+        while ((c = get_byte(s)) != 0 && c != EOF) ;
+    }
+    if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
+        for (len = 0; len < 2; len++) (void)get_byte(s);
+    }
+    s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
+}
+
+ /* ===========================================================================
+ * Cleanup then free the given gz_stream. Return a zlib error code.
+   Try freeing in the reverse order of allocations.
+ */
+local int destroy (s)
+    gz_stream *s;
+{
+    int err = Z_OK;
+
+    if (!s) return Z_STREAM_ERROR;
+
+    TRYFREE(s->msg);
+
+    if (s->stream.state != NULL) {
+        if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+            err = Z_STREAM_ERROR;
+#else
+            err = deflateEnd(&(s->stream));
+#endif
+        } else if (s->mode == 'r') {
+            err = inflateEnd(&(s->stream));
+        }
+    }
+    if (s->file != NULL && fclose(s->file)) {
+#ifdef ESPIPE
+        if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
+#endif
+            err = Z_ERRNO;
+    }
+    if (s->z_err < 0) err = s->z_err;
+
+    TRYFREE(s->inbuf);
+    TRYFREE(s->outbuf);
+    TRYFREE(s->path);
+    TRYFREE(s);
+    return err;
+}
+
+/* ===========================================================================
+     Reads the given number of uncompressed bytes from the compressed file.
+   gzread returns the number of bytes actually read (0 for end of file).
+*/
+int ZEXPORT gzread (file, buf, len)
+    gzFile file;
+    voidp buf;
+    unsigned len;
+{
+    gz_stream *s = (gz_stream*)file;
+    Bytef *start = (Bytef*)buf; /* starting point for crc computation */
+    Byte  *next_out; /* == stream.next_out but not forced far (for MSDOS) */
+
+    if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
+
+    if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
+    if (s->z_err == Z_STREAM_END) return 0;  /* EOF */
+
+    next_out = (Byte*)buf;
+    s->stream.next_out = (Bytef*)buf;
+    s->stream.avail_out = len;
+
+    if (s->stream.avail_out && s->back != EOF) {
+        *next_out++ = s->back;
+        s->stream.next_out++;
+        s->stream.avail_out--;
+        s->back = EOF;
+        s->out++;
+        if (s->last) {
+            s->z_err = Z_STREAM_END;
+            return 1;
+        }
+    }
+
+    while (s->stream.avail_out != 0) {
+
+        if (s->transparent) {
+            /* Copy first the lookahead bytes: */
+            uInt n = s->stream.avail_in;
+            if (n > s->stream.avail_out) n = s->stream.avail_out;
+            if (n > 0) {
+                zmemcpy(s->stream.next_out, s->stream.next_in, n);
+                next_out += n;
+                s->stream.next_out = next_out;
+                s->stream.next_in   += n;
+                s->stream.avail_out -= n;
+                s->stream.avail_in  -= n;
+            }
+            if (s->stream.avail_out > 0) {
+                s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out,
+                                             s->file);
+            }
+            len -= s->stream.avail_out;
+            s->in  += len;
+            s->out += len;
+            if (len == 0) s->z_eof = 1;
+            return (int)len;
+        }
+        if (s->stream.avail_in == 0 && !s->z_eof) {
+
+            errno = 0;
+            s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+            if (s->stream.avail_in == 0) {
+                s->z_eof = 1;
+                if (ferror(s->file)) {
+                    s->z_err = Z_ERRNO;
+                    break;
+                }
+            }
+            s->stream.next_in = s->inbuf;
+        }
+        s->in += s->stream.avail_in;
+        s->out += s->stream.avail_out;
+        s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
+        s->in -= s->stream.avail_in;
+        s->out -= s->stream.avail_out;
+
+        if (s->z_err == Z_STREAM_END) {
+            /* Check CRC and original size */
+            s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+            start = s->stream.next_out;
+
+            if (getLong(s) != s->crc) {
+                s->z_err = Z_DATA_ERROR;
+            } else {
+                (void)getLong(s);
+                /* The uncompressed length returned by above getlong() may be
+                 * different from s->out in case of concatenated .gz files.
+                 * Check for such files:
+                 */
+                check_header(s);
+                if (s->z_err == Z_OK) {
+                    inflateReset(&(s->stream));
+                    s->crc = crc32(0L, Z_NULL, 0);
+                }
+            }
+        }
+        if (s->z_err != Z_OK || s->z_eof) break;
+    }
+    s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+
+    return (int)(len - s->stream.avail_out);
+}
+
+
+/* ===========================================================================
+      Reads one byte from the compressed file. gzgetc returns this byte
+   or -1 in case of end of file or error.
+*/
+int ZEXPORT gzgetc(file)
+    gzFile file;
+{
+    unsigned char c;
+
+    return gzread(file, &c, 1) == 1 ? c : -1;
+}
+
+
+/* ===========================================================================
+      Push one byte back onto the stream.
+*/
+int ZEXPORT gzungetc(c, file)
+    int c;
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF;
+    s->back = c;
+    s->out--;
+    s->last = (s->z_err == Z_STREAM_END);
+    if (s->last) s->z_err = Z_OK;
+    s->z_eof = 0;
+    return c;
+}
+
+
+/* ===========================================================================
+      Reads bytes from the compressed file until len-1 characters are
+   read, or a newline character is read and transferred to buf, or an
+   end-of-file condition is encountered.  The string is then terminated
+   with a null character.
+      gzgets returns buf, or Z_NULL in case of error.
+
+      The current implementation is not optimized at all.
+*/
+char * ZEXPORT gzgets(file, buf, len)
+    gzFile file;
+    char *buf;
+    int len;
+{
+    char *b = buf;
+    if (buf == Z_NULL || len <= 0) return Z_NULL;
+
+    while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ;
+    *buf = '\0';
+    return b == buf && len > 0 ? Z_NULL : b;
+}
+
+
+#ifndef NO_GZCOMPRESS
+/* ===========================================================================
+     Writes the given number of uncompressed bytes into the compressed file.
+   gzwrite returns the number of bytes actually written (0 in case of error).
+*/
+int ZEXPORT gzwrite (file, buf, len)
+    gzFile file;
+    voidpc buf;
+    unsigned len;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+    s->stream.next_in = (Bytef*)buf;
+    s->stream.avail_in = len;
+
+    while (s->stream.avail_in != 0) {
+
+        if (s->stream.avail_out == 0) {
+
+            s->stream.next_out = s->outbuf;
+            if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+                s->z_err = Z_ERRNO;
+                break;
+            }
+            s->stream.avail_out = Z_BUFSIZE;
+        }
+        s->in += s->stream.avail_in;
+        s->out += s->stream.avail_out;
+        s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
+        s->in -= s->stream.avail_in;
+        s->out -= s->stream.avail_out;
+        if (s->z_err != Z_OK) break;
+    }
+    s->crc = crc32(s->crc, (const Bytef *)buf, len);
+
+    return (int)(len - s->stream.avail_in);
+}
+
+
+/* ===========================================================================
+     Converts, formats, and writes the args to the compressed file under
+   control of the format string, as in fprintf. gzprintf returns the number of
+   uncompressed bytes actually written (0 in case of error).
+*/
+#ifdef STDC
+#include <stdarg.h>
+
+int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...)
+{
+    char buf[Z_PRINTF_BUFSIZE];
+    va_list va;
+    int len;
+
+    buf[sizeof(buf) - 1] = 0;
+    va_start(va, format);
+#ifdef NO_vsnprintf
+#  ifdef HAS_vsprintf_void
+    (void)vsprintf(buf, format, va);
+    va_end(va);
+    for (len = 0; len < sizeof(buf); len++)
+        if (buf[len] == 0) break;
+#  else
+    len = vsprintf(buf, format, va);
+    va_end(va);
+#  endif
+#else
+#  ifdef HAS_vsnprintf_void
+    (void)vsnprintf(buf, sizeof(buf), format, va);
+    va_end(va);
+    len = strlen(buf);
+#  else
+    len = vsnprintf(buf, sizeof(buf), format, va);
+    va_end(va);
+#  endif
+#endif
+    if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+        return 0;
+    return gzwrite(file, buf, (unsigned)len);
+}
+#else /* not ANSI C */
+
+int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+                       a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+    gzFile file;
+    const char *format;
+    int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+        a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+{
+    char buf[Z_PRINTF_BUFSIZE];
+    int len;
+
+    buf[sizeof(buf) - 1] = 0;
+#ifdef NO_snprintf
+#  ifdef HAS_sprintf_void
+    sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+            a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+    for (len = 0; len < sizeof(buf); len++)
+        if (buf[len] == 0) break;
+#  else
+    len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+                a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#  endif
+#else
+#  ifdef HAS_snprintf_void
+    snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+             a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+    len = strlen(buf);
+#  else
+    len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+                 a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#  endif
+#endif
+    if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+        return 0;
+    return gzwrite(file, buf, len);
+}
+#endif
+
+/* ===========================================================================
+      Writes c, converted to an unsigned char, into the compressed file.
+   gzputc returns the value that was written, or -1 in case of error.
+*/
+int ZEXPORT gzputc(file, c)
+    gzFile file;
+    int c;
+{
+    unsigned char cc = (unsigned char) c; /* required for big endian systems */
+
+    return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1;
+}
+
+
+/* ===========================================================================
+      Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+      gzputs returns the number of characters written, or -1 in case of error.
+*/
+int ZEXPORT gzputs(file, s)
+    gzFile file;
+    const char *s;
+{
+    return gzwrite(file, (char*)s, (unsigned)strlen(s));
+}
+
+
+/* ===========================================================================
+     Flushes all pending output into the compressed file. The parameter
+   flush is as in the deflate() function.
+*/
+local int do_flush (file, flush)
+    gzFile file;
+    int flush;
+{
+    uInt len;
+    int done = 0;
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+    s->stream.avail_in = 0; /* should be zero already anyway */
+
+    for (;;) {
+        len = Z_BUFSIZE - s->stream.avail_out;
+
+        if (len != 0) {
+            if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) {
+                s->z_err = Z_ERRNO;
+                return Z_ERRNO;
+            }
+            s->stream.next_out = s->outbuf;
+            s->stream.avail_out = Z_BUFSIZE;
+        }
+        if (done) break;
+        s->out += s->stream.avail_out;
+        s->z_err = deflate(&(s->stream), flush);
+        s->out -= s->stream.avail_out;
+
+        /* Ignore the second of two consecutive flushes: */
+        if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
+
+        /* deflate has finished flushing only when it hasn't used up
+         * all the available space in the output buffer:
+         */
+        done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
+
+        if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
+    }
+    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+
+int ZEXPORT gzflush (file, flush)
+     gzFile file;
+     int flush;
+{
+    gz_stream *s = (gz_stream*)file;
+    int err = do_flush (file, flush);
+
+    if (err) return err;
+    fflush(s->file);
+    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+#endif /* NO_GZCOMPRESS */
+
+/* ===========================================================================
+      Sets the starting position for the next gzread or gzwrite on the given
+   compressed file. The offset represents a number of bytes in the
+      gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error.
+      SEEK_END is not implemented, returns error.
+      In this version of the library, gzseek can be extremely slow.
+*/
+z_off_t ZEXPORT gzseek (file, offset, whence)
+    gzFile file;
+    z_off_t offset;
+    int whence;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || whence == SEEK_END ||
+        s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
+        return -1L;
+    }
+
+    if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+        return -1L;
+#else
+        if (whence == SEEK_SET) {
+            offset -= s->in;
+        }
+        if (offset < 0) return -1L;
+
+        /* At this point, offset is the number of zero bytes to write. */
+        if (s->inbuf == Z_NULL) {
+            s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */
+            if (s->inbuf == Z_NULL) return -1L;
+            zmemzero(s->inbuf, Z_BUFSIZE);
+        }
+        while (offset > 0)  {
+            uInt size = Z_BUFSIZE;
+            if (offset < Z_BUFSIZE) size = (uInt)offset;
+
+            size = gzwrite(file, s->inbuf, size);
+            if (size == 0) return -1L;
+
+            offset -= size;
+        }
+        return s->in;
+#endif
+    }
+    /* Rest of function is for reading only */
+
+    /* compute absolute position */
+    if (whence == SEEK_CUR) {
+        offset += s->out;
+    }
+    if (offset < 0) return -1L;
+
+    if (s->transparent) {
+        /* map to fseek */
+        s->back = EOF;
+        s->stream.avail_in = 0;
+        s->stream.next_in = s->inbuf;
+        if (fseek(s->file, offset, SEEK_SET) < 0) return -1L;
+
+        s->in = s->out = offset;
+        return offset;
+    }
+
+    /* For a negative seek, rewind and use positive seek */
+    if (offset >= s->out) {
+        offset -= s->out;
+    } else if (gzrewind(file) < 0) {
+        return -1L;
+    }
+    /* offset is now the number of bytes to skip. */
+
+    if (offset != 0 && s->outbuf == Z_NULL) {
+        s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+        if (s->outbuf == Z_NULL) return -1L;
+    }
+    if (offset && s->back != EOF) {
+        s->back = EOF;
+        s->out++;
+        offset--;
+        if (s->last) s->z_err = Z_STREAM_END;
+    }
+    while (offset > 0)  {
+        int size = Z_BUFSIZE;
+        if (offset < Z_BUFSIZE) size = (int)offset;
+
+        size = gzread(file, s->outbuf, (uInt)size);
+        if (size <= 0) return -1L;
+        offset -= size;
+    }
+    return s->out;
+}
+
+/* ===========================================================================
+     Rewinds input file.
+*/
+int ZEXPORT gzrewind (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'r') return -1;
+
+    s->z_err = Z_OK;
+    s->z_eof = 0;
+    s->back = EOF;
+    s->stream.avail_in = 0;
+    s->stream.next_in = s->inbuf;
+    s->crc = crc32(0L, Z_NULL, 0);
+    if (!s->transparent) (void)inflateReset(&s->stream);
+    s->in = 0;
+    s->out = 0;
+    return fseek(s->file, s->start, SEEK_SET);
+}
+
+/* ===========================================================================
+     Returns the starting position for the next gzread or gzwrite on the
+   given compressed file. This position represents a number of bytes in the
+   uncompressed data stream.
+*/
+z_off_t ZEXPORT gztell (file)
+    gzFile file;
+{
+    return gzseek(file, 0L, SEEK_CUR);
+}
+
+/* ===========================================================================
+     Returns 1 when EOF has previously been detected reading the given
+   input stream, otherwise zero.
+*/
+int ZEXPORT gzeof (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    /* With concatenated compressed files that can have embedded
+     * crc trailers, z_eof is no longer the only/best indicator of EOF
+     * on a gz_stream. Handle end-of-stream error explicitly here.
+     */
+    if (s == NULL || s->mode != 'r') return 0;
+    if (s->z_eof) return 1;
+    return s->z_err == Z_STREAM_END;
+}
+
+/* ===========================================================================
+   Outputs a long in LSB order to the given file
+*/
+local void putLong (file, x)
+    FILE *file;
+    uLong x;
+{
+    int n;
+    for (n = 0; n < 4; n++) {
+        fputc((int)(x & 0xff), file);
+        x >>= 8;
+    }
+}
+
+/* ===========================================================================
+   Reads a long in LSB order from the given gz_stream. Sets z_err in case
+   of error.
+*/
+local uLong getLong (s)
+    gz_stream *s;
+{
+    uLong x = (uLong)get_byte(s);
+    int c;
+
+    x += ((uLong)get_byte(s))<<8;
+    x += ((uLong)get_byte(s))<<16;
+    c = get_byte(s);
+    if (c == EOF) s->z_err = Z_DATA_ERROR;
+    x += ((uLong)c)<<24;
+    return x;
+}
+
+/* ===========================================================================
+     Flushes all pending output if necessary, closes the compressed file
+   and deallocates all the (de)compression state.
+*/
+int ZEXPORT gzclose (file)
+    gzFile file;
+{
+    int err;
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL) return Z_STREAM_ERROR;
+
+    if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+        return Z_STREAM_ERROR;
+#else
+        err = do_flush (file, Z_FINISH);
+        if (err != Z_OK) return destroy((gz_stream*)file);
+
+        putLong (s->file, s->crc);
+        putLong (s->file, (uLong)(s->in & 0xffffffff));
+#endif
+    }
+    return destroy((gz_stream*)file);
+}
+
+/* ===========================================================================
+     Returns the error message for the last error which occured on the
+   given compressed file. errnum is set to zlib error number. If an
+   error occured in the file system and not in the compression library,
+   errnum is set to Z_ERRNO and the application may consult errno
+   to get the exact error code.
+*/
+const char * ZEXPORT gzerror (file, errnum)
+    gzFile file;
+    int *errnum;
+{
+    char *m;
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL) {
+        *errnum = Z_STREAM_ERROR;
+        return (const char*)ERR_MSG(Z_STREAM_ERROR);
+    }
+    *errnum = s->z_err;
+    if (*errnum == Z_OK) return (const char*)"";
+
+    m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg);
+
+    if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err);
+
+    TRYFREE(s->msg);
+    s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3);
+    if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR);
+    strcpy(s->msg, s->path);
+    strcat(s->msg, ": ");
+    strcat(s->msg, m);
+    return (const char*)s->msg;
+}
+
+/* ===========================================================================
+     Clear the error and end-of-file flags, and do the same for the real file.
+*/
+void ZEXPORT gzclearerr (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL) return;
+    if (s->z_err != Z_STREAM_END) s->z_err = Z_OK;
+    s->z_eof = 0;
+    clearerr(s->file);
+}
diff --git a/zlib/infback.c b/zlib/infback.c
new file mode 100644 (file)
index 0000000..110b03b
--- /dev/null
@@ -0,0 +1,619 @@
+/* infback.c -- inflate using a call-back interface
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+   This code is largely copied from inflate.c.  Normally either infback.o or
+   inflate.o would be linked into an application--not both.  The interface
+   with inffast.c is retained so that optimized assembler-coded versions of
+   inflate_fast() can be used with either inflate.c or infback.c.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+
+/*
+   strm provides memory allocation functions in zalloc and zfree, or
+   Z_NULL to use the library memory allocation functions.
+
+   windowBits is in the range 8..15, and window is a user-supplied
+   window and output buffer that is 2**windowBits bytes.
+ */
+int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)
+z_stream FAR *strm;
+int windowBits;
+unsigned char FAR *window;
+const char *version;
+int stream_size;
+{
+    struct inflate_state FAR *state;
+
+    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+        stream_size != (int)(sizeof(z_stream)))
+        return Z_VERSION_ERROR;
+    if (strm == Z_NULL || window == Z_NULL ||
+        windowBits < 8 || windowBits > 15)
+        return Z_STREAM_ERROR;
+    strm->msg = Z_NULL;                 /* in case we return an error */
+    if (strm->zalloc == (alloc_func)0) {
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+    }
+    if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+    state = (struct inflate_state FAR *)ZALLOC(strm, 1,
+                                               sizeof(struct inflate_state));
+    if (state == Z_NULL) return Z_MEM_ERROR;
+    Tracev((stderr, "inflate: allocated\n"));
+    strm->state = (voidpf)state;
+    state->wbits = windowBits;
+    state->wsize = 1U << windowBits;
+    state->window = window;
+    state->write = 0;
+    state->whave = 0;
+    return Z_OK;
+}
+
+/*
+   Return state with length and distance decoding tables and index sizes set to
+   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
+   If BUILDFIXED is defined, then instead this routine builds the tables the
+   first time it's called, and returns those tables the first time and
+   thereafter.  This reduces the size of the code by about 2K bytes, in
+   exchange for a little execution time.  However, BUILDFIXED should not be
+   used for threaded applications, since the rewriting of the tables and virgin
+   may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+    static int virgin = 1;
+    static code *lenfix, *distfix;
+    static code fixed[544];
+
+    /* build fixed huffman tables if first call (may not be thread safe) */
+    if (virgin) {
+        unsigned sym, bits;
+        static code *next;
+
+        /* literal/length table */
+        sym = 0;
+        while (sym < 144) state->lens[sym++] = 8;
+        while (sym < 256) state->lens[sym++] = 9;
+        while (sym < 280) state->lens[sym++] = 7;
+        while (sym < 288) state->lens[sym++] = 8;
+        next = fixed;
+        lenfix = next;
+        bits = 9;
+        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+        /* distance table */
+        sym = 0;
+        while (sym < 32) state->lens[sym++] = 5;
+        distfix = next;
+        bits = 5;
+        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+        /* do this just once */
+        virgin = 0;
+    }
+#else /* !BUILDFIXED */
+#   include "inffixed.h"
+#endif /* BUILDFIXED */
+    state->lencode = lenfix;
+    state->lenbits = 9;
+    state->distcode = distfix;
+    state->distbits = 5;
+}
+
+/* Macros for inflateBack(): */
+
+/* Load returned state from inflate_fast() */
+#define LOAD() \
+    do { \
+        put = strm->next_out; \
+        left = strm->avail_out; \
+        next = strm->next_in; \
+        have = strm->avail_in; \
+        hold = state->hold; \
+        bits = state->bits; \
+    } while (0)
+
+/* Set state from registers for inflate_fast() */
+#define RESTORE() \
+    do { \
+        strm->next_out = put; \
+        strm->avail_out = left; \
+        strm->next_in = next; \
+        strm->avail_in = have; \
+        state->hold = hold; \
+        state->bits = bits; \
+    } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+    do { \
+        hold = 0; \
+        bits = 0; \
+    } while (0)
+
+/* Assure that some input is available.  If input is requested, but denied,
+   then return a Z_BUF_ERROR from inflateBack(). */
+#define PULL() \
+    do { \
+        if (have == 0) { \
+            have = in(in_desc, &next); \
+            if (have == 0) { \
+                next = Z_NULL; \
+                ret = Z_BUF_ERROR; \
+                goto inf_leave; \
+            } \
+        } \
+    } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflateBack()
+   with an error if there is no input available. */
+#define PULLBYTE() \
+    do { \
+        PULL(); \
+        have--; \
+        hold += (unsigned long)(*next++) << bits; \
+        bits += 8; \
+    } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator.  If there is
+   not enough available input to do that, then return from inflateBack() with
+   an error. */
+#define NEEDBITS(n) \
+    do { \
+        while (bits < (unsigned)(n)) \
+            PULLBYTE(); \
+    } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+    ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+    do { \
+        hold >>= (n); \
+        bits -= (unsigned)(n); \
+    } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+    do { \
+        hold >>= bits & 7; \
+        bits -= bits & 7; \
+    } while (0)
+
+/* Assure that some output space is available, by writing out the window
+   if it's full.  If the write fails, return from inflateBack() with a
+   Z_BUF_ERROR. */
+#define ROOM() \
+    do { \
+        if (left == 0) { \
+            put = state->window; \
+            left = state->wsize; \
+            state->whave = left; \
+            if (out(out_desc, put, left)) { \
+                ret = Z_BUF_ERROR; \
+                goto inf_leave; \
+            } \
+        } \
+    } while (0)
+
+/*
+   strm provides the memory allocation functions and window buffer on input,
+   and provides information on the unused input on return.  For Z_DATA_ERROR
+   returns, strm will also provide an error message.
+
+   in() and out() are the call-back input and output functions.  When
+   inflateBack() needs more input, it calls in().  When inflateBack() has
+   filled the window with output, or when it completes with data in the
+   window, it calls out() to write out the data.  The application must not
+   change the provided input until in() is called again or inflateBack()
+   returns.  The application must not change the window/output buffer until
+   inflateBack() returns.
+
+   in() and out() are called with a descriptor parameter provided in the
+   inflateBack() call.  This parameter can be a structure that provides the
+   information required to do the read or write, as well as accumulated
+   information on the input and output such as totals and check values.
+
+   in() should return zero on failure.  out() should return non-zero on
+   failure.  If either in() or out() fails, than inflateBack() returns a
+   Z_BUF_ERROR.  strm->next_in can be checked for Z_NULL to see whether it
+   was in() or out() that caused in the error.  Otherwise,  inflateBack()
+   returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
+   error, or Z_MEM_ERROR if it could not allocate memory for the state.
+   inflateBack() can also return Z_STREAM_ERROR if the input parameters
+   are not correct, i.e. strm is Z_NULL or the state was not initialized.
+ */
+int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)
+z_stream FAR *strm;
+in_func in;
+void FAR *in_desc;
+out_func out;
+void FAR *out_desc;
+{
+    struct inflate_state FAR *state;
+    unsigned char FAR *next;    /* next input */
+    unsigned char FAR *put;     /* next output */
+    unsigned have, left;        /* available input and output */
+    unsigned long hold;         /* bit buffer */
+    unsigned bits;              /* bits in bit buffer */
+    unsigned copy;              /* number of stored or match bytes to copy */
+    unsigned char FAR *from;    /* where to copy match bytes from */
+    code this;                  /* current decoding table entry */
+    code last;                  /* parent table entry */
+    unsigned len;               /* length to copy for repeats, bits to drop */
+    int ret;                    /* return code */
+    static const unsigned short order[19] = /* permutation of code lengths */
+        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+    /* Check that the strm exists and that the state was initialized */
+    if (strm == Z_NULL || strm->state == Z_NULL)
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* Reset the state */
+    strm->msg = Z_NULL;
+    state->mode = TYPE;
+    state->last = 0;
+    state->whave = 0;
+    next = strm->next_in;
+    have = next != Z_NULL ? strm->avail_in : 0;
+    hold = 0;
+    bits = 0;
+    put = state->window;
+    left = state->wsize;
+
+    /* Inflate until end of block marked as last */
+    for (;;)
+        switch (state->mode) {
+        case TYPE:
+            /* determine and dispatch block type */
+            if (state->last) {
+                BYTEBITS();
+                state->mode = DONE;
+                break;
+            }
+            NEEDBITS(3);
+            state->last = BITS(1);
+            DROPBITS(1);
+            switch (BITS(2)) {
+            case 0:                             /* stored block */
+                Tracev((stderr, "inflate:     stored block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = STORED;
+                break;
+            case 1:                             /* fixed block */
+                fixedtables(state);
+                Tracev((stderr, "inflate:     fixed codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = LEN;              /* decode codes */
+                break;
+            case 2:                             /* dynamic block */
+                Tracev((stderr, "inflate:     dynamic codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = TABLE;
+                break;
+            case 3:
+                strm->msg = (char *)"invalid block type";
+                state->mode = BAD;
+            }
+            DROPBITS(2);
+            break;
+
+        case STORED:
+            /* get and verify stored block length */
+            BYTEBITS();                         /* go to byte boundary */
+            NEEDBITS(32);
+            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+                strm->msg = (char *)"invalid stored block lengths";
+                state->mode = BAD;
+                break;
+            }
+            state->length = (unsigned)hold & 0xffff;
+            Tracev((stderr, "inflate:       stored length %u\n",
+                    state->length));
+            INITBITS();
+
+            /* copy stored block from input to output */
+            while (state->length != 0) {
+                copy = state->length;
+                PULL();
+                ROOM();
+                if (copy > have) copy = have;
+                if (copy > left) copy = left;
+                zmemcpy(put, next, copy);
+                have -= copy;
+                next += copy;
+                left -= copy;
+                put += copy;
+                state->length -= copy;
+            }
+            Tracev((stderr, "inflate:       stored end\n"));
+            state->mode = TYPE;
+            break;
+
+        case TABLE:
+            /* get dynamic table entries descriptor */
+            NEEDBITS(14);
+            state->nlen = BITS(5) + 257;
+            DROPBITS(5);
+            state->ndist = BITS(5) + 1;
+            DROPBITS(5);
+            state->ncode = BITS(4) + 4;
+            DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+            if (state->nlen > 286 || state->ndist > 30) {
+                strm->msg = (char *)"too many length or distance symbols";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            Tracev((stderr, "inflate:       table sizes ok\n"));
+
+            /* get code length code lengths (not a typo) */
+            state->have = 0;
+            while (state->have < state->ncode) {
+                NEEDBITS(3);
+                state->lens[order[state->have++]] = (unsigned short)BITS(3);
+                DROPBITS(3);
+            }
+            while (state->have < 19)
+                state->lens[order[state->have++]] = 0;
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 7;
+            ret = inflate_table(CODES, state->lens, 19, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid code lengths set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       code lengths ok\n"));
+
+            /* get length and distance code code lengths */
+            state->have = 0;
+            while (state->have < state->nlen + state->ndist) {
+                for (;;) {
+                    this = state->lencode[BITS(state->lenbits)];
+                    if ((unsigned)(this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                if (this.val < 16) {
+                    NEEDBITS(this.bits);
+                    DROPBITS(this.bits);
+                    state->lens[state->have++] = this.val;
+                }
+                else {
+                    if (this.val == 16) {
+                        NEEDBITS(this.bits + 2);
+                        DROPBITS(this.bits);
+                        if (state->have == 0) {
+                            strm->msg = (char *)"invalid bit length repeat";
+                            state->mode = BAD;
+                            break;
+                        }
+                        len = (unsigned)(state->lens[state->have - 1]);
+                        copy = 3 + BITS(2);
+                        DROPBITS(2);
+                    }
+                    else if (this.val == 17) {
+                        NEEDBITS(this.bits + 3);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 3 + BITS(3);
+                        DROPBITS(3);
+                    }
+                    else {
+                        NEEDBITS(this.bits + 7);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 11 + BITS(7);
+                        DROPBITS(7);
+                    }
+                    if (state->have + copy > state->nlen + state->ndist) {
+                        strm->msg = (char *)"invalid bit length repeat";
+                        state->mode = BAD;
+                        break;
+                    }
+                    while (copy--)
+                        state->lens[state->have++] = (unsigned short)len;
+                }
+            }
+
+            /* build code tables */
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 9;
+            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid literal/lengths set";
+                state->mode = BAD;
+                break;
+            }
+            state->distcode = (code const FAR *)(state->next);
+            state->distbits = 6;
+            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+                            &(state->next), &(state->distbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid distances set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       codes ok\n"));
+            state->mode = LEN;
+
+        case LEN:
+            /* use inflate_fast() if we have enough input and output */
+            if (have >= 6 && left >= 258) {
+                RESTORE();
+                if (state->whave < state->wsize)
+                    state->whave = state->wsize - left;
+                inflate_fast(strm, state->wsize);
+                LOAD();
+                break;
+            }
+
+            /* get a literal, length, or end-of-block code */
+            for (;;) {
+                this = state->lencode[BITS(state->lenbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if (this.op && (this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->lencode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            state->length = (unsigned)this.val;
+
+            /* process literal */
+            if (this.op == 0) {
+                Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+                        "inflate:         literal '%c'\n" :
+                        "inflate:         literal 0x%02x\n", this.val));
+                ROOM();
+                *put++ = (unsigned char)(state->length);
+                left--;
+                state->mode = LEN;
+                break;
+            }
+
+            /* process end of block */
+            if (this.op & 32) {
+                Tracevv((stderr, "inflate:         end of block\n"));
+                state->mode = TYPE;
+                break;
+            }
+
+            /* invalid code */
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid literal/length code";
+                state->mode = BAD;
+                break;
+            }
+
+            /* length code -- get extra bits, if any */
+            state->extra = (unsigned)(this.op) & 15;
+            if (state->extra != 0) {
+                NEEDBITS(state->extra);
+                state->length += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            Tracevv((stderr, "inflate:         length %u\n", state->length));
+
+            /* get distance code */
+            for (;;) {
+                this = state->distcode[BITS(state->distbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if ((this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->distcode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+            state->offset = (unsigned)this.val;
+
+            /* get distance extra bits, if any */
+            state->extra = (unsigned)(this.op) & 15;
+            if (state->extra != 0) {
+                NEEDBITS(state->extra);
+                state->offset += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            if (state->offset > state->wsize - (state->whave < state->wsize ?
+                                                left : 0)) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
+
+            /* copy match from window to output */
+            do {
+                ROOM();
+                copy = state->wsize - state->offset;
+                if (copy < left) {
+                    from = put + copy;
+                    copy = left - copy;
+                }
+                else {
+                    from = put - state->offset;
+                    copy = left;
+                }
+                if (copy > state->length) copy = state->length;
+                state->length -= copy;
+                left -= copy;
+                do {
+                    *put++ = *from++;
+                } while (--copy);
+            } while (state->length != 0);
+            break;
+
+        case DONE:
+            /* inflate stream terminated properly -- write leftover output */
+            ret = Z_STREAM_END;
+            if (left < state->wsize) {
+                if (out(out_desc, state->window, state->wsize - left))
+                    ret = Z_BUF_ERROR;
+            }
+            goto inf_leave;
+
+        case BAD:
+            ret = Z_DATA_ERROR;
+            goto inf_leave;
+
+        default:                /* can't happen, but makes compilers happy */
+            ret = Z_STREAM_ERROR;
+            goto inf_leave;
+        }
+
+    /* Return unused input */
+  inf_leave:
+    strm->next_in = next;
+    strm->avail_in = have;
+    return ret;
+}
+
+int ZEXPORT inflateBackEnd(strm)
+z_stream FAR *strm;
+{
+    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+        return Z_STREAM_ERROR;
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+    Tracev((stderr, "inflate: end\n"));
+    return Z_OK;
+}
diff --git a/zlib/inffast.c b/zlib/inffast.c
new file mode 100644 (file)
index 0000000..c716440
--- /dev/null
@@ -0,0 +1,305 @@
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifndef ASMINF
+
+/* Allow machine dependent optimization for post-increment or pre-increment.
+   Based on testing to date,
+   Pre-increment preferred for:
+   - PowerPC G3 (Adler)
+   - MIPS R5000 (Randers-Pehrson)
+   Post-increment preferred for:
+   - none
+   No measurable difference:
+   - Pentium III (Anderson)
+   - 68060 (Nikl)
+ */
+#ifdef POSTINC
+#  define OFF 0
+#  define PUP(a) *(a)++
+#else
+#  define OFF 1
+#  define PUP(a) *++(a)
+#endif
+
+/*
+   Decode literal, length, and distance codes and write out the resulting
+   literal and match bytes until either not enough input or output is
+   available, an end-of-block is encountered, or a data error is encountered.
+   When large enough input and output buffers are supplied to inflate(), for
+   example, a 16K input buffer and a 64K output buffer, more than 95% of the
+   inflate execution time is spent in this routine.
+
+   Entry assumptions:
+
+        state->mode == LEN
+        strm->avail_in >= 6
+        strm->avail_out >= 258
+        start >= strm->avail_out
+        state->bits < 8
+
+   On return, state->mode is one of:
+
+        LEN -- ran out of enough output space or enough available input
+        TYPE -- reached end of block code, inflate() to interpret next block
+        BAD -- error in block data
+
+   Notes:
+
+    - The maximum input bits used by a length/distance pair is 15 bits for the
+      length code, 5 bits for the length extra, 15 bits for the distance code,
+      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
+      Therefore if strm->avail_in >= 6, then there is enough input to avoid
+      checking for available input while decoding.
+
+    - The maximum bytes that a single length/distance pair can output is 258
+      bytes, which is the maximum length that can be coded.  inflate_fast()
+      requires strm->avail_out >= 258 for each loop to avoid checking for
+      output space.
+ */
+void inflate_fast(strm, start)
+z_streamp strm;
+unsigned start;         /* inflate()'s starting value for strm->avail_out */
+{
+    struct inflate_state FAR *state;
+    unsigned char FAR *in;      /* local strm->next_in */
+    unsigned char FAR *last;    /* while in < last, enough input available */
+    unsigned char FAR *out;     /* local strm->next_out */
+    unsigned char FAR *beg;     /* inflate()'s initial strm->next_out */
+    unsigned char FAR *end;     /* while out < end, enough space available */
+    unsigned wsize;             /* window size or zero if not using window */
+    unsigned whave;             /* valid bytes in the window */
+    unsigned write;             /* window write index */
+    unsigned char FAR *window;  /* allocated sliding window, if wsize != 0 */
+    unsigned long hold;         /* local strm->hold */
+    unsigned bits;              /* local strm->bits */
+    code const FAR *lcode;      /* local strm->lencode */
+    code const FAR *dcode;      /* local strm->distcode */
+    unsigned lmask;             /* mask for first level of length codes */
+    unsigned dmask;             /* mask for first level of distance codes */
+    code this;                  /* retrieved table entry */
+    unsigned op;                /* code bits, operation, extra bits, or */
+                                /*  window position, window bytes to copy */
+    unsigned len;               /* match length, unused bytes */
+    unsigned dist;              /* match distance */
+    unsigned char FAR *from;    /* where to copy match from */
+
+    /* copy state to local variables */
+    state = (struct inflate_state FAR *)strm->state;
+    in = strm->next_in - OFF;
+    last = in + (strm->avail_in - 5);
+    out = strm->next_out - OFF;
+    beg = out - (start - strm->avail_out);
+    end = out + (strm->avail_out - 257);
+    wsize = state->wsize;
+    whave = state->whave;
+    write = state->write;
+    window = state->window;
+    hold = state->hold;
+    bits = state->bits;
+    lcode = state->lencode;
+    dcode = state->distcode;
+    lmask = (1U << state->lenbits) - 1;
+    dmask = (1U << state->distbits) - 1;
+
+    /* decode literals and length/distances until end-of-block or not enough
+       input data or output space */
+    do {
+        if (bits < 15) {
+            hold += (unsigned long)(PUP(in)) << bits;
+            bits += 8;
+            hold += (unsigned long)(PUP(in)) << bits;
+            bits += 8;
+        }
+        this = lcode[hold & lmask];
+      dolen:
+        op = (unsigned)(this.bits);
+        hold >>= op;
+        bits -= op;
+        op = (unsigned)(this.op);
+        if (op == 0) {                          /* literal */
+            Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+                    "inflate:         literal '%c'\n" :
+                    "inflate:         literal 0x%02x\n", this.val));
+            PUP(out) = (unsigned char)(this.val);
+        }
+        else if (op & 16) {                     /* length base */
+            len = (unsigned)(this.val);
+            op &= 15;                           /* number of extra bits */
+            if (op) {
+                if (bits < op) {
+                    hold += (unsigned long)(PUP(in)) << bits;
+                    bits += 8;
+                }
+                len += (unsigned)hold & ((1U << op) - 1);
+                hold >>= op;
+                bits -= op;
+            }
+            Tracevv((stderr, "inflate:         length %u\n", len));
+            if (bits < 15) {
+                hold += (unsigned long)(PUP(in)) << bits;
+                bits += 8;
+                hold += (unsigned long)(PUP(in)) << bits;
+                bits += 8;
+            }
+            this = dcode[hold & dmask];
+          dodist:
+            op = (unsigned)(this.bits);
+            hold >>= op;
+            bits -= op;
+            op = (unsigned)(this.op);
+            if (op & 16) {                      /* distance base */
+                dist = (unsigned)(this.val);
+                op &= 15;                       /* number of extra bits */
+                if (bits < op) {
+                    hold += (unsigned long)(PUP(in)) << bits;
+                    bits += 8;
+                    if (bits < op) {
+                        hold += (unsigned long)(PUP(in)) << bits;
+                        bits += 8;
+                    }
+                }
+                dist += (unsigned)hold & ((1U << op) - 1);
+                hold >>= op;
+                bits -= op;
+                Tracevv((stderr, "inflate:         distance %u\n", dist));
+                op = (unsigned)(out - beg);     /* max distance in output */
+                if (dist > op) {                /* see if copy from window */
+                    op = dist - op;             /* distance back in window */
+                    if (op > whave) {
+                        strm->msg = (char *)"invalid distance too far back";
+                        state->mode = BAD;
+                        break;
+                    }
+                    from = window - OFF;
+                    if (write == 0) {           /* very common case */
+                        from += wsize - op;
+                        if (op < len) {         /* some from window */
+                            len -= op;
+                            do {
+                                PUP(out) = PUP(from);
+                            } while (--op);
+                            from = out - dist;  /* rest from output */
+                        }
+                    }
+                    else if (write < op) {      /* wrap around window */
+                        from += wsize + write - op;
+                        op -= write;
+                        if (op < len) {         /* some from end of window */
+                            len -= op;
+                            do {
+                                PUP(out) = PUP(from);
+                            } while (--op);
+                            from = window - OFF;
+                            if (write < len) {  /* some from start of window */
+                                op = write;
+                                len -= op;
+                                do {
+                                    PUP(out) = PUP(from);
+                                } while (--op);
+                                from = out - dist;      /* rest from output */
+                            }
+                        }
+                    }
+                    else {                      /* contiguous in window */
+                        from += write - op;
+                        if (op < len) {         /* some from window */
+                            len -= op;
+                            do {
+                                PUP(out) = PUP(from);
+                            } while (--op);
+                            from = out - dist;  /* rest from output */
+                        }
+                    }
+                    while (len > 2) {
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        len -= 3;
+                    }
+                    if (len) {
+                        PUP(out) = PUP(from);
+                        if (len > 1)
+                            PUP(out) = PUP(from);
+                    }
+                }
+                else {
+                    from = out - dist;          /* copy direct from output */
+                    do {                        /* minimum length is three */
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        len -= 3;
+                    } while (len > 2);
+                    if (len) {
+                        PUP(out) = PUP(from);
+                        if (len > 1)
+                            PUP(out) = PUP(from);
+                    }
+                }
+            }
+            else if ((op & 64) == 0) {          /* 2nd level distance code */
+                this = dcode[this.val + (hold & ((1U << op) - 1))];
+                goto dodist;
+            }
+            else {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+        }
+        else if ((op & 64) == 0) {              /* 2nd level length code */
+            this = lcode[this.val + (hold & ((1U << op) - 1))];
+            goto dolen;
+        }
+        else if (op & 32) {                     /* end-of-block */
+            Tracevv((stderr, "inflate:         end of block\n"));
+            state->mode = TYPE;
+            break;
+        }
+        else {
+            strm->msg = (char *)"invalid literal/length code";
+            state->mode = BAD;
+            break;
+        }
+    } while (in < last && out < end);
+
+    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+    len = bits >> 3;
+    in -= len;
+    bits -= len << 3;
+    hold &= (1U << bits) - 1;
+
+    /* update state and return */
+    strm->next_in = in + OFF;
+    strm->next_out = out + OFF;
+    strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+    strm->avail_out = (unsigned)(out < end ?
+                                 257 + (end - out) : 257 - (out - end));
+    state->hold = hold;
+    state->bits = bits;
+    return;
+}
+
+/*
+   inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+   - Using bit fields for code structure
+   - Different op definition to avoid & for extra bits (do & for table bits)
+   - Three separate decoding do-loops for direct, window, and write == 0
+   - Special case for distance > 1 copies to do overlapped load and store copy
+   - Explicit branch predictions (based on measured branch probabilities)
+   - Deferring match copy and interspersed it with decoding subsequent codes
+   - Swapping literal/length else
+   - Swapping window/direct else
+   - Larger unrolled copy loops (three is about right)
+   - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
diff --git a/zlib/inffast.h b/zlib/inffast.h
new file mode 100644 (file)
index 0000000..1e88d2d
--- /dev/null
@@ -0,0 +1,11 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+void inflate_fast OF((z_streamp strm, unsigned start));
diff --git a/zlib/inffixed.h b/zlib/inffixed.h
new file mode 100644 (file)
index 0000000..75ed4b5
--- /dev/null
@@ -0,0 +1,94 @@
+    /* inffixed.h -- table for decoding fixed codes
+     * Generated automatically by makefixed().
+     */
+
+    /* WARNING: this file should *not* be used by applications. It
+       is part of the implementation of the compression library and
+       is subject to change. Applications should only use zlib.h.
+     */
+
+    static const code lenfix[512] = {
+        {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+        {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+        {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+        {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+        {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+        {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+        {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+        {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+        {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+        {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+        {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+        {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+        {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+        {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+        {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+        {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+        {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+        {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+        {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+        {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+        {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+        {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+        {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+        {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+        {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+        {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+        {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+        {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+        {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+        {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+        {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+        {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+        {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+        {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+        {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+        {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+        {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+        {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+        {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+        {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+        {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+        {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+        {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+        {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+        {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+        {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+        {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+        {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+        {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+        {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+        {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+        {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+        {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+        {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+        {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+        {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+        {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+        {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+        {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+        {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+        {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+        {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+        {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+        {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+        {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+        {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+        {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+        {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+        {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+        {0,9,255}
+    };
+
+    static const code distfix[32] = {
+        {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+        {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+        {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+        {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+        {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+        {22,5,193},{64,5,0}
+    };
diff --git a/zlib/inflate.c b/zlib/inflate.c
new file mode 100644 (file)
index 0000000..a53b5c7
--- /dev/null
@@ -0,0 +1,1270 @@
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * Change history:
+ *
+ * 1.2.beta0    24 Nov 2002
+ * - First version -- complete rewrite of inflate to simplify code, avoid
+ *   creation of window when not needed, minimize use of window when it is
+ *   needed, make inffast.c even faster, implement gzip decoding, and to
+ *   improve code readability and style over the previous zlib inflate code
+ *
+ * 1.2.beta1    25 Nov 2002
+ * - Use pointers for available input and output checking in inffast.c
+ * - Remove input and output counters in inffast.c
+ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
+ * - Remove unnecessary second byte pull from length extra in inffast.c
+ * - Unroll direct copy to three copies per loop in inffast.c
+ *
+ * 1.2.beta2    4 Dec 2002
+ * - Change external routine names to reduce potential conflicts
+ * - Correct filename to inffixed.h for fixed tables in inflate.c
+ * - Make hbuf[] unsigned char to match parameter type in inflate.c
+ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
+ *   to avoid negation problem on Alphas (64 bit) in inflate.c
+ *
+ * 1.2.beta3    22 Dec 2002
+ * - Add comments on state->bits assertion in inffast.c
+ * - Add comments on op field in inftrees.h
+ * - Fix bug in reuse of allocated window after inflateReset()
+ * - Remove bit fields--back to byte structure for speed
+ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
+ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
+ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
+ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
+ * - Use local copies of stream next and avail values, as well as local bit
+ *   buffer and bit count in inflate()--for speed when inflate_fast() not used
+ *
+ * 1.2.beta4    1 Jan 2003
+ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
+ * - Move a comment on output buffer sizes from inffast.c to inflate.c
+ * - Add comments in inffast.c to introduce the inflate_fast() routine
+ * - Rearrange window copies in inflate_fast() for speed and simplification
+ * - Unroll last copy for window match in inflate_fast()
+ * - Use local copies of window variables in inflate_fast() for speed
+ * - Pull out common write == 0 case for speed in inflate_fast()
+ * - Make op and len in inflate_fast() unsigned for consistency
+ * - Add FAR to lcode and dcode declarations in inflate_fast()
+ * - Simplified bad distance check in inflate_fast()
+ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
+ *   source file infback.c to provide a call-back interface to inflate for
+ *   programs like gzip and unzip -- uses window as output buffer to avoid
+ *   window copying
+ *
+ * 1.2.beta5    1 Jan 2003
+ * - Improved inflateBack() interface to allow the caller to provide initial
+ *   input in strm.
+ * - Fixed stored blocks bug in inflateBack()
+ *
+ * 1.2.beta6    4 Jan 2003
+ * - Added comments in inffast.c on effectiveness of POSTINC
+ * - Typecasting all around to reduce compiler warnings
+ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
+ *   make compilers happy
+ * - Changed type of window in inflateBackInit() to unsigned char *
+ *
+ * 1.2.beta7    27 Jan 2003
+ * - Changed many types to unsigned or unsigned short to avoid warnings
+ * - Added inflateCopy() function
+ *
+ * 1.2.0        9 Mar 2003
+ * - Changed inflateBack() interface to provide separate opaque descriptors
+ *   for the in() and out() functions
+ * - Changed inflateBack() argument and in_func typedef to swap the length
+ *   and buffer address return values for the input function
+ * - Check next_in and next_out for Z_NULL on entry to inflate()
+ *
+ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifdef MAKEFIXED
+#  ifndef BUILDFIXED
+#    define BUILDFIXED
+#  endif
+#endif
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+local int updatewindow OF((z_streamp strm, unsigned out));
+#ifdef BUILDFIXED
+   void makefixed OF((void));
+#endif
+local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf,
+                              unsigned len));
+
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    strm->total_in = strm->total_out = state->total = 0;
+    strm->msg = Z_NULL;
+    state->mode = HEAD;
+    state->last = 0;
+    state->havedict = 0;
+    state->wsize = 0;
+    state->whave = 0;
+    state->hold = 0;
+    state->bits = 0;
+    state->lencode = state->distcode = state->next = state->codes;
+    Tracev((stderr, "inflate: reset\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
+z_streamp strm;
+int windowBits;
+const char *version;
+int stream_size;
+{
+    struct inflate_state FAR *state;
+
+    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+        stream_size != (int)(sizeof(z_stream)))
+        return Z_VERSION_ERROR;
+    if (strm == Z_NULL) return Z_STREAM_ERROR;
+    strm->msg = Z_NULL;                 /* in case we return an error */
+    if (strm->zalloc == (alloc_func)0) {
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+    }
+    if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+    state = (struct inflate_state FAR *)
+            ZALLOC(strm, 1, sizeof(struct inflate_state));
+    if (state == Z_NULL) return Z_MEM_ERROR;
+    Tracev((stderr, "inflate: allocated\n"));
+    strm->state = (voidpf)state;
+    if (windowBits < 0) {
+        state->wrap = 0;
+        windowBits = -windowBits;
+    }
+    else {
+        state->wrap = (windowBits >> 4) + 1;
+#ifdef GUNZIP
+        if (windowBits < 48) windowBits &= 15;
+#endif
+    }
+    if (windowBits < 8 || windowBits > 15) {
+        ZFREE(strm, state);
+        strm->state = Z_NULL;
+        return Z_STREAM_ERROR;
+    }
+    state->wbits = (unsigned)windowBits;
+    state->window = Z_NULL;
+    return inflateReset(strm);
+}
+
+int ZEXPORT inflateInit_(strm, version, stream_size)
+z_streamp strm;
+const char *version;
+int stream_size;
+{
+    return inflateInit2_(strm, DEF_WBITS, version, stream_size);
+}
+
+/*
+   Return state with length and distance decoding tables and index sizes set to
+   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
+   If BUILDFIXED is defined, then instead this routine builds the tables the
+   first time it's called, and returns those tables the first time and
+   thereafter.  This reduces the size of the code by about 2K bytes, in
+   exchange for a little execution time.  However, BUILDFIXED should not be
+   used for threaded applications, since the rewriting of the tables and virgin
+   may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+    static int virgin = 1;
+    static code *lenfix, *distfix;
+    static code fixed[544];
+
+    /* build fixed huffman tables if first call (may not be thread safe) */
+    if (virgin) {
+        unsigned sym, bits;
+        static code *next;
+
+        /* literal/length table */
+        sym = 0;
+        while (sym < 144) state->lens[sym++] = 8;
+        while (sym < 256) state->lens[sym++] = 9;
+        while (sym < 280) state->lens[sym++] = 7;
+        while (sym < 288) state->lens[sym++] = 8;
+        next = fixed;
+        lenfix = next;
+        bits = 9;
+        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+        /* distance table */
+        sym = 0;
+        while (sym < 32) state->lens[sym++] = 5;
+        distfix = next;
+        bits = 5;
+        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+        /* do this just once */
+        virgin = 0;
+    }
+#else /* !BUILDFIXED */
+#   include "inffixed.h"
+#endif /* BUILDFIXED */
+    state->lencode = lenfix;
+    state->lenbits = 9;
+    state->distcode = distfix;
+    state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include <stdio.h>
+
+/*
+   Write out the inffixed.h that is #include'd above.  Defining MAKEFIXED also
+   defines BUILDFIXED, so the tables are built on the fly.  makefixed() writes
+   those tables to stdout, which would be piped to inffixed.h.  A small program
+   can simply call makefixed to do this:
+
+    void makefixed(void);
+
+    int main(void)
+    {
+        makefixed();
+        return 0;
+    }
+
+   Then that can be linked with zlib built with MAKEFIXED defined and run:
+
+    a.out > inffixed.h
+ */
+void makefixed()
+{
+    unsigned low, size;
+    struct inflate_state state;
+
+    fixedtables(&state);
+    puts("    /* inffixed.h -- table for decoding fixed codes");
+    puts("     * Generated automatically by makefixed().");
+    puts("     */");
+    puts("");
+    puts("    /* WARNING: this file should *not* be used by applications.");
+    puts("       It is part of the implementation of this library and is");
+    puts("       subject to change. Applications should only use zlib.h.");
+    puts("     */");
+    puts("");
+    size = 1U << 9;
+    printf("    static const code lenfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 7) == 0) printf("\n        ");
+        printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits,
+               state.lencode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n    };");
+    size = 1U << 5;
+    printf("\n    static const code distfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 6) == 0) printf("\n        ");
+        printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+               state.distcode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n    };");
+}
+#endif /* MAKEFIXED */
+
+/*
+   Update the window with the last wsize (normally 32K) bytes written before
+   returning.  If window does not exist yet, create it.  This is only called
+   when a window is already in use, or when output has been written during this
+   inflate call, but the end of the deflate stream has not been reached yet.
+   It is also called to create a window for dictionary data when a dictionary
+   is loaded.
+
+   Providing output buffers larger than 32K to inflate() should provide a speed
+   advantage, since only the last 32K of output is copied to the sliding window
+   upon return from inflate(), and since all distances after the first 32K of
+   output will fall in the output data, making match copies simpler and faster.
+   The advantage may be dependent on the size of the processor's data caches.
+ */
+local int updatewindow(strm, out)
+z_streamp strm;
+unsigned out;
+{
+    struct inflate_state FAR *state;
+    unsigned copy, dist;
+
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* if it hasn't been done already, allocate space for the window */
+    if (state->window == Z_NULL) {
+        state->window = (unsigned char FAR *)
+                        ZALLOC(strm, 1U << state->wbits,
+                               sizeof(unsigned char));
+        if (state->window == Z_NULL) return 1;
+    }
+
+    /* if window not in use yet, initialize */
+    if (state->wsize == 0) {
+        state->wsize = 1U << state->wbits;
+        state->write = 0;
+        state->whave = 0;
+    }
+
+    /* copy state->wsize or less output bytes into the circular window */
+    copy = out - strm->avail_out;
+    if (copy >= state->wsize) {
+        zmemcpy(state->window, strm->next_out - state->wsize, state->wsize);
+        state->write = 0;
+        state->whave = state->wsize;
+    }
+    else {
+        dist = state->wsize - state->write;
+        if (dist > copy) dist = copy;
+        zmemcpy(state->window + state->write, strm->next_out - copy, dist);
+        copy -= dist;
+        if (copy) {
+            zmemcpy(state->window, strm->next_out - copy, copy);
+            state->write = copy;
+            state->whave = state->wsize;
+        }
+        else {
+            state->write += dist;
+            if (state->write == state->wsize) state->write = 0;
+            if (state->whave < state->wsize) state->whave += dist;
+        }
+    }
+    return 0;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#ifdef GUNZIP
+#  define UPDATE(check, buf, len) \
+    (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
+#else
+#  define UPDATE(check, buf, len) adler32(check, buf, len)
+#endif
+
+/* check macros for header crc */
+#ifdef GUNZIP
+#  define CRC2(check, word) \
+    do { \
+        hbuf[0] = (unsigned char)(word); \
+        hbuf[1] = (unsigned char)((word) >> 8); \
+        check = crc32(check, hbuf, 2); \
+    } while (0)
+
+#  define CRC4(check, word) \
+    do { \
+        hbuf[0] = (unsigned char)(word); \
+        hbuf[1] = (unsigned char)((word) >> 8); \
+        hbuf[2] = (unsigned char)((word) >> 16); \
+        hbuf[3] = (unsigned char)((word) >> 24); \
+        check = crc32(check, hbuf, 4); \
+    } while (0)
+#endif
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+    do { \
+        put = strm->next_out; \
+        left = strm->avail_out; \
+        next = strm->next_in; \
+        have = strm->avail_in; \
+        hold = state->hold; \
+        bits = state->bits; \
+    } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+    do { \
+        strm->next_out = put; \
+        strm->avail_out = left; \
+        strm->next_in = next; \
+        strm->avail_in = have; \
+        state->hold = hold; \
+        state->bits = bits; \
+    } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+    do { \
+        hold = 0; \
+        bits = 0; \
+    } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+   if there is no input available. */
+#define PULLBYTE() \
+    do { \
+        if (have == 0) goto inf_leave; \
+        have--; \
+        hold += (unsigned long)(*next++) << bits; \
+        bits += 8; \
+    } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator.  If there is
+   not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+    do { \
+        while (bits < (unsigned)(n)) \
+            PULLBYTE(); \
+    } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+    ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+    do { \
+        hold >>= (n); \
+        bits -= (unsigned)(n); \
+    } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+    do { \
+        hold >>= bits & 7; \
+        bits -= bits & 7; \
+    } while (0)
+
+/* Reverse the bytes in a 32-bit value */
+#define REVERSE(q) \
+    ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+     (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+/*
+   inflate() uses a state machine to process as much input data and generate as
+   much output data as possible before returning.  The state machine is
+   structured roughly as follows:
+
+    for (;;) switch (state) {
+    ...
+    case STATEn:
+        if (not enough input data or output space to make progress)
+            return;
+        ... make progress ...
+        state = STATEm;
+        break;
+    ...
+    }
+
+   so when inflate() is called again, the same case is attempted again, and
+   if the appropriate resources are provided, the machine proceeds to the
+   next state.  The NEEDBITS() macro is usually the way the state evaluates
+   whether it can proceed or should return.  NEEDBITS() does the return if
+   the requested bits are not available.  The typical use of the BITS macros
+   is:
+
+        NEEDBITS(n);
+        ... do something with BITS(n) ...
+        DROPBITS(n);
+
+   where NEEDBITS(n) either returns from inflate() if there isn't enough
+   input left to load n bits into the accumulator, or it continues.  BITS(n)
+   gives the low n bits in the accumulator.  When done, DROPBITS(n) drops
+   the low n bits off the accumulator.  INITBITS() clears the accumulator
+   and sets the number of available bits to zero.  BYTEBITS() discards just
+   enough bits to put the accumulator on a byte boundary.  After BYTEBITS()
+   and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+   NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+   if there is no input available.  The decoding of variable length codes uses
+   PULLBYTE() directly in order to pull just enough bytes to decode the next
+   code, and no more.
+
+   Some states loop until they get enough input, making sure that enough
+   state information is maintained to continue the loop where it left off
+   if NEEDBITS() returns in the loop.  For example, want, need, and keep
+   would all have to actually be part of the saved state in case NEEDBITS()
+   returns:
+
+    case STATEw:
+        while (want < need) {
+            NEEDBITS(n);
+            keep[want++] = BITS(n);
+            DROPBITS(n);
+        }
+        state = STATEx;
+    case STATEx:
+
+   As shown above, if the next state is also the next case, then the break
+   is omitted.
+
+   A state may also return if there is not enough output space available to
+   complete that state.  Those states are copying stored data, writing a
+   literal byte, and copying a matching string.
+
+   When returning, a "goto inf_leave" is used to update the total counters,
+   update the check value, and determine whether any progress has been made
+   during that inflate() call in order to return the proper return code.
+   Progress is defined as a change in either strm->avail_in or strm->avail_out.
+   When there is a window, goto inf_leave will update the window with the last
+   output written.  If a goto inf_leave occurs in the middle of decompression
+   and there is no window currently, goto inf_leave will create one and copy
+   output to the window for the next call of inflate().
+
+   In this implementation, the flush parameter of inflate() only affects the
+   return code (per zlib.h).  inflate() always writes as much as possible to
+   strm->next_out, given the space available and the provided input--the effect
+   documented in zlib.h of Z_SYNC_FLUSH.  Furthermore, inflate() always defers
+   the allocation of and copying into a sliding window until necessary, which
+   provides the effect documented in zlib.h for Z_FINISH when the entire input
+   stream available.  So the only thing the flush parameter actually does is:
+   when flush is set to Z_FINISH, inflate() cannot return Z_OK.  Instead it
+   will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int ZEXPORT inflate(strm, flush)
+z_streamp strm;
+int flush;
+{
+    struct inflate_state FAR *state;
+    unsigned char FAR *next;    /* next input */
+    unsigned char FAR *put;     /* next output */
+    unsigned have, left;        /* available input and output */
+    unsigned long hold;         /* bit buffer */
+    unsigned bits;              /* bits in bit buffer */
+    unsigned in, out;           /* save starting available input and output */
+    unsigned copy;              /* number of stored or match bytes to copy */
+    unsigned char FAR *from;    /* where to copy match bytes from */
+    code this;                  /* current decoding table entry */
+    code last;                  /* parent table entry */
+    unsigned len;               /* length to copy for repeats, bits to drop */
+    int ret;                    /* return code */
+#ifdef GUNZIP
+    unsigned char hbuf[4];      /* buffer for gzip header crc calculation */
+#endif
+    static const unsigned short order[19] = /* permutation of code lengths */
+        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+    if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL ||
+        (strm->next_in == Z_NULL && strm->avail_in != 0))
+        return Z_STREAM_ERROR;
+
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->mode == TYPE) state->mode = TYPEDO;      /* skip check */
+    LOAD();
+    in = have;
+    out = left;
+    ret = Z_OK;
+    for (;;)
+        switch (state->mode) {
+        case HEAD:
+            if (state->wrap == 0) {
+                state->mode = TYPEDO;
+                break;
+            }
+            NEEDBITS(16);
+#ifdef GUNZIP
+            if ((state->wrap & 2) && hold == 0x8b1f) {  /* gzip header */
+                state->check = crc32(0L, Z_NULL, 0);
+                CRC2(state->check, hold);
+                INITBITS();
+                state->mode = FLAGS;
+                break;
+            }
+            state->flags = 0;           /* expect zlib header */
+            if (!(state->wrap & 1) ||   /* check if zlib header allowed */
+#else
+            if (
+#endif
+                ((BITS(8) << 8) + (hold >> 8)) % 31) {
+                strm->msg = (char *)"incorrect header check";
+                state->mode = BAD;
+                break;
+            }
+            if (BITS(4) != Z_DEFLATED) {
+                strm->msg = (char *)"unknown compression method";
+                state->mode = BAD;
+                break;
+            }
+            DROPBITS(4);
+            if (BITS(4) + 8 > state->wbits) {
+                strm->msg = (char *)"invalid window size";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:   zlib header ok\n"));
+            strm->adler = state->check = adler32(0L, Z_NULL, 0);
+            state->mode = hold & 0x200 ? DICTID : TYPE;
+            INITBITS();
+            break;
+#ifdef GUNZIP
+        case FLAGS:
+            NEEDBITS(16);
+            state->flags = (int)(hold);
+            if ((state->flags & 0xff) != Z_DEFLATED) {
+                strm->msg = (char *)"unknown compression method";
+                state->mode = BAD;
+                break;
+            }
+            if (state->flags & 0xe000) {
+                strm->msg = (char *)"unknown header flags set";
+                state->mode = BAD;
+                break;
+            }
+            if (state->flags & 0x0200) CRC2(state->check, hold);
+            INITBITS();
+            state->mode = TIME;
+        case TIME:
+            NEEDBITS(32);
+            if (state->flags & 0x0200) CRC4(state->check, hold);
+            INITBITS();
+            state->mode = OS;
+        case OS:
+            NEEDBITS(16);
+            if (state->flags & 0x0200) CRC2(state->check, hold);
+            INITBITS();
+            state->mode = EXLEN;
+        case EXLEN:
+            if (state->flags & 0x0400) {
+                NEEDBITS(16);
+                state->length = (unsigned)(hold);
+                if (state->flags & 0x0200) CRC2(state->check, hold);
+                INITBITS();
+            }
+            state->mode = EXTRA;
+        case EXTRA:
+            if (state->flags & 0x0400) {
+                copy = state->length;
+                if (copy > have) copy = have;
+                if (copy) {
+                    if (state->flags & 0x0200)
+                        state->check = crc32(state->check, next, copy);
+                    have -= copy;
+                    next += copy;
+                    state->length -= copy;
+                }
+                if (state->length) goto inf_leave;
+            }
+            state->mode = NAME;
+        case NAME:
+            if (state->flags & 0x0800) {
+                if (have == 0) goto inf_leave;
+                copy = 0;
+                do {
+                    len = (unsigned)(next[copy++]);
+                } while (len && copy < have);
+                if (state->flags & 0x02000)
+                    state->check = crc32(state->check, next, copy);
+                have -= copy;
+                next += copy;
+                if (len) goto inf_leave;
+            }
+            state->mode = COMMENT;
+        case COMMENT:
+            if (state->flags & 0x1000) {
+                if (have == 0) goto inf_leave;
+                copy = 0;
+                do {
+                    len = (unsigned)(next[copy++]);
+                } while (len && copy < have);
+                if (state->flags & 0x02000)
+                    state->check = crc32(state->check, next, copy);
+                have -= copy;
+                next += copy;
+                if (len) goto inf_leave;
+            }
+            state->mode = HCRC;
+        case HCRC:
+            if (state->flags & 0x0200) {
+                NEEDBITS(16);
+                if (hold != (state->check & 0xffff)) {
+                    strm->msg = (char *)"header crc mismatch";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+            }
+            strm->adler = state->check = crc32(0L, Z_NULL, 0);
+            state->mode = TYPE;
+            break;
+#endif
+        case DICTID:
+            NEEDBITS(32);
+            strm->adler = state->check = REVERSE(hold);
+            INITBITS();
+            state->mode = DICT;
+        case DICT:
+            if (state->havedict == 0) {
+                RESTORE();
+                return Z_NEED_DICT;
+            }
+            strm->adler = state->check = adler32(0L, Z_NULL, 0);
+            state->mode = TYPE;
+        case TYPE:
+            if (flush == Z_BLOCK) goto inf_leave;
+        case TYPEDO:
+            if (state->last) {
+                BYTEBITS();
+                state->mode = CHECK;
+                break;
+            }
+            NEEDBITS(3);
+            state->last = BITS(1);
+            DROPBITS(1);
+            switch (BITS(2)) {
+            case 0:                             /* stored block */
+                Tracev((stderr, "inflate:     stored block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = STORED;
+                break;
+            case 1:                             /* fixed block */
+                fixedtables(state);
+                Tracev((stderr, "inflate:     fixed codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = LEN;              /* decode codes */
+                break;
+            case 2:                             /* dynamic block */
+                Tracev((stderr, "inflate:     dynamic codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = TABLE;
+                break;
+            case 3:
+                strm->msg = (char *)"invalid block type";
+                state->mode = BAD;
+            }
+            DROPBITS(2);
+            break;
+        case STORED:
+            BYTEBITS();                         /* go to byte boundary */
+            NEEDBITS(32);
+            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+                strm->msg = (char *)"invalid stored block lengths";
+                state->mode = BAD;
+                break;
+            }
+            state->length = (unsigned)hold & 0xffff;
+            Tracev((stderr, "inflate:       stored length %u\n",
+                    state->length));
+            INITBITS();
+            state->mode = COPY;
+        case COPY:
+            copy = state->length;
+            if (copy) {
+                if (copy > have) copy = have;
+                if (copy > left) copy = left;
+                if (copy == 0) goto inf_leave;
+                zmemcpy(put, next, copy);
+                have -= copy;
+                next += copy;
+                left -= copy;
+                put += copy;
+                state->length -= copy;
+                break;
+            }
+            Tracev((stderr, "inflate:       stored end\n"));
+            state->mode = TYPE;
+            break;
+        case TABLE:
+            NEEDBITS(14);
+            state->nlen = BITS(5) + 257;
+            DROPBITS(5);
+            state->ndist = BITS(5) + 1;
+            DROPBITS(5);
+            state->ncode = BITS(4) + 4;
+            DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+            if (state->nlen > 286 || state->ndist > 30) {
+                strm->msg = (char *)"too many length or distance symbols";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            Tracev((stderr, "inflate:       table sizes ok\n"));
+            state->have = 0;
+            state->mode = LENLENS;
+        case LENLENS:
+            while (state->have < state->ncode) {
+                NEEDBITS(3);
+                state->lens[order[state->have++]] = (unsigned short)BITS(3);
+                DROPBITS(3);
+            }
+            while (state->have < 19)
+                state->lens[order[state->have++]] = 0;
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 7;
+            ret = inflate_table(CODES, state->lens, 19, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid code lengths set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       code lengths ok\n"));
+            state->have = 0;
+            state->mode = CODELENS;
+        case CODELENS:
+            while (state->have < state->nlen + state->ndist) {
+                for (;;) {
+                    this = state->lencode[BITS(state->lenbits)];
+                    if ((unsigned)(this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                if (this.val < 16) {
+                    NEEDBITS(this.bits);
+                    DROPBITS(this.bits);
+                    state->lens[state->have++] = this.val;
+                }
+                else {
+                    if (this.val == 16) {
+                        NEEDBITS(this.bits + 2);
+                        DROPBITS(this.bits);
+                        if (state->have == 0) {
+                            strm->msg = (char *)"invalid bit length repeat";
+                            state->mode = BAD;
+                            break;
+                        }
+                        len = state->lens[state->have - 1];
+                        copy = 3 + BITS(2);
+                        DROPBITS(2);
+                    }
+                    else if (this.val == 17) {
+                        NEEDBITS(this.bits + 3);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 3 + BITS(3);
+                        DROPBITS(3);
+                    }
+                    else {
+                        NEEDBITS(this.bits + 7);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 11 + BITS(7);
+                        DROPBITS(7);
+                    }
+                    if (state->have + copy > state->nlen + state->ndist) {
+                        strm->msg = (char *)"invalid bit length repeat";
+                        state->mode = BAD;
+                        break;
+                    }
+                    while (copy--)
+                        state->lens[state->have++] = (unsigned short)len;
+                }
+            }
+
+            /* build code tables */
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 9;
+            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid literal/lengths set";
+                state->mode = BAD;
+                break;
+            }
+            state->distcode = (code const FAR *)(state->next);
+            state->distbits = 6;
+            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+                            &(state->next), &(state->distbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid distances set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       codes ok\n"));
+            state->mode = LEN;
+        case LEN:
+            if (have >= 6 && left >= 258) {
+                RESTORE();
+                inflate_fast(strm, out);
+                LOAD();
+                break;
+            }
+            for (;;) {
+                this = state->lencode[BITS(state->lenbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if (this.op && (this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->lencode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            state->length = (unsigned)this.val;
+            if ((int)(this.op) == 0) {
+                Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+                        "inflate:         literal '%c'\n" :
+                        "inflate:         literal 0x%02x\n", this.val));
+                state->mode = LIT;
+                break;
+            }
+            if (this.op & 32) {
+                Tracevv((stderr, "inflate:         end of block\n"));
+                state->mode = TYPE;
+                break;
+            }
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid literal/length code";
+                state->mode = BAD;
+                break;
+            }
+            state->extra = (unsigned)(this.op) & 15;
+            state->mode = LENEXT;
+        case LENEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->length += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            Tracevv((stderr, "inflate:         length %u\n", state->length));
+            state->mode = DIST;
+        case DIST:
+            for (;;) {
+                this = state->distcode[BITS(state->distbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if ((this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->distcode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+            state->offset = (unsigned)this.val;
+            state->extra = (unsigned)(this.op) & 15;
+            state->mode = DISTEXT;
+        case DISTEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->offset += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            if (state->offset > state->whave + out - left) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
+            state->mode = MATCH;
+        case MATCH:
+            if (left == 0) goto inf_leave;
+            copy = out - left;
+            if (state->offset > copy) {         /* copy from window */
+                copy = state->offset - copy;
+                if (copy > state->write) {
+                    copy -= state->write;
+                    from = state->window + (state->wsize - copy);
+                }
+                else
+                    from = state->window + (state->write - copy);
+                if (copy > state->length) copy = state->length;
+            }
+            else {                              /* copy from output */
+                from = put - state->offset;
+                copy = state->length;
+            }
+            if (copy > left) copy = left;
+            left -= copy;
+            state->length -= copy;
+            do {
+                *put++ = *from++;
+            } while (--copy);
+            if (state->length == 0) state->mode = LEN;
+            break;
+        case LIT:
+            if (left == 0) goto inf_leave;
+            *put++ = (unsigned char)(state->length);
+            left--;
+            state->mode = LEN;
+            break;
+        case CHECK:
+            if (state->wrap) {
+                NEEDBITS(32);
+                out -= left;
+                strm->total_out += out;
+                state->total += out;
+                if (out)
+                    strm->adler = state->check =
+                        UPDATE(state->check, put - out, out);
+                out = left;
+                if ((
+#ifdef GUNZIP
+                     state->flags ? hold :
+#endif
+                     REVERSE(hold)) != state->check) {
+                    strm->msg = (char *)"incorrect data check";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+                Tracev((stderr, "inflate:   check matches trailer\n"));
+            }
+#ifdef GUNZIP
+            state->mode = LENGTH;
+        case LENGTH:
+            if (state->wrap && state->flags) {
+                NEEDBITS(32);
+                if (hold != (state->total & 0xffffffffUL)) {
+                    strm->msg = (char *)"incorrect length check";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+                Tracev((stderr, "inflate:   length matches trailer\n"));
+            }
+#endif
+            state->mode = DONE;
+        case DONE:
+            ret = Z_STREAM_END;
+            goto inf_leave;
+        case BAD:
+            ret = Z_DATA_ERROR;
+            goto inf_leave;
+        case MEM:
+            return Z_MEM_ERROR;
+        case SYNC:
+        default:
+            return Z_STREAM_ERROR;
+        }
+
+    /*
+       Return from inflate(), updating the total counts and the check value.
+       If there was no progress during the inflate() call, return a buffer
+       error.  Call updatewindow() to create and/or update the window state.
+       Note: a memory error from inflate() is non-recoverable.
+     */
+  inf_leave:
+    RESTORE();
+    if (state->wsize || (state->mode < CHECK && out != strm->avail_out))
+        if (updatewindow(strm, out)) {
+            state->mode = MEM;
+            return Z_MEM_ERROR;
+        }
+    in -= strm->avail_in;
+    out -= strm->avail_out;
+    strm->total_in += in;
+    strm->total_out += out;
+    state->total += out;
+    if (state->wrap && out)
+        strm->adler = state->check =
+            UPDATE(state->check, strm->next_out - out, out);
+    strm->data_type = state->bits + (state->last ? 64 : 0) +
+                      (state->mode == TYPE ? 128 : 0);
+    if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+        ret = Z_BUF_ERROR;
+    return ret;
+}
+
+int ZEXPORT inflateEnd(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->window != Z_NULL) ZFREE(strm, state->window);
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+    Tracev((stderr, "inflate: end\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+const Bytef *dictionary;
+uInt dictLength;
+{
+    struct inflate_state FAR *state;
+    unsigned long id;
+
+    /* check state */
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->mode != DICT) return Z_STREAM_ERROR;
+
+    /* check for correct dictionary id */
+    id = adler32(0L, Z_NULL, 0);
+    id = adler32(id, dictionary, dictLength);
+    if (id != state->check) return Z_DATA_ERROR;
+
+    /* copy dictionary to window */
+    if (updatewindow(strm, strm->avail_out)) {
+        state->mode = MEM;
+        return Z_MEM_ERROR;
+    }
+    if (dictLength > state->wsize) {
+        zmemcpy(state->window, dictionary + dictLength - state->wsize,
+                state->wsize);
+        state->whave = state->wsize;
+    }
+    else {
+        zmemcpy(state->window + state->wsize - dictLength, dictionary,
+                dictLength);
+        state->whave = dictLength;
+    }
+    state->havedict = 1;
+    Tracev((stderr, "inflate:   dictionary set\n"));
+    return Z_OK;
+}
+
+/*
+   Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff.  Return when found
+   or when out of input.  When called, *have is the number of pattern bytes
+   found in order so far, in 0..3.  On return *have is updated to the new
+   state.  If on return *have equals four, then the pattern was found and the
+   return value is how many bytes were read including the last byte of the
+   pattern.  If *have is less than four, then the pattern has not been found
+   yet and the return value is len.  In the latter case, syncsearch() can be
+   called again with more data and the *have state.  *have is initialized to
+   zero for the first call.
+ */
+local unsigned syncsearch(have, buf, len)
+unsigned FAR *have;
+unsigned char FAR *buf;
+unsigned len;
+{
+    unsigned got;
+    unsigned next;
+
+    got = *have;
+    next = 0;
+    while (next < len && got < 4) {
+        if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+            got++;
+        else if (buf[next])
+            got = 0;
+        else
+            got = 4 - got;
+        next++;
+    }
+    *have = got;
+    return next;
+}
+
+int ZEXPORT inflateSync(strm)
+z_streamp strm;
+{
+    unsigned len;               /* number of bytes to look at or looked at */
+    unsigned long in, out;      /* temporary to save total_in and total_out */
+    unsigned char buf[4];       /* to restore bit buffer to byte string */
+    struct inflate_state FAR *state;
+
+    /* check parameters */
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+    /* if first time, start search in bit buffer */
+    if (state->mode != SYNC) {
+        state->mode = SYNC;
+        state->hold <<= state->bits & 7;
+        state->bits -= state->bits & 7;
+        len = 0;
+        while (state->bits >= 8) {
+            buf[len++] = (unsigned char)(state->hold);
+            state->hold >>= 8;
+            state->bits -= 8;
+        }
+        state->have = 0;
+        syncsearch(&(state->have), buf, len);
+    }
+
+    /* search available input */
+    len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
+    strm->avail_in -= len;
+    strm->next_in += len;
+    strm->total_in += len;
+
+    /* return no joy or set up to restart inflate() on a new block */
+    if (state->have != 4) return Z_DATA_ERROR;
+    in = strm->total_in;  out = strm->total_out;
+    inflateReset(strm);
+    strm->total_in = in;  strm->total_out = out;
+    state->mode = TYPE;
+    return Z_OK;
+}
+
+/*
+   Returns true if inflate is currently at the end of a block generated by
+   Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+   implementation to provide an additional safety check. PPP uses
+   Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+   block. When decompressing, PPP checks that at the end of input packet,
+   inflate is waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    return state->mode == STORED && state->bits == 0;
+}
+
+int ZEXPORT inflateCopy(dest, source)
+z_streamp dest;
+z_streamp source;
+{
+    struct inflate_state FAR *state;
+    struct inflate_state FAR *copy;
+    unsigned char FAR *window;
+
+    /* check input */
+    if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL ||
+        source->zalloc == (alloc_func)0 || source->zfree == (free_func)0)
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)source->state;
+
+    /* allocate space */
+    copy = (struct inflate_state FAR *)
+           ZALLOC(source, 1, sizeof(struct inflate_state));
+    if (copy == Z_NULL) return Z_MEM_ERROR;
+    window = Z_NULL;
+    if (state->window != Z_NULL) {
+        window = (unsigned char FAR *)
+                 ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
+        if (window == Z_NULL) {
+            ZFREE(source, copy);
+            return Z_MEM_ERROR;
+        }
+    }
+
+    /* copy state */
+    *dest = *source;
+    *copy = *state;
+    copy->lencode = copy->codes + (state->lencode - state->codes);
+    copy->distcode = copy->codes + (state->distcode - state->codes);
+    copy->next = copy->codes + (state->next - state->codes);
+    if (window != Z_NULL)
+        zmemcpy(window, state->window, 1U << state->wbits);
+    copy->window = window;
+    dest->state = (voidpf)copy;
+    return Z_OK;
+}
diff --git a/zlib/inflate.h b/zlib/inflate.h
new file mode 100644 (file)
index 0000000..9a12c8f
--- /dev/null
@@ -0,0 +1,117 @@
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+   trailer decoding by inflate().  NO_GZIP would be used to avoid linking in
+   the crc code when it is not needed.  For shared libraries, gzip decoding
+   should be left enabled. */
+#ifndef NO_GZIP
+#  define GUNZIP
+#endif
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+    HEAD,       /* i: waiting for magic header */
+#ifdef GUNZIP
+    FLAGS,      /* i: waiting for method and flags (gzip) */
+    TIME,       /* i: waiting for modification time (gzip) */
+    OS,         /* i: waiting for extra flags and operating system (gzip) */
+    EXLEN,      /* i: waiting for extra length (gzip) */
+    EXTRA,      /* i: waiting for extra bytes (gzip) */
+    NAME,       /* i: waiting for end of file name (gzip) */
+    COMMENT,    /* i: waiting for end of comment (gzip) */
+    HCRC,       /* i: waiting for header crc (gzip) */
+#endif
+    DICTID,     /* i: waiting for dictionary check value */
+    DICT,       /* waiting for inflateSetDictionary() call */
+        TYPE,       /* i: waiting for type bits, including last-flag bit */
+        TYPEDO,     /* i: same, but skip check to exit inflate on new block */
+        STORED,     /* i: waiting for stored size (length and complement) */
+        COPY,       /* i/o: waiting for input or output to copy stored block */
+        TABLE,      /* i: waiting for dynamic block table lengths */
+        LENLENS,    /* i: waiting for code length code lengths */
+        CODELENS,   /* i: waiting for length/lit and distance code lengths */
+            LEN,        /* i: waiting for length/lit code */
+            LENEXT,     /* i: waiting for length extra bits */
+            DIST,       /* i: waiting for distance code */
+            DISTEXT,    /* i: waiting for distance extra bits */
+            MATCH,      /* o: waiting for output space to copy string */
+            LIT,        /* o: waiting for output space to write literal */
+    CHECK,      /* i: waiting for 32-bit check value */
+#ifdef GUNZIP
+    LENGTH,     /* i: waiting for 32-bit length (gzip) */
+#endif
+    DONE,       /* finished check, done -- remain here until reset */
+    BAD,        /* got a data error -- remain here until reset */
+    MEM,        /* got an inflate() memory error -- remain here until reset */
+    SYNC        /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+    State transitions between above modes -
+
+    (most modes can go to the BAD or MEM mode -- not shown for clarity)
+
+    Process header:
+        HEAD -> (gzip) or (zlib)
+        (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME
+        NAME -> COMMENT -> HCRC -> TYPE
+        (zlib) -> DICTID or TYPE
+        DICTID -> DICT -> TYPE
+    Read deflate blocks:
+            TYPE -> STORED or TABLE or LEN or CHECK
+            STORED -> COPY -> TYPE
+            TABLE -> LENLENS -> CODELENS -> LEN
+    Read deflate codes:
+                LEN -> LENEXT or LIT or TYPE
+                LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+                LIT -> LEN
+    Process trailer:
+        CHECK -> LENGTH -> DONE
+ */
+
+/* state maintained between inflate() calls.  Approximately 7K bytes. */
+struct inflate_state {
+    inflate_mode mode;          /* current inflate mode */
+    int last;                   /* true if processing last block */
+    int wrap;                   /* bit 0 true for zlib, bit 1 true for gzip */
+    int havedict;               /* true if dictionary provided */
+    int flags;                  /* gzip header method and flags (0 if zlib) */
+    unsigned long check;        /* protected copy of check value */
+    unsigned long total;        /* protected copy of output count */
+        /* sliding window */
+    unsigned wbits;             /* log base 2 of requested window size */
+    unsigned wsize;             /* window size or zero if not using window */
+    unsigned whave;             /* valid bytes in the window */
+    unsigned write;             /* window write index */
+    unsigned char FAR *window;  /* allocated sliding window, if needed */
+        /* bit accumulator */
+    unsigned long hold;         /* input bit accumulator */
+    unsigned bits;              /* number of bits in "in" */
+        /* for string and stored block copying */
+    unsigned length;            /* literal or length of data to copy */
+    unsigned offset;            /* distance back to copy string from */
+        /* for table and code decoding */
+    unsigned extra;             /* extra bits needed */
+        /* fixed and dynamic code tables */
+    code const FAR *lencode;    /* starting table for length/literal codes */
+    code const FAR *distcode;   /* starting table for distance codes */
+    unsigned lenbits;           /* index bits for lencode */
+    unsigned distbits;          /* index bits for distcode */
+        /* dynamic table building */
+    unsigned ncode;             /* number of code length code lengths */
+    unsigned nlen;              /* number of length code lengths */
+    unsigned ndist;             /* number of distance code lengths */
+    unsigned have;              /* number of code lengths in lens[] */
+    code FAR *next;             /* next available space in codes[] */
+    unsigned short lens[320];   /* temporary storage for code lengths */
+    unsigned short work[288];   /* work area for code table building */
+    code codes[ENOUGH];         /* space for code tables */
+};
diff --git a/zlib/inftrees.c b/zlib/inftrees.c
new file mode 100644 (file)
index 0000000..3bb5639
--- /dev/null
@@ -0,0 +1,321 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#define MAXBITS 15
+
+const char inflate_copyright[] =
+   " inflate 1.2.1 Copyright 1995-2003 Mark Adler ";
+/*
+  If you use the zlib library in a product, an acknowledgment is welcome
+  in the documentation of your product. If for some reason you cannot
+  include such an acknowledgment, I would appreciate that you keep this
+  copyright string in the executable of your product.
+ */
+
+/*
+   Build a set of tables to decode the provided canonical Huffman code.
+   The code lengths are lens[0..codes-1].  The result starts at *table,
+   whose indices are 0..2^bits-1.  work is a writable array of at least
+   lens shorts, which is used as a work area.  type is the type of code
+   to be generated, CODES, LENS, or DISTS.  On return, zero is success,
+   -1 is an invalid code, and +1 means that ENOUGH isn't enough.  table
+   on return points to the next available entry's address.  bits is the
+   requested root table index bits, and on return it is the actual root
+   table index bits.  It will differ if the request is greater than the
+   longest code or if it is less than the shortest code.
+ */
+int inflate_table(type, lens, codes, table, bits, work)
+codetype type;
+unsigned short FAR *lens;
+unsigned codes;
+code FAR * FAR *table;
+unsigned FAR *bits;
+unsigned short FAR *work;
+{
+    unsigned len;               /* a code's length in bits */
+    unsigned sym;               /* index of code symbols */
+    unsigned min, max;          /* minimum and maximum code lengths */
+    unsigned root;              /* number of index bits for root table */
+    unsigned curr;              /* number of index bits for current table */
+    unsigned drop;              /* code bits to drop for sub-table */
+    int left;                   /* number of prefix codes available */
+    unsigned used;              /* code entries in table used */
+    unsigned huff;              /* Huffman code */
+    unsigned incr;              /* for incrementing code, index */
+    unsigned fill;              /* index for replicating entries */
+    unsigned low;               /* low bits for current root entry */
+    unsigned mask;              /* mask for low root bits */
+    code this;                  /* table entry for duplication */
+    code FAR *next;             /* next available space in table */
+    const unsigned short FAR *base;     /* base value table to use */
+    const unsigned short FAR *extra;    /* extra bits table to use */
+    int end;                    /* use base and extra for symbol > end */
+    unsigned short count[MAXBITS+1];    /* number of codes of each length */
+    unsigned short offs[MAXBITS+1];     /* offsets in table for each length */
+    static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+    static const unsigned short lext[31] = { /* Length codes 257..285 extra */
+        16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+        19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 76, 66};
+    static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577, 0, 0};
+    static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
+        16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+        23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+        28, 28, 29, 29, 64, 64};
+
+    /*
+       Process a set of code lengths to create a canonical Huffman code.  The
+       code lengths are lens[0..codes-1].  Each length corresponds to the
+       symbols 0..codes-1.  The Huffman code is generated by first sorting the
+       symbols by length from short to long, and retaining the symbol order
+       for codes with equal lengths.  Then the code starts with all zero bits
+       for the first code of the shortest length, and the codes are integer
+       increments for the same length, and zeros are appended as the length
+       increases.  For the deflate format, these bits are stored backwards
+       from their more natural integer increment ordering, and so when the
+       decoding tables are built in the large loop below, the integer codes
+       are incremented backwards.
+
+       This routine assumes, but does not check, that all of the entries in
+       lens[] are in the range 0..MAXBITS.  The caller must assure this.
+       1..MAXBITS is interpreted as that code length.  zero means that that
+       symbol does not occur in this code.
+
+       The codes are sorted by computing a count of codes for each length,
+       creating from that a table of starting indices for each length in the
+       sorted table, and then entering the symbols in order in the sorted
+       table.  The sorted table is work[], with that space being provided by
+       the caller.
+
+       The length counts are used for other purposes as well, i.e. finding
+       the minimum and maximum length codes, determining if there are any
+       codes at all, checking for a valid set of lengths, and looking ahead
+       at length counts to determine sub-table sizes when building the
+       decoding tables.
+     */
+
+    /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+    for (len = 0; len <= MAXBITS; len++)
+        count[len] = 0;
+    for (sym = 0; sym < codes; sym++)
+        count[lens[sym]]++;
+
+    /* bound code lengths, force root to be within code lengths */
+    root = *bits;
+    for (max = MAXBITS; max >= 1; max--)
+        if (count[max] != 0) break;
+    if (root > max) root = max;
+    if (max == 0) return -1;            /* no codes! */
+    for (min = 1; min <= MAXBITS; min++)
+        if (count[min] != 0) break;
+    if (root < min) root = min;
+
+    /* check for an over-subscribed or incomplete set of lengths */
+    left = 1;
+    for (len = 1; len <= MAXBITS; len++) {
+        left <<= 1;
+        left -= count[len];
+        if (left < 0) return -1;        /* over-subscribed */
+    }
+    if (left > 0 && (type == CODES || (codes - count[0] != 1)))
+        return -1;                      /* incomplete set */
+
+    /* generate offsets into symbol table for each length for sorting */
+    offs[1] = 0;
+    for (len = 1; len < MAXBITS; len++)
+        offs[len + 1] = offs[len] + count[len];
+
+    /* sort symbols by length, by symbol order within each length */
+    for (sym = 0; sym < codes; sym++)
+        if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+    /*
+       Create and fill in decoding tables.  In this loop, the table being
+       filled is at next and has curr index bits.  The code being used is huff
+       with length len.  That code is converted to an index by dropping drop
+       bits off of the bottom.  For codes where len is less than drop + curr,
+       those top drop + curr - len bits are incremented through all values to
+       fill the table with replicated entries.
+
+       root is the number of index bits for the root table.  When len exceeds
+       root, sub-tables are created pointed to by the root entry with an index
+       of the low root bits of huff.  This is saved in low to check for when a
+       new sub-table should be started.  drop is zero when the root table is
+       being filled, and drop is root when sub-tables are being filled.
+
+       When a new sub-table is needed, it is necessary to look ahead in the
+       code lengths to determine what size sub-table is needed.  The length
+       counts are used for this, and so count[] is decremented as codes are
+       entered in the tables.
+
+       used keeps track of how many table entries have been allocated from the
+       provided *table space.  It is checked when a LENS table is being made
+       against the space in *table, ENOUGH, minus the maximum space needed by
+       the worst case distance code, MAXD.  This should never happen, but the
+       sufficiency of ENOUGH has not been proven exhaustively, hence the check.
+       This assumes that when type == LENS, bits == 9.
+
+       sym increments through all symbols, and the loop terminates when
+       all codes of length max, i.e. all codes, have been processed.  This
+       routine permits incomplete codes, so another loop after this one fills
+       in the rest of the decoding tables with invalid code markers.
+     */
+
+    /* set up for code type */
+    switch (type) {
+    case CODES:
+        base = extra = work;    /* dummy value--not used */
+        end = 19;
+        break;
+    case LENS:
+        base = lbase;
+        base -= 257;
+        extra = lext;
+        extra -= 257;
+        end = 256;
+        break;
+    default:            /* DISTS */
+        base = dbase;
+        extra = dext;
+        end = -1;
+    }
+
+    /* initialize state for loop */
+    huff = 0;                   /* starting code */
+    sym = 0;                    /* starting code symbol */
+    len = min;                  /* starting code length */
+    next = *table;              /* current table to fill in */
+    curr = root;                /* current table index bits */
+    drop = 0;                   /* current bits to drop from code for index */
+    low = (unsigned)(-1);       /* trigger new sub-table when len > root */
+    used = 1U << root;          /* use root table entries */
+    mask = used - 1;            /* mask for comparing low */
+
+    /* check available table space */
+    if (type == LENS && used >= ENOUGH - MAXD)
+        return 1;
+
+    /* process all codes and make table entries */
+    for (;;) {
+        /* create table entry */
+        this.bits = (unsigned char)(len - drop);
+        if ((int)(work[sym]) < end) {
+            this.op = (unsigned char)0;
+            this.val = work[sym];
+        }
+        else if ((int)(work[sym]) > end) {
+            this.op = (unsigned char)(extra[work[sym]]);
+            this.val = base[work[sym]];
+        }
+        else {
+            this.op = (unsigned char)(32 + 64);         /* end of block */
+            this.val = 0;
+        }
+
+        /* replicate for those indices with low len bits equal to huff */
+        incr = 1U << (len - drop);
+        fill = 1U << curr;
+        do {
+            fill -= incr;
+            next[(huff >> drop) + fill] = this;
+        } while (fill != 0);
+
+        /* backwards increment the len-bit code huff */
+        incr = 1U << (len - 1);
+        while (huff & incr)
+            incr >>= 1;
+        if (incr != 0) {
+            huff &= incr - 1;
+            huff += incr;
+        }
+        else
+            huff = 0;
+
+        /* go to next symbol, update count, len */
+        sym++;
+        if (--(count[len]) == 0) {
+            if (len == max) break;
+            len = lens[work[sym]];
+        }
+
+        /* create new sub-table if needed */
+        if (len > root && (huff & mask) != low) {
+            /* if first time, transition to sub-tables */
+            if (drop == 0)
+                drop = root;
+
+            /* increment past last table */
+            next += 1U << curr;
+
+            /* determine length of next table */
+            curr = len - drop;
+            left = (int)(1 << curr);
+            while (curr + drop < max) {
+                left -= count[curr + drop];
+                if (left <= 0) break;
+                curr++;
+                left <<= 1;
+            }
+
+            /* check for enough space */
+            used += 1U << curr;
+            if (type == LENS && used >= ENOUGH - MAXD)
+                return 1;
+
+            /* point entry in root table to sub-table */
+            low = huff & mask;
+            (*table)[low].op = (unsigned char)curr;
+            (*table)[low].bits = (unsigned char)root;
+            (*table)[low].val = (unsigned short)(next - *table);
+        }
+    }
+
+    /*
+       Fill in rest of table for incomplete codes.  This loop is similar to the
+       loop above in incrementing huff for table indices.  It is assumed that
+       len is equal to curr + drop, so there is no loop needed to increment
+       through high index bits.  When the current sub-table is filled, the loop
+       drops back to the root table to fill in any remaining entries there.
+     */
+    this.op = (unsigned char)64;                /* invalid code marker */
+    this.bits = (unsigned char)(len - drop);
+    this.val = (unsigned short)0;
+    while (huff != 0) {
+        /* when done with sub-table, drop back to root table */
+        if (drop != 0 && (huff & mask) != low) {
+            drop = 0;
+            len = root;
+            next = *table;
+            curr = root;
+            this.bits = (unsigned char)len;
+        }
+
+        /* put invalid code marker in table */
+        next[huff >> drop] = this;
+
+        /* backwards increment the len-bit code huff */
+        incr = 1U << (len - 1);
+        while (huff & incr)
+            incr >>= 1;
+        if (incr != 0) {
+            huff &= incr - 1;
+            huff += incr;
+        }
+        else
+            huff = 0;
+    }
+
+    /* set return parameters */
+    *table += used;
+    *bits = root;
+    return 0;
+}
diff --git a/zlib/inftrees.h b/zlib/inftrees.h
new file mode 100644 (file)
index 0000000..82d365a
--- /dev/null
@@ -0,0 +1,55 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* Structure for decoding tables.  Each entry provides either the
+   information needed to do the operation requested by the code that
+   indexed that table entry, or it provides a pointer to another
+   table that indexes more bits of the code.  op indicates whether
+   the entry is a pointer to another table, a literal, a length or
+   distance, an end-of-block, or an invalid code.  For a table
+   pointer, the low four bits of op is the number of index bits of
+   that table.  For a length or distance, the low four bits of op
+   is the number of extra bits to get after the code.  bits is
+   the number of bits in this code or part of the code to drop off
+   of the bit buffer.  val is the actual byte to output in the case
+   of a literal, the base length or distance, or the offset from
+   the current table to the next table.  Each entry is four bytes. */
+typedef struct {
+    unsigned char op;           /* operation, extra bits, table bits */
+    unsigned char bits;         /* bits in this part of the code */
+    unsigned short val;         /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+    00000000 - literal
+    0000tttt - table link, tttt != 0 is the number of table index bits
+    0001eeee - length or distance, eeee is the number of extra bits
+    01100000 - end of block
+    01000000 - invalid code
+ */
+
+/* Maximum size of dynamic tree.  The maximum found in a long but non-
+   exhaustive search was 1004 code structures (850 for length/literals
+   and 154 for distances, the latter actually the result of an
+   exhaustive search).  The true maximum is not known, but the value
+   below is more than safe. */
+#define ENOUGH 1440
+#define MAXD 154
+
+/* Type of code to build for inftable() */
+typedef enum {
+    CODES,
+    LENS,
+    DISTS
+} codetype;
+
+extern int inflate_table OF((codetype type, unsigned short FAR *lens,
+                             unsigned codes, code FAR * FAR *table,
+                             unsigned FAR *bits, unsigned short FAR *work));
diff --git a/zlib/minigzip.c b/zlib/minigzip.c
new file mode 100644 (file)
index 0000000..3816637
--- /dev/null
@@ -0,0 +1,322 @@
+/* minigzip.c -- simulate gzip using the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * minigzip is a minimal implementation of the gzip utility. This is
+ * only an example of using zlib and isn't meant to replace the
+ * full-featured gzip. No attempt is made to deal with file systems
+ * limiting names to 14 or 8+3 characters, etc... Error checking is
+ * very limited. So use minigzip only for testing; use gzip for the
+ * real thing. On MSDOS, use only on file names without extension
+ * or in pipe mode.
+ */
+
+/* @(#) $Id: minigzip.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#include <stdio.h>
+#include "zlib.h"
+
+#ifdef STDC
+#  include <string.h>
+#  include <stdlib.h>
+#else
+   extern void exit  OF((int));
+#endif
+
+#ifdef USE_MMAP
+#  include <sys/types.h>
+#  include <sys/mman.h>
+#  include <sys/stat.h>
+#endif
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+#  include <fcntl.h>
+#  include <io.h>
+#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+#  define SET_BINARY_MODE(file)
+#endif
+
+#ifdef VMS
+#  define unlink delete
+#  define GZ_SUFFIX "-gz"
+#endif
+#ifdef RISCOS
+#  define unlink remove
+#  define GZ_SUFFIX "-gz"
+#  define fileno(file) file->__file
+#endif
+#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+#  include <unix.h> /* for fileno */
+#endif
+
+#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
+  extern int unlink OF((const char *));
+#endif
+
+#ifndef GZ_SUFFIX
+#  define GZ_SUFFIX ".gz"
+#endif
+#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
+
+#define BUFLEN      16384
+#define MAX_NAME_LEN 1024
+
+#ifdef MAXSEG_64K
+#  define local static
+   /* Needed for systems with limitation on stack size. */
+#else
+#  define local
+#endif
+
+char *prog;
+
+void error            OF((const char *msg));
+void gz_compress      OF((FILE   *in, gzFile out));
+#ifdef USE_MMAP
+int  gz_compress_mmap OF((FILE   *in, gzFile out));
+#endif
+void gz_uncompress    OF((gzFile in, FILE   *out));
+void file_compress    OF((char  *file, char *mode));
+void file_uncompress  OF((char  *file));
+int  main             OF((int argc, char *argv[]));
+
+/* ===========================================================================
+ * Display error message and exit
+ */
+void error(msg)
+    const char *msg;
+{
+    fprintf(stderr, "%s: %s\n", prog, msg);
+    exit(1);
+}
+
+/* ===========================================================================
+ * Compress input to output then close both files.
+ */
+
+void gz_compress(in, out)
+    FILE   *in;
+    gzFile out;
+{
+    local char buf[BUFLEN];
+    int len;
+    int err;
+
+#ifdef USE_MMAP
+    /* Try first compressing with mmap. If mmap fails (minigzip used in a
+     * pipe), use the normal fread loop.
+     */
+    if (gz_compress_mmap(in, out) == Z_OK) return;
+#endif
+    for (;;) {
+        len = (int)fread(buf, 1, sizeof(buf), in);
+        if (ferror(in)) {
+            perror("fread");
+            exit(1);
+        }
+        if (len == 0) break;
+
+        if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
+    }
+    fclose(in);
+    if (gzclose(out) != Z_OK) error("failed gzclose");
+}
+
+#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
+
+/* Try compressing the input file at once using mmap. Return Z_OK if
+ * if success, Z_ERRNO otherwise.
+ */
+int gz_compress_mmap(in, out)
+    FILE   *in;
+    gzFile out;
+{
+    int len;
+    int err;
+    int ifd = fileno(in);
+    caddr_t buf;    /* mmap'ed buffer for the entire input file */
+    off_t buf_len;  /* length of the input file */
+    struct stat sb;
+
+    /* Determine the size of the file, needed for mmap: */
+    if (fstat(ifd, &sb) < 0) return Z_ERRNO;
+    buf_len = sb.st_size;
+    if (buf_len <= 0) return Z_ERRNO;
+
+    /* Now do the actual mmap: */
+    buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
+    if (buf == (caddr_t)(-1)) return Z_ERRNO;
+
+    /* Compress the whole file at once: */
+    len = gzwrite(out, (char *)buf, (unsigned)buf_len);
+
+    if (len != (int)buf_len) error(gzerror(out, &err));
+
+    munmap(buf, buf_len);
+    fclose(in);
+    if (gzclose(out) != Z_OK) error("failed gzclose");
+    return Z_OK;
+}
+#endif /* USE_MMAP */
+
+/* ===========================================================================
+ * Uncompress input to output then close both files.
+ */
+void gz_uncompress(in, out)
+    gzFile in;
+    FILE   *out;
+{
+    local char buf[BUFLEN];
+    int len;
+    int err;
+
+    for (;;) {
+        len = gzread(in, buf, sizeof(buf));
+        if (len < 0) error (gzerror(in, &err));
+        if (len == 0) break;
+
+        if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
+            error("failed fwrite");
+        }
+    }
+    if (fclose(out)) error("failed fclose");
+
+    if (gzclose(in) != Z_OK) error("failed gzclose");
+}
+
+
+/* ===========================================================================
+ * Compress the given file: create a corresponding .gz file and remove the
+ * original.
+ */
+void file_compress(file, mode)
+    char  *file;
+    char  *mode;
+{
+    local char outfile[MAX_NAME_LEN];
+    FILE  *in;
+    gzFile out;
+
+    strcpy(outfile, file);
+    strcat(outfile, GZ_SUFFIX);
+
+    in = fopen(file, "rb");
+    if (in == NULL) {
+        perror(file);
+        exit(1);
+    }
+    out = gzopen(outfile, mode);
+    if (out == NULL) {
+        fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
+        exit(1);
+    }
+    gz_compress(in, out);
+
+    unlink(file);
+}
+
+
+/* ===========================================================================
+ * Uncompress the given file and remove the original.
+ */
+void file_uncompress(file)
+    char  *file;
+{
+    local char buf[MAX_NAME_LEN];
+    char *infile, *outfile;
+    FILE  *out;
+    gzFile in;
+    uInt len = (uInt)strlen(file);
+
+    strcpy(buf, file);
+
+    if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
+        infile = file;
+        outfile = buf;
+        outfile[len-3] = '\0';
+    } else {
+        outfile = file;
+        infile = buf;
+        strcat(infile, GZ_SUFFIX);
+    }
+    in = gzopen(infile, "rb");
+    if (in == NULL) {
+        fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
+        exit(1);
+    }
+    out = fopen(outfile, "wb");
+    if (out == NULL) {
+        perror(file);
+        exit(1);
+    }
+
+    gz_uncompress(in, out);
+
+    unlink(infile);
+}
+
+
+/* ===========================================================================
+ * Usage:  minigzip [-d] [-f] [-h] [-r] [-1 to -9] [files...]
+ *   -d : decompress
+ *   -f : compress with Z_FILTERED
+ *   -h : compress with Z_HUFFMAN_ONLY
+ *   -r : compress with Z_RLE
+ *   -1 to -9 : compression level
+ */
+
+int main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    int uncompr = 0;
+    gzFile file;
+    char outmode[20];
+
+    strcpy(outmode, "wb6 ");
+
+    prog = argv[0];
+    argc--, argv++;
+
+    while (argc > 0) {
+      if (strcmp(*argv, "-d") == 0)
+        uncompr = 1;
+      else if (strcmp(*argv, "-f") == 0)
+        outmode[3] = 'f';
+      else if (strcmp(*argv, "-h") == 0)
+        outmode[3] = 'h';
+      else if (strcmp(*argv, "-r") == 0)
+        outmode[3] = 'R';
+      else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
+               (*argv)[2] == 0)
+        outmode[2] = (*argv)[1];
+      else
+        break;
+      argc--, argv++;
+    }
+    if (argc == 0) {
+        SET_BINARY_MODE(stdin);
+        SET_BINARY_MODE(stdout);
+        if (uncompr) {
+            file = gzdopen(fileno(stdin), "rb");
+            if (file == NULL) error("can't gzdopen stdin");
+            gz_uncompress(file, stdout);
+        } else {
+            file = gzdopen(fileno(stdout), outmode);
+            if (file == NULL) error("can't gzdopen stdout");
+            gz_compress(stdin, file);
+        }
+    } else {
+        do {
+            if (uncompr) {
+                file_uncompress(*argv);
+            } else {
+                file_compress(*argv, outmode);
+            }
+        } while (argv++, --argc);
+    }
+    return 0;
+}
diff --git a/zlib/trees.c b/zlib/trees.c
new file mode 100644 (file)
index 0000000..1ea0a2a
--- /dev/null
@@ -0,0 +1,1215 @@
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2003 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ *  ALGORITHM
+ *
+ *      The "deflation" process uses several Huffman trees. The more
+ *      common source values are represented by shorter bit sequences.
+ *
+ *      Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values).  The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ *  REFERENCES
+ *
+ *      Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ *      Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ *      Storer, James A.
+ *          Data Compression:  Methods and Theory, pp. 49-50.
+ *          Computer Science Press, 1988.  ISBN 0-7167-8156-5.
+ *
+ *      Sedgewick, R.
+ *          Algorithms, p290.
+ *          Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $Id: trees.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef DEBUG
+#  include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6      16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10    17
+/* repeat a zero length 3-10 times  (3 bits of repeat count) */
+
+#define REPZ_11_138  18
+/* repeat a zero length 11-138 times  (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+   = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+   = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+   = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+   = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN  512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+#  include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+    const ct_data *static_tree;  /* static tree or NULL */
+    const intf *extra_bits;      /* extra bits for each code or NULL */
+    int     extra_base;          /* base index for extra_bits */
+    int     elems;               /* max number of elements in the tree */
+    int     max_length;          /* max bit length for the codes */
+};
+
+local static_tree_desc  static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc  static_d_desc =
+{static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS};
+
+local static_tree_desc  static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0,   BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block     OF((deflate_state *s));
+local void pqdownheap     OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen     OF((deflate_state *s, tree_desc *desc));
+local void gen_codes      OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree     OF((deflate_state *s, tree_desc *desc));
+local void scan_tree      OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree      OF((deflate_state *s, ct_data *tree, int max_code));
+local int  build_bl_tree  OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+                              int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+                              ct_data *dtree));
+local void set_data_type  OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup      OF((deflate_state *s));
+local void bi_flush       OF((deflate_state *s));
+local void copy_block     OF((deflate_state *s, charf *buf, unsigned len,
+                              int header));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef DEBUG
+#  define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+   /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG */
+#  define send_code(s, c, tree) \
+     { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+       send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+    put_byte(s, (uch)((w) & 0xff)); \
+    put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG
+local void send_bits      OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+    deflate_state *s;
+    int value;  /* value to send */
+    int length; /* number of bits */
+{
+    Tracevv((stderr," l %2d v %4x ", length, value));
+    Assert(length > 0 && length <= 15, "invalid length");
+    s->bits_sent += (ulg)length;
+
+    /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+     * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+     * unused bits in value.
+     */
+    if (s->bi_valid > (int)Buf_size - length) {
+        s->bi_buf |= (value << s->bi_valid);
+        put_short(s, s->bi_buf);
+        s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+        s->bi_valid += length - Buf_size;
+    } else {
+        s->bi_buf |= value << s->bi_valid;
+        s->bi_valid += length;
+    }
+}
+#else /* !DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+  if (s->bi_valid > (int)Buf_size - len) {\
+    int val = value;\
+    s->bi_buf |= (val << s->bi_valid);\
+    put_short(s, s->bi_buf);\
+    s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+    s->bi_valid += len - Buf_size;\
+  } else {\
+    s->bi_buf |= (value) << s->bi_valid;\
+    s->bi_valid += len;\
+  }\
+}
+#endif /* DEBUG */
+
+
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+    static int static_init_done = 0;
+    int n;        /* iterates over tree elements */
+    int bits;     /* bit counter */
+    int length;   /* length value */
+    int code;     /* code value */
+    int dist;     /* distance index */
+    ush bl_count[MAX_BITS+1];
+    /* number of codes at each bit length for an optimal tree */
+
+    if (static_init_done) return;
+
+    /* For some embedded targets, global variables are not initialized: */
+    static_l_desc.static_tree = static_ltree;
+    static_l_desc.extra_bits = extra_lbits;
+    static_d_desc.static_tree = static_dtree;
+    static_d_desc.extra_bits = extra_dbits;
+    static_bl_desc.extra_bits = extra_blbits;
+
+    /* Initialize the mapping length (0..255) -> length code (0..28) */
+    length = 0;
+    for (code = 0; code < LENGTH_CODES-1; code++) {
+        base_length[code] = length;
+        for (n = 0; n < (1<<extra_lbits[code]); n++) {
+            _length_code[length++] = (uch)code;
+        }
+    }
+    Assert (length == 256, "tr_static_init: length != 256");
+    /* Note that the length 255 (match length 258) can be represented
+     * in two different ways: code 284 + 5 bits or code 285, so we
+     * overwrite length_code[255] to use the best encoding:
+     */
+    _length_code[length-1] = (uch)code;
+
+    /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+    dist = 0;
+    for (code = 0 ; code < 16; code++) {
+        base_dist[code] = dist;
+        for (n = 0; n < (1<<extra_dbits[code]); n++) {
+            _dist_code[dist++] = (uch)code;
+        }
+    }
+    Assert (dist == 256, "tr_static_init: dist != 256");
+    dist >>= 7; /* from now on, all distances are divided by 128 */
+    for ( ; code < D_CODES; code++) {
+        base_dist[code] = dist << 7;
+        for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+            _dist_code[256 + dist++] = (uch)code;
+        }
+    }
+    Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+    /* Construct the codes of the static literal tree */
+    for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+    n = 0;
+    while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+    while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+    while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+    while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+    /* Codes 286 and 287 do not exist, but we must include them in the
+     * tree construction to get a canonical Huffman tree (longest code
+     * all ones)
+     */
+    gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+    /* The static distance tree is trivial: */
+    for (n = 0; n < D_CODES; n++) {
+        static_dtree[n].Len = 5;
+        static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+    }
+    static_init_done = 1;
+
+#  ifdef GEN_TREES_H
+    gen_trees_header();
+#  endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+#  ifndef DEBUG
+#    include <stdio.h>
+#  endif
+
+#  define SEPARATOR(i, last, width) \
+      ((i) == (last)? "\n};\n\n" :    \
+       ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+    FILE *header = fopen("trees.h", "w");
+    int i;
+
+    Assert (header != NULL, "Can't open trees.h");
+    fprintf(header,
+            "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+    fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+    for (i = 0; i < L_CODES+2; i++) {
+        fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+                static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+    }
+
+    fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+    for (i = 0; i < D_CODES; i++) {
+        fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+                static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+    }
+
+    fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n");
+    for (i = 0; i < DIST_CODE_LEN; i++) {
+        fprintf(header, "%2u%s", _dist_code[i],
+                SEPARATOR(i, DIST_CODE_LEN-1, 20));
+    }
+
+    fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+    for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+        fprintf(header, "%2u%s", _length_code[i],
+                SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+    }
+
+    fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+    for (i = 0; i < LENGTH_CODES; i++) {
+        fprintf(header, "%1u%s", base_length[i],
+                SEPARATOR(i, LENGTH_CODES-1, 20));
+    }
+
+    fprintf(header, "local const int base_dist[D_CODES] = {\n");
+    for (i = 0; i < D_CODES; i++) {
+        fprintf(header, "%5u%s", base_dist[i],
+                SEPARATOR(i, D_CODES-1, 10));
+    }
+
+    fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void _tr_init(s)
+    deflate_state *s;
+{
+    tr_static_init();
+
+    s->l_desc.dyn_tree = s->dyn_ltree;
+    s->l_desc.stat_desc = &static_l_desc;
+
+    s->d_desc.dyn_tree = s->dyn_dtree;
+    s->d_desc.stat_desc = &static_d_desc;
+
+    s->bl_desc.dyn_tree = s->bl_tree;
+    s->bl_desc.stat_desc = &static_bl_desc;
+
+    s->bi_buf = 0;
+    s->bi_valid = 0;
+    s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG
+    s->compressed_len = 0L;
+    s->bits_sent = 0L;
+#endif
+
+    /* Initialize the first block of the first file: */
+    init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+    deflate_state *s;
+{
+    int n; /* iterates over tree elements */
+
+    /* Initialize the trees. */
+    for (n = 0; n < L_CODES;  n++) s->dyn_ltree[n].Freq = 0;
+    for (n = 0; n < D_CODES;  n++) s->dyn_dtree[n].Freq = 0;
+    for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+    s->dyn_ltree[END_BLOCK].Freq = 1;
+    s->opt_len = s->static_len = 0L;
+    s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+    top = s->heap[SMALLEST]; \
+    s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+    pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+   (tree[n].Freq < tree[m].Freq || \
+   (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+    deflate_state *s;
+    ct_data *tree;  /* the tree to restore */
+    int k;               /* node to move down */
+{
+    int v = s->heap[k];
+    int j = k << 1;  /* left son of k */
+    while (j <= s->heap_len) {
+        /* Set j to the smallest of the two sons: */
+        if (j < s->heap_len &&
+            smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+            j++;
+        }
+        /* Exit if v is smaller than both sons */
+        if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+        /* Exchange v with the smallest son */
+        s->heap[k] = s->heap[j];  k = j;
+
+        /* And continue down the tree, setting j to the left son of k */
+        j <<= 1;
+    }
+    s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ *    above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ *     array bl_count contains the frequencies for each bit length.
+ *     The length opt_len is updated; static_len is also updated if stree is
+ *     not null.
+ */
+local void gen_bitlen(s, desc)
+    deflate_state *s;
+    tree_desc *desc;    /* the tree descriptor */
+{
+    ct_data *tree        = desc->dyn_tree;
+    int max_code         = desc->max_code;
+    const ct_data *stree = desc->stat_desc->static_tree;
+    const intf *extra    = desc->stat_desc->extra_bits;
+    int base             = desc->stat_desc->extra_base;
+    int max_length       = desc->stat_desc->max_length;
+    int h;              /* heap index */
+    int n, m;           /* iterate over the tree elements */
+    int bits;           /* bit length */
+    int xbits;          /* extra bits */
+    ush f;              /* frequency */
+    int overflow = 0;   /* number of elements with bit length too large */
+
+    for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+    /* In a first pass, compute the optimal bit lengths (which may
+     * overflow in the case of the bit length tree).
+     */
+    tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+    for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+        n = s->heap[h];
+        bits = tree[tree[n].Dad].Len + 1;
+        if (bits > max_length) bits = max_length, overflow++;
+        tree[n].Len = (ush)bits;
+        /* We overwrite tree[n].Dad which is no longer needed */
+
+        if (n > max_code) continue; /* not a leaf node */
+
+        s->bl_count[bits]++;
+        xbits = 0;
+        if (n >= base) xbits = extra[n-base];
+        f = tree[n].Freq;
+        s->opt_len += (ulg)f * (bits + xbits);
+        if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+    }
+    if (overflow == 0) return;
+
+    Trace((stderr,"\nbit length overflow\n"));
+    /* This happens for example on obj2 and pic of the Calgary corpus */
+
+    /* Find the first bit length which could increase: */
+    do {
+        bits = max_length-1;
+        while (s->bl_count[bits] == 0) bits--;
+        s->bl_count[bits]--;      /* move one leaf down the tree */
+        s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+        s->bl_count[max_length]--;
+        /* The brother of the overflow item also moves one step up,
+         * but this does not affect bl_count[max_length]
+         */
+        overflow -= 2;
+    } while (overflow > 0);
+
+    /* Now recompute all bit lengths, scanning in increasing frequency.
+     * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+     * lengths instead of fixing only the wrong ones. This idea is taken
+     * from 'ar' written by Haruhiko Okumura.)
+     */
+    for (bits = max_length; bits != 0; bits--) {
+        n = s->bl_count[bits];
+        while (n != 0) {
+            m = s->heap[--h];
+            if (m > max_code) continue;
+            if (tree[m].Len != (unsigned) bits) {
+                Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+                s->opt_len += ((long)bits - (long)tree[m].Len)
+                              *(long)tree[m].Freq;
+                tree[m].Len = (ush)bits;
+            }
+            n--;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ *     zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+    ct_data *tree;             /* the tree to decorate */
+    int max_code;              /* largest code with non zero frequency */
+    ushf *bl_count;            /* number of codes at each bit length */
+{
+    ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+    ush code = 0;              /* running code value */
+    int bits;                  /* bit index */
+    int n;                     /* code index */
+
+    /* The distribution counts are first used to generate the code values
+     * without bit reversal.
+     */
+    for (bits = 1; bits <= MAX_BITS; bits++) {
+        next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+    }
+    /* Check that the bit counts in bl_count are consistent. The last code
+     * must be all ones.
+     */
+    Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+            "inconsistent bit counts");
+    Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+    for (n = 0;  n <= max_code; n++) {
+        int len = tree[n].Len;
+        if (len == 0) continue;
+        /* Now reverse the bits */
+        tree[n].Code = bi_reverse(next_code[len]++, len);
+
+        Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+             n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+    }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ *     and corresponding code. The length opt_len is updated; static_len is
+ *     also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+    deflate_state *s;
+    tree_desc *desc; /* the tree descriptor */
+{
+    ct_data *tree         = desc->dyn_tree;
+    const ct_data *stree  = desc->stat_desc->static_tree;
+    int elems             = desc->stat_desc->elems;
+    int n, m;          /* iterate over heap elements */
+    int max_code = -1; /* largest code with non zero frequency */
+    int node;          /* new node being created */
+
+    /* Construct the initial heap, with least frequent element in
+     * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+     * heap[0] is not used.
+     */
+    s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+    for (n = 0; n < elems; n++) {
+        if (tree[n].Freq != 0) {
+            s->heap[++(s->heap_len)] = max_code = n;
+            s->depth[n] = 0;
+        } else {
+            tree[n].Len = 0;
+        }
+    }
+
+    /* The pkzip format requires that at least one distance code exists,
+     * and that at least one bit should be sent even if there is only one
+     * possible code. So to avoid special checks later on we force at least
+     * two codes of non zero frequency.
+     */
+    while (s->heap_len < 2) {
+        node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+        tree[node].Freq = 1;
+        s->depth[node] = 0;
+        s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+        /* node is 0 or 1 so it does not have extra bits */
+    }
+    desc->max_code = max_code;
+
+    /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+     * establish sub-heaps of increasing lengths:
+     */
+    for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+    /* Construct the Huffman tree by repeatedly combining the least two
+     * frequent nodes.
+     */
+    node = elems;              /* next internal node of the tree */
+    do {
+        pqremove(s, tree, n);  /* n = node of least frequency */
+        m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+        s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+        s->heap[--(s->heap_max)] = m;
+
+        /* Create a new node father of n and m */
+        tree[node].Freq = tree[n].Freq + tree[m].Freq;
+        s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
+                                s->depth[n] : s->depth[m]) + 1);
+        tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+        if (tree == s->bl_tree) {
+            fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+                    node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+        }
+#endif
+        /* and insert the new node in the heap */
+        s->heap[SMALLEST] = node++;
+        pqdownheap(s, tree, SMALLEST);
+
+    } while (s->heap_len >= 2);
+
+    s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+    /* At this point, the fields freq and dad are set. We can now
+     * generate the bit lengths.
+     */
+    gen_bitlen(s, (tree_desc *)desc);
+
+    /* The field len is now set, we can generate the bit codes */
+    gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+    deflate_state *s;
+    ct_data *tree;   /* the tree to be scanned */
+    int max_code;    /* and its largest code of non zero frequency */
+{
+    int n;                     /* iterates over all tree elements */
+    int prevlen = -1;          /* last emitted length */
+    int curlen;                /* length of current code */
+    int nextlen = tree[0].Len; /* length of next code */
+    int count = 0;             /* repeat count of the current code */
+    int max_count = 7;         /* max repeat count */
+    int min_count = 4;         /* min repeat count */
+
+    if (nextlen == 0) max_count = 138, min_count = 3;
+    tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+    for (n = 0; n <= max_code; n++) {
+        curlen = nextlen; nextlen = tree[n+1].Len;
+        if (++count < max_count && curlen == nextlen) {
+            continue;
+        } else if (count < min_count) {
+            s->bl_tree[curlen].Freq += count;
+        } else if (curlen != 0) {
+            if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+            s->bl_tree[REP_3_6].Freq++;
+        } else if (count <= 10) {
+            s->bl_tree[REPZ_3_10].Freq++;
+        } else {
+            s->bl_tree[REPZ_11_138].Freq++;
+        }
+        count = 0; prevlen = curlen;
+        if (nextlen == 0) {
+            max_count = 138, min_count = 3;
+        } else if (curlen == nextlen) {
+            max_count = 6, min_count = 3;
+        } else {
+            max_count = 7, min_count = 4;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+    deflate_state *s;
+    ct_data *tree; /* the tree to be scanned */
+    int max_code;       /* and its largest code of non zero frequency */
+{
+    int n;                     /* iterates over all tree elements */
+    int prevlen = -1;          /* last emitted length */
+    int curlen;                /* length of current code */
+    int nextlen = tree[0].Len; /* length of next code */
+    int count = 0;             /* repeat count of the current code */
+    int max_count = 7;         /* max repeat count */
+    int min_count = 4;         /* min repeat count */
+
+    /* tree[max_code+1].Len = -1; */  /* guard already set */
+    if (nextlen == 0) max_count = 138, min_count = 3;
+
+    for (n = 0; n <= max_code; n++) {
+        curlen = nextlen; nextlen = tree[n+1].Len;
+        if (++count < max_count && curlen == nextlen) {
+            continue;
+        } else if (count < min_count) {
+            do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+        } else if (curlen != 0) {
+            if (curlen != prevlen) {
+                send_code(s, curlen, s->bl_tree); count--;
+            }
+            Assert(count >= 3 && count <= 6, " 3_6?");
+            send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+        } else if (count <= 10) {
+            send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+        } else {
+            send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+        }
+        count = 0; prevlen = curlen;
+        if (nextlen == 0) {
+            max_count = 138, min_count = 3;
+        } else if (curlen == nextlen) {
+            max_count = 6, min_count = 3;
+        } else {
+            max_count = 7, min_count = 4;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+    deflate_state *s;
+{
+    int max_blindex;  /* index of last bit length code of non zero freq */
+
+    /* Determine the bit length frequencies for literal and distance trees */
+    scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+    scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+    /* Build the bit length tree: */
+    build_tree(s, (tree_desc *)(&(s->bl_desc)));
+    /* opt_len now includes the length of the tree representations, except
+     * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+     */
+
+    /* Determine the number of bit length codes to send. The pkzip format
+     * requires that at least 4 bit length codes be sent. (appnote.txt says
+     * 3 but the actual value used is 4.)
+     */
+    for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+        if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+    }
+    /* Update opt_len to include the bit length tree and counts */
+    s->opt_len += 3*(max_blindex+1) + 5+5+4;
+    Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+            s->opt_len, s->static_len));
+
+    return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+    deflate_state *s;
+    int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+    int rank;                    /* index in bl_order */
+
+    Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+    Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+            "too many codes");
+    Tracev((stderr, "\nbl counts: "));
+    send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+    send_bits(s, dcodes-1,   5);
+    send_bits(s, blcodes-4,  4); /* not -3 as stated in appnote.txt */
+    for (rank = 0; rank < blcodes; rank++) {
+        Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+        send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+    }
+    Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+    send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+    Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+    send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+    Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void _tr_stored_block(s, buf, stored_len, eof)
+    deflate_state *s;
+    charf *buf;       /* input block */
+    ulg stored_len;   /* length of input block */
+    int eof;          /* true if this is the last block for a file */
+{
+    send_bits(s, (STORED_BLOCK<<1)+eof, 3);  /* send block type */
+#ifdef DEBUG
+    s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+    s->compressed_len += (stored_len + 4) << 3;
+#endif
+    copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void _tr_align(s)
+    deflate_state *s;
+{
+    send_bits(s, STATIC_TREES<<1, 3);
+    send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+    s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+    bi_flush(s);
+    /* Of the 10 bits for the empty block, we have already sent
+     * (10 - bi_valid) bits. The lookahead for the last real code (before
+     * the EOB of the previous block) was thus at least one plus the length
+     * of the EOB plus what we have just sent of the empty static block.
+     */
+    if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+        send_bits(s, STATIC_TREES<<1, 3);
+        send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+        s->compressed_len += 10L;
+#endif
+        bi_flush(s);
+    }
+    s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+void _tr_flush_block(s, buf, stored_len, eof)
+    deflate_state *s;
+    charf *buf;       /* input block, or NULL if too old */
+    ulg stored_len;   /* length of input block */
+    int eof;          /* true if this is the last block for a file */
+{
+    ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+    int max_blindex = 0;  /* index of last bit length code of non zero freq */
+
+    /* Build the Huffman trees unless a stored block is forced */
+    if (s->level > 0) {
+
+         /* Check if the file is ascii or binary */
+        if (s->data_type == Z_UNKNOWN) set_data_type(s);
+
+        /* Construct the literal and distance trees */
+        build_tree(s, (tree_desc *)(&(s->l_desc)));
+        Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+                s->static_len));
+
+        build_tree(s, (tree_desc *)(&(s->d_desc)));
+        Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+                s->static_len));
+        /* At this point, opt_len and static_len are the total bit lengths of
+         * the compressed block data, excluding the tree representations.
+         */
+
+        /* Build the bit length tree for the above two trees, and get the index
+         * in bl_order of the last bit length code to send.
+         */
+        max_blindex = build_bl_tree(s);
+
+        /* Determine the best encoding. Compute the block lengths in bytes. */
+        opt_lenb = (s->opt_len+3+7)>>3;
+        static_lenb = (s->static_len+3+7)>>3;
+
+        Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+                opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+                s->last_lit));
+
+        if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+    } else {
+        Assert(buf != (char*)0, "lost buf");
+        opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+    }
+
+#ifdef FORCE_STORED
+    if (buf != (char*)0) { /* force stored block */
+#else
+    if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+                       /* 4: two words for the lengths */
+#endif
+        /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+         * Otherwise we can't have processed more than WSIZE input bytes since
+         * the last block flush, because compression would have been
+         * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+         * transform a block into a stored block.
+         */
+        _tr_stored_block(s, buf, stored_len, eof);
+
+#ifdef FORCE_STATIC
+    } else if (static_lenb >= 0) { /* force static trees */
+#else
+    } else if (static_lenb == opt_lenb) {
+#endif
+        send_bits(s, (STATIC_TREES<<1)+eof, 3);
+        compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+#ifdef DEBUG
+        s->compressed_len += 3 + s->static_len;
+#endif
+    } else {
+        send_bits(s, (DYN_TREES<<1)+eof, 3);
+        send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+                       max_blindex+1);
+        compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+#ifdef DEBUG
+        s->compressed_len += 3 + s->opt_len;
+#endif
+    }
+    Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+    /* The above check is made mod 2^32, for files larger than 512 MB
+     * and uLong implemented on 32 bits.
+     */
+    init_block(s);
+
+    if (eof) {
+        bi_windup(s);
+#ifdef DEBUG
+        s->compressed_len += 7;  /* align on byte boundary */
+#endif
+    }
+    Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+           s->compressed_len-7*eof));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int _tr_tally (s, dist, lc)
+    deflate_state *s;
+    unsigned dist;  /* distance of matched string */
+    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+    s->d_buf[s->last_lit] = (ush)dist;
+    s->l_buf[s->last_lit++] = (uch)lc;
+    if (dist == 0) {
+        /* lc is the unmatched char */
+        s->dyn_ltree[lc].Freq++;
+    } else {
+        s->matches++;
+        /* Here, lc is the match length - MIN_MATCH */
+        dist--;             /* dist = match distance - 1 */
+        Assert((ush)dist < (ush)MAX_DIST(s) &&
+               (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+               (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");
+
+        s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+        s->dyn_dtree[d_code(dist)].Freq++;
+    }
+
+#ifdef TRUNCATE_BLOCK
+    /* Try to guess if it is profitable to stop the current block here */
+    if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
+        /* Compute an upper bound for the compressed length */
+        ulg out_length = (ulg)s->last_lit*8L;
+        ulg in_length = (ulg)((long)s->strstart - s->block_start);
+        int dcode;
+        for (dcode = 0; dcode < D_CODES; dcode++) {
+            out_length += (ulg)s->dyn_dtree[dcode].Freq *
+                (5L+extra_dbits[dcode]);
+        }
+        out_length >>= 3;
+        Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+               s->last_lit, in_length, out_length,
+               100L - out_length*100L/in_length));
+        if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+    }
+#endif
+    return (s->last_lit == s->lit_bufsize-1);
+    /* We avoid equality with lit_bufsize because of wraparound at 64K
+     * on 16 bit machines and because stored blocks are restricted to
+     * 64K-1 bytes.
+     */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+    deflate_state *s;
+    ct_data *ltree; /* literal tree */
+    ct_data *dtree; /* distance tree */
+{
+    unsigned dist;      /* distance of matched string */
+    int lc;             /* match length or unmatched char (if dist == 0) */
+    unsigned lx = 0;    /* running index in l_buf */
+    unsigned code;      /* the code to send */
+    int extra;          /* number of extra bits to send */
+
+    if (s->last_lit != 0) do {
+        dist = s->d_buf[lx];
+        lc = s->l_buf[lx++];
+        if (dist == 0) {
+            send_code(s, lc, ltree); /* send a literal byte */
+            Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+        } else {
+            /* Here, lc is the match length - MIN_MATCH */
+            code = _length_code[lc];
+            send_code(s, code+LITERALS+1, ltree); /* send the length code */
+            extra = extra_lbits[code];
+            if (extra != 0) {
+                lc -= base_length[code];
+                send_bits(s, lc, extra);       /* send the extra length bits */
+            }
+            dist--; /* dist is now the match distance - 1 */
+            code = d_code(dist);
+            Assert (code < D_CODES, "bad d_code");
+
+            send_code(s, code, dtree);       /* send the distance code */
+            extra = extra_dbits[code];
+            if (extra != 0) {
+                dist -= base_dist[code];
+                send_bits(s, dist, extra);   /* send the extra distance bits */
+            }
+        } /* literal or match pair ? */
+
+        /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+        Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
+               "pendingBuf overflow");
+
+    } while (lx < s->last_lit);
+
+    send_code(s, END_BLOCK, ltree);
+    s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+local void set_data_type(s)
+    deflate_state *s;
+{
+    int n = 0;
+    unsigned ascii_freq = 0;
+    unsigned bin_freq = 0;
+    while (n < 7)        bin_freq += s->dyn_ltree[n++].Freq;
+    while (n < 128)    ascii_freq += s->dyn_ltree[n++].Freq;
+    while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
+    s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII);
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+    unsigned code; /* the value to invert */
+    int len;       /* its bit length */
+{
+    register unsigned res = 0;
+    do {
+        res |= code & 1;
+        code >>= 1, res <<= 1;
+    } while (--len > 0);
+    return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+    deflate_state *s;
+{
+    if (s->bi_valid == 16) {
+        put_short(s, s->bi_buf);
+        s->bi_buf = 0;
+        s->bi_valid = 0;
+    } else if (s->bi_valid >= 8) {
+        put_byte(s, (Byte)s->bi_buf);
+        s->bi_buf >>= 8;
+        s->bi_valid -= 8;
+    }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+    deflate_state *s;
+{
+    if (s->bi_valid > 8) {
+        put_short(s, s->bi_buf);
+    } else if (s->bi_valid > 0) {
+        put_byte(s, (Byte)s->bi_buf);
+    }
+    s->bi_buf = 0;
+    s->bi_valid = 0;
+#ifdef DEBUG
+    s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+    deflate_state *s;
+    charf    *buf;    /* the input data */
+    unsigned len;     /* its length */
+    int      header;  /* true if block header must be written */
+{
+    bi_windup(s);        /* align on byte boundary */
+    s->last_eob_len = 8; /* enough lookahead for inflate */
+
+    if (header) {
+        put_short(s, (ush)len);
+        put_short(s, (ush)~len);
+#ifdef DEBUG
+        s->bits_sent += 2*16;
+#endif
+    }
+#ifdef DEBUG
+    s->bits_sent += (ulg)len<<3;
+#endif
+    while (len--) {
+        put_byte(s, *buf++);
+    }
+}
diff --git a/zlib/trees.h b/zlib/trees.h
new file mode 100644 (file)
index 0000000..72facf9
--- /dev/null
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{  8}}, {{140},{  8}}, {{ 76},{  8}}, {{204},{  8}}, {{ 44},{  8}},
+{{172},{  8}}, {{108},{  8}}, {{236},{  8}}, {{ 28},{  8}}, {{156},{  8}},
+{{ 92},{  8}}, {{220},{  8}}, {{ 60},{  8}}, {{188},{  8}}, {{124},{  8}},
+{{252},{  8}}, {{  2},{  8}}, {{130},{  8}}, {{ 66},{  8}}, {{194},{  8}},
+{{ 34},{  8}}, {{162},{  8}}, {{ 98},{  8}}, {{226},{  8}}, {{ 18},{  8}},
+{{146},{  8}}, {{ 82},{  8}}, {{210},{  8}}, {{ 50},{  8}}, {{178},{  8}},
+{{114},{  8}}, {{242},{  8}}, {{ 10},{  8}}, {{138},{  8}}, {{ 74},{  8}},
+{{202},{  8}}, {{ 42},{  8}}, {{170},{  8}}, {{106},{  8}}, {{234},{  8}},
+{{ 26},{  8}}, {{154},{  8}}, {{ 90},{  8}}, {{218},{  8}}, {{ 58},{  8}},
+{{186},{  8}}, {{122},{  8}}, {{250},{  8}}, {{  6},{  8}}, {{134},{  8}},
+{{ 70},{  8}}, {{198},{  8}}, {{ 38},{  8}}, {{166},{  8}}, {{102},{  8}},
+{{230},{  8}}, {{ 22},{  8}}, {{150},{  8}}, {{ 86},{  8}}, {{214},{  8}},
+{{ 54},{  8}}, {{182},{  8}}, {{118},{  8}}, {{246},{  8}}, {{ 14},{  8}},
+{{142},{  8}}, {{ 78},{  8}}, {{206},{  8}}, {{ 46},{  8}}, {{174},{  8}},
+{{110},{  8}}, {{238},{  8}}, {{ 30},{  8}}, {{158},{  8}}, {{ 94},{  8}},
+{{222},{  8}}, {{ 62},{  8}}, {{190},{  8}}, {{126},{  8}}, {{254},{  8}},
+{{  1},{  8}}, {{129},{  8}}, {{ 65},{  8}}, {{193},{  8}}, {{ 33},{  8}},
+{{161},{  8}}, {{ 97},{  8}}, {{225},{  8}}, {{ 17},{  8}}, {{145},{  8}},
+{{ 81},{  8}}, {{209},{  8}}, {{ 49},{  8}}, {{177},{  8}}, {{113},{  8}},
+{{241},{  8}}, {{  9},{  8}}, {{137},{  8}}, {{ 73},{  8}}, {{201},{  8}},
+{{ 41},{  8}}, {{169},{  8}}, {{105},{  8}}, {{233},{  8}}, {{ 25},{  8}},
+{{153},{  8}}, {{ 89},{  8}}, {{217},{  8}}, {{ 57},{  8}}, {{185},{  8}},
+{{121},{  8}}, {{249},{  8}}, {{  5},{  8}}, {{133},{  8}}, {{ 69},{  8}},
+{{197},{  8}}, {{ 37},{  8}}, {{165},{  8}}, {{101},{  8}}, {{229},{  8}},
+{{ 21},{  8}}, {{149},{  8}}, {{ 85},{  8}}, {{213},{  8}}, {{ 53},{  8}},
+{{181},{  8}}, {{117},{  8}}, {{245},{  8}}, {{ 13},{  8}}, {{141},{  8}},
+{{ 77},{  8}}, {{205},{  8}}, {{ 45},{  8}}, {{173},{  8}}, {{109},{  8}},
+{{237},{  8}}, {{ 29},{  8}}, {{157},{  8}}, {{ 93},{  8}}, {{221},{  8}},
+{{ 61},{  8}}, {{189},{  8}}, {{125},{  8}}, {{253},{  8}}, {{ 19},{  9}},
+{{275},{  9}}, {{147},{  9}}, {{403},{  9}}, {{ 83},{  9}}, {{339},{  9}},
+{{211},{  9}}, {{467},{  9}}, {{ 51},{  9}}, {{307},{  9}}, {{179},{  9}},
+{{435},{  9}}, {{115},{  9}}, {{371},{  9}}, {{243},{  9}}, {{499},{  9}},
+{{ 11},{  9}}, {{267},{  9}}, {{139},{  9}}, {{395},{  9}}, {{ 75},{  9}},
+{{331},{  9}}, {{203},{  9}}, {{459},{  9}}, {{ 43},{  9}}, {{299},{  9}},
+{{171},{  9}}, {{427},{  9}}, {{107},{  9}}, {{363},{  9}}, {{235},{  9}},
+{{491},{  9}}, {{ 27},{  9}}, {{283},{  9}}, {{155},{  9}}, {{411},{  9}},
+{{ 91},{  9}}, {{347},{  9}}, {{219},{  9}}, {{475},{  9}}, {{ 59},{  9}},
+{{315},{  9}}, {{187},{  9}}, {{443},{  9}}, {{123},{  9}}, {{379},{  9}},
+{{251},{  9}}, {{507},{  9}}, {{  7},{  9}}, {{263},{  9}}, {{135},{  9}},
+{{391},{  9}}, {{ 71},{  9}}, {{327},{  9}}, {{199},{  9}}, {{455},{  9}},
+{{ 39},{  9}}, {{295},{  9}}, {{167},{  9}}, {{423},{  9}}, {{103},{  9}},
+{{359},{  9}}, {{231},{  9}}, {{487},{  9}}, {{ 23},{  9}}, {{279},{  9}},
+{{151},{  9}}, {{407},{  9}}, {{ 87},{  9}}, {{343},{  9}}, {{215},{  9}},
+{{471},{  9}}, {{ 55},{  9}}, {{311},{  9}}, {{183},{  9}}, {{439},{  9}},
+{{119},{  9}}, {{375},{  9}}, {{247},{  9}}, {{503},{  9}}, {{ 15},{  9}},
+{{271},{  9}}, {{143},{  9}}, {{399},{  9}}, {{ 79},{  9}}, {{335},{  9}},
+{{207},{  9}}, {{463},{  9}}, {{ 47},{  9}}, {{303},{  9}}, {{175},{  9}},
+{{431},{  9}}, {{111},{  9}}, {{367},{  9}}, {{239},{  9}}, {{495},{  9}},
+{{ 31},{  9}}, {{287},{  9}}, {{159},{  9}}, {{415},{  9}}, {{ 95},{  9}},
+{{351},{  9}}, {{223},{  9}}, {{479},{  9}}, {{ 63},{  9}}, {{319},{  9}},
+{{191},{  9}}, {{447},{  9}}, {{127},{  9}}, {{383},{  9}}, {{255},{  9}},
+{{511},{  9}}, {{  0},{  7}}, {{ 64},{  7}}, {{ 32},{  7}}, {{ 96},{  7}},
+{{ 16},{  7}}, {{ 80},{  7}}, {{ 48},{  7}}, {{112},{  7}}, {{  8},{  7}},
+{{ 72},{  7}}, {{ 40},{  7}}, {{104},{  7}}, {{ 24},{  7}}, {{ 88},{  7}},
+{{ 56},{  7}}, {{120},{  7}}, {{  4},{  7}}, {{ 68},{  7}}, {{ 36},{  7}},
+{{100},{  7}}, {{ 20},{  7}}, {{ 84},{  7}}, {{ 52},{  7}}, {{116},{  7}},
+{{  3},{  8}}, {{131},{  8}}, {{ 67},{  8}}, {{195},{  8}}, {{ 35},{  8}},
+{{163},{  8}}, {{ 99},{  8}}, {{227},{  8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch _dist_code[DIST_CODE_LEN] = {
+ 0,  1,  2,  3,  4,  4,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8,
+ 8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0,  0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+    0,     1,     2,     3,     4,     6,     8,    12,    16,    24,
+   32,    48,    64,    96,   128,   192,   256,   384,   512,   768,
+ 1024,  1536,  2048,  3072,  4096,  6144,  8192, 12288, 16384, 24576
+};
+
diff --git a/zlib/uncompr.c b/zlib/uncompr.c
new file mode 100644 (file)
index 0000000..1ddf716
--- /dev/null
@@ -0,0 +1,61 @@
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: uncompr.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be large enough to hold the
+   entire uncompressed data. (The size of the uncompressed data must have
+   been saved previously by the compressor and transmitted to the decompressor
+   by some mechanism outside the scope of this compression library.)
+   Upon exit, destLen is the actual size of the compressed buffer.
+     This function can be used to decompress a whole file at once if the
+   input file is mmap'ed.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+{
+    z_stream stream;
+    int err;
+
+    stream.next_in = (Bytef*)source;
+    stream.avail_in = (uInt)sourceLen;
+    /* Check for source > 64K on 16-bit machine: */
+    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+    stream.next_out = dest;
+    stream.avail_out = (uInt)*destLen;
+    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+
+    err = inflateInit(&stream);
+    if (err != Z_OK) return err;
+
+    err = inflate(&stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        inflateEnd(&stream);
+        if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
+            return Z_DATA_ERROR;
+        return err;
+    }
+    *destLen = stream.total_out;
+
+    err = inflateEnd(&stream);
+    return err;
+}
diff --git a/zlib/zconf.in.h b/zlib/zconf.in.h
new file mode 100644 (file)
index 0000000..2852e31
--- /dev/null
@@ -0,0 +1,323 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: zconf.in.h,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+#  define deflateInit_  z_deflateInit_
+#  define deflate       z_deflate
+#  define deflateEnd    z_deflateEnd
+#  define inflateInit_  z_inflateInit_
+#  define inflate       z_inflate
+#  define inflateEnd    z_inflateEnd
+#  define deflateInit2_ z_deflateInit2_
+#  define deflateSetDictionary z_deflateSetDictionary
+#  define deflateCopy   z_deflateCopy
+#  define deflateReset  z_deflateReset
+#  define deflatePrime  z_deflatePrime
+#  define deflateParams z_deflateParams
+#  define deflateBound  z_deflateBound
+#  define inflateInit2_ z_inflateInit2_
+#  define inflateSetDictionary z_inflateSetDictionary
+#  define inflateSync   z_inflateSync
+#  define inflateSyncPoint z_inflateSyncPoint
+#  define inflateCopy   z_inflateCopy
+#  define inflateReset  z_inflateReset
+#  define compress      z_compress
+#  define compress2     z_compress2
+#  define compressBound z_compressBound
+#  define uncompress    z_uncompress
+#  define adler32       z_adler32
+#  define crc32         z_crc32
+#  define get_crc_table z_get_crc_table
+
+#  define Byte          z_Byte
+#  define uInt          z_uInt
+#  define uLong         z_uLong
+#  define Bytef         z_Bytef
+#  define charf         z_charf
+#  define intf          z_intf
+#  define uIntf         z_uIntf
+#  define uLongf        z_uLongf
+#  define voidpf        z_voidpf
+#  define voidp         z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+#  define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+#  define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+#  define WINDOWS
+#endif
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+#  define WIN32
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+#    ifndef SYS16BIT
+#      define SYS16BIT
+#    endif
+#  endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+#  define MAXSEG_64K
+#endif
+#ifdef MSDOS
+#  define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+#  ifndef STDC
+#    define STDC
+#  endif
+#  if __STDC_VERSION__ >= 199901L
+#    ifndef STDC99
+#      define STDC99
+#    endif
+#  endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+#  define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */
+#  define STDC
+#endif
+
+#ifndef STDC
+#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+#    define const       /* note: need a more gentle solution here */
+#  endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+#  define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            (1 << (windowBits+2)) +  (1 << (memLevel+9))
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+#  if defined(M_I86SM) || defined(M_I86MM)
+     /* MSC small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef _MSC_VER
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#  if (defined(__SMALL__) || defined(__MEDIUM__))
+     /* Turbo C small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef __BORLANDC__
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+   /* If building or using zlib as a DLL, define ZLIB_DLL.
+    * This is not mandatory, but it offers a little performance increase.
+    */
+#  ifdef ZLIB_DLL
+#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+#      ifdef ZLIB_INTERNAL
+#        define ZEXTERN extern __declspec(dllexport)
+#      else
+#        define ZEXTERN extern __declspec(dllimport)
+#      endif
+#    endif
+#  endif  /* ZLIB_DLL */
+   /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+    * define ZLIB_WINAPI.
+    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+    */
+#  ifdef ZLIB_WINAPI
+#    ifdef FAR
+#      undef FAR
+#    endif
+#    include <windows.h>
+     /* No need for _export, use ZLIB.DEF instead. */
+     /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+#    define ZEXPORT WINAPI
+#    ifdef WIN32
+#      define ZEXPORTVA WINAPIV
+#    else
+#      define ZEXPORTVA FAR CDECL
+#    endif
+#  endif
+#endif
+
+#if defined (__BEOS__)
+#  ifdef ZLIB_DLL
+#    ifdef ZLIB_INTERNAL
+#      define ZEXPORT   __declspec(dllexport)
+#      define ZEXPORTVA __declspec(dllexport)
+#    else
+#      define ZEXPORT   __declspec(dllimport)
+#      define ZEXPORTVA __declspec(dllimport)
+#    endif
+#  endif
+#endif
+
+#ifndef ZEXTERN
+#  define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+#  define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+#  define ZEXPORTVA
+#endif
+
+#ifndef FAR
+#  define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char  Byte;  /* 8 bits */
+#endif
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+#  define Bytef Byte FAR
+#else
+   typedef Byte  FAR Bytef;
+#endif
+typedef char  FAR charf;
+typedef int   FAR intf;
+typedef uInt  FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void const *voidpc;
+   typedef void FAR   *voidpf;
+   typedef void       *voidp;
+#else
+   typedef Byte const *voidpc;
+   typedef Byte FAR   *voidpf;
+   typedef Byte       *voidp;
+#endif
+
+#if 0           /* HAVE_UNISTD_H -- this line is updated by ./configure */
+#  include <sys/types.h> /* for off_t */
+#  include <unistd.h>    /* for SEEK_* and off_t */
+#  ifdef VMS
+#    include <unixio.h>   /* for off_t */
+#  endif
+#  define z_off_t  off_t
+#endif
+#ifndef SEEK_SET
+#  define SEEK_SET        0       /* Seek from beginning of file.  */
+#  define SEEK_CUR        1       /* Seek from current position.  */
+#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+#  define  z_off_t long
+#endif
+
+#if defined(__OS400__)
+#define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+#  define NO_vsnprintf
+#  ifdef FAR
+#    undef FAR
+#  endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+#   pragma map(deflateInit_,"DEIN")
+#   pragma map(deflateInit2_,"DEIN2")
+#   pragma map(deflateEnd,"DEEND")
+#   pragma map(deflateBound,"DEBND")
+#   pragma map(inflateInit_,"ININ")
+#   pragma map(inflateInit2_,"ININ2")
+#   pragma map(inflateEnd,"INEND")
+#   pragma map(inflateSync,"INSY")
+#   pragma map(inflateSetDictionary,"INSEDI")
+#   pragma map(compressBound,"CMBND")
+#   pragma map(inflate_table,"INTABL")
+#   pragma map(inflate_fast,"INFA")
+#   pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/zlib/zlib.3 b/zlib/zlib.3
new file mode 100644 (file)
index 0000000..8900984
--- /dev/null
@@ -0,0 +1,159 @@
+.TH ZLIB 3 "17 November 2003"
+.SH NAME
+zlib \- compression/decompression library
+.SH SYNOPSIS
+[see
+.I zlib.h
+for full description]
+.SH DESCRIPTION
+The
+.I zlib
+library is a general purpose data compression library.
+The code is thread safe.
+It provides in-memory compression and decompression functions,
+including integrity checks of the uncompressed data.
+This version of the library supports only one compression method (deflation)
+but other algorithms will be added later
+and will have the same stream interface.
+.LP
+Compression can be done in a single step if the buffers are large enough
+(for example if an input file is mmap'ed),
+or can be done by repeated calls of the compression function.
+In the latter case,
+the application must provide more input and/or consume the output
+(providing more output space) before each call.
+.LP
+The library also supports reading and writing files in
+.IR gzip (1)
+(.gz) format
+with an interface similar to that of stdio.
+.LP
+The library does not install any signal handler.
+The decoder checks the consistency of the compressed data,
+so the library should never crash even in case of corrupted input.
+.LP
+All functions of the compression library are documented in the file
+.IR zlib.h .
+The distribution source includes examples of use of the library
+in the files
+.I example.c
+and
+.IR minigzip.c .
+.LP
+Changes to this version are documented in the file
+.I ChangeLog
+that accompanies the source,
+and are concerned primarily with bug fixes and portability enhancements.
+.LP
+A Java implementation of
+.I zlib
+is available in the Java Development Kit 1.1:
+.IP
+http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html
+.LP
+A Perl interface to
+.IR zlib ,
+written by Paul Marquess (pmqs@cpan.org),
+is available at CPAN (Comprehensive Perl Archive Network) sites,
+including:
+.IP
+http://www.cpan.org/modules/by-module/Compress/
+.LP
+A Python interface to
+.IR zlib ,
+written by A.M. Kuchling (amk@magnet.com),
+is available in Python 1.5 and later versions:
+.IP
+http://www.python.org/doc/lib/module-zlib.html
+.LP
+A
+.I zlib
+binding for
+.IR tcl (1),
+written by Andreas Kupries (a.kupries@westend.com),
+is availlable at:
+.IP
+http://www.westend.com/~kupries/doc/trf/man/man.html
+.LP
+An experimental package to read and write files in .zip format,
+written on top of
+.I zlib
+by Gilles Vollant (info@winimage.com),
+is available at:
+.IP
+http://www.winimage.com/zLibDll/unzip.html
+and also in the
+.I contrib/minizip
+directory of the main
+.I zlib
+web site.
+.SH "SEE ALSO"
+The
+.I zlib
+web site can be found at either of these locations:
+.IP
+http://www.zlib.org
+.br
+http://www.gzip.org/zlib/
+.LP
+The data format used by the zlib library is described by RFC
+(Request for Comments) 1950 to 1952 in the files:
+.IP
+http://www.ietf.org/rfc/rfc1950.txt (concerning zlib format)
+.br
+http://www.ietf.org/rfc/rfc1951.txt (concerning deflate format)
+.br
+http://www.ietf.org/rfc/rfc1952.txt (concerning gzip format)
+.LP
+These documents are also available in other formats from:
+.IP
+ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
+.LP
+Mark Nelson (markn@ieee.org) wrote an article about
+.I zlib
+for the Jan. 1997 issue of  Dr. Dobb's Journal;
+a copy of the article is available at:
+.IP
+http://dogma.net/markn/articles/zlibtool/zlibtool.htm
+.SH "REPORTING PROBLEMS"
+Before reporting a problem,
+please check the
+.I zlib
+web site to verify that you have the latest version of
+.IR zlib ;
+otherwise,
+obtain the latest version and see if the problem still exists.
+Please read the
+.I zlib
+FAQ at:
+.IP
+http://www.gzip.org/zlib/zlib_faq.html
+.LP
+before asking for help.
+Send questions and/or comments to zlib@gzip.org,
+or (for the Windows DLL version) to Gilles Vollant (info@winimage.com).
+.SH AUTHORS
+Version 1.2.1
+Copyright (C) 1995-2003 Jean-loup Gailly (jloup@gzip.org)
+and Mark Adler (madler@alumni.caltech.edu).
+.LP
+This software is provided "as-is,"
+without any express or implied warranty.
+In no event will the authors be held liable for any damages
+arising from the use of this software.
+See the distribution directory with respect to requirements
+governing redistribution.
+The deflate format used by
+.I zlib
+was defined by Phil Katz.
+The deflate and
+.I zlib
+specifications were written by L. Peter Deutsch.
+Thanks to all the people who reported problems and suggested various
+improvements in
+.IR zlib ;
+who are too numerous to cite here.
+.LP
+UNIX manual page by R. P. C. Rodgers,
+U.S. National Library of Medicine (rodgers@nlm.nih.gov).
+.\" end of man page
diff --git a/zlib/zlib.h b/zlib/zlib.h
new file mode 100644 (file)
index 0000000..92edf96
--- /dev/null
@@ -0,0 +1,1200 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.2.1, November 17th, 2003
+
+  Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+
+  The data format used by the zlib library is described by RFCs (Request for
+  Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
+  (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.1"
+#define ZLIB_VERNUM 0x1210
+
+/*
+     The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed
+  data.  This version of the library supports only one compression method
+  (deflation) but other algorithms will be added later and will have the same
+  stream interface.
+
+     Compression can be done in a single step if the buffers are large
+  enough (for example if an input file is mmap'ed), or can be done by
+  repeated calls of the compression function.  In the latter case, the
+  application must provide more input and/or consume the output
+  (providing more output space) before each call.
+
+     The compressed data format used by the in-memory functions is the zlib
+  format, which is a zlib wrapper documented in RFC 1950, wrapped around a
+  deflate stream, which is itself documented in RFC 1951.
+
+     The library also supports reading and writing files in gzip (.gz) format
+  with an interface similar to that of stdio using the functions that start
+  with "gz".  The gzip format is different from the zlib format.  gzip is a
+  gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+     The zlib format was designed to be compact and fast for use in memory
+  and on communications channels.  The gzip format was designed for single-
+  file compression on file systems, has a larger header than zlib to maintain
+  directory information, and uses a different, slower check method than zlib.
+
+     This library does not provide any functions to write gzip files in memory.
+  However such functions could be easily written using zlib's deflate function,
+  the documentation in the gzip RFC, and the examples in gzio.c.
+
+     The library does not install any signal handler. The decoder checks
+  the consistency of the compressed data, so the library should never
+  crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void   (*free_func)  OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+    Bytef    *next_in;  /* next input byte */
+    uInt     avail_in;  /* number of bytes available at next_in */
+    uLong    total_in;  /* total nb of input bytes read so far */
+
+    Bytef    *next_out; /* next output byte should be put there */
+    uInt     avail_out; /* remaining free space at next_out */
+    uLong    total_out; /* total nb of bytes output so far */
+
+    char     *msg;      /* last error message, NULL if no error */
+    struct internal_state FAR *state; /* not visible by applications */
+
+    alloc_func zalloc;  /* used to allocate the internal state */
+    free_func  zfree;   /* used to free the internal state */
+    voidpf     opaque;  /* private data object passed to zalloc and zfree */
+
+    int     data_type;  /* best guess about the data type: ascii or binary */
+    uLong   adler;      /* adler32 value of the uncompressed data */
+    uLong   reserved;   /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+   The application must update next_in and avail_in when avail_in has
+   dropped to zero. It must update next_out and avail_out when avail_out
+   has dropped to zero. The application must initialize zalloc, zfree and
+   opaque before calling the init function. All other fields are set by the
+   compression library and must not be updated by the application.
+
+   The opaque value provided by the application will be passed as the first
+   parameter for calls of zalloc and zfree. This can be useful for custom
+   memory management. The compression library attaches no meaning to the
+   opaque value.
+
+   zalloc must return Z_NULL if there is not enough memory for the object.
+   If zlib is used in a multi-threaded application, zalloc and zfree must be
+   thread safe.
+
+   On 16-bit systems, the functions zalloc and zfree must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this
+   if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+   pointers returned by zalloc for objects of exactly 65536 bytes *must*
+   have their offset normalized to zero. The default allocation function
+   provided by this library ensures this (see zutil.c). To reduce memory
+   requirements and avoid any allocation of 64K objects, at the expense of
+   compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+   The fields total_in and total_out can be used for statistics or
+   progress reports. After compression, total_in holds the total size of
+   the uncompressed data and may be saved for use in the decompressor
+   (particularly if the decompressor wants to decompress everything in
+   a single step).
+*/
+
+                        /* constants */
+
+#define Z_NO_FLUSH      0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH    2
+#define Z_FULL_FLUSH    3
+#define Z_FINISH        4
+#define Z_BLOCK         5
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK            0
+#define Z_STREAM_END    1
+#define Z_NEED_DICT     2
+#define Z_ERRNO        (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR   (-3)
+#define Z_MEM_ERROR    (-4)
+#define Z_BUF_ERROR    (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION         0
+#define Z_BEST_SPEED             1
+#define Z_BEST_COMPRESSION       9
+#define Z_DEFAULT_COMPRESSION  (-1)
+/* compression levels */
+
+#define Z_FILTERED            1
+#define Z_HUFFMAN_ONLY        2
+#define Z_RLE                 3
+#define Z_DEFAULT_STRATEGY    0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY   0
+#define Z_ASCII    1
+#define Z_UNKNOWN  2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED   8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+                        /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is
+   not compatible with the zlib.h header file used by the application.
+   This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+     Initializes the internal stream state for compression. The fields
+   zalloc, zfree and opaque must be initialized before by the caller.
+   If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+   use default allocation functions.
+
+     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+   1 gives best speed, 9 gives best compression, 0 gives no compression at
+   all (the input data is simply copied a block at a time).
+   Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+   compression (currently equivalent to level 6).
+
+     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+   with the version assumed by the caller (ZLIB_VERSION).
+   msg is set to null if there is no error message.  deflateInit does not
+   perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+    deflate compresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may introduce some
+  output latency (reading input without producing any output) except when
+  forced to flush.
+
+    The detailed semantics are as follows. deflate performs one or both of the
+  following actions:
+
+  - Compress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in and avail_in are updated and
+    processing will resume at this point for the next call of deflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly. This action is forced if the parameter flush is non zero.
+    Forcing flush frequently degrades the compression ratio, so this parameter
+    should be set only when necessary (in interactive applications).
+    Some output may be provided even if flush is not set.
+
+  Before the call of deflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating avail_in or avail_out accordingly; avail_out
+  should never be zero before the call. The application can consume the
+  compressed output when it wants, for example when the output buffer is full
+  (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+  and with zero avail_out, it must be called again after making room in the
+  output buffer because there might be more output pending.
+
+    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+  flushed to the output buffer and the output is aligned on a byte boundary, so
+  that the decompressor can get all input data available so far. (In particular
+  avail_in is zero after the call if enough output space has been provided
+  before the call.)  Flushing may degrade compression for some compression
+  algorithms and so it should be used only when necessary.
+
+    If flush is set to Z_FULL_FLUSH, all output is flushed as with
+  Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+  restart from this point if previous compressed data has been damaged or if
+  random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+  the compression.
+
+    If deflate returns with avail_out == 0, this function must be called again
+  with the same value of the flush parameter and more output space (updated
+  avail_out), until the flush is complete (deflate returns with non-zero
+  avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+  avail_out is greater than six to avoid repeated flush markers due to
+  avail_out == 0 on return.
+
+    If the parameter flush is set to Z_FINISH, pending input is processed,
+  pending output is flushed and deflate returns with Z_STREAM_END if there
+  was enough output space; if deflate returns with Z_OK, this function must be
+  called again with Z_FINISH and more output space (updated avail_out) but no
+  more input data, until it returns with Z_STREAM_END or an error. After
+  deflate has returned Z_STREAM_END, the only possible operations on the
+  stream are deflateReset or deflateEnd.
+
+    Z_FINISH can be used immediately after deflateInit if all the compression
+  is to be done in a single step. In this case, avail_out must be at least
+  the value returned by deflateBound (see below). If deflate does not return
+  Z_STREAM_END, then it must be called again as described above.
+
+    deflate() sets strm->adler to the adler32 checksum of all input read
+  so far (that is, total_in bytes).
+
+    deflate() may update data_type if it can make a good guess about
+  the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+  binary. This field is only for information purposes and does not affect
+  the compression algorithm in any manner.
+
+    deflate() returns Z_OK if some progress has been made (more input
+  processed or more output produced), Z_STREAM_END if all input has been
+  consumed and all output has been produced (only when flush is set to
+  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+  if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+  (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
+  fatal, and deflate() can be called again with more input and more output
+  space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+   stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+   prematurely (some input or output was discarded). In the error case,
+   msg may be set but then points to a static string (which must not be
+   deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+     Initializes the internal stream state for decompression. The fields
+   next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+   the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+   value depends on the compression method), inflateInit determines the
+   compression method from the zlib header and allocates all data structures
+   accordingly; otherwise the allocation will be deferred to the first call of
+   inflate.  If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+   use default allocation functions.
+
+     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller.  msg is set to null if there is no error
+   message. inflateInit does not perform any decompression apart from reading
+   the zlib header if present: this will be done by inflate().  (So next_in and
+   avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+    inflate decompresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may introduce
+  some output latency (reading input without producing any output) except when
+  forced to flush.
+
+  The detailed semantics are as follows. inflate performs one or both of the
+  following actions:
+
+  - Decompress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in is updated and processing
+    will resume at this point for the next call of inflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly.  inflate() provides as much output as possible, until there
+    is no more input data or no more space in the output buffer (see below
+    about the flush parameter).
+
+  Before the call of inflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating the next_* and avail_* values accordingly.
+  The application can consume the uncompressed output when it wants, for
+  example when the output buffer is full (avail_out == 0), or after each
+  call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+  must be called again after making room in the output buffer because there
+  might be more output pending.
+
+    The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH,
+  Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much
+  output as possible to the output buffer. Z_BLOCK requests that inflate() stop
+  if and when it get to the next deflate block boundary. When decoding the zlib
+  or gzip format, this will cause inflate() to return immediately after the
+  header and before the first block. When doing a raw inflate, inflate() will
+  go ahead and process the first block, and will return when it gets to the end
+  of that block, or when it runs out of data.
+
+    The Z_BLOCK option assists in appending to or combining deflate streams.
+  Also to assist in this, on return inflate() will set strm->data_type to the
+  number of unused bits in the last byte taken from strm->next_in, plus 64
+  if inflate() is currently decoding the last block in the deflate stream,
+  plus 128 if inflate() returned immediately after decoding an end-of-block
+  code or decoding the complete header up to just before the first byte of the
+  deflate stream. The end-of-block will not be indicated until all of the
+  uncompressed data from that block has been written to strm->next_out.  The
+  number of unused bits may in general be greater than seven, except when
+  bit 7 of data_type is set, in which case the number of unused bits will be
+  less than eight.
+
+    inflate() should normally be called until it returns Z_STREAM_END or an
+  error. However if all decompression is to be performed in a single step
+  (a single call of inflate), the parameter flush should be set to
+  Z_FINISH. In this case all pending input is processed and all pending
+  output is flushed; avail_out must be large enough to hold all the
+  uncompressed data. (The size of the uncompressed data may have been saved
+  by the compressor for this purpose.) The next operation on this stream must
+  be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+  is never required, but can be used to inform inflate that a faster approach
+  may be used for the single inflate() call.
+
+     In this implementation, inflate() always flushes as much output as
+  possible to the output buffer, and always uses the faster approach on the
+  first call. So the only effect of the flush parameter in this implementation
+  is on the return value of inflate(), as noted below, or when it returns early
+  because Z_BLOCK is used.
+
+     If a preset dictionary is needed after this call (see inflateSetDictionary
+  below), inflate sets strm-adler to the adler32 checksum of the dictionary
+  chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+  strm->adler to the adler32 checksum of all output produced so far (that is,
+  total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+  below. At the end of the stream, inflate() checks that its computed adler32
+  checksum is equal to that saved by the compressor and returns Z_STREAM_END
+  only if the checksum is correct.
+
+    inflate() will decompress and check either zlib-wrapped or gzip-wrapped
+  deflate data.  The header type is detected automatically.  Any information
+  contained in the gzip header is not retained, so applications that need that
+  information should instead use raw inflate, see inflateInit2() below, or
+  inflateBack() and perform their own processing of the gzip header and
+  trailer.
+
+    inflate() returns Z_OK if some progress has been made (more input processed
+  or more output produced), Z_STREAM_END if the end of the compressed data has
+  been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+  corrupted (input stream not conforming to the zlib format or incorrect check
+  value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+  if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+  Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+  output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+  inflate() can be called again with more input and more output space to
+  continue decompressing. If Z_DATA_ERROR is returned, the application may then
+  call inflateSync() to look for a good compression block if a partial recovery
+  of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+   was inconsistent. In the error case, msg may be set but then points to a
+   static string (which must not be deallocated).
+*/
+
+                        /* Advanced functions */
+
+/*
+    The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+                                     int  level,
+                                     int  method,
+                                     int  windowBits,
+                                     int  memLevel,
+                                     int  strategy));
+
+     This is another version of deflateInit with more compression options. The
+   fields next_in, zalloc, zfree and opaque must be initialized before by
+   the caller.
+
+     The method parameter is the compression method. It must be Z_DEFLATED in
+   this version of the library.
+
+     The windowBits parameter is the base two logarithm of the window size
+   (the size of the history buffer). It should be in the range 8..15 for this
+   version of the library. Larger values of this parameter result in better
+   compression at the expense of memory usage. The default value is 15 if
+   deflateInit is used instead.
+
+     windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
+   determines the window size. deflate() will then generate raw deflate data
+   with no zlib header or trailer, and will not compute an adler32 check value.
+
+     windowBits can also be greater than 15 for optional gzip encoding. Add
+   16 to windowBits to write a simple gzip header and trailer around the
+   compressed data instead of a zlib wrapper. The gzip header will have no
+   file name, no extra data, no comment, no modification time (set to zero),
+   no header crc, and the operating system will be set to 255 (unknown).
+
+     The memLevel parameter specifies how much memory should be allocated
+   for the internal compression state. memLevel=1 uses minimum memory but
+   is slow and reduces compression ratio; memLevel=9 uses maximum memory
+   for optimal speed. The default value is 8. See zconf.h for total memory
+   usage as a function of windowBits and memLevel.
+
+     The strategy parameter is used to tune the compression algorithm. Use the
+   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+   string match), or Z_RLE to limit match distances to one (run-length
+   encoding). Filtered data consists mostly of small values with a somewhat
+   random distribution. In this case, the compression algorithm is tuned to
+   compress them better. The effect of Z_FILTERED is to force more Huffman
+   coding and less string matching; it is somewhat intermediate between
+   Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as
+   Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy
+   parameter only affects the compression ratio but not the correctness of the
+   compressed output even if it is not set appropriately.
+
+      deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+   method). msg is set to null if there is no error message.  deflateInit2 does
+   not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the compression dictionary from the given byte sequence
+   without producing any compressed output. This function must be called
+   immediately after deflateInit, deflateInit2 or deflateReset, before any
+   call of deflate. The compressor and decompressor must use exactly the same
+   dictionary (see inflateSetDictionary).
+
+     The dictionary should consist of strings (byte sequences) that are likely
+   to be encountered later in the data to be compressed, with the most commonly
+   used strings preferably put towards the end of the dictionary. Using a
+   dictionary is most useful when the data to be compressed is short and can be
+   predicted with good accuracy; the data can then be compressed better than
+   with the default empty dictionary.
+
+     Depending on the size of the compression data structures selected by
+   deflateInit or deflateInit2, a part of the dictionary may in effect be
+   discarded, for example if the dictionary is larger than the window size in
+   deflate or deflate2. Thus the strings most likely to be useful should be
+   put at the end of the dictionary, not at the front.
+
+     Upon return of this function, strm->adler is set to the adler32 value
+   of the dictionary; the decompressor may later use this value to determine
+   which dictionary has been used by the compressor. (The adler32 value
+   applies to the whole dictionary even if only a subset of the dictionary is
+   actually used by the compressor.) If a raw deflate was requested, then the
+   adler32 value is not computed and strm->adler is not set.
+
+     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+   parameter is invalid (such as NULL dictionary) or the stream state is
+   inconsistent (for example if deflate has already been called for this stream
+   or if the compression method is bsort). deflateSetDictionary does not
+   perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when several compression strategies will be
+   tried, for example when there are several ways of pre-processing the input
+   data with a filter. The streams that will be discarded should then be freed
+   by calling deflateEnd.  Note that deflateCopy duplicates the internal
+   compression state which can be quite large, so this strategy is slow and
+   can consume lots of memory.
+
+     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being NULL). msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to deflateEnd followed by deflateInit,
+   but does not free and reallocate all the internal compression state.
+   The stream will keep the same compression level and any other attributes
+   that may have been set by deflateInit2.
+
+      deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+                                      int level,
+                                      int strategy));
+/*
+     Dynamically update the compression level and compression strategy.  The
+   interpretation of level and strategy is as in deflateInit2.  This can be
+   used to switch between compression and straight copy of the input data, or
+   to switch to a different kind of input data requiring a different
+   strategy. If the compression level is changed, the input available so far
+   is compressed with the old level (and may be flushed); the new level will
+   take effect only at the next call of deflate().
+
+     Before the call of deflateParams, the stream state must be set as for
+   a call of deflate(), since the currently available input may have to
+   be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+     deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+   stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+   if strm->avail_out was zero.
+*/
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+                                       uLong sourceLen));
+/*
+     deflateBound() returns an upper bound on the compressed size after
+   deflation of sourceLen bytes.  It must be called after deflateInit()
+   or deflateInit2().  This would be used to allocate an output buffer
+   for deflation in a single pass, and so would be called before deflate().
+*/
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+                                     int bits,
+                                     int value));
+/*
+     deflatePrime() inserts bits in the deflate output stream.  The intent
+  is that this function is used to start off the deflate output with the
+  bits leftover from a previous deflate stream when appending to it.  As such,
+  this function can only be used for raw deflate, and must be used before the
+  first deflate() call after a deflateInit2() or deflateReset().  bits must be
+  less than or equal to 16, and that many of the least significant bits of
+  value will be inserted in the output.
+
+      deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+                                     int  windowBits));
+
+     This is another version of inflateInit with an extra parameter. The
+   fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+   before by the caller.
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library. The default value is 15 if inflateInit is used
+   instead. windowBits must be greater than or equal to the windowBits value
+   provided to deflateInit2() while compressing, or it must be equal to 15 if
+   deflateInit2() was not used. If a compressed stream with a larger window
+   size is given as input, inflate() will return with the error code
+   Z_DATA_ERROR instead of trying to allocate a larger window.
+
+     windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+   determines the window size. inflate() will then process raw deflate data,
+   not looking for a zlib or gzip header, not generating a check value, and not
+   looking for any check values for comparison at the end of the stream. This
+   is for use with other formats that use the deflate compressed data format
+   such as zip.  Those formats provide their own check values. If a custom
+   format is developed using the raw deflate format for compressed data, it is
+   recommended that a check value such as an adler32 or a crc32 be applied to
+   the uncompressed data as is done in the zlib, gzip, and zip formats.  For
+   most applications, the zlib format should be used as is. Note that comments
+   above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+     windowBits can also be greater than 15 for optional gzip decoding. Add
+   32 to windowBits to enable zlib and gzip decoding with automatic header
+   detection, or add 16 to decode only the gzip format (the zlib format will
+   return a Z_DATA_ERROR).
+
+     inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
+   memLevel). msg is set to null if there is no error message.  inflateInit2
+   does not perform any decompression apart from reading the zlib header if
+   present: this will be done by inflate(). (So next_in and avail_in may be
+   modified, but next_out and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the decompression dictionary from the given uncompressed byte
+   sequence. This function must be called immediately after a call of inflate
+   if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
+   can be determined from the adler32 value returned by this call of
+   inflate. The compressor and decompressor must use exactly the same
+   dictionary (see deflateSetDictionary).
+
+     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+   parameter is invalid (such as NULL dictionary) or the stream state is
+   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+   expected one (incorrect adler32 value). inflateSetDictionary does not
+   perform any decompression: this will be done by subsequent calls of
+   inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+    Skips invalid compressed data until a full flush point (see above the
+  description of deflate with Z_FULL_FLUSH) can be found, or until all
+  available input is skipped. No output is provided.
+
+    inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+  if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+  or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+  case, the application may save the current current value of total_in which
+  indicates where valid compressed data was found. In the error case, the
+  application may repeatedly call inflateSync, providing more input each time,
+  until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when randomly accessing a large stream.  The
+   first pass through the stream can periodically record the inflate state,
+   allowing restarting inflate at those points when randomly accessing the
+   stream.
+
+     inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being NULL). msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to inflateEnd followed by inflateInit,
+   but does not free and reallocate all the internal decompression state.
+   The stream will keep attributes that may have been set by inflateInit2.
+
+      inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_stream FAR *strm, int windowBits,
+                                        unsigned char FAR *window));
+
+     Initialize the internal stream state for decompression using inflateBack()
+   calls.  The fields zalloc, zfree and opaque in strm must be initialized
+   before the call.  If zalloc and zfree are Z_NULL, then the default library-
+   derived memory allocation routines are used.  windowBits is the base two
+   logarithm of the window size, in the range 8..15.  window is a caller
+   supplied buffer of that size.  Except for special applications where it is
+   assured that deflate was used with small window sizes, windowBits must be 15
+   and a 32K byte window must be supplied to be able to decompress general
+   deflate streams.
+
+     See inflateBack() for the usage of these routines.
+
+     inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+   the paramaters are invalid, Z_MEM_ERROR if the internal state could not
+   be allocated, or Z_VERSION_ERROR if the version of the library does not
+   match the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_stream FAR *strm,
+                                    in_func in, void FAR *in_desc,
+                                    out_func out, void FAR *out_desc));
+/*
+     inflateBack() does a raw inflate with a single call using a call-back
+   interface for input and output.  This is more efficient than inflate() for
+   file i/o applications in that it avoids copying between the output and the
+   sliding window by simply making the window itself the output buffer.  This
+   function trusts the application to not change the output buffer passed by
+   the output function, at least until inflateBack() returns.
+
+     inflateBackInit() must be called first to allocate the internal state
+   and to initialize the state with the user-provided window buffer.
+   inflateBack() may then be used multiple times to inflate a complete, raw
+   deflate stream with each call.  inflateBackEnd() is then called to free
+   the allocated state.
+
+     A raw deflate stream is one with no zlib or gzip header or trailer.
+   This routine would normally be used in a utility that reads zip or gzip
+   files and writes out uncompressed files.  The utility would decode the
+   header and process the trailer on its own, hence this routine expects
+   only the raw deflate stream to decompress.  This is different from the
+   normal behavior of inflate(), which expects either a zlib or gzip header and
+   trailer around the deflate stream.
+
+     inflateBack() uses two subroutines supplied by the caller that are then
+   called by inflateBack() for input and output.  inflateBack() calls those
+   routines until it reads a complete deflate stream and writes out all of the
+   uncompressed data, or until it encounters an error.  The function's
+   parameters and return types are defined above in the in_func and out_func
+   typedefs.  inflateBack() will call in(in_desc, &buf) which should return the
+   number of bytes of provided input, and a pointer to that input in buf.  If
+   there is no input available, in() must return zero--buf is ignored in that
+   case--and inflateBack() will return a buffer error.  inflateBack() will call
+   out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].  out()
+   should return zero on success, or non-zero on failure.  If out() returns
+   non-zero, inflateBack() will return with an error.  Neither in() nor out()
+   are permitted to change the contents of the window provided to
+   inflateBackInit(), which is also the buffer that out() uses to write from.
+   The length written by out() will be at most the window size.  Any non-zero
+   amount of input may be provided by in().
+
+     For convenience, inflateBack() can be provided input on the first call by
+   setting strm->next_in and strm->avail_in.  If that input is exhausted, then
+   in() will be called.  Therefore strm->next_in must be initialized before
+   calling inflateBack().  If strm->next_in is Z_NULL, then in() will be called
+   immediately for input.  If strm->next_in is not Z_NULL, then strm->avail_in
+   must also be initialized, and then if strm->avail_in is not zero, input will
+   initially be taken from strm->next_in[0 .. strm->avail_in - 1].
+
+     The in_desc and out_desc parameters of inflateBack() is passed as the
+   first parameter of in() and out() respectively when they are called.  These
+   descriptors can be optionally used to pass any information that the caller-
+   supplied in() and out() functions need to do their job.
+
+     On return, inflateBack() will set strm->next_in and strm->avail_in to
+   pass back any unused input that was provided by the last in() call.  The
+   return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+   if in() or out() returned an error, Z_DATA_ERROR if there was a format
+   error in the deflate stream (in which case strm->msg is set to indicate the
+   nature of the error), or Z_STREAM_ERROR if the stream was not properly
+   initialized.  In the case of Z_BUF_ERROR, an input or output error can be
+   distinguished using strm->next_in which will be Z_NULL only if in() returned
+   an error.  If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to
+   out() returning non-zero.  (in() will always be called before out(), so
+   strm->next_in is assured to be defined if out() returns non-zero.)  Note
+   that inflateBack() cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_stream FAR *strm));
+/*
+     All memory allocated by inflateBackInit() is freed.
+
+     inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+   state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+    Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+     1.0: size of uInt
+     3.2: size of uLong
+     5.4: size of voidpf (pointer)
+     7.6: size of z_off_t
+
+    Compiler, assembler, and debug options:
+     8: DEBUG
+     9: ASMV or ASMINF -- use ASM code
+     10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+     11: 0 (reserved)
+
+    One-time table building (smaller code, but not thread-safe if true):
+     12: BUILDFIXED -- build static block decoding tables when needed
+     13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+     14,15: 0 (reserved)
+
+    Library content (indicates missing functionality):
+     16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+                          deflate code when not needed)
+     17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+                    and decode gzip streams (to avoid linking crc code)
+     18-19: 0 (reserved)
+
+    Operation variations (changes in library functionality):
+     20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+     21: FASTEST -- deflate algorithm with only one, lowest compression level
+     22,23: 0 (reserved)
+
+    The sprintf variant used by gzprintf (zero is best):
+     24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+     26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+    Remainder:
+     27-31: 0 (reserved)
+ */
+
+
+                        /* utility functions */
+
+/*
+     The following utility functions are implemented on top of the
+   basic stream-oriented functions. To simplify the interface, some
+   default options are assumed (compression level and memory usage,
+   standard memory allocation functions). The source code of these
+   utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,
+                                 const Bytef *source, uLong sourceLen));
+/*
+     Compresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be at least the value returned
+   by compressBound(sourceLen). Upon exit, destLen is the actual size of the
+   compressed buffer.
+     This function can be used to compress a whole file at once if the
+   input file is mmap'ed.
+     compress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,
+                                  const Bytef *source, uLong sourceLen,
+                                  int level));
+/*
+     Compresses the source buffer into the destination buffer. The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer. Upon entry, destLen is the total size of the
+   destination buffer, which must be at least the value returned by
+   compressBound(sourceLen). Upon exit, destLen is the actual size of the
+   compressed buffer.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+     compressBound() returns an upper bound on the compressed size after
+   compress() or compress2() on sourceLen bytes.  It would be used before
+   a compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
+                                   const Bytef *source, uLong sourceLen));
+/*
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be large enough to hold the
+   entire uncompressed data. (The size of the uncompressed data must have
+   been saved previously by the compressor and transmitted to the decompressor
+   by some mechanism outside the scope of this compression library.)
+   Upon exit, destLen is the actual size of the compressed buffer.
+     This function can be used to decompress a whole file at once if the
+   input file is mmap'ed.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen  OF((const char *path, const char *mode));
+/*
+     Opens a gzip (.gz) file for reading or writing. The mode parameter
+   is as in fopen ("rb" or "wb") but can also include a compression level
+   ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+   Huffman only compression as in "wb1h", or 'R' for run-length encoding
+   as in "wb1R". (See the description of deflateInit2 for more information
+   about the strategy parameter.)
+
+     gzopen can be used to read a file which is not in gzip format; in this
+   case gzread will directly read from the file without decompression.
+
+     gzopen returns NULL if the file could not be opened or if there was
+   insufficient memory to allocate the (de)compression state; errno
+   can be checked to distinguish the two cases (if errno is zero, the
+   zlib error is Z_MEM_ERROR).  */
+
+ZEXTERN gzFile ZEXPORT gzdopen  OF((int fd, const char *mode));
+/*
+     gzdopen() associates a gzFile with the file descriptor fd.  File
+   descriptors are obtained from calls like open, dup, creat, pipe or
+   fileno (in the file has been previously opened with fopen).
+   The mode parameter is as in gzopen.
+     The next call of gzclose on the returned gzFile will also close the
+   file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+   descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+     gzdopen returns NULL if there was insufficient memory to allocate
+   the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+     Dynamically update the compression level or strategy. See the description
+   of deflateInit2 for the meaning of these parameters.
+     gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+   opened for writing.
+*/
+
+ZEXTERN int ZEXPORT    gzread  OF((gzFile file, voidp buf, unsigned len));
+/*
+     Reads the given number of uncompressed bytes from the compressed file.
+   If the input file was not in gzip format, gzread copies the given number
+   of bytes into the buffer.
+     gzread returns the number of uncompressed bytes actually read (0 for
+   end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT    gzwrite OF((gzFile file,
+                                   voidpc buf, unsigned len));
+/*
+     Writes the given number of uncompressed bytes into the compressed file.
+   gzwrite returns the number of uncompressed bytes actually written
+   (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA   gzprintf OF((gzFile file, const char *format, ...));
+/*
+     Converts, formats, and writes the args to the compressed file under
+   control of the format string, as in fprintf. gzprintf returns the number of
+   uncompressed bytes actually written (0 in case of error).  The number of
+   uncompressed bytes written is limited to 4095. The caller should assure that
+   this limit is not exceeded. If it is exceeded, then gzprintf() will return
+   return an error (0) with nothing written. In this case, there may also be a
+   buffer overflow with unpredictable consequences, which is possible only if
+   zlib was compiled with the insecure functions sprintf() or vsprintf()
+   because the secure snprintf() or vsnprintf() functions were not available.
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+      Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+      gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+      Reads bytes from the compressed file until len-1 characters are read, or
+   a newline character is read and transferred to buf, or an end-of-file
+   condition is encountered.  The string is then terminated with a null
+   character.
+      gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT    gzputc OF((gzFile file, int c));
+/*
+      Writes c, converted to an unsigned char, into the compressed file.
+   gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT    gzgetc OF((gzFile file));
+/*
+      Reads one byte from the compressed file. gzgetc returns this byte
+   or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT    gzungetc OF((int c, gzFile file));
+/*
+      Push one character back onto the stream to be read again later.
+   Only one character of push-back is allowed.  gzungetc() returns the
+   character pushed, or -1 on failure.  gzungetc() will fail if a
+   character has been pushed but not read yet, or if c is -1. The pushed
+   character will be discarded if the stream is repositioned with gzseek()
+   or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT    gzflush OF((gzFile file, int flush));
+/*
+     Flushes all pending output into the compressed file. The parameter
+   flush is as in the deflate() function. The return value is the zlib
+   error number (see function gzerror below). gzflush returns Z_OK if
+   the flush parameter is Z_FINISH and all output could be flushed.
+     gzflush should be called only when strictly necessary because it can
+   degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT    gzseek OF((gzFile file,
+                                      z_off_t offset, int whence));
+/*
+      Sets the starting position for the next gzread or gzwrite on the
+   given compressed file. The offset represents a number of bytes in the
+   uncompressed data stream. The whence parameter is defined as in lseek(2);
+   the value SEEK_END is not supported.
+     If the file is opened for reading, this function is emulated but can be
+   extremely slow. If the file is opened for writing, only forward seeks are
+   supported; gzseek then compresses a sequence of zeroes up to the new
+   starting position.
+
+      gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error, in
+   particular if the file is opened for writing and the new starting position
+   would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT    gzrewind OF((gzFile file));
+/*
+     Rewinds the given file. This function is supported only for reading.
+
+   gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
+/*
+     Returns the starting position for the next gzread or gzwrite on the
+   given compressed file. This position represents a number of bytes in the
+   uncompressed data stream.
+
+   gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+     Returns 1 when EOF has previously been detected reading the given
+   input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT    gzclose OF((gzFile file));
+/*
+     Flushes all pending output if necessary, closes the compressed file
+   and deallocates all the (de)compression state. The return value is the zlib
+   error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+     Returns the error message for the last error which occurred on the
+   given compressed file. errnum is set to zlib error number. If an
+   error occurred in the file system and not in the compression library,
+   errnum is set to Z_ERRNO and the application may consult errno
+   to get the exact error code.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+     Clears the error and end-of-file flags for file. This is analogous to the
+   clearerr() function in stdio. This is useful for continuing to read a gzip
+   file that is being written concurrently.
+*/
+
+                        /* checksum functions */
+
+/*
+     These functions are not related to compression but are exported
+   anyway because they might be useful in applications using the
+   compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum. If buf is NULL, this function returns
+   the required initial value for the checksum.
+   An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+   much faster. Usage example:
+
+     uLong adler = adler32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       adler = adler32(adler, buffer, length);
+     }
+     if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));
+/*
+     Update a running crc with the bytes buf[0..len-1] and return the updated
+   crc. If buf is NULL, this function returns the required initial value
+   for the crc. Pre- and post-conditioning (one's complement) is performed
+   within this function so it shouldn't be done by the application.
+   Usage example:
+
+     uLong crc = crc32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       crc = crc32(crc, buffer, length);
+     }
+     if (crc != original_crc) error();
+*/
+
+
+                        /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,
+                                      int windowBits, int memLevel,
+                                      int strategy, const char *version,
+                                      int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
+                                      const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_stream FAR *strm, int windowBits,
+                                         unsigned char FAR *window,
+                                         const char *version,
+                                         int stream_size));
+#define deflateInit(strm, level) \
+        deflateInit_((strm), (level),       ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+        inflateInit_((strm),                ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+        deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+                      (strategy),           ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+        inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+        inflateBackInit_((strm), (windowBits), (window), \
+        ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+    struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char   * ZEXPORT zError           OF((int err));
+ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table    OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
diff --git a/zlib/zutil.c b/zlib/zutil.c
new file mode 100644 (file)
index 0000000..c75208c
--- /dev/null
@@ -0,0 +1,319 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: zutil.c,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#include "zutil.h"
+
+#ifndef NO_DUMMY_DECL
+struct internal_state      {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef STDC
+extern void exit OF((int));
+#endif
+
+const char * const z_errmsg[10] = {
+"need dictionary",     /* Z_NEED_DICT       2  */
+"stream end",          /* Z_STREAM_END      1  */
+"",                    /* Z_OK              0  */
+"file error",          /* Z_ERRNO         (-1) */
+"stream error",        /* Z_STREAM_ERROR  (-2) */
+"data error",          /* Z_DATA_ERROR    (-3) */
+"insufficient memory", /* Z_MEM_ERROR     (-4) */
+"buffer error",        /* Z_BUF_ERROR     (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char * ZEXPORT zlibVersion()
+{
+    return ZLIB_VERSION;
+}
+
+uLong ZEXPORT zlibCompileFlags()
+{
+    uLong flags;
+
+    flags = 0;
+    switch (sizeof(uInt)) {
+    case 2:     break;
+    case 4:     flags += 1;     break;
+    case 8:     flags += 2;     break;
+    default:    flags += 3;
+    }
+    switch (sizeof(uLong)) {
+    case 2:     break;
+    case 4:     flags += 1 << 2;        break;
+    case 8:     flags += 2 << 2;        break;
+    default:    flags += 3 << 2;
+    }
+    switch (sizeof(voidpf)) {
+    case 2:     break;
+    case 4:     flags += 1 << 4;        break;
+    case 8:     flags += 2 << 4;        break;
+    default:    flags += 3 << 4;
+    }
+    switch (sizeof(z_off_t)) {
+    case 2:     break;
+    case 4:     flags += 1 << 6;        break;
+    case 8:     flags += 2 << 6;        break;
+    default:    flags += 3 << 6;
+    }
+#ifdef DEBUG
+    flags += 1 << 8;
+#endif
+#if defined(ASMV) || defined(ASMINF)
+    flags += 1 << 9;
+#endif
+#ifdef ZLIB_WINAPI
+    flags += 1 << 10;
+#endif
+#ifdef BUILDFIXED
+    flags += 1 << 12;
+#endif
+#ifdef DYNAMIC_CRC_TABLE
+    flags += 1 << 13;
+#endif
+#ifdef NO_GZCOMPRESS
+    flags += 1 << 16;
+#endif
+#ifdef NO_GZIP
+    flags += 1 << 17;
+#endif
+#ifdef PKZIP_BUG_WORKAROUND
+    flags += 1 << 20;
+#endif
+#ifdef FASTEST
+    flags += 1 << 21;
+#endif
+#ifdef STDC
+#  ifdef NO_vsnprintf
+        flags += 1 << 25;
+#    ifdef HAS_vsprintf_void
+        flags += 1 << 26;
+#    endif
+#  else
+#    ifdef HAS_vsnprintf_void
+        flags += 1 << 26;
+#    endif
+#  endif
+#else
+        flags += 1 << 24;
+#  ifdef NO_snprintf
+        flags += 1 << 25;
+#    ifdef HAS_sprintf_void
+        flags += 1 << 26;
+#    endif
+#  else
+#    ifdef HAS_snprintf_void
+        flags += 1 << 26;
+#    endif
+#  endif
+#endif
+    return flags;
+}
+
+#ifdef DEBUG
+
+#  ifndef verbose
+#    define verbose 0
+#  endif
+int z_verbose = verbose;
+
+void z_error (m)
+    char *m;
+{
+    fprintf(stderr, "%s\n", m);
+    exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+    int err;
+{
+    return ERR_MSG(err);
+}
+
+#if defined(_WIN32_WCE)
+    /* does not exist on WCE */
+    int errno = 0;
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+    Bytef* dest;
+    const Bytef* source;
+    uInt  len;
+{
+    if (len == 0) return;
+    do {
+        *dest++ = *source++; /* ??? to be unrolled */
+    } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+    const Bytef* s1;
+    const Bytef* s2;
+    uInt  len;
+{
+    uInt j;
+
+    for (j = 0; j < len; j++) {
+        if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+    }
+    return 0;
+}
+
+void zmemzero(dest, len)
+    Bytef* dest;
+    uInt  len;
+{
+    if (len == 0) return;
+    do {
+        *dest++ = 0;  /* ??? to be unrolled */
+    } while (--len != 0);
+}
+#endif
+
+
+#ifdef SYS16BIT
+
+#ifdef __TURBOC__
+/* Turbo C in 16-bit mode */
+
+#  define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+    voidpf org_ptr;
+    voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+    voidpf buf = opaque; /* just to make some compilers happy */
+    ulg bsize = (ulg)items*size;
+
+    /* If we allocate less than 65520 bytes, we assume that farmalloc
+     * will return a usable pointer which doesn't have to be normalized.
+     */
+    if (bsize < 65520L) {
+        buf = farmalloc(bsize);
+        if (*(ush*)&buf != 0) return buf;
+    } else {
+        buf = farmalloc(bsize + 16L);
+    }
+    if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+    table[next_ptr].org_ptr = buf;
+
+    /* Normalize the pointer to seg:0 */
+    *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+    *(ush*)&buf = 0;
+    table[next_ptr++].new_ptr = buf;
+    return buf;
+}
+
+void  zcfree (voidpf opaque, voidpf ptr)
+{
+    int n;
+    if (*(ush*)&ptr != 0) { /* object < 64K */
+        farfree(ptr);
+        return;
+    }
+    /* Find the original pointer */
+    for (n = 0; n < next_ptr; n++) {
+        if (ptr != table[n].new_ptr) continue;
+
+        farfree(table[n].org_ptr);
+        while (++n < next_ptr) {
+            table[n-1] = table[n];
+        }
+        next_ptr--;
+        return;
+    }
+    ptr = opaque; /* just to make some compilers happy */
+    Assert(0, "zcfree: ptr not found");
+}
+
+#endif /* __TURBOC__ */
+
+
+#ifdef M_I86
+/* Microsoft C in 16-bit mode */
+
+#  define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+#  define _halloc  halloc
+#  define _hfree   hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+    if (opaque) opaque = 0; /* to make compiler happy */
+    return _halloc((long)items, size);
+}
+
+void  zcfree (voidpf opaque, voidpf ptr)
+{
+    if (opaque) opaque = 0; /* to make compiler happy */
+    _hfree(ptr);
+}
+
+#endif /* M_I86 */
+
+#endif /* SYS16BIT */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp  malloc OF((uInt size));
+extern voidp  calloc OF((uInt items, uInt size));
+extern void   free   OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+    voidpf opaque;
+    unsigned items;
+    unsigned size;
+{
+    if (opaque) items += size - size; /* make compiler happy */
+    return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
+                              (voidpf)calloc(items, size);
+}
+
+void  zcfree (opaque, ptr)
+    voidpf opaque;
+    voidpf ptr;
+{
+    free(ptr);
+    if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
diff --git a/zlib/zutil.h b/zlib/zutil.h
new file mode 100644 (file)
index 0000000..ff64e0b
--- /dev/null
@@ -0,0 +1,258 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id: zutil.h,v 1.1 2005/01/12 07:44:59 mmondor Exp $ */
+
+#ifndef ZUTIL_H
+#define ZUTIL_H
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#ifdef STDC
+#  include <stddef.h>
+#  include <string.h>
+#  include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+    extern int errno;
+#else
+#   include <errno.h>
+#endif
+
+#ifndef local
+#  define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char  uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long  ulg;
+
+extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+  return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+        /* common constants */
+
+#ifndef DEF_WBITS
+#  define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+#  define DEF_MEM_LEVEL 8
+#else
+#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES    2
+/* The three kinds of block type */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+        /* target dependencies */
+
+#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
+#  define OS_CODE  0x00
+#  if defined(__TURBOC__) || defined(__BORLANDC__)
+#    if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+       /* Allow compilation with ANSI keywords only enabled */
+       void _Cdecl farfree( void *block );
+       void *_Cdecl farmalloc( unsigned long nbytes );
+#    else
+#      include <alloc.h>
+#    endif
+#  else /* MSC or DJGPP */
+#    include <malloc.h>
+#  endif
+#endif
+
+#ifdef AMIGA
+#  define OS_CODE  0x01
+#endif
+
+#if defined(VAXC) || defined(VMS)
+#  define OS_CODE  0x02
+#  define F_OPEN(name, mode) \
+     fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#if defined(ATARI) || defined(atarist)
+#  define OS_CODE  0x05
+#endif
+
+#ifdef OS2
+#  define OS_CODE  0x06
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+#  define OS_CODE  0x07
+#  if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+#    include <unix.h> /* for fdopen */
+#  else
+#    ifndef fdopen
+#      define fdopen(fd,mode) NULL /* No fdopen() */
+#    endif
+#  endif
+#endif
+
+#ifdef TOPS20
+#  define OS_CODE  0x0a
+#endif
+
+#ifdef WIN32
+#  ifndef __CYGWIN__  /* Cygwin is Unix, not Win32 */
+#    define OS_CODE  0x0b
+#  endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+#  define OS_CODE  0x0f
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+#  define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600))
+#  if defined(_WIN32_WCE)
+#    define fdopen(fd,mode) NULL /* No fdopen() */
+#    ifndef _PTRDIFF_T_DEFINED
+       typedef int ptrdiff_t;
+#      define _PTRDIFF_T_DEFINED
+#    endif
+#  else
+#    define fdopen(fd,type)  _fdopen(fd,type)
+#  endif
+#endif
+
+        /* common defaults */
+
+#ifndef OS_CODE
+#  define OS_CODE  0x03  /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+#  define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+         /* functions */
+
+#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
+#  ifndef HAVE_VSNPRINTF
+#    define HAVE_VSNPRINTF
+#  endif
+#endif
+#if defined(__CYGWIN__)
+#  ifndef HAVE_VSNPRINTF
+#    define HAVE_VSNPRINTF
+#  endif
+#endif
+#ifndef HAVE_VSNPRINTF
+#  ifdef MSDOS
+     /* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
+        but for now we just assume it doesn't. */
+#    define NO_vsnprintf
+#  endif
+#  ifdef __TURBOC__
+#    define NO_vsnprintf
+#  endif
+#  ifdef WIN32
+     /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
+#    if !defined(vsnprintf) && !defined(NO_vsnprintf)
+#      define vsnprintf _vsnprintf
+#    endif
+#  endif
+#  ifdef __SASC
+#    define NO_vsnprintf
+#  endif
+#endif
+
+#ifdef HAVE_STRERROR
+   extern char *strerror OF((int));
+#  define zstrerror(errnum) strerror(errnum)
+#else
+#  define zstrerror(errnum) ""
+#endif
+
+#if defined(pyr)
+#  define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+  * You may have to use the same strategy for Borland C (untested).
+  * The __SC__ check is for Symantec.
+  */
+#  define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+#  define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+#  ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+#    define zmemcpy _fmemcpy
+#    define zmemcmp _fmemcmp
+#    define zmemzero(dest, len) _fmemset(dest, 0, len)
+#  else
+#    define zmemcpy memcpy
+#    define zmemcmp memcmp
+#    define zmemzero(dest, len) memset(dest, 0, len)
+#  endif
+#else
+   extern void zmemcpy  OF((Bytef* dest, const Bytef* source, uInt len));
+   extern int  zmemcmp  OF((const Bytef* s1, const Bytef* s2, uInt len));
+   extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  include <stdio.h>
+   extern int z_verbose;
+   extern void z_error    OF((char *m));
+#  define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+#  define Trace(x) {if (z_verbose>=0) fprintf x ;}
+#  define Tracev(x) {if (z_verbose>0) fprintf x ;}
+#  define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void   zcfree  OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+           (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr)  (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* ZUTIL_H */