commit ead43a77394f6c0f539c6ad10574d30b9de0fb4f Author: Graeme Walker Date: Sat Sep 8 12:00:00 2001 +0000 v0.9.1 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..c11291d --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ + +AUTHORS +======= +Graeme Walker diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU 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. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), 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 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 show them these terms so they know 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. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License 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 derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 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 License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +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. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary 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 + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 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 Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing 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 for copying, distributing or modifying +the Program or works based on it. + + 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. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. 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 this 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 +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. 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 + + 11. 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. + + 12. 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 + + 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 the public, 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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +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) year 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 is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..3caad0d --- /dev/null +++ b/ChangeLog @@ -0,0 +1,9 @@ +Change Log +========== + +0.9 -> 0.9.1 +------------ +Improved documentation from doxygen. +More complete use of namespaces. +Experimental compile-time support for IPv6. + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..fad2a08 --- /dev/null +++ b/INSTALL @@ -0,0 +1,189 @@ +Introduction +============ + + What follows are generic installation instructions for doing a +standard GNU "./configure; make; make install" installation from source. +The E-MailRelay userguide describes what you have to do after the "make +install" (under GNU/Linux) in order to get the emailrelay daemon to start +up at boot-time and automatically forward e-mail to your ISP. + +Basic Installation +================== + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..0e7bb85 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = +SUBDIRS = src bin lib doc diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..471f459 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,352 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = . + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +EXTRA_DIST = +SUBDIRS = src bin lib doc +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +DIST_COMMON = README ./stamp-h.in AUTHORS COPYING ChangeLog INSTALL \ +Makefile.am Makefile.in NEWS aclocal.m4 config.h.in configure \ +configure.in install-sh missing mkinstalldirs + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status + +$(ACLOCAL_M4): configure.in + cd $(srcdir) && $(ACLOCAL) + +config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck +$(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES) + cd $(srcdir) && $(AUTOCONF) + +config.h: stamp-h + @if test ! -f $@; then \ + rm -f stamp-h; \ + $(MAKE) stamp-h; \ + else :; fi +stamp-h: $(srcdir)/config.h.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES= CONFIG_HEADERS=config.h \ + $(SHELL) ./config.status + @echo timestamp > stamp-h 2> /dev/null +$(srcdir)/config.h.in: $(srcdir)/stamp-h.in + @if test ! -f $@; then \ + rm -f $(srcdir)/stamp-h.in; \ + $(MAKE) $(srcdir)/stamp-h.in; \ + else :; fi +$(srcdir)/stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOHEADER) + @echo timestamp > $(srcdir)/stamp-h.in 2> /dev/null + +mostlyclean-hdr: + +clean-hdr: + +distclean-hdr: + -rm -f config.h + +maintainer-clean-hdr: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. + +@SET_MAKE@ + +all-recursive install-data-recursive install-exec-recursive \ +installdirs-recursive install-recursive uninstall-recursive \ +check-recursive installcheck-recursive info-recursive dvi-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ + rev="$$subdir $$rev"; \ + test "$$subdir" = "." && dot_seen=yes; \ + done; \ + test "$$dot_seen" = "no" && rev=". $$rev"; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)config.h.in$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags config.h.in $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + -rm -rf $(distdir) + GZIP=$(GZIP_ENV) $(TAR) zxf $(distdir).tar.gz + mkdir $(distdir)/=build + mkdir $(distdir)/=inst + dc_install_base=`cd $(distdir)/=inst && pwd`; \ + cd $(distdir)/=build \ + && ../configure --srcdir=.. --prefix=$$dc_install_base \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) dist + -rm -rf $(distdir) + @banner="$(distdir).tar.gz is ready for distribution"; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + echo "$$dashes" +dist: distdir + -chmod -R a+r $(distdir) + GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) + -rm -rf $(distdir) +dist-all: distdir + -chmod -R a+r $(distdir) + GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) + -rm -rf $(distdir) +distdir: $(DISTFILES) + -rm -rf $(distdir) + mkdir $(distdir) + -chmod 777 $(distdir) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + chmod 777 $(distdir)/$$subdir; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(distdir) distdir=../$(distdir)/$$subdir distdir) \ + || exit 1; \ + fi; \ + done +info-am: +info: info-recursive +dvi-am: +dvi: dvi-recursive +check-am: all-am +check: check-recursive +installcheck-am: +installcheck: installcheck-recursive +all-recursive-am: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +install-exec-am: +install-exec: install-exec-recursive + +install-data-am: +install-data: install-data-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-recursive +uninstall-am: +uninstall: uninstall-recursive +all-am: Makefile config.h +all-redirect: all-recursive-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: installdirs-recursive +installdirs-am: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-hdr mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-recursive + +clean-am: clean-hdr clean-tags clean-generic mostlyclean-am + +clean: clean-recursive + +distclean-am: distclean-hdr distclean-tags distclean-generic clean-am + +distclean: distclean-recursive + -rm -f config.status + +maintainer-clean-am: maintainer-clean-hdr maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-recursive + -rm -f config.status + +.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \ +install-data-recursive uninstall-data-recursive install-exec-recursive \ +uninstall-exec-recursive installdirs-recursive uninstalldirs-recursive \ +all-recursive check-recursive installcheck-recursive info-recursive \ +dvi-recursive mostlyclean-recursive distclean-recursive clean-recursive \ +maintainer-clean-recursive tags tags-recursive mostlyclean-tags \ +distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ +dvi-am dvi check check-am installcheck-am installcheck all-recursive-am \ +install-exec-am install-exec install-data-am install-data install-am \ +install uninstall-am uninstall all-redirect all-am all installdirs-am \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..158be8d --- /dev/null +++ b/NEWS @@ -0,0 +1,10 @@ + +To do +===== + +from 0.9.1 +---------- +Better use of autoconf +Port to BSD & solaris +test IPv6 + diff --git a/README b/README new file mode 100644 index 0000000..46a612f --- /dev/null +++ b/README @@ -0,0 +1,67 @@ +E-MailRelay +=========== + +Abstract +-------- +E-MailRelay is a simple store-and-forward SMTP MTA, designed for standalone +machines with an intermittent (dial-up) connection to the wider Internet. +In most situations the only configuration required is to specify the mail +gateway address on the command line. + +C++ source code is available for Linux and Windows. Distribution is under +the GNU General Public License. + +Quick start +----------- +The "emailrelay" program can be run as an SMTP server daemon using the +command "emailrelay --as-server", and stored mail can be forwarded by +running the command "emailrelay --as-client :smtp". + +The "--as-server" command is typically run automatically at boot time, +using the boot scripts under "/etc/init.d" or "/sbin/init.d", while the +"--as-client" command is normally put into pppd's "ip-up" script ("/etc/ppp/ip-up") +in place of "sendmail -q". + +The program requires a writeable spool directory to store e-mail messages. +The directory defaults to "/usr/local/var/spool/emailrelay", but it +can be changed by using the "--spool-dir" switch. + +By default the server will try to listen on TCP port 25. If another SMTP +server is running then it will fail to start up, with a "cannot bind" error +message. The port number can be changed by using the "--port" switch. + +To test the program out without a full installation: +(1) run the server as "emailrelay --no-daemon --no-syslog --log --spool-dir ${HOME} --port 10001 &" +(2) reconfigure your e-mail client to use port 10001 rather than 25 ("smtp") +(3) send some test messages to addressees on the Internet +(4) connect to the Internet +(5) forward the stored messages using "emailrelay --spool-dir ${HOME} --as-client :smtp" +(6) clean up with "killall emailrelay ; rm ${HOME}/emailrelay.*" + +Documentation +------------- +The following documentation is provided: +* README -- this document +* COPYING -- the GNU General Public License +* INSTALL -- build & install instructions (based on generic GNU text) +* doc/userguide.txt -- user guide +* doc/reference.txt -- reference document +* doc/developer.txt -- developer guide + +And for completeness the following stub documents are also included: +* NEWS +* AUTHORS +* ChangeLog + +Configurations +-------------- +The code was developed on SuSE Linux 7.1 using: +* linux 2.2.18, +* gcc 2.95.2, +* glibc 2.2.7 (libc.so.6), +* gnu make 3.79.1, +* autoconf 2.13 + +and ported to Windows 98 using +* MSVC 6.0 + diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..f23ba29 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,127 @@ +dnl aclocal.m4 generated automatically by aclocal 1.4 + +dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without +dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A +dnl PARTICULAR PURPOSE. + +# Do all the work for Automake. This macro actually does too much -- +# some checks are only needed if your package does certain things. +# But this isn't really a big deal. + +# serial 1 + +dnl Usage: +dnl AM_INIT_AUTOMAKE(package,version, [no-define]) + +AC_DEFUN(AM_INIT_AUTOMAKE, +[AC_REQUIRE([AC_PROG_INSTALL]) +PACKAGE=[$1] +AC_SUBST(PACKAGE) +VERSION=[$2] +AC_SUBST(VERSION) +dnl test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi +ifelse([$3],, +AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) +AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])) +AC_REQUIRE([AM_SANITY_CHECK]) +AC_REQUIRE([AC_ARG_PROGRAM]) +dnl FIXME This is truly gross. +missing_dir=`cd $ac_aux_dir && pwd` +AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) +AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) +AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) +AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) +AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) +AC_REQUIRE([AC_PROG_MAKE_SET])]) + +# +# Check to make sure that the build environment is sane. +# + +AC_DEFUN(AM_SANITY_CHECK, +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftestfile +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` + if test "[$]*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftestfile` + fi + if test "[$]*" != "X $srcdir/configure conftestfile" \ + && test "[$]*" != "X conftestfile $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "[$]2" = conftestfile + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +rm -f conftest* +AC_MSG_RESULT(yes)]) + +dnl AM_MISSING_PROG(NAME, PROGRAM, DIRECTORY) +dnl The program must properly implement --version. +AC_DEFUN(AM_MISSING_PROG, +[AC_MSG_CHECKING(for working $2) +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if ($2 --version) < /dev/null > /dev/null 2>&1; then + $1=$2 + AC_MSG_RESULT(found) +else + $1="$3/missing $2" + AC_MSG_RESULT(missing) +fi +AC_SUBST($1)]) + +# Like AC_CONFIG_HEADER, but automatically create stamp file. + +AC_DEFUN(AM_CONFIG_HEADER, +[AC_PREREQ([2.12]) +AC_CONFIG_HEADER([$1]) +dnl When config.status generates a header, we must update the stamp-h file. +dnl This file resides in the same directory as the config header +dnl that is generated. We must strip everything past the first ":", +dnl and everything past the last "/". +AC_OUTPUT_COMMANDS(changequote(<<,>>)dnl +ifelse(patsubst(<<$1>>, <<[^ ]>>, <<>>), <<>>, +<>CONFIG_HEADERS" || echo timestamp > patsubst(<<$1>>, <<^\([^:]*/\)?.*>>, <<\1>>)stamp-h<<>>dnl>>, +<>; do + case " <<$>>CONFIG_HEADERS " in + *" <<$>>am_file "*<<)>> + echo timestamp > `echo <<$>>am_file | sed -e 's%:.*%%' -e 's%[^/]*$%%'`stamp-h$am_indx + ;; + esac + am_indx=`expr "<<$>>am_indx" + 1` +done<<>>dnl>>) +changequote([,]))]) + diff --git a/bin/Makefile.am b/bin/Makefile.am new file mode 100644 index 0000000..abf7b0e --- /dev/null +++ b/bin/Makefile.am @@ -0,0 +1,10 @@ +noinst_SCRIPTS = emailrelay-filter.sh emailrelay-test.sh +libexec_SCRIPTS = emailrelay.sh +pkgdata_DATA = emailrelay-notify.sh emailrelay-deliver.sh +EXTRA_DIST = txt2html.sh $(noinst_SCRIPTS) $(libexec_SCRIPTS) $(pkgdata_DATA) +CLEANFILES = $(noinst_SCRIPTS) $(libexec_SCRIPTS) $(pkgdata_DATA) +TESTS = emailrelay-test.sh +SUFFIXES = .sh_ .sh +.sh_.sh: + cp $(srcdir)/../bin/$*.sh_ $*.sh + chmod ugo+x $*.sh diff --git a/bin/Makefile.in b/bin/Makefile.in new file mode 100644 index 0000000..a8ffb1e --- /dev/null +++ b/bin/Makefile.in @@ -0,0 +1,252 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +noinst_SCRIPTS = emailrelay-filter.sh emailrelay-test.sh +libexec_SCRIPTS = emailrelay.sh +pkgdata_DATA = emailrelay-notify.sh emailrelay-deliver.sh +EXTRA_DIST = txt2html.sh $(noinst_SCRIPTS) $(libexec_SCRIPTS) $(pkgdata_DATA) +CLEANFILES = $(noinst_SCRIPTS) $(libexec_SCRIPTS) $(pkgdata_DATA) +TESTS = emailrelay-test.sh +SUFFIXES = .sh_ .sh +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = +SCRIPTS = $(libexec_SCRIPTS) $(noinst_SCRIPTS) + +DATA = $(pkgdata_DATA) + +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +.SUFFIXES: .sh .sh_ +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps bin/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +install-libexecSCRIPTS: $(libexec_SCRIPTS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(libexecdir) + @list='$(libexec_SCRIPTS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(libexecdir)/`echo $$p|sed '$(transform)'`"; \ + $(INSTALL_SCRIPT) $$p $(DESTDIR)$(libexecdir)/`echo $$p|sed '$(transform)'`; \ + else if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(libexecdir)/`echo $$p|sed '$(transform)'`"; \ + $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(libexecdir)/`echo $$p|sed '$(transform)'`; \ + else :; fi; fi; \ + done + +uninstall-libexecSCRIPTS: + @$(NORMAL_UNINSTALL) + list='$(libexec_SCRIPTS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(libexecdir)/`echo $$p|sed '$(transform)'`; \ + done + +install-pkgdataDATA: $(pkgdata_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) + @list='$(pkgdata_DATA)'; for p in $$list; do \ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkgdatadir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkgdatadir)/$$p; \ + else if test -f $$p; then \ + echo " $(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p"; \ + $(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \ + fi; fi; \ + done + +uninstall-pkgdataDATA: + @$(NORMAL_UNINSTALL) + list='$(pkgdata_DATA)'; for p in $$list; do \ + rm -f $(DESTDIR)$(pkgdatadir)/$$p; \ + done +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = bin + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +check-TESTS: $(TESTS) + @failed=0; all=0; \ + srcdir=$(srcdir); export srcdir; \ + for tst in $(TESTS); do \ + if test -f $$tst; then dir=.; \ + else dir="$(srcdir)"; fi; \ + if $(TESTS_ENVIRONMENT) $$dir/$$tst; then \ + all=`expr $$all + 1`; \ + echo "PASS: $$tst"; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + failed=`expr $$failed + 1`; \ + echo "FAIL: $$tst"; \ + fi; \ + done; \ + if test "$$failed" -eq 0; then \ + banner="All $$all tests passed"; \ + else \ + banner="$$failed of $$all tests failed"; \ + fi; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + echo "$$dashes"; \ + test "$$failed" -eq 0 +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-libexecSCRIPTS +install-exec: install-exec-am + +install-data-am: install-pkgdataDATA +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-libexecSCRIPTS uninstall-pkgdataDATA +uninstall: uninstall-am +all-am: Makefile $(SCRIPTS) $(DATA) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libexecdir) $(DESTDIR)$(pkgdatadir) + + +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: uninstall-libexecSCRIPTS install-libexecSCRIPTS \ +uninstall-pkgdataDATA install-pkgdataDATA tags distdir check-TESTS \ +info-am info dvi-am dvi check check-am installcheck-am installcheck \ +install-exec-am install-exec install-data-am install-data install-am \ +install uninstall-am uninstall all-redirect all-am all installdirs \ +mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + +.sh_.sh: + cp $(srcdir)/../bin/$*.sh_ $*.sh + chmod ugo+x $*.sh + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/emailrelay-deliver.sh b/bin/emailrelay-deliver.sh new file mode 100755 index 0000000..ea0aada --- /dev/null +++ b/bin/emailrelay-deliver.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === +# +# emailrelay-deliver.sh +# +# An example script which looks for local mail in the MailRelay +# spool directory, and delivers it using 'procmail'. +# + +store="/var/spool/mailrelay" +postmaster="root" + +# parse the command line +# +if test $# -ge 1 +then + store="${1}" +fi + +# check the spool directory is valid +# +if test \! -d "${store}" +then + echo `basename $0`: invalid spool directory >&2 + exit 1 +fi + +# for each e-mail to a local recipient... +# +for file in ${store}/mail-relay.*.envelope.local "" +do + if test -f "${file}" + then + content="`echo ${file} | sed 's/envelope/content/'`" + + deliver_to="`fgrep X-MailRelay-LocalTo ${file} | sed 's/X-MailRelay-LocalTo: //' | tr -d '\015' | sed \"s/postmaster/${postmaster}/g\"`" + if test "${deliver_to}" = "" + then + deliver_to="${postmaster}" + fi + + # deliver using procmail + # + if test -f "${content}" + then + echo `basename $0`: delivering `basename ${content}` to ${deliver_to} + procmail -d ${deliver_to} < ${content} + rc=$? + if test "${rc}" -eq 0 + then + echo '' # rm -f "${file}" 2>/dev/null + fi + fi + fi +done + diff --git a/bin/emailrelay-filter.sh b/bin/emailrelay-filter.sh new file mode 100755 index 0000000..2056b85 --- /dev/null +++ b/bin/emailrelay-filter.sh @@ -0,0 +1,194 @@ +#!/bin/sh +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === +# +# A doxygen filter. +# + +awk="gawk" + +# PreFilter() +# Removes banner comments (including legalese) from the top of the file. +# +PreFilter() +{ + ${awk} ' BEGIN { + in_start = 1 + } + { + if( in_start && !match( $0 , "^//" ) ) + in_start = 0 + if( !in_start ) + print + } ' +} + +# SourceFilter() +# Adds doxygen annotation for implementation class comments. +# +SourceFilter() +{ + ${awk} ' BEGIN { in_class_comment = 0 } + { + start = match( $0 , "// Class:" ) == 1 + end = match( $0 , "class" ) == 1 + + if( !in_class_comment && start ) + { + in_class_comment = 1 + print "/**" + } + else if( !in_class_comment ) + { + print + } + else if( end ) + { + in_class_comment = 0 + print " */" + print + } + else + { + sub( "^// Description: " , "" ) + sub( "^// See also: " , "\\see " ) + sub( "^//" , "" ) + printf( " * %s\n" , $0 ) + } + } + ' +} + +# HeaderFilter() +# Adds doxygen annotation to header-file comments. +# +HeaderFilter() +{ + ${awk} ' BEGIN { + was_comment_line = 0 + was_code_line = 0 + re_namespace = "^[[:space:]]*namespace" + re_comment = "^[[:space:]]*//" + re_code = "^[[:space:]]*///" + } + { + is_namespace_line = match($0,re_namespace) + + is_comment_line = match($0,re_comment) + re_comment_length = RLENGTH + + is_code_line = match($0,re_code) + re_code_length = RLENGTH + + if( is_namespace_line ) + { + printf( "/*! \\namespace %s */\n" , $2 ) + } + + if( is_code_line ) + { + sub( "///" , "//" ) + } + + if( is_comment_line ) + { + indent = substr( $0 , 1 , re_comment_length-2 ) + sub( "Class: " , "\\class " ) + sub( "Typedef: .*" , "" ) + sub( "Description: " , "" ) + sub( "See also: " , "\\see " ) + if( was_comment_line ) + sub( re_comment , indent " " ) + else if( length(indent) ) + sub( re_comment , indent "/**<" ) + else + sub( re_comment , indent "/** " ) + } + + if( is_code_line && !was_code_line ) + { + print indent "\\code" + } + + if( was_code_line && !is_code_line ) + { + print indent "\\endcode" + } + + if( was_comment_line && !is_comment_line ) + { + print indent "*/" + } + + print + + was_comment_line = is_comment_line + was_code_line = is_code_line + } ' +} + +# PostFilter() +# Deals with nested-class descriptions with a format like "class Foo // comment". +# +PostFilter() +{ + ${awk} ' + { + if( match( $0 , "^[[:space:]]*class[[:space:]][^/]*//" ) || + match( $0 , "^[[:space:]]*struct[[:space:]][^/]*//" ) ) + { + class = substr( $0 , 1 , RLENGTH-2 ) + description = substr( $0 , RLENGTH+1 ) + printf( " /** %s */\n" , description ) + printf( "%s\n" , class ) + } + else + { + print + } + } ' +} + +BasicFilter() +{ + cat + echo '/* \\file */' +} + +name="`basename \"${1}\"`" +type="`echo \"${name}\" | ${awk} -F . '{print $NF}'`" +classes="`fgrep '// Class:' \"${1}\" | wc -l`" + +if test "${name}" = "gdef.h" -o "${name}" = "gnet.h" +then + cat "${1}" | BasicFilter + +elif test "${type}" = "cpp" -a "${classes}" -eq 0 +then + cat "${1}" | BasicFilter + +elif test "${type}" = "cpp" -a "${classes}" -gt 0 +then + cat "${1}" | SourceFilter + +else + cat "${1}" | PreFilter | HeaderFilter | PostFilter +fi + diff --git a/bin/emailrelay-notify.sh b/bin/emailrelay-notify.sh new file mode 100755 index 0000000..e627516 --- /dev/null +++ b/bin/emailrelay-notify.sh @@ -0,0 +1,94 @@ +#!/bin/sh +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === +# +# emailrelay-notify.sh +# +# An example script which looks for failed mail in the MailRelay spool +# directory, and sends failure notification messages using 'procmail'. +# + +tmp="/tmp/`basename $0`.$$.tmp" +trap "rm -f ${tmp} 2>/dev/null ; exit 0" 0 1 2 3 13 15 + +# parse the command line +# +store="/var/spool/mailrelay" +if test $# -ge 1 +then + store="${1}" +fi + +# check the spool directory +# +if test \! -d "${store}" +then + echo `basename $0`: invalid spool directory >&2 + exit 1 +fi + +# for each failed e-mail... +# +for file in ${store}/mail-relay.*.envelope.bad "" +do + if test -f "${file}" + then + content="`echo ${file} | sed 's/envelope/content/' | sed 's/.bad//'`" + reason="`fgrep MailRelay-Reason ${file} | sed 's/X-[^ ]*Reason: //' | tr -d '\015'`" + from="`fgrep MailRelay-From ${file} | sed 's/X-MailRelay-From: //' | tr -d '\015'`" + deliver_to="${from}" + if test "${deliver_to}" = "" + then + deliver_to="postmaster" + fi + + # create a notification message header + # + echo "To: ${deliver_to}" > ${tmp} + echo "From: postmaster" >> ${tmp} + echo "Subject: Your e-mail could not be delivered" >> ${tmp} + echo " " >> ${tmp} + + # add the message content + # + if test "${reason}" != "" + then + echo "Reason: ${reason}" >> ${tmp} + fi + if test -f "${content}" + then + egrep -i '^To:|^Subject:' ${content} >> ${tmp} + echo " " >> ${tmp} + echo "The original mail is saved as \"${content}\"." >> ${tmp} + echo "You should make a copy of this file if necessary and then delete it." >> ${tmp} + fi + + # deliver the notification using procmail + # + echo `basename $0`: delivering `basename ${content}` to ${deliver_to} + procmail -d ${deliver_to} < ${tmp} + rc=$? + if test "${rc}" -eq 0 + then + rm -f "${file}" 2>/dev/null + fi + fi +done + diff --git a/bin/emailrelay-test.sh b/bin/emailrelay-test.sh new file mode 100755 index 0000000..cdc8756 --- /dev/null +++ b/bin/emailrelay-test.sh @@ -0,0 +1,148 @@ +#!/bin/sh +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === +# +# emailrelay-test.sh +# +# Test the E-MailRelay system. +# +# Creates three temporary spool directories under /tmp and runs +# three emailrelay servers to bucket-brigade a test message from one to +# the next. The test succeeds if the message gets into the third +# spool directory. +# +# If this test takes more than a second or two then it has failed. +# + +exe="../src/main/emailrelay" +poke="../src/main/emailrelay-poke" +pp="1001" # port-prefix +base_dir="/tmp/`basename $0`.$$.tmp" +exit_code="1" + +Cleanup() +{ + kill `cat ${base_dir}/pid-* 2>/dev/null` 2>/dev/null + if test -d ${base_dir} + then + grep "." ${base_dir}/log-? > /tmp/`basename $0`.out 2>/dev/null + fi + rm -rf ${base_dir} 2>/dev/null +} + +Trap() +{ + Cleanup + exit ${exit_code} +} + +RunServer() +{ + port_="${1}" + spool_="${2}" + log_="${3}" + pidfile_="${4}" + extra_="${5}" + + mkdir -p ${base_dir}/${spool_} + ${exe} --log --no-syslog --port ${port_} --spool-dir ${base_dir}/${spool_} \ + --pid-file ${base_dir}/${pidfile_} ${extra_} 2> ${base_dir}/${log_} +} + +RunClient() +{ + to_="${1}" + spool_="${2}" + log_="${3}" + pidfile_="${4}" + + ${exe} --forward --no-daemon --dont-serve --log --no-syslog \ + --pid-file ${base_dir}/${pidfile_} \ + --forward-to ${to_} --spool-dir ${base_dir}/${spool_} 2> ${base_dir}/${log_} +} + +RunPoke() +{ + port_="${1}" + log_="${2}" + + ${poke} ${port_} > ${base_dir}/${log_} +} + +Content() +{ + echo "To: recipient-1@localhost, recipient-2@localhost" + echo "Subject: test message 1" + echo "From: sender" + echo " " + echo "Content" +} + +Envelope() +{ + echo "X-MailRelay-Format: #2821.2" + echo "X-MailRelay-Content: 8bit" + echo "X-MailRelay-From: sender" + echo "X-MailRelay-ToCount: 2" + echo "X-MailRelay-To-Remote: recipient-1@localhost" + echo "X-MailRelay-To-Remote: recipient-2@localhost" + echo "X-MailRelay-End: 1" +} + +CrLf() +{ + sed 's/$/£/' | tr '£' '\r' +} + +CheckResults() +{ + if test -f ${base_dir}/store-3/*.envelope -a -f ${base_dir}/store-3/*.content + then + exit_code="0" + echo `basename $0`: succeeded + else + echo `basename $0`: failed: see /tmp/`basename $0`.out >&2 + fi +} + +StartTimer() +{ + ( sleep 5 ; Cleanup ) & +} + +CreateMessages() +{ + mkdir -p ${base_dir}/store-1 + Content | CrLf > ${base_dir}/store-1/emailrelay.0.1.content + Envelope | CrLf > ${base_dir}/store-1/emailrelay.0.1.envelope +} + +trap "Trap ; exit" 1 2 3 13 15 +trap "Trap 0 ; exit" 0 + +StartTimer +RunServer ${pp}1 store-2 log-1 pid-1 +RunServer ${pp}2 store-2 log-2 pid-2 "--admin ${pp}4 --forward-to localhost:${pp}3" +RunServer ${pp}3 store-3 log-3 pid-3 +CreateMessages +RunClient localhost:${pp}1 store-1 log-c pid-4 +RunPoke ${pp}4 log-p +CheckResults + diff --git a/bin/emailrelay.sh b/bin/emailrelay.sh new file mode 100755 index 0000000..704f3c8 --- /dev/null +++ b/bin/emailrelay.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === +# +# emailrelay.sh +# +# A shell-script wrapper for E-MailRelay designed for +# use in the SysV-init system (/etc/init.d). +# +# usage: emailrelay.sh { start | stop } +# + +# configuration +# +var_run="/var/run" +if test \! -d "${var_run}" ; then var_run="/tmp" ; fi +params="" + +# initialisation +# +pid_file="${var_run}/emailrelay.pid" + +# check the command line +# +usage="{ start | stop }" +if test $# -eq 0 +then + echo usage: `basename $0` "${usage}" >&2 + exit 2 +fi + +# process the command line +# +if test "${1}" = "start" +then + # "start" + # + rm -f "${pid_file}" 2>/dev/null + emailrelay --as-server --pid-file "${pid_file}" ${params} $@ + +elif test "${1}" = "stop" +then + # "stop" + # + if test -f "${pid_file}" && test "`cat ${pid_file}`" != "" + then + kill "`cat ${pid_file}`" + fi + +else + echo usage: `basename $0` "${usage}" >&2 + exit 2 +fi + diff --git a/bin/txt2html.sh b/bin/txt2html.sh new file mode 100755 index 0000000..ee487e1 --- /dev/null +++ b/bin/txt2html.sh @@ -0,0 +1,235 @@ +#!/bin/sh +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === +# +# txt2html.sh +# + +awk="awk" + +file="${1}" +if test "${file}" = "" +then + echo usage: `basename $0` '' >&2 + exit 2 +fi + +if test \! -f "${file}" +then + echo `basename $0`: no such file: ${file} >&2 + exit 1 +fi + +title="`basename ${file}`" +if test "${2}" != "" +then + title="${2}" +fi + +# === + +Main() +{ +${awk} -v title="${1}" ' +BEGIN { + printf( "\n" ) + printf( "\n" ) + printf( "%s\n" , title ) + printf( "\n" ) + printf( "\n" ) +} + +function escape( line ) +{ + gsub( "&" , "\\&" , line ) + gsub( "<" , "\\<" , line ) + gsub( ">" , "\\>" , line ) + return line +} + +function dequote( line ) +{ + quote = "\"" + not_quote = "[^" quote "]" + gsub( quote not_quote "*" quote , "&" , line ) + gsub( "" quote , "" , line ) + gsub( quote "" , "" , line ) + return line +} + +function fn( line ) +{ + gsub( "[^[:space:]][^[:space:]]*\\(\\)" , "&" , line ) + return line +} + +function output( line ) +{ + printf( "%s\n" , fn(dequote(escape(line))) ) +} + +function tagOutput( line , tag ) +{ + printf( "<%s>%s\n" , tag , fn(dequote(escape(line))) , tag ) +} + +function process( line , next_ ) +{ + is_blank = match( line , "^[[:space:]]*$" ) + is_sub_para = match( line , "^[[:space:]][[:space:]][^[:space:]]" ) + is_code = match( line , "^[[:space:]]" ) && !is_sub_para + is_heading = match( next_ , "^==*[[:space:]]*$" ) + is_sub_heading = match( next_ , "^--*[[:space:]]*$" ) + is_list_item = match( line , "^\\* " ) + is_numbered_item = match( line , "^\\([[:digit:]][[:digit:]]*\\)" ) + is_heading_line = match( line , "^==*[[:space:]]*$" ) + is_sub_heading_line = match( line , "^--*[[:space:]]*$" ) + + if( is_blank ) + { + printf( "


\n" ) + } + else if( is_code ) + { + tagOutput( line , "pre" ) + } + else if( is_sub_para ) + { + tagOutput( line , "sub" ) + } + else if( is_list_item ) + { + gsub( "^\\* " , "" , line ) + tagOutput( line , "li" ) + } + else if( is_numbered_item ) + { + gsub( "^\\([[:digit:]][[:digit:]]*\\) " , "" , line ) + tagOutput( line , "LI" ) + } + else if( is_heading ) + { + printf( "

%s

\n" , line ) + } + else if( is_sub_heading ) + { + printf( "

%s

\n" , line ) + } + else if( !is_heading_line && !is_sub_heading_line ) + { + output( line ) + } +} + +{ + if( NR != 1 ) + process( previous , $0 ) + previous = $0 +} + +END { + process( previous , "" ) + printf( "\n" ) + printf( "\n" ) +} ' +} + +# == + +AugmentLists() +{ +${awk} -v item_tag="${1}" -v list_tag="${2}" ' +{ + line = $0 + is_list_item = match( line , "^<" item_tag ">.*$" ) + + if( is_list_item && !in_list ) + printf( "<%s>\n" , list_tag ) + else if( in_list && !is_list_item ) + printf( "\n" , list_tag ) + + print + in_list = is_list_item +} ' +} + +# == + +Elide() +{ +${awk} -v tag="${1}" ' +{ + line = $0 + is_tag_line = match( line , "^<" tag ">.*$" ) + + core = substr( line , length(tag)+3 , length(line)-length(tag)-length(tag)-5 ) + + if( is_tag_line && !in_tag ) + printf( "<%s>%s" , tag , core ) + else if( is_tag_line && in_tag ) + printf( "\n%s" , core ) + else if( !is_tag_line && in_tag ) + printf( "\n%s\n" , tag , line ) + else + print line + + in_tag = is_tag_line +} ' +} + +# == + +Compress() +{ +${awk} ' +function process( previous , line , next_ ) +{ + re_blank = "^


$" + re_heading = "^<[Hh][[:digit:]]>" + re_pre_start = "^

"
+	re_pre_end = "
$" + if( match(line,re_blank) && ( match(next_,re_heading) || match(previous,re_heading) ) ) + { + } + else if( match(line,re_blank) && match(next_,re_pre_start) ) + { + } + else + { + print line + } +} +{ + if( NR >= 2 ) + process( l2 , l1 , $0 ) + l2 = l1 + l1 = $0 +} +END { + process( l2 , l1 , "" ) + process( l1 , "" , "" ) +} +' +} + +# == + +cat "${file}" | Main "${title}" | Compress | AugmentLists li bl | AugmentLists LI ol | Elide "sub" | Elide "pre" + diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..04f46c4 --- /dev/null +++ b/config.h.in @@ -0,0 +1,41 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if you have the glob function. */ +#undef HAVE_GLOB + +/* Define if you have the socket function. */ +#undef HAVE_SOCKET + +/* Define if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define if you have the header file. */ +#undef HAVE_NDIR_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_DIR_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_NDIR_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Name of package */ +#undef PACKAGE + +/* Version number of package */ +#undef VERSION + diff --git a/configure b/configure new file mode 100755 index 0000000..6b3426f --- /dev/null +++ b/configure @@ -0,0 +1,2222 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +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' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +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 + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # 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 ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$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" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) 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) + # 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 << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$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) + 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 ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + 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 "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# 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 + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=src/main/gsmtp.h + +# 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_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + 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 "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# 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 "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + +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 + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# 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 +# 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 $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:556: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/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 + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +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 "$ac_t""$INSTALL" 1>&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_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6 +echo "configure:609: checking whether build environment is sane" >&5 +# Just in case +sleep 1 +echo timestamp > conftestfile +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftestfile` + fi + if test "$*" != "X $srcdir/configure conftestfile" \ + && test "$*" != "X conftestfile $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { echo "configure: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" 1>&2; exit 1; } + fi + + test "$2" = conftestfile + ) +then + # Ok. + : +else + { echo "configure: error: newly created file is older than distributed files! +Check your system clock" 1>&2; exit 1; } +fi +rm -f conftest* +echo "$ac_t""yes" 1>&6 +if test "$program_transform_name" = s,x,x,; then + program_transform_name= +else + # Double any \ or $. echo might interpret backslashes. + cat <<\EOF_SED > conftestsed +s,\\,\\\\,g; s,\$,$$,g +EOF_SED + program_transform_name="`echo $program_transform_name|sed -f conftestsed`" + rm -f conftestsed +fi +test "$program_prefix" != NONE && + program_transform_name="s,^,${program_prefix},; $program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" + +# sed with no file args requires a program. +test "$program_transform_name" = "" && program_transform_name="s,x,x," + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:666: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 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 conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + +PACKAGE=emailrelay + +VERSION=0.9.1 + +if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then + { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; } +fi +cat >> confdefs.h <> confdefs.h <&6 +echo "configure:712: checking for working aclocal" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (aclocal --version) < /dev/null > /dev/null 2>&1; then + ACLOCAL=aclocal + echo "$ac_t""found" 1>&6 +else + ACLOCAL="$missing_dir/missing aclocal" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working autoconf""... $ac_c" 1>&6 +echo "configure:725: checking for working autoconf" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (autoconf --version) < /dev/null > /dev/null 2>&1; then + AUTOCONF=autoconf + echo "$ac_t""found" 1>&6 +else + AUTOCONF="$missing_dir/missing autoconf" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working automake""... $ac_c" 1>&6 +echo "configure:738: checking for working automake" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (automake --version) < /dev/null > /dev/null 2>&1; then + AUTOMAKE=automake + echo "$ac_t""found" 1>&6 +else + AUTOMAKE="$missing_dir/missing automake" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working autoheader""... $ac_c" 1>&6 +echo "configure:751: checking for working autoheader" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (autoheader --version) < /dev/null > /dev/null 2>&1; then + AUTOHEADER=autoheader + echo "$ac_t""found" 1>&6 +else + AUTOHEADER="$missing_dir/missing autoheader" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working makeinfo""... $ac_c" 1>&6 +echo "configure:764: checking for working makeinfo" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (makeinfo --version) < /dev/null > /dev/null 2>&1; then + MAKEINFO=makeinfo + echo "$ac_t""found" 1>&6 +else + MAKEINFO="$missing_dir/missing makeinfo" + echo "$ac_t""missing" 1>&6 +fi + + + + + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:791: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +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 $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:821: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +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 $# -gt 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 + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:872: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:904: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 915 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:920: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:946: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:951: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:979: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&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 + +for ac_prog in $CCC c++ g++ gcc CC cxx cc++ 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 $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1015: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CXX="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CXX="$ac_cv_prog_CXX" +if test -n "$CXX"; then + echo "$ac_t""$CXX" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$CXX" && break +done +test -n "$CXX" || CXX="gcc" + + +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:1047: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works" >&5 + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +cat > conftest.$ac_ext << EOF + +#line 1058 "configure" +#include "confdefs.h" + +int main(){return(0);} +EOF +if { (eval echo configure:1063: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cxx_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cxx_cross=no + else + ac_cv_prog_cxx_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cxx_works=no +fi +rm -fr conftest* +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +echo "$ac_t""$ac_cv_prog_cxx_works" 1>&6 +if test $ac_cv_prog_cxx_works = no; then + { echo "configure: error: installation or configuration problem: C++ compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:1089: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cxx_cross" 1>&6 +cross_compiling=$ac_cv_prog_cxx_cross + +echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6 +echo "configure:1094: checking whether we are using GNU C++" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.C <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gxx=yes +else + ac_cv_prog_gxx=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gxx" 1>&6 + +if test $ac_cv_prog_gxx = yes; then + GXX=yes +else + GXX= +fi + +ac_test_CXXFLAGS="${CXXFLAGS+set}" +ac_save_CXXFLAGS="$CXXFLAGS" +CXXFLAGS= +echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6 +echo "configure:1122: checking whether ${CXX-g++} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.cc +if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then + ac_cv_prog_cxx_g=yes +else + ac_cv_prog_cxx_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cxx_g" 1>&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS="$ac_save_CXXFLAGS" +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1156: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&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 +# 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 $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1195: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/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 + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +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 "$ac_t""$INSTALL" 1>&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_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# Extract the first word of "doxygen", so it can be a program name with args. +set dummy doxygen; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1250: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_HAVE_DOXYGEN'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$HAVE_DOXYGEN"; then + ac_cv_prog_HAVE_DOXYGEN="$HAVE_DOXYGEN" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_HAVE_DOXYGEN="yes" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +HAVE_DOXYGEN="$ac_cv_prog_HAVE_DOXYGEN" +if test -n "$HAVE_DOXYGEN"; then + echo "$ac_t""$HAVE_DOXYGEN" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +# Extract the first word of "man2html", so it can be a program name with args. +set dummy man2html; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1279: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_HAVE_MAN2HTML'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$HAVE_MAN2HTML"; then + ac_cv_prog_HAVE_MAN2HTML="$HAVE_MAN2HTML" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_HAVE_MAN2HTML="yes" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +HAVE_MAN2HTML="$ac_cv_prog_HAVE_MAN2HTML" +if test -n "$HAVE_MAN2HTML"; then + echo "$ac_t""$HAVE_MAN2HTML" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + + +echo $ac_n "checking how to run the C++ preprocessor""... $ac_c" 1>&6 +echo "configure:1308: checking how to run the C++ preprocessor" >&5 +if test -z "$CXXCPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CXXCPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + CXXCPP="${CXX-g++} -E" + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1326: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CXXCPP=/lib/cpp +fi +rm -f conftest* + ac_cv_prog_CXXCPP="$CXXCPP" +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross +fi +fi +CXXCPP="$ac_cv_prog_CXXCPP" +echo "$ac_t""$CXXCPP" 1>&6 + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1351: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1364: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + 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 < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + 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 < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#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); } + +EOF +if { (eval echo configure:1434: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 +echo "configure:1462: checking for $ac_hdr that defines DIR" >&5 +if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include <$ac_hdr> +int main() { +DIR *dirp = 0; +; return 0; } +EOF +if { (eval echo configure:1475: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_dirent_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then +echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 +echo "configure:1500: checking for opendir in -ldir" >&5 +ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldir $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -ldir" +else + echo "$ac_t""no" 1>&6 +fi + +else +echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 +echo "configure:1544: checking for opendir in -lx" >&5 +ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lx $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -lx" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 +echo "configure:1589: checking whether time.h and sys/time.h may both be included" >&5 +if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +int main() { +struct tm *tp; +; return 0; } +EOF +if { (eval echo configure:1603: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_time=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_time=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_time" 1>&6 +if test $ac_cv_header_time = yes; then + cat >> confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + +for ac_hdr in unistd.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1627: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1637: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in sys/time.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1667: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1677: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1704: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +for ac_func in glob +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1739: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* 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(); + +int main() { + +/* 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 +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1770: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + +for ac_func in socket +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1797: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* 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(); + +int main() { + +/* 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 +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1828: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + +trap '' 1 2 15 +cat > confcache <<\EOF +# 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. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# 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 \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile src/Makefile src/glib/Makefile src/gnet/Makefile src/main/Makefile src/win32/Makefile lib/Makefile lib/gcc2.95/Makefile lib/msvc6.0/Makefile bin/Makefile doc/Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@PACKAGE@%$PACKAGE%g +s%@VERSION@%$VERSION%g +s%@ACLOCAL@%$ACLOCAL%g +s%@AUTOCONF@%$AUTOCONF%g +s%@AUTOMAKE@%$AUTOMAKE%g +s%@AUTOHEADER@%$AUTOHEADER%g +s%@MAKEINFO@%$MAKEINFO%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CC@%$CC%g +s%@CXX@%$CXX%g +s%@RANLIB@%$RANLIB%g +s%@HAVE_DOXYGEN@%$HAVE_DOXYGEN%g +s%@HAVE_MAN2HTML@%$HAVE_MAN2HTML%g +s%@CXXCPP@%$CXXCPP%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# 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_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # 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" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# 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='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. 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.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# 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.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..71898b7 --- /dev/null +++ b/configure.in @@ -0,0 +1,46 @@ +dnl +dnl Copyright (C) 2001 Graeme Walker +dnl +dnl This program is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU General Public License +dnl as published by the Free Software Foundation; either +dnl version 2 of the License, or (at your option) any later +dnl version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +dnl +dnl === +dnl Process this file with autoconf to produce a configure script. +AC_INIT(src/main/gsmtp.h) +AM_INIT_AUTOMAKE(emailrelay,0.9.1) +AM_CONFIG_HEADER(config.h) +AC_LANG_CPLUSPLUS + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CXX +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_CHECK_PROG(HAVE_DOXYGEN,doxygen,yes) +AC_CHECK_PROG(HAVE_MAN2HTML,man2html,yes) + +dnl Checks for libraries. + +dnl Checks for header files, functions and typedefs +AC_HEADER_STDC +AC_HEADER_DIRENT +AC_HEADER_TIME +AC_CHECK_HEADERS(unistd.h) +AC_CHECK_HEADERS(sys/time.h) +AC_TYPE_SIZE_T +AC_CHECK_FUNCS(glob) +AC_CHECK_FUNCS(socket) + +AC_OUTPUT(Makefile src/Makefile src/glib/Makefile src/gnet/Makefile src/main/Makefile src/win32/Makefile lib/Makefile lib/gcc2.95/Makefile lib/msvc6.0/Makefile bin/Makefile doc/Makefile) diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..4c118be --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,39 @@ +EXTRA_DIST = developer.txt reference.txt userguide.txt index.html emailrelay.1 emailrelay-poke.1 index.html +noinst_SCRIPTS = .dox +pkgdata_DATA = readme.html developer.html reference.html userguide.html man.html index.html +CLEANFILES = $(pkgdata_DATA) $(noinst_SCRIPTS) html + +SUFFIXES = .txt .html + +filter=$(srcdir)/../bin/emailrelay-filter.sh +converter=$(srcdir)/../bin/txt2html.sh + +.txt.html: + $(converter) $< > $*.html + +.dox: $(filter) + if test "$(HAVE_DOXYGEN)" = "yes" ; then cat $(top_srcdir)/src/main/doxygen.cfg | sed "s:__TOP__:$(top_srcdir):g" | doxygen - && touch .dox ; else echo no doxygen ; fi + +man.html: emailrelay.1 + if test "$(HAVE_MAN2HTML)" = "yes" ; then man2html emailrelay.1 > man.html ; else echo no man2html ; fi + +$(filter): $(filter)_ + @cp $(filter)_ $(filter) + @chmod ugo+x $(filter) + +$(converter): $(converter)_ + @cp $(converter)_ $(converter) + @chmod ugo+x $(converter) + +developer.html reference.html userguide.html: $(converter) + +readme.html: $(top_srcdir)/README $(converter) + $(converter) $(top_srcdir)/README > readme.html + +install-data-local: + $(mkinstalldirs) $(destdir)$(mandir)/man1 + $(INSTALL) $(top_srcdir)/doc/emailrelay.1 $(destdir)$(mandir)/man1/emailrelay.1 + $(INSTALL) $(top_srcdir)/doc/emailrelay-poke.1 $(destdir)$(mandir)/man1/emailrelay-poke.1 + $(mkinstalldirs) $(destdir)$(pkgdatadir)/html + if test "$(HAVE_DOXYGEN)" = "yes" ; then for file in html/* ; do $(INSTALL) $$file $(destdir)$(pkgdatadir)/$$file ; done ; fi + diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 0000000..0eb93b1 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,239 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +EXTRA_DIST = developer.txt reference.txt userguide.txt index.html emailrelay.1 emailrelay-poke.1 index.html +noinst_SCRIPTS = .dox +pkgdata_DATA = readme.html developer.html reference.html userguide.html man.html index.html +CLEANFILES = $(pkgdata_DATA) $(noinst_SCRIPTS) html + +SUFFIXES = .txt .html + +filter = $(srcdir)/../bin/emailrelay-filter.sh +converter = $(srcdir)/../bin/txt2html.sh +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = +SCRIPTS = $(noinst_SCRIPTS) + +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DATA = $(pkgdata_DATA) + +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +.SUFFIXES: .html .txt +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps doc/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +install-pkgdataDATA: $(pkgdata_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) + @list='$(pkgdata_DATA)'; for p in $$list; do \ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkgdatadir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkgdatadir)/$$p; \ + else if test -f $$p; then \ + echo " $(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p"; \ + $(INSTALL_DATA) $$p $(DESTDIR)$(pkgdatadir)/$$p; \ + fi; fi; \ + done + +uninstall-pkgdataDATA: + @$(NORMAL_UNINSTALL) + list='$(pkgdata_DATA)'; for p in $$list; do \ + rm -f $(DESTDIR)$(pkgdatadir)/$$p; \ + done +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = doc + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: install-pkgdataDATA install-data-local +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-pkgdataDATA +uninstall: uninstall-am +all-am: Makefile $(SCRIPTS) $(DATA) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) + + +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: uninstall-pkgdataDATA install-pkgdataDATA tags distdir info-am \ +info dvi-am dvi check check-am installcheck-am installcheck \ +install-exec-am install-exec install-data-local install-data-am \ +install-data install-am install uninstall-am uninstall all-redirect \ +all-am all installdirs mostlyclean-generic distclean-generic \ +clean-generic maintainer-clean-generic clean mostlyclean distclean \ +maintainer-clean + + +.txt.html: + $(converter) $< > $*.html + +.dox: $(filter) + if test "$(HAVE_DOXYGEN)" = "yes" ; then cat $(top_srcdir)/src/main/doxygen.cfg | sed "s:__TOP__:$(top_srcdir):g" | doxygen - && touch .dox ; else echo no doxygen ; fi + +man.html: emailrelay.1 + if test "$(HAVE_MAN2HTML)" = "yes" ; then man2html emailrelay.1 > man.html ; else echo no man2html ; fi + +$(filter): $(filter)_ + @cp $(filter)_ $(filter) + @chmod ugo+x $(filter) + +$(converter): $(converter)_ + @cp $(converter)_ $(converter) + @chmod ugo+x $(converter) + +developer.html reference.html userguide.html: $(converter) + +readme.html: $(top_srcdir)/README $(converter) + $(converter) $(top_srcdir)/README > readme.html + +install-data-local: + $(mkinstalldirs) $(destdir)$(mandir)/man1 + $(INSTALL) $(top_srcdir)/doc/emailrelay.1 $(destdir)$(mandir)/man1/emailrelay.1 + $(INSTALL) $(top_srcdir)/doc/emailrelay-poke.1 $(destdir)$(mandir)/man1/emailrelay-poke.1 + $(mkinstalldirs) $(destdir)$(pkgdatadir)/html + if test "$(HAVE_DOXYGEN)" = "yes" ; then for file in html/* ; do $(INSTALL) $$file $(destdir)$(pkgdatadir)/$$file ; done ; fi + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/doc/developer.txt b/doc/developer.txt new file mode 100644 index 0000000..56d962a --- /dev/null +++ b/doc/developer.txt @@ -0,0 +1,161 @@ +E-MailRelay Developer reference +=============================== + +Module structure +---------------- +There are two C++ libraries in the E-MailRelay code: "glib" provides low-level +classes for file-system abstraction, date and time representation, string +utility functions, logging, command line parsing etc., and "gnet" provides +network classes using the Berkley socket and Winsock APIs. Both libraries +are portable between POSIX-like systems (eg. Linux) and Windows. + +The application-level classes are implemented within the "GSmtp" namespace. +The key classes in this namespace are "ClientProtocol", "ServerProtocol" and +"MessageStore". The protocol and message-store functionality is brought +together by the high-level "GSmtp::Server" and "GSmtp::Client" classes. + +Under Windows the "gnet" library needs to create hidden GUI windows in +order to receive network events. (Windows has historically built network +event processing on top of the GUI event system, rather then implementing +both GUI and network event handling on top of a generic event notification +system as POSIX systems do.) The Windows GUI and event classes are put into +a separate "src/win32" directory. + +For a quick tour of the code look at the following headers: +* src/glib/gpath.h +* src/glib/gstr.h +* lib/gcc2.95/sstream +* src/gnet/gevent.h +* src/gnet/gaddress.h +* src/gnet/gsocket.h +* src/gnet/gserver.h +* src/main/gmessagestore.h +* src/main/gserverprotocol.h +* src/main/gsmtpserver.h + + +Directory structure +------------------- + +* src + + Parent directory for source code. + +* src/glib + + A low-level class library, including classes for file-system abstraction, + date and time, string utility functions, logging, command line parsing etc. + +* src/gnet + + A network library using Berkley sockets or Winsock. + +* src/win32 + + Additional classes for windows event processing. + +* src/main + + Application-level classes for E-MailRelay. + +* lib + + Parent directory for ANSI C++ fixes + +* lib/gcc2.95 + + Standard headers which are missing in gcc2.95 + +* lib/msvc6.0 + + Standard headers which are missing (or broken) in msvc6.0 + +Portability +----------- +The E-MailRelay code is written in ANSI C++, using the following +language/library features: +* templates +* exceptions +* namespaces +* "explicit" +* STL +* std::string & std::stringstream +* dynamic_cast<> & RTTI +* static_cast<> & const_cast<> + +but not: +* defaulted template parameters +* templated member functions +* covariant return +* "mutable" + +The header files "gdef.h" in "src/glib", and "gnet.h" in "src/gnet" are +intended to be used to fix up portability issues such as missing standard +types, non-standard system headers etc. Conditional compilation is not +used outside of these headers (with the odd exception). + +Deficiencies in the ANSI C++ headers files provided by the compiler +are fixed up in the "lib" directory tree. For example, the msvc6.0 +compiler sometimes does not put its names into the "std" namespace, +even though the std-namespace headers are used. This can be worked round +by additional "using" declarations in the "lib/msvc6.0" headers. +These work-rounds are kept out of the "src" tree because they are likely +to be fixed in later compiler releases. Standards-compliant compilers +should not need to include any headers from the "lib" directory tree. + +Windows/unix portability is generally addressed by providing a common +class declaration with two implementations. Where necessary a pimple +pattern is used to hide the system-specific parts of the declaration. + +A good example is the "GDirectory" class used for iterating through files +in a directory. The header file "src/glib/gdirectory.h" is common to both +systems, but two implementations are provided in "gdirectory_unix.cpp" +and "gdirectory_win32.cpp". The unix implementation uses opendir() and +glob(), while the windows implementation uses FindFirstFile(). + +Sometimes only parts of the implementation are system-specific. In these +cases there are three source files per header. For example, "gsocket.cpp", +"gsocket_win32.cpp" and "gsocket_unix.cpp" in the "src/gnet" directory. + +Porting +------- +If trying a port using a good ANSI C++ compiler then start by removing +files from the "lib/gcc2.95" directory (or edit makefiles to remove the +include path), and then review the following header files: "src/glib/gdef.h", +"src/gnet/gnet.h", "src/glib/gmemory.h". + +The unix (ie. POSIX-like) implementation of the directory iteration class +uses glob(). This functionality is only used by the message store code in +the "src/main" directory. If glob() presents a porting challenge then +the filename matching could be removed from the directory iterator and +moved into the message store class. + +IPv6 +---- +IPv6 is supported at compile-time by selecting source files in the +"src/gnet" directory ending "_ipv6.cpp" rather than "_ipv4.cpp". +The code should be regarded as experimental. + +Doxygen +------- +The commenting style used in header files is compatible with Doxygen +if passed through the simple awk-based preprocessor "emailrelay-filter.sh". +A "make" in the "doc" directory will run Doxygen if it is found on +your path. + +Windows build +------------- +A simple project file "emailrelay.dsp" for msvc6.0 is provided in the +"src/main" directory. + +Style +----- +Tabs are used for indenting, not multiple spaces, but only at the left +hand edge, and never within a line. This allows the reader to choose +how deep the indenting should be (appropriate to their window size) by +setting the editor's tabstop. Using spaces does not allow the reader this +freedom. + + + +Copyright (C) 2001 Graeme Walker . All rights reserved. diff --git a/doc/emailrelay-poke.1 b/doc/emailrelay-poke.1 new file mode 100644 index 0000000..a8ff5e1 --- /dev/null +++ b/doc/emailrelay-poke.1 @@ -0,0 +1,35 @@ +.\" +.\" Copyright (C) 2001 Graeme Walker +.\" +.\" This program is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU General Public License +.\" as published by the Free Software Foundation; either +.\" version 2 of the License, or (at your option) any later +.\" version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.TH EMAILRELAY 1 local +.SH NAME +emailrelay-poke \- e-mail transfer agent poke utility +.SH SYNOPSIS +.B emailrelay-poke +[ admin-port [ admin-command ] ] +.SH DESCRIPTION +.I emailrelay-poke +is a small utility which connects to the +.I emailrelay +administration interface and executes the +.B flush +command. +.SH SEE ALSO +.BR emailrelay (1) +.SH AUTHOR +Graeme Walker, mailto:graeme_walker@users.sourceforge.net diff --git a/doc/emailrelay.1 b/doc/emailrelay.1 new file mode 100644 index 0000000..d6b12fa --- /dev/null +++ b/doc/emailrelay.1 @@ -0,0 +1,156 @@ +.\" +.\" Copyright (C) 2001 Graeme Walker +.\" +.\" This program is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU General Public License +.\" as published by the Free Software Foundation; either +.\" version 2 of the License, or (at your option) any later +.\" version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.TH EMAILRELAY 1 local +.SH NAME +emailrelay \- e-mail transfer agent +.SH SYNOPSIS +.B emailrelay +[OPTIONS] +.LP +.B emailrelay +--as-server +.LP +.B emailrelay +--as-client +.I server-address +.SH DESCRIPTION +.I emailrelay +is an simple e-mail message transfer agent. It is intended to be used +on stand-alone machines which have a dial-up connection to an ISP. +.LP +It runs in two modes: a storage deamon +.RI ( --as-server ) +and a forwarding +agent +.RI ( --as-client ). +The storage daemon is an SMTP server which stores e-mail +messages in a local spool directory. The forwarding agent acts as an +SMTP client, which passes the spooled e-mail messages on to an ISP's SMTP +server. +.SH OPTIONS +.TP +.B \-a,--admin \fIadmin-port\fR +Enables the administration interface and specifies its listening port number. +.TP +.B \-d,--as-server +Equivalent to \fI--close-stderr\fR \fI--log\fR. +.TP +.B \-e,--close-stderr +Closes the standard error stream when daemonising. +.TP +.B \-f,--forward +Forwards stored mail on startup (requires \fI--forward-to\fR). +.TP +.B \-h,--help +Displays help text and exits. +.TP +.B \-i,--pid-file \fIpid-file\fR +Records the daemon process-id in the given file. +.TP +.B \-l,--log +Writes log information on standard error (if open) and syslog (if not disabled). +.TP +.B \-n,--no-syslog +Disables syslog output. +.TP +.B \-o,--forward-to \fIhost:port\fR +Specifies the remote smtp server (required by \fI--forward\fR and \fI--admin\fR). +.TP +.B \-p,--port \fIport\fR +Specifies the smtp listening port number. +.TP +.B \-q,--as-client \fIhost:port\fR +Equivalent to \fI--no-syslog\fR \fI--no-daemon\fR \fI--log\fR \fI--dont-serve\fR \fI--forward\fR \fI--forward-to\fR. +.TP +.B \-r,--remote-clients +Allows remote clients to connect. +.TP +.B \-s,--spool-dir \fIdir\fR +Specifies the spool directory (default is \fI/usr/local/var/spool/emailrelay\fR). +.TP +.B \-t,--no-daemon +Does not detach from the terminal. +.TP +.B \-v,--verbose +Generates more verbose logging (if compiled-in and logging enabled and stderr open). +.TP +.B \-x,--dont-serve +Stops the process acting as a server (usually used with \fI--forward\fR). +.TP +.B \-V,--version +Displays version information and exits. +.SH "DIAGNOSTICS" +If the +.IR --log , +.I --as-client +or +.I --as-server +switches are in force then warning and error messages +are sent to +.I syslog +(using the +.BR LOG_MAIL +facility) and to +.IR stderr . +Output to +.I syslog +can be disabled with +.IR --no-syslog , +and output to stderr can be disabled in daemon mode with +.IR --close-stderr . +.PP +Failed e-mail messages are kept in the spool directory and given +a +.I .bad +filename suffix. The failure reason is usually recorded within the +envelope file iteself. +.SH FILES +.IP \(bu 2 +/usr/local/sbin/emailrelay +.IP \(bu 2 +/usr/local/libexec/emailrelay-poke +.IP \(bu 2 +/usr/local/libexec/emailrelay.sh +.IP \(bu 2 +/usr/local/share/emailrelay/*.html +.IP \(bu 2 +/usr/local/share/emailrelay/html/*.html +.IP \(bu 2 +/usr/local/share/emailrelay/emailrelay-notify.sh +.IP \(bu 2 +/usr/local/share/emailrelay/emailrelay-deliver.sh +.IP \(bu 2 +/usr/local/man/man1/emailrelay.1 +.IP \(bu 2 +/usr/local/man/man1/emailrelay-poke.1 +.IP \(bu 2 +/usr/local/var/spool/emailrelay/emailrelay.*.envelope +.IP \(bu 2 +/usr/local/var/spool/emailrelay/emailrelay.*.content +.SH SEE ALSO +E-MailRelay user guide +.br +E-MailRelay reference +.br +.BR syslog (3), +.BR pppd (8), +.BR init.d (7) +.BR emailrelay-poke (1), +.SH AUTHOR +Graeme Walker, mailto:graeme_walker@users.sourceforge.net diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..e0cd002 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,15 @@ + + + + +

E-MailRelay Documentation

+ +
  • Readme
  • +
  • User guide
  • +
  • Reference manual
  • +
  • Notes for developers
  • +
  • Source code documentation (generated by doxygen)
  • +
  • Man page (generated by man2html)
  • +
    + + diff --git a/doc/reference.txt b/doc/reference.txt new file mode 100644 index 0000000..13fb178 --- /dev/null +++ b/doc/reference.txt @@ -0,0 +1,190 @@ +E-MailRelay Reference Manual +============================ + +Document +-------- +This is the E-MailRelay reference guide. It contains material which is supplementary +to the user guide. + +Usage +----- +The "emailrelay" program supports the following command-line usage: + + emailrelay [ [ ...]] + +where is: + +* --version (-V) + Displays version information and exits. + +* --admin (-a) + Enables the administration interface and specifies its listening port number. + +* --as-server (-d) + Equivalent to "--close-stderr --log". + +* --close-stderr (-e) + Closes the standard error stream when daemonising. + +* --forward (-f) + Forwards stored mail on startup (requires --forward-to). + +* --help (-h) + Displays help text and exits. + +* --pid-file (-i) + Records the daemon process-id in the given file. + +* --log (-l) + Writes log information on standard error (if open) and syslog (if not disabled). + +* --no-syslog (-n) + Disables syslog output. + +* --forward-to (-o) + Specifies the remote smtp server (required by --forward and --admin). + +* --port (-p) + Specifies the smtp listening port number. + +* --as-client (-q) + Equivalent to "--no-syslog --no-daemon --log --dont-serve --forward --forward-to". + +* --remote-clients (-r) + Allows remote clients to connect. + +* --spool-dir (-s) + Specifies the spool directory (default is "/usr/local/var/spool/emailrelay"). + +* --no-daemon (-t) + Does not detach from the terminal. + +* --verbose (-v) + Generates more verbose logging (if compiled-in and logging enabled and stderr open). + +* --dont-serve (-x) + Stops the process acting as a server (usually used with --forward). + +If no command-line switches are supplied at all then the default +behaviour is: +* to run as a daemon, detached from the terminal +* listen on the standard SMTP port (25) +* store e-mail messages in "/usr/local/var/spool/emailrelay" +* reject connections from remote clients +* disable the administration interface +* generate no logging or diagnostic messages + +To foward spooled messages to the ISP the command-line switch "--as-client" +is provided to run the program... +* in foreground, exiting when all spooled mail has been processed +* forwarding spooled mail from "/usr/local/var/spool/emailrelay" +* without listening on any port +* with error, warning and information messages sent to stderr +* without using syslog + +The "--as-server" switch makes sure that logging is enabled and that +the standard error stream is closed. + +Message store +------------- +Mail messages are stored as text files in the configured spool directory. Each +message is represented as an envelope file and a content file. The envelope +file contains parameters relevant to the SMTP dialogue, and the content file +contains the RFC822 headers and body text. Note that the content is largely +ignored by the SMTP protocol. + +The filenames used in the message store have a prefix of "emailrelay", followed +by a process-id and sequence number, followed by "envelope" or "content". The +envelope files then have an additional suffix to implement a simple locking +scheme. + +The envelope suffixes are: +* ".new" -- while the envelope is first being written +* -- while the message is spooled +* ".busy" -- while the message is being forwarded +* ".bad" -- if the message cannot be forwarded +* ".local" -- for copies of the envelope file for delivery to local recipeints + +Copies of the content file for delivery to local recipeints will also have +a "local" suffix. + +If a message cannot be forwarded the envelope file is given a "bad" suffix, +and the failure reason is written into the file. + +SMTP issues +----------- +Local delivery: + + E-MailRelay will reject all local recipients, with the exception of "postmaster". + This is in line with its intended purpose as a simple mail relay, rather than + a fully-fledged routing MTA. Any addressee (except "postmaster") without an "at" + sign (@) will be rejected at the time the message is submitted by the e-mail + front-end. + + Delivery of mail to a local "postmaster" is a feature of E-MailRelay which is + provided for completeness and for comformance to the SMTP specification. It is + only relevant if you are in the habit of sending mail to yourself as "postmaster"; + mail to "postmaster" from an external source is not processed by E-MailRelay + and should be delivered normally. + + Note that E-MailRelay daemon does not actually deliver mail to the postmaster + mailbox. All it does is create an envelope and content file in the spool directory + with a ".local" suffix. Some external system, such as a shell script run + from cron calling "procmail", should be used to process the ".local" files. + An example script is provided. + +Timeouts: + + Client-side timeouts are not implemented. If the ISP server is very slow then + in a typical setup the dial-up line will be dropped due to inactivity. This + will have the desired effect of aborting the message submission. + +Message loops: + + Message loops are not detected. + +Eight bit messages: + + The 8BITMIME SMTP extension is supported, however no attempt is made to + re-encode 8-bit messages into 7-bit messages if the downstream server + does not. + +Administration interface +------------------------ +If enabled, the server will provide a network interface for performing administration +tasks. This is a simple command-line interface which is compatible with telnet. + +Currently the only supported command is "flush", which tries to forward spooled mail +to the configured dowstream SMTP server. The downstream server address must have been +defined on the "emailrelay" command line at start-up using the "--forward-to" switch; +it cannot be specified through the administration interface. + +Security issues +--------------- +E-MailRelay runs with a umask of 177. It does not call exec() or system(). It does not +change its effective userid. No configuration parameters can be changed through the +administrative interface. By default connections to the SMTP and administrative ports +will be rejected if they come from remote machines. + +Files +----- +By default "make install" installs the following files: +* /usr/local/sbin/emailrelay +* /usr/local/libexec/emailrelay-poke +* /usr/local/libexec/emailrelay.sh +* /usr/local/var/spool/emailrelay/empty_file +* /usr/local/share/emailrelay/emailrelay-notify.sh +* /usr/local/share/emailrelay/emailrelay-deliver.sh + +This directory structure is constrained by the autoconf and GNU standards. Preferred +locations would be something like this: +* /usr/sbin/emailrelay +* /opt/emailrelay/bin/emailrelay-poke +* /sbin/init.d/emailrelay.sh +* /var/spool/emailrelay/ +* /opt/emailrelay/examples/emailrelay-notify.sh +* /opt/emailrelay/examples/emailrelay-deliver.sh + + + +Copyright (C) 2001 Graeme Walker . All rights reserved. diff --git a/doc/userguide.txt b/doc/userguide.txt new file mode 100644 index 0000000..4fa0368 --- /dev/null +++ b/doc/userguide.txt @@ -0,0 +1,188 @@ +E-MailRelay User Guide +====================== + +Document +-------- +This document is the user guide for E-MailRelay V0.9.1. + +What is it? +----------- +E-MailRelay is a simple e-mail store-and-forward transfer agent. It's a program +which runs in the background and accepts e-mail front-ends (KMail, Mutt, Netscape etc.), +stores the messages on the hard disk, and when next connected to the Internet forwards +them to a downstream SMTP server for onward delivery. + +The E-MailRelay program ("emailrelay") can run in two modes: a storage daemon, or +a forwarding agent. As a storage daemon it waits for connections from your +e-mail front-end and stores the mail which it receives in a spool directory. +As a forwarding agent it pulls messages out of the spool directory and passes +them on to a remote server -- typically an ISP mail server. + +E-MailRelay uses the Simple Message Transfer Protocol (SMTP). When running as a +storage daemon it acts as an SMTP server, and when running as a forwarding agent it +acts as an SMTP client. + +E-MailRelay runs on GNU/Linux and Windows. + +What it's not +------------- +E-MailRelay does not get involved in processing incoming e-mail messages; it only +operates on outgoing messages. Incoming e-mail messages will probably be retrieved +from your ISP by your e-mail front-end program, using the POP3 or IMAP protocols. + +E-MailRelay is not a routing MTA. It is designed to be used in situations where all +outgoing e-mail message go out to the Internet, so it is not an appropriate choice +if you send e-mail to other people who use the same machine or to people who are +on the same local area network. + +Why use it? +----------- +The motivation for developing E-MailRelay is that e-mail store-and-forward using +SMTP is conceptually a very simple thing, but most popular MTAs are complex. +E-MailRelay just stores messages and then forwards them on to your ISP, whereas a +fully-featured MTA does clever things with address re-writing, message routing, +local delivery, loop detection, fancy DNS lookups etc. + +In particular the configuration of some popular MTAs is notoriously complex +and arcane, whereas the only thing the E-MailRelay system needs is the name of +your ISP's mail server. + +With the move away from dial-up Internet connections a simple store-and-forward +MTA like E-MailRelay becomes more relevant to mobile computers and PDAs which +need to store mail while away from the network. + +The source code for E-MailRelay is well-structured, portable ANSI C++, without any +thrid-party library dependencies. It could therefore be the basis for other SMTP +projects such as, for example, a protocol bridge between SMTP and proprietary mail +transfer protocols used in private e-mail networks. + +Running E-MailRelay +------------------- +To run E-MailRelay as a storage daemon use the command: + + emailrelay --as-server + +To run E-MailRelay as a forwarding agent (once connected to the Internet), use a +command like this: + + emailrelay --as-client mail.myisp.net:smtp + +where "mail.myisp.net" is replaced with the name of your ISP's SMTP server. + +(E-MailRelay can also combine the two roles of storage daemon and forwarding +client in one process. In this mode the storage service is always available, +but the forwarding activity has to be triggered via the "administration" +interface. The administration interface is a command-line network service +compatible with telnet. The interface must be enabled using the "--admin" +command line switch, specifying the listening port number.) + +For more information on the command-line options refer to the reference guide +or run: + + emailrelay --help + + +Starting the daemon at boot-time +-------------------------------- +The standard installation of E-MailRelay (using "make install") puts most of the +files into the right places, but it does not set things up so that the daemon +starts at boot time, or that e-mail gets forwarded automatically when you +connect to the Internet. You have to do those bits yourself because of the +differences between the various GNU/Linux distributions. + +Many systems, including the most popular GNU/Linux distributions, use the +System-V mechanism for starting daemons at boot time. The directory "/etc/init.d" +(or "/sbin/init.d") contains a start/stop script for each daemon process, and +then symbolic links in the "rc.d" subdirectories control which scripts are +run when entering or leaving a particular run-level (). The links point back +into the start/stop script in the parent directory, using a "S" prefix for the +starting link, and a "K" prefix for the stopping link. The numeric part of +the link name determines the order in which the links are called. + +Before you start you will need to know where your "init.d" directory can be found +and what your default run level is: + + $ ls -d /*/init.d + $ runlevel | awk '{print $2}' + +Assuming these are "/etc/init.d" and "5" you should (as root) copy the E-MailRelay +start/stop script into "/etc/init.d": + + $ cp /usr/local/libexec/emailrelay.sh /etc/init.d + +Then determine an appropriate numeric value for the link names by looking at +the "sendmail" links: + + $ cd /etc/init.d/rc5.d + $ ls *sendmail* + +Assuming sendmail links are "S10sendmail" and "K10sendmail", create +the "emailrelay" links in the same format: + + $ cd /etc/init.d/rc5.d + $ ln -s ../emailrelay.sh S10emailrelay + $ ln -s ../emailrelay.sh K10emailrelay + +And finally remove sendmail from the run-level (otherwise both +daemons compete for the standard SMTP listening port): + + $ cd /etc/init.d/rc5.d + $ rm *sendmail + +(There are also KDE and GNOME GUIs which you can use to do the latter steps.) + +Automatic triggering of onward delivery +--------------------------------------- +This section assumes that you are using "pppd" to establish your dial-up Internet +connection. (Note that KDE's "kppp" and Red Hat's "rp3" are graphical front-ends +to the underlying "pppd" daemon.) + +The ppp daemon calls the script "/etc/ppp/ip-up" when it has successfully established +a dial-up link to your ISP. This script will probably set up IP routes, update the +DNS configuration, initialise a firewall, run "fetchmail" and "sendmail", etc. It may +also call out to another script, "ip-up.local" which is available for you to put +stuff into without having to grub around inside "ip-up" itself. + +The simplest approach for editing "ip-up" is to look for a "sendmail -q" line. If +you find "sendmail -q" then it should be sufficient to replace it with this: + + emailrelay --as-client :smtp + +where you substitute your ISP's SMTP server address for . + +Or if your "ip-up" calls out to "ip-up.local" then create a two-line "ip-up.local" +script: + + $ cd /etc/ppp + $ cat << EOF > ip-up.local + #!/bin/sh + exec /usr/local/sbin/emailrelay --as-client :smtp + EOF + $ chmod +x ip-up.local + +Glossary +-------- + +ISP = Internet Service Provider. The company your modem calls to connect to the Internet. + +MTA = Message Transfer Agent. Something which accepts incoming e-mail messages and +passes them on either to a local user, or to another MTA. A sophisticated MTA program, +which is widely used on the Internet, is "sendmail". + +SMTP = Simple Message Transfer Protocol. A set of rules which dictate how e-mail messages +are passed from one part of the e-mail system to the next. The protocol rules are set +out in the document called RFC2821. + +POP3 = Post Office Protocol 3. A protocol for fetching incoming e-mail messages from your +ISP's mail server. Many e-mail front-ends (Mutt, KMail, Netscape, etc) will fetch messages +directly from your ISP using the POP protocol, or you may have a program like "fetchmail" +doing it on their behalf. + +IMAP = Internet Message Access Protocol. A newer alternative to POP3. + +PPP = Point to Point Protocol. A low-level protocol used in dial-up connections to an ISP. +Usually implemented by the "pppd" program on GNU/Linux. + + + +Copyright (C) 2001 Graeme Walker . All rights reserved. diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..e9de238 --- /dev/null +++ b/install-sh @@ -0,0 +1,251 @@ +#!/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. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# 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=: + chmodcmd="" + 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/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..b4b37ea --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = gcc2.95 msvc6.0 diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..0b0dace --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,275 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +SUBDIRS = gcc2.95 msvc6.0 +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps lib/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. + +@SET_MAKE@ + +all-recursive install-data-recursive install-exec-recursive \ +installdirs-recursive install-recursive uninstall-recursive \ +check-recursive installcheck-recursive info-recursive dvi-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ + rev="$$subdir $$rev"; \ + test "$$subdir" = "." && dot_seen=yes; \ + done; \ + test "$$dot_seen" = "no" && rev=". $$rev"; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = lib + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + chmod 777 $(distdir)/$$subdir; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(top_distdir) distdir=../$(distdir)/$$subdir distdir) \ + || exit 1; \ + fi; \ + done +info-am: +info: info-recursive +dvi-am: +dvi: dvi-recursive +check-am: all-am +check: check-recursive +installcheck-am: +installcheck: installcheck-recursive +install-exec-am: +install-exec: install-exec-recursive + +install-data-am: +install-data: install-data-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-recursive +uninstall-am: +uninstall: uninstall-recursive +all-am: Makefile +all-redirect: all-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: installdirs-recursive +installdirs-am: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-recursive + +clean-am: clean-tags clean-generic mostlyclean-am + +clean: clean-recursive + +distclean-am: distclean-tags distclean-generic clean-am + +distclean: distclean-recursive + +maintainer-clean-am: maintainer-clean-tags maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-recursive + +.PHONY: install-data-recursive uninstall-data-recursive \ +install-exec-recursive uninstall-exec-recursive installdirs-recursive \ +uninstalldirs-recursive all-recursive check-recursive \ +installcheck-recursive info-recursive dvi-recursive \ +mostlyclean-recursive distclean-recursive clean-recursive \ +maintainer-clean-recursive tags tags-recursive mostlyclean-tags \ +distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ +dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ +install-exec install-data-am install-data install-am install \ +uninstall-am uninstall all-redirect all-am all installdirs-am \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/gcc2.95/Makefile.am b/lib/gcc2.95/Makefile.am new file mode 100644 index 0000000..af457e2 --- /dev/null +++ b/lib/gcc2.95/Makefile.am @@ -0,0 +1,22 @@ +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + +EXTRA_DIST = exception iostream limits sstream xlocale + diff --git a/lib/gcc2.95/Makefile.in b/lib/gcc2.95/Makefile.in new file mode 100644 index 0000000..889ce4d --- /dev/null +++ b/lib/gcc2.95/Makefile.in @@ -0,0 +1,191 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +EXTRA_DIST = exception iostream limits sstream xlocale +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../config.h +CONFIG_CLEAN_FILES = +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps lib/gcc2.95/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = lib/gcc2.95 + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: tags distdir info-am info dvi-am dvi check check-am \ +installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/gcc2.95/exception b/lib/gcc2.95/exception new file mode 100644 index 0000000..120c6e9 --- /dev/null +++ b/lib/gcc2.95/exception @@ -0,0 +1,37 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// exception +// + +#ifndef G_STD_EXCEPTION +#define G_STD_EXCEPTION + +namespace std +{ + class exception + { + public: + virtual const char * what() const = 0 ; + } ; +} ; + +#endif + diff --git a/lib/gcc2.95/iostream b/lib/gcc2.95/iostream new file mode 100644 index 0000000..3b7e6da --- /dev/null +++ b/lib/gcc2.95/iostream @@ -0,0 +1,36 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// iostream +// + +#ifndef G_STD_IOSTREAM +#define G_STD_IOSTREAM + +// gnu's iostream does only this +#include + +namespace std +{ + typedef ::ios ios_base ; +} ; + +#endif + diff --git a/lib/gcc2.95/limits b/lib/gcc2.95/limits new file mode 100644 index 0000000..9a336d2 --- /dev/null +++ b/lib/gcc2.95/limits @@ -0,0 +1,33 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// limits +// + +#ifndef G_STD_LIMITS_H +#define G_STD_LIMITS_H + +namespace std +{ + #include +} ; + +#endif + diff --git a/lib/gcc2.95/sstream b/lib/gcc2.95/sstream new file mode 100644 index 0000000..4536716 --- /dev/null +++ b/lib/gcc2.95/sstream @@ -0,0 +1,67 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// sstream +// +// Forwards compatibility from ostrstream to std::stringstream. +// + +#ifndef G_STD_SSTREAM_H +#define G_STD_SSTREAM_H + +#include +#include + +namespace std +{ + class stringstream : public ostrstream + { + public: + stringstream() ; + stringstream( char * , size_t ) ; + std::string str() const ; + } ; +} ; + +inline +std::stringstream::stringstream() +{ +} + +inline +std::stringstream::stringstream( char * , size_t ) +{ +} + +inline +std::string std::stringstream::str() const +{ + std::stringstream * This = const_cast(this) ; + ostrstream * base = This ; + (*base) << '\0' ; + char * p = base->str() ; + std::string s( p ? p : "" ) ; + base->rdbuf()->freeze( 0 ) ; + return s ; +} + +#endif + + diff --git a/lib/gcc2.95/xlocale b/lib/gcc2.95/xlocale new file mode 100644 index 0000000..48527f6 --- /dev/null +++ b/lib/gcc2.95/xlocale @@ -0,0 +1,30 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// xlocale +// + +#ifndef G_STD_XLOCALE +#define G_STD_XLOCALE + +// empty + +#endif + diff --git a/lib/msvc6.0/Makefile.am b/lib/msvc6.0/Makefile.am new file mode 100644 index 0000000..63f67fa --- /dev/null +++ b/lib/msvc6.0/Makefile.am @@ -0,0 +1,21 @@ +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + +EXTRA_DIST = cstdio cstdlib cstring ctime diff --git a/lib/msvc6.0/Makefile.in b/lib/msvc6.0/Makefile.in new file mode 100644 index 0000000..55048fb --- /dev/null +++ b/lib/msvc6.0/Makefile.in @@ -0,0 +1,191 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +EXTRA_DIST = cstdio cstdlib cstring ctime +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../config.h +CONFIG_CLEAN_FILES = +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps lib/msvc6.0/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = lib/msvc6.0 + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: tags distdir info-am info dvi-am dvi check check-am \ +installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/msvc6.0/cstdio b/lib/msvc6.0/cstdio new file mode 100644 index 0000000..f6a3fe7 --- /dev/null +++ b/lib/msvc6.0/cstdio @@ -0,0 +1,35 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// cstdio +// + +#ifndef G_PORT_CSTDIO_H +#define G_PORT_CSTDIO_H + +#include +namespace std +{ + using ::remove ; + using ::rename ; +} ; + +#endif + diff --git a/lib/msvc6.0/cstdlib b/lib/msvc6.0/cstdlib new file mode 100644 index 0000000..f560e3b --- /dev/null +++ b/lib/msvc6.0/cstdlib @@ -0,0 +1,35 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// cstdlib +// + +#ifndef G_PORT_CSTDLIB_H +#define G_PORT_CSTDLIB_H + +#include "stdlib.h" +namespace std +{ + using ::rand ; + using ::srand ; + using ::getenv ; +} ; + +#endif diff --git a/lib/msvc6.0/cstring b/lib/msvc6.0/cstring new file mode 100644 index 0000000..e10fb4c --- /dev/null +++ b/lib/msvc6.0/cstring @@ -0,0 +1,43 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// cstring +// + +#ifndef G_PORT_CSTRING_H +#define G_PORT_CSTRING_H + +#include "string.h" +namespace std +{ + using ::strlen ; + using ::strcat ; + using ::strncat ; + using ::strchr ; + using ::strrchr ; + using ::strdup ; + using ::strcpy ; + using ::strstr ; + using ::strspn ; + using ::strcspn ; +} ; + +#endif + diff --git a/lib/msvc6.0/ctime b/lib/msvc6.0/ctime new file mode 100644 index 0000000..956b330 --- /dev/null +++ b/lib/msvc6.0/ctime @@ -0,0 +1,40 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// ctime +// + +#ifndef G_PORT_CTIME_H +#define G_PORT_CTIME_H + +#include +namespace std +{ + using ::time_t ; + using ::tm ; + using ::time ; + using ::localtime ; + using ::gmtime ; + using ::mktime ; + using ::_tzset ; +} ; + +#endif + diff --git a/missing b/missing new file mode 100755 index 0000000..7789652 --- /dev/null +++ b/missing @@ -0,0 +1,190 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright (C) 1996, 1997 Free Software Foundation, Inc. +# Franc,ois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, 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. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing - GNU libit 0.0" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`configure.in'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`configure.in'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`configure.in'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' configure.in` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`configure.in'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100755 index 0000000..4f58503 --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +# $Id: mkinstalldirs,v 1.13 1999/01/05 03:18:55 bje Exp $ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..ac4074a --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = glib gnet main win32 diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..945f528 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,275 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +SUBDIRS = glib gnet main win32 +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps src/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. + +@SET_MAKE@ + +all-recursive install-data-recursive install-exec-recursive \ +installdirs-recursive install-recursive uninstall-recursive \ +check-recursive installcheck-recursive info-recursive dvi-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ + rev="$$subdir $$rev"; \ + test "$$subdir" = "." && dot_seen=yes; \ + done; \ + test "$$dot_seen" = "no" && rev=". $$rev"; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + chmod 777 $(distdir)/$$subdir; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(top_distdir) distdir=../$(distdir)/$$subdir distdir) \ + || exit 1; \ + fi; \ + done +info-am: +info: info-recursive +dvi-am: +dvi: dvi-recursive +check-am: all-am +check: check-recursive +installcheck-am: +installcheck: installcheck-recursive +install-exec-am: +install-exec: install-exec-recursive + +install-data-am: +install-data: install-data-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-recursive +uninstall-am: +uninstall: uninstall-recursive +all-am: Makefile +all-redirect: all-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: installdirs-recursive +installdirs-am: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-recursive + +clean-am: clean-tags clean-generic mostlyclean-am + +clean: clean-recursive + +distclean-am: distclean-tags distclean-generic clean-am + +distclean: distclean-recursive + +maintainer-clean-am: maintainer-clean-tags maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-recursive + +.PHONY: install-data-recursive uninstall-data-recursive \ +install-exec-recursive uninstall-exec-recursive installdirs-recursive \ +uninstalldirs-recursive all-recursive check-recursive \ +installcheck-recursive info-recursive dvi-recursive \ +mostlyclean-recursive distclean-recursive clean-recursive \ +maintainer-clean-recursive tags tags-recursive mostlyclean-tags \ +distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ +dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ +install-exec install-data-am install-data install-am install \ +uninstall-am uninstall all-redirect all-am all installdirs-am \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/glib/Makefile.am b/src/glib/Makefile.am new file mode 100644 index 0000000..0fd4db5 --- /dev/null +++ b/src/glib/Makefile.am @@ -0,0 +1,73 @@ +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === +EXTRA_DIST=garg_win32.cpp \ + gdaemon_win32.cpp \ + gdatetime_win32.cpp \ + gdirectory_win32.cpp \ + gfs_win32.cpp \ + glogoutput_win32.cpp \ + gpid_win32.cpp \ + gfile_win32.cpp \ + gnumber.cpp +INCLUDES = -I$(top_srcdir)/lib/gcc2.95 +noinst_LIBRARIES = libglib.a +libglib_a_SOURCES = garg.cpp \ + garg_unix.cpp \ + gdaemon_unix.cpp \ + gdate.cpp \ + gdatetime.cpp \ + gdatetime_unix.cpp \ + gdirectory.cpp \ + gdirectory_unix.cpp \ + gexception.cpp \ + gfile.cpp \ + gfile_unix.cpp \ + gfs_unix.cpp \ + ggetopt.cpp \ + glog.cpp \ + glogoutput.cpp \ + glogoutput_unix.cpp \ + gpath.cpp \ + gpid_unix.cpp \ + gstr.cpp \ + gtime.cpp \ + gdef.h \ + garg.h \ + gdaemon.h \ + gdate.h \ + gdatetime.h \ + gdirectory.h \ + gexception.h \ + gfile.h \ + gfs.h \ + ggetopt.h \ + glog.h \ + glogoutput.h \ + gnumber.h \ + gpath.h \ + gpid.h \ + gstr.h \ + gtime.h \ + gstrings.h \ + gdebug.h \ + gassert.h \ + gconvert.h \ + gmemory.h + diff --git a/src/glib/Makefile.in b/src/glib/Makefile.in new file mode 100644 index 0000000..d38499a --- /dev/null +++ b/src/glib/Makefile.in @@ -0,0 +1,292 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +EXTRA_DIST = garg_win32.cpp gdaemon_win32.cpp gdatetime_win32.cpp gdirectory_win32.cpp gfs_win32.cpp glogoutput_win32.cpp gpid_win32.cpp gfile_win32.cpp gnumber.cpp + +INCLUDES = -I$(top_srcdir)/lib/gcc2.95 +noinst_LIBRARIES = libglib.a +libglib_a_SOURCES = garg.cpp garg_unix.cpp gdaemon_unix.cpp gdate.cpp gdatetime.cpp gdatetime_unix.cpp gdirectory.cpp gdirectory_unix.cpp gexception.cpp gfile.cpp gfile_unix.cpp gfs_unix.cpp ggetopt.cpp glog.cpp glogoutput.cpp glogoutput_unix.cpp gpath.cpp gpid_unix.cpp gstr.cpp gtime.cpp gdef.h garg.h gdaemon.h gdate.h gdatetime.h gdirectory.h gexception.h gfile.h gfs.h ggetopt.h glog.h glogoutput.h gnumber.h gpath.h gpid.h gstr.h gtime.h gstrings.h gdebug.h gassert.h gconvert.h gmemory.h + +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + + +DEFS = @DEFS@ -I. -I$(srcdir) -I../.. +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +libglib_a_LIBADD = +libglib_a_OBJECTS = garg.o garg_unix.o gdaemon_unix.o gdate.o \ +gdatetime.o gdatetime_unix.o gdirectory.o gdirectory_unix.o \ +gexception.o gfile.o gfile_unix.o gfs_unix.o ggetopt.o glog.o \ +glogoutput.o glogoutput_unix.o gpath.o gpid_unix.o gstr.o gtime.o +AR = ar +CXXFLAGS = @CXXFLAGS@ +CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(libglib_a_SOURCES) +OBJECTS = $(libglib_a_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .cpp .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps src/glib/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-noinstLIBRARIES: + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +distclean-noinstLIBRARIES: + +maintainer-clean-noinstLIBRARIES: + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +libglib.a: $(libglib_a_OBJECTS) $(libglib_a_DEPENDENCIES) + -rm -f libglib.a + $(AR) cru libglib.a $(libglib_a_OBJECTS) $(libglib_a_LIBADD) + $(RANLIB) libglib.a +.cpp.o: + $(CXXCOMPILE) -c $< + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src/glib + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile $(LIBRARIES) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-noinstLIBRARIES mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-noinstLIBRARIES clean-compile clean-tags clean-generic \ + mostlyclean-am + +clean: clean-am + +distclean-am: distclean-noinstLIBRARIES distclean-compile \ + distclean-tags distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-noinstLIBRARIES \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-noinstLIBRARIES distclean-noinstLIBRARIES \ +clean-noinstLIBRARIES maintainer-clean-noinstLIBRARIES \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ +check-am installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/glib/garg.cpp b/src/glib/garg.cpp new file mode 100644 index 0000000..9778b1a --- /dev/null +++ b/src/glib/garg.cpp @@ -0,0 +1,140 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// garg.cpp +// + +#include "gdef.h" +#include "garg.h" +#include "gpath.h" +#include "gstr.h" +#include "gdebug.h" +#include "gassert.h" + +G::Arg::Arg() +{ + // now use parse() +} + +G::Arg::Arg( const Arg & other ) : + m_array(other.m_array) , + m_prefix(other.m_prefix) +{ +} + +G::Arg & G::Arg::operator=( const Arg & rhs ) +{ + if( this != &rhs ) + { + m_array = rhs.m_array ; + m_prefix = rhs.m_prefix ; + } + return *this ; +} + +G::Arg::~Arg() +{ +} + +G::Arg::Arg( int argc , char *argv[] ) +{ + for( int i = 0 ; i < argc ; i++ ) + m_array.push_back( argv[i] ) ; + + setPrefix() ; +} + +void G::Arg::setPrefix() +{ + G_ASSERT( m_array.size() > 0U ) ; + Path path( m_array[0U] ) ; + path.removeExtension() ; + m_prefix = path.basename() ; +} + +void G::Arg::parse( HINSTANCE hinstance , const std::string & command_line ) +{ + m_array.push_back( moduleName(hinstance) ) ; + G::Str::splitIntoTokens( command_line , m_array , " \t" ) ; + setPrefix() ; +} + +bool G::Arg::contains( const std::string & sw , size_t sw_args ) const +{ + return find( sw , sw_args , NULL ) ; +} + +bool G::Arg::find( const std::string & sw , size_t sw_args , size_t * index_p ) const +{ + for( size_t i = 1 ; i < m_array.size() ; i++ ) // start from v[1] + { + if( sw == m_array[i] && (i+sw_args) < m_array.size() ) + { + if( index_p != NULL ) + *index_p = i ; + return true ; + } + } + return false ; +} + +void G::Arg::remove( const std::string & sw , size_t sw_args ) +{ + size_t i = 0U ; + const bool found = find( sw , sw_args , &i ) ; + if( found ) + removeAt( i , sw_args ) ; +} + +void G::Arg::removeAt( size_t sw_index , size_t sw_args ) +{ + G_ASSERT( sw_index > 0U && sw_index < m_array.size() ) ; + if( sw_index > 0U && sw_index < m_array.size() ) + { + Array::iterator p = m_array.begin() + sw_index ; + p = m_array.erase( p ) ; + for( size_t i = 0U ; i < sw_args && p != m_array.end() ; i++ ) + p = m_array.erase( p ) ; + } +} + +size_t G::Arg::index( const std::string & sw , size_t sw_args ) const +{ + size_t i = 0U ; + const bool found = find( sw , sw_args , &i ) ; + return found ? i : 0U ; +} + +size_t G::Arg::c() const +{ + return m_array.size() ; +} + +std::string G::Arg::v( size_t i ) const +{ + G_ASSERT( i < m_array.size() ) ; + return m_array[i] ; +} + +std::string G::Arg::prefix() const +{ + return m_prefix ; +} + diff --git a/src/glib/garg.h b/src/glib/garg.h new file mode 100644 index 0000000..7afdb56 --- /dev/null +++ b/src/glib/garg.h @@ -0,0 +1,114 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// garg.h +// + +#ifndef G_ARG_H +#define G_ARG_H + +#include "gdef.h" +#include +#include + +namespace G +{ + class Arg ; +} ; + +// Class: G::Arg +// Description: A command line parser class. +// +// Also does command line splitting for Windows: +// the single command line string is split into +// an argv[] array, including argv[0]. +// +class G::Arg +{ +public: + Arg( int argc , char *argv[] ) ; + // Constructor taking argc/argv. + + Arg() ; + // Default constructor for Windows. + // Initialise (once) with parse(). + + void parse( HINSTANCE hinstance , const std::string & command_line ) ; + // Windows only. + // + // Parses the given command line, splitting + // it up into an array of tokens. + // The program name is automatically + // added as the first token (cf. argv[0]). + + ~Arg() ; + // Destructor. + + unsigned int c() const ; + // Returns the number of tokens in the + // command line, including the program + // name. + + std::string v( size_t i ) const ; + // Returns the i'th argument. + // Precondition: i < c() + + std::string prefix() const ; + // Returns the basename of v(0) without + // any extension. + + bool contains( const std::string & sw , size_t sw_args = 0U ) const ; + // Returns true if the command line + // contains the given switch with enough + // command line arguments left to satisfy + // the given number of switch arguments. + + size_t index( const std::string & sw , size_t sw_args = 0U ) const ; + // Returns the index of the given switch. + // Returns zero if not present. + + void remove( const std::string & sw , size_t sw_args = 0U ) ; + // Removes the given switch and its + // arguments. + // + // Precondition: contains() + + void removeAt( size_t sw_index , size_t sw_args = 0U ) ; + // Removes the given argument and the + // following ones. + + Arg & operator=( const Arg & ) ; + // Assignment operator. + + Arg( const Arg & ) ; + // Copy constructor. + +private: + static std::string moduleName( HINSTANCE h ) ; + bool find( const std::string & sw , size_t sw_args , size_t *index_p ) const ; + void setPrefix() ; + +private: + typedef std::vector Array ; + Array m_array ; + std::string m_prefix ; +} ; + +#endif diff --git a/src/glib/garg_unix.cpp b/src/glib/garg_unix.cpp new file mode 100644 index 0000000..047498a --- /dev/null +++ b/src/glib/garg_unix.cpp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// garg_unix.cpp +// + +#include "gdef.h" +#include "garg.h" +#include "gdebug.h" + +std::string G::Arg::moduleName( HINSTANCE hinstance ) +{ + return std::string() ; +} + diff --git a/src/glib/garg_win32.cpp b/src/glib/garg_win32.cpp new file mode 100644 index 0000000..e8b9bdb --- /dev/null +++ b/src/glib/garg_win32.cpp @@ -0,0 +1,37 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// garg_win32.cpp +// + +#include "gdef.h" +#include "garg.h" +#include "gdebug.h" + +std::string G::Arg::moduleName( HINSTANCE hinstance ) +{ + char buffer[260] ; + size_t size = sizeof(buffer) ; + *buffer = '\0' ; + ::GetModuleFileName( hinstance , buffer , size-1U ) ; + buffer[size-1U] = '\0' ; + return std::string(buffer) ; +} + diff --git a/src/glib/gassert.h b/src/glib/gassert.h new file mode 100644 index 0000000..99eef31 --- /dev/null +++ b/src/glib/gassert.h @@ -0,0 +1,37 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gassert.h +// + +#ifndef G_ASSERT_H +#define G_ASSERT_H + +#include "gdef.h" +#include "glogoutput.h" + +#if defined(_DEBUG) && ! defined(G_NO_ASSERT) + #define G_ASSERT( test ) G::LogOutput::assertion( __FILE__ , __LINE__ , test , #test ) +#else + #define G_ASSERT( test ) + //#define G_ASSERT( test ) G::LogOutput::assertion( 0 , 0 , test , NULL ) +#endif + +#endif diff --git a/src/glib/gconvert.h b/src/glib/gconvert.h new file mode 100644 index 0000000..b527162 --- /dev/null +++ b/src/glib/gconvert.h @@ -0,0 +1,48 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gconvert.h +// + +#ifndef G_CONVERT_H +#define G_CONVERT_H + +#include "gdef.h" +#include "gexception.h" + +G_EXCEPTION( GConvertOverflow , "arithmetic overflow" ) ; + +// Template function: GConvert +// Description: Does arithmetic conversions with +// overflow checking. +// +template +inline +Tout GConvert( const Tin & in ) +{ + Tout out = in ; + Tin copy = out ; + if( in != copy ) + throw GConvertOverflow( std::stringstream() << in ) ; + return out ; +} + +#endif + diff --git a/src/glib/gdaemon.h b/src/glib/gdaemon.h new file mode 100644 index 0000000..5477dc4 --- /dev/null +++ b/src/glib/gdaemon.h @@ -0,0 +1,79 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdaemon.h +// + +#ifndef G_DAEMON_H +#define G_DAEMON_H + +#include "gdef.h" +#include "gexception.h" +#include "gpath.h" +#include +#include + +namespace G +{ + class Daemon ; +} ; + +// Class: G::Daemon +// Description: A class for deamonising the calling process. +// Deamonisation includes fork()ing, detaching from the +// controlling terminal, setting the process umask, etc. +// The windows implementation does nothing. +// +class G::Daemon +{ +public: + G_EXCEPTION( CannotFork , "cannot fork" ) ; + G_EXCEPTION( BadPidFile , "invalid pid file" ) ; + enum Who { Parent , Child } ; + + static void detach() ; + // Detaches from the parent environment. + // This typically involves fork()ing, + // _exit()ing the parent, and calling + // setsid() in the child. + + static void detach( const Path & pid_file ) ; + // An overload which writes the new process-id + // to a file. The path must be absolute. + // Throws BadPidFile on error. + + static void closeFiles( bool keep_stderr = false ) ; + // Closes all open file descriptors. + + static void closeStderr() ; + // Closes stderr. + + static void setUmask() ; + // Sets a tight umask. + +private: + Daemon() ; + static Who fork() ; + static void setsid() ; + static void cd( const std::string & ) ; +} ; + +#endif + diff --git a/src/glib/gdaemon_unix.cpp b/src/glib/gdaemon_unix.cpp new file mode 100644 index 0000000..40f6354 --- /dev/null +++ b/src/glib/gdaemon_unix.cpp @@ -0,0 +1,107 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdaemon_unix.cpp +// + +#include "gdef.h" +#include "gdaemon.h" +#include "gpid.h" + +//static +void G::Daemon::detach( const Path & pid_file ) +{ + if( !pid_file.isAbsolute() ) + throw BadPidFile(std::string("must be an absolute path: ")+pid_file.str()) ; + + if( !std::ofstream(pid_file.str().c_str()).good() ) + throw BadPidFile(std::string("cannot create file: ")+pid_file.str()) ; + + detach() ; + + std::ofstream file( pid_file.str().c_str() ) ; + file << Pid() << std::endl ; +} + +//static +void G::Daemon::detach() +{ + // see Stevens, ISBN 0-201-563137-7, ch 13. + + if( fork() == Parent ) + ::_exit( 0 ) ; + + setsid() ; + cd( "/" ) ; + + if( fork() == Parent ) + ::_exit( 0 ) ; +} + +void G::Daemon::setsid() +{ + pid_t session_id = ::setsid() ; + if( session_id == -1 ) + ; // no-op +} + +void G::Daemon::cd( const std::string & dir ) +{ + if( 0 != ::chdir( dir.c_str() ) ) + ; // ignore it +} + +void G::Daemon::setUmask() +{ + // (note that ansi std::ofstream does not support file permissions, + // so rely on the umask to keep things secure) + mode_t new_mode = 0177 ; // create as -rw------- + mode_t old_mode = ::umask( new_mode ) ; +} + +G::Daemon::Who G::Daemon::fork() +{ + pid_t pid = ::fork() ; + if( pid < 0 ) + { + throw CannotFork() ; + } + return pid == 0 ? Child : Parent ; +} + +void G::Daemon::closeStderr() +{ + ::close( STDERR_FILENO ) ; +} + +void G::Daemon::closeFiles( bool keep_stderr ) +{ + int n = 256U ; + long rc = ::sysconf( _SC_OPEN_MAX ) ; + if( rc > 0L ) + n = static_cast( rc ) ; + + for( int fd = 0 ; fd < n ; fd++ ) + { + if( !keep_stderr || fd != STDERR_FILENO ) + ::close( fd ) ; + } +} + diff --git a/src/glib/gdaemon_win32.cpp b/src/glib/gdaemon_win32.cpp new file mode 100644 index 0000000..e84e429 --- /dev/null +++ b/src/glib/gdaemon_win32.cpp @@ -0,0 +1,48 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdaemon_win32.cpp +// + +#include "gdef.h" +#include "gdaemon.h" + +//static +void G::Daemon::detach( const Path & ) +{ +} + +//static +void G::Daemon::detach() +{ +} + +void G::Daemon::closeFiles( bool keep_stderr ) +{ +} + +void G::Daemon::setUmask() +{ +} + +void G::Daemon::closeStderr() +{ +} + diff --git a/src/glib/gdate.cpp b/src/glib/gdate.cpp new file mode 100644 index 0000000..14bac14 --- /dev/null +++ b/src/glib/gdate.cpp @@ -0,0 +1,266 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdate.cpp +// + +#include "gdef.h" +#include "gdate.h" +#include "gdebug.h" +#include +#include + +//static +int G::Date::yearUpperLimit() +{ + return 2035 ; // see mktime() +} + +//static +int G::Date::yearLowerLimit() +{ + return 1970 ; // see mktime() +} + +G::Date::Date() +{ + init( G::DateTime::utc(G::DateTime::now()) ) ; +} + +G::Date::Date( G::DateTime::EpochTime t , const LocalTime & ) +{ + init( G::DateTime::local(t) ) ; +} + +G::Date::Date( const G::DateTime::BrokenDownTime & tm ) +{ + init( tm ) ; +} + +G::Date::Date( const LocalTime & ) +{ + init( G::DateTime::local(G::DateTime::now()) ) ; +} + +G::Date::Date( int year , G::Date::Month month , int day_of_month ) +{ + G_ASSERT( year >= yearLowerLimit() ) ; + G_ASSERT( year <= yearUpperLimit() ) ; + G_ASSERT( day_of_month > 0 ) ; + G_ASSERT( day_of_month < 32 ) ; + G_ASSERT( month >= 1 ) ; + G_ASSERT( month <= 12 ) ; + + m_year = year ; + m_month = month ; + m_day = day_of_month ; + m_weekday_set = false ; +} + +void G::Date::init( const G::DateTime::BrokenDownTime & tm ) +{ + m_year = tm.tm_year + 1900 ; + m_month = tm.tm_mon + 1 ; + m_day = tm.tm_mday ; + m_weekday_set = false ; + m_weekday = sunday ; +} + +std::string G::Date::string( Format ) const +{ + std::stringstream ss ; + ss << m_year << "/" << m_month << "/" << m_day ; + return ss.str() ; +} + +int G::Date::monthday() const +{ + return m_day ; +} + +std::string G::Date::monthdayString() const +{ + std::stringstream ss ; + ss << m_day ; + return ss.str() ; +} + +G::Date::Weekday G::Date::weekday() const +{ + if( ! m_weekday_set ) + { + G::DateTime::BrokenDownTime tm ; + tm.tm_year = m_year - 1900 ; + tm.tm_mon = m_month - 1 ; + tm.tm_mday = m_day ; + tm.tm_hour = 12 ; + tm.tm_min = 0 ; + tm.tm_sec = 0 ; + tm.tm_wday = 0 ; // ignored + tm.tm_yday = 0 ; // ignored + tm.tm_isdst = 0 ; // ignored + + G::DateTime::BrokenDownTime out = G::DateTime::utc(G::DateTime::epochTime(tm)) ; + + const_cast(this)->m_weekday_set = true ; + const_cast(this)->m_weekday = Weekday(out.tm_wday) ; + } + return m_weekday ; +} + +std::string G::Date::weekdayString( bool brief ) const +{ + if( weekday() == sunday ) return brief ? "Sun" : "Sunday" ; + if( weekday() == monday ) return brief ? "Mon" : "Monday" ; + if( weekday() == tuesday ) return brief ? "Tue" : "Tuesday" ; + if( weekday() == wednesday ) return brief ? "Wed" : "Wednesday" ; + if( weekday() == thursday ) return brief ? "Thu" : "Thursday" ; + if( weekday() == friday ) return brief ? "Fri" : "Friday" ; + if( weekday() == saturday ) return brief ? "Sat" : "Saturday" ; + return "" ; +} + +G::Date::Month G::Date::month() const +{ + return Month(m_month) ; +} + +std::string G::Date::monthString( bool brief ) const +{ + if( month() == january ) return brief ? "Jan" : "January" ; + if( month() == february ) return brief ? "Feb" : "February" ; + if( month() == march ) return brief ? "Mar" : "March" ; + if( month() == april ) return brief ? "Apr" : "April" ; + if( month() == may ) return brief ? "May" : "May" ; + if( month() == june ) return brief ? "Jun" : "June" ; + if( month() == july ) return brief ? "Jul" : "July" ; + if( month() == august ) return brief ? "Aug" : "August" ; + if( month() == september ) return brief ? "Sep" : "September" ; + if( month() == october ) return brief ? "Oct" : "October" ; + if( month() == november ) return brief ? "Nov" : "November" ; + if( month() == december ) return brief ? "Dec" : "December" ; + return "" ; +} + +int G::Date::year() const +{ + return m_year ; +} + +std::string G::Date::yearString() const +{ + std::stringstream ss ; + ss << m_year ; + return ss.str() ; +} + +G::Date &G::Date::operator++() +{ + ++m_day ; + if( m_day == (lastDay(m_month,m_year)+1U) ) + { + m_day = 1U ; + ++m_month ; + if( m_month == 13U ) + { + m_month = 1U ; + ++m_year ; + } + } + if( m_weekday_set ) + { + if( m_weekday == saturday ) + m_weekday = sunday ; + else + m_weekday = Weekday(int(m_weekday)+1) ; + } + return *this ; +} + +G::Date & G::Date::operator--() +{ + if( m_day == 1U ) + { + if( m_month == 1U ) + { + m_year-- ; + m_month = 12U ; + } + else + { + m_month-- ; + } + + m_day = lastDay( m_month , m_year ) ; + } + else + { + m_day-- ; + } + if( m_weekday_set ) + { + if( m_weekday == sunday ) + m_weekday = saturday ; + else + m_weekday = Weekday(int(m_weekday)-1) ; + } + return *this ; +} + +//static +unsigned int G::Date::lastDay( unsigned int month , unsigned int year ) +{ + unsigned int end = 30U ; + if( month == 1U || + month == 3U || + month == 5U || + month == 7U || + month == 8U || + month == 10U || + month == 12U ) + { + end = 31U ; + } + else if( month == 2U ) + { + end = isLeapYear(year) ? 29U : 28U ; + } + return end ; +} + +//static +bool G::Date::isLeapYear( unsigned int y ) +{ + return y >= 1800U && ( y % 400U == 0U || ( y % 100U != 0U && y % 4U == 0U ) ) ; +} + +bool G::Date::operator==( const Date &other ) const +{ + return + year() == other.year() && + month() == other.month() && + monthday() == other.monthday() ; +} + +bool G::Date::operator!=( const Date &other ) const +{ + return !( other == *this ) ; +} + diff --git a/src/glib/gdate.h b/src/glib/gdate.h new file mode 100644 index 0000000..d62b61c --- /dev/null +++ b/src/glib/gdate.h @@ -0,0 +1,134 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdate.h +// + +#ifndef G_DATE_H +#define G_DATE_H + +#include "gdef.h" +#include "gdatetime.h" +#include "gdebug.h" +#include +#include +#include + +namespace G +{ + class Date ; +} ; + +// Class: G::Date +// Description: A date (dd/mm/yyyy) class. +// +class G::Date +{ +public: + class LocalTime // An overload discriminator class for Date constructors. + {} ; + + enum Weekday + { sunday, monday, tuesday, wednesday, thursday, friday, saturday } ; + + enum Month + { january = 1 , february , march , april , may , june , july , + august , september , october , november , december } ; + + enum Format + { yyyy_mm_dd_slash } ; + + static int yearUpperLimit() ; + // Returns the smallest supported year value. + + static int yearLowerLimit() ; + // Returns the largest supported year value. + + Date() ; + // Default constructor the current date + // in the UTC timezone. + + explicit Date( const LocalTime & ) ; + // Constructor for the current date + // in the local timezone. + + explicit Date( const G::DateTime::BrokenDownTime & tm ) ; + // Constructor for the specified date. + + explicit Date( G::DateTime::EpochTime t , const LocalTime & ) ; + // Constructor for the date in the local + // timezone as at the given epoch time. + + Date( int year , Month month , int day_of_month ) ; + // Constructor for the specified date. + + std::string string( Format format = yyyy_mm_dd_slash ) const ; + // Returns a string representation of the date. + + Weekday weekday() const ; + // Returns the day of the week. + + std::string weekdayString( bool brief = false ) const ; + // Returns a string representation of the day of the week. + + int monthday() const ; + // Returns the day of the month. + + std::string monthdayString() const ; + // Returns a string representation of the day of the month. + + Month month() const ; + // Returns the month. + + std::string monthString( bool brief = false ) const ; + // Returns the month as a string. + + int year() const ; + // Returns the year. + + std::string yearString() const ; + // Returns the year as a string. + + Date &operator++() ; + // Increments the date by one day. + + Date &operator--() ; + // Decrements the date by one day. + + bool operator==( const Date &rhs ) const ; + // Comparison operator. + + bool operator!=( const Date &rhs ) const ; + // Comparison operator. + +private: + void init( const G::DateTime::BrokenDownTime & ) ; + static unsigned int lastDay( unsigned int month , unsigned int year ) ; + static bool isLeapYear( unsigned int y ) ; + +private: + unsigned int m_day ; + unsigned int m_month ; + unsigned int m_year ; + bool m_weekday_set ; + Weekday m_weekday ; +} ; + +#endif diff --git a/src/glib/gdatetime.cpp b/src/glib/gdatetime.cpp new file mode 100644 index 0000000..6320a63 --- /dev/null +++ b/src/glib/gdatetime.cpp @@ -0,0 +1,103 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdatetime.cpp +// + +#include "gdef.h" +#include "gdatetime.h" +#include "gstr.h" +#include "gassert.h" +#include + +const unsigned int minute = 60U ; +const unsigned int hour = 60U * minute ; +const unsigned int day = 24U * hour ; + +G::DateTime::EpochTime G::DateTime::now() +{ + return std::time(NULL) ; +} + +G::DateTime::EpochTime G::DateTime::epochTime( const BrokenDownTime & bdt_in ) +{ + // get a rough starting point + BrokenDownTime bdt( bdt_in ) ; + EpochTime start = std::mktime( &bdt ) ; // localtime + + // iterate over all timezones + const unsigned int delta = minute * 30U ; + for( EpochTime t = (start-day-delta) ; t <= (start+day+delta) ; t += delta ) + { + if( equivalent( t , bdt_in ) ) + return t ; + } + throw Error() ; +} + +G::DateTime::BrokenDownTime G::DateTime::utc( EpochTime epoch_time ) +{ + BrokenDownTime result ; + G::DateTime::gmtime_r( &epoch_time , &result ) ; + return result ; +} + +G::DateTime::BrokenDownTime G::DateTime::local( EpochTime epoch_time ) +{ + BrokenDownTime bdt_local ; + G::DateTime::localtime_r( &epoch_time , &bdt_local ) ; + return bdt_local ; +} + +G::DateTime::Offset G::DateTime::offset( EpochTime utc ) +{ + BrokenDownTime bdt_local = local(utc) ; + + EpochTime local = epochTime(bdt_local) ; + bool ahead = local >= utc ; // ie. east-of + unsigned int n = ahead ? (local-utc) : (utc-local) ; + return Offset( ahead , n ) ; +} + +std::string G::DateTime::offsetString( Offset offset ) +{ + unsigned int hh = offset.second / 3600U ; + unsigned int mm = (offset.second / 60U) % 60 ; + + std::stringstream ss ; + char sign = (offset.first || (hh==0&&mm==0)) ? '+' : '-' ; + ss << sign << (hh/10U) << (hh%10U) << (mm/10) << (mm%10) ; + return ss.str() ; +} + +bool G::DateTime::equivalent( EpochTime t , const BrokenDownTime & bdt_in ) +{ + BrokenDownTime bdt_test = utc(t) ; + return same( bdt_test , bdt_in ) ; +} + +bool G::DateTime::same( const BrokenDownTime & bdt1 , const BrokenDownTime & bdt2 ) +{ + return + bdt1.tm_mday == bdt2.tm_mday && + bdt1.tm_hour == bdt2.tm_hour && + bdt1.tm_min == bdt2.tm_min ; +} + diff --git a/src/glib/gdatetime.h b/src/glib/gdatetime.h new file mode 100644 index 0000000..f2620dc --- /dev/null +++ b/src/glib/gdatetime.h @@ -0,0 +1,85 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdatetime.h +// + +#ifndef G_DATE_TIME_H +#define G_DATE_TIME_H + +#include +#include "gdef.h" +#include "gexception.h" +#include +#include + +namespace G +{ + class DateTime ; +} ; + +// Class: G::DateTime +// Description: A low-level static class used by Date and Time. +// +class G::DateTime +{ +public: + G_EXCEPTION( Error , "date/time error" ) ; + typedef std::time_t EpochTime ; + typedef struct std::tm BrokenDownTime ; + typedef std::pair Offset ; + + static EpochTime now() ; + // Returns the current epoch time. + + static EpochTime epochTime( const BrokenDownTime & broken_down_time ) ; + // Converts from UTC broken-down-time to epoch time. + + static BrokenDownTime utc( EpochTime epoch_time ) ; + // Converts from epoch time to UTC broken-down-time. + + static BrokenDownTime local( EpochTime epoch_time ) ; + // Converts from epoch time to local broken-down-time. + + static Offset offset( EpochTime epoch_time ) ; + // Returns the offset between UTC and localtime + // as at 'epoch_time'. The returned pair has + // 'first' set to true if localtime is + // ahead of (ie. east of) UTC. + // + // (Note that this may be a relatively expensive + // operation.) + + static std::string offsetString( Offset offset ) ; + // Uses the five-character format "+/-hhmm". + // See also RFC2822. + + static void tzset() ; + // Calls std::tzset() (portably). Used for testing. + +private: + static bool equivalent( EpochTime , const BrokenDownTime & ) ; + static bool same( const BrokenDownTime & , const BrokenDownTime & ) ; + static std::tm * gmtime_r( const std::time_t * , std::tm * ) ; + static std::tm * localtime_r( const std::time_t * , std::tm * ) ; + DateTime() ; +} ; + +#endif diff --git a/src/glib/gdatetime_unix.cpp b/src/glib/gdatetime_unix.cpp new file mode 100644 index 0000000..d396945 --- /dev/null +++ b/src/glib/gdatetime_unix.cpp @@ -0,0 +1,41 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdatetime_unix.cpp +// + +#include "gdef.h" +#include "gdatetime.h" + +void G::DateTime::tzset() +{ + std::tzset() ; +} + +std::tm * G::DateTime::gmtime_r( const std::time_t * t , std::tm * p ) +{ + return std::gmtime_r(t,p) ; +} + +std::tm * G::DateTime::localtime_r( const std::time_t * t , std::tm * p ) +{ + return std::localtime_r(t,p) ; +} + diff --git a/src/glib/gdatetime_win32.cpp b/src/glib/gdatetime_win32.cpp new file mode 100644 index 0000000..0c95d5f --- /dev/null +++ b/src/glib/gdatetime_win32.cpp @@ -0,0 +1,43 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdatetime_win32.cpp +// + +#include "gdef.h" +#include "gdatetime.h" + +void G::DateTime::tzset() +{ + std::_tzset() ; +} + +struct std::tm * G::DateTime::gmtime_r( const time_t * t , struct std::tm * p ) +{ + *p = *(std::gmtime(t)) ; + return p ; +} + +struct std::tm * G::DateTime::localtime_r( const time_t * t , struct std::tm * p ) +{ + *p = *(std::localtime(t)) ; + return p ; +} + diff --git a/src/glib/gdebug.h b/src/glib/gdebug.h new file mode 100644 index 0000000..14e55a0 --- /dev/null +++ b/src/glib/gdebug.h @@ -0,0 +1,35 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdebug.h +// + +#ifndef G_DEBUG_H +#define G_DEBUG_H + +#include "gdef.h" +#include "glogoutput.h" +#include "glog.h" +#include "gassert.h" + +// backwards compatibility... +//typedef LogOutput GDebug ; + +#endif diff --git a/src/glib/gdef.h b/src/glib/gdef.h new file mode 100644 index 0000000..149b079 --- /dev/null +++ b/src/glib/gdef.h @@ -0,0 +1,190 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdef.h +// + +// This header is always the first header included in source +// files. It takes care of some portability issues, and +// is a good candidate for precompilation. It requires +// either G_UNIX or G_WIN32 to be defined on the compiler +// command line, although G_UNIX may also be inferred from +// autoconf's HAVE_CONFIG_H. +// + +#ifndef G_DEF_H +#define G_DEF_H + + // Autoconf stuff (mostly commented out) + // + #if HAVE_CONFIG_H + #if 0 + #include + + #if HAVE_UNISTD_H + #include + #include + #endif + + #if HAVE_DIRENT_H + #include + #else + #define dirent direct + #if HAVE_SYS_NDIR_H + #include + #endif + #if HAVE_SYS_DIR_H + #include + #endif + #if HAVE_NDIR_H + #include + #endif + #endif + + #if TIME_WITH_SYS_TIME + #include + #include + #else + #if HAVE_SYS_TIME_H + #include + #else + #include + #endif + #endif + #endif + #if ! defined( G_UNIX ) + #define G_UNIX + #endif + #endif + + // Check operating-system switches + // + #if !defined( G_WIN32 ) && !defined( G_UNIX ) + #error invalid compilation switches + #endif + #if defined( G_WIN32 ) && defined( G_UNIX ) + #error invalid compilation switches + #endif + + // Define supplementary o/s compilation switches + // + #if defined( G_WIN32 ) && ! defined( G_WINDOWS ) + #define G_WINDOWS + #endif + + // Define the compiler and its capabilities + // + #if defined( _MSC_VER ) + #define G_COMPILER_IS_MICROSOFT 1 + // #define G_COMPILER_HAS_... 0 + #endif + #if defined( __GNUC__ ) + #define G_COMPILER_IS_GNU 1 + // #define G_COMPILER_HAS_... 1 + #endif + + // Modify compiler error handling for system headers + // + #if defined( G_COMPILER_IS_MICROSOFT ) + #pragma warning( disable : 4514 ) // don't reenable + #pragma warning( push , 3 ) + #pragma warning( disable : 4201 ) + #pragma warning( disable : 4514 ) // again + #pragma warning( disable : 4663 4018 4146 4018 ) + #pragma warning( disable : 4244 4100 4512 4511 ) + #endif + + // Include main operating-system headers + // + #if defined( G_WINDOWS ) + #include + #include + #else + #include + #include + #endif + + // Restore complier error handling + // + #if defined( G_COMPILER_IS_MICROSOFT ) + #pragma warning( default : 4201 ) + #pragma warning( default : 4663 4018 4146 4018 ) + #pragma warning( default : 4244 4100 4512 4511 ) + #pragma warning( pop ) + #endif + + // Define Windows-style types (under Unix these + // are only used for unimplemented declarations) + // + #if ! defined( G_WINDOWS ) + typedef unsigned char BOOL ; + typedef unsigned int HWND ; + typedef unsigned int HINSTANCE ; + typedef unsigned int HANDLE ; + #endif + + // Include commonly-used system headers + // + #include + #include + #include + #include + #include + #include + #include + #include + + // Define fixed-size types + // + typedef unsigned long g_uint32_t ; + typedef unsigned short g_uint16_t ; + + // Define short-name types + // + typedef unsigned char uchar_t ; + + // Define missing standard types + // + #if defined( G_WINDOWS ) + typedef int ssize_t ; // (should be in sys/types.h) + #endif + + // STL portability macros + // + #if 1 + #define GAllocator(T) + #define GLessAllocator(T1,T2) + #else + #define GAllocator(T) ,std::allocator + #define GLessAllocator(T1,T2) ,std::allocator,std::less + #endif + + // Modify compiler error handling + // + #if G_COMPILER_IS_MICROSOFT + #pragma warning( disable : 4100 ) // unused formal parameter + #pragma warning( disable : 4355 ) // 'this' in initialiser list + #pragma warning( disable : 4511 ) // cannot create default copy ctor + #pragma warning( disable : 4512 ) // cannot create default op=() + #pragma warning( disable : 4786 ) // truncation in debug info + #endif + +#endif + diff --git a/src/glib/gdirectory.cpp b/src/glib/gdirectory.cpp new file mode 100644 index 0000000..ec97ec3 --- /dev/null +++ b/src/glib/gdirectory.cpp @@ -0,0 +1,75 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdirectory.cpp +// + +#include "gdef.h" +#include "gdirectory.h" +#include "gfs.h" +#include "glog.h" + +G::Directory::Directory() : + m_path(".") +{ +} + +G::Directory G::Directory::root() +{ + std::string s ; + s.append( 1U , G::FileSystem::slash() ) ; + return Directory( s ) ; +} + +G::Directory::Directory( const char * path ) : + m_path(path) +{ +} + +G::Directory::Directory( const std::string & path ) : + m_path(path) +{ +} + +G::Directory::Directory( const Path & path ) : + m_path(path) +{ +} + +G::Directory::~Directory() +{ +} + +G::Directory::Directory( const Directory &other ) : + m_path(other.m_path) +{ +} + +G::Directory &G::Directory::operator=( const Directory &rhs ) +{ + m_path = rhs.m_path ; + return *this ; +} + +G::Path G::Directory::path() const +{ + return m_path ; +} + diff --git a/src/glib/gdirectory.h b/src/glib/gdirectory.h new file mode 100644 index 0000000..481a166 --- /dev/null +++ b/src/glib/gdirectory.h @@ -0,0 +1,137 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdirectory.h +// + +#ifndef G_DIRECTORY_H +#define G_DIRECTORY_H + +#include "gdef.h" +#include "gpath.h" +#include +#include + +namespace G +{ + class DirectoryIteratorImp ; + class Directory ; + class DirectoryIterator ; +} ; + +// Class: G::Directory +// Description: An encapsulation of a file system directory +// which allows for iterating through the set of contained +// files. +// See also: Path, FileSystem, File +// +class G::Directory +{ +public: + Directory() ; + // Default constructor for the current directory. + + explicit Directory( const char * path ) ; + // Constructor. + + explicit Directory( const Path & path ) ; + // Constructor. + + explicit Directory( const std::string & path ) ; + // Constructor. + + virtual ~Directory() ; + // Virtual destructor. + + bool valid( bool for_creating_files = false ) const ; + // Returns true if the object + // represents a valid directory. + // + // Does additional checks if the + // 'for-creating-files' parameter + // is true. But note that the + // answer is not definitive -- + // file creation may fail, even + // if valid() returns true. + + Path path() const ; + // Returns the directory's path. + + Directory( const Directory &other ) ; + // Copy constructor. + + Directory &operator=( const Directory & ) ; + // Assignment operator. + + static Directory root() ; + // Returns a root directory object. For DOSy file + // systems this will not contain a drive part. + +private: + Path m_path ; +} ; + +// Class: G::DirectoryIterator +// Description: An iterator for Directory. +// The iteration model is +/// while(iter.more()) { (void)iter.file() ; } +// +class G::DirectoryIterator +{ +public: + explicit DirectoryIterator( const Directory &dir , const char *wc = NULL ) ; + // Constructor taking a directory reference + // and an optional wildcard specification. + + ~DirectoryIterator() ; + // Destructor. + + bool error() const ; + // Returns true on error. The caller should stop the iteration. + + bool more() ; + // Returns true if more. + + bool isDir() const ; + // Returns true if the current item is a directory. + + std::string modificationTimeString() const ; + // Returns the last-modified time for the file in an undefined + // format -- used for comparison. + + std::string sizeString() const ; + // Returns the file size as a decimal string. The value + // may be more than 32 bits. See also class Number. + + Path filePath() const ; + // Returns the path of the current item. + + Path fileName() const ; + // Returns the name of the current item. + +private: + DirectoryIterator( const DirectoryIterator & ) ; + void operator=( const DirectoryIterator & ) ; + +private: + DirectoryIteratorImp *m_imp ; +} ; + +#endif diff --git a/src/glib/gdirectory_unix.cpp b/src/glib/gdirectory_unix.cpp new file mode 100644 index 0000000..7e46b7c --- /dev/null +++ b/src/glib/gdirectory_unix.cpp @@ -0,0 +1,241 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdirectory_unix.cpp +// + +#include "gdef.h" +#include "gdirectory.h" +#include "gfs.h" +#include "gdebug.h" +#include "glog.h" +#include +#include +#include + +namespace G +{ + class DirectoryIteratorImp ; +} ; + +bool G::Directory::valid( bool for_creation ) const +{ + bool rc = true ; + struct stat statbuf ; + if( ::stat( m_path.pathCstr() , &statbuf ) ) + { + rc = false ; + } + else if( !(statbuf.st_mode & S_IFDIR) ) + { + rc = false ; + } + else + { + DIR * p = ::opendir( m_path.pathCstr() ) ; + if( p == NULL ) + rc = false ; + else + ::closedir( p ) ; + } + + if( rc && for_creation ) + { + // (see also GNU/Linux ::euidaccess()) + if( 0 != ::access( m_path.pathCstr() , W_OK ) ) + rc = false ; + } + + G_DEBUG( "G::Directory::valid: \"" << m_path.str() << "\" is " << (rc?"":"not ") << "a directory" ) ; + return rc ; +} + +// === + +// Class: G::DirectoryIteratorImp +// Description: A pimple-pattern implementation class for DirectoryIterator. +// +class G::DirectoryIteratorImp +{ +private: + glob_t m_glob ; + Directory m_dir ; + bool m_first ; + size_t m_index ; + bool m_error ; + +public: + DirectoryIteratorImp( const Directory &dir , const std::string & wildcard ) ; + ~DirectoryIteratorImp() ; + bool isDir() const ; + bool more() ; + bool error() const ; + std::string modificationTimeString() const ; + std::string sizeString() const ; + Path filePath() const ; + Path fileName() const ; + +private: + void operator=( const DirectoryIteratorImp & ) ; + DirectoryIteratorImp( const DirectoryIteratorImp & ) ; + static int onError( const char * path , int errno ) ; +} ; + +// === + +G::DirectoryIterator::DirectoryIterator( const Directory &dir , const char *wc ) +{ + m_imp = new DirectoryIteratorImp( dir , wc ) ; +} + +bool G::DirectoryIterator::error() const +{ + return m_imp->error() ; +} + +bool G::DirectoryIterator::more() +{ + return m_imp->more() ; +} + +G::Path G::DirectoryIterator::filePath() const +{ + return m_imp->filePath() ; +} + +G::Path G::DirectoryIterator::fileName() const +{ + return m_imp->fileName() ; +} + +bool G::DirectoryIterator::isDir() const +{ + return m_imp->isDir() ; +} + +std::string G::DirectoryIterator::modificationTimeString() const +{ + return m_imp->modificationTimeString() ; +} + +std::string G::DirectoryIterator::sizeString() const +{ + return m_imp->sizeString() ; +} + +G::DirectoryIterator::~DirectoryIterator() +{ + delete m_imp ; +} + +// === + +//static +int G::DirectoryIteratorImp::onError( const char * , int ) +{ + const int abort = 1 ; + return abort ; +} + +G::DirectoryIteratorImp::DirectoryIteratorImp( const Directory &dir , + const std::string & wildcard ) : + m_dir(dir) , + m_first(true) , + m_index(0U) , + m_error(false) +{ + m_glob.gl_pathc = 0 ; + m_glob.gl_pathv = NULL ; + + Path wild_path( m_dir.path() ) ; + wild_path.pathAppend( wildcard.empty() ? std::string("*") : wildcard ) ; + + G_DEBUG( "G::DirectoryIteratorImp::ctor: glob(\"" << wild_path << "\")" ) ; + + int flags = 0 | GLOB_ERR ; + int error = ::glob( wild_path.pathCstr() , flags , onError , &m_glob ) ; + if( error ) + { + G_DEBUG( "G::DirectoryIteratorImp::ctor: glob() error: " << error ) ; + m_error = true ; + } + else + { + G_ASSERT( m_glob.gl_pathv != NULL ) ; + G_ASSERT( m_glob.gl_pathc == 0 || m_glob.gl_pathv[0] != NULL ) ; + } +} + +bool G::DirectoryIteratorImp::error() const +{ + return m_error ; +} + +bool G::DirectoryIteratorImp::more() +{ + if( m_error ) + return false ; + + if( ! m_first ) + m_index++ ; + m_first = false ; + + if( m_index >= m_glob.gl_pathc ) + return false ; + + return true ; +} + +G::Path G::DirectoryIteratorImp::filePath() const +{ + G_ASSERT( m_index < m_glob.gl_pathc ) ; + G_ASSERT( m_glob.gl_pathv != NULL ) ; + G_ASSERT( m_glob.gl_pathv[m_index] != NULL ) ; + const char * file_path = m_glob.gl_pathv[m_index] ; + return Path( file_path ) ; +} + +G::Path G::DirectoryIteratorImp::fileName() const +{ + return Path( filePath().basename() ) ; +} + +bool G::DirectoryIteratorImp::isDir() const +{ + Directory dir( filePath().str() ) ; + return dir.valid() ; +} + +G::DirectoryIteratorImp::~DirectoryIteratorImp() +{ + if( ! m_error ) + ::globfree( &m_glob ) ; +} + +std::string G::DirectoryIteratorImp::modificationTimeString() const +{ + return std::string() ; // for now +} + +std::string G::DirectoryIteratorImp::sizeString() const +{ + return std::string("0") ; // for now +} + diff --git a/src/glib/gdirectory_win32.cpp b/src/glib/gdirectory_win32.cpp new file mode 100644 index 0000000..c06bee5 --- /dev/null +++ b/src/glib/gdirectory_win32.cpp @@ -0,0 +1,302 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdirectory_win32.cpp +// + +#include "gdef.h" +#include "gdirectory.h" +#include "gfs.h" +#include "gnumber.h" +#include "gdebug.h" +#include "glog.h" + +namespace G +{ + class DirectoryIteratorImp ; +} ; + +bool G::Directory::valid( bool for_creation ) const +{ + DWORD attributes = ::GetFileAttributes( m_path.pathCstr() ) ; + if( attributes == 0xFFFFFFFF ) + { + (void)::GetLastError() ; + return false ; + } + return ( attributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 ; +} + +// === + +// Class: G::DirectoryIteratorImp +// Description: A pimple-pattern implementation class for DirectoryIterator. +// +class G::DirectoryIteratorImp +{ +private: + WIN32_FIND_DATA m_context ; + HANDLE m_handle ; + Directory m_dir ; + bool m_error ; + bool m_first ; + bool m_special ; // if wc = ".." -- FindFirstFile returns ".."'s proper name -- this class returns ".." + +public: + DirectoryIteratorImp( const Directory &dir , const char *wildcard ) ; + ~DirectoryIteratorImp() ; + bool isDir() const ; + bool more() ; + bool error() const ; + std::string modificationTimeString() const ; + std::string sizeString() const ; + Path filePath() const ; + Path fileName() const ; + +private: + void operator=( const DirectoryIteratorImp & ) ; + DirectoryIteratorImp( const DirectoryIteratorImp & ) ; +} ; + +// === + +G::DirectoryIterator::DirectoryIterator( const Directory &dir , const char *wc ) +{ + m_imp = new DirectoryIteratorImp( dir , wc ) ; +} + +bool G::DirectoryIterator::error() const +{ + return m_imp->error() ; +} + +bool G::DirectoryIterator::more() +{ + return m_imp->more() ; +} + +G::Path G::DirectoryIterator::filePath() const +{ + return m_imp->filePath() ; +} + +G::Path G::DirectoryIterator::fileName() const +{ + return m_imp->fileName() ; +} + +bool G::DirectoryIterator::isDir() const +{ + return m_imp->isDir() ; +} + +std::string G::DirectoryIterator::modificationTimeString() const +{ + return m_imp->modificationTimeString() ; +} + +std::string G::DirectoryIterator::sizeString() const +{ + return m_imp->sizeString() ; +} + +G::DirectoryIterator::~DirectoryIterator() +{ + delete m_imp ; +} + +// === + +G::DirectoryIteratorImp::DirectoryIteratorImp( const Directory & dir , + const char *wildcard ) : + m_dir(dir) , + m_error(false) , + m_first(true) , + m_special(false) +{ + if( wildcard == NULL ) + { + wildcard = "*.*" ; + } + else if( std::string(wildcard) == ".." ) + { + G_DEBUG( "DirectoryIteratorImp: special work-round for .." ) ; + m_special = true ; + return ; + } + + Path wild_path( m_dir.path() ) ; + wild_path.pathAppend( wildcard ) ; + + G_DEBUG( "G::DirectoryIteratorImp::ctor: FindFirstFile(\"" + << wild_path << "\")" ) ; + + m_handle = ::FindFirstFile( wild_path.pathCstr() , &m_context ) ; + if( m_handle == INVALID_HANDLE_VALUE ) + { + DWORD err = ::GetLastError() ; + if( err == ERROR_FILE_NOT_FOUND ) + { + G_DEBUG( "G::DirectoryIteratorImp::ctor: none" ) ; + } + else + { + m_error = true ; + G_DEBUG( "G::DirectoryIteratorImp::ctor: error " << err << " for " + << wild_path ) ; + } + } + else + { + G_DEBUG( "G::DirectoryIteratorImp::ctor: first \"" + << m_context.cFileName << "\"" ) ; + } +} + +bool G::DirectoryIteratorImp::error() const +{ + return m_error ; +} + +bool G::DirectoryIteratorImp::more() +{ + if( m_special ) + { + bool rc = m_first ; + m_first = false ; + return rc ; + } + + if( m_handle == INVALID_HANDLE_VALUE ) + return false ; + + if( m_first ) + { + m_first = false ; + if( ::strcmp(m_context.cFileName,".") && + ::strcmp(m_context.cFileName,"..") ) + return true ; + + G_DEBUG( "G::DirectoryIteratorImp::more: ignoring " << m_context.cFileName); + } + + for(;;) + { + bool rc = ::FindNextFile( m_handle , &m_context ) != 0 ; + if( !rc ) + { + DWORD err = ::GetLastError() ; + if( err == ERROR_NO_MORE_FILES ) + { + G_DEBUG( "G::DirectoryIteratorImp::more: no more" ) ; + } + else + { + G_DEBUG( "G::DirectoryIteratorImp::more: error" ) ; + m_error = true ; + } + ::FindClose( m_handle ) ; + m_handle = INVALID_HANDLE_VALUE ; + return false ; + } + + // go round again if . or .. + if( ::strcmp(m_context.cFileName,".") && + ::strcmp(m_context.cFileName,"..") ) + { + G_DEBUG( "G::DirectoryIteratorImp::more: " << m_context.cFileName ) ; + break ; + } + else + { + G_DEBUG( "G::DirectoryIteratorImp::more: ignoring " << m_context.cFileName ) ; + } + } + + return true ; +} + +G::Path G::DirectoryIteratorImp::filePath() const +{ + Path file_path( m_dir.path() ) ; + if( m_special ) + { + file_path.pathAppend( ".." ) ; + } + else + { + G_ASSERT( m_handle != INVALID_HANDLE_VALUE ) ; + file_path.pathAppend( m_context.cFileName ) ; + } + return file_path ; +} + +G::Path G::DirectoryIteratorImp::fileName() const +{ + if( m_special ) + { + return Path("..") ; + } + else + { + G_ASSERT( m_handle != INVALID_HANDLE_VALUE ) ; + return Path(m_context.cFileName) ; + } +} + +bool G::DirectoryIteratorImp::isDir() const +{ + return m_special || m_context.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ; +} + +G::DirectoryIteratorImp::~DirectoryIteratorImp() +{ + if( m_handle != INVALID_HANDLE_VALUE ) + ::FindClose( m_handle ) ; +} + +std::string G::DirectoryIteratorImp::modificationTimeString() const +{ + if( m_special ) + { + // ?? + G_ASSERT( !"modificationTimeString() not fully implemented for .." ) ; + return std::string() ; + } + + char buffer[50U] ; + ::sprintf( buffer , "%lX%08lX" , + (unsigned long)m_context.ftLastWriteTime.dwHighDateTime , + (unsigned long)m_context.ftLastWriteTime.dwLowDateTime ) ; + + return std::string(buffer) ; +} + +std::string G::DirectoryIteratorImp::sizeString() const +{ + if( m_special ) + { + return std::string( "0" ) ; + } + + Number size( m_context.nFileSizeHigh , m_context.nFileSizeLow ) ; + return size.displayString() ; +} + diff --git a/src/glib/gexception.cpp b/src/glib/gexception.cpp new file mode 100644 index 0000000..ec03412 --- /dev/null +++ b/src/glib/gexception.cpp @@ -0,0 +1,82 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gexception.cpp +// + +#include "gdef.h" +#include "gexception.h" + +G::Exception::Exception() +{ +} + +G::Exception::Exception( const char * what ) : + m_what(what?what:"") +{ +} + +G::Exception::Exception( const std::string & what ) : + m_what(what) +{ +} + +G::Exception::~Exception() +{ +} + +const char * G::Exception::what() const +{ + return m_what.c_str() ; +} + +void G::Exception::append( const char * more ) +{ + if( more != NULL && *more != '\0' ) + { + m_what += ": " ; + m_what += more ; + } +} + +void G::Exception::append( const std::string & more ) +{ + m_what += ": " ; + m_what += more ; +} + + +void G::Exception::append( std::ostream & stream ) +{ + std::stringstream * ss = dynamic_cast(&stream) ; + if( ss != NULL ) + { + append( ss->str() ) ; + } +} + +void G::Exception::prepend( const char * context ) +{ + if( context != NULL && *context != '\0' ) + { + m_what = std::string(context) + ": " + m_what ; + } +} + diff --git a/src/glib/gexception.h b/src/glib/gexception.h new file mode 100644 index 0000000..c389cc3 --- /dev/null +++ b/src/glib/gexception.h @@ -0,0 +1,89 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gexception.h +// + +#ifndef G_EXCEPTION_H +#define G_EXCEPTION_H + +#include "gdef.h" +#include +#include + +namespace G +{ + class Exception ; +} ; + +// Class: G::Exception +// Description: A general-purpose exception class derived from std::exception +// and containing a std::string. +// +class G::Exception : public std::exception +{ +protected: + std::string m_what ; + +public: + Exception() ; + // Default constructor. + + explicit Exception( const char * what ) ; + // Constructor. + + explicit Exception( const std::string & what ) ; + // Constructor. + + virtual ~Exception() ; + // Destructor. + + virtual const char * what() const ; + // Override from std::exception. + + void prepend( const char * context ) ; + // Prepends context to the what string. + // Inserts a separator as needed. + + void append( const char * more ) ; + // Appends 'more' to the what string. + // Inserts a separator as needed. + + void append( const std::string & more ) ; + // Appends 'more' to the what string. + // Inserts a separator as needed. + + void append( std::ostream & s ) ; + // Appends the contents of the given std::stringstream + // (sic) to the what string. Does nothing if the + // dynamic type of 's' is not a std::stringstream. + // Inserts a separator as needed. + // + // This method allows a derived-class exception + // to be constructed and thrown on one + // line using iostream formatting. + // Eg. throw Error( std::stringstream() << a << b ) ; +} ; + +#define G_EXCEPTION( class_name , description ) class class_name : public G::Exception { public: class_name() { m_what = description ; } public: explicit class_name ( std::ostream & stream ) { m_what = description ; append(stream) ; } public: explicit class_name( const char * more ) { m_what = description ; append(more) ; } public: explicit class_name( const std::string & more ) { m_what = description ; append(more) ; } } +#define G_EXCEPTION_ G_EXCEPTION + +#endif + diff --git a/src/glib/gfile.cpp b/src/glib/gfile.cpp new file mode 100644 index 0000000..7991cb1 --- /dev/null +++ b/src/glib/gfile.cpp @@ -0,0 +1,85 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gfile.cpp +// + +#include "gdef.h" +#include "gfile.h" +#include "glog.h" +#include +#include + +bool G::File::remove( const Path & path , const G::File::NoThrow & ) +{ + bool rc = 0 == std::remove( path.pathCstr() ) ; + G_DEBUG( "G::File::remove: \"" << path << "\": success=" << rc ) ; + return rc ; +} + +void G::File::remove( const Path & path ) +{ + if( 0 != std::remove( path.pathCstr() ) ) + { + //int error = std::errno ; + throw CannotRemove( path.str() ) ; + } + G_DEBUG( "G::File::remove: \"" << path << "\"" ) ; +} + +bool G::File::rename( const Path & from , const Path & to , const NoThrow & ) +{ + bool rc = 0 == std::rename( from.pathCstr() , to.pathCstr() ) ; + G_DEBUG( "G::File::rename: \"" << from << "\" -> \"" << to << "\": success=" << rc ) ; + return rc ; +} + +void G::File::rename( const Path & from , const Path & to ) +{ + if( 0 != std::rename( from.pathCstr() , to.pathCstr() ) ) + { + //int error = std::errno ; + throw CannotRename( from.str() ) ; + } + G_DEBUG( "G::File::rename: \"" << from << "\" -> \"" << to << "\"" ) ; +} + +void G::File::copy( const Path & from , const Path & to ) +{ + if( !copy(from,to,NoThrow()) ) + throw CannotCopy( from.str() ) ; +} + +bool G::File::copy( const Path & from , const Path & to , const NoThrow & ) +{ + std::ifstream in( from.str().c_str() , std::ios_base::binary | std::ios_base::in ) ; + std::ofstream out( to.str().c_str() , std::ios_base::binary | std::ios_base::out | std::ios_base::trunc ) ; + char c ; + while( in.get(c) ) + out << c ; + return in.eof() && out.good() ; +} + +void G::File::mkdir( const Path & dir ) +{ + if( ! mkdir( dir , NoThrow() ) ) + throw CannotMkdir( dir.str() ) ; +} + diff --git a/src/glib/gfile.h b/src/glib/gfile.h new file mode 100644 index 0000000..61cfd6e --- /dev/null +++ b/src/glib/gfile.h @@ -0,0 +1,76 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gfile.h +// + +#ifndef G_FILE_H +#define G_FILE_H + +#include "gdef.h" +#include "gpath.h" +#include "gexception.h" +#include // std::remove() + +namespace G +{ + class File ; +} ; + +// Class: G::File +// Description: A simple static class for dealing with files. +// See also: Path, FileSystem, Directory +// +class G::File +{ +public: + G_EXCEPTION( CannotRemove , "cannot delete file" ) ; + G_EXCEPTION( CannotRename , "cannot rename file" ) ; + G_EXCEPTION( CannotCopy , "cannot copy file" ) ; + G_EXCEPTION( CannotMkdir , "cannot mkdir" ) ; + class NoThrow // An overload discriminator class for File methods. + {} ; + + static bool remove( const Path & path , const NoThrow & ) ; + // Deletes the file or directory. Returns false on error. + + static void remove( const Path & path ) ; + // Deletes the file or directory. Throws an exception on error. + + static bool rename( const Path & from , const Path & to , const NoThrow & ) ; + // Renames the file. Returns false on error. + + static void rename( const Path & from , const Path & to ) ; + // Renames the file. + + static bool copy( const Path & from , const Path & to , const NoThrow & ) ; + // Copies a file. Returns false on error. + + static void copy( const Path & from , const Path & to ) ; + // Copies a file. + + static bool mkdir( const Path & dir , const NoThrow & ) ; + // Creates a directory. Returns false on error. + + static void mkdir( const Path & dir ) ; + // Creates a directory. +} ; + +#endif diff --git a/src/glib/gfile_unix.cpp b/src/glib/gfile_unix.cpp new file mode 100644 index 0000000..607567c --- /dev/null +++ b/src/glib/gfile_unix.cpp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gfile_unix.cpp +// + +#include "gdef.h" +#include "gfile.h" +#include + +bool G::File::mkdir( const Path & dir , const NoThrow & ) +{ + return 0 == ::mkdir( dir.str().c_str() , S_IRUSR | S_IWUSR | S_IXUSR ) ; +} + diff --git a/src/glib/gfile_win32.cpp b/src/glib/gfile_win32.cpp new file mode 100644 index 0000000..46496dc --- /dev/null +++ b/src/glib/gfile_win32.cpp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gfile_win32.cpp +// + +#include "gdef.h" +#include "gfile.h" +#include +#include + +bool G::File::mkdir( const Path & dir , const NoThrow & ) +{ + return 0 == ::_mkdir( dir.str().c_str() ) ; +} + diff --git a/src/glib/gfs.h b/src/glib/gfs.h new file mode 100644 index 0000000..38b8306 --- /dev/null +++ b/src/glib/gfs.h @@ -0,0 +1,52 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gfs.h +// + +#ifndef G_FS_H +#define G_FS_H + +#include "gdef.h" + +namespace G +{ + class FileSystem ; +} ; + +// Class: G::FileSystem +// Description: Provides information about the +// local operating system's file system +// conventions. +// See also: Path, File +// +class G::FileSystem +{ +public: + static const char *nullDevice() ; + static bool allowsSpaces() ; + static char slash() ; // separator + static char nonSlash() ; // to be converted to slash() + static bool caseSensitive() ; + static bool usesDriveLetters() ; // : + static bool leadingDoubleSlash() ; // win32 network paths +} ; + +#endif diff --git a/src/glib/gfs_unix.cpp b/src/glib/gfs_unix.cpp new file mode 100644 index 0000000..f43c4d8 --- /dev/null +++ b/src/glib/gfs_unix.cpp @@ -0,0 +1,61 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gfs_unix.cpp +// + +#include "gdef.h" +#include "gfs.h" + +const char *G::FileSystem::nullDevice() +{ + return "/dev/null" ; +} + +char G::FileSystem::slash() +{ + return '/' ; +} + +char G::FileSystem::nonSlash() +{ + return '\\' ; +} + +bool G::FileSystem::allowsSpaces() +{ + return false ; +} + +bool G::FileSystem::caseSensitive() +{ + return true ; +} + +bool G::FileSystem::usesDriveLetters() +{ + return false ; +} + +bool G::FileSystem::leadingDoubleSlash() +{ + return false ; +} + diff --git a/src/glib/gfs_win32.cpp b/src/glib/gfs_win32.cpp new file mode 100644 index 0000000..0ee74bf --- /dev/null +++ b/src/glib/gfs_win32.cpp @@ -0,0 +1,60 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gfs_win32.cpp +// + +#include "gdef.h" +#include "gfs.h" + +const char *G::FileSystem::nullDevice() +{ + return "NUL" ; +} + +char G::FileSystem::slash() +{ + return '\\' ; +} + +char G::FileSystem::nonSlash() +{ + return '/' ; +} + +bool G::FileSystem::allowsSpaces() +{ + return true ; // (but false for win16) +} + +bool G::FileSystem::caseSensitive() +{ + return false ; +} + +bool G::FileSystem::usesDriveLetters() +{ + return true ; +} + +bool G::FileSystem::leadingDoubleSlash() +{ + return true ; // (but false for win16) +} diff --git a/src/glib/ggetopt.cpp b/src/glib/ggetopt.cpp new file mode 100644 index 0000000..6e5c6b7 --- /dev/null +++ b/src/glib/ggetopt.cpp @@ -0,0 +1,500 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// ggetopt.cpp +// + +#include "gdef.h" +#include "glog.h" +#include "gstrings.h" +#include "gstr.h" +#include "ggetopt.h" +#include "gassert.h" +#include "gdebug.h" + +static void GetOpt__pushBack( void * out , const std::string & s ) +{ + reinterpret_cast(out)->push_back(s) ; +} + +G::GetOpt::GetOpt( const Arg & args_in , const std::string & spec , + char sep_major , char sep_minor , char escape ) : + m_args(args_in) +{ + parseSpec( spec , sep_major , sep_minor , escape ) ; + size_t n = parseArgs( args_in ) ; + remove( n ) ; +} + +void G::GetOpt::parseSpec( const std::string & spec , char sep_major , char sep_minor , char escape ) +{ + if( spec.find(sep_minor) == std::string::npos ) + parseOldSpec( spec ) ; + else + parseNewSpec( spec , sep_major , sep_minor , escape ) ; +} + +void G::GetOpt::parseOldSpec( const std::string & spec ) +{ + for( size_t i = 0U ; i < spec.length() ; i++ ) + { + char c = spec.at(i) ; + if( c != ':' ) + { + bool valued = (i+1U) < spec.length() && spec.at(i+1U) == ':' ; + addSpec( c , valued ) ; + } + } +} + +void G::GetOpt::parseNewSpec( const std::string & spec , char sep_major , + char sep_minor , char escape ) +{ + Strings outer ; + std::string ws_major( 1U , sep_major ) ; + G::Str::splitIntoFields( spec , outer , ws_major , escape , false ) ; + for( Strings::iterator p = outer.begin() ; p != outer.end() ; ++p ) + { + StringArray inner ; + std::string ws_minor( 1U , sep_minor ) ; + G::Str::splitIntoFields( *p , inner , ws_minor , escape ) ; + if( inner.size() != 5U ) + throw InvalidSpecification(std::stringstream() << "\"" << *p << "\" (" << ws_minor << ")") ; + bool valued = G::Str::toUInt( inner[3U] ) != 0U ; + addSpec( inner[0U].at(0U) , inner[1U] , inner[2U] , valued , inner[4U] ) ; + } +} + +void G::GetOpt::addSpec( char c , bool valued ) +{ + addSpec( c , std::string() , std::string() , valued , std::string() ) ; +} + +void G::GetOpt::addSpec( char c , const std::string & name , const std::string & description , + bool valued , const std::string & value_description ) +{ + if( c == '\0' ) + throw InvalidSpecification() ; + + std::pair rc = + m_spec_map.insert( std::make_pair(c,SwitchSpec(c,name,description,valued,value_description)) ) ; + + if( ! rc.second ) + throw InvalidSpecification("duplication") ; +} + +bool G::GetOpt::valued( const std::string & name ) const +{ + return valued(key(name)) ; +} + +bool G::GetOpt::valued( char c ) const +{ + SwitchSpecMap::const_iterator p = m_spec_map.find( c ) ; + if( p == m_spec_map.end() ) + return false ; + else + return (*p).second.valued ; +} + +char G::GetOpt::key( const std::string & name ) const +{ + for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p ) + { + if( (*p).second.name == name ) + { + return (*p).first ; + } + } + G_DEBUG( "G::GetOpt::key: " << name << " not found" ) ; + return '\0' ; +} + +//static +size_t G::GetOpt::wrapDefault() +{ + return 79U ; +} + +//static +size_t G::GetOpt::widthLimit( size_t w ) +{ + return (w != 0U && w < 50U) ? 50U : w ; +} + +void G::GetOpt::showUsage( std::ostream & stream , const std::string & exe , const std::string & args , + size_t tab_stop , size_t width ) const +{ + stream + << usageSummary(exe,args,width) << std::endl + << usageHelp(tab_stop,width) ; +} + +std::string G::GetOpt::usageSummary( const std::string & exe , const std::string & args , size_t width ) const +{ + std::string s = std::string("usage: ") + exe + " " + usageSummarySwitches() + args ; + if( width != 0U ) + { + return G::Str::wrap( s , "" , " " , widthLimit(width) ) ; + } + else + { + return s ; + } +} + +std::string G::GetOpt::usageSummarySwitches() const +{ + return usageSummaryPartOne() + usageSummaryPartTwo() ; +} + +std::string G::GetOpt::usageSummaryPartOne() const +{ + // summarise the single-character switches, excluding those which take a value + std::stringstream ss ; + bool first = true ; + for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p ) + { + if( ! (*p).second.valued ) + { + if( first ) + ss << "[-" ; + first = false ; + ss << (*p).first ; + } + } + + std::string s = ss.str() ; + if( s.length() ) s.append( "] " ) ; + return s ; +} + +std::string G::GetOpt::usageSummaryPartTwo() const +{ + std::stringstream ss ; + const char * sep = "" ; + for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p ) + { + ss << sep << "[" ; + if( (*p).second.name.length() ) + { + ss << "--" << (*p).second.name ; + } + else + { + ss << "-" << (*p).first ; + } + if( (*p).second.valued ) + { + std::string vd = (*p).second.value_description ; + if( vd.empty() ) vd = "value" ; + ss << " <" << vd << ">" ; + } + ss << "]" ; + sep = " " ; + } + return ss.str() ; +} + +std::string G::GetOpt::usageHelp( size_t tab_stop , size_t width ) const +{ + return usageHelpCore( " " , tab_stop , widthLimit(width) ) ; +} + +std::string G::GetOpt::usageHelpCore( const std::string & prefix , size_t tab_stop , size_t width ) const +{ + std::string result ; + for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p ) + { + std::string line( prefix ) ; + line.append( "-" ) ; + line.append( 1U , (*p).first ) ; + + if( (*p).second.name.length() ) + { + line.append( ",--" ) ; + line.append( (*p).second.name ) ; + } + + if( (*p).second.valued ) + { + std::string vd = (*p).second.value_description ; + if( vd.empty() ) vd = "value" ; + line.append( " <" ) ; + line.append( vd ) ; + line.append( ">" ) ; + } + line.append( 1U , ' ' ) ; + + if( line.length() < tab_stop ) + line.append( tab_stop-line.length() , ' ' ) ; + + line.append( (*p).second.description ) ; + + if( width ) + { + std::string indent( tab_stop , ' ' ) ; + line = G::Str::wrap( line , "" , indent , width ) ; + } + + result.append( line ) ; + } + return result ; +} + +size_t G::GetOpt::parseArgs( const Arg & args_in ) +{ + size_t i = 1U ; + for( ; i < args_in.c() ; i++ ) + { + const std::string & arg = args_in.v(i) ; + if( arg == "--" ) + { + i++ ; + break ; + } + + if( isSwitchSet(arg) ) + { + for( size_t n = 1U ; n < arg.length() ; n++ ) + processSwitch( arg.at(n) ) ; + } + else if( isOldSwitch(arg) ) + { + char c = arg.at(1U) ; + if( valued(c) && (i+1U) >= args_in.c() ) + errorNoValue( c ) ; + else if( valued(c) ) + processSwitch( c , args_in.v(++i) ) ; + else + processSwitch( c ) ; + } + else if( isNewSwitch(arg) ) + { + std::string name = arg.substr( 2U ) ; + if( valued(name) && (i+1U) >= args_in.c() ) + errorNoValue( name ) ; + else if( valued(name) ) + processSwitch( name , args_in.v(++i) ) ; + else + processSwitch( name ) ; + } + else + { + break ; + } + } + i-- ; + G_DEBUG( "G::GetOpt::parseArgs: removing " << i << " switch args" ) ; + return i ; +} + +bool G::GetOpt::isOldSwitch( const std::string & arg ) const +{ + return + ( arg.length() > 1U && arg.at(0U) == '-' ) && + ! isNewSwitch( arg ) ; +} + +bool G::GetOpt::isNewSwitch( const std::string & arg ) const +{ + return arg.length() > 2U && arg.at(0U) == '-' && arg.at(1U) == '-' ; +} + +bool G::GetOpt::isSwitchSet( const std::string & arg ) const +{ + return isOldSwitch(arg) && arg.length() > 2U ; +} + +void G::GetOpt::errorNoValue( char c ) +{ + std::string e("no value supplied for -") ; + e.append( 1U , c ) ; + m_errors.push_back( e ) ; +} + +void G::GetOpt::errorNoValue( const std::string & name ) +{ + std::string e("no value supplied for --") ; + e.append( name ) ; + m_errors.push_back( e ) ; +} + +void G::GetOpt::errorUnknownSwitch( char c ) +{ + std::string e("invalid switch: -") ; + e.append( 1U , c ) ; + m_errors.push_back( e ) ; +} + +void G::GetOpt::errorUnknownSwitch( const std::string & name ) +{ + std::string e("invalid switch: --") ; + e.append( name ) ; + m_errors.push_back( e ) ; +} + +void G::GetOpt::processSwitch( const std::string & name ) +{ + if( !valid(name) ) + { + errorUnknownSwitch( name ) ; + return ; + } + + char c = key(name) ; + if( valued(c) ) + { + errorNoValue( name ) ; + return ; + } + + m_map.insert( std::make_pair(c,std::make_pair(false,std::string())) ) ; +} + +void G::GetOpt::processSwitch( const std::string & name , const std::string & value ) +{ + if( !valid(name) ) + { + errorUnknownSwitch( name ) ; + return ; + } + + char c = key(name) ; + m_map.insert( std::make_pair(c,std::make_pair(true,value)) ) ; +} + +void G::GetOpt::processSwitch( char c ) +{ + if( !valid(c) ) + { + errorUnknownSwitch( c ) ; + return ; + } + + if( valued(c) ) + { + errorNoValue( c ) ; + return ; + } + + m_map.insert( std::make_pair(c,std::make_pair(false,std::string())) ) ; +} + +void G::GetOpt::processSwitch( char c , const std::string & value ) +{ + if( !valid(c) ) + errorUnknownSwitch( c ) ; + + m_map.insert( std::make_pair(c,std::make_pair(true,value)) ) ; +} + +G::Strings G::GetOpt::errorList() const +{ + return m_errors ; +} + +bool G::GetOpt::contains( char c ) const +{ + SwitchMap::const_iterator p = m_map.find( c ) ; + return p != m_map.end() ; +} + +bool G::GetOpt::contains( const std::string & name ) const +{ + char c = key(name) ; + SwitchMap::const_iterator p = m_map.find( c ) ; + return p != m_map.end() ; +} + +std::string G::GetOpt::value( char c ) const +{ + G_ASSERT( contains(c) ) ; + SwitchMap::const_iterator p = m_map.find( c ) ; + Value value_pair = (*p).second ; + return value_pair.second ; +} + +std::string G::GetOpt::value( const std::string & name ) const +{ + return value( key(name) ) ; +} + +G::Arg G::GetOpt::args() const +{ + return m_args ; +} + +void G::GetOpt::show( std::ostream &stream , std::string prefix ) const +{ + for( SwitchMap::const_iterator p = m_map.begin() ; p != m_map.end() ; ++p ) + { + char c = (*p).first ; + Value v = (*p).second ; + + SwitchSpecMap::const_iterator q = m_spec_map.find( c ) ; + std::string name ; + if( q != m_spec_map.end() ) + name = (*q).second.name ; + + stream << prefix << "--" << name ; + if( v.first ) + stream << " = \"" << v.second << "\"" ; + stream << std::endl ; + } +} + +bool G::GetOpt::hasErrors() const +{ + return m_errors.size() != 0U ; +} + +void G::GetOpt::showErrors( std::ostream &stream , std::string prefix_1 , + std::string prefix_2 ) const +{ + if( m_errors.size() != 0U ) + { + for( Strings::const_iterator p = m_errors.begin() ; + p != m_errors.end() ; ++p ) + { + stream << prefix_1 << prefix_2 << *p << std::endl ; + } + } +} + +void G::GetOpt::remove( size_t n ) +{ + if( n != 0U ) + { + m_args.removeAt(1U,n-1U) ; + } +} + +bool G::GetOpt::valid( const std::string & name ) const +{ + return valid( key(name) ) ; +} + +bool G::GetOpt::valid( char c ) const +{ + return m_spec_map.find( c ) != m_spec_map.end() ; +} + + diff --git a/src/glib/ggetopt.h b/src/glib/ggetopt.h new file mode 100644 index 0000000..3f7d3f9 --- /dev/null +++ b/src/glib/ggetopt.h @@ -0,0 +1,175 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// ggetopt.h +// + +#ifndef G_GETOPT_H +#define G_GETOPT_H + +#include "gdef.h" +#include "garg.h" +#include "gstrings.h" +#include "gexception.h" +#include +#include +#include + +namespace G +{ + class GetOpt ; +} ; + +// Class: G::GetOpt +// Description: A command line switch parser. +// +class G::GetOpt +{ +public: + typedef std::vector StringArray ; + G_EXCEPTION( InvalidSpecification , "invalid options specification string" ) ; + + GetOpt( const Arg & arg , const std::string & spec , + char sep_major = '|' , char sep_minor = '/' , char escape = '\\' ) ; + // Constructor taking a Arg reference and a + // specification string. Supports old-fashioned + // getopt specification strings such as "p:dv", and + // also new-stye specifications like + // "p/port/port number/1|d/debug/show debug/0|v/verbose/show more/0". + // In the new-style specification each switch definition + // is made up of the following... + // + // + // + // -- 0 is none, and 1 is a string + // + + Arg args() const ; + // Returns all the non-switch command-line arguments. + + Strings errorList() const ; + // Returns the list of errors. + + static size_t wrapDefault() ; + // Returns a default word-wrapping width. + + std::string usageSummary( const std::string & exe , const std::string & args , + size_t wrap_width = wrapDefault() ) const ; + // Returns a one-line usage summary, as + // "usage: " + + std::string usageSummarySwitches() const ; + // Returns the one-line summary of switches. Does _not_ + // include the usual "usage: " prefix + // or non-switch arguments. + + std::string usageHelp( size_t tab_stop = 30U , size_t wrap_width = wrapDefault() ) const ; + // Returns a multi-line string giving help on each switch. + + void showUsage( std::ostream & stream , const std::string & exe , + const std::string & args , size_t tab_stop = 30U , + size_t wrap_width = wrapDefault() ) const ; + // Streams out multi-line usage text using + // usageSummary() and usageHelp(). Does nothing + // about non-switch arguments. + + bool hasErrors() const ; + // Returns true if there are errors. + + void showErrors( std::ostream & stream , std::string prefix_1 , + std::string prefix_2 = std::string(": ") ) const ; + // A convenience function which streams out each errorList() + // item to the given stream, prefixed with the given + // prefix(es). The two prefixes are simply concatenated. + + void show( std::ostream & stream , std::string prefix ) const ; + // For debugging. + + bool contains( char switch_letter ) const ; + // Returns true if the command line contains + // the given switch. + + bool contains( const std::string & switch_name ) const ; + // Returns true if the command line contains + // the given switch. + + std::string value( const std::string & switch_name ) const ; + // Returns the value related to the given + // value-based switch. + + std::string value( char switch_letter ) const ; + // Returns the value related to the given + // value-based switch. + +private: + struct SwitchSpec + { + char c ; + std::string name ; + std::string description ; + bool valued ; + std::string value_description ; + SwitchSpec(char c_,const std::string &name_,const std::string &description_, + bool v_,const std::string &vd_) : + c(c_) , name(name_) , description(description_) , + valued(v_) , value_description(vd_) {} + } ; + typedef std::map SwitchSpecMap ; + typedef std::pair Value ; + typedef std::map SwitchMap ; + + void operator=( const GetOpt & ) ; + GetOpt( const GetOpt & ) ; + void parseSpec( const std::string & spec , char , char , char ) ; + void parseOldSpec( const std::string & spec ) ; + void parseNewSpec( const std::string & spec , char , char , char ) ; + void addSpec( char c , bool valued ) ; + void addSpec( char c , const std::string & name , const std::string & , bool valued , const std::string & ) ; + size_t parseArgs( const Arg & args_in ) ; + bool isOldSwitch( const std::string & arg ) const ; + bool isNewSwitch( const std::string & arg ) const ; + bool isSwitchSet( const std::string & arg ) const ; + void processSwitch( char c ) ; + void processSwitch( char c , const std::string & value ) ; + void processSwitch( const std::string & s ) ; + void processSwitch( const std::string & s , const std::string & value ) ; + bool valued( char c ) const ; + bool valued( const std::string & ) const ; + void errorNoValue( char c ) ; + void errorNoValue( const std::string & ) ; + void errorUnknownSwitch( char c ) ; + void errorUnknownSwitch( const std::string & ) ; + char key( const std::string & s ) const ; + void remove( size_t n ) ; + bool valid( const std::string & ) const ; + bool valid( char c ) const ; + std::string usageSummaryPartOne() const ; + std::string usageSummaryPartTwo() const ; + std::string usageHelpCore( const std::string & , size_t , size_t ) const ; + static size_t widthLimit( size_t ) ; + +private: + SwitchSpecMap m_spec_map ; + SwitchMap m_map ; + Strings m_errors ; + Arg m_args ; +} ; + +#endif diff --git a/src/glib/glog.cpp b/src/glib/glog.cpp new file mode 100644 index 0000000..1d5937e --- /dev/null +++ b/src/glib/glog.cpp @@ -0,0 +1,124 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glog.cpp +// + +#include "gdef.h" +#include "glog.h" +#include "glogoutput.h" + +namespace G +{ + class LogImp ; +} ; + +// Class: LogImp +// Description: An implementation class used by Log. +// +class G::LogImp +{ +public: + static std::stringstream &s() ; + static bool active() ; + static void empty() ; + static const char *m_file ; + static int m_line ; + static std::stringstream *m_ss ; +} ; + +const char *G::LogImp::m_file = NULL ; +std::stringstream *G::LogImp::m_ss = NULL ; +int G::LogImp::m_line = 0 ; + +std::stringstream &G::LogImp::s() +{ + if( m_ss == NULL ) + m_ss = new std::stringstream ; + return *m_ss ; +} + +void G::LogImp::empty() +{ + delete m_ss ; + m_ss = NULL ; + m_ss = new std::stringstream ; +} + +bool G::LogImp::active() +{ + LogOutput * output = G::LogOutput::instance() ; + if( output == NULL ) + { + return false ; + } + else + { + bool a = output->enable(true) ; + output->enable(a) ; + return a ; + } +} + +// === + +G::Log::End G::Log::end( G::Log::Severity severity ) +{ + return End(severity) ; +} + +G::Log::Stream & G::Log::stream() +{ + if( G::LogImp::active() ) + { + return G::LogImp::s() ; + } + else + { + static char buffer[3] ; + static std::stringstream dummy( buffer , sizeof(buffer) ) ; + return dummy ; + } +} + +void G::Log::onEnd( G::Log::Severity severity ) +{ + if( G::LogImp::active() ) + { + G::LogOutput::output( severity , G::LogImp::m_file , G::LogImp::m_line , + G::LogImp::s().str().c_str() ) ; + + // empty the stream + G::LogImp::empty() ; + } + G::LogImp::m_file = NULL ; + G::LogImp::m_line = 0 ; +} + +void G::Log::setFile( const char *file ) +{ + G::LogImp::m_file = file ; +} + +void G::Log::setLine( int line ) +{ + G::LogImp::m_line = line ; +} + diff --git a/src/glib/glog.h b/src/glib/glog.h new file mode 100644 index 0000000..0e75bd2 --- /dev/null +++ b/src/glib/glog.h @@ -0,0 +1,130 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glog.h +// + +#ifndef G_LOG_H +#define G_LOG_H + +#include "gdef.h" + +namespace G +{ + class Log ; +} ; + +// Class: G::Log +// Description: A static class for doing iostream-based logging. +// The G_LOG/G_DEBUG/G_WARNING/G_ERROR macros are provided as a +// convenient way of using this interface. +// +// Usage: +/// G::Log::stream() +/// << G::Log::Line(__FILE__,__LINE__) +/// << a << b << G::Log::end() ; +// or +/// G_LOG( a << b ) ; +// +// See also: LogOutput +// +class G::Log +{ +public: + typedef std::ostream Stream ; + enum Severity { s_Log , s_Debug , s_Warning , s_Error , s_Assertion } ; + + struct End // A private implementation class for Log. An End object from end() must be streamed out to flush a spooled message. + { Severity m_s ; End(Severity s) : m_s(s) {} } ; + + class Line // A class for adding line number information to the Log output. + { public: Line( const char *file , int line ) ; } ; + + static End end( Severity severity ) ; + // Returns an End object which can be used to close off a quantum + // of logging. (The End::op<<() function calls G::Log::onEnd().) + + static Stream &stream() ; + // Returns a stream for streaming messages into. + + static void onEnd( Severity s ) ; + // A pseudo-private method used by ::operator<<(End). + +private: + friend class G::Log::Line ; + static void setFile( const char *file ) ; + static void setLine( int line ) ; + Log() ; +} ; + +namespace G +{ + inline + std::ostream &operator<<( std::ostream &stream , + const G::Log::Line & ) + { + return stream ; + } +} ; + +namespace G +{ + inline + std::ostream &operator<<( std::ostream &stream , + const G::Log::End &end ) + { + G::Log::onEnd( end.m_s ) ; + return stream ; + } +} ; + +namespace G +{ + inline + G::Log::Line::Line( const char *file , int line ) + { + G::Log::setFile( file ) ; + G::Log::setLine( line ) ; + } +} ; + +// Macros: G_LOG, G_DEBUG, G_WARNING, G_ERROR +// The debug macro is for debugging during development. The log macro +// is used for progress logging, typically in long-lived server processes. +// The warning and error macros are used for error warning/error messages. +// In programs where logging can be disabled completely (see LogOutput) +// then warning/error messages should also get raised by some another +// independent means. +// +#define G_LOG_OUTPUT( expr , severity ) { G::Log::stream() << G::Log::Line(__FILE__,__LINE__) << expr << G::Log::end(severity) ; } +#if defined(_DEBUG) && ! defined(G_NO_DEBUG) +#define G_DEBUG( expr ) G_LOG_OUTPUT( expr , G::Log::s_Debug ) +#else +#define G_DEBUG( expr ) +#endif +#if ! defined(G_NO_LOG) +#define G_LOG( expr ) G_LOG_OUTPUT( expr , G::Log::s_Log ) +#else +#define G_LOG( expr ) +#endif +#define G_WARNING( expr ) G_LOG_OUTPUT( expr , G::Log::s_Warning ) +#define G_ERROR( expr ) G_LOG_OUTPUT( expr , G::Log::s_Error ) + +#endif diff --git a/src/glib/glogoutput.cpp b/src/glib/glogoutput.cpp new file mode 100644 index 0000000..9b0fbc7 --- /dev/null +++ b/src/glib/glogoutput.cpp @@ -0,0 +1,178 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glogoutput.cpp +// + +#include "gdef.h" +#include "glogoutput.h" +#include +#include + +G::LogOutput *G::LogOutput::m_this = NULL ; + +G::LogOutput::LogOutput( bool enabled , bool verbose ) : + m_enabled(enabled) , + m_verbose(verbose) , + m_syslog(false) +{ + if( m_this == NULL ) + m_this = this ; +} + +G::LogOutput::~LogOutput() +{ + if( m_this == this ) + m_this = NULL ; +} + +bool G::LogOutput::enable( bool enabled ) +{ + bool was_enabled = m_enabled ; + m_enabled = enabled ; + return was_enabled ; +} + +//static +void G::LogOutput::itoa( char *out , unsigned int n ) +{ + n = n % 1000000U ; + + if( n == 0U ) + { + out[0] = '0' ; + out[1] = '\0' ; + } + else + { + char buffer[15] ; + char *p = buffer + sizeof(buffer) - 1 ; + *p-- = '\0' ; + + for( ; n > 0U ; --p ) + { + *p = '0' + (n % 10U) ; + n /= 10U ; + } + + ::strcpy( out , p+1 ) ; + } +} + +//static +void G::LogOutput::output( G::Log::Severity severity , const char *text ) +{ + if( m_this != NULL ) + { + if( severity != G::Log::s_Debug || m_this->m_verbose ) + { + m_this->rawOutput( severity , text ? text : "" ) ; + if( text && text[0] && text[std::strlen(text)-1] != '\n' ) + m_this->rawOutput( severity , "\n" ) ; + } + } +} + +//static +void G::LogOutput::output( G::Log::Severity severity , const char *file, unsigned line, const char *text ) +{ + file = file ? file : "" ; + text = text ? text : "" ; + + // no-op if disabled + if( m_this == NULL || !m_this->m_enabled ) + return ; + + char buffer[500] ; + buffer[0] = '\0' ; + if( severity == G::Log::s_Debug ) + fileAndLine( buffer , sizeof(buffer) , file , line ) ; + std::strncat( buffer + std::strlen(buffer) , text , sizeof(buffer) - 1 - std::strlen(buffer) ) ; + output( severity , buffer ) ; +} + +G::LogOutput *G::LogOutput::instance() +{ + return m_this ; +} + +void G::LogOutput::onAssert() +{ + // no-op +} + +//static +void G::LogOutput::fileAndLine( char *buffer , size_t size , const char *file , int line ) +{ + const char *forward = ::strrchr( file , '/' ) ; + const char *back = ::strrchr( file , '\\' ) ; + const char *last = forward > back ? forward : back ; + const char *basename = last ? (last+1) : file ; + + std::strncat( buffer+std::strlen(buffer) , basename , size-std::strlen(buffer)-1 ) ; + std::strncat( buffer+std::strlen(buffer) , "(" , size-std::strlen(buffer)-1 ) ; + char b[15] ; + itoa( b , line ) ; + std::strncat( buffer+std::strlen(buffer) , b , size-std::strlen(buffer)-1 ) ; + std::strncat( buffer+std::strlen(buffer) , "): " , size-std::strlen(buffer)-1 ) ; +} + +void G::LogOutput::assertion( const char *file , unsigned line , bool test , const char *test_string ) +{ + if( !test ) + { + char buffer[100] ; + std::strcpy( buffer , "Assertion error: " ) ; + size_t size = sizeof(buffer) - 10 ; // -10 for luck + if( file ) + { + fileAndLine( buffer , size , file , line ) ; + } + if( test_string ) + { + std::strncat( buffer+std::strlen(buffer) , test_string , size-std::strlen(buffer)-1); + } + + if( instance() ) + { + // forward to derived classes -- these + // overrides may safely re-enter this method -- + // all code in this class is re-entrant + // + instance()->onAssert() ; + } + + output( G::Log::s_Assertion , buffer ) ; + halt() ; + } +} + +//static +void G::LogOutput::halt() +{ + abort() ; +} + +void G::LogOutput::syslog( SyslogFacility facility ) +{ + m_syslog = true ; + m_facility = facility ; +} + diff --git a/src/glib/glogoutput.h b/src/glib/glogoutput.h new file mode 100644 index 0000000..6cc3943 --- /dev/null +++ b/src/glib/glogoutput.h @@ -0,0 +1,113 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glogoutput.h +// + +#ifndef G_LOG_OUTPUT_H +#define G_LOG_OUTPUT_H + +#include "gdef.h" +#include "glog.h" + +namespace G +{ + class LogOutput ; +} ; + +// Class: G::LogOutput +// Description: Controls and implements low-level logging output, as used by the Log interface. +// Applications should normally instantiate a LogOutput object in main() to enable +// log output. +// See also: Log +// +class G::LogOutput +{ +public: + enum SyslogFacility { User , Daemon , Mail , Cron } ; // etc. + + explicit LogOutput( bool logging_enabled , bool verbose = true ) ; + // Constructor. If there is no LogOutput object, + // or if 'logging_enabled' is false, then there is no + // output of any sort. If both parameters are true + // then debug messages will be generated in addition + // to the log/warning/error messages (as long + // as it was compiled in). + // + // More than one LogOutput object may be created, but + // only the first one controls output. + + virtual ~LogOutput() ; + // Destructor. + + virtual void rawOutput( G::Log::Severity s , const char *string ) ; + // Overridable. Used to do the final message + // output (with OutputDebugString() or stderr). + + static LogOutput *instance() ; + // Returns a pointer to the controlling + // LogOutput object. Returns NULL if none. + + bool enable( bool debug_enabled = true ) ; + // Enables or disables debug output. + // Returns the previous setting. + + void syslog() ; + // Enables logging to the syslog system under Unix. + + void syslog( SyslogFacility facility ) ; + // Enables logging to the syslog system under Unix, + // using the specified facility. + + static void output( G::Log::Severity s , const char *raw_output ) ; + // Generates debug output if there is an extant + // LogOutput object which is enabled. Uses rawOutput(). + + static void output( G::Log::Severity s , const char *file , unsigned line , const char *text ) ; + // Generates debug output if there is an extant + // LogOutput object which is enabled. Uses rawOutput(). + + static void assertion( const char *file , unsigned line , bool test , const char *test_string ) ; + // Makes an assertion check (regardless of any LogOutput + // object). Calls output() if the 'file' parameter is + // not null. + + virtual void onAssert() ; + // Called during an assertion failure. This allows + // Windows applications to stop timers etc. which + // cause reentrancy problems and infinitely recursive + // dialog box creation. + +private: + LogOutput( const LogOutput & ) ; + void operator=( const LogOutput & ) ; + static void itoa( char *out , unsigned int ) ; + static void fileAndLine( char * , size_t , const char * , int ) ; + static void halt() ; + +private: + static LogOutput *m_this ; + bool m_enabled ; + bool m_verbose ; + bool m_syslog ; + SyslogFacility m_facility ; +} ; + +#endif diff --git a/src/glib/glogoutput_unix.cpp b/src/glib/glogoutput_unix.cpp new file mode 100644 index 0000000..1e3a75a --- /dev/null +++ b/src/glib/glogoutput_unix.cpp @@ -0,0 +1,64 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glogoutput_unix.cpp +// + +#include "gdef.h" +#include "glogoutput.h" +#include +#include + +namespace +{ + int mode( G::LogOutput::SyslogFacility facility , G::Log::Severity severity ) + { + int m = 0 ; + + if( facility == G::LogOutput::User ) m |= LOG_USER ; + else if( facility == G::LogOutput::Daemon ) m |= LOG_DAEMON ; + else if( facility == G::LogOutput::Mail ) m |= LOG_MAIL ; + else if( facility == G::LogOutput::Cron ) m |= LOG_CRON ; + // etc... + + if( severity == G::Log::s_Warning ) m |= LOG_WARNING ; + else if( severity == G::Log::s_Error ) m |= LOG_ERR ; + else if( severity == G::Log::s_Log ) m |= LOG_INFO ; + else m |= LOG_CRIT ; + + return m ; + } +} ; + +void G::LogOutput::rawOutput( G::Log::Severity severity , const char *message ) +{ + if( severity != G::Log::s_Debug && m_syslog && !(message[0]=='\n'&&message[1]=='\0') ) + { + ::syslog( mode(m_facility,severity) , "%s" , message ) ; + } + std::cerr << message ; + std::cerr.flush() ; +} + +void G::LogOutput::syslog() +{ + syslog( User ) ; +} + diff --git a/src/glib/glogoutput_win32.cpp b/src/glib/glogoutput_win32.cpp new file mode 100644 index 0000000..3a1ef0d --- /dev/null +++ b/src/glib/glogoutput_win32.cpp @@ -0,0 +1,60 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glogoutput_win32.cpp +// + +#include "gdef.h" +#include "glogoutput.h" +#include // getenv +#include // strlen + +namespace std { using ::getenv ; using ::strlen ; } ; + +void G::LogOutput::rawOutput( G::Log::Severity severity , const char *message ) +{ + std::cerr << message ; + std::cerr.flush() ; + + if( std::getenv("GLOGOUTPUT_DEBUGGER") != NULL ) + { + ::OutputDebugString( message ) ; + } + + static bool first = true ; + static const char * filename = NULL ; + if( first ) + { + first = false ; + const char * key = "GLOGOUTPUT_FILE" ; + filename = std::getenv(key) ; + } + if( filename != NULL && *filename != '\0' ) + { + static std::ofstream file( filename ) ; + file << message ; + file.flush() ; + } +} + +void G::LogOutput::syslog() +{ +} + diff --git a/src/glib/gmemory.h b/src/glib/gmemory.h new file mode 100644 index 0000000..049b59b --- /dev/null +++ b/src/glib/gmemory.h @@ -0,0 +1,60 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gmemory.h +// + +#ifndef G_MEMORY_H +#define G_MEMORY_H + +#include "gdef.h" +#include + +// Template function: operator<<= +// Description: A fix for the problem of resetting +// an auto_ptr portably. MSVC6.0 does not have a reset +// method, and GCC has a non-const assignment +// operators. This means that the MSVC code and +// the gcc code for resetting an auto_ptr are +// radically different. This operator hides +// those differences. +// +template +void operator<<=( std::auto_ptr & ap , T * p ) +{ + #ifdef G_WINDOWS + ap = std::auto_ptr( p ) ; + #else + ap.reset( p ) ; + #endif +} + +// Template function: operator<<= +// Description: A version for null pointers. +// +template +void operator<<=( std::auto_ptr & ap , int null_pointer ) +{ + //operator<<=( ap , (T*)(0) ) ; + T * p = 0 ; + ap <<= p ; +} + +#endif diff --git a/src/glib/gnumber.cpp b/src/glib/gnumber.cpp new file mode 100644 index 0000000..545a5e9 --- /dev/null +++ b/src/glib/gnumber.cpp @@ -0,0 +1,545 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gnumber.cc +// + +#include "gdef.h" +#include "gnumber.h" +#include "gdebug.h" +#include +#include +#include + +const g_uint32_t bit31 = 0x80000000L ; + +inline bool msb( g_uint32_t n ) +{ + return !!( n & bit31 ) ; +} ; + +G::Number::~Number() +{ +} + +G::Number::Number() : + m_high(0) , + m_low(0) +{ +} + +G::Number::Number( g_uint32_t n32 ) : + m_low(n32) , + m_high(0) +{ +} + +G::Number::Number( g_uint32_t high , g_uint32_t low ) : + m_low(low) , + m_high(high) +{ +} + +G::Number::Number( const Number &other ) +{ + m_low = other.m_low ; + m_high = other.m_high ; +} + +G::Number &G::Number::operator=( const Number &other ) +{ + m_low = other.m_low ; + m_high = other.m_high ; + return *this ; +} + +bool G::Number::big() const +{ + return m_high != 0 ; +} + +bool G::Number::big( long* ) const +{ + // optimise for the common case + if( sizeof(long) <= sizeof(g_uint32_t) ) + { + return m_high != 0 || m_low > LONG_MAX ; + } + + // or more generally... + Number n = LONG_MAX ; + return (*this) > n ; +} + +bool G::Number::big( unsigned long* ) const +{ + if( sizeof(unsigned long) <= sizeof(g_uint32_t) ) + { + return m_high != 0 || m_low > ULONG_MAX ; + } + + Number n = ULONG_MAX ; + return (*this) > n ; +} + +g_uint32_t G::Number::value() const +{ + if( big() ) + { + g_uint32_t rc = 0 ; + rc = ~rc ; + return rc ; + } + return m_low ; +} + +unsigned long G::Number::value( unsigned long *p ) +{ + if( big((unsigned long*)0) ) + { + if( p != NULL ) *p = ULONG_MAX ; + return ULONG_MAX ; + } + + unsigned long ul = m_high ; + ul <<= 32 ; // ignore warnings -- this is in case sizeof(long) > 32 + ul |= m_low ; + if( p != NULL ) *p = ul ; + return ul ; +} + +G::Number::operator double() +{ + double d = m_high ; + d *= static_cast(0x10000L) ; // <<=16 + d *= static_cast(0x10000L) ; // <<=16 + d += m_low ; + return d ; +} + +G::Number::operator unsigned long() +{ + return value( (unsigned long *)0 ) ; +} + +long G::Number::value( long *p ) +{ + if( big((long*)0) ) + { + if( p != NULL ) *p = LONG_MAX ; + return LONG_MAX ; + } + + long l = m_high ; + l <<= 32 ; // ignore warnings -- this is in case sizeof(long) > 32 + l |= m_low ; + if( p != NULL ) *p = l ; + return l ; +} + +G::Number &G::Number::to48bits() +{ + m_high &= 0xffff ; + return *this ; +} + +g_uint32_t G::Number::high() const +{ + return m_high ; +} + +g_uint32_t G::Number::low() const +{ + return m_low ; +} + +void G::Number::lshift( unsigned places ) +{ + if( places == 0 ) + { + } + else if( places < 32 ) + { + m_high <<= places ; + m_high |= (m_low >> (32-places)) ; + m_low <<= places ; + } + else if( places == 32 ) + { + lshift32() ; + } + else if( places < 64 ) + { + m_high = m_low ; + m_low = 0 ; + m_high <<= ( places - 32 ) ; + } + else + { + m_high = m_low = 0 ; + } +} + +void G::Number::lshift32() +{ + m_high = m_low ; + m_low = 0 ; +} + +void G::Number::rshift( unsigned places ) +{ + if( places == 0 ) + { + } + else if( places < 32 ) + { + m_low >>= places ; + m_low |= (m_high << (32-places)) ; + m_high >>= places ; + } + else if( places == 32 ) + { + rshift32() ; + } + else if( places < 64 ) + { + m_low = m_high ; + m_high = 0 ; + m_low >>= ( places - 32 ) ; + } + else + { + m_high = m_low = 0 ; + } +} + +void G::Number::rshift32() +{ + m_low = m_high ; + m_high = 0 ; +} + +G::Number G::Number::operator++(int) +{ + Number old = (*this) ; + ++(*this) ; + return old ; +} + +G::Number G::Number::operator--(int) +{ + Number old = (*this) ; + --(*this) ; + return old ; +} + +G::Number &G::Number::operator++() +{ + m_low++ ; + if( m_low == 0 ) + m_high++ ; + return *this ; +} + +G::Number &G::Number::operator--() +{ + if( m_low == 0 ) + m_high-- ; + m_low-- ; + return *this ; +} + +G::Number G::Number::operator+( const Number &other ) const +{ + Number result = (*this) ; + result += other ; + return result ; +} + +G::Number G::Number::operator-( const Number &other ) const +{ + Number result = (*this) ; + result -= other ; + return result ; +} + +G::Number G::Number::operator*( const Number &other ) const +{ + Number result = (*this) ; + result *= other ; + return result ; +} + +G::Number G::Number::operator/( g_uint16_t divisor ) const +{ + Number result = (*this) ; + result /= divisor ; + return result ; +} + +G::Number &G::Number::operator-=( const Number &other ) +{ + if( &other == this ) + { + m_high = m_low = 0 ; + return *this ; + } + + // create two's complement of other.. + Number copy = other ; + copy.m_high = ~copy.m_high ; + copy.m_low = ~copy.m_low ; + ++copy ; + + // ..and add + (*this) += copy ; + return *this ; +} + +G::Number &G::Number::operator+=( const Number &other ) +{ + if( &other == this ) + { + m_high <<= 1 ; + m_high += msb( m_low ) ; + m_low <<= 1 ; + return *this ; + } + + bool a = msb( m_low ) ; + bool b = msb( other.m_low ) ; + + g_uint32_t new_low = m_low + other.m_low ; + bool c = msb( new_low ) ; + m_low = new_low ; + + bool carry = ( a && b ) | ( (a ^ b) && !c ) ; + + m_high += other.m_high ; + if( carry ) + m_high++ ; + + return *this ; +} + +G::Number &G::Number::operator/=( g_uint16_t divisor ) +{ + Number remainder ; + Number quotient ; + divide16( (*this) , divisor , quotient , remainder ) ; + (*this) = quotient ; + return *this ; +} + +G::Number &G::Number::operator%=( g_uint16_t divisor ) +{ + Number remainder ; + Number quotient ; + divide16( (*this) , divisor , quotient , remainder ) ; + (*this) = remainder ; + return *this ; +} + +void G::Number::divide16( const Number &const_dividend , g_uint16_t divisor , Number "ient , Number &remainder ) +{ + // division is complicated by the need for a double-width + // accumulator ie. an accumulator double the width of the + // divisor -- since we restrict ourselves to using built-in + // types of 32 bits (for portability) this divisor is limited + // to 16 bits. Full 64/64 bit division must be built using + // this 64/16 bit division and a 128-bit accumulator. + + // the algorithm is basic long division -- consider dividing + // a four digit (16-bit) hex number by a one-digit (4-bit) + // hex number -- then scale the algorithm up to 64/16 bits. + + G_ASSERT( divisor != 0 ) ; + quotient = 0 ; + remainder = 0 ; + Number accumulator = 0 ; + Number dividend = const_dividend ; + + for( size_t i = 0 ; i < 4 ; i++ ) + { + // shift the dividend into the accumulator + accumulator <<= 16 ; + accumulator.m_low |= (dividend.m_high >> 16) ; + dividend <<= 16 ; + G_ASSERT( accumulator.m_high == 0 ) ; // assert 32 bit accumulator + + // shift the partial quotient into the full quotient + quotient <<= 16 ; + quotient.m_low |= (accumulator.m_low / divisor) ; + G_ASSERT( ((accumulator.m_low / divisor) >>16) == 0 ) ; // assert 16 bit partial quotient + + // set the accumulator to the partial remainder + accumulator = (accumulator.m_low % divisor) ; + G_ASSERT( (accumulator.m_low >> 16) == 0 ) ; // assert 16 bit accumulator + } + + remainder = accumulator ; +} + +G::Number &G::Number::operator*=( const Number &other ) +{ + if( &other == this ) + { + Number copy( other ) ; + operator*=( copy ) ; + return *this ; + } + + // 16 * 16 -> 32 + if( m_high == 0 && other.m_high == 0 && + m_low <= 0xffff && other.m_low <= 0xffff ) + { + m_low *= other.m_low ; + } + + // 32 * 32 -> 64 + else if( m_high == 0 && other.m_high == 0 ) + { + // four 16-bit numbers + Number a( (m_low >> 16) & 0xffff ) ; + Number b( m_low & 0xffff ) ; + Number c( (other.m_low >> 16) & 0xffff ) ; + Number d( other.m_low & 0xffff ) ; + + // one 32-bit partial product + Number bd = b ; bd *= d ; + + // two 48-bit partial products + Number cb = c ; cb *= b ; cb.lshift(16) ; + Number ad = a ; ad *= d ; ad.lshift(16) ; + + // one 64-bit partial product + Number ac = a ; ac *= c ; ac.lshift32() ; + + // 64-bit product + Number product = bd ; + product += cb ; + product += ad ; + product += ac ; + m_low = product.m_low ; + m_high = product.m_high ; + } + + // 64 * 64 -> 64 + else + { + // four 32-bit numbers + Number a( m_high ) ; + Number b( m_low ) ; + Number c( other.m_high ) ; + Number d( other.m_low ) ; + + // three 64-bit partial products + Number bd = b ; bd *= d ; + Number cb = c ; cb *= b ; cb.lshift32() ; + Number ad = a ; ad *= d ; ad.lshift32() ; + + // 64-bit product + Number product = bd ; + product += cb ; + product += ad ; + m_low = product.m_low ; + m_high = product.m_high ; + } + + return *this ; +} + +bool G::Number::operator==( const Number &other ) const +{ + return m_high == other.m_high && m_low == other.m_low ; +} + +bool G::Number::operator!=( const Number &other ) const +{ + return !( *this == other ) ; +} + +bool G::Number::operator<( const Number &other ) const +{ + if( m_high == other.m_high ) + return m_low < other.m_low ; + else + return m_high < other.m_high ; +} + +bool G::Number::operator<=( const Number &other ) const +{ + return ( *this < other ) || ( *this == other ) ; +} + +bool G::Number::operator>( const Number &other ) const +{ + return !( *this <= other ) ; +} + +bool G::Number::operator>=( const Number &other ) const +{ + return !( *this < other ) ; +} + +G::Number &G::Number::operator|=( const Number &other ) +{ + m_high |= other.m_high ; + m_low |= other.m_low ; + return *this ; +} + +G::Number &G::Number::operator<<=( unsigned places ) +{ + lshift( places ) ; + return *this ; +} + +G::Number &G::Number::operator>>=( unsigned places ) +{ + rshift( places ) ; + return *this ; +} + +std::string G::Number::displayString() const +{ + std::stringstream ss ; + if( m_high != 0 ) + { + ss << m_high ; + ss.width( 8U ) ; + ss << m_low ; + } + else + { + ss << m_low ; + } + return ss.str() ; +} + +namespace G +{ + std::ostream & operator<<( std::ostream & stream , const Number n ) + { + stream << n.displayString() ; + return stream ; + } +} ; + diff --git a/src/glib/gnumber.h b/src/glib/gnumber.h new file mode 100644 index 0000000..df6b5fc --- /dev/null +++ b/src/glib/gnumber.h @@ -0,0 +1,213 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gnumber.h +// + +#ifndef G_NUMBER_H +#define G_NUMBER_H + +#include "gdef.h" +#include + +namespace G +{ + class Number ; +} ; + +// Class: G::Number +// Description: A Number object represents an unsigned integer +// of up to 64 bits. The implementation is very old and not complete. +// +// See also: a64l(3c), LONGLONG, uint64_t, u_longlong_t +// +class G::Number +{ +public: + Number() ; + // Default constructor. The value is set to zero. + + Number( g_uint32_t n32 ) ; + // Constructor taking one 32-bit argument. + + Number( g_uint32_t high , g_uint32_t low ) ; + // Constructor taking two 32-bit arguments. + + Number( const Number &other ) ; + // Copy constructor. + + ~Number() ; + // Destructor. + + g_uint32_t value() const ; + // Returns the numeric value. Returns the maximum + // 32-bit value if the value is too big for a g_uint32_t. + // See big(). (This behaviour is generally preferable to + // truncating the value to 32 bits since a very big + // number can suddenly become a small number. Other + // member functions are available to do truncation.) + + unsigned long value( unsigned long *ulp ) ; + // Returns (by value and reference) the value + // as an unsigned long. The value returned is + // ULONG_MAX if the number is too big for an + // unsigned long. See big(unsigned long*) and + // operator unsigned long(). + + long value( long *lp ) ; + // Returns (by value and reference) the value + // as a long integer. The value returned is + // LONG_MAX if the number is too big for a long. + // See big(long*). + + operator unsigned long() ; + // Cast operator. The value returned is ULONG_MAX if + // the number is too big for an unsigned long. See + // big(unsigned long*) and value(unsigned long*). + + operator double() ; + // Cast operator returning a double precision + // floating point value. + + bool big() const ; + // Returns true if the number is bigger than + // 32 bits. See value(). + + bool big( long *dummy ) const ; + // Returns true if the number is too big + // for a long. See operator long(). + // The dummy parameter is ignored. + + bool big( unsigned long *dummy ) const ; + // Returns true if the number is too big + // for an unsigned long. See operator + // unsigned long(). The dummy parameter + // is ignored. + + Number &operator=( const Number &other ) ; + // Assignment operator. + + Number &to48bits() ; + // Truncates the value to 48 bits. The top 16 bits + // are zeroed. + + g_uint32_t high() const ; + // Returns the most significant 32 bits. + + g_uint32_t low() const ; + // Returns the least significant 32 bits. + + Number operator+ ( const Number &addend ) const ; + // Addition operator. + + Number &operator+=( const Number &addend ) ; + // Self-addition operator. + + Number operator- ( const Number &subtrahend ) const ; + // Subtraction operator. + + Number &operator-=( const Number &subtrahend ) ; + // Self-subtraction operator. + + Number operator* ( const Number &multiplicand ) const ; + // Multiplcation operator. + + Number &operator*=( const Number &multiplicand ) ; + // Self-multiplication operator. + + Number operator/ ( g_uint16_t divisor ) const ; + // Division operator. + + Number &operator/=( g_uint16_t divisor ) ; + // Self-division operator. + + Number operator% ( g_uint16_t divisor ) const ; + // Modulo operator. + + Number &operator%=( g_uint16_t divisor ) ; + // Self-modulo operator. + + Number &operator|=( const Number &other ) ; + // Bitwise OR operator. + + void lshift( unsigned places ) ; + // Left-shifts this number by the given number of bits. + + void lshift32() ; + // Left-shifts this number 32 bits. + + void rshift( unsigned places ) ; + // Right-shifts this number by the given number of bits. + + void rshift32() ; + // Right-shifts this number 32 bits. + + Number &operator<<=( unsigned places ) ; + // Left-shift operator. + + Number &operator>>=( unsigned places ) ; + // Right-shift operator. + + Number operator++(int) ; + // Post-increment operator. + + Number operator--(int) ; + // Post-decrement operator. + + Number &operator++() ; + // Pre-increment operator. + + Number &operator--() ; + // Post-increment operator. + + bool operator==( const Number &other ) const ; + // Equality comparison operator. + + bool operator!=( const Number &other ) const ; + // Inequality comparison operator. + + bool operator<( const Number &other ) const ; + // Less-than comparison operator. + + bool operator<=( const Number &other ) const ; + // Less-than-or-equal comparison operator. + + bool operator>( const Number &other ) const ; + // Greater-than comparison operator. + + bool operator>=( const Number &other ) const ; + // Greater-than-or-equal comparison operator. + + friend std::ostream &operator<<( std::ostream &stream , const Number n ) ; + // Global function which streams out a Number object. + + std::string displayString() const ; + // Returns a printable string representation. + +private: + static void divide16( const Number ÷nd , g_uint16_t divisor , Number " , Number &rem ) ; + +private: + g_uint32_t m_high ; + g_uint32_t m_low ; +} ; + +#endif + diff --git a/src/glib/gpath.cpp b/src/glib/gpath.cpp new file mode 100644 index 0000000..d3baed5 --- /dev/null +++ b/src/glib/gpath.cpp @@ -0,0 +1,424 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gpath.cpp +// + +#include "gdef.h" +#include "gpath.h" +#include "gfs.h" +#include "gstr.h" +#include "gdebug.h" +#include "glog.h" +#include + +G::Path::Path() : + m_dot(NULL) +{ + validate( "d-ctor" ) ; +} + +G::Path::~Path() +{ +} + +G::Path::Path( const std::string & path ) +{ + set( path ) ; + validate( "c-ctor" ) ; +} + +G::Path::Path( const char *path ) +{ + set( std::string(path) ) ; + validate( "ctor(cstr)" ) ; +} + +G::Path::Path( const Path & other ) +{ + set( other.str() ) ; + validate( "ctor(Path)" ) ; +} + +void G::Path::set( const std::string & path ) +{ + clear() ; + m_str = path ; + normalise() ; +} + +void G::Path::clear() +{ + m_extension = "" ; + m_str = "" ; + m_dot = NULL ; +} + +void G::Path::normalise() +{ + char ns[2] ; char s[2] ; char ss[3] ; + s[0] = ss[0] = ss[1] = FileSystem::slash() ; + ns[0] = FileSystem::nonSlash() ; + ns[1] = s[1] = ss[2] = '\0' ; + + // normalise spaces + if( !FileSystem::allowsSpaces() ) + Str::replaceAll( m_str , " " , "" ) ; + + // normalise alternate (non) slash characters + Str::replaceAll( m_str , ns , s ) ; + + // save leading double-slashes + bool has_leading_double_slash = + FileSystem::leadingDoubleSlash() && + m_str.find( ss ) == 0U ; + + // normalise double slashes + Str::replaceAll( m_str , ss , s ) ; + + // normalise funny characters + Str::replaceAll( m_str , "\t" , "" ) ; + Str::replaceAll( m_str , "\n" , "" ) ; + Str::replaceAll( m_str , "\r" , "" ) ; + + // normalise case + if( !FileSystem::caseSensitive() ) + Str::toLower(m_str) ; + + // remove trailing slashes where appropriate + while( ( + m_str.size() > 1U && + m_str.at(m_str.size()-1) == FileSystem::slash() && + !( FileSystem::usesDriveLetters() && + m_str.size() == 3U && + m_str.at(1) == ':' ) ) ) + { + m_str.resize( m_str.size()-1U ) ; + } + + // restore leading double slash + if( has_leading_double_slash ) + m_str = s + m_str ; + + // prepare a pointer to the extension + const char *slash = std::strrchr( m_str.c_str() , FileSystem::slash() ) ; + m_dot = std::strrchr( m_str.c_str() , '.' ) ; + if( m_dot != NULL && slash != NULL && m_dot < slash ) // ie. if "foo.bar/bletch" + m_dot = NULL ; + + // make a copy of the extension + if( m_dot != NULL ) + { + m_extension = std::string(m_dot+1U) ; + } +} + +bool G::Path::valid() const +{ + const char *slash = std::strrchr( m_str.c_str() , FileSystem::slash() ) ; + const char *dot = std::strrchr( m_str.c_str() , '.' ) ; + if( dot && slash && dot < slash ) + dot = NULL ; + + return m_dot == dot ; +} + +const char *G::Path::pathCstr() const +{ + validate("pathCstr") ; + return m_str.c_str() ; +} + +std::string G::Path::str() const +{ + validate("str") ; + return m_str ; +} + +void G::Path::streamOut( std::ostream & stream ) const +{ + stream << str() ; +} + +void G::Path::validate( const char * where ) const +{ + if( !valid() ) + { + G_ERROR( "G::Path::validate: " << where << ": \"" << m_str << "\"" ) ; + G_ASSERT( !"invalid Path" ) ; + } +} + +bool G::Path::simple() const +{ + return dirname().str().empty() ; +} + +bool G::Path::isRelative() const +{ + return !isAbsolute() ; +} + +bool G::Path::isAbsolute() const +{ + if( hasNetworkDrive() ) + return true ; + + std::string str(m_str) ; + if( hasDriveLetter() ) + str.erase( 0U , driveString().length() ) ; + + return str.length() > 0U && str.at(0U) == FileSystem::slash() ; +} + +void G::Path::setExtension( const std::string & extension ) +{ + bool dotted = extension.length() && extension.at(0U) == '.' ; + + Path copy = *this ; + copy.removeExtension() ; + + std::string s( copy.str() ) ; + s.append( 1U , '.' ) ; + s.append( dotted ? extension.substr(1U) : extension ) ; + + set( s ) ; + validate( "setExtension" ) ; +} + +std::string G::Path::basename() const +{ + // for consistency compare m_str with dirname() and return the + // difference, excluding any leading slash + + std::string result( m_str ) ; + std::string head( dirname().str() ) ; + if( head.length() == 0 ) + { + } + else + { + result.erase( 0U , head.length() ) ; + if( result.at(0) == FileSystem::slash() ) + result.erase(0U,1U) ; + } + + return result ; +} + +G::Path G::Path::dirname() const +{ + validate("dirname") ; + + std::string result ; + + if( FileSystem::usesDriveLetters() && + m_str.size() >= 2 && m_str.at(1) == ':' ) + { + if( noSlash() ) + { + if( m_str.size() > 2 ) + result = driveString() ; + else + result = "" ; + } + else + { + if( m_str.rfind(FileSystem::slash()) == 2U ) + { + if( m_str.size() == 3U ) + { + result = "" ; + } + else + { + result = noTail() ; + if( result.length() == 2 ) + result.append( slashString().c_str() ) ; + } + } + else + { + result = noTail() ; + } + } + } + else if( FileSystem::leadingDoubleSlash() && + m_str.size() >= 2U && + m_str.substr(0U,2U) == doubleSlashString() ) + { + size_t slash_count = 0U ; + for( const char * p = m_str.c_str() ; *p ; p++ ) + if( *p == FileSystem::slash() ) + slash_count++ ; + + if( slash_count > 3U ) + result = noTail() ; + else + result = "" ; + } + else + { + if( noSlash() || m_str.size() == 1U ) + { + result = "" ; + } + else + { + result = noTail() ; + if( result.length() == 0 ) + result = slashString() ; + } + } + + return Path(result) ; +} + +std::string G::Path::noTail() const +{ + G_ASSERT( !noSlash() ) ; + return m_str.substr( 0 , m_str.rfind(slashString()) ) ; +} + +bool G::Path::noSlash() const +{ + return m_str.find( slashString() ) == std::string::npos ; +} + +size_t G::Path::slashAt() const +{ + size_t position = m_str.find( slashString() ) ; + G_ASSERT( position != std::string::npos ) ; + return position ; +} + +std::string G::Path::slashString() +{ + return std::string ( 1U , FileSystem::slash() ) ; +} + +std::string G::Path::doubleSlashString() +{ + return std::string ( 2U , FileSystem::slash() ) ; +} + +bool G::Path::hasDriveLetter() const +{ + return + FileSystem::usesDriveLetters() && + m_str.size() >= 2U && + m_str.at(1U) == ':' ; +} + +bool G::Path::hasNetworkDrive() const +{ + return + FileSystem::leadingDoubleSlash() && + m_str.size() > 2U&& + m_str.at(0U) == FileSystem::slash() && + m_str.at(1U) == FileSystem::slash() ; +} + +std::string G::Path::driveString() const +{ + G_ASSERT( m_str.at(1U) == ':' ) ; + G_ASSERT( FileSystem::usesDriveLetters() ) ; + return std::string( 1U , m_str.at(0U) ) + std::string(":") ; +} + +void G::Path::removeExtension() +{ + if( m_dot != NULL ) + { + m_str.resize( m_str.size() - std::strlen(m_dot) ) ; + m_dot = NULL ; + normalise() ; // in case of dir/foo.bar.bletch + } + + validate("removeExtension") ; +} + +void G::Path::setDirectory( const std::string & dir ) +{ + std::string temp( basename() ) ; + set( dir ) ; + pathAppend( temp ) ; + validate("setDirectory") ; +} + +void G::Path::pathAppend( const std::string & tail ) +{ + // if empty or root or just a drive letter... + if( m_str.size() == 0U || m_str == slashString() || + ( hasDriveLetter() && m_str == driveString() ) ) + { + ; // no-op + } + else + { + m_str.append( slashString().c_str() ) ; + } + + m_str.append( tail ) ; + normalise() ; + validate("pathAppend") ; +} + +std::string G::Path::extension() const +{ + return m_extension ; +} + +G::Strings G::Path::split( bool no_dot ) const +{ + Path path( *this ) ; + Strings list ; + for( unsigned int part = 0U ;; part++ ) + { + std::string front = path.dirname().str() ; + std::string back = path.basename() ; + + // if a dot in the middle or end of the path... + if( back == std::string(".") && no_dot && front.length() != 0U ) + ; // ...ignore it + else + list.push_front( back ) ; + + if( front.length() == 0U ) + break ; + + path = Path(front) ; + } + return list ; +} + +bool G::Path::operator==( const Path & other ) const +{ + return m_str == other.m_str ; +} + +G::Path &G::Path::operator=( const Path & other ) +{ + if( &other != this ) + set( other.str() ) ; + return *this ; +} + + diff --git a/src/glib/gpath.h b/src/glib/gpath.h new file mode 100644 index 0000000..28d4886 --- /dev/null +++ b/src/glib/gpath.h @@ -0,0 +1,179 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gpath.h +// + +#ifndef G_PATH_H +#define G_PATH_H + +#include "gdef.h" +#include "gstrings.h" +#include +#include + +namespace G +{ + class Path ; +} ; + +// Class: G::Path +// Description: A Path object represents a file system +// path. The class is concerned with path syntax, not +// file system i/o. This class is necessary because +// of the mess Microsoft made with drive letters (like +// having a cwd associated with each drive). +// See also: File, Directory, FileSystem +// +class G::Path +{ +public: + Path() ; + // Default constructor. Creates + // a zero-length path. + + Path( const std::string & path ) ; + // Constructor. + + Path( const char *path ) ; + // Constructor. + + Path( const Path &other ) ; + // Copy constructor. + + virtual ~Path() ; + // Virtual destructor. + + bool simple() const ; + // Returns true if the path is just a + // file/directory name without + // any separators. Note that if the path + // is simple() then dirname() will + // return the empty string. + + std::string str() const ; + // Returns the path string. + + const char *pathCstr() const ; + // Returns the path string. + + std::string basename() const ; + // Returns the path, excluding drive/directory parts. + // Does nothing with the extension (cf. basename(1)). + + Path dirname() const ; + // Returns the drive/directory parts of the path. If + // this path is the top of the tree then the + // null path is returned. + // + // eg. c:foo\bar.exe -> c:foo + // eg. c:\foo\bar.exe -> c:\foo + // eg. c:bar.exe -> c: + // eg. c:\file -> c:\ + // eg. c:\ -> + // eg. c: -> + // eg. \foo\bar.exe -> \foo + // eg. \ -> + // eg. foo\bar\bletch -> foo\bar + // eg. foo\bar -> foo + // eg. bar.exe -> + // eg. \\machine\drive\dir\file.cc -> \\machine\drive\dir + // eg. \\machine\drive\file -> \\machine\drive + // eg. \\machine\drive -> + + std::string extension() const ; + // Returns the path's original extension, even + // after removeExtension(). Returns + // the zero-length string if there is none. + + void removeExtension() ; + // Modifies the path by removing any extension. + // However, the extension returned by extension() + // is unchanged. + + void setExtension( const std::string & extension ) ; + // Replaces the extension. Any leading dot in the + // given string is ignored. (The given extension + // will be returned by subsequent calls + // to extension().) + + bool isAbsolute() const ; + // Returns !isRelative(). + + bool isRelative() const ; + // Returns true if the path is a relative + // path. + + bool hasDriveLetter() const ; + // Returns true if the path has a leading + // drive letter (and the file-system + // uses drive letters). + + Path & operator=( const Path &other ) ; + // Assignment operator. + + void setDirectory( const std::string & dir ) ; + // Sets the drive/directory. + + void pathAppend( const std::string & tail ) ; + // Appends a filename to the path. A path separator + // is added if necessary. + + Strings split( bool no_dot = true ) const ; + // Spits the path into a list + // of component parts. + + bool operator==( const Path & path ) const ; + // Comparison operator. + + void streamOut( std::ostream & stream ) const ; + // Streams out the path. + +private: + void set( const std::string & path ) ; + void normalise() ; + void clear() ; + void validate( const char * ) const ; + bool valid() const ; + static std::string slashString() ; + static std::string doubleSlashString() ; + std::string driveString() const ; + size_t slashAt() const ; + bool noSlash() const ; + std::string noTail() const ; + bool hasNetworkDrive() const ; + +private: + std::string m_str ; + std::string m_extension ; + const char *m_dot ; +} ; + +namespace G +{ + inline + std::ostream & operator<<( std::ostream & stream , const Path & path ) + { + path.streamOut( stream ) ; + return stream ; + } +} ; + +#endif diff --git a/src/glib/gpid.h b/src/glib/gpid.h new file mode 100644 index 0000000..8d02108 --- /dev/null +++ b/src/glib/gpid.h @@ -0,0 +1,77 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gpid.h +// + +#ifndef G_PID_H +#define G_PID_H + +#include "gdef.h" +#include + +namespace G +{ + class PidImp ; + class Pid ; +} ; + +// Class: G::Pid +// Description: A process-id class. Uses a pimple +// pattern to hide windows/unix type differences. +// +class G::Pid +{ +public: + Pid() ; + // Default constructor for this + // process's id. + + ~Pid() ; + // Destructor. + + Pid( const Pid & ) ; + // Copy constructor. + + Pid & operator=( const Pid & ) ; + // Assignment operator. + + std::string str() const ; + // Returns a string representation. + + bool operator==( const Pid & ) const ; + // Comparison operator. + +private: + PidImp * m_imp ; +} ; + +namespace G +{ + inline + std::ostream & operator<<( std::ostream & stream , const Pid & pid ) + { + stream << pid.str() ; + return stream ; + } +} ; + +#endif + diff --git a/src/glib/gpid_unix.cpp b/src/glib/gpid_unix.cpp new file mode 100644 index 0000000..f3afb48 --- /dev/null +++ b/src/glib/gpid_unix.cpp @@ -0,0 +1,81 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gpid_unix.cpp +// + +#include "gdef.h" +#include "gpid.h" +#include +#include +#include + +namespace G +{ + class PidImp ; +} ; + +// Class: G::PidImp +// Description: A pimple implementation class for GPid. +// +class G::PidImp +{ +public: + pid_t m_pid ; +} ; + +// === + +G::Pid::Pid() : m_imp(NULL) +{ + m_imp = new PidImp ; + m_imp->m_pid = ::getpid() ; +} + +G::Pid::~Pid() +{ + delete m_imp ; +} + +G::Pid::Pid( const Pid & other ) : + m_imp(NULL) +{ + m_imp = new PidImp ; + m_imp->m_pid = other.m_imp->m_pid ; +} + +G::Pid & G::Pid::operator=( const Pid & rhs ) +{ + m_imp->m_pid = rhs.m_imp->m_pid ; + return *this ; +} + +std::string G::Pid::str() const +{ + std::stringstream ss ; + ss << m_imp->m_pid ; + return ss.str() ; +} + +bool G::Pid::operator==( const Pid & rhs ) const +{ + return m_imp->m_pid == rhs.m_imp->m_pid ; +} + diff --git a/src/glib/gpid_win32.cpp b/src/glib/gpid_win32.cpp new file mode 100644 index 0000000..33df1c5 --- /dev/null +++ b/src/glib/gpid_win32.cpp @@ -0,0 +1,79 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gpid_win32.cpp +// + +#include "gdef.h" +#include "gpid.h" +#include + +namespace G +{ + class PidImp ; +} ; + +// Class: G::PidImp +// Description: A pimple implementation class for GPid. +// +class G::PidImp +{ +public: + int m_pid ; +} ; + +// === + +G::Pid::Pid() : m_imp(NULL) +{ + m_imp = new PidImp ; + m_imp->m_pid = ::_getpid() ; +} + +G::Pid::~Pid() +{ + delete m_imp ; +} + +G::Pid::Pid( const Pid & other ) : + m_imp(NULL) +{ + m_imp = new PidImp ; + m_imp->m_pid = other.m_imp->m_pid ; +} + +G::Pid & G::Pid::operator=( const Pid & rhs ) +{ + m_imp->m_pid = rhs.m_imp->m_pid ; + return *this ; +} + +std::string G::Pid::str() const +{ + std::stringstream ss ; + ss << m_imp->m_pid ; + return ss.str() ; +} + +bool G::Pid::operator==( const Pid & rhs ) const +{ + return m_imp->m_pid == rhs.m_imp->m_pid ; +} ; + diff --git a/src/glib/gstr.cpp b/src/glib/gstr.cpp new file mode 100644 index 0000000..f82e110 --- /dev/null +++ b/src/glib/gstr.cpp @@ -0,0 +1,467 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gstr.cpp +// + +#include "gdef.h" +#include "gstr.h" +#include "gdebug.h" +#include +#include +#include +#include +#include +#include +#include + +bool G::Str::replace( std::string &s , const std::string &from , + const std::string &to , size_t *pos_p ) +{ + if( from.length() == 0 ) + return false ; + + size_t pos = pos_p == NULL ? 0 : *pos_p ; + if( pos >= s.length() ) + return false ; + + pos = s.find( from , pos ) ; + if( pos == std::string::npos ) + { + return false ; + } + else + { + s.replace( pos , from.length() , to ) ; + if( pos_p != NULL ) + *pos_p = pos + to.length() ; + return true ; + } +} + +size_t G::Str::replaceAll( std::string &s , const std::string &from , + const std::string &to ) +{ + size_t count = 0U ; + for( size_t pos = 0U ; replace( s , from , to , &pos ) ; count++ ) + ; // no-op + return count ; +} + +void G::Str::trimLeft( std::string & s , const std::string & ws ) +{ + size_t n = s.find_first_not_of( ws ) ; + if( n == std::string::npos ) + s = std::string() ; + else if( n != 0U ) + s.erase( 0U , n ) ; +} + +void G::Str::trimRight( std::string & s , const std::string & ws ) +{ + size_t n = s.find_last_not_of( ws ) ; + if( n == std::string::npos ) + s = std::string() ; + else if( n != 0U ) + s.erase( n+1U , s.length()-n-1U ) ; +} + +void G::Str::trim( std::string & s , const std::string & ws ) +{ + trimLeft(s,ws) ; trimRight(s,ws) ; +} + +bool G::Str::isNumeric( const std::string & s , bool allow_minus_sign ) +{ + const char * p = s.c_str() ; + if( allow_minus_sign && *p == '-' ) + p++ ; + + for( ; *p ; p++ ) + { + if( *p < '0' || *p > '9' ) + return false ; + } + return true ; +} + +bool G::Str::isPrintableAscii( const std::string & s ) +{ + for( const char * p = s.c_str() ; *p ; p++ ) + { + if( *p < 0x20 || *p >= 0x7f ) + return false ; + } + return true ; +} + +bool G::Str::isUShort( const std::string & s ) +{ + try + { + (void) toUShort(s) ; + } + catch( Overflow & ) + { + return false ; + } + catch( InvalidFormat & ) + { + return false ; + } + return true ; +} + +bool G::Str::isUInt( const std::string & s ) +{ + try + { + (void) toUInt(s) ; + } + catch( Overflow & ) + { + return false ; + } + catch( InvalidFormat & ) + { + return false ; + } + return true ; +} + +bool G::Str::isULong( const std::string & s ) +{ + try + { + (void) toULong(s) ; + } + catch( Overflow & ) + { + return false ; + } + catch( InvalidFormat & ) + { + return false ; + } + return true ; +} + +unsigned int G::Str::toUInt( const std::string &s , bool limited ) +{ + unsigned long ulong_val = toULong( s ) ; + unsigned int uint_val = static_cast( ulong_val ) ; + + if( uint_val != ulong_val ) + { + if( limited ) + uint_val = UINT_MAX ; + else + throw Overflow( s ) ; + } + + return uint_val ; +} + +unsigned long G::Str::toULong( const std::string &s , bool limited ) +{ + char * end = NULL ; + unsigned long result = ::strtoul( s.c_str(), &end, 0 ) ; + + if( end == 0 || end[0] != '\0' ) + throw InvalidFormat( s ) ; + + if( result == ULONG_MAX ) + { + if( limited ) + result = ULONG_MAX ; + else + throw Overflow( s ) ; + } + + return result ; +} + +unsigned short G::Str::toUShort( const std::string &s , bool limited ) +{ + unsigned long ulong_val = toULong( s ) ; + unsigned short ushort_val = static_cast( ulong_val ) ; + + if( ushort_val != ulong_val ) + { + if( limited ) + ushort_val = USHRT_MAX ; + else + throw Overflow( s ) ; + } + + return ushort_val ; +} + +void G::Str::toLower( std::string &s ) +{ + for( std::string::iterator p = s.begin() ; p != s.end() ; ++p ) + { + *p = ::tolower( *p ) ; + } +} + +void G::Str::toUpper( std::string &s ) +{ + for( std::string::iterator p = s.begin() ; p != s.end() ; ++p ) + { + *p = ::toupper( *p ) ; + } +} + +std::string G::Str::toPrintableAscii( char c , char escape ) +{ + if( c == escape ) + { + return std::string( 2U , c ) ; + } + else if( c >= 0x20 && c < 0x7f ) + { + return std::string( 1U , c ) ; + } + + std::string result( 1U , escape ) ; + if( c == '\n' ) + { + result.append( 1U , 'n' ) ; + } + else if( c == '\t' ) + { + result.append( 1U , 't' ) ; + } + else + { + unsigned int n = c ; + result.append( 1U , char('0'+((n/64U)%8U)) ) ; + result.append( 1U , char('0'+((n/8U)%8U)) ) ; + result.append( 1U , char('0'+(n%8U)) ) ; + } + return result ; +} + +std::string G::Str::toPrintableAscii( const std::string & in , char escape ) +{ + std::string result ; + for( const char * p = in.c_str() ; *p ; ++p ) + result.append( toPrintableAscii(*p,escape) ) ; + return result ; +} + +std::string G::Str::readLineFrom( std::istream & stream , char ignore ) +{ + std::string line ; + char c ; + while( stream.get(c) ) // ie. while(stream.good()) + { + if( c == '\n' ) + break ; + if( ignore != '\0' && c == ignore ) + ; + else + line.append(1U,c) ; + } + return line ; +} + +std::string G::Str::readLineFrom( std::istream & stream , const std::string & eol ) +{ + const size_t eol_length = eol.length() ; + std::string line ; + char c ; + for( size_t line_length = 1U ; stream.get(c) ; ++line_length ) + { + line.append(1U,c) ; + if( line_length >= eol_length ) + { + const size_t offset = line_length - eol_length ; + if( line.find(eol,offset) == offset ) + { + line.erase(offset) ; + return line ; + } + } + } + return line ; +} + +std::string G::Str::wrap( std::string text , const std::string & prefix_1 , + const std::string & prefix_2 , size_t width ) +{ + std::string ws( " \t\n" ) ; + std::stringstream ss ; + for( bool first_line = true ; text.length() ; first_line = false ) + { + const size_t prefix_length = + first_line ? prefix_1.length() : prefix_2.length() ; + size_t w = (width > prefix_length) ? (width-prefix_length) : width ; + + const size_t pos_nl = text.find_first_of("\n") ; + if( pos_nl != std::string::npos && pos_nl != 0U && pos_nl < w ) + { + w = pos_nl ; + } + + std::string line = text ; + if( text.length() > w ) + { + line = text.substr( 0U , w ) ; + if( text.find_first_of(ws,w) != w ) + { + const size_t white_space = line.find_last_of( ws ) ; + const size_t black_space = line.find_first_not_of( ws ) ; + if( white_space != std::string::npos && + black_space != std::string::npos && + (white_space+1U) != black_space ) + { + line = line.substr( 0U , white_space ) ; + } + } + } + + if( line.length() != 0U ) + { + ss << ( first_line ? prefix_1 : prefix_2 ) << line << std::endl ; + } + + text = text.length() == line.length() ? + std::string() : text.substr(line.length()) ; + + const size_t black_space = text.find_first_not_of( ws ) ; + if( black_space != 0U && black_space != std::string::npos ) + { + size_t newlines = 0U ; + for( size_t pos = 0U ; pos < black_space ; ++pos ) + { + if( text.at(pos) == '\n' ) + { + newlines++ ; + if( newlines > 1U ) + ss << prefix_2 << std::endl ; + } + } + + text = text.substr( black_space ) ; + } + } + return ss.str() ; +} + +void G::Str::listPushBack( void * out , const std::string & s ) +{ + reinterpret_cast(out)->push_back( s ) ; +} + +void G::Str::arrayPushBack( void * out , const std::string & s ) +{ + reinterpret_cast(out)->push_back( s ) ; +} + +void G::Str::splitIntoTokens( const std::string &in , Strings &out , + const std::string & ws ) +{ + splitIntoTokens( in , reinterpret_cast(&out) , + &listPushBack , ws ) ; +} + +void G::Str::splitIntoTokens( const std::string &in , StringArray &out , + const std::string & ws ) +{ + splitIntoTokens( in , reinterpret_cast(&out) , + &arrayPushBack , ws ) ; +} + +void G::Str::splitIntoTokens( const std::string & in , + void * out , void (*fn)(void*,const std::string&) , + const std::string & ws ) +{ + for( size_t p = 0U ; p != std::string::npos ; ) + { + p = in.find_first_not_of( ws , p ) ; + if( p != std::string::npos ) + { + size_t end = in.find_first_of( ws , p ) ; + size_t len = end == std::string::npos ? end : (end-p) ; + (*fn)( out , in.substr( p , len ) ) ; + p = end ; + } + } +} + +void G::Str::splitIntoFields( const std::string & in , Strings &out , + const std::string & ws , char escape , bool discard_bogus ) +{ + splitIntoFields( in , reinterpret_cast(&out) , + &listPushBack , ws , escape , discard_bogus ) ; +} + +void G::Str::splitIntoFields( const std::string & in , StringArray &out , + const std::string & ws , char escape , bool discard_bogus ) +{ + splitIntoFields( in , reinterpret_cast(&out) , + &arrayPushBack , ws , escape , discard_bogus ) ; +} + +void G::Str::splitIntoFields( const std::string & in_in , void * out , + void (*fn)(void*,const std::string&) , + const std::string & ws , char escape , bool discard_bogus ) +{ + std::string all( ws ) ; + if( escape != '\0' ) + all.append( 1U , escape ) ; + + if( in_in.length() ) + { + std::string in = in_in ; + size_t start = 0U ; + size_t last_pos = in.length() - 1U ; + size_t pos = 0U ; + for(;;) + { + if( pos >= in.length() ) break ; + pos = in.find_first_of( all , pos ) ; + if( pos == std::string::npos ) break ; + if( in.at(pos) == escape ) + { + const bool valid = + pos != last_pos && + in.find(all,pos+1U) == (pos+1U) ; + + if( valid || discard_bogus ) + in.erase( pos , 1U ) ; + else + pos++ ; + pos++ ; + } + else + { + (*fn)( out , in.substr(start,pos-start) ) ; + pos++ ; + start = pos ; + } + } + (*fn)( out , in.substr(start,pos-start) ) ; + } +} + + diff --git a/src/glib/gstr.h b/src/glib/gstr.h new file mode 100644 index 0000000..bcd293c --- /dev/null +++ b/src/glib/gstr.h @@ -0,0 +1,202 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gstr.h +// + +#ifndef G_STR_H +#define G_STR_H + +#include "gdef.h" +#include "gexception.h" +#include "gstrings.h" +#include +#include +#include +#include + +namespace G +{ + class Str ; +} ; + +// Class: G::Str +// Description: A static class which provides string helper functions. +// +class G::Str +{ +public: + G_EXCEPTION( Overflow , "conversion error: over/underflow" ) ; + G_EXCEPTION( InvalidFormat, "conversion error: invalid format" ) ; + typedef std::vector StringArray ; + + static bool replace( std::string &s , + const std::string &from , const std::string &to , + size_t *pos_p = NULL ) ; + // Replaces 'from' with 'to', starting at offset '*pos_p'. + // Returns true if a substitution was made, and adjusts + // '*pos_p' by to.length(). + + static size_t replaceAll( std::string &s , const std::string &from , + const std::string &to ) ; + // Does a global replace on string 's', replacing all + // occurences of sub-string 'from' with 'to'. Returns + // the number of substitutions made. + + static void trimLeft( std::string & s , const std::string & ws ) ; + // Trims the lhs of s, taking off any of the 'ws' characters. + + static void trimRight( std::string & s , const std::string & ws ) ; + // Trims the rhs of s, taking off any of the 'ws' characters. + + static void trim( std::string & s , const std::string & ws ) ; + // Trims both ends of s, taking off any of the 'ws' characters. + + static bool isNumeric( const std::string & s , bool allow_minus_sign = false ) ; + // Returns true if every character is a decimal digit. + // Empty strings return true. + + static bool isPrintableAscii( const std::string & s ) ; + // Returns true if every character is a 7-bit, non-control + // character (ie. 0x20<=c<0x7f). Empty strings return true. + + static bool isUShort( const std::string & s ) ; + // Returns true if the string can be converted into + // an unsigned short without throwing an exception. + + static bool isUInt( const std::string & s ) ; + // Returns true if the string can be converted into + // an unsigned integer without throwing an exception. + + static bool isULong( const std::string & s ) ; + // Returns true if the string can be converted into + // an unsigned long without throwing an exception. + + static unsigned int toUInt( const std::string & s , bool limited = false ) ; + // Converts string 's' to an unsigned int. + // + // If 'limited' is true then very large numeric strings + // are limited to the maximum value of the numeric type, + // without an Overflow exception. + // + // Exception: Overflow + // Exception: InvalidFormat + + static unsigned long toULong( const std::string &s , bool limited = false ) ; + // Converts string 's' to an unsigned long. + // + // If 'limited' is true then very large numeric strings + // are limited to the maximum value of the numeric type, + // without an Overflow exception. + // + // Exception: Overflow + // Exception: InvalidFormat + + static unsigned short toUShort( const std::string &s , bool limited = false ) ; + // Converts string 's' to an unsigned short. + // + // If 'limited' is true then very large numeric strings + // are limited to the maximum value of the numeric type, + // without an Overflow exception. + // + // Exception: Overflow + // Exception: InvalidFormat + + static void toUpper( std::string &s ) ; + // Replaces all lowercase characters in string 's' by + // uppercase characters. + + static void toLower( std::string &s ) ; + // Replaces all uppercase characters in string 's' by + // lowercase characters. + + static std::string toPrintableAscii( char c , char escape = '\\' ) ; + // Returns a printable, 7-bit-ascii string representing the given + // character. Typical return values include "\\", "\n", + // "\t", "\007", etc. + + static std::string toPrintableAscii( const std::string & in , char escape = '\\' ) ; + // Returns a printable, 7-bit-ascii string representing the given input. + + static std::string readLineFrom( std::istream &stream , char ignore = '\015' ) ; + // Reads a string from the given stream using newline as the + // terminator. The terminator is read from the stream + // but not put into the returned string. + // + // May return a partial line at the end of the stream. + // + // Any 'ignore' characters (control-m by default) read + // from the stream are discarded. An 'ignore' + // character of NUL ('\0') disables this feature. + + static std::string readLineFrom( std::istream &stream , const std::string & eol ) ; + // An overload which uses 'eol' as the terminator, and + // without the 'ignore' feature. + + static std::string wrap( std::string text , + const std::string & prefix_first_line , const std::string & prefix_subsequent_lines , + size_t width = 70U ) ; + // Does word-wrapping. The return value is a string with + // embedded newlines. + + static void splitIntoTokens( const std::string & in , Strings &out , + const std::string & ws ) ; + // Splits the string into 'ws'-delimited tokens. The + // behaviour is like ::strtok() in that adjacent delimiters + // count as one and leading and trailing delimiters are ignored. + // Ths output array is cleared first. + + static void splitIntoTokens( const std::string & in , StringArray & out , + const std::string & ws ) ; + // Overload for vector. + + static void splitIntoFields( const std::string & in , Strings &out , + const std::string & seperators , char escape = '\0' , + bool discard_bogus_escapes = true ) ; + // Splits the string into fields. Duplicated, leading + // and trailing separator characters are all significant. + // Ths output array is cleared first. + // + // If a non-null escape character is given then any escaped + // separator is not used for splitting. If the 'discard...' + // parameter is true then escapes will never appear in the + // output, except for where there were originally double escapes. + // This is the preferred behaviour but it can create problems + // if doing nested splitting -- the escapes are lost by the + // time the sub-strings are split. + + static void splitIntoFields( const std::string & in , StringArray & out , + const std::string & seperators , char escape = '\0' , + bool discard_bogus_escapes = true ) ; + // Overload for vector. + +private: + static void listPushBack( void * , const std::string & ) ; + static void arrayPushBack( void * , const std::string & ) ; + static void splitIntoFields( const std::string & , void * , + void (*fn)(void*,const std::string&) , + const std::string & , char , bool ) ; + static void splitIntoTokens( const std::string & , void * , + void (*fn)(void*,const std::string&) , const std::string & ) ; + Str() ; // not implemented +} ; + +#endif + diff --git a/src/glib/gstrings.h b/src/glib/gstrings.h new file mode 100644 index 0000000..cb69bc1 --- /dev/null +++ b/src/glib/gstrings.h @@ -0,0 +1,42 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gstrings.h +// + +#ifndef G_STRINGS_H +#define G_STRINGS_H + +#include +#include + +namespace G +{ + +// Typedef: Strings +// Description: A std::list of std::strings. +// See also: Str +// +typedef std::list Strings ; + +} ; + +#endif + diff --git a/src/glib/gtime.cpp b/src/glib/gtime.cpp new file mode 100644 index 0000000..3a21e72 --- /dev/null +++ b/src/glib/gtime.cpp @@ -0,0 +1,98 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gtime.cpp +// + +#include "gdef.h" +#include "gtime.h" +#include "gdatetime.h" +#include "gassert.h" + +G::Time::Time( const G::DateTime::BrokenDownTime & tm ) +{ + m_hh = tm.tm_hour ; + m_mm = tm.tm_min ; + m_ss = tm.tm_sec ; +} + +G::Time::Time() +{ + G::DateTime::BrokenDownTime tm = G::DateTime::utc( G::DateTime::now() ) ; + m_hh = tm.tm_hour ; + m_mm = tm.tm_min ; + m_ss = tm.tm_sec ; +} + +G::Time::Time( G::DateTime::EpochTime t ) +{ + G::DateTime::BrokenDownTime tm = G::DateTime::utc( t ) ; + m_hh = tm.tm_hour ; + m_mm = tm.tm_min ; + m_ss = tm.tm_sec ; +} + +G::Time::Time( const LocalTime & ) +{ + G::DateTime::BrokenDownTime tm = G::DateTime::local( G::DateTime::now() ) ; + m_hh = tm.tm_hour ; + m_mm = tm.tm_min ; + m_ss = tm.tm_sec ; +} + +G::Time::Time( G::DateTime::EpochTime t , const LocalTime & ) +{ + G::DateTime::BrokenDownTime tm = G::DateTime::local( t ) ; + m_hh = tm.tm_hour ; + m_mm = tm.tm_min ; + m_ss = tm.tm_sec ; +} + +unsigned int G::Time::hours() const +{ + return m_hh ; +} + +unsigned int G::Time::minutes() const +{ + return m_mm ; +} + +unsigned int G::Time::seconds() const +{ + return m_ss ; +} + +std::string G::Time::hhmmss( const char * sep ) +{ + if( sep == NULL ) sep = "" ; + std::stringstream ss ; + ss << (m_hh/10U) << (m_hh%10U) << sep << (m_mm/10U) << (m_mm%10U) << sep << (m_ss/10U) << (m_ss%10U) ; + return ss.str() ; +} + +std::string G::Time::hhmm( const char * sep ) +{ + if( sep == NULL ) sep = "" ; + std::stringstream ss ; + ss << (m_hh/10U) << (m_hh%10U) << sep << (m_mm/10U) << (m_mm%10U) ; + return ss.str() ; +} + diff --git a/src/glib/gtime.h b/src/glib/gtime.h new file mode 100644 index 0000000..9eb0848 --- /dev/null +++ b/src/glib/gtime.h @@ -0,0 +1,83 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gtime.h +// + +#ifndef G_TIME_H +#define G_TIME_H + +#include "gdef.h" +#include "gexception.h" +#include "gdatetime.h" +#include + +namespace G +{ + class Time ; +} ; + +// Class: G::Time +// Description: A simple time-of-day (hh/mm/ss) class. +// See also: Date, DateTime +// +class G::Time +{ +public: + class LocalTime // An overload discriminator class for Time constructors. + {} ; + + explicit Time( const G::DateTime::BrokenDownTime & tm ) ; + // Constructor for the given broken-down time. + + explicit Time( G::DateTime::EpochTime t ) ; + // Constructor for the given epoch time. + + Time() ; + // Constructor for now. + + Time( G::DateTime::EpochTime t , const LocalTime & ) ; + // Localtime constructor for the given epoch time. + + explicit Time( const LocalTime & ) ; + // Localtime constructor for now. + + unsigned int hours() const ; + // Returns the hours (0 <= h < 24). + + unsigned int minutes() const ; + // Returns the minutes (0 <= m < 60). + + unsigned int seconds() const ; + // Returns the seconds (0 <= s <= 61 [sic]). + + std::string hhmmss( const char * sep = NULL ) ; + // Returns a hhmmss string. + + std::string hhmm( const char * sep = NULL ) ; + // Returns a hhmm string. + +private: + unsigned int m_hh ; + unsigned int m_mm ; + unsigned int m_ss ; +} ; + +#endif diff --git a/src/gnet/Makefile.am b/src/gnet/Makefile.am new file mode 100644 index 0000000..5634bfe --- /dev/null +++ b/src/gnet/Makefile.am @@ -0,0 +1,60 @@ +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + +EXTRA_DIST=gclient_win32.cpp \ + gdescriptor_win32.cpp \ + gevent_win32.cpp \ + glocal_win32.cpp \ + grequest.cpp \ + gresolve_win32.cpp \ + gsocket_win32.cpp \ + gwinsock.cpp +INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib +noinst_LIBRARIES = libgnet.a +libgnet_a_SOURCES = gaddress_ipv4.cpp \ + gclient.cpp \ + gclient_unix.cpp \ + gdescriptor_unix.cpp \ + gevent.cpp \ + gevent_unix.cpp \ + geventserver.cpp \ + glinebuffer.cpp \ + glocal_unix.cpp \ + gresolve.cpp \ + gresolve_ipv4.cpp \ + gresolve_unix.cpp \ + gselect.cpp \ + gserver.cpp \ + gsocket.cpp \ + gsocket_unix.cpp \ + gaddress.h \ + gclient.h \ + gdescriptor.h \ + gevent.h \ + geventserver.h \ + glinebuffer.h \ + glocal.h \ + gnet.h \ + grequest.h \ + gresolve.h \ + gselect.h \ + gserver.h \ + gsocket.h \ + gwinsock.h diff --git a/src/gnet/Makefile.in b/src/gnet/Makefile.in new file mode 100644 index 0000000..5d6ac1b --- /dev/null +++ b/src/gnet/Makefile.in @@ -0,0 +1,292 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +EXTRA_DIST = gclient_win32.cpp gdescriptor_win32.cpp gevent_win32.cpp glocal_win32.cpp grequest.cpp gresolve_win32.cpp gsocket_win32.cpp gwinsock.cpp + +INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib +noinst_LIBRARIES = libgnet.a +libgnet_a_SOURCES = gaddress_ipv4.cpp gclient.cpp gclient_unix.cpp gdescriptor_unix.cpp gevent.cpp gevent_unix.cpp geventserver.cpp glinebuffer.cpp glocal_unix.cpp gresolve.cpp gresolve_ipv4.cpp gresolve_unix.cpp gselect.cpp gserver.cpp gsocket.cpp gsocket_unix.cpp gaddress.h gclient.h gdescriptor.h gevent.h geventserver.h glinebuffer.h glocal.h gnet.h grequest.h gresolve.h gselect.h gserver.h gsocket.h gwinsock.h + +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + + +DEFS = @DEFS@ -I. -I$(srcdir) -I../.. +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +libgnet_a_LIBADD = +libgnet_a_OBJECTS = gaddress_ipv4.o gclient.o gclient_unix.o \ +gdescriptor_unix.o gevent.o gevent_unix.o geventserver.o glinebuffer.o \ +glocal_unix.o gresolve.o gresolve_ipv4.o gresolve_unix.o gselect.o \ +gserver.o gsocket.o gsocket_unix.o +AR = ar +CXXFLAGS = @CXXFLAGS@ +CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(libgnet_a_SOURCES) +OBJECTS = $(libgnet_a_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .cpp .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps src/gnet/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-noinstLIBRARIES: + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +distclean-noinstLIBRARIES: + +maintainer-clean-noinstLIBRARIES: + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +libgnet.a: $(libgnet_a_OBJECTS) $(libgnet_a_DEPENDENCIES) + -rm -f libgnet.a + $(AR) cru libgnet.a $(libgnet_a_OBJECTS) $(libgnet_a_LIBADD) + $(RANLIB) libgnet.a +.cpp.o: + $(CXXCOMPILE) -c $< + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src/gnet + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile $(LIBRARIES) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-noinstLIBRARIES mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-noinstLIBRARIES clean-compile clean-tags clean-generic \ + mostlyclean-am + +clean: clean-am + +distclean-am: distclean-noinstLIBRARIES distclean-compile \ + distclean-tags distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-noinstLIBRARIES \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-noinstLIBRARIES distclean-noinstLIBRARIES \ +clean-noinstLIBRARIES maintainer-clean-noinstLIBRARIES \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ +check-am installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/gnet/gaddress.h b/src/gnet/gaddress.h new file mode 100644 index 0000000..6520288 --- /dev/null +++ b/src/gnet/gaddress.h @@ -0,0 +1,172 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gaddress.h +// + +#ifndef G_ADDRESS_H +#define G_ADDRESS_H + +#include "gdef.h" +#include "gnet.h" +#include "gexception.h" +#include +class GAddressImp ; + +namespace GNet +{ + class Address ; + class AddressImp ; +} ; + +// Class: GNet::Address +// +// Description: The Address class encapsulates an +// IP transport address. The address is stored +// internally as a 'sockaddr_in[6]' structure. +// +// See also: GNet::Resolver +// +class GNet::Address +{ +public: + G_EXCEPTION( Error , "address error" ) ; + G_EXCEPTION( BadFamily , "unsupported address family" ) ; + G_EXCEPTION( BadString , "invalid ip address string" ) ; + class Localhost // An overload discriminator class for GNet::Address. + {} ; + + Address( const Address &addr ) ; + // Copy constructor. + + Address( const sockaddr *addr , int len ) ; + // Constructor using a given sockaddr. + // + // The given sockaddr address must be an Internet + // address (ie. 'addr->sa_family' must be AF_INET + // or AF_INET6, and 'len' must be the sizeof sockaddr_in + // or sockaddr_in6). + // + // Throws an exception if an invalid structure. + + Address( const hostent & h , unsigned int port ) ; + // Constructor taking the host part from the + // first address in the given hostent's list of + // alternatives. Throws an exception if + // an invalid port number. See also: validPort() + + Address( const hostent & h , const servent & s ) ; + // Constructor taking the host part from the + // first address in the given hostent's list of + // alternatives, and the port number from + // the given servent structure. + + explicit Address( const servent & s ) ; + // Constructor for a local INADDR_ANY address, taking + // the port from the given 'servent' structure. + + explicit Address( unsigned int port ) ; + // Constructor for a local INADDR_ANY address with the + // given port number. Throws an exception if + // an invalid port number. See also: validPort() + + explicit Address( unsigned int port , Localhost ) ; + // Constructor for a local INADDR_LOOPBACK address with + // the given port number. Throws an exception if + // an invalid port number. See also: validPort() + + explicit Address( const std::string & display_string ) ; + // Constructor taking a string originally obtained + // from displayString(). + // + // Throws an exception if an invalid string. + // + // See also validString(). + + ~Address() ; + // Destructor. + + static Address invalidAddress() ; + // Returns an invalid address. Should only be + // needed by socket classes. + + static Address localhost( unsigned int port = 0U ) ; + // Returns a localhost ("loopback") address. + // This is a convenience function as an + // alternative to the Localhost constructor. + + void operator=( const Address &addr ) ; + // Assignment operator. + + const sockaddr * address() const ; + // Returns the sockaddr address. Typically used when making + // socket system calls. Never returns NULL. + + sockaddr * address() ; + // This is a non-const version of address() for compiling on + // systems which are not properly const-clean. + + int length() const; + // Returns the size of the sockaddr address. + // See address(). + + std::string displayString( bool with_port = true ) const ; + // Returns a string which represents the address for + // debugging and diagnostics purposes. + + std::string hostString() const ; + // Returns a string which represents the host part + // of the address for debugging and diagnostics purposes. + + unsigned int port() const; + // Returns port part of address. + + static bool validPort( unsigned int n ) ; + // Returns true if the port number is within the + // valid range. This can be used to avoid exceptions from + // the relevant constructors. + + static bool validString( const std::string & display_string , std::string * reason = NULL ) ; + // Returns true if the display string is valid. + // This can be used to avoid exceptions from + // the relevant constructor. + + bool operator==( const Address &other ) const ; + // Comparison operator. + + bool sameHost( const Address &other ) const ; + // Returns true if the two addresses have the + // same host part (ie. ignoring the port). + // (But note that a host can have more than + // one host address.) + + void setPort( unsigned int port ) ; + // Sets the port number. Throws an exception + // if an invalid port number (ie. too big). + +private: + void setHost( const hostent & ) ; + +private: + AddressImp * m_imp ; +}; + +#endif + diff --git a/src/gnet/gaddress_ipv4.cpp b/src/gnet/gaddress_ipv4.cpp new file mode 100644 index 0000000..12d1399 --- /dev/null +++ b/src/gnet/gaddress_ipv4.cpp @@ -0,0 +1,437 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gaddress_ipv4.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gaddress.h" +#include "gconvert.h" +#include "gstrings.h" +#include "gstr.h" +#include "gassert.h" +#include "gdebug.h" +#include +#include + +// Class: GNet::AddressImp +// Description: A pimple-pattern implementation class for GNet::Address. +// +class GNet::AddressImp +{ +public: + typedef sockaddr_in address_type ; + + explicit AddressImp( unsigned int port ) ; // (not in_port_t -- see validPort(), setPort() etc) + explicit AddressImp( const servent & s ) ; + explicit AddressImp( const std::string & s ) ; + AddressImp( unsigned int port , Address::Localhost ) ; + AddressImp( const hostent & h , unsigned int port ) ; + AddressImp( const hostent & h , const servent & s ) ; + AddressImp( const sockaddr * addr , size_t len ) ; + AddressImp( const AddressImp & other ) ; + + const sockaddr * raw() const ; + sockaddr * raw() ; + + unsigned int port() const ; + void setPort( unsigned int port ) ; + + static bool validString( const std::string & s , std::string * reason_p = NULL ) ; + static bool validPort( unsigned int port ) ; + + bool same( const AddressImp & other ) const ; + bool sameHost( const AddressImp & other ) const ; + + std::string displayString() const ; + std::string hostString() const ; + +private: + void init() ; + void set( const sockaddr * specific ) ; + bool setAddress( const std::string & display_string , std::string & reason ) ; + static bool validPart( const std::string & s ) ; + static bool validPortNumber( const std::string & s ) ; + static bool validNumber( const std::string & s ) ; + void setHost( const hostent & h ) ; + static bool sameAddr( const ::in_addr & a , const ::in_addr & b ) ; + +private: + address_type m_inet ; + static char m_port_separator ; +} ; + +char GNet::AddressImp::m_port_separator = ':' ; + +void GNet::AddressImp::init() +{ + ::memset( &m_inet, 0, sizeof(m_inet) ); + m_inet.sin_family = AF_INET ; + m_inet.sin_port = 0 ; +} + +GNet::AddressImp::AddressImp( unsigned int port ) +{ + init() ; + m_inet.sin_addr.s_addr = htonl(INADDR_ANY); + setPort( port ) ; +} + +GNet::AddressImp::AddressImp( unsigned int port , Address::Localhost ) +{ + init() ; + m_inet.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + setPort( port ) ; +} + +GNet::AddressImp::AddressImp( const hostent & h , unsigned int port ) +{ + init() ; + setHost( h ) ; + setPort( port ) ; +} + +GNet::AddressImp::AddressImp( const hostent & h , const servent & s ) +{ + init() ; + setHost( h ) ; + m_inet.sin_port = s.s_port ; +} + +GNet::AddressImp::AddressImp( const servent & s ) +{ + init() ; + m_inet.sin_addr.s_addr = htonl(INADDR_ANY); + m_inet.sin_port = s.s_port ; +} + +GNet::AddressImp::AddressImp( const sockaddr * addr , size_t len ) +{ + init() ; + + if( addr == NULL ) + throw Address::Error() ; + + if( addr->sa_family != AF_INET || len != sizeof(address_type) ) + throw Address::BadFamily() ; + + set( addr ) ; +} + +GNet::AddressImp::AddressImp( const AddressImp & other ) +{ + m_inet = other.m_inet ; +} + +GNet::AddressImp::AddressImp( const std::string & s ) +{ + init() ; + std::string reason ; + if( ! setAddress( s , reason ) ) + throw Address::BadString( s ) ; +} + +bool GNet::AddressImp::setAddress( const std::string & display_string , std::string & reason ) +{ + if( !validString(display_string,&reason) ) + return false ; + + const size_t pos = display_string.rfind(m_port_separator) ; + std::string port_part = display_string.substr(pos+1U) ; + std::string host_part = display_string.substr(0U,pos) ; + + m_inet.sin_family = AF_INET ; + m_inet.sin_addr.s_addr = ::inet_addr( host_part.c_str() ) ; + setPort( G::Str::toUInt(port_part) ) ; + + G_ASSERT( displayString() == display_string ) ; + return true ; +} + +void GNet::AddressImp::setPort( unsigned int port ) +{ + G_ASSERT( validPort(port) ) ; + const in_port_t in_port = ::GConvert(port) ; + m_inet.sin_port = htons( in_port ) ; +} + +void GNet::AddressImp::setHost( const hostent & h ) +{ + if( h.h_addrtype != AF_INET || h.h_addr_list[0] == NULL ) + throw Address::BadFamily() ; + + const char * first = h.h_addr_list[0U] ; + const in_addr * raw = reinterpret_cast(first) ; + m_inet.sin_addr = *raw ; +} + +std::string GNet::AddressImp::displayString() const +{ + std::stringstream ss ; + ss << hostString() ; + ss << m_port_separator << port() ; + return ss.str() ; +} + +std::string GNet::AddressImp::hostString() const +{ + std::stringstream ss ; + ss << ::inet_ntoa(m_inet.sin_addr) ; + return ss.str() ; +} + +//static +bool GNet::AddressImp::validPort( unsigned int port ) +{ + return port <= 0xFFFFU ; // port numbers are now explicitly 16 bits, not short ints +} + +//static +bool GNet::AddressImp::validString( const std::string & s , std::string * reason_p ) +{ + std::string buffer ; + if( reason_p == NULL ) reason_p = &buffer ; + std::string & reason = *reason_p ; + + const size_t pos = s.rfind(m_port_separator) ; + if( pos == std::string::npos ) + { + reason = "no port separator" ; + return false ; + } + + std::string port_part = s.substr(pos+1U) ; + if( !validPortNumber(port_part) ) + { + reason = "invalid port number" ; + return false ; + } + + std::string host_part = s.substr(0U,pos) ; + + // (could test the string by passing it + // to inet_addr() here, but the man page points + // out that the error return value is not + // definitive) + + G::Strings parts ; + G::Str::splitIntoFields( host_part , parts , "." ) ; + if( parts.size() != 4U ) + { + reason = "invalid number of dotted parts" ; + return false ; + } + + G::Strings::iterator p = parts.begin() ; + reason = "invalid dotted part" ; + if( ! validPart(*p) ) return false ; p++ ; + if( ! validPart(*p) ) return false ; p++ ; + if( ! validPart(*p) ) return false ; p++ ; + if( ! validPart(*p) ) return false ; + + reason = "" ; + return true ; +} + +//static +bool GNet::AddressImp::validPortNumber( const std::string & s ) +{ + return validNumber(s) && G::Str::isUInt(s) && validPort(G::Str::toUInt(s)) ; +} + +//static +bool GNet::AddressImp::validNumber( const std::string & s ) +{ + return s.length() != 0U && G::Str::isNumeric(s) ; +} + +//static +bool GNet::AddressImp::validPart( const std::string & s ) +{ + return validNumber(s) && G::Str::toUInt(s,true) <= 255U ; +} + +bool GNet::AddressImp::same( const AddressImp & other ) const +{ + return + m_inet.sin_family == other.m_inet.sin_family && + m_inet.sin_family == AF_INET && + sameAddr( m_inet.sin_addr , other.m_inet.sin_addr ) && + m_inet.sin_port == other.m_inet.sin_port ; +} + +bool GNet::AddressImp::sameHost( const AddressImp & other ) const +{ + return + m_inet.sin_family == other.m_inet.sin_family && + m_inet.sin_family == AF_INET && + sameAddr( m_inet.sin_addr , other.m_inet.sin_addr ) ; +} + +//static +bool GNet::AddressImp::sameAddr( const ::in_addr & a , const ::in_addr & b ) +{ + return a.s_addr == b.s_addr ; +} + +unsigned int GNet::AddressImp::port() const +{ + return ntohs( m_inet.sin_port ) ; +} + +const sockaddr * GNet::AddressImp::raw() const +{ + return reinterpret_cast(&m_inet) ; +} + +sockaddr * GNet::AddressImp::raw() +{ + return reinterpret_cast(&m_inet) ; +} + +void GNet::AddressImp::set( const sockaddr * specific ) +{ + m_inet = *(reinterpret_cast(specific)) ; +} + +// === + +//static +GNet::Address GNet::Address::invalidAddress() +{ + return Address( 0U ) ; +} + +GNet::Address::Address( unsigned int port ) : + m_imp( new AddressImp(port) ) +{ +} + +GNet::Address::Address( unsigned int port , Localhost dummy ) : + m_imp( new AddressImp(port,dummy) ) +{ +} + +GNet::Address::Address( const hostent & h , unsigned int port ) : + m_imp( new AddressImp(h,port) ) +{ +} + +GNet::Address::Address( const hostent & h , const servent & s ) : + m_imp( new AddressImp(h,s) ) +{ +} + +GNet::Address::Address( const servent & s ) : + m_imp( new AddressImp(s) ) +{ +} + +GNet::Address::Address( const sockaddr *addr , int len ) : + m_imp( new AddressImp(addr,len) ) +{ +} + +GNet::Address::Address( const Address & other ) : + m_imp( new AddressImp(*other.m_imp) ) +{ +} + +GNet::Address::Address( const std::string & s ) : + m_imp( new AddressImp(s) ) +{ +} + +GNet::Address::~Address() +{ + delete m_imp ; +} + +void GNet::Address::setPort( unsigned int port ) +{ + m_imp->setPort( port ) ; +} + +void GNet::Address::operator=( const Address & addr ) +{ + delete m_imp ; + m_imp = NULL ; + m_imp = new AddressImp(*addr.m_imp) ; +} + +bool GNet::Address::operator==( const Address & other ) const +{ + return m_imp->same(*other.m_imp) ; +} + +bool GNet::Address::sameHost( const Address & other ) const +{ + return m_imp->sameHost(*other.m_imp) ; +} + +std::string GNet::Address::displayString( bool with_port ) const +{ + return with_port ? m_imp->displayString() : m_imp->hostString() ; +} + +std::string GNet::Address::hostString() const +{ + return m_imp->hostString() ; +} + +//static +bool GNet::Address::validString( const std::string & s , std::string * reason_p ) +{ + return AddressImp::validString( s , reason_p ) ; +} + +sockaddr * GNet::Address::address() +{ + return m_imp->raw() ; +} + +const sockaddr * GNet::Address::address() const +{ + return m_imp->raw() ; +} + +int GNet::Address::length() const +{ + return sizeof(AddressImp::address_type) ; +} + +unsigned int GNet::Address::port() const +{ + return m_imp->port() ; +} + +//static +bool GNet::Address::validPort( unsigned int port ) +{ + return AddressImp::validPort( port ) ; +} + +//static +GNet::Address GNet::Address::localhost( unsigned int port ) +{ + return Address( port , Localhost() ) ; +} + diff --git a/src/gnet/gclient.cpp b/src/gnet/gclient.cpp new file mode 100644 index 0000000..54fd40d --- /dev/null +++ b/src/gnet/gclient.cpp @@ -0,0 +1,465 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gclient.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gaddress.h" +#include "gsocket.h" +#include "gdatetime.h" +#include "gresolve.h" +#include "gclient.h" +#include "gdebug.h" +#include "gassert.h" +#include "glog.h" + +namespace +{ + const int c_retries = 10 ; // number of retries when using a priviledged local port number + const int c_port_start = 512 ; + const int c_port_end = 1024 ; +} ; + +namespace GNet +{ + class ClientResolver ; +} ; + +// Class: GNet::ClientResolver +// Description: A resolver class which calls ClientImp::resolveCon() when done. +// +class GNet::ClientResolver : public Resolver +{ +private: + ClientImp & m_client_imp ; + +public: + ClientResolver( ClientImp & imp ) ; + void resolveCon( bool success , + const Address &address , std::string reason ) ; + +private: + ClientResolver( const ClientResolver & ) ; + void operator=( const ClientResolver & ) ; +} ; + +inline +GNet::ClientResolver::ClientResolver( ClientImp & imp ) : + m_client_imp(imp) +{ +} + +// === + +// Class: GNet::ClientImp +// Description: A pimple-pattern implementation class for GClient. +// +class GNet::ClientImp : public EventHandler +{ +private: + ClientResolver m_resolver ; + StreamSocket * m_s ; + Address m_address ; + Client & m_interface ; + bool m_priviledged ; + enum Status { Success , Failure , Retry , ImmediateSuccess } ; + static bool m_first ; + enum State { Idle , Resolving , Connecting , Connected , Failed , Disconnected } ; + State m_state ; + bool m_quit_on_disconnect ; + +public: + ClientImp( Client &intaface , bool priviledged , bool quit_on_disconnect ) ; + virtual ~ClientImp() ; + void resolveCon( bool ok , const Address & address , std::string reason ) ; + void readEvent() ; + void writeEvent() ; + void exceptionEvent() ; + bool connect( std::string host , std::string service , std::string *error , bool sync_dns ) ; + std::string startConnecting( const Address & , bool & ) ; + Status connectCore( Address , std::string * , bool , unsigned int ) ; + void disconnect() ; + StreamSocket & s() ; + void run() ; + void close() ; + void blocked() ; + bool connected() const ; + void setState( State ) ; + +private: + ClientImp( const ClientImp & ) ; + void operator=( const ClientImp & ) ; + static int getRandomPort() ; +} ; + +// === + +GNet::Client::Client( bool priviledged , bool quit_on_disconnect ) : + m_imp(NULL) +{ + G_DEBUG( "Client::ctor" ) ; + m_imp = new ClientImp( *this , priviledged , quit_on_disconnect ) ; +} + +GNet::Client::~Client() +{ + delete m_imp ; +} + +bool GNet::Client::connect( std::string host , std::string service , std::string *error_p , bool sync_dns ) +{ + return m_imp->connect( host , service , error_p , sync_dns ) ; +} + +void GNet::Client::run() +{ + m_imp->run() ; +} + +bool GNet::Client::connected() const +{ + return m_imp->connected() ; +} + +void GNet::Client::blocked() +{ + m_imp->blocked() ; +} + +void GNet::Client::disconnect() +{ + m_imp->disconnect() ; +} + +// === + +bool GNet::ClientImp::m_first = true ; + +GNet::ClientImp::ClientImp( Client &intaface , bool priviledged , bool quit_on_disconnect ) : + m_interface(intaface) , + m_state(Idle) , + m_s(NULL) , + m_resolver(*this) , + m_priviledged(priviledged) , + m_address(Address::invalidAddress()) , + m_quit_on_disconnect(quit_on_disconnect) +{ + G_DEBUG( "ClientImp::ctor" ) ; +} + +int GNet::ClientImp::getRandomPort() +{ + if( m_first ) + { + std::srand( static_cast(G::DateTime::now()) ) ; + m_first = false ; + } + + int r = std::rand() ; + if( r < 0 ) r = -r ; + r = r % (c_port_end - c_port_start) ; + return r + c_port_start ; +} + +GNet::StreamSocket & GNet::ClientImp::s() +{ + G_ASSERT( m_s != NULL ) ; + return *m_s ; +} + +GNet::ClientImp::~ClientImp() +{ + setState( Disconnected ) ; // for quit() + close() ; +} + +void GNet::ClientImp::disconnect() +{ + setState( Disconnected ) ; + close() ; +} + +void GNet::ClientImp::close() +{ + delete m_s ; + m_s = NULL ; +} + +bool GNet::ClientImp::connected() const +{ + return m_state == Connected ; +} + +bool GNet::ClientImp::connect( std::string host , std::string service , + std::string *error_p , bool sync_dns ) +{ + G_DEBUG( "GNet::ClientImp::connect: \"" << host << "\", \"" << service << "\"" ) ; + + std::string dummy_error_string ; + if( error_p == NULL ) + error_p = &dummy_error_string ; + std::string &error = *error_p ; + + if( sync_dns ) + { + std::pair pair = Resolver::resolve( host , service ) ; + std::string & resolve_reason = pair.second ; + if( resolve_reason.length() != 0U ) + { + error = resolve_reason ; + setState( Failed ) ; + return false ; + } + bool immediate = false ; + std::string connect_reason = startConnecting( pair.first.address , immediate ) ; + if( connect_reason.length() != 0U ) + { + error = connect_reason ; + setState( Failed ) ; + return false ; + } + if( immediate ) + { + G_WARNING( "GNet::Client::connect: immediate connection" ) ; // delete soon + s().addReadHandler( *this ) ; + s().addExceptionHandler( *this ) ; + setState( Connected ) ; + m_interface.onConnect( s() ) ; // from within connect() ? + } + else + { + setState( Connecting ) ; + } + } + else + { + std::string address_string( host ) ; + address_string.append( ":" ) ; + address_string.append( service.c_str() ) ; + if( !m_resolver.resolveReq( address_string ) ) + { + error = "invalid host/service: " ; + error.append( address_string.c_str() ) ; + setState( Failed ) ; + return false ; + } + setState( Resolving ) ; + } + return true ; +} + +void GNet::ClientImp::resolveCon( bool success , const Address &address , + std::string resolve_reason ) +{ + if( success ) + { + G_DEBUG( "GNet::ClientImp::resolveCon: " << address.displayString() ) ; + bool immediate = false ; + std::string connect_reason = startConnecting( address , immediate ) ; + if( connect_reason.length() ) + { + close() ; + setState( Failed ) ; + m_interface.onError( connect_reason ) ; + } + setState( immediate ? Connected : Connecting ) ; + } + else + { + resolve_reason = std::string("resolver error: ") + resolve_reason ; + close() ; + setState( Failed ) ; + m_interface.onError( resolve_reason ) ; + } +} + +std::string GNet::ClientImp::startConnecting( const Address & address , bool & immediate ) +{ + // save the target address + G_DEBUG( "GNet::ClientImp::startConnecting: " << address.displayString() ) ; + m_address = address ; + + // create and open a socket + // + m_s = new StreamSocket ; + if( !s().valid() ) + { + return std::string( "error: cannot open socket" ) ; + } + + // specifiy this as a 'write' event handler for the socket + // (before the connect() in case it is reentrant) + // + s().addWriteHandler( *this ) ; + + Status status = Failure ; + std::string error ; + if( m_priviledged ) + { + for( int i = 0 ; i < c_retries ; i++ ) + { + int port = getRandomPort() ; + G_DEBUG( "GNet::ClientImp::resolveCon: trying to bind port " << port ) ; + status = connectCore( address, &error, true, port ) ; + if( status != Retry ) + break ; + } + } + else + { + status = connectCore( address , &error , false , 0 ) ; + } + + immediate = status == ImmediateSuccess ; + if( status != Success ) + s().dropWriteHandler() ; + + if( status == Success ) error = std::string() ; + return error ; +} + +GNet::ClientImp::Status GNet::ClientImp::connectCore( Address remote_address , + std::string *error_p , bool set_port , unsigned int port ) +{ + G_ASSERT( error_p != NULL ) ; + std::string &error = *error_p ; + + Address local_address( set_port ? port : 0 ) ; + bool bound = s().bind(local_address) ; + if( !bound ) + { + error = "error: cannot bind socket" ; + return Retry ; + } + G_DEBUG( "GNet::ClientImp::connectCore: bound local address " + << local_address.displayString() ) ; + + // initiate the connection + // + bool immediate = false ; + if( !s().connect( remote_address , &immediate ) ) + { + G_DEBUG( "GNet::ClientImp::connect: immediate failure" ) ; + error = "error: cannot connect to " ; + error.append( remote_address.displayString().c_str() ) ; + + // we should return Failure here, but Microsoft's stack + // will happily bind the same local address more than once, + // so it is the connect that fails, not the bind, if + // the port was already in use + // + return Retry ; + } + else + { + return immediate ? ImmediateSuccess : Success ; + } +} + +void GNet::ClientImp::blocked() +{ + s().addWriteHandler( *this ) ; +} + +void GNet::ClientImp::writeEvent() +{ + G_DEBUG( "GNet::ClientImp::writeEvent" ) ; + + if( m_state == Connected ) + { + s().dropWriteHandler() ; + m_interface.onWriteable() ; + } + else if( m_state == Connecting && s().hasPeer() ) + { + s().addReadHandler( *this ) ; + s().addExceptionHandler( *this ) ; + s().dropWriteHandler() ; + + setState( Connected ) ; + m_interface.onConnect( s() ) ; + } + else if( m_state == Connecting ) + { + std::string message( "error: cannot connect to " ) ; + message.append( m_address.displayString().c_str() ) ; + setState( Failed ) ; + close() ; + m_interface.onError( message ) ; + } +} + +void GNet::ClientImp::readEvent() +{ + char buffer[200U] ; + ssize_t n = s().read( buffer , sizeof(buffer) ) ; + + if( n <= 0 ) + { + if( s().eWouldBlock() ) return ; // for windows + + close() ; + setState( Disconnected ) ; + m_interface.onDisconnect() ; + } + else + { + G_ASSERT( n <= sizeof(buffer) ) ; + G_DEBUG( "GNet::ClientImp::readEvent: " << n << " byte(s)" ) ; + m_interface.onData( buffer , n ) ; + } +} + +void GNet::ClientImp::exceptionEvent() +{ + G_DEBUG( "GNet::ClientImp::exceptionEvent" ) ; + close() ; + setState( Failed ) ; + m_interface.onDisconnect() ; +} + +void GNet::ClientImp::setState( State new_state ) +{ + if( m_quit_on_disconnect && + (new_state == Disconnected || new_state == Failed) && + (m_state != Disconnected && m_state != Failed) ) + { + G_DEBUG( "GNet::ClientImp::setState: " << m_state + << " -> " << new_state << ": quitting the event loop" ) ; + EventSources::instance().quit() ; + } + m_state = new_state ; +} + +void GNet::ClientImp::run() +{ + EventSources::instance().run() ; +} + +// === + +void GNet::ClientResolver::resolveCon( bool success , const Address &address , + std::string reason ) +{ + m_client_imp.resolveCon( success , address , reason ) ; +} + diff --git a/src/gnet/gclient.h b/src/gnet/gclient.h new file mode 100644 index 0000000..cf672b5 --- /dev/null +++ b/src/gnet/gclient.h @@ -0,0 +1,117 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gclient.h +// + +#ifndef G_CLIENT_H +#define G_CLIENT_H + +#include "gdef.h" +#include "gnet.h" +#include "gaddress.h" +#include "gsocket.h" +#include "gevent.h" +#include + +namespace GNet +{ + class Client ; + class ClientImp ; +} ; + +// Class: GNet::Client +// Description: An application-level class for making an outgoing connection +// to a remote server. The class handles address resolution and connection +// issues, and it reads incoming data. There is some support for flow-control +// issues when writing data out to the server. +// +class GNet::Client +{ +public: + explicit Client( bool priviledged = false , bool quit_on_disconnect = false ) ; + // Constructor. If the 'priviledged' parameter + // is true the the local endpoint of the + // outgoing connection is bound to a + // priviledged port number (ie. < 1024), + // selected at random. + // + // If the 'quit' parameter is true then the client will + // call EventSources::quit() once it fails to connect, + // disconnects or looses the connection. Cleary 'quit' + // should only be true if this client is the only thing + // using the event loop. + + bool connect( std::string host, std::string service, + std::string *error_string = NULL , + bool sync_dns = synchronousDnsDefault() ); + // Initates a connection to the remote server. + // Typically called before calling run(). + + bool connected() const ; + // Returns true if connected to the peer. + + void disconnect() ; + // Disconnects from the peer. + + void blocked() ; + // To be called when a Socket::write() fails + // due to flow control. The virtual method + // onWriteable() will then be called when + // the connection unblocks. + + void run() ; + // Starts the main event loop using EventSources::run(). + + static bool synchronousDnsDefault() ; + // Returns true if DNS queries should normally be + // synchronous on this platform. + + virtual ~Client() ; + // Destructor. + +protected: + friend class ClientImp ; + + virtual void onConnect( Socket & socket ) = 0 ; + // Called once connected. May (unfortunately) be + // called from within connect(). + + virtual void onDisconnect() = 0 ; + // Called when disconnected by the peer. + + virtual void onData( const char * data , size_t size ) = 0 ; + // Called on receipt of data. + + virtual void onError( const std::string &error ) = 0 ; + // Called when an asyncronous, and fatal, error occurs. + + virtual void onWriteable() = 0 ; + // Called when a blocked connection become writeable. + +private: + Client( const Client& ) ; // Copy constructor. Not implemented. + void operator=( const Client& ) ; // Assignment operator. Not implemented. + +private: + ClientImp * m_imp ; +} ; + +#endif diff --git a/src/gnet/gclient_unix.cpp b/src/gnet/gclient_unix.cpp new file mode 100644 index 0000000..e7399d0 --- /dev/null +++ b/src/gnet/gclient_unix.cpp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gclient_unix.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gclient.h" + +bool GNet::Client::synchronousDnsDefault() +{ + return true ; +} + diff --git a/src/gnet/gclient_win32.cpp b/src/gnet/gclient_win32.cpp new file mode 100644 index 0000000..14d9202 --- /dev/null +++ b/src/gnet/gclient_win32.cpp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gclient_win32.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gclient.h" + +bool GNet::Client::synchronousDnsDefault() +{ + return false ; +} + diff --git a/src/gnet/gdescriptor.h b/src/gnet/gdescriptor.h new file mode 100644 index 0000000..1b8a130 --- /dev/null +++ b/src/gnet/gdescriptor.h @@ -0,0 +1,38 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdescriptor.h +// + +#ifndef G_DESCRIPTOR_H +#define G_DESCRIPTOR_H + +#include "gdef.h" +#include "gnet.h" + +namespace GNet +{ + typedef ::SOCKET Descriptor ; // SOCKET defined in gnet.h + bool Descriptor__valid( Descriptor fd ) ; + Descriptor Descriptor__invalid() ; +} ; + +#endif + diff --git a/src/gnet/gdescriptor_unix.cpp b/src/gnet/gdescriptor_unix.cpp new file mode 100644 index 0000000..8add78e --- /dev/null +++ b/src/gnet/gdescriptor_unix.cpp @@ -0,0 +1,37 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdescriptor_unix.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gdescriptor.h" + +bool GNet::Descriptor__valid( Descriptor fd ) +{ + return fd > 0 ; +} + +GNet::Descriptor GNet::Descriptor__invalid() +{ + return -1 ; +} + diff --git a/src/gnet/gdescriptor_win32.cpp b/src/gnet/gdescriptor_win32.cpp new file mode 100644 index 0000000..797de81 --- /dev/null +++ b/src/gnet/gdescriptor_win32.cpp @@ -0,0 +1,37 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gdescriptor_win32.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gdescriptor.h" + +bool GNet::Descriptor__valid( Descriptor fd ) +{ + return fd != INVALID_SOCKET ; +} + +GNet::Descriptor GNet::Descriptor__invalid() +{ + return INVALID_SOCKET ; +} + diff --git a/src/gnet/gevent.cpp b/src/gnet/gevent.cpp new file mode 100644 index 0000000..be4b3b2 --- /dev/null +++ b/src/gnet/gevent.cpp @@ -0,0 +1,206 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gevent.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gevent.h" +#include "gdebug.h" +#include "gassert.h" +#include "glog.h" + +GNet::EventHandler::EventHandler() +{ +} + +void GNet::EventHandler::readEvent() +{ + G_DEBUG( "GNet::EventHandler::readEvent: no override" ) ; +} + +void GNet::EventHandler::writeEvent() +{ + G_DEBUG( "GNet::EventHandler::writeEvent: no override" ) ; +} + +void GNet::EventHandler::exceptionEvent() +{ + G_DEBUG( "GNet::EventHandler::exceptionEvent: no override" ) ; +} + +// === + +GNet::EventSources * GNet::EventSources::m_this = NULL ; + +GNet::EventSources::EventSources() +{ + if( m_this == NULL ) + m_this = this ; + else + G_WARNING( "GNet::EventSources::ctor: multiple instances" ) ; +} + +GNet::EventSources::~EventSources() +{ + if( m_this == this ) + m_this = NULL ; +} + +GNet::EventSources & GNet::EventSources::instance() +{ + G_ASSERT( m_this != NULL ) ; + return *m_this ; +} + +// === + +GNet::EventHandlerList::EventHandlerList( std::string type ) : + m_type(type) , + m_lock(0U) , + m_copied(false) +{ +} + +//static +bool GNet::EventHandlerList::contains( const EventHandlerListImp & list , Descriptor fd ) +{ + for( List::const_iterator p = list.begin() ; p != list.end() ; ++p ) + { + if( (*p).m_fd == fd ) + return true ; + } + return false ; +} + +bool GNet::EventHandlerList::contains( Descriptor fd ) const +{ + return contains( m_list , fd ) ; +} + +std::string GNet::EventHandlerList::asString() const +{ + return asString( m_list ) ; +} + +std::string GNet::EventHandlerList::asString( const EventHandlerListImp & list ) const +{ + std::stringstream ss ; + const char * sep = "" ; + for( List::const_iterator p = list.begin() ; p != list.end() ; ++p ) + { + ss << sep << fd( p ) ; + sep = "," ; + } + return ss.str() ; +} + +GNet::EventHandlerListImp & GNet::EventHandlerList::list() +{ + // lazy copy + if( m_lock != 0U && !m_copied ) + { + m_copy = m_list ; + m_copied = true ; + } + + return m_lock == 0U ? m_list : m_copy ; +} + +void GNet::EventHandlerList::add( Descriptor fd , EventHandler * handler ) +{ + G_ASSERT( handler != NULL ) ; + if( ! contains(list(),fd) ) + { + G_DEBUG( "GNet::EventHandlerList::add: " << m_type << "-list: adding " << fd << (m_lock?" (deferred)":"") ) ; + list().push_back( EventHandlerListItem(fd,handler) ) ; + } +} + +void GNet::EventHandlerList::remove( Descriptor fd ) +{ + G_DEBUG( "GNet::EventHandlerList::remove: " << m_type << "-list: removing " << fd << (m_lock?" (deferred)":"") ) ; + bool found = false ; + for( List::iterator p = list().begin() ; p != list().end() ; ++p ) + { + if( (*p).m_fd == fd ) + { + list().erase( p ) ; + found = true ; + break ; + } + } + //if( !found ) G_DEBUG( "GNet::EventHandlerList::remove: cannot find " << fd ) ; +} + +GNet::EventHandler * GNet::EventHandlerList::find( Descriptor fd ) +{ + for( List::iterator p = m_list.begin() ; p != m_list.end() ; ++p ) + { + if( (*p).m_fd == fd ) + return (*p).m_handler ; + } + //G_DEBUG( "GNet::EventHandlerList::find: cannot find entry for " << fd ) ; + return NULL ; +} + +GNet::EventHandlerList::Iterator GNet::EventHandlerList::begin() const +{ + return m_list.begin() ; +} + +GNet::EventHandlerList::Iterator GNet::EventHandlerList::end() const +{ + return m_list.end() ; +} + +//static +GNet::Descriptor GNet::EventHandlerList::fd( Iterator i ) +{ + return (*i).m_fd ; +} + +//static +GNet::EventHandler & GNet::EventHandlerList::handler( Iterator i ) +{ + EventHandler * p = (*i).m_handler ; + G_ASSERT( p != NULL ) ; + return *p ; +} + +void GNet::EventHandlerList::lock() +{ + m_lock++ ; +} + +void GNet::EventHandlerList::unlock() +{ + m_lock-- ; + if( m_lock == 0U && m_copied ) + { + G_DEBUG( "GNet::EventHandlerList::unlock: " << m_type << "-list: " + << "commiting deferred changes: before=" << asString(m_list) + << ": after=" << asString(m_copy) ) ; + m_list = m_copy ; + m_copied = false ; + } +} + diff --git a/src/gnet/gevent.h b/src/gnet/gevent.h new file mode 100644 index 0000000..5428ecc --- /dev/null +++ b/src/gnet/gevent.h @@ -0,0 +1,237 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gevent.h +// + +#ifndef G_EVENT_H +#define G_EVENT_H + +#include "gdef.h" +#include "gnet.h" +#include "gdescriptor.h" +#include +#include + +namespace GNet +{ + class EventHandler ; + class EventSources ; + class EventHandlerListItem ; + class EventHandlerList ; +} ; + +// Class: GNet::EventHandler +// Description: A pseudo-abstract base class for classes +// which handle asynchronous socket events. ("Pseudo" because +// there are empty implementations for the virtual methods, +// since you dont want to have to override all three every time.) +// +// An event handler object has its virtual methods called when +// an event is detected on the associated file descriptor. +// +// A file descriptor and its associated event handler +// are typically kept in a EventHandlerListItem +// structure, within the EventSources singleton. +// +class GNet::EventHandler +{ +public: + EventHandler() ; + virtual void readEvent() /*=0*/ ; + virtual void writeEvent() /*=0*/ ; + virtual void exceptionEvent() /*=0*/ ; +private: + EventHandler( const EventHandler & ) ; + void operator=( const EventHandler & ) ; +} ; + +// Class: GNet::EventSources +// Description: An abstract base class for a +// singleton which keeps track of open sockets and their +// associated handlers. Derived classes are used to +// implement different event loops, such as ::select() +// or WinSock. +// +// In practice sockets are added and removed from the +// class by calling GNet::Socket::addReadHandler() etc rather +// than EventSources::addRead(). This is to improve the +// encapsulation of the GNet::Descriptor data type within +// Socket. +// +// The class has a static member for finding an instance, +// but instances are not created automatically. +// +class GNet::EventSources +{ +private: + static EventSources *m_this ; + +protected: + EventSources() ; + // Constructor. + +public: + static EventSources * create() ; + // A factory method which creates an instance + // of a derived class on the heap. + + static EventSources & instance() ; + // Returns a reference to an instance + // of the class, if any. Asserts if none. + // Does not do any instantiation itself. + + virtual ~EventSources() ; + // Destructor. + + virtual bool init() = 0 ; + // Initialises the object. + + virtual void run() = 0 ; + // Runs the main event loop. + + virtual void quit() = 0 ; + // Causes run() to return (once the call stack + // has unwound). + + virtual void addRead( Descriptor fd , EventHandler &handler ) = 0 ; + // Adds the given event source descriptor + // and associated handler to the read list. + // See also Socket::addReadHandler(). + + virtual void addWrite( Descriptor fd , EventHandler &handler ) = 0 ; + // Adds the given event source descriptor + // and associated handler to the write list. + // See also Socket::addWriteHandler(). + + virtual void addException( Descriptor fd , EventHandler &handler ) = 0 ; + // Adds the given event source descriptor + // and associated handler to the exception list. + // See also Socket::addExceptionHandler(). + + virtual void dropRead( Descriptor fd ) = 0 ; + // Removes the given event source descriptor + // from the list of read sources. + // See also Socket::dropReadHandler(). + + virtual void dropWrite( Descriptor fd ) = 0 ; + // Removes the given event source descriptor + // from the list of write sources. + // See also Socket::dropWriteHandler(). + + virtual void dropException( Descriptor fd ) = 0 ; + // Removes the given event source descriptor + // from the list of exception sources. + // See also Socket::dropExceptionHandler(). +} ; + + +// Class: GNet::EventHandlerListItem +// Description: A private class which contains a file descriptor +// and a reference to its handler. +// +class GNet::EventHandlerListItem +{ +public: + Descriptor m_fd ; + EventHandler * m_handler ; + EventHandlerListItem( Descriptor fd = Descriptor__invalid() , + EventHandler * handler = NULL ) : + m_fd(fd) , m_handler(handler) {} +} ; + +namespace GNet +{ + typedef std::list< EventHandlerListItem GAllocator(EventHandlerListItem) > + EventHandlerListImp ; +} ; + +// Class: GNet::EventHandlerList +// Description: A class which can be used in the implemention +// of classes derived from GEventSources. +// +class GNet::EventHandlerList +{ +public: + typedef EventHandlerListImp List ; + typedef List::const_iterator Iterator ; + +public: + explicit EventHandlerList( std::string type ) ; + // Constructor. The type parameter (eg. "read") + // is used only in debugging messages. + + void add( Descriptor fd , EventHandler *handler ) ; + // Adds a file-descriptor/handler pair to + // the list. + + void remove( Descriptor fd ) ; + // Removes a file-descriptor from the list. + + bool contains( Descriptor fd ) const ; + // Returns true if the list contains the + // given file-descriptor. + + EventHandler * find( Descriptor fd ) ; + // Finds the handler associated with the + // given file descriptor. + + void lock() ; + // Locks the list so that add() and remove() are + // deferred until the matching unlock(). This + // is needed during iteration -- see begin()/end(). + + void unlock() ; + // Applies any deferred changes. See lock(). + + Iterator begin() const ; + // Returns an iterator (using the STL model). + + Iterator end() const ; + // Returns an end iterator (using the STL model). + + static Descriptor fd( Iterator i ) ; + // Returns the iterator's file descriptor. + + static EventHandler & handler( Iterator i ) ; + // Returns the iterator's handler. + + std::string asString() const ; + // Returns a descriptive string for the list. Used + // for debugging. + +private: + EventHandlerList( const EventHandlerList & ) ; + void operator=( const EventHandlerList & ) ; + static bool contains( const EventHandlerListImp & , Descriptor fd ) ; + EventHandlerListImp & list() ; + std::string asString( const EventHandlerListImp & ) const ; + +private: + std::string m_type ; // for debugging + List m_list ; + List m_copy ; + unsigned int m_lock ; + bool m_copied ; +} ; + + +#endif + diff --git a/src/gnet/gevent_unix.cpp b/src/gnet/gevent_unix.cpp new file mode 100644 index 0000000..5e7bb41 --- /dev/null +++ b/src/gnet/gevent_unix.cpp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gevent_unix.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gevent.h" +#include "gselect.h" + +GNet::EventSources * GNet::EventSources::create() +{ + return new Select ; +} + diff --git a/src/gnet/gevent_win32.cpp b/src/gnet/gevent_win32.cpp new file mode 100644 index 0000000..c319894 --- /dev/null +++ b/src/gnet/gevent_win32.cpp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gevent_win32.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gevent.h" +#include "gwinsock.h" + +GNet::EventSources * GNet::EventSources::create() +{ + return new Winsock ; +} + diff --git a/src/gnet/geventserver.cpp b/src/gnet/geventserver.cpp new file mode 100644 index 0000000..e5aaaf1 --- /dev/null +++ b/src/gnet/geventserver.cpp @@ -0,0 +1,74 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// geventserver.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "geventserver.h" +#include "gevent.h" +#include "gexception.h" +#include "glog.h" + +// Class: GNet::EventServerImp +// Description: A pimple-pattern implementation class for GNet::EventServer. +// +class GNet::EventServerImp +{ +public: + std::auto_ptr m_event_loop ; + +public: + EventServerImp() ; + void run() ; +} ; + +// === + +GNet::EventServer::EventServer( unsigned int listening_port ) : + m_imp(NULL) +{ + m_imp = new EventServerImp ; + init( listening_port ) ; // Server::init() +} + +GNet::EventServer::~EventServer() +{ + delete m_imp ; +} + +void GNet::EventServer::run() +{ + m_imp->run() ; +} + +// === + +GNet::EventServerImp::EventServerImp() : + m_event_loop(EventSources::create()) +{ +} + +void GNet::EventServerImp::run() +{ + m_event_loop->run() ; +} + diff --git a/src/gnet/geventserver.h b/src/gnet/geventserver.h new file mode 100644 index 0000000..385587c --- /dev/null +++ b/src/gnet/geventserver.h @@ -0,0 +1,65 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// geventserver.h +// + +#ifndef G_EVENT_SERVER_H +#define G_EVENT_SERVER_H + +#include "gdef.h" +#include "gnet.h" +#include "gserver.h" + +namespace GNet +{ + class EventServer ; + class EventServerImp ; +} ; + +// Class: GNet::EventServer +// Description: A simple derivation from Server which +// simply adds an event loop. Only one instance +// may be created. +// See also: GNet::EventSources +// +class GNet::EventServer : public Server +{ +public: + explicit EventServer( unsigned int listening_port ) ; + // Constructor. Throws exceptions on + // error. + + virtual ~EventServer() ; + // Destructor. + + void run() ; + // Runs the event loop. + +private: + EventServer( const EventServer & ) ; + void operator=( const EventServer & ) ; + +private: + EventServerImp * m_imp ; +} ; + +#endif + diff --git a/src/gnet/glinebuffer.cpp b/src/gnet/glinebuffer.cpp new file mode 100644 index 0000000..4f6738a --- /dev/null +++ b/src/gnet/glinebuffer.cpp @@ -0,0 +1,99 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glinebuffer.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "glinebuffer.h" +#include "gdebug.h" +#include "gassert.h" + +GNet::LineBuffer::LineBuffer( const std::string & eol ) : + m_eol(eol) +{ +} + +//static +std::string GNet::LineBuffer::asString( char c ) +{ + std::stringstream ss ; + if( c == '\n' ) + ss << "\\n" ; + else if( c >= ' ' && c < 0x7f ) + ss << c ; + else + ss << "\\" << (unsigned int)c ; + return ss.str() ; +} + +void GNet::LineBuffer::add( const std::string & segment ) +{ + size_t n = segment.size() ; + for( size_t i = 0U ; i < n ; i++ ) + { + if( m_lines.empty() ) + m_lines.push_back( std::string() ) ; + + char c = segment.at(i) ; + m_lines.back().append( 1U , c ) ; + if( terminated() ) + { + m_lines.push_back( std::string() ) ; + } + } +} + +bool GNet::LineBuffer::terminated() const +{ + G_ASSERT( ! m_lines.empty() ) ; + const std::string & s = m_lines.back() ; + if( s.length() < m_eol.length() ) + { + return false ; + } + else + { + size_t pos = s.length() - m_eol.length() ; + return s.find(m_eol,pos) == pos ; + } +} + +bool GNet::LineBuffer::more() const +{ + return + ( m_lines.size() == 1U && terminated() ) || + m_lines.size() > 1U ; +} + +std::string GNet::LineBuffer::line() +{ + G_ASSERT( ! m_lines.empty() ) ; + std::string result = m_lines.front() ; + size_t n = result.length() ; + G_ASSERT( n >= m_eol.length() ) ; + n -= m_eol.length() ; + G_ASSERT( result.find(m_eol,n) == n ) ; + result.erase( n , m_eol.length() ) ; + m_lines.pop_front() ; + return result ; +} + diff --git a/src/gnet/glinebuffer.h b/src/gnet/glinebuffer.h new file mode 100644 index 0000000..85fd44a --- /dev/null +++ b/src/gnet/glinebuffer.h @@ -0,0 +1,81 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glinebuffer.h +// + +#ifndef G_LINE_BUFFER_H +#define G_LINE_BUFFER_H + +#include "gdef.h" +#include "gnet.h" +#include "gstrings.h" +#include +#include + +namespace GNet +{ + class LineBuffer ; +} ; + +// Class: GNet::LineBuffer +// Description: A class which does line buffering. Raw +// data is added, and newline-delimited lines are +// extracted. +// Usage: +/// { +/// GNet::LineBuffer buffer ; +/// buffer.add("abc") ; +/// buffer.add("def\nABC\nDE") ; +/// buffer.add("F\n") ; +/// while( buffer.more() ) +/// cout << buffer.line() << endl ; +/// } +// +class GNet::LineBuffer +{ +public: + explicit LineBuffer( const std::string & eol = std::string("\n") ) ; + // Constructor. + + void add( const std::string & segment ) ; + // Adds a data segment. + + bool more() const ; + // Returns true if there are complete + // line(s) to be extracted. + + std::string line() ; + // Extracts a line. The line terminator + // is not included. + +private: + LineBuffer( const LineBuffer & ) ; + void operator=( const LineBuffer & ) ; + bool terminated() const ; + static std::string asString( char c ) ; + +private: + G::Strings m_lines ; + std::string m_eol ; +} ; + +#endif + diff --git a/src/gnet/glocal.h b/src/gnet/glocal.h new file mode 100644 index 0000000..5c3de32 --- /dev/null +++ b/src/gnet/glocal.h @@ -0,0 +1,69 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glocal.h +// + +#ifndef G_GNET_LOCAL_SYSTEM_H +#define G_GNET_LOCAL_SYSTEM_H + +#include "gdef.h" +#include "gnet.h" +#include "gaddress.h" +#include "gexception.h" + +namespace GNet +{ + class Local ; +} ; + +// Class: GNet::Local +// Description: A static class for getting information +// about the local system. +// +class GNet::Local +{ +public: + G_EXCEPTION( Error , "local domainname/hostname error" ) ; + + static std::string hostname() ; + // Returns the hostname. + + static std::string domainname() ; + // Returns the domainname. + + static std::string fqdn() ; + // Returns the fully-qualified-domain-name + // (hostname() + domainname()). + + static Address canonicalAddress() ; + // Returns the address associated with + // the canonical hostname. + + static Address localhostAddress() ; + // A convenience function returning the + // "127.0.0.1:0" address. + +private: + Local() ; +} ; + +#endif + diff --git a/src/gnet/glocal_unix.cpp b/src/gnet/glocal_unix.cpp new file mode 100644 index 0000000..37205eb --- /dev/null +++ b/src/gnet/glocal_unix.cpp @@ -0,0 +1,73 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glocal_unix.cpp +// + +#include "gdef.h" +#include "glocal.h" +#include "gresolve.h" +#include "glog.h" +#include + +std::string GNet::Local::hostname() +{ + struct ::utsname info ; + int rc = ::uname( &info ) ; + if( rc == -1 ) throw Error("uname") ; + return std::string(info.nodename) ; +} + +std::string GNet::Local::domainname() +{ + // (see also: getdomainname() -- returns empty) + + std::string full = fqdn() ; + size_t pos = full.rfind( '.' ) ; + if( pos == std::string::npos ) + throw Error( "invalid fqdn" ) ; + + G_DEBUG( "GNet::Local::domainname: \"" << full.substr(0U,pos) << "\"" ) ; + return full.substr( 0U , pos ) ; +} + +GNet::Address GNet::Local::canonicalAddress() +{ + std::pair rc = Resolver::resolve( hostname() , "0" ) ; + if( rc.second.length() != 0U ) + throw Error(rc.second) ; + return rc.first.address ; +} + +GNet::Address GNet::Local::localhostAddress() +{ + return Address::localhost( 0U ) ; +} + +std::string GNet::Local::fqdn() +{ + std::pair rc = Resolver::resolve( hostname() , "0" ) ; + if( rc.second.length() != 0U ) + throw Error(rc.second) ; + + G_DEBUG( "GNet::Local::fqdn: \"" << rc.first.canonical_name << "\"" ) ; + return rc.first.canonical_name ; +} + diff --git a/src/gnet/glocal_win32.cpp b/src/gnet/glocal_win32.cpp new file mode 100644 index 0000000..18eb57d --- /dev/null +++ b/src/gnet/glocal_win32.cpp @@ -0,0 +1,91 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// glocal_win32.cpp +// + +#include "gdef.h" +#include "glocal.h" +#include "gresolve.h" +#include "glog.h" + +std::string GNet::Local::hostname() +{ + char buffer[1024U] ; + if( 0 != ::gethostname( buffer , sizeof(buffer)-1U ) ) + { + int error = ::WSAGetLastError() ; + throw Error( std::stringstream() << "gethostname() (" << error << ")" ) ; + } + return std::string(buffer) ; +} + +std::string GNet::Local::domainname() +{ + std::string full = fqdn() ; + size_t pos = full.find( '.' ) ; + if( pos == std::string::npos ) + throw Error( std::stringstream() << "invalid fqdn: no dots in \"" << full << "\"" ) ; + return full.substr(pos+1U) ; +} + +GNet::Address GNet::Local::canonicalAddress() +{ + static bool first = true ; // avoid multiple synchronous DNS queries + static Address result( Address::invalidAddress() ) ; + if( first ) + { + first = false ; + + std::pair rc = + Resolver::resolve( hostname() , "0" ) ; + + if( rc.second.length() != 0U ) + throw Error( std::stringstream() << "resolve: " << rc.second ) ; + + result = rc.first.address ; + } + return result ; +} + +GNet::Address GNet::Local::localhostAddress() +{ + return Address::localhost( 0U ) ; +} + +std::string GNet::Local::fqdn() +{ + static bool first = true ; // avoid multiple synchronous DNS queries + static std::string result ; + if( first ) + { + first = false ; + + std::pair rc = + Resolver::resolve( hostname() , "0" ) ; + + if( rc.second.length() != 0U ) + throw Error( std::stringstream() << "resolve: " << rc.second ) ; + + result = rc.first.canonical_name ; + } + return result ; +} + diff --git a/src/gnet/gnet.h b/src/gnet/gnet.h new file mode 100644 index 0000000..1b856d4 --- /dev/null +++ b/src/gnet/gnet.h @@ -0,0 +1,49 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gnet.h +// + +#ifndef G_NET_H +#define G_NET_H + +// Title: gnet.h +// Description: gnet.h includes o/s-dependent network header files. + +#ifdef G_UNIX + #include + #include + #include + #include + #include + #include // for inet_ntoa() + typedef int SOCKET ; // used in gdescriptor.h +#else + #include + typedef int socklen_t ; + typedef unsigned short in_port_t ; +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff // (should be in netinet/in.h) +#endif + +#endif + diff --git a/src/gnet/grequest.cpp b/src/gnet/grequest.cpp new file mode 100644 index 0000000..c28f3bf --- /dev/null +++ b/src/gnet/grequest.cpp @@ -0,0 +1,218 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// grequest.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "grequest.h" +#include "gwinhid.h" +#include "gdebug.h" +#include "gassert.h" +#include "glog.h" + +GNet::Request::Request( bool host ) : + m_magic(magic) , + m_host(host) , + m_handle(0) , + m_error(0) , + m_done(false) , + m_numeric(false) , + m_address(Address::invalidAddress()) +{ +} + +const char * GNet::Request::reason( bool host_error , int error ) +{ + switch( error ) + { + case WSAHOST_NOT_FOUND: return "host not found" ; + case WSAENOBUFS: return "buffer overflow" ; + case WSATRY_AGAIN: return "resource error" ; + case WSANO_RECOVERY: return "general failure" ; + case WSANO_DATA: return host_error ? "no such host" : "no such service" ; + case WSANOTINITIALISED: return "not initialised" ; + case WSAEWOULDBLOCK: return "would block" ; + case WSAENETDOWN: return "network down" ; + case WSAEINPROGRESS: return "blocking operation in progress" ; + case WSAEINTR: return "interrupted" ; + } + return "undefined error" ; +} + +bool GNet::Request::valid() const +{ + return m_numeric || m_handle != 0 ; +} + +std::string GNet::Request::reason() const +{ + G_ASSERT( m_handle == 0 ) ; + G_DEBUG( "GNet::Request::reason: \"" << reason(m_host,m_error) << "\"" ) ; + return reason(m_host,m_error) ; +} + +GNet::Request::~Request() +{ + if( m_handle ) + ::WSACancelAsyncRequest( m_handle ) ; + + m_magic = 0 ; +} + +bool GNet::Request::onMessage( WPARAM wparam , LPARAM lparam ) +{ + if( m_numeric ) + { + G_ASSERT( wparam == 0 ) ; + G_ASSERT( lparam == 0 ) ; + G_ASSERT( m_handle == 0 ) ; + G_ASSERT( !m_done ) ; + G_ASSERT( m_error == 0 ) ; + } + else + { + WORD error = WSAGETASYNCERROR( lparam ) ; + HANDLE handle = (HANDLE)wparam ; + + G_DEBUG( "GNet::Request::onMessage: handle = " << handle << " , error = " << error ) ; + G_DEBUG( "GNet::Request::onMessage: m_handle = " << m_handle ) ; + + G_ASSERT( m_magic == magic ) ; + G_ASSERT( handle == m_handle ) ; + G_ASSERT( m_handle != 0 ) ; + G_ASSERT( !m_done ) ; + + m_error = (int)error ; + } + + m_done = true ; + m_handle = 0 ; + + return m_error == 0 ; +} + +// HOST... + +GNet::HostRequest::HostRequest( std::string host_name , HWND hwnd , unsigned msg ) : + Request(true) +{ + if( numeric(host_name,m_address) ) + { + m_numeric = true ; + ::PostMessage( hwnd , msg , 0 , 0 ) ; + return ; + } + + m_handle = ::WSAAsyncGetHostByName( hwnd , msg , + host_name.c_str() , m_buffer , sizeof(m_buffer) ) ; + + if( m_handle == 0 ) + m_error = ::WSAGetLastError() ; + + G_DEBUG( "GNet::HostRequest::ctor: host \"" << host_name << "\", " + << "handle " << m_handle ) ; +} + +bool GNet::HostRequest::numeric( std::string string , Address & address ) +{ + string.append( ":0" ) ; + bool rc = Address::validString(string) ; + if( rc ) + { + G_DEBUG( "GNet::HostRequest::numeric: host part of \"" << string << "\" is already numeric" ) ; + m_address = Address(string) ; + } + + return rc ; +} + +GNet::Address GNet::HostRequest::result() const +{ + G_ASSERT( m_done && m_handle == 0 ) ; + if( m_numeric ) + { + return m_address ; + } + else + { + const hostent *h = reinterpret_cast(m_buffer) ; + G_ASSERT( h != NULL ) ; + return Address( *h , 0U ) ; + } +} + +// SERVICE... + +GNet::ServiceRequest::ServiceRequest( std::string service_name , bool udp , HWND hwnd , unsigned msg ) : + Request(false) +{ + if( numeric(service_name,m_address) ) + { + m_numeric = true ; + ::PostMessage( hwnd , msg , 0 , 0 ) ; + return ; + } + + m_handle = ::WSAAsyncGetServByName( hwnd , msg , + service_name.c_str() , protocol(udp) , + m_buffer , sizeof(m_buffer) ) ; + + if( m_handle == 0 ) + m_error = ::WSAGetLastError() ; + + G_DEBUG( "GNet::ServiceRequest::ctor: service \"" + << service_name << "\", handle " << m_handle ) ; +} + +bool GNet::ServiceRequest::numeric( std::string string , Address & address ) +{ + string = std::string("0.0.0.0:") + string ; + bool rc = Address::validString(string) ; + if( rc ) + { + G_DEBUG( "GNet::ServiceRequest::numeric: service part of \"" << string << "\" is already numeric" ) ; + m_address = Address(string) ; + } + + return rc ; +} + +const char * GNet::ServiceRequest::protocol( bool udp ) +{ + return udp ? "udp" : "tcp" ; +} + +GNet::Address GNet::ServiceRequest::result() const +{ + G_ASSERT( m_done && m_handle == 0 ) ; + if( m_numeric ) + { + return m_address ; + } + else + { + const servent *s = reinterpret_cast(m_buffer) ; + G_ASSERT( s != NULL ) ; + return Address( *s ) ; + } +} + diff --git a/src/gnet/grequest.h b/src/gnet/grequest.h new file mode 100644 index 0000000..64e92b7 --- /dev/null +++ b/src/gnet/grequest.h @@ -0,0 +1,116 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// grequest.h +// + +#ifndef G_REQUEST_H +#define G_REQUEST_H + +#include "gdef.h" +#include "gnet.h" +#include "gaddress.h" +#include "gwindow.h" + +namespace GNet +{ + class Request ; + class HostRequest ; + class ServiceRequest ; +} ; + +// Class: GNet::Request +// Description: A base class for making +// asynchronous DNS requests. +// +class GNet::Request +{ +protected: + enum { magic = 968 } ; + int m_magic ; + int m_error ; + HANDLE m_handle ; + char m_buffer[MAXGETHOSTSTRUCT] ; + bool m_host ; + bool m_done ; + bool m_numeric ; + GNet::Address m_address ; + +protected: + explicit Request( bool host ) ; + // Constructor. Derived class constructors + // should issue the appropriate request. + +public: + virtual ~Request() ; + // Virtual destructor. Cancels any + // outstanding request. + + bool valid() const ; + // Returns true if the constructor + // initiated a request properly. + + std::string reason() const ; + // Returns the failure reason if + // valid() or onMessage() returned + // false. + + bool onMessage( WPARAM wparam , LPARAM lparam ) ; + // To be called when the request has + // been completed. Returns false + // on error. + +private: + Request( const Request & ) ; + void operator=( const Request & ) ; + static const char *reason( bool host , int error ) ; +} ; + +// Class: GNet::HostRequest +// Description: A derivation of GNet::Request used for hostname lookup requests. +// +class GNet::HostRequest : public Request +{ +public: + HostRequest( std::string host_name , HWND hwnd , unsigned msg ) ; + Address result() const ; // zero port + +private: + bool numeric( std::string s , Address & address ) ; + HostRequest( const HostRequest & ) ; + void operator=( const HostRequest & ) ; +} ; + +// Class: GNet::ServiceRequest +// Description: A derivation of GNet::Request used for service (port) lookup requests. +// +class GNet::ServiceRequest : public Request +{ +public: + ServiceRequest( std::string service_name , bool udp , HWND hwnd , unsigned msg ) ; + Address result() const ; // zero host address + +private: + static const char * protocol( bool udp ) ; + bool numeric( std::string s , Address & address ) ; +} ; + +#endif + diff --git a/src/gnet/gresolve.cpp b/src/gnet/gresolve.cpp new file mode 100644 index 0000000..68b6f40 --- /dev/null +++ b/src/gnet/gresolve.cpp @@ -0,0 +1,102 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gresolve.cpp +// + +#include "gdef.h" +#include "gresolve.h" +#include "gexception.h" +#include "gsocket.h" +#include "gevent.h" +#include "gstr.h" +#include "gdebug.h" +#include "glog.h" + +//static +unsigned int GNet::Resolver::resolveService( const std::string & service_name , bool udp , std::string & error ) +{ + if( service_name.length() != 0U && G::Str::isNumeric(service_name) ) + { + if( ! G::Str::isUInt(service_name) ) + { + error = "silly port number" ; + return 0U ; + } + + unsigned int port = G::Str::toUInt(service_name) ; + if( ! Address::validPort(port) ) + { + error = "invalid port number" ; + return 0U ; + } + + return port ; + } + else + { + servent * service = ::getservbyname( service_name.c_str() , udp ? "udp" : "tcp" ) ; + if( service == NULL ) + { + error = "invalid service name" ; + return 0U ; + } + Address service_address( *service ) ; + return service_address.port() ; + } +} + +//static +GNet::Resolver::HostInfoPair GNet::Resolver::resolve( const std::string & host_name , + const std::string & service_name , bool udp ) +{ + HostInfo host_info ; + const bool valid_host = resolveHost( host_name.c_str() , host_info ) ; + if( !valid_host ) + { + G_DEBUG( "GNet::Resolver::resolve: host error: \"" << host_name << "\"" ) ; + return HostInfoPair( HostInfo() , "invalid hostname" ) ; + } + + std::string error ; + unsigned int port = resolveService( service_name , udp , error ) ; + if( ! error.empty() ) + { + G_DEBUG( "GNet::Resolver::resolve: service error: \"" << service_name << "\": " << error ) ; + return HostInfoPair( HostInfo() , error ) ; + } + + host_info.address.setPort( port ) ; + + G_DEBUG( "GNet::Resolver::resolve: \"" << host_name << "\" + " + << "\"" << service_name << "\" -> " + << "\"" << host_info.address.displayString() << "\" " + << "(" << host_info.canonical_name << ")" ) ; + + return HostInfoPair( host_info , std::string() ) ; +} + +// === + +GNet::Resolver::HostInfo::HostInfo() : + address( Address::invalidAddress() ) +{ +} + diff --git a/src/gnet/gresolve.h b/src/gnet/gresolve.h new file mode 100644 index 0000000..06681ca --- /dev/null +++ b/src/gnet/gresolve.h @@ -0,0 +1,122 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gresolver.h +// + +#ifndef G_RESOLVER_H +#define G_RESOLVER_H + +#include "gdef.h" +#include "gnet.h" +#include "gaddress.h" + +namespace GNet +{ + class Resolver ; + class ResolverImp ; +} ; + +// Class: GNet::Resolver +// Description: A class for asynchronous TCP name-to-address +// resolution. (The motivation for a fully asynchronous +// interface is so that GUIs and single-threaded servers +// are not blocked during DNS lookup. However, simple +// clients, especially those without a GUI, can reasonably +// use synchronous lookup.) +// +class GNet::Resolver +{ +public: + Resolver(); + // Default constructor. + // Postcondition: state == idle + + virtual ~Resolver() ; + // Virtual destructor. + + bool resolveReq( std::string name , bool udp = false ) ; + // Initiates a name-to-address resolution. Returns + // false if a confirmation will not be generated. + // + // The name should be in the format: + // + // {|}:{|} + // host-address := ... + // + // (Clearly if either part is in its numeric format then the + // resolver has no name-to-address conversion work to do + // for that part.) + // + // Postcondition: state == resolving (returns true) + // Postcondition: state == idle (returns false) + + bool resolveReq( std::string host_name, std::string service_name , bool udp = false ) ; + // Alternative form of ResolveReq(std::string,bool) with + // separate hostname and service name parameters. + // A zero-length host_name defaults to "0.0.0.0". A + // zero-length service name defaults to "0". + + virtual void resolveCon( bool success, const Address &address, + std::string failure_reason ) ; + // Called when the resolution process is complete. + // This function is never called from within + // resolveReq(). + // + // Precondition: state == resolving + // Postcondition: state == idle + + bool busy() const ; + // Returns true if there is a pending resolve request. + // + // Postcondition: state == resolving <= returns true + // Postcondition: state == idle <= returns false + + void cancelReq() ; + // Cancels an outstanding resolve request. + // + // Precondition: state == resolving + // Postcondition: state == idle + + struct HostInfo // Holds the results of a name resolution (address + full-name). + { + Address address ; + std::string canonical_name ; + HostInfo() ; + } ; + + typedef std::pair HostInfoPair ; + + static HostInfoPair resolve( const std::string & host_name , const std::string & service_name , bool udp = false ) ; + // Does syncronous name resolution. The returned + // error string ('pair.second') is zero length + // on success. Not implemented on all platforms. + +private: + void operator=( const Resolver & ) ; + Resolver( const Resolver & ) ; + static bool resolveHost( const std::string & host_name , HostInfo & ) ; + static unsigned int resolveService( const std::string & host_name , bool , std::string & ) ; + +private: + ResolverImp *m_imp ; +} ; + +#endif diff --git a/src/gnet/gresolve_ipv4.cpp b/src/gnet/gresolve_ipv4.cpp new file mode 100644 index 0000000..105549a --- /dev/null +++ b/src/gnet/gresolve_ipv4.cpp @@ -0,0 +1,38 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gresolve_ipv4.cpp +// + +#include "gdef.h" +#include "gresolve.h" + +//static +bool GNet::Resolver::resolveHost( const std::string & host_name , HostInfo & host_info ) +{ + hostent * host = ::gethostbyname( host_name.c_str() ) ; + if( host != NULL ) + { + host_info.canonical_name = std::string(host->h_name) ; + host_info.address = Address( *host , 0U ) ; + } + return host != NULL ; +} + diff --git a/src/gnet/gresolve_unix.cpp b/src/gnet/gresolve_unix.cpp new file mode 100644 index 0000000..f29a778 --- /dev/null +++ b/src/gnet/gresolve_unix.cpp @@ -0,0 +1,224 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gresolve_unix.cpp +// + +#include "gdef.h" +#include "gresolve.h" +#include "gexception.h" +#include "gsocket.h" +#include "gevent.h" +#include "gstr.h" +#include "gdebug.h" +#include "glog.h" + +// Class: GNet::ResolverImp +// Description: A pimple-pattern implementation class for GNet::Resolver. +// +class GNet::ResolverImp : public EventHandler +{ +public: + ResolverImp( Resolver & resolver , unsigned int port ) ; + virtual ~ResolverImp() ; + bool resolveReq( std::string host_part, std::string service_part , bool udp ) ; + void cancelReq() ; + bool busy() const ; + +private: + void operator=( const ResolverImp & ) ; + ResolverImp( const ResolverImp & ) ; + void end() ; + void readEvent() ; + void writeEvent() ; + +private: + Address m_address ; + Resolver & m_outer ; + StreamSocket * m_s ; + std::string m_request ; +} ; + +// === + +GNet::ResolverImp::ResolverImp( Resolver & resolver , unsigned int port ) : + m_s(NULL) , + m_outer(resolver) , + m_address(Address::localhost(port)) +{ +} + +GNet::ResolverImp::~ResolverImp() +{ + delete m_s ; +} + +bool GNet::ResolverImp::resolveReq( std::string host_part, std::string service_part , bool udp ) +{ + if( m_s != NULL ) + return false ; // still busy + + m_request = + host_part + std::string(":") + + service_part + std::string(":") + + ( udp ? std::string("udp") : std::string("tcp") ) + + std::string("\n") ; + + m_s = new StreamSocket ; + if( ! m_s->valid() || ! m_s->bind() || ! m_s->connect(m_address) ) + { + delete m_s ; + m_s = NULL ; + return false ; + } + else + { + m_s->addWriteHandler( *this ) ; + return true ; + } +} + +void GNet::ResolverImp::writeEvent() +{ + G_ASSERT( m_s != NULL ) ; + + std::pair peer_pair = m_s->getPeerAddress() ; + if( peer_pair.first ) + { + m_s->addReadHandler( *this ) ; + m_s->dropWriteHandler() ; + m_s->write( m_request.c_str() , m_request.length() ) ; + } + else + { + end() ; + m_outer.resolveCon( false , Address::invalidAddress() , + std::string("cannot connect to the resolver daemon at ") + m_address.displayString() ) ; + } +} + +void GNet::ResolverImp::readEvent() +{ + G_ASSERT( m_s != NULL ) ; + + static char buffer[200U] ; + ssize_t rc = m_s->read( buffer , sizeof(buffer) ) ; + G_DEBUG( "GNet::ResolverImp::readEvent: " << rc << " byte(s)" ) ; + + end() ; + if( rc == 0 ) + { + m_outer.resolveCon( false , Address::invalidAddress() , "disconnected" ) ; + } + else + { + std::string result( buffer , rc ) ; + G_DEBUG( "GNet::ResolverImp::readEvent: \"" << result << "\"" ) ; + G::Str::trimRight( result , " \n" ) ; + if( Address::validString(result) ) + { + m_outer.resolveCon( true , Address(result) , "" ) ; + } + else + { + std::string reason = result ; + reason = G::Str::isPrintableAscii( reason ) ? reason : std::string("dns error") ; + m_outer.resolveCon( false , Address::invalidAddress() , reason ) ; + } + } +} + +void GNet::ResolverImp::cancelReq() +{ + end() ; +} + +void GNet::ResolverImp::end() +{ + delete m_s ; + m_s = NULL ; +} + +bool GNet::ResolverImp::busy() const +{ + return m_s != NULL ; +} + +// === + +GNet::Resolver::Resolver() : + m_imp(NULL) +{ + const unsigned int port = 208U ; + m_imp = new ResolverImp( *this , port ) ; +} + +GNet::Resolver::~Resolver() +{ + delete m_imp ; +} + +bool GNet::Resolver::resolveReq( std::string name , bool udp ) +{ + if( m_imp == NULL ) + return false ; + + size_t colon = name.find( ":" ) ; + if( colon == std::string::npos ) + { + return false ; + } + + std::string host_part = name.substr( 0U , colon ) ; + std::string service_part = name.substr( colon+1U ) ; + + return m_imp->resolveReq( host_part , service_part , udp ) ; +} + +bool GNet::Resolver::resolveReq( std::string host_part, std::string service_part , bool udp ) +{ + if( m_imp == NULL ) + return false ; + + if( host_part.length() == 0 ) + host_part = "0.0.0.0" ; + + if( service_part.length() == 0 ) + service_part = "0" ; + + return m_imp->resolveReq( host_part , service_part , udp ) ; +} + +void GNet::Resolver::resolveCon( bool , const Address & , std::string ) +{ + // no-op +} + +bool GNet::Resolver::busy() const +{ + return m_imp->busy() ; +} + +void GNet::Resolver::cancelReq() +{ + m_imp->cancelReq() ; +} + + diff --git a/src/gnet/gresolve_win32.cpp b/src/gnet/gresolve_win32.cpp new file mode 100644 index 0000000..328c92e --- /dev/null +++ b/src/gnet/gresolve_win32.cpp @@ -0,0 +1,257 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gresolve_win32.cpp +// + +#include "gdef.h" +#include "gresolve.h" +#include "gwinhid.h" +#include "gappinst.h" +#include "grequest.h" +#include "gdebug.h" +#include "gassert.h" +#include "glog.h" + +// Class: GNet::ResolverImp +// Description: A pimple-pattern implementation class for GNet::Resolver. +// +class GNet::ResolverImp : public GGui::WindowHidden +{ +private: + Resolver & m_if ; + HostRequest * m_host_request ; + ServiceRequest * m_service_request ; + + std::string m_host ; + std::string m_service ; + bool m_udp ; + + Address m_result ; + +public: + explicit ResolverImp( Resolver & resolver ) ; + bool valid() ; + virtual ~ResolverImp() ; + bool resolveReq( std::string host_part, std::string service_part , bool udp ) ; + void cancelReq() ; + bool busy() const ; + +private: + void operator=( const ResolverImp & ) ; + ResolverImp( const ResolverImp & ) ; + const char *errorString( bool host_error , int error ) ; + void cleanup() ; + void saveHost( const Address &address ) ; + void saveService( const Address &address ) ; + +protected: + LRESULT onUser( WPARAM wparam , LPARAM lparam ) ; +} ; + +// === + +GNet::ResolverImp::ResolverImp( Resolver & resolver ) : + GGui::WindowHidden( GGui::ApplicationInstance::hinstance() ) , + m_result(Address::invalidAddress()) , + m_if(resolver) , + m_host_request(NULL) , + m_service_request(NULL) +{ +} + +GNet::ResolverImp::~ResolverImp() +{ + cleanup() ; +} + +bool GNet::ResolverImp::valid() +{ + return handle() != 0 ; +} + +bool GNet::ResolverImp::resolveReq( std::string host_part, std::string service_part , bool udp ) +{ + G_ASSERT( !busy() ) ; + if( busy() ) + return false ; + + m_host = host_part ; + m_service = service_part ; + m_udp = udp ; + + m_host_request = new HostRequest( host_part , handle() , WM_USER ) ; + if( !m_host_request->valid() ) + { + std::string reason = m_host_request->reason() ; // not used + cleanup() ; + return false ; + } + + return true ; +} + +void GNet::ResolverImp::cancelReq() +{ + cleanup() ; +} + +void GNet::ResolverImp::cleanup() +{ + delete m_host_request ; + m_host_request = NULL ; + + delete m_service_request ; + m_service_request = NULL ; +} + +bool GNet::ResolverImp::busy() const +{ + return + m_service_request != NULL || + m_host_request != NULL ; +} + +void GNet::ResolverImp::saveHost( const Address & address ) +{ + m_result = address ; +} + +void GNet::ResolverImp::saveService( const Address & address ) +{ + G_DEBUG( "GNet::ResolveImp::saveService: " << address.displayString() ) ; + m_result.setPort( address.port() ) ; +} + +LRESULT GNet::ResolverImp::onUser( WPARAM wparam , LPARAM lparam ) +{ + G_DEBUG( "GNet::ResolverImp::onUser: wparam = " << wparam << ", lparam = " << lparam ) ; + + if( m_host_request != NULL ) + { + if( m_host_request->onMessage( wparam , lparam ) ) + { + saveHost( m_host_request->result() ) ; + cleanup() ; + + m_service_request = new ServiceRequest( m_service , m_udp , handle() , WM_USER ) ; + if( !m_service_request->valid() ) + { + std::string reason = m_service_request->reason() ; + cleanup() ; + m_if.resolveCon( false, Address::invalidAddress(), reason ) ; + } + } + else + { + std::string reason = m_host_request->reason() ; + cleanup() ; + m_if.resolveCon( false , Address::invalidAddress() , reason ) ; + } + } + else if( m_service_request != NULL ) + { + if( m_service_request->onMessage( wparam , lparam ) ) + { + saveService( m_service_request->result() ) ; + Address address( m_result ) ; + cleanup() ; + m_if.resolveCon( true , address , std::string() ) ; // success + } + else + { + std::string reason = m_service_request->reason() ; + cleanup() ; + m_if.resolveCon( false , Address::invalidAddress() , reason ) ; + } + } + else + { + ; + } + + return 0 ; +} + +// === + +GNet::Resolver::Resolver() : + m_imp(0) +{ + m_imp = new ResolverImp( *this ) ; + if( !m_imp->valid() ) + { + delete m_imp ; + m_imp = 0 ; + } +} + +GNet::Resolver::~Resolver() +{ + delete m_imp ; +} + +bool GNet::Resolver::resolveReq( std::string name , bool udp ) +{ + if( m_imp == NULL ) + return false ; + + size_t colon = name.find( ":" ) ; + if( colon == std::string::npos ) + { + return false ; + } + + std::string host_part = name.substr( 0U , colon ) ; + std::string service_part = name.substr( colon+1U ) ; + + return m_imp->resolveReq( host_part , service_part , udp ) ; +} + +bool GNet::Resolver::resolveReq( std::string host_part, std::string service_part , bool udp ) +{ + if( m_imp == NULL ) + return false ; + + if( host_part.length() == 0 ) + host_part = "0.0.0.0" ; + + if( service_part.length() == 0 ) + service_part = "0" ; + + return m_imp->resolveReq( host_part , service_part , udp ) ; +} + +void GNet::Resolver::resolveCon( bool , const Address &, std::string ) +{ + // no-op +} + +bool GNet::Resolver::busy() const +{ + return m_imp != NULL ? m_imp->busy() : true ; +} + +void GNet::Resolver::cancelReq() +{ + if( m_imp != NULL ) + m_imp->cancelReq() ; +} + diff --git a/src/gnet/gselect.cpp b/src/gnet/gselect.cpp new file mode 100644 index 0000000..6432dae --- /dev/null +++ b/src/gnet/gselect.cpp @@ -0,0 +1,203 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gselect.cpp +// + +#include "gdef.h" +#include "gselect.h" +#include "gdebug.h" +#include +#include + +namespace GNet +{ + class Lock ; + class FdSet ; +} ; + +// Class: GNet::Lock +// Description: A private implementation class used by GNet::Select to +// lock data structures in the face of reentrancy. +// +class GNet::Lock +{ +public: + EventHandlerList & m_list ; + explicit Lock( EventHandlerList & list ) ; + ~Lock() ; +private: + Lock( const Lock & ) ; + void operator=( const Lock & ) ; +} ; + +// Class: GNet::FdSet +// Description: A static implementation interface used by GNet::Select +// to do fd_set iteration. +// +class GNet::FdSet +{ +public: + static int init( int n , fd_set * set , const EventHandlerList & list ) ; + static void raiseEvents( fd_set * set , EventHandlerList & list , + void (EventHandler::*method)() , const char * type ) ; +private: + FdSet() ; +} ; + +// === + +inline +GNet::Lock::Lock( EventHandlerList & list ) : + m_list(list) +{ + m_list.lock() ; +} + +inline +GNet::Lock::~Lock() +{ + m_list.unlock() ; +} + +// === + +//static +int GNet::FdSet::init( int n , fd_set * set , const EventHandlerList & list ) +{ + FD_ZERO( set ) ; + for( EventHandlerList::Iterator p = list.begin() ; p != list.end() ; ++p ) + { + Descriptor fd = EventHandlerList::fd( p ) ; + FD_SET( fd , set ) ; + if( (fd+1) > n ) + n = (fd+1) ; + } + return n ; +} + +//static +void GNet::FdSet::raiseEvents( fd_set * set , EventHandlerList & list , + void (EventHandler::*method)() , const char * type ) +{ + GNet::Lock lock( list ) ; + for( EventHandlerList::Iterator p = list.begin() ; p != list.end() ; ++p ) + { + Descriptor fd = EventHandlerList::fd( p ) ; + if( FD_ISSET( fd , set ) ) + { + G_DEBUG( "raiseEvents: " << type << " event on fd " << fd ) ; + EventHandler & h = EventHandlerList::handler( p ) ; + (h.*method)() ; + } + } +} + +// === + +GNet::Select::Select() : + m_read_list(std::string("read")) , + m_write_list(std::string("write")) , + m_exception_list(std::string("exception")) , + m_quit(false) +{ +} + +GNet::Select::~Select() +{ +} + +bool GNet::Select::init() +{ + return true ; +} + +void GNet::Select::run() +{ + while( !m_quit ) + { + runOnce() ; + } + m_quit = false ; +} + +void GNet::Select::quit() +{ + m_quit = true ; +} + +void GNet::Select::runOnce() +{ + int n = 1 ; + fd_set r ; n = FdSet::init( n , &r , m_read_list ) ; + fd_set w ; n = FdSet::init( n , &w , m_write_list ) ; + fd_set e ; n = FdSet::init( n , &e , m_exception_list ) ; + + struct timeval * const infinite = NULL ; + + G_DEBUG( "GNet::Select::runOnce: selecting: fd(max) = " << (n-1) << ": " + << "read-list=\"" << m_read_list.asString() << "\": " + << "write-list=\"" << m_write_list.asString() << "\": " + << "exception-list=\"" << m_exception_list.asString() << "\"" ) ; + + int rc = ::select( n , &r , &w , &e , infinite ) ; + if( rc > 0 ) + { + G_DEBUG( "GNet::Select::runOnce: detected event(s) on " << rc << " fd(s)" ) ; + FdSet::raiseEvents( &r , m_read_list , & EventHandler::readEvent , "read" ) ; + FdSet::raiseEvents( &w , m_write_list , & EventHandler::writeEvent , "write" ) ; + FdSet::raiseEvents( &e , m_exception_list , & EventHandler::exceptionEvent , "exception" ) ; + } + else if( rc < 0 ) + { + throw Error() ; + } +} + +void GNet::Select::addRead( Descriptor fd , EventHandler & handler ) +{ + m_read_list.add( fd , & handler ) ; +} + +void GNet::Select::addWrite( Descriptor fd , EventHandler & handler ) +{ + m_write_list.add( fd , & handler ) ; +} + +void GNet::Select::addException( Descriptor fd , EventHandler & handler ) +{ + m_exception_list.add( fd , & handler ) ; +} + +void GNet::Select::dropRead( Descriptor fd ) +{ + m_read_list.remove( fd ) ; +} + +void GNet::Select::dropWrite( Descriptor fd ) +{ + m_write_list.remove( fd ) ; +} + +void GNet::Select::dropException( Descriptor fd ) +{ + m_exception_list.remove( fd ) ; +} + diff --git a/src/gnet/gselect.h b/src/gnet/gselect.h new file mode 100644 index 0000000..5b732e6 --- /dev/null +++ b/src/gnet/gselect.h @@ -0,0 +1,90 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gselect.h +// + +#ifndef G_SELECT_H +#define G_SELECT_H + +#include "gdef.h" +#include "gnet.h" +#include "gevent.h" +#include "gexception.h" + +namespace GNet +{ + class Select ; +} ; + +// Class: GNet::Select +// Description: A event-source class which uses ::select(). +// +class GNet::Select : public EventSources +{ +public: + G_EXCEPTION( Error , "select() error" ) ; + + Select() ; + // Constructor. + + virtual ~Select() ; + // Destructor. + + virtual bool init() ; + // Override from EventSources. Does nothing. Returns true. + + virtual void run() ; + // Override from EventSources. Runs the event loop. + + virtual void quit() ; + // Override from EventSources. Causes run() to return. + + virtual void addRead( Descriptor fd , EventHandler &handler ) ; + // See EventSources. + + virtual void addWrite( Descriptor fd , EventHandler &handler ) ; + // See EventSources. + + virtual void addException( Descriptor fd , EventHandler &handler ) ; + // See EventSources. + + virtual void dropRead( Descriptor fd ) ; + // See EventSources. + + virtual void dropWrite( Descriptor fd ) ; + // See EventSources. + + virtual void dropException( Descriptor fd ) ; + // See EventSources. + +private: + Select( const Select & ) ; + void operator=( const Select & ) ; + void runOnce() ; + +private: + bool m_quit ; + EventHandlerList m_read_list ; + EventHandlerList m_write_list ; + EventHandlerList m_exception_list ; +} ; + +#endif diff --git a/src/gnet/gserver.cpp b/src/gnet/gserver.cpp new file mode 100644 index 0000000..4a50c20 --- /dev/null +++ b/src/gnet/gserver.cpp @@ -0,0 +1,154 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gserver.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gserver.h" +#include "gdebug.h" +#include "gassert.h" + +GNet::ServerPeer::ServerPeer( StreamSocket * s , Address a ) : + m_socket(s) , + m_address(a) , + m_ref_count(1U) +{ + G_ASSERT( m_socket != NULL ) ; + G_DEBUG( "GNet::ServerPeer::ctor: fd " << m_socket->asString() << ": " << m_address.displayString() ) ; + m_socket->addReadHandler( *this ) ; +} + +GNet::ServerPeer::~ServerPeer() +{ + G_DEBUG( "GNet::ServerPeer::dtor: fd " << m_socket->asString() ) ; + m_socket->dropReadHandler() ; + delete m_socket ; + m_socket = NULL ; +} + +std::string GNet::ServerPeer::asString() const +{ + return m_socket->asString() ; // ie. the fd +} + +GNet::StreamSocket & GNet::ServerPeer::socket() +{ + G_ASSERT( m_socket != NULL ) ; + return *m_socket ; +} + +void GNet::ServerPeer::readEvent() +{ + G_DEBUG( "GNet::ServerPeer::readEvent: " << (void*)this ) ; + + char buffer[500] ; + buffer[0] = '\0' ; + size_t buffer_size = sizeof(buffer) ; + ssize_t rc = m_socket->read( buffer , buffer_size ) ; + + if( rc <= 0 ) + { + doDelete() ; + } + else + { + size_t n = rc ; + onData( buffer , n ) ; + } +} + +void GNet::ServerPeer::doDelete() +{ + onDelete() ; + delete this ; +} + +void GNet::ServerPeer::up() +{ + m_ref_count++ ; +} + +bool GNet::ServerPeer::down() +{ + m_ref_count-- ; + return m_ref_count == 0U ; +} + +// === + +GNet::Server::Server( unsigned int listening_port ) +{ + init( listening_port ) ; +} + +GNet::Server::Server() : + m_socket(NULL) +{ +} + +void GNet::Server::init( unsigned int listening_port ) +{ + m_socket = new StreamSocket ; + G_DEBUG( "GNet::Server::init: " << (void*)this << ": listening on port " << listening_port ) ; + Address local_address( listening_port ) ; + if( ! m_socket->bind( local_address ) ) + throw CannotBind( local_address.displayString() ) ; + if( ! m_socket->listen() ) + throw CannotListen() ; + m_socket->addReadHandler( *this ) ; +} + +GNet::Server::~Server() +{ + delete m_socket ; +} + +void GNet::Server::readEvent() +{ + // read-event-on-listening-port => new connection to accept + + G_DEBUG( "GNet::Server::readEvent: " << (void*)this ) ; + G_ASSERT( m_socket != NULL ) ; + AcceptPair pair = m_socket->accept() ; + if( pair.first.get() == NULL ) + { + G_WARNING( "GNet::Server::readEvent: accept error" ) ; + } + else + { + G_DEBUG( "GNet::Server::readEvent: new connection from " << pair.second.displayString() ) ; + ServerPeer * peer = newPeer(pair.first.release(),pair.second) ; + if( peer != NULL ) + G_DEBUG( "GNet::Server::readEvent: new connection accepted onto fd " << peer->asString() ) ; + } +} + +void GNet::Server::writeEvent() +{ + G_DEBUG( "GNet::Server::writeEvent" ) ; +} + +void GNet::Server::exceptionEvent() +{ + G_DEBUG( "GNet::Server::exceptionEvent" ) ; +} + diff --git a/src/gnet/gserver.h b/src/gnet/gserver.h new file mode 100644 index 0000000..32c7164 --- /dev/null +++ b/src/gnet/gserver.h @@ -0,0 +1,160 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gserver.h +// + +#ifndef G_SERVER_H +#define G_SERVER_H + +#include "gdef.h" +#include "gnet.h" +#include "gsocket.h" +#include "gselect.h" +#include "gevent.h" +#include +#include + +namespace GNet +{ + class Server ; + class ServerPeer ; +} ; + +// Class: GNet::Server +// Description: An application-level class for implementing a +// simple TCP server. The user must instantiate a separate +// event-source object (such as GNet::Select) for event handling. +// See also: GNet::ServerPeer +// +class GNet::Server : public EventHandler +{ +public: + G_EXCEPTION( CannotBind , "cannot bind the listening port" ) ; + G_EXCEPTION( CannotListen , "cannot listen" ) ; + + explicit Server( unsigned int listening_port ) ; + // Constructor. Throws exceptions on + // error. + + Server() ; + // Default constructor. Initialise with init(). + + void init( unsigned int listening_port ) ; + // Iniailisation after default construction. + + virtual ~Server() ; + // Destructor. + +protected: + virtual ServerPeer * newPeer( StreamSocket * socket , + Address peer_address ) = 0 ; + // A factory method which new()s a ServerPeer-derived + // object. This method is called when a new connection + // comes into this server. The new ServerPeer object + // is used to represent the state of the client/server + // connection. + // + // The 'socket' parameter points to a socket + // object on the heap. Ownership is transferred. + // + // The implementation shoud pass the 'socket' and + // 'peer_address' parameters through to the ServerPeer + // base-class constructor. + // + // May return NULL. + +private: + Server( const Server& ) ; + void operator=( const Server& ) ; + virtual void readEvent() ; // see EventHandler + virtual void writeEvent() ; // see EventHandler + virtual void exceptionEvent() ; // see EventHandler + +private: + StreamSocket * m_socket ; +} ; + +// Class: GNet::ServerPeer +// Description: An abstract base class for the GNet::Server's +// connection to a remote client. Instances are created +// on the heap by the Server::newPeer() override, and they +// delete themselves when the connection is lost. +// See also: GNet::Server, GNet::EventHandler +// +class GNet::ServerPeer : public EventHandler +{ +public: + ServerPeer( StreamSocket * , Address ) ; + // Constructor. This constructor is + // only used from within the + // override of GServer::newPeer(). + + void up() ; + // Increases this object's internal reference + // count. See also down(). Reference counting + // is provided for the convenience of + // reference-counting wrappers, but is + // not used by this library. + + bool down() ; + // Decreases this object's internal reference + // count. Returns true if the reference + // count is now zero. + // Usage: if(down()) doDelete() + + void doDelete() ; + // Does "onDelete(); delete this". + + std::string asString() const ; + // Returns a string representation of the + // socket descriptor. Typically used in + // log message to destinguish one connection + // from another. + +protected: + virtual ~ServerPeer() ; + // Destructor. Note that objects will delete + // themselves when they detect that the + // connection has been lost -- see doDelete(). + + virtual void onDelete() = 0 ; + // Called just before destruction. (Note + // that the object typically deletes itself.) + + virtual void onData( const char * , size_t ) = 0 ; + // Called on receipt of data. + + StreamSocket & socket() ; + // Returns a reference to the client-server + // connection socket. + +private: + void readEvent() ; + ServerPeer( const ServerPeer & ) ; + void operator=( const ServerPeer & ) ; + +private: + unsigned int m_ref_count ; + Address m_address ; + StreamSocket * m_socket ; +} ; + +#endif diff --git a/src/gnet/gsocket.cpp b/src/gnet/gsocket.cpp new file mode 100644 index 0000000..bb62d51 --- /dev/null +++ b/src/gnet/gsocket.cpp @@ -0,0 +1,419 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsocket.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gassert.h" +#include "gsocket.h" +#include "gmemory.h" +#include "gdebug.h" + +GNet::Socket::Socket( int domain, int type, int protocol ) : + m_reason( 0 ) +{ + (void)open( domain , type , protocol ) ; +} + +bool GNet::Socket::open( int domain, int type, int protocol ) +{ + m_socket = ::socket( domain, type, protocol ) ; + if( !valid() ) + { + m_reason = reason() ; + G_DEBUG( "GNet::Socket::open: cannot open a socket (" << m_reason << ")" ) ; + return false ; + } + + if( !setNonBlock() ) + { + m_reason = reason() ; + doClose() ; + G_ASSERT( !valid() ) ; + G_DEBUG( "GNet::Socket::open: cannot make socket non-blocking (" << m_reason << ")" ) ; + return false ; + } + + return true ; +} + +GNet::Socket::Socket( Descriptor s ) : + m_socket(s) , + m_reason( 0 ) +{ + ; +} + +GNet::Socket::~Socket() +{ + close() ; +} + +void GNet::Socket::drop() +{ + dropReadHandler() ; + dropWriteHandler() ; + dropExceptionHandler() ; +} + +void GNet::Socket::close() +{ + drop() ; + if( valid() ) + doClose() ; + + G_ASSERT( !valid() ) ; +} + +bool GNet::Socket::valid() const +{ + return valid( m_socket ) ; +} + +bool GNet::Socket::bind() +{ + Address address( 0U ) ; + return bind( address ) ; +} + +bool GNet::Socket::bind( const Address & local_address ) +{ + if( !valid() ) + return false ; + + // optionally allow immediate re-use -- may cause problems + setReuse() ; + + G_DEBUG( "Socket::bind: binding " << local_address.displayString() + << " on fd " << m_socket ) ; + + int rc = ::bind( m_socket, local_address.address() , local_address.length() ) ; + if( error(rc) ) + { + m_reason = reason() ; + return false ; + } + + const bool debug = true ; + if( debug ) + { + std::pair pair = getLocalAddress() ; + if( ! pair.first ) + { + G_WARNING( "GNet::Socket::bind: cannot get bound address" ) ; + return false ; + } + + G_DEBUG( "GNet::Socket::bind: bound " << pair.second.displayString() << " on fd " + << m_socket ) ; + } + + return true ; +} + +bool GNet::Socket::connect( const Address &addr , bool *done ) +{ + if( !valid() ) + return false; + + G_DEBUG( "GNet::Socket::connect: connecting to " << addr.displayString() ) ; + int rc = ::connect( m_socket, addr.address(), addr.length() ) ; + if( error(rc) ) + { + m_reason = reason() ; + if( eInProgress() ) + { + G_DEBUG( "GNet::Socket::connect: connection in progress" ) ; + if( done != NULL ) *done = false ; + return true ; + } + + G_DEBUG( "GNet::Socket::connect: synchronous connect failure: " << m_reason ) ; + return false; + } + + if( done != NULL ) *done = true ; + return true; +} + +ssize_t GNet::Socket::write( const char *buf, size_t len ) +{ + if( static_cast(len) < 0 ) + G_WARNING( "GNet::Socket::write: too big" ) ; // EMSGSIZE from ::send() ? + + ssize_t nsent = ::send( m_socket, buf, len, 0 ) ; + + if( sizeError(nsent) ) // if -1 + { + m_reason = reason() ; + G_DEBUG( "GNet::Socket::write: write error " << m_reason ) ; + return -1 ; + } + + if( nsent < len ) + m_reason = reason() ; + + G_DEBUG( "GNet::Socket::write: wrote " << nsent << "/" << len << " byte(s)" ) ; + return nsent; +} + +void GNet::Socket::setNoLinger() +{ + struct linger options ; + options.l_onoff = 0 ; + options.l_linger = 0 ; + socklen_t sizeof_options = sizeof(options) ; + (void)setsockopt( m_socket , SOL_SOCKET , SO_LINGER , + (char*)&options , sizeof_options ) ; +} + +void GNet::Socket::setKeepAlive() +{ + int keep_alive = 1 ; + (void)::setsockopt( m_socket , SOL_SOCKET , + SO_KEEPALIVE , (char*)&keep_alive , sizeof(keep_alive) ) ; +} + +void GNet::Socket::setReuse() +{ + int on = 1 ; + (void)::setsockopt( m_socket , SOL_SOCKET , + SO_REUSEADDR , (char*)&on , sizeof(on) ) ; +} + +bool GNet::Socket::listen( int backlog ) +{ + int rc = ::listen( m_socket, backlog ) ; + if( error(rc) ) + { + m_reason = reason() ; + return false ; + } + return true ; +} + +std::pair GNet::Socket::getAddress( bool local ) const +{ + std::pair error_pair( false , Address::invalidAddress() ) ; + + if( !valid() ) + return error_pair ; + + sockaddr saddr ; + socklen_t addr_length = sizeof(saddr) ; + int rc = + local ? + ::getsockname( m_socket , &saddr , &addr_length ) : + ::getpeername( m_socket , &saddr , &addr_length ) ; + + if( error(rc) ) + { + const_cast(this)->m_reason = reason() ; + return error_pair ; + } + + return std::pair( true , Address( &saddr, addr_length ) ) ; +} + +std::pair GNet::Socket::getLocalAddress() const +{ + return getAddress( true ) ; +} + +std::pair GNet::Socket::getPeerAddress() const +{ + return getAddress( false ) ; +} + +bool GNet::Socket::hasPeer() const +{ + return getPeerAddress().first ; +} + +void GNet::Socket::addReadHandler( EventHandler & handler ) +{ + G_ASSERT( valid() ) ; + G_DEBUG( "GNet::Socket::addReadHandler: fd " << m_socket ) ; + EventSources::instance().addRead( m_socket , handler ) ; +} + +void GNet::Socket::dropReadHandler() +{ + EventSources::instance().dropRead( m_socket ) ; +} + +void GNet::Socket::addWriteHandler( EventHandler & handler ) +{ + G_ASSERT( valid() ) ; + G_DEBUG( "GNet::Socket::addWriteHandler: fd " << m_socket ) ; + EventSources::instance().addWrite( m_socket , handler ) ; +} + +void GNet::Socket::addExceptionHandler( EventHandler & handler ) +{ + G_ASSERT( valid() ) ; + G_DEBUG( "GNet::Socket::addExceptionHandler: fd " << m_socket ) ; + EventSources::instance().addException( m_socket , handler ) ; +} + +void GNet::Socket::dropWriteHandler() +{ + EventSources::instance().dropWrite( m_socket ) ; +} + +void GNet::Socket::dropExceptionHandler() +{ + EventSources::instance().dropException( m_socket ) ; +} + +std::string GNet::Socket::asString() const +{ + std::stringstream ss ; + ss << m_socket ; + return ss.str() ; +} + +//=================================================================== + +GNet::StreamSocket::StreamSocket() : + Socket( PF_INET , SOCK_STREAM ) +{ + setNoLinger() ; + setKeepAlive() ; +} + +GNet::StreamSocket::StreamSocket( Descriptor s ) : + Socket( s ) +{ +} + +GNet::StreamSocket::~StreamSocket() +{ +} + +bool GNet::StreamSocket::reopen() +{ + G_ASSERT( !valid() ) ; + if( valid() ) + close() ; + + return open( PF_INET , SOCK_STREAM ) ; +} + +ssize_t GNet::StreamSocket::read( char *buf , size_t len ) +{ + if( len == 0 ) return 0 ; + ssize_t nread = ::recv( m_socket , buf , len , 0 ) ; + if( sizeError(nread) ) + { + m_reason = reason() ; + G_DEBUG( "GNet::StreamSocket::read: read error " << m_reason ) ; + return -1 ; + } + + G_DEBUG( "GNet::StreamSocket::read: fd " << m_socket << ": read " << nread << " bytes(s)" ) ; + return nread ; +} + +GNet::AcceptPair GNet::StreamSocket::accept() +{ + AcceptPair pair( NULL , Address::invalidAddress() ) ; + + sockaddr addr ; + socklen_t addr_length = sizeof(addr) ; + Descriptor new_socket = ::accept( m_socket, &addr, &addr_length ) ; + if( valid(new_socket) ) + { + pair.second = Address( &addr , addr_length ) ; + G_DEBUG( "GNet::StreamSocket::accept: " << pair.second.displayString() ) ; + pair.first <<= new StreamSocket(new_socket) ; + pair.first.get()->setNoLinger() ; + } + else + { + m_reason = reason() ; + G_DEBUG( "GNet::StreamSocket::accept: failure" ) ; + } + return pair ; +} + +//=================================================================== + +GNet::DatagramSocket::DatagramSocket() : + Socket( PF_INET , SOCK_DGRAM ) +{ +} + +GNet::DatagramSocket::~DatagramSocket() +{ +} + +void GNet::DatagramSocket::disconnect() +{ + int rc = ::connect( m_socket , 0 , 0 ) ; + if( error(rc) ) + m_reason = reason() ; +} + +bool GNet::DatagramSocket::reopen() +{ + G_ASSERT( !valid() ) ; + if( valid() ) + close() ; + + return open( PF_INET , SOCK_DGRAM ) ; +} + +ssize_t GNet::DatagramSocket::read( void *buf , size_t len , Address & src_address ) +{ + sockaddr sender ; + socklen_t sender_len = sizeof(sender) ; + ssize_t nread = ::recvfrom( m_socket, (char*)buf, len, 0, &sender, &sender_len ) ; + if( sizeError(nread) ) + { + m_reason = reason() ; + return -1 ; + } + + src_address = Address( &sender , sender_len ) ; + return nread ; +} + +ssize_t GNet::DatagramSocket::write( const char *buf , size_t len , const Address &dst ) +{ + G_DEBUG( "GNet::DatagramSocket::write: sending " << len << " bytes to " + << dst.displayString() ) ; + + ssize_t nsent = ::sendto( m_socket, buf, + len, 0, dst.address(), dst.length() ) ; + + if( nsent < 0 ) + { + m_reason = reason() ; + G_DEBUG( "GNet::DatagramSocket::write: write error " << m_reason ) ; + return -1 ; + } + return nsent ; +} + + + diff --git a/src/gnet/gsocket.h b/src/gnet/gsocket.h new file mode 100644 index 0000000..5b9e5eb --- /dev/null +++ b/src/gnet/gsocket.h @@ -0,0 +1,368 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsocket.h +// + +#ifndef G_SOCKET_H +#define G_SOCKET_H + +#include "gdef.h" +#include "gnet.h" +#include "gaddress.h" +#include "gevent.h" +#include "gdescriptor.h" +#include + +namespace GNet +{ + class Socket ; + class StreamSocket ; + class DatagramSocket ; + class AcceptPair ; +} ; + +// Class: GNet::Socket +// +// Description: The Socket class encapsulates an asynchronous +// (ie. non-blocking) Unix socket file descriptor or a Windows +// 'SOCKET' handle. The class hides all differences between BSD +// sockets and WinSock. +// +// (Non-blocking network i/o is particularly appropriate for single- +// threaded server processes which manage multiple client connections. +// The main disagvantage is that flow control has to be managed +// explicitly: see Socket::write() and Socket::eWouldBlock().) +// +class GNet::Socket +{ +public: + virtual ~Socket() ; + // Destructor. + + bool valid() const ; + // Returns true if the socket handle + // is valid (open). + + std::pair getLocalAddress() const ; + // Retrieves local address of the socket. + // Pair.first is false on error. + + std::pair getPeerAddress() const ; + // Retrieves address of socket's peer. + // 'Pair.first' is false on error. + + bool hasPeer() const ; + // Returns true if the socket has a valid + // peer. This can be used to see if a + // connect succeeded. + + virtual void close() ; + // Closes the socket. + // + // Postcondition: !valid() + + virtual bool reopen() = 0 ; + // Reopens a closed socket. The new socket is + // _not_ registered with the event source to + // receive read and write events. + // + // Returns false on error. + // + // Postcondition: !valid() + + bool bind( const Address &address ) ; + // Binds the socket with an INADDR_ANY network address + // and the port number taken from the given + // address. This is used for listening + // sockets. + + bool bind(); + // Binds the socket with an INADDR_ANY network address + // and a zero port number. This is used to + // initialise the socket prior to connect(). + + bool connect( const Address &addr , bool *done = NULL ) ; + // Initiates a connection to (or association + // with) the given address. Returns false on + // error. + // + // If successful, a 'done' flag is returned by + // reference indicating whether the connect completed + // immediately. Normally a stream socket connection + // will take some time to complete so the 'done' flag + // will be false: the completion will be indicated by + // a write event some time later. + // + // For datagram sockets this sets up an association + // between two addresses. The socket should first be + // bound with a local address. + + bool listen( int backlog = 1 ); + // Starts the socket listening on the bound + // address for incoming connections or incoming + // datagrams. + + virtual ssize_t write( const char *buf, size_t len ) ; + // Sends data. For datagram sockets the datagram + // is sent to the address specified in the + // previous connect(). Returns the amount + // of data submitted. + // + // If write() returns -1 then use eWouldBlock() to + // determine whether there was a flow control + // problem. If write() returns -1 and eWouldBlock() + // returns false then the connection is lost and + // the socket should be closed. If write() returns + // less than 'len' then assume that there was a partial + // flow control problem (do not use eWouldBlock()). + // + // (This is virtual to allow overloading -- + // not overriding -- in derived classes.) + + bool eWouldBlock() ; + // Returns true if the previous socket operation + // failed with the EWOULDBLOCK or EGAIN error status. + // When writing this indicates a flow control + // problem; when reading it indicates that there + // is nothing to read. + + bool eInProgress() ; + // Returns true if the previous socket operation + // failed with the EINPROGRESS error status. + // When connecting this can be considered a + // non-error. + + bool eMsgSize() ; + // Returns true if the previous socket operation + // failed with the EMSGSIZE error status. + // When writing to a datagram socket this + // indicates that the message was too big + // to send atomically. + + void addReadHandler( EventHandler & handler ) ; + // Adds this socket to the event source list + // so that the given handler receives read + // events. + + void dropReadHandler(); + // Reverses addReadHandler(). + + void addWriteHandler( EventHandler & handler ) ; + // Adds this socket to the event source list + // so that the given handler receives write + // events when flow control is released. + // (Not used for datagram sockets.) + + void dropWriteHandler() ; + // Reverses addWriteHandler(). + + void addExceptionHandler( EventHandler & handler ); + // Adds this socket to the event source list + // so that the given handler receives exception + // events. An exception event should + // be treated as a disconnection event. + // (Not used for datagram sockets.) + + void dropExceptionHandler() ; + // Reverses addExceptionHandler(). + + std::string asString() const ; + // Returns the socket handle as a string. + // Only used in debugging. + +protected: + Socket( int domain, int type, int protocol = 0 ) ; + // Constructor used by derived classes. + // Opens the socket using ::socket(). + + bool open( int domain , int type , int protocol = 0 ) ; + // Used by derived classes to implement reopen(). + // Opens the socket using ::socket(). + // Returns false on error. + + Socket( Descriptor s ) ; + // Constructor which creates a socket object from + // an existing socket handle. Used only by + // StreamSocket::accept(). + +protected: + static bool valid( Descriptor s ) ; + static int reason() ; + static bool error( int rc ) ; + static bool sizeError( ssize_t size ) ; + void setFault() ; + void setNoLinger() ; + void setReuse() ; + void setKeepAlive() ; + std::pair getAddress( bool ) const ; + +private: + void doClose() ; + bool setNonBlock() ; + +protected: + int m_reason ; + Descriptor m_socket ; + +private: + Socket( const Socket& ); + void operator=( const Socket& ); + void drop() ; +}; + +// === + +// Class: GNet::AcceptPair +// Description: A class which behaves like std::pair,Address>. +// +// (The standard pair<> template cannot be used because gcc's auto_ptr<> has +// a non-const copy constructor and assignment operator -- the pair<> op=() +// fails to compile because the rhs of the 'first' assignment is const, +// not matching any op=() in auto_ptr<>. Note the use of const_cast<>() below.) +// +class GNet::AcceptPair +{ +public: + typedef std::auto_ptr first_type ; + typedef Address second_type ; + first_type first ; + second_type second ; + AcceptPair( StreamSocket * new_p , Address a ) ; + AcceptPair( const AcceptPair & other ) ; + AcceptPair & operator=( const AcceptPair & rhs ) ; +} ; + +// === + +// Class: GNet::StreamSocket +// Description: A derivation of Socket for a stream socket. +// +class GNet::StreamSocket : public Socket +{ +public: + StreamSocket() ; + // Default constructor. + + virtual ~StreamSocket() ; + // Destructor. + + ssize_t read( char *buffer , size_t buffer_length ) ; + // Reads from the TCP stream. Returns + // 0 if the connection has been lost. + // Returns -1 on error. + + AcceptPair accept() ; + // Accepts an incoming connection, returning + // a new()ed socket and the peer address. + + virtual bool reopen() ; + // Inherited from Socket. + +private: + StreamSocket( const StreamSocket & ) ; + // Copy constructor. Not implemented. + + void operator=( const StreamSocket & ) ; + // Assignment operator. Not implemented. + + StreamSocket( Descriptor s ) ; + // A private constructor used in accept(). +}; + +// === + +// Class: GNet::DatagramSocket +// Description: A derivation of Socket for a connectionless +// datagram socket. +// +class GNet::DatagramSocket : public Socket +{ +public: + DatagramSocket(); + // Default constructor. + + virtual ~DatagramSocket() ; + // Destructor. + + ssize_t read( void * buffer , size_t len , Address & src ) ; + // Reads a datagram and returns the sender's address + // by reference. If connect() has been used then + // only datagrams from the address specified in the + // connect() call will be received. + + ssize_t write( const char * buffer , size_t len , const Address & dst ) ; + // Sends a datagram to the given address. + // This form of write() should be used + // if there is no connect() assocation + // in effect. + + ssize_t write( const char * buffer , size_t len ) ; + // See Socket::write(). + + void disconnect() ; + // Releases the association between two + // datagram endpoints reversing the effect + // of the previous Socket::connect(). + + virtual bool reopen() ; + // Inherited from Socket. + +private: + DatagramSocket( const DatagramSocket & ) ; + // Copy constructor. Not implemented. + + void operator=( const DatagramSocket & ) ; + // Assignment operator. Not implemented. +} ; + +// === + +inline +ssize_t GNet::DatagramSocket::write( const char *buf, size_t len ) +{ + return Socket::write(buf,len) ; +} + +// === + +inline +GNet::AcceptPair::AcceptPair( StreamSocket * p , Address a ) : + first(p) , + second(a) +{ +} + +inline +GNet::AcceptPair::AcceptPair( const AcceptPair & other ) : + first(const_cast(other.first)) , + second(other.second) +{ +} + +inline +GNet::AcceptPair & GNet::AcceptPair::operator=( const AcceptPair & rhs ) +{ + first = const_cast(rhs.first) ; + second = rhs.second ; return *this ; +} + +#endif + diff --git a/src/gnet/gsocket_unix.cpp b/src/gnet/gsocket_unix.cpp new file mode 100644 index 0000000..ea640dd --- /dev/null +++ b/src/gnet/gsocket_unix.cpp @@ -0,0 +1,95 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsocket_unix.cc +// + +#include "gdef.h" +#include "gnet.h" +#include "gsocket.h" +#include "gdebug.h" +#include "gassert.h" +#include "glog.h" +#include +#include +#include +#include + +bool GNet::Socket::valid( Descriptor s ) +{ + return s >= 0 ; +} + +bool GNet::Socket::setNonBlock() +{ + G_ASSERT( valid() ) ; + + int mode = ::fcntl( m_socket , F_GETFL ) ; + if( mode < 0 ) + return false ; + + int rc = ::fcntl( m_socket , F_SETFL , mode | O_NONBLOCK ) ; + return rc >= 0 ; +} + +int GNet::Socket::reason() +{ + int r = errno ; + G_DEBUG( "GNet::Socket::reason: " << (r==EINPROGRESS?"":"error ") << r << ": " << ::strerror(r) ) ; + return r ; +} + +void GNet::Socket::doClose() +{ + G_ASSERT( valid() ) ; + ::close( m_socket ); + m_socket = -1; +} + +bool GNet::Socket::error( int rc ) +{ + return rc < 0 ; +} + +bool GNet::Socket::sizeError( ssize_t size ) +{ + return size < 0 ; +} + +bool GNet::Socket::eWouldBlock() +{ + return m_reason == EWOULDBLOCK || m_reason == EAGAIN ; +} + +bool GNet::Socket::eInProgress() +{ + return m_reason == EINPROGRESS ; +} + +bool GNet::Socket::eMsgSize() +{ + return m_reason == EMSGSIZE ; +} + +void GNet::Socket::setFault() +{ + m_reason = EFAULT ; +} + diff --git a/src/gnet/gsocket_win32.cpp b/src/gnet/gsocket_win32.cpp new file mode 100644 index 0000000..e514c12 --- /dev/null +++ b/src/gnet/gsocket_win32.cpp @@ -0,0 +1,93 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsocket_win32.cc +// + +#include "gdef.h" +#include "gnet.h" +#include "gdebug.h" +#include "gassert.h" +#include "gsocket.h" +#include "glog.h" +#include + +bool GNet::Socket::valid( Descriptor s ) +{ + // (beware loosing WSAGetLastError() information, so...) + // (put no debug here) + return Descriptor__valid( s ) ; +} + +int GNet::Socket::reason() +{ + // (put no debug here) + int r = ::WSAGetLastError() ; + G_DEBUG( "GNet::Socket::reason: error " << r ) ; + return r ; +} + +void GNet::Socket::doClose() +{ + G_ASSERT( valid() ) ; + ::closesocket( m_socket ); + m_socket = Descriptor__invalid() ; +} + +bool GNet::Socket::error( int rc ) +{ + // (put no debug here) + return rc == SOCKET_ERROR ; +} + +bool GNet::Socket::sizeError( ssize_t size ) +{ + // (put no debug here) + return size == SOCKET_ERROR ; +} + +bool GNet::Socket::eWouldBlock() +{ + return m_reason == WSAEWOULDBLOCK ; +} + +bool GNet::Socket::eInProgress() +{ + // (Winsock WSAEINPROGRESS has different semantics to Unix) + return m_reason == WSAEWOULDBLOCK ; // (sic) +} + +bool GNet::Socket::eMsgSize() +{ + return m_reason == WSAEMSGSIZE ; +} + +bool GNet::Socket::setNonBlock() +{ + unsigned long ul = 1 ; + return ioctlsocket( m_socket , FIONBIO , &ul ) != SOCKET_ERROR ; + // (put no debug here) +} + +void GNet::Socket::setFault() +{ + m_reason = WSAEFAULT ; +} + diff --git a/src/gnet/gwinsock.cpp b/src/gnet/gwinsock.cpp new file mode 100644 index 0000000..f5b2432 --- /dev/null +++ b/src/gnet/gwinsock.cpp @@ -0,0 +1,260 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gwinsock.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gpump.h" +#include "gappinst.h" +#include "gwinhid.h" +#include "gwinsock.h" +#include "gassert.h" +#include "gdebug.h" +#include "glog.h" + +namespace GNet +{ + class WinsockWindow ; +} ; + +// Class: GNet::WinsockWindow +// Description: An private implementation class used by GNet::Winsock +// to hook into GGui::Window event processing. +// +class GNet::WinsockWindow : public GGui::WindowHidden +{ +public: + WinsockWindow( Winsock & ws , HINSTANCE h ) ; +private: + virtual LRESULT onUser( WPARAM , LPARAM ) ; + Winsock & m_ws ; +} ; + +GNet::WinsockWindow::WinsockWindow( Winsock & ws , HINSTANCE hinstance ) : + m_ws(ws) , + GGui::WindowHidden(hinstance) +{ +} + +LRESULT GNet::WinsockWindow::onUser( WPARAM w , LPARAM l ) +{ + m_ws.onMessage( w , l ) ; + return 0 ; +} + +// === + +GNet::Winsock::Winsock() : + m_window(NULL) , + m_read_list("read") , + m_write_list("write") , + m_exception_list("exception") , + m_success(false) , + m_hwnd(0) , + m_msg(0) +{ +} + +std::string GNet::Winsock::id() const +{ + return m_id ; +} + +bool GNet::Winsock::load( const char * ) +{ + ; // not implemented + return true ; +} + +bool GNet::Winsock::init() +{ + HINSTANCE hinstance = GGui::ApplicationInstance::hinstance() ; + m_window = new WinsockWindow( *this , hinstance ) ; + if( m_window->handle() == 0 ) + { + G_WARNING( "GNet::Winsock::init: cannot create hidden window" ) ; + return false ; + } + return attach( m_window->handle() , WM_USER ) ; +} + +bool GNet::Winsock::attach( HWND hwnd , unsigned msg ) +{ + m_hwnd = hwnd ; + m_msg = msg ; + + WSADATA info ; + WORD version = MAKEWORD( 1 , 1 ) ; + int rc = ::WSAStartup( version , &info ) ; + if( rc != 0 ) + { + m_reason = "winsock startup failure" ; + return false ; + } + + if( LOBYTE( info.wVersion ) != 1 || + HIBYTE( info.wVersion ) != 1 ) + { + m_reason = "incompatible winsock version" ; + ::WSACleanup() ; + return false ; + } + + m_id = info.szDescription ; + G_DEBUG( "GNet::Winsock::attach: winsock \"" << m_id << "\"" ) ; + m_success = true ; + return true ; +} + +std::string GNet::Winsock::reason() const +{ + return m_reason ; +} + +GNet::Winsock::~Winsock() +{ + if( m_success ) + ::WSACleanup() ; + delete m_window ; +} + +void GNet::Winsock::addRead( Descriptor fd , EventHandler &handler ) +{ + m_read_list.add( fd , &handler ) ; + update( fd ) ; +} + +void GNet::Winsock::addWrite( Descriptor fd , EventHandler &handler ) +{ + m_write_list.add( fd , &handler ) ; + update( fd ) ; +} + +void GNet::Winsock::addException( Descriptor fd , EventHandler &handler ) +{ + m_exception_list.add( fd , &handler ) ; + update( fd ) ; +} + +void GNet::Winsock::dropRead( Descriptor fd ) +{ + m_read_list.remove( fd ) ; + update( fd ) ; +} + +void GNet::Winsock::dropWrite( Descriptor fd ) +{ + m_write_list.remove( fd ) ; + update( fd ) ; +} + +void GNet::Winsock::dropException( Descriptor fd ) +{ + m_exception_list.remove( fd ) ; + update( fd ) ; +} + +namespace +{ + const long READ_EVENTS = (FD_READ | FD_ACCEPT | FD_OOB) ; + const long WRITE_EVENTS = (FD_WRITE | FD_CONNECT) ; + const long EXCEPTION_EVENTS = (FD_CLOSE) ; +} ; + +void GNet::Winsock::update( Descriptor fd ) +{ + G_ASSERT( m_success ) ; + G_ASSERT( m_hwnd != 0 ) ; + G_ASSERT( m_msg != 0 ) ; + ::WSAAsyncSelect( fd , m_hwnd , m_msg , desiredEvents(fd) ) ; +} + +long GNet::Winsock::desiredEvents( Descriptor fd ) +{ + long mask = 0 ; + + if( m_read_list.contains(fd) ) + mask |= FD_READ | FD_ACCEPT | FD_OOB ; + + if( m_write_list.contains(fd) ) + mask |= FD_WRITE | FD_CONNECT ; + + if( m_exception_list.contains(fd) ) + mask |= FD_CLOSE ; + + return mask ; +} + +GNet::EventHandler * GNet::Winsock::findHandler( EventHandlerList &list , Descriptor fd ) +{ + return list.find( fd ) ; +} + +void GNet::Winsock::onMessage( WPARAM wparam , LPARAM lparam ) +{ + int fd = wparam ; + int event = WSAGETSELECTEVENT( lparam ) ; + int err = WSAGETSELECTERROR( lparam ) ; + + G_DEBUG( "GNet::Winsock::processMessage: winsock select message: " + << "w=" << wparam << " l=" << lparam + << " fd=" << fd << " evt=" << event << " err=" << err ) ; + + if( event & READ_EVENTS ) + { + EventHandler *handler = findHandler( m_read_list , fd ) ; + if( handler ) + handler->readEvent(); + } + else if( event & WRITE_EVENTS ) + { + EventHandler *handler = findHandler( m_write_list , fd ) ; + if( handler ) + handler->writeEvent(); + } + else if( event & EXCEPTION_EVENTS ) + { + EventHandler *handler = findHandler( m_exception_list , fd ) ; + if( handler ) + handler->exceptionEvent(); + } + else if( err ) + { + G_DEBUG( "GNet::Winsock::processMessage: winsock select error: " << err ) ; + } + else + { + G_DEBUG( "GNet::Winsock::processMessage: unwanted winsock event: " << event ) ; + } +} + +void GNet::Winsock::run() +{ + GGui::Pump::run() ; +} + +void GNet::Winsock::quit() +{ + GGui::Pump::quit() ; +} + + diff --git a/src/gnet/gwinsock.h b/src/gnet/gwinsock.h new file mode 100644 index 0000000..1c25046 --- /dev/null +++ b/src/gnet/gwinsock.h @@ -0,0 +1,126 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gwinsock.h +// + +#ifndef G_WINSOCK_H +#define G_WINSOCK_H + +#include "gdef.h" +#include "gnet.h" +#include "gwinbase.h" +#include "gevent.h" + +namespace GNet +{ + class Winsock ; +} ; + +// Class: GNet::Winsock +// Description: A windows-specific class which initialises +// the WinSock system, and integrates the windows message +// queue with it. Note that WinSock events are delivered +// as messages to the application's main message queue, +// and these messages have to be passed on the the +// WinSock layer. +// +class GNet::Winsock : public EventSources +{ +public: + Winsock() ; + // Default constructor. Initialise with load() (optional) + // and then either init() or attach(). + + bool load( const char * dllpath ) ; + // Loads the given WinSock DLL. Must be the first + // method called after construction. + // Returns false on error. + + virtual bool init() ; + // Override from EventSources. Initialses the + // WinSock library, passing it the handle + // of an internally-created hidden window. + // Returns false on error. + + bool attach( HWND hwnd , unsigned int msg ) ; + // Initialises the WinSock library, passin + // it the specified window handle an + // message number. WinSock events are sent + // to that window. Returns false on error. + // + // For simple, synchronous programs + // the window handle may be zero. + + std::string reason() const ; + // Returns the reason for initialisation + // failure. + + std::string id() const ; + // Returns the WinSock implementation's + // identification string. Returns the + // zero length string on error. + + virtual ~Winsock() ; + // Destructor. Releases the WinSock resources + // if this is the last Winsock object. + + void onMessage( WPARAM wparam , LPARAM lparam ) ; + // To be called on receipt of a window + // message corresponding to the constructor's + // 'msg' parameter. + + virtual void run() ; + // Override from EventSources. Calls GGui::Pump::run() + + virtual void quit() ; + // Override from EventSources. Calls GGui::Pump::quit(). + +protected: + virtual void addRead( Descriptor fd , EventHandler & handler ) ; + virtual void addWrite( Descriptor fd , EventHandler & handler ) ; + virtual void addException( Descriptor fd , EventHandler & handler ) ; + virtual void dropRead( Descriptor fd ) ; + virtual void dropWrite( Descriptor fd ) ; + virtual void dropException( Descriptor fd ) ; + +private: + Winsock( const Winsock & other ) ; + void operator=( const Winsock & other ) ; + EventHandler *findHandler( EventHandlerList & list , Descriptor fd ) ; + void update( Descriptor fd ) ; + long desiredEvents( Descriptor fd ) ; + +private: + bool m_initialised ; + GGui::WindowBase * m_window ; + bool m_success ; + std::string m_reason ; + std::string m_id ; + HWND m_hwnd ; + unsigned m_msg ; + EventHandlerList m_read_list ; + EventHandlerList m_write_list ; + EventHandlerList m_exception_list ; +} ; + +#endif + + diff --git a/src/main/Makefile.am b/src/main/Makefile.am new file mode 100644 index 0000000..0d3bf9e --- /dev/null +++ b/src/main/Makefile.am @@ -0,0 +1,51 @@ +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + +# force symbol stripping on 'make install' +AM_INSTALL_PROGRAM_FLAGS=-s + +# change the local-state directory from .../var to .../var/spool/emailrelay +localstatedir = ${prefix}/var/spool/emailrelay + +EXTRA_DIST=gmessagestore_win32.cpp emailrelay.dsp empty_file doxygen.cfg +INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib -I$(top_srcdir)/src/gnet +sbin_PROGRAMS = emailrelay +libexec_PROGRAMS = emailrelay-poke +localstate_DATA = empty_file +emailrelay_SOURCES = gadminserver.cpp \ + gclientprotocol.cpp \ + gmessagestore.cpp \ + gmessagestore_unix.cpp \ + gprotocolmessage.cpp \ + gserverprotocol.cpp \ + gsmtpclient.cpp \ + gsmtpserver.cpp \ + mailrelay.cpp \ + gadminserver.h \ + gclientprotocol.h \ + gmessagestore.h \ + gprotocolmessage.h \ + gserverprotocol.h \ + gsmtp.h \ + gsmtpclient.h \ + gsmtpserver.h +emailrelay_poke_SOURCES=poke.c +emailrelay_LDADD = $(top_builddir)/src/glib/libglib.a $(top_builddir)/src/gnet/libgnet.a + diff --git a/src/main/Makefile.in b/src/main/Makefile.in new file mode 100644 index 0000000..b412f81 --- /dev/null +++ b/src/main/Makefile.in @@ -0,0 +1,381 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + +# force symbol stripping on 'make install' + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +AM_INSTALL_PROGRAM_FLAGS = -s + +# change the local-state directory from .../var to .../var/spool/emailrelay +localstatedir = ${prefix}/var/spool/emailrelay + +EXTRA_DIST = gmessagestore_win32.cpp emailrelay.dsp empty_file doxygen.cfg +INCLUDES = -I$(top_srcdir)/lib/gcc2.95 -I$(top_srcdir)/src/glib -I$(top_srcdir)/src/gnet +sbin_PROGRAMS = emailrelay +libexec_PROGRAMS = emailrelay-poke +localstate_DATA = empty_file +emailrelay_SOURCES = gadminserver.cpp gclientprotocol.cpp gmessagestore.cpp gmessagestore_unix.cpp gprotocolmessage.cpp gserverprotocol.cpp gsmtpclient.cpp gsmtpserver.cpp mailrelay.cpp gadminserver.h gclientprotocol.h gmessagestore.h gprotocolmessage.h gserverprotocol.h gsmtp.h gsmtpclient.h gsmtpserver.h + +emailrelay_poke_SOURCES = poke.c +emailrelay_LDADD = $(top_builddir)/src/glib/libglib.a $(top_builddir)/src/gnet/libgnet.a +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../config.h +CONFIG_CLEAN_FILES = +PROGRAMS = $(libexec_PROGRAMS) $(sbin_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) -I../.. +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +emailrelay_poke_OBJECTS = poke.o +emailrelay_poke_LDADD = $(LDADD) +emailrelay_poke_DEPENDENCIES = +emailrelay_poke_LDFLAGS = +emailrelay_OBJECTS = gadminserver.o gclientprotocol.o gmessagestore.o \ +gmessagestore_unix.o gprotocolmessage.o gserverprotocol.o gsmtpclient.o \ +gsmtpserver.o mailrelay.o +emailrelay_DEPENDENCIES = $(top_builddir)/src/glib/libglib.a \ +$(top_builddir)/src/gnet/libgnet.a +emailrelay_LDFLAGS = +CXXFLAGS = @CXXFLAGS@ +CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DATA = $(localstate_DATA) + +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(emailrelay_poke_SOURCES) $(emailrelay_SOURCES) +OBJECTS = $(emailrelay_poke_OBJECTS) $(emailrelay_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .cpp .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps src/main/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-libexecPROGRAMS: + +clean-libexecPROGRAMS: + -test -z "$(libexec_PROGRAMS)" || rm -f $(libexec_PROGRAMS) + +distclean-libexecPROGRAMS: + +maintainer-clean-libexecPROGRAMS: + +install-libexecPROGRAMS: $(libexec_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(libexecdir) + @list='$(libexec_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(libexecdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(libexecdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-libexecPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(libexec_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(libexecdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +mostlyclean-sbinPROGRAMS: + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) + +distclean-sbinPROGRAMS: + +maintainer-clean-sbinPROGRAMS: + +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(sbindir) + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(sbin_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +emailrelay-poke: $(emailrelay_poke_OBJECTS) $(emailrelay_poke_DEPENDENCIES) + @rm -f emailrelay-poke + $(LINK) $(emailrelay_poke_LDFLAGS) $(emailrelay_poke_OBJECTS) $(emailrelay_poke_LDADD) $(LIBS) + +emailrelay: $(emailrelay_OBJECTS) $(emailrelay_DEPENDENCIES) + @rm -f emailrelay + $(CXXLINK) $(emailrelay_LDFLAGS) $(emailrelay_OBJECTS) $(emailrelay_LDADD) $(LIBS) +.cpp.o: + $(CXXCOMPILE) -c $< + +install-localstateDATA: $(localstate_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(localstatedir) + @list='$(localstate_DATA)'; for p in $$list; do \ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(localstatedir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(localstatedir)/$$p; \ + else if test -f $$p; then \ + echo " $(INSTALL_DATA) $$p $(DESTDIR)$(localstatedir)/$$p"; \ + $(INSTALL_DATA) $$p $(DESTDIR)$(localstatedir)/$$p; \ + fi; fi; \ + done + +uninstall-localstateDATA: + @$(NORMAL_UNINSTALL) + list='$(localstate_DATA)'; for p in $$list; do \ + rm -f $(DESTDIR)$(localstatedir)/$$p; \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src/main + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-libexecPROGRAMS install-sbinPROGRAMS \ + install-localstateDATA +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-libexecPROGRAMS uninstall-sbinPROGRAMS \ + uninstall-localstateDATA +uninstall: uninstall-am +all-am: Makefile $(PROGRAMS) $(DATA) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libexecdir) $(DESTDIR)$(sbindir) \ + $(DESTDIR)$(localstatedir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-libexecPROGRAMS mostlyclean-sbinPROGRAMS \ + mostlyclean-compile mostlyclean-tags \ + mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-libexecPROGRAMS clean-sbinPROGRAMS clean-compile \ + clean-tags clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-libexecPROGRAMS distclean-sbinPROGRAMS \ + distclean-compile distclean-tags distclean-generic \ + clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-libexecPROGRAMS \ + maintainer-clean-sbinPROGRAMS maintainer-clean-compile \ + maintainer-clean-tags maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-libexecPROGRAMS distclean-libexecPROGRAMS \ +clean-libexecPROGRAMS maintainer-clean-libexecPROGRAMS \ +uninstall-libexecPROGRAMS install-libexecPROGRAMS \ +mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS clean-sbinPROGRAMS \ +maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \ +install-sbinPROGRAMS mostlyclean-compile distclean-compile \ +clean-compile maintainer-clean-compile uninstall-localstateDATA \ +install-localstateDATA tags mostlyclean-tags distclean-tags clean-tags \ +maintainer-clean-tags distdir info-am info dvi-am dvi check check-am \ +installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/main/doxygen.cfg b/src/main/doxygen.cfg new file mode 100644 index 0000000..abd1a3e --- /dev/null +++ b/src/main/doxygen.cfg @@ -0,0 +1,170 @@ +# Doxygen configuration generated by Doxywizard version 0.1 +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = E-MailRelay +PROJECT_NUMBER = 0.9.1 +OUTPUT_DIRECTORY = +OUTPUT_LANGUAGE = English +EXTRACT_ALL = YES +EXTRACT_PRIVATE = +EXTRACT_STATIC = +HIDE_UNDOC_MEMBERS = +HIDE_UNDOC_CLASSES = +BRIEF_MEMBER_DESC = +REPEAT_BRIEF = +ALWAYS_DETAILED_SEC = +FULL_PATH_NAMES = +STRIP_FROM_PATH = +INTERNAL_DOCS = +CLASS_DIAGRAMS = +SOURCE_BROWSER = +INLINE_SOURCES = +STRIP_CODE_COMMENTS = +CASE_SENSE_NAMES = +SHORT_NAMES = +HIDE_SCOPE_NAMES = +VERBATIM_HEADERS = +SHOW_INCLUDE_FILES = +JAVADOC_AUTOBRIEF = YES +INHERIT_DOCS = +INLINE_INFO = +SORT_MEMBER_DOCS = +DISTRIBUTE_GROUP_DOC = +TAB_SIZE = +ENABLED_SECTIONS = +GENERATE_TODOLIST = +GENERATE_TESTLIST = +GENERATE_BUGLIST = +ALIASES = +MAX_INITIALIZER_LINES = +OPTIMIZE_OUTPUT_FOR_C = +SHOW_USED_FILES = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = NO +WARN_IF_UNDOCUMENTED = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = __TOP__/src/glib \ + __TOP__/src/gnet \ + __TOP__/src/main +FILE_PATTERNS = *.cpp \ + *.h +RECURSIVE = YES +EXCLUDE = src/glib/gdef.h \ + src/gnet/gnet.h \ + src/win32 \ + src/gnet/resolverd.cpp \ + src/gnet/gwinsock.cpp \ + src/gnet/gwinsock.h \ + src/gnet/gaddress_ipv6.cpp \ + src/gnet/gresolve_ipv6.cpp \ + src/gnet/grequest.cpp \ + src/gnet/grequest.h +EXCLUDE_PATTERNS = */*_win32.* */old/* +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +IMAGE_PATH = +INPUT_FILTER = __TOP__/bin/emailrelay-filter.sh +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = +COLS_IN_ALPHA_INDEX = +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = +HTML_OUTPUT = html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = +GENERATE_HTMLHELP = +GENERATE_CHI = +BINARY_TOC = +TOC_EXPAND = +DISABLE_INDEX = +ENUM_VALUES_PER_LINE = +GENERATE_TREEVIEW = +TREEVIEW_WIDTH = +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = +LATEX_OUTPUT = latex +COMPACT_LATEX = +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = +USE_PDFLATEX = +LATEX_BATCHMODE = +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = +RTF_OUTPUT = rtf +COMPACT_RTF = +RTF_HYPERLINKS = +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = "G_EXCEPTION(name,literal)= " \ + GAllocator(t)= \ + GLessAllocator(t1,t2)= +EXPAND_AS_DEFINED = +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +HAVE_DOT = +CLASS_GRAPH = +COLLABORATION_GRAPH = +INCLUDE_GRAPH = +INCLUDED_BY_GRAPH = +GRAPHICAL_HIERARCHY = +DOT_PATH = +MAX_DOT_GRAPH_WIDTH = +MAX_DOT_GRAPH_HEIGHT = +GENERATE_LEGEND = +DOT_CLEANUP = +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = +CGI_NAME = search.cgi +CGI_URL = +DOC_URL = +DOC_ABSPATH = +BIN_ABSPATH = /usr/local/bin/ +EXT_DOC_PATHS = diff --git a/src/main/emailrelay.dsp b/src/main/emailrelay.dsp new file mode 100644 index 0000000..84c8543 --- /dev/null +++ b/src/main/emailrelay.dsp @@ -0,0 +1,486 @@ +# Microsoft Developer Studio Project File - Name="emailrelay" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=emailrelay - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "emailrelay.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "emailrelay.mak" CFG="emailrelay - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "emailrelay - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "emailrelay - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "emailrelay - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "emailrelay - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GR /GX /ZI /Od /I "../../src/glib" /I "../../src/gnet" /I "../../lib/msvc6.0" /I "../../src/win32" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "G_WIN32" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "emailrelay - Win32 Release" +# Name "emailrelay - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\gnet\gaddress_ipv4.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gadminserver.cpp +# End Source File +# Begin Source File + +SOURCE=..\win32\gappinst.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\garg.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\garg_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gclient.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gclient_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gclientprotocol.cpp +# End Source File +# Begin Source File + +SOURCE=..\win32\gcracker.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdaemon_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdate.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdatetime.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdatetime_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gdescriptor_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdirectory.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdirectory_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gevent.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gevent_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\geventserver.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gexception.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gfile.cpp +# End Source File +# Begin Source File + +SOURCE=..\glib\gfile_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gfs_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\ggetopt.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\glinebuffer.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\glocal_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\glog.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\glogoutput.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\glogoutput_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gmessagestore.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gmessagestore_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\glib\gnumber.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gpath.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gpid_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gprotocolmessage.cpp +# End Source File +# Begin Source File + +SOURCE=..\win32\gpump.cpp +# End Source File +# Begin Source File + +SOURCE=..\win32\gpump_nodialog.cpp +# End Source File +# Begin Source File + +SOURCE=..\gnet\grequest.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gresolve.cpp +# End Source File +# Begin Source File + +SOURCE=..\gnet\gresolve_ipv4.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gresolve_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\gnet\gserver.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gserverprotocol.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gsmtpclient.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gsmtpserver.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gsocket.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gsocket_win32.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gstr.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gtime.cpp +# End Source File +# Begin Source File + +SOURCE=..\win32\gwinbase.cpp +# End Source File +# Begin Source File + +SOURCE=..\win32\gwindow.cpp +# End Source File +# Begin Source File + +SOURCE=..\win32\gwinhid.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gwinsock.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\mailrelay.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\src\gnet\gaddress.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gadminserver.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\garg.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gassert.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gclient.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gclientprotocol.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gconvert.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdaemon.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdate.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdatetime.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdebug.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdef.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gdescriptor.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gdirectory.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gevent.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\geventserver.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gexception.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gfile.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gfs.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\ggetopt.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\glinebuffer.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\glocal.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\glog.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\glogoutput.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gmemory.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gmessagestore.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gnet.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gpath.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gpid.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gprotocolmessage.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\grequest.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gresolve.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gselect.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gserver.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gserverprotocol.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gsmtp.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gsmtpclient.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\main\gsmtpserver.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gsocket.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gstr.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gstrings.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\glib\gtime.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\gnet\gwinsock.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/src/main/empty_file b/src/main/empty_file new file mode 100644 index 0000000..e69de29 diff --git a/src/main/gadminserver.cpp b/src/main/gadminserver.cpp new file mode 100644 index 0000000..f9bca39 --- /dev/null +++ b/src/main/gadminserver.cpp @@ -0,0 +1,154 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gadmin.h +// + +#include "gdef.h" +#include "gnet.h" +#include "gsmtp.h" +#include "gadminserver.h" +#include "gstr.h" +#include "gmemory.h" + +GSmtp::AdminClient::AdminClient( AdminPeer & admin_peer ) : + Client(MessageStore::instance(),admin_peer,false) +{ +} + +// === + +GSmtp::AdminPeer::AdminPeer( GNet::StreamSocket * s , GNet::Address a , AdminServer & server , + const std::string & server_address ) : + GNet::ServerPeer( s , a ) , + m_server(server) , + m_buffer(crlf()) , + m_server_address(server_address) +{ +} + +void GSmtp::AdminPeer::onCompletion( std::string s ) +{ + if( s.empty() ) + send( "OK" ) ; + else + send( std::string("error: ") + s ) ; +} + +void GSmtp::AdminPeer::onDelete() +{ +} + +void GSmtp::AdminPeer::onData( const char * data , size_t n ) +{ + m_buffer.add( std::string(data,n) ) ; + while( m_buffer.more() ) + { + if( ! processLine( m_buffer.line() ) ) + return ; + } +} + +bool GSmtp::AdminPeer::processLine( const std::string & line ) +{ + if( is(line,"FLUSH") ) + { + flush( m_server_address ) ; + } + else if( is(line,"HELP") ) + { + help() ; + } + else if( is(line,"QUIT") ) + { + doDelete() ; + return false ; + } + else if( line.find_first_not_of(" \r\n\t") != std::string::npos ) + { + send( "error: unrecognised command" ) ; + } + return true ; +} + +//static +std::string GSmtp::AdminPeer::crlf() +{ + return "\015\012" ; +} + +//static +bool GSmtp::AdminPeer::is( const std::string & line_in , const char * key ) +{ + std::string line( line_in ) ; + G::Str::trim( line , " \t" ) ; + G::Str::toUpper( line ) ; + return line.find(key) == 0U ; +} + +void GSmtp::AdminPeer::help() +{ + send( "commands: FLUSH, HELP, QUIT" ) ; +} + +void GSmtp::AdminPeer::flush( const std::string & address ) +{ + G_DEBUG( "GSmtp::AdminPeer: flush: \"" << address << "\"" ) ; + + if( m_client.get() != NULL && m_client->busy() ) + { + send( "error: still working" ) ; + } + else + { + m_client <<= new AdminClient( *this ) ; + std::string rc = m_client->init( address ) ; + if( rc.length() != 0U ) + { + send( std::string("error: ") + rc ) ; + } + } +} + +void GSmtp::AdminPeer::send( std::string line ) +{ + line.append( crlf() ) ; + ssize_t rc = socket().write( line.data() , line.length() ) ; + if( rc < line.length() ) + { + doDelete() ; // onDelete() and "delete this" + } +} + +// === + +GSmtp::AdminServer::AdminServer( unsigned int port , bool allow_remote , const std::string & address ) : + GNet::Server( port ) , + m_allow_remote( allow_remote ) , + m_server_address( address ) +{ + G_DEBUG( "GSmtp::AdminServer: administrative interface listening on port " << port ) ; +} + +GNet::ServerPeer * GSmtp::AdminServer::newPeer( GNet::StreamSocket * s , GNet::Address a ) +{ + return new AdminPeer( s , a , *this , m_server_address ) ; +} + diff --git a/src/main/gadminserver.h b/src/main/gadminserver.h new file mode 100644 index 0000000..f5fb97a --- /dev/null +++ b/src/main/gadminserver.h @@ -0,0 +1,103 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gadminserver.h +// + +#ifndef G_SMTP_ADMIN_H +#define G_SMTP_ADMIN_H + +#include "gdef.h" +#include "gsmtp.h" +#include "gserver.h" +#include "glinebuffer.h" +#include "gserverprotocol.h" +#include "gsmtpclient.h" +#include +#include + +namespace GSmtp +{ + class AdminPeer ; + class AdminServer ; + class AdminClient ; +} ; + +// Class: GSmtp::AdminClient +// Description: A private implementation class. +// +class GSmtp::AdminClient : public GSmtp::Client +{ +public: + AdminClient( AdminPeer & admin_peer ) ; +} ; + +// === + +// Class: GSmtp::AdminPeer +// Description: A derivation of ServerPeer for the administration interface. +// See also: AdminServer +// +class GSmtp::AdminPeer : public GNet::ServerPeer , public GSmtp::Client::ClientCallback +{ +public: + AdminPeer( GNet::StreamSocket * , GNet::Address , AdminServer & server , const std::string & ) ; + // Constructor. + +private: + AdminPeer( const AdminPeer & ) ; + void operator=( const AdminPeer & ) ; + virtual void onDelete() ; // from GNet::ServerPeer + virtual void onData( const char * , size_t ) ; // from GNet::ServerPeer + virtual void onCompletion( std::string ) ; // from Client::ClientCallback + bool processLine( const std::string & line ) ; + static bool is( const std::string & , const char * ) ; + void flush( const std::string & ) ; + void help() ; + void send( std::string ) ; + +private: + GNet::LineBuffer m_buffer ; + static std::string crlf() ; + AdminServer & m_server ; + std::auto_ptr m_client ; + std::string m_server_address ; +} ; + +// Class: GSmtp::AdminServer +// Description: A server class which implements the emailrelay administration interface. +// +class GSmtp::AdminServer : public GNet::Server +{ +public: + AdminServer( unsigned int port , bool allow_remote , const std::string & server_address ) ; + // Constructor. + +private: + virtual GNet::ServerPeer * newPeer( GNet::StreamSocket * , GNet::Address ) ; + AdminServer( const AdminServer & ) ; + void operator=( const AdminServer & ) ; + +private: + bool m_allow_remote ; + std::string m_server_address ; +} ; + +#endif diff --git a/src/main/gclientprotocol.cpp b/src/main/gclientprotocol.cpp new file mode 100644 index 0000000..3d5e107 --- /dev/null +++ b/src/main/gclientprotocol.cpp @@ -0,0 +1,361 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gclientprotocol.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gsmtp.h" +#include "glocal.h" +#include "gfile.h" +#include "gstr.h" +#include "gmemory.h" +#include "gclientprotocol.h" +#include "gresolve.h" +#include "glog.h" +#include "gassert.h" + +GSmtp::ClientProtocol::ClientProtocol( Sender & sender , const std::string & thishost ) : + m_state(sStart) , + m_thishost(thishost) , + m_sender(sender) , + m_callback(NULL) , + m_server_has_8bitmime(false) , + m_said_hello(false) +{ +} + +void GSmtp::ClientProtocol::start( const std::string & from , const G::Strings & to , bool eight_bit , + std::auto_ptr content , Callback & callback ) +{ + G_DEBUG( "GSmtp::ClientProtocol::start" ) ; + m_to = to ; + m_from = from ; + m_content = content ; + m_callback = &callback ; + m_server_has_8bitmime = false ; + m_message_is_8bit = eight_bit ; + m_reply = Reply() ; + if( m_state != sStart && m_state != sEnd ) + throw NotReady() ; + + if( m_said_hello ) + { + m_state = sSentMail ; + sendMail() ; + } + else + { + m_state = sSentEhlo ; + send( std::string("EHLO ") + m_thishost ) ; + } +} + +bool GSmtp::ClientProtocol::done() const +{ + return m_state == sEnd ; +} + +void GSmtp::ClientProtocol::sendComplete() +{ + if( m_state == sData ) + { + size_t n = 0U ; + while( sendLine() ) + n++ ; + + G_LOG( "GSmtp::ClientProtocol: tx>>: [" << n << " line(s) of content]" ) ; + if( endOfContent() ) + { + m_state = sDone ; + send(".",true) ; + } + } +} + +//static +bool GSmtp::ClientProtocol::parseReply( Reply & stored_reply , const std::string & rx , std::string & reason ) +{ + Reply this_reply = Reply( rx ) ; + if( ! this_reply.validFormat() ) + { + stored_reply = Reply() ; + reason = "invalid reply format" ; + return false ; + } + else if( stored_reply.validFormat() && stored_reply.incomplete() ) + { + if( ! stored_reply.add(this_reply) ) + { + stored_reply = Reply() ; + reason = "invalid continuation line" ; + return false ; + } + } + else + { + stored_reply = this_reply ; + } + return ! stored_reply.incomplete() ; +} + +void GSmtp::ClientProtocol::apply( const std::string & rx ) +{ + G_LOG( "GSmtp::ClientProtocol: rx<<: \"" << G::Str::toPrintableAscii(rx) << "\"" ) ; + + std::string reason ; + bool complete = parseReply( m_reply , rx , reason ) ; + if( complete ) + { + applyEvent( m_reply ) ; + } + else + { + if( reason.length() != 0U ) + send( std::string("550 syntax error: ")+reason ) ; + } +} + +void GSmtp::ClientProtocol::sendMail() +{ + std::string mail_from = std::string("MAIL FROM:<") + m_from + ">" ; + if( m_server_has_8bitmime ) + { + mail_from.append( " BODY=8BITMIME" ) ; + } + else if( m_message_is_8bit ) + { + throw NarrowPipe() ; // (could do better) + } + send( mail_from ) ; +} + +void GSmtp::ClientProtocol::applyEvent( const Reply & reply ) +{ + if( reply.is(Reply::ServiceReady_220) ) + { + ; // no-op + } + else if( m_state == sReset ) + { + m_state = sStart ; + m_said_hello = false ; + } + else if( m_state == sStart ) + { + ; + } + else if( m_state == sSentEhlo && reply.is(Reply::SyntaxError_500) ) + { + m_state = sSentHelo ; + send( std::string("HELO ") + m_thishost ) ; + } + else if( (m_state==sSentEhlo || m_state==sSentHelo) && reply.is(Reply::Ok_250) ) + { + m_server_has_8bitmime = m_state == sSentEhlo && reply.textContains("8BITMIME") ; + m_said_hello = true ; + + m_state = sSentMail ; + sendMail() ; + } + else if( m_state == sSentMail && reply.is(Reply::Ok_250) ) + { + if( m_to.size() == 0U ) + { + // should never get here -- messages with no remote recipients + // are filtered out by the message store + throw NoRecipients() ; + } + + m_state = sSentRcpt ; + send( std::string("RCPT TO:<") + m_to.front() + std::string(">") ) ; + m_to.pop_front() ; + } + else if( m_state == sSentRcpt && m_to.size() != 0U && reply.positive() ) + { + send( std::string("RCPT TO:<") + m_to.front() + std::string(">") ) ; + m_to.pop_front() ; + } + else if( m_state == sSentRcpt && reply.positive() ) + { + m_state = sSentData ; + send( std::string("DATA") ) ; + } + else if( m_state == sSentData && reply.is(Reply::OkForData_354) ) + { + m_state = sData ; + + size_t n = 0U ; + while( sendLine() ) + n++ ; + + G_LOG( "GSmtp::ClientProtocol: tx>>: [" << n << " line(s) of content]" ) ; + if( endOfContent() ) + { + m_state = sDone ; + send( "." , true ) ; + } + } + else if( m_state == sDone && reply.is(Reply::Ok_250) ) + { + m_state = sEnd ; + if( m_callback ) + { + Callback * cb = m_callback ; + m_callback = NULL ; + cb->callback( true ) ; + } + } + else + { + G_WARNING( "GSmtp::ClientProtocol: protocol error: " << static_cast(m_state) ) ; + m_state = sReset ; + send( "RSET" ) ; + } +} + +bool GSmtp::ClientProtocol::sendLine() +{ + std::string line = G::Str::readLineFrom( *(m_content.get()) , crlf() ) ; + if( m_content->good() ) + { + return send( line , false , false ) ; + } + else + { + return false ; + } +} + +bool GSmtp::ClientProtocol::endOfContent() const +{ + return !m_content->good() ; +} + +bool GSmtp::ClientProtocol::send( const std::string & line , bool eot , bool log ) +{ + if( log ) + G_LOG( "GSmtp::ClientProtocol: tx>>: \"" << G::Str::toPrintableAscii(line) << "\"" ) ; + + if( !eot && line.length() && line.at(0U) == '.' ) + return m_sender.protocolSend( std::string(".")+line+crlf() ) ; + else + return m_sender.protocolSend( line + crlf() ) ; +} + +//static +std::string GSmtp::ClientProtocol::crlf() +{ + return std::string("\015\012") ; +} + +// === + +GSmtp::ClientProtocolReply::ClientProtocolReply( const std::string & line ) : + m_valid(false) , + m_complete(false) +{ + if( line.length() >= 3U && + is_digit(line.at(0U)) && + line.at(0U) <= '5' && + is_digit(line.at(1U)) && + is_digit(line.at(2U)) && + ( line.length() == 3U || line.at(3U) == ' ' || line.at(3U) == '-' ) ) + { + m_valid = true ; + m_complete = line.length() == 3U || line.at(3U) == ' ' ; + m_value = G::Str::toUInt( line.substr(0U,3U) ) ; + if( line.length() > 3U ) + { + m_text = line.substr(3U) ; + G::Str::trimLeft( m_text , " \t" ) ; + } + } +} + +bool GSmtp::ClientProtocolReply::validFormat() const +{ + return m_valid ; +} + +bool GSmtp::ClientProtocolReply::incomplete() const +{ + return ! m_complete ; +} + +bool GSmtp::ClientProtocolReply::positive() const +{ + return m_valid && m_value < 400U ; +} + +unsigned int GSmtp::ClientProtocolReply::value() const +{ + return m_valid ? m_value : 0 ; +} + +bool GSmtp::ClientProtocolReply::is( Value v ) const +{ + return value() == static_cast( v ) ; +} + +std::string GSmtp::ClientProtocolReply::text() const +{ + return m_text ; +} + +//static +bool GSmtp::ClientProtocolReply::is_digit( char c ) +{ + return c >= '0' && c <= '9' ; +} + +GSmtp::ClientProtocolReply::Type GSmtp::ClientProtocolReply::type() const +{ + G_ASSERT( m_valid && (m_value/100U) >= 1U && (m_value/100U) <= 5U ) ; + return static_cast( m_value / 100U ) ; +} + +GSmtp::ClientProtocolReply::SubType GSmtp::ClientProtocolReply::subType() const +{ + unsigned int n = ( m_value / 10U ) % 10U ; + if( n < 4U ) + return static_cast( n ) ; + else + return Invalid_SubType ; +} + +bool GSmtp::ClientProtocolReply::add( const ClientProtocolReply & other ) +{ + G_ASSERT( other.m_valid ) ; + G_ASSERT( m_valid ) ; + G_ASSERT( !m_complete ) ; + + m_complete = other.m_complete ; + m_text.append( std::string("\n") + other.text() ) ; + return value() == other.value() ; +} + +bool GSmtp::ClientProtocolReply::textContains( std::string key ) const +{ + G::Str::toUpper(key) ; + return m_text.find(key) != std::string::npos ; +} + diff --git a/src/main/gclientprotocol.h b/src/main/gclientprotocol.h new file mode 100644 index 0000000..faf55ee --- /dev/null +++ b/src/main/gclientprotocol.h @@ -0,0 +1,180 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gclientprotocol.h +// + +#ifndef G_SMTP_CLIENT_PROTOCOL_H +#define G_SMTP_CLIENT_PROTOCOL_H + +#include "gdef.h" +#include "gnet.h" +#include "gsmtp.h" +#include "gmessagestore.h" +#include "gstrings.h" +#include "gexception.h" +#include +#include + +namespace GSmtp +{ + class ClientProtocol ; + class ClientProtocolReply ; +} ; + +// Class: GSmtp::ClientProtocolReply +// Description: A private implementation class used +// by ClientProtocol. +// +class GSmtp::ClientProtocolReply +{ +public: + enum Type + { + PositivePreliminary = 1 , + PositiveCompletion = 2 , + PositiveIntermediate = 3 , + TransientNegative = 4 , + PermanentNegative = 5 + } ; + enum SubType + { + Syntax = 0 , + Information = 1 , + Connections = 2 , + MailSystem = 3 , + Invalid_SubType = 4 + } ; + enum Value + { + Invalid = 0 , + ServiceReady_220 = 220 , + SyntaxError_500 = 500 , + BadSequence_503 = 503 , + NotImplemented_502 = 502 , + OkForData_354 = 354 , + Ok_250 = 250 + } ; + explicit ClientProtocolReply( const std::string & line = std::string() ) ; + bool incomplete() const ; + bool add( const ClientProtocolReply & other ) ; + bool validFormat() const ; + bool positive() const ; // <400 + bool is( Value v ) const ; + unsigned int value() const ; + std::string text() const ; + Type type() const ; + SubType subType() const ; + bool textContains( std::string s ) const ; +private: + bool m_complete ; + bool m_valid ; + unsigned int m_value ; + std::string m_text ; +private: + static bool is_digit( char ) ; +} ; + +// Class: GSmtp::ClientProtocol +// Description: Implements the client-side SMTP protocol. +// +class GSmtp::ClientProtocol +{ +public: + G_EXCEPTION( NotReady , "not ready" ) ; + G_EXCEPTION( NoRecipients , "no recipients" ) ; + G_EXCEPTION( NarrowPipe , "cannot send an 8-bit message to a 7-bit server" ) ; + typedef ClientProtocolReply Reply ; + + class Sender // An interface used by ClientProtocol to send protocol messages. + { + public: virtual bool protocolSend( const std::string & ) = 0 ; + // Called by the Protocol class to send + // network data to the peer. + // + // Returns false if not all of the string + // was sent, either due to flow control + // or disconnection. After false os returned + // the user should call sendComplete() once + // the full string has been sent. + + private: void operator=( const Sender & ) ; + public: virtual ~Sender() {} + } ; + + class Callback // A callback interface used by ClientProtocol. + { + public: virtual void callback( bool ok ) = 0 ; + // Called once the protocol has finished with + // a given message. See ClientProtocol::start(). + + private: void operator=( const Callback & ) ; + public: virtual ~Callback() {} + } ; + + ClientProtocol( Sender & sender , const std::string & thishost ) ; + // Constructor. The sender reference is kept. + // The Sender interface is used to send + // protocol messages to the peer. + + void start( const std::string & from , const G::Strings & to , bool eight_bit , + std::auto_ptr content , Callback & callback ) ; + // Starts transmission of the given message. + // + // The 'callback' parameter is used to + // signal that the message has been + // processed. + + void sendComplete() ; + // Called when a blocked connection becomes unblocked. + // See ClientProtocol::Sender::protocolSend(). + + void apply( const std::string & rx ) ; + // Called on receipt of a line of text from the server. + + bool done() const ; + // Returns true if the protocol is in the end state. + +private: + bool send( const std::string & , bool eot = false , bool log = true ) ; + bool sendLine() ; + void sendMail() ; + bool endOfContent() const ; + static std::string crlf() ; + void applyEvent( const Reply & event ) ; + static bool parseReply( Reply & , const std::string & , std::string & ) ; + +private: + enum State { sStart , sSentEhlo , sSentHelo , sSentMail , + sSentRcpt , sSentData , sData , sDone , sEnd , sReset } ; + Sender & m_sender ; + std::string m_thishost ; + State m_state ; + std::string m_from ; + G::Strings m_to ; + Callback * m_callback ; + std::auto_ptr m_content ; + bool m_server_has_8bitmime ; + bool m_said_hello ; + bool m_message_is_8bit ; + Reply m_reply ; +} ; + +#endif diff --git a/src/main/gmessagestore.cpp b/src/main/gmessagestore.cpp new file mode 100644 index 0000000..140364f --- /dev/null +++ b/src/main/gmessagestore.cpp @@ -0,0 +1,755 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gmessagestore.cpp +// + +#include "gdef.h" +#include "gsmtp.h" +#include "gpid.h" +#include "gdirectory.h" +#include "gmessagestore.h" +#include "gmemory.h" +#include "gpath.h" +#include "gfile.h" +#include "gstr.h" +#include "glog.h" +#include "gassert.h" +#include +#include + +// Class: MessageStoreImp +// Description: Pimple-pattern implementation for MessageStore. +// Passes out unique sequence numbers, filesystem paths and +// i/o streams to NewMessageImp. +// +class GSmtp::MessageStoreImp +{ +private: + G::Path m_dir ; + unsigned long m_seq ; +public: + MessageStoreImp( const G::Path & dir ) ; + + // new message... + static void checkPath( const G::Path & ) ; + const G::Path & dir() const ; + unsigned long newSeq() ; + std::auto_ptr stream( const G::Path & path ); + G::Path contentPath( unsigned long seq ) const ; + G::Path envelopePath( unsigned long seq ) const ; + G::Path envelopeWorkingPath( unsigned long seq ) const ; + + // stored message... + bool empty() const ; + std::auto_ptr get() ; + MessageStore::Iterator iterator() ; + + // both... + static std::string x() ; + static std::string format() ; +private: + G::Path fullPath( const std::string & filename ) const ; + std::string filePrefix( unsigned long seq ) const ; + std::string getline( std::istream & ) const ; + std::string value( const std::string & ) const ; + std::string crlf() const ; +} ; + +// === + +// Class: StoredMessageImp +// Description: A concete derived class implementing the +// StoredMessage (parent) and Callback (grandparent) interfaces. +// +class GSmtp::StoredMessageImp : public GSmtp::StoredMessage +{ +public: + G_EXCEPTION( InvalidFormat , "invalid format field in envelope" ) ; + G_EXCEPTION( NoEnd , "invalid envelope file: no end marker" ) ; + G_EXCEPTION( InvalidTo , "invalid 'to' line in envelope file" ) ; + G_EXCEPTION( NoRecipients , "no remote recipients" ) ; + G_EXCEPTION( OpenError , "cannot open the envelope" ) ; + G_EXCEPTION( StreamError , "envelope reading/parsing error" ) ; + explicit StoredMessageImp( const G::Path & envelope_path ) ; + virtual ~StoredMessageImp() ; + bool lock() ; + bool readEnvelope( std::string & reason ) ; + bool readEnvelopeCore( std::string & reason ) ; + bool openContent( std::string & reason ) ; + virtual bool eightBit() const ; + virtual const std::string & from() const ; + virtual const G::Strings & to() const ; + virtual void destroy() ; + virtual void fail( const std::string & reason ) ; + virtual std::auto_ptr extractContentStream() ; + +private: + StoredMessageImp( const StoredMessageImp & ) ; + void operator=( const StoredMessageImp & ) ; + std::string crlf() const ; + std::string getline( std::istream & stream ) const ; + std::string value( const std::string & s ) const ; + G::Path contentPath() const ; + +private: + G::Strings m_to_local ; + G::Strings m_to_remote ; + std::string m_from ; + G::Path m_envelope_path ; + std::auto_ptr m_content ; + bool m_eight_bit ; +} ; + +// === + +// Class: NewMessageImp +// Description: A concrete derived class implementing the +// NewMessage interface. Writes itself to the i/o streams +// supplied by MessageStoreImp. +// +class GSmtp::NewMessageImp : public GSmtp::NewMessage +{ +public: + NewMessageImp( const std::string & from , MessageStoreImp & store ) ; + virtual ~NewMessageImp() ; + virtual void addTo( const std::string & to , bool local ) ; + virtual void addText( const std::string & line ) ; + virtual void store() ; +private: + MessageStoreImp & m_store ; + unsigned long m_seq ; + std::string m_from ; + G::Strings m_to_local ; + G::Strings m_to_remote ; + std::auto_ptr m_content ; + G::Path m_content_path ; + bool m_eight_bit ; +private: + bool saveEnvelope( std::ostream & stream , const std::string & where ) const ; + std::string crlf() const ; + static bool isEightBit( const std::string & line ) ; + void deliver( const G::Strings & , const G::Path & , const G::Path & , const G::Path & ) ; +} ; + +// === + +// Class: MessageStoreIteratorImp +// Description: A 'body' class for the MessageStoreIterator +// 'handle'. The handle/body pattern allows us to copy +// iterators by value, and therefore return them +// from MessageStore::iterator(). +// +class GSmtp::MessageStoreIteratorImp +{ +public: + explicit MessageStoreIteratorImp( const G::Directory & dir ) ; + std::auto_ptr next() ; +public: + unsigned long m_ref_count ; +private: + G::DirectoryIterator m_iter ; +private: + MessageStoreIteratorImp( const MessageStoreIteratorImp & ) ; + void operator=( const MessageStoreIteratorImp & ) ; +} ; + +// === + +GSmtp::MessageStoreIteratorImp::MessageStoreIteratorImp( const G::Directory & dir ) : + m_iter( dir , "*.envelope" ) , + m_ref_count(1UL) +{ +} + +std::auto_ptr GSmtp::MessageStoreIteratorImp::next() +{ + while( !m_iter.error() && m_iter.more() ) + { + std::auto_ptr m( new StoredMessageImp(m_iter.filePath()) ) ; + if( m->lock() ) + { + std::string reason ; + if( m->readEnvelope(reason) && m->openContent(reason) ) + return std::auto_ptr( m.release() ) ; + + m->fail( reason ) ; + } + G_WARNING( "GSmtp::MessageStore: cannot process file: \"" << m_iter.filePath() << "\"" ) ; + } + return std::auto_ptr(NULL) ; +} + +// === + +GSmtp::MessageStore::Iterator::Iterator() : + m_imp(NULL) +{ +} + +GSmtp::MessageStore::Iterator::Iterator( MessageStoreIteratorImp * imp ) : + m_imp(imp) +{ + G_ASSERT( m_imp->m_ref_count == 1UL ) ; +} + +std::auto_ptr GSmtp::MessageStore::Iterator::next() +{ + return m_imp ? m_imp->next() : std::auto_ptr(NULL) ; +} + +GSmtp::MessageStore::Iterator::~Iterator() +{ + if( m_imp ) + { + m_imp->m_ref_count-- ; + if( m_imp->m_ref_count == 0UL ) + delete m_imp ; + } +} + +GSmtp::MessageStore::Iterator::Iterator( const Iterator & other ) : + m_imp(other.m_imp) +{ + if( m_imp ) + m_imp->m_ref_count++ ; +} + +GSmtp::MessageStore::Iterator & GSmtp::MessageStore::Iterator::operator=( const Iterator & rhs ) +{ + if( this != &rhs ) + { + if( m_imp ) + { + m_imp->m_ref_count-- ; + if( m_imp->m_ref_count == 0UL ) + delete m_imp ; + } + m_imp = rhs.m_imp ; + if( m_imp ) + { + m_imp->m_ref_count++ ; + } + } + return * this ; +} + +// === + +GSmtp::StoredMessage::~StoredMessage() +{ + // empty +} + +// === + +GSmtp::NewMessage::~NewMessage() +{ + // empty +} + +// === + +GSmtp::MessageStore * GSmtp::MessageStore::m_this = NULL ; + +GSmtp::MessageStore::MessageStore( const G::Path & directory_path ) : + m_imp(NULL) +{ + if( m_this == NULL ) + m_this = this ; + + MessageStoreImp::checkPath( directory_path ) ; + m_imp = new MessageStoreImp( directory_path ) ; +} + +GSmtp::MessageStore & GSmtp::MessageStore::instance() +{ + if( m_this == NULL ) + throw NoInstance() ; + return * m_this ; +} + +GSmtp::MessageStore::~MessageStore() +{ + if( m_this == this ) + m_this = NULL ; + delete m_imp ; +} + +G::Path GSmtp::MessageStore::directory() const +{ + return m_imp->dir() ; +} + +std::auto_ptr GSmtp::MessageStore::newMessage( const std::string & from ) +{ + std::auto_ptr new_message( new NewMessageImp(from,*m_imp) ) ; + return new_message ; +} + +bool GSmtp::MessageStore::empty() const +{ + return m_imp->empty() ; +} + +std::auto_ptr GSmtp::MessageStore::get() +{ + return m_imp->get() ; +} + +GSmtp::MessageStore::Iterator GSmtp::MessageStore::iterator() +{ + return m_imp->iterator() ; +} + +// === + +GSmtp::NewMessageImp::NewMessageImp( const std::string & from , MessageStoreImp & store ) : + m_from(from) , + m_store(store), + m_eight_bit(false) +{ + m_seq = store.newSeq() ; + + m_content_path = m_store.contentPath( m_seq ) ; + G_LOG( "GSmtp::NewMessage: content file: " << m_content_path ) ; + + std::auto_ptr content_stream = m_store.stream( m_content_path ) ; + m_content = content_stream ; +} + +GSmtp::NewMessageImp::~NewMessageImp() +{ +} + +void GSmtp::NewMessageImp::addTo( const std::string & to , bool local ) +{ + if( local ) + m_to_local.push_back( to ) ; + else + m_to_remote.push_back( to ) ; +} + +void GSmtp::NewMessageImp::addText( const std::string & line ) +{ + if( ! m_eight_bit ) + m_eight_bit = isEightBit(line) ; + + *(m_content.get()) << line << crlf() ; +} + +bool GSmtp::NewMessageImp::isEightBit( const std::string & line ) +{ + const size_t n = line.length() ; + for( size_t i = 0U ; i < n ; --i ) + { + const unsigned char c = static_cast(line.at(i)) ; + if( c > 0x7fU ) + return true ; + } + return false ; +} + +void GSmtp::NewMessageImp::store() +{ + // flush the content file + m_content->flush() ; + if( ! m_content->good() ) + throw GSmtp::MessageStore::WriteError( m_content_path.str() ) ; + m_content <<= 0 ; + + // write the envelope + G::Path p0 = m_store.envelopeWorkingPath( m_seq ) ; + G::Path p1 = m_store.envelopePath( m_seq ) ; + bool ok = false ; + { + std::auto_ptr envelope_stream = m_store.stream( p0 ) ; + ok = saveEnvelope( *(envelope_stream.get()) , p0.str() ) ; + } + + // deliver to local mailboxes (in theory) + if( ok && m_to_local.size() != 0U ) + { + deliver( m_to_local , m_content_path , p0 , p1 ) ; + } + + // commit the envelope, or rollback the content + if( ! ok || ! G::File::rename(p0,p1,G::File::NoThrow() ) ) + { + G_ASSERT( m_content_path.str().length() != 0U ) ; + G::File::remove( m_content_path , G::File::NoThrow() ) ; + throw GSmtp::MessageStore::WriteError( p0.str() ) ; + } +} + +void GSmtp::NewMessageImp::deliver( const G::Strings & to , + const G::Path & content_path , const G::Path & envelope_path_now , + const G::Path & envelope_path_later ) +{ + // could shell out to "procmail" or "deliver" here, but keep it + // simple and within the scope of a "message-store" class + + G_LOG( "GSmtp::NewMessage: copying message for local recipient(s): " + << content_path.basename() << ".local" ) ; + + G::File::copy( content_path.str() , content_path.str()+".local" ) ; + G::File::copy( envelope_path_now.str() , envelope_path_later.str()+".local" ) ; +} + +bool GSmtp::NewMessageImp::saveEnvelope( std::ostream & stream , const std::string & where ) const +{ + G_LOG( "GSmtp::NewMessage: envelope file: " << where ) ; + + const std::string x( MessageStoreImp::x() ) ; + + stream << x << "Format: " << MessageStoreImp::format() << crlf() ; + stream << x << "Content: " << (m_eight_bit?"8":"7") << "bit" << crlf() ; + stream << x << "From: " << m_from << crlf() ; + stream << x << "ToCount: " << (m_to_local.size()+m_to_remote.size()) << crlf() ; + { + G::Strings::const_iterator to_p = m_to_local.begin() ; + for( ; to_p != m_to_local.end() ; ++to_p ) + stream << x << "To-Local: " << *to_p << crlf() ; + } + { + G::Strings::const_iterator to_p = m_to_remote.begin() ; + for( ; to_p != m_to_remote.end() ; ++to_p ) + stream << x << "To-Remote: " << *to_p << crlf() ; + } + stream << x << "End: 1" << crlf() ; + stream.flush() ; + return stream.good() ; +} + +std::string GSmtp::NewMessageImp::crlf() const +{ + return std::string( "\015\012" ) ; +} + +// === + +GSmtp::MessageStoreImp::MessageStoreImp( const G::Path & dir ) : + m_dir(dir), + m_seq(1UL) +{ +} + +//static +std::string GSmtp::MessageStoreImp::x() +{ + return "X-MailRelay-" ; +} + +//static +std::string GSmtp::MessageStoreImp::format() +{ + return "#2821.2" ; +} + +const G::Path & GSmtp::MessageStoreImp::dir() const +{ + return m_dir ; +} + +//static +void GSmtp::MessageStoreImp::checkPath( const G::Path & directory_path ) +{ + // (void) G::File::mkdir( directory_path ) ; + G::Directory dir_test( directory_path ) ; + if( ! dir_test.valid() ) + { + throw MessageStore::InvalidDirectory( directory_path.str() ) ; + } + if( ! dir_test.valid(true) ) + { + G_WARNING( "GSmtp::MessageStore: " + << "directory not writable: \"" + << directory_path << "\"" ) ; + } +} + +std::auto_ptr GSmtp::MessageStoreImp::stream( const G::Path & path ) +{ + std::auto_ptr ptr( + new std::ofstream( path.pathCstr() , + std::ios_base::binary | std::ios_base::out | std::ios_base::trunc ) ) ; + return ptr ; +} + +G::Path GSmtp::MessageStoreImp::contentPath( unsigned long seq ) const +{ + return fullPath( filePrefix(seq) + ".content" ) ; +} + +G::Path GSmtp::MessageStoreImp::envelopePath( unsigned long seq ) const +{ + return fullPath( filePrefix(seq) + ".envelope" ) ; +} + +G::Path GSmtp::MessageStoreImp::envelopeWorkingPath( unsigned long seq ) const +{ + return fullPath( filePrefix(seq) + ".envelope.new" ) ; +} + +std::string GSmtp::MessageStoreImp::filePrefix( unsigned long seq ) const +{ + std::stringstream ss ; + ss << "emailrelay." << G::Pid() << "." << seq ; + return ss.str() ; +} + +G::Path GSmtp::MessageStoreImp::fullPath( const std::string & filename ) const +{ + G::Path p( m_dir ) ; + p.pathAppend( filename ) ; + return p ; +} + +unsigned long GSmtp::MessageStoreImp::newSeq() +{ + return m_seq++ ; +} + +bool GSmtp::MessageStoreImp::empty() const +{ + G::Directory dir(m_dir) ; + G::DirectoryIterator iter( dir , "*.envelope" ) ; + bool no_more = iter.error() || !iter.more() ; + if( no_more ) + G_DEBUG( "GSmtp::MessageStoreImp: no files to process" ) ; + return no_more ; +} + +GSmtp::MessageStore::Iterator GSmtp::MessageStoreImp::iterator() +{ + return MessageStore::Iterator( new MessageStoreIteratorImp(G::Directory(m_dir)) ) ; +} + +std::auto_ptr GSmtp::MessageStoreImp::get() +{ + MessageStore::Iterator iter = iterator() ; + return iter.next() ; +} + +// === + +GSmtp::StoredMessageImp::StoredMessageImp( const G::Path & path ) : + m_envelope_path(path) +{ + G_DEBUG( "StoredMessageImp: \"" << path << "\"" ) ; +} + +GSmtp::StoredMessageImp::~StoredMessageImp() +{ +} + +bool GSmtp::StoredMessageImp::eightBit() const +{ + return m_eight_bit ; +} + +bool GSmtp::StoredMessageImp::readEnvelope( std::string & reason ) +{ + try + { + return readEnvelopeCore( reason ) ; + } + catch( std::exception & e ) + { + reason = e.what() ; + return false ; + } +} + +bool GSmtp::StoredMessageImp::readEnvelopeCore( std::string & reason ) +{ + std::ifstream stream( m_envelope_path.str().c_str() , std::ios_base::binary | std::ios_base::in ) ; + if( ! stream.good() ) + throw OpenError() ; + + std::string format_line = getline(stream) ; + if( value(format_line) != MessageStoreImp::format() ) + throw InvalidFormat( value(format_line) + "!=" + MessageStoreImp::format() ) ; + + std::string content_line = getline(stream) ; + m_eight_bit = value(content_line) == "8bit" ; + + m_from = value(getline(stream)) ; + G_DEBUG( "GSmtp::StoredMessageImp::readEnvelope: from \"" << m_from << "\"" ) ; + + std::string to_count_line = getline(stream) ; + unsigned int to_count = G::Str::toUInt( value(to_count_line) ) ; + + for( unsigned int i = 0U ; i < to_count ; i++ ) + { + std::string to_line = getline(stream) ; + bool is_local = to_line.find(MessageStoreImp::x()+"To-Local") == 0U ; + bool is_remote = to_line.find(MessageStoreImp::x()+"To-Remote") == 0U ; + if( ! is_local && ! is_remote ) + throw InvalidTo(to_line) ; + + G_DEBUG( "GSmtp::StoredMessageImp::readEnvelope: to " + "[" << (i+1U) << "/" << to_count << "] " + "(" << (is_local?"local":"remote") << ") " + << "\"" << value(to_line) << "\"" ) ; + + if( is_local ) + m_to_local.push_back( value(to_line) ) ; + else + m_to_remote.push_back( value(to_line) ) ; + } + + std::string end = getline(stream) ; + if( end.find(MessageStoreImp::x()+"End") != 0U ) + throw NoEnd() ; + + if( m_to_remote.size() == 0U ) + throw NoRecipients() ; + + if( ! stream.good() ) + throw StreamError() ; + + return stream.good() ; +} + +bool GSmtp::StoredMessageImp::openContent( std::string & reason ) +{ + try + { + G::Path content_path = contentPath() ; + G_DEBUG( "GSmtp::MessageStoreImp::openContent: \"" << content_path << "\"" ) ; + std::auto_ptr stream( new std::ifstream( + content_path.str().c_str() , std::ios_base::in | std::ios_base::binary ) ) ; + if( !stream->good() ) + { + reason = "cannot open content file" ; + return false ; + } + + G_LOG( "GSmtp::MessageStore: processing envelope \"" << m_envelope_path.basename() << "\"" ) ; + G_LOG( "GSmtp::MessageStore: processing content \"" << content_path.basename() << "\"" ) ; + + m_content = stream ; + return true ; + } + catch( std::exception & e ) + { + G_WARNING( "GSmtp::MessageStoreImp: exception: " << e.what() ) ; + reason = e.what() ; + return false ; + } +} + +std::string GSmtp::StoredMessageImp::getline( std::istream & stream ) const +{ + return G::Str::readLineFrom( stream , crlf() ) ; +} + +std::string GSmtp::StoredMessageImp::value( const std::string & s ) const +{ + size_t pos = s.find(' ') ; + if( pos == std::string::npos ) + throw MessageStore::FormatError() ; + return s.substr(pos+1U) ; +} + +std::string GSmtp::StoredMessageImp::crlf() const +{ + return std::string( "\015\012" ) ; +} + +bool GSmtp::StoredMessageImp::lock() +{ + G::Path & src = m_envelope_path ; + G::Path dst( src.str() + ".busy" ) ; + bool ok = G::File::rename( src , dst , G::File::NoThrow() ) ; + if( ok ) + { + G_LOG( "GSmtp::StoredMessage: locking file \"" << src.basename() << "\"" ) ; + m_envelope_path = dst ; + } + return ok ; +} + +void GSmtp::StoredMessageImp::fail( const std::string & reason ) +{ + try + { + { + std::ofstream file( m_envelope_path.str().c_str() , + std::ios_base::binary | std::ios_base::ate ) ; + file << MessageStoreImp::x() << "Reason: " << reason ; + } + + G::Path env_temp( m_envelope_path ) ; // "foo.envelope.busy" + env_temp.removeExtension() ; // "foo.envelope" + G::Path bad( env_temp.str() + ".bad" ) ; // "foo.envelope.bad" + G_LOG( "GSmtp::StoredMessage: failing file: " + << "\"" << m_envelope_path.basename() << "\" -> " + << "\"" << bad.basename() << "\"" ) ; + + G::File::rename( m_envelope_path , bad , G::File::NoThrow() ) ; + } + catch(...) + { + } +} + +void GSmtp::StoredMessageImp::destroy() +{ + try + { + G_LOG( "GSmtp::StoredMessage: deleting file: \"" << m_envelope_path.basename() << "\"" ) ; + G::File::remove( m_envelope_path , G::File::NoThrow() ) ; + + G::Path content_path = contentPath() ; + G_LOG( "GSmtp::StoredMessage: deleting file: \"" << content_path.basename() << "\"" ) ; + m_content <<= 0 ; // close it first + G::File::remove( content_path , G::File::NoThrow() ) ; + } + catch(...) + { + } +} + +const std::string & GSmtp::StoredMessageImp::from() const +{ + return m_from ; +} + +const G::Strings & GSmtp::StoredMessageImp::to() const +{ + return m_to_remote ; +} + +std::auto_ptr GSmtp::StoredMessageImp::extractContentStream() +{ + G_ASSERT( m_content.get() != NULL ) ; + return m_content ; +} + +G::Path GSmtp::StoredMessageImp::contentPath() const +{ + G::Path path( m_envelope_path ) ; // "foo.envelope.busy" + path.removeExtension() ; // "foo.envelope" + path.setExtension( "content" ) ; // "foo.content" + return path ; +} + diff --git a/src/main/gmessagestore.h b/src/main/gmessagestore.h new file mode 100644 index 0000000..616bb78 --- /dev/null +++ b/src/main/gmessagestore.h @@ -0,0 +1,189 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gmessagestore.h +// + +#ifndef G_SMTP_MESSAGE_STORE_H +#define G_SMTP_MESSAGE_STORE_H + +#include "gdef.h" +#include "gsmtp.h" +#include "gexception.h" +#include "gstrings.h" +#include "gpath.h" + +namespace GSmtp +{ + class MessageStore ; + class StoredMessage ; + class NewMessage ; + + class MessageStoreImp ; + class MessageStoreIteratorImp ; + class StoredMessageImp ; + class NewMessageImp ; +} ; + +// Class: GSmtp::NewMessage +// Description: An abstract class to allow the creation +// of a new message in the message store. +// See also: MessageStore, MessageStore::newMessage() +// +class GSmtp::NewMessage +{ +public: + virtual void addTo( const std::string & to , bool local ) = 0 ; + // Adds a 'to' address. + + virtual void addText( const std::string & line ) = 0 ; + // Adds a line of content. + + virtual void store() = 0 ; + // Stores the message in the message store. + + virtual ~NewMessage() ; + // Destructor. + +private: + void operator=( const NewMessage & ) ; +} ; + +// Class: GSmtp::StoredMessage +// Description: An abstract class for messages which have +// come from the store. +// See also: MessageStore, MessageStore::get() +// +class GSmtp::StoredMessage +{ +public: + virtual const std::string & from() const = 0 ; + // Returns the envelope 'from' field. + + virtual const G::Strings & to() const = 0 ; + // Returns the envelope 'to' fields. + + virtual std::auto_ptr extractContentStream() = 0 ; + // Extracts the content stream. + // Can only be called once. + + virtual void destroy() = 0 ; + // Deletes the message within the store. + + virtual void fail( const std::string & reason ) = 0 ; + // Marks the message as failed within the store. + + virtual bool eightBit() const = 0 ; + // Returns true if the message content (header+body) + // contains a character with the most significant + // bit set. + + virtual ~StoredMessage() ; + // Destructor. + +private: + void operator=( const StoredMessage & ) ; +} ; + +// Class: GSmtp::MessageStore +// Description: A singleton class which allows SMTP messages +// (envelope+content) to be stored and retrieved. +// +// The implementation puts separate envelope and content +// files in a spool directory. The content file is +// written first. The presence of a matching envelope +// file is used to indicate that the content file +// is valid and that it has been commited to the +// care of the SMTP system for delivery. +// +// See also: NewMessage, StoredMessage, ProtocolMessage +// +class GSmtp::MessageStore +{ +public: + G_EXCEPTION( InvalidDirectory , "invalid spool directory" ) ; + G_EXCEPTION( WriteError , "error writing file" ) ; + G_EXCEPTION( NoInstance , "no message store instance" ) ; + G_EXCEPTION( FormatError , "format error" ) ; + class Iterator // An iterator class for GSmtp::MessageStore. + { + public: std::auto_ptr next() ; + private: MessageStoreIteratorImp * m_imp ; + public: Iterator() ; + public: explicit Iterator( MessageStoreIteratorImp * ) ; + public: ~Iterator() ; + public: Iterator( const Iterator & ) ; + public: Iterator & operator=( const Iterator & ) ; + } ; + + static MessageStore & instance() ; + // Singleton access. + + static G::Path defaultDirectory() ; + // Returns a default spool directory, such as + // "/usr/local/var/spool/emailrelay". (Typically + // has an os-specific implementation.) + + explicit MessageStore( const G::Path & directory ) ; + // Constructor. Throws exceptions if + // not a valid storage directory. + + ~MessageStore() ; + // Destructor. + + std::auto_ptr newMessage( const std::string & from ) ; + // Creates a new message. + + bool empty() const ; + // Returns true if the message store is empty. + + std::auto_ptr get() ; + // Pulls a message out of the store (selected + // at random). Returns a NULL smart pointer + // if there are no messages to extract. + // + // As a side effect some stored messages may be + // marked as bad, or deleted (if they + // have no recipients). + + Iterator iterator() ; + // Returns a read iterator. (Note that copies of + // iterators share state. For independent iterators + // call iterator() for each.) + // + // As a side effect of iteration some stored + // messages may be marked as bad, or deleted (if + // they have no recipients). + + G::Path directory() const ; + // Returns the storage directory (as passed + // to the constructor). + +private: + MessageStore( const MessageStore & ) ; + void operator=( const MessageStore & ) ; + +private: + static MessageStore * m_this ; + MessageStoreImp * m_imp ; +} ; + +#endif + diff --git a/src/main/gmessagestore_unix.cpp b/src/main/gmessagestore_unix.cpp new file mode 100644 index 0000000..936b5ff --- /dev/null +++ b/src/main/gmessagestore_unix.cpp @@ -0,0 +1,34 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gmessagestore_unix.cpp +// + +#include "gdef.h" +#include "gsmtp.h" +#include "gmessagestore.h" +#include "gpath.h" + +//static +G::Path GSmtp::MessageStore::defaultDirectory() +{ + return G::Path( "/usr/local/var/spool/emailrelay" ) ; +} + diff --git a/src/main/gmessagestore_win32.cpp b/src/main/gmessagestore_win32.cpp new file mode 100644 index 0000000..88fb7f0 --- /dev/null +++ b/src/main/gmessagestore_win32.cpp @@ -0,0 +1,41 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gmessagestore_win32.cpp +// + +#include "gdef.h" +#include "gsmtp.h" +#include "gmessagestore.h" +#include "gpath.h" + +//static +G::Path GSmtp::MessageStore::defaultDirectory() +{ + char buffer[(MAX_PATH * 2U) + 1U] ; + if( 0 == ::GetWindowsDirectory( buffer , sizeof(buffer)-1U ) ) + buffer[0] = '\0' ; + + G::Path path( buffer ) ; + path.pathAppend( "spool" ) ; + path.pathAppend( "emailrelay" ) ; + return path ; +} + diff --git a/src/main/gprotocolmessage.cpp b/src/main/gprotocolmessage.cpp new file mode 100644 index 0000000..b7be1c7 --- /dev/null +++ b/src/main/gprotocolmessage.cpp @@ -0,0 +1,170 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gprotocolmessage.cpp +// + +#include "gdef.h" +#include "gsmtp.h" +#include "gprotocolmessage.h" +#include "gmessagestore.h" +#include "gmemory.h" +#include "gstr.h" +#include "gassert.h" +#include "glog.h" + +// Class: GSmtp::ProtocolMessageImp +// Description: A private pimple-pattern implementation class for GSmtp::ProtocolMessage. +// +class GSmtp::ProtocolMessageImp +{ +public: + std::auto_ptr m_msg ; +} ; + +// === + +GSmtp::ProtocolMessage::ProtocolMessage() : + m_imp(NULL) +{ + m_imp = new ProtocolMessageImp ; +} + +GSmtp::ProtocolMessage::~ProtocolMessage() +{ + delete m_imp ; +} + +void GSmtp::ProtocolMessage::clear() +{ + m_imp->m_msg <<= 0 ; +} + +bool GSmtp::ProtocolMessage::setFrom( const std::string & from ) +{ + try + { + if( from.length() == 0U ) + return false ; + + G_ASSERT( m_imp->m_msg.get() == NULL ) ; + clear() ; // just in case + std::auto_ptr new_msg = MessageStore::instance().newMessage( from ) ; + m_imp->m_msg = new_msg ; + return true ; + } + catch( std::exception & e ) + { + G_ERROR( "GSmtp::ProtocolMessage::setFrom: error: " << e.what() ) ; + return false ; + } +} + +bool GSmtp::ProtocolMessage::addTo( const std::string & to ) +{ + G_ASSERT( m_imp->m_msg.get() != NULL ) ; + if( to.length() > 0U && m_imp->m_msg.get() != NULL ) + { + bool is_local = isLocal(to) ; + if( is_local && !isValid(to) ) + { + G_WARNING( "GSmtp::ProtocolMessage: rejecting local recipent (not postmaster): " << to ) ; + return false ; + } + else + { + m_imp->m_msg->addTo( to , is_local ) ; + return true ; + } + } + else + { + return false ; + } +} + +void GSmtp::ProtocolMessage::addReceived( const std::string & line ) +{ + addText( line ) ; +} + +void GSmtp::ProtocolMessage::addText( const std::string & line ) +{ + G_ASSERT( m_imp->m_msg.get() != NULL ) ; + if( m_imp->m_msg.get() != NULL ) + m_imp->m_msg->addText( line ) ; +} + +std::string GSmtp::ProtocolMessage::process() +{ + try + { + G_ASSERT( m_imp->m_msg.get() != NULL ) ; + if( m_imp->m_msg.get() != NULL ) + { + m_imp->m_msg->store() ; + } + clear() ; + return std::string() ; + } + catch( std::exception & e ) + { + G_ERROR( "GSmtp::ProtocolMessage::process: error: " << e.what() ) ; + clear() ; + return std::string( e.what() ) ; + } +} + +std::pair GSmtp::ProtocolMessage::verify( const std::string & user ) +{ + G_DEBUG( "GSmtp::ProtocolMessage::verify: \"" << user << "\"" ) ; + std::pair rc( isLocal(user) , std::string() ) ; + if( isLocal(user) && isValid(user) ) + rc.second = fullName(user) ; + return rc ; +} + +//static +bool GSmtp::ProtocolMessage::isLocal( const std::string & user ) +{ + return user.find('@') == std::string::npos ; +} + +//static +bool GSmtp::ProtocolMessage::isValid( const std::string & user ) +{ + // only recognise one local mailbox + return isPostmaster(user) ; +} + +//static +bool GSmtp::ProtocolMessage::isPostmaster( std::string user ) +{ + G::Str::toUpper( user ) ; + G::Str::trim( user , " \t" ) ; + return user == "POSTMASTER" ; +} + +//static +std::string GSmtp::ProtocolMessage::fullName( const std::string & user ) +{ + return "Local postmaster " ; +} + diff --git a/src/main/gprotocolmessage.h b/src/main/gprotocolmessage.h new file mode 100644 index 0000000..e3d2ff8 --- /dev/null +++ b/src/main/gprotocolmessage.h @@ -0,0 +1,110 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gprotocolmessage.h +// + +#ifndef G_SMTP_PROTOCOL_MESSAGE_H +#define G_SMTP_PROTOCOL_MESSAGE_H + +#include "gdef.h" +#include "gsmtp.h" +#include "gstrings.h" +#include + +namespace GSmtp +{ + class ProtocolMessage ; + class ProtocolMessageImp ; +} ; + +// Class: GSmtp::ProtocolMessage +// Description: An interface used by the ServerProtocol +// class to assemble and process an incoming message. +// It implements the three 'buffers' mentioned in +// RFC2821 (esp. section 4.1.1). Also does mail-address +// validation. +// +// This class serves to decouple the ServerProtocol class from +// the MessageStore (or whatever else is downstream). +// +class GSmtp::ProtocolMessage +{ +public: + ProtocolMessage() ; + // Default constructor. + + ~ProtocolMessage() ; + // Destructor. + + static std::pair verify( const std::string & ) ; + // Checks an address returning + // |. + // + // (If syntactically local then 'first' is + // returned true. If local and valid then + // 'second' is set to the full description. + // If syntactically remote, then 'first' + // is returned false and 'second' is empty.) + + void clear() ; + // Clears the message state. + + bool setFrom( const std::string & from_user ) ; + // Sets the message envelope 'from'. + // Returns false if an invalid user. + + bool addTo( const std::string & to_user ) ; + // Adds an envelope 'to'. + // Returns false if an invalid user. + // Precondition: setFrom() called + // since clear() or process(). + + void addReceived( const std::string & ) ; + // Adds a 'received' line to the + // start of the content. + // Precondition: at least one + // successful addTo() call + + void addText( const std::string & ) ; + // Adds text. + // Precondition: at least one + // successful addTo() call + + std::string process() ; + // Processes and clears the message. + // Returns a non-zero-length reason + // string on error. + +private: + static bool isLocal( const std::string & ) ; + static bool isValid( const std::string & ) ; + static bool isPostmaster( std::string ) ; + static std::string fullName( const std::string & ) ; + ProtocolMessage( const ProtocolMessage & ) ; + void operator=( const ProtocolMessage & ) ; + +private: + G::Strings m_to_list ; + ProtocolMessageImp * m_imp ; +} ; + +#endif + diff --git a/src/main/gserverprotocol.cpp b/src/main/gserverprotocol.cpp new file mode 100644 index 0000000..9b4b8bc --- /dev/null +++ b/src/main/gserverprotocol.cpp @@ -0,0 +1,508 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gserverprotocol.cpp +// + +#include "gdef.h" +#include "gsmtp.h" +#include "gserverprotocol.h" +#include "gdate.h" +#include "gtime.h" +#include "gdatetime.h" +#include "gstr.h" +#include "glog.h" +#include "gassert.h" +#include + +GSmtp::ServerProtocol::ServerProtocol( Sender & sender , const std::string & thishost , + const std::string & peer_address ) : + m_thishost(thishost) , + m_sender(sender) , + m_state(sStart) , + m_ss(NULL) , + m_peer_address(peer_address) +{ + // (dont send anything to the peer from this ctor -- the Sender + // object is not fuly constructed) + + addTransition( eQuit , s_Any , sEnd , &GSmtp::ServerProtocol::doQuit ) ; + addTransition( eUnknown , s_Any , s_Same , &GSmtp::ServerProtocol::doUnknown ) ; + addTransition( eRset , s_Any , sIdle , &GSmtp::ServerProtocol::doRset ) ; + addTransition( eNoop , s_Any , s_Same , &GSmtp::ServerProtocol::doNoop ) ; + addTransition( eVrfy , s_Any , s_Same , &GSmtp::ServerProtocol::doVrfy ) ; + + addTransition( eEhlo , s_Any , sIdle , &GSmtp::ServerProtocol::doEhlo , s_Same ) ; + addTransition( eHelo , s_Any , sIdle , &GSmtp::ServerProtocol::doHelo , s_Same ) ; + addTransition( eMail , sIdle , sGotMail , &GSmtp::ServerProtocol::doMail , sIdle ) ; + addTransition( eRcpt , sGotMail, sGotRcpt , &GSmtp::ServerProtocol::doRcpt , sGotMail ) ; + addTransition( eRcpt , sGotRcpt, sGotRcpt , &GSmtp::ServerProtocol::doRcpt ) ; + addTransition( eData , sGotMail, sIdle , &GSmtp::ServerProtocol::doNoRecipients ) ; + addTransition( eData , sGotRcpt, sData , &GSmtp::ServerProtocol::doData ) ; +} + +void GSmtp::ServerProtocol::init( const std::string & ident ) +{ + sendGreeting( m_thishost , ident ) ; +} + +void GSmtp::ServerProtocol::addTransition( Event e , State state_from , State state_to , Action action , State state_alt ) +{ + if( state_alt == s_Same ) state_alt = state_to ; + m_map.insert( Map::value_type( e , Transition(state_from,state_to,action,state_alt) ) ) ; +} + +void GSmtp::ServerProtocol::sendGreeting( const std::string & thishost , const std::string & ident ) +{ + ss() << "220 " << thishost << " -- " << ident << " -- Service ready" << end() ; +} + +bool GSmtp::ServerProtocol::apply( const std::string & line ) +{ + if( m_state == sData ) + { + if( isEndOfText(line) ) + { + G_LOG( "GSmtp::ServerProtocol: rx<<: [message content not logged]" ) ; + G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::toPrintableAscii(line) << "\"" ) ; + std::string reason = m_message.process() ; + const bool success = reason.empty() ; + m_state = sIdle ; + sendCompletionReply( success , reason ) ; + } + else + { + m_message.addText( isEscaped(line) ? line.substr(1U) : line ) ; + } + return false ; + } + else + { + G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::toPrintableAscii(line) << "\"" ) ; + Event event = commandEvent( commandString(line) ) ; + State new_state = applyEvent( event , line ) ; + return new_state == sEnd ; + } +} + +GSmtp::ServerProtocol::State GSmtp::ServerProtocol::applyEvent( Event event , const std::string & line ) +{ + // look up in the multimap keyed on current-state + event + // + Map::iterator p = m_map.find(event) ; + for( ; p != m_map.end() && (*p).first == event ; ++p ) + { + if( (*p).second.from == s_Any || (*p).second.from == m_state ) + { + // change state + // + State old_state = m_state ; + if( (*p).second.to != s_Same ) + m_state = (*p).second.to ; + State state = m_state ; + + // perform action + // + bool predicate = true ; + (this->*((*p).second.action))( line , predicate ) ; + + // respond to predicate -- note that + // if the state is sEnd then we cannot + // touch any member variables + // + if( state != sEnd && !predicate ) + { + State alt_state = (*p).second.alt ; + m_state = alt_state == s_Same ? old_state : alt_state ; + state = m_state ; + } + return state ; + } + } + sendOutOfSequence( line ) ; + return m_state ; +} + +void GSmtp::ServerProtocol::doQuit( const std::string & , bool & ) +{ + sendClosing() ; + m_sender.protocolDone() ; + // do nothing more -- this object may have been deleted already +} + +void GSmtp::ServerProtocol::doNoop( const std::string & , bool & ) +{ + sendOk() ; +} + +void GSmtp::ServerProtocol::doVrfy( const std::string & line , bool & ) +{ + size_t pos = line.find_first_of( " \t" ) ; + std::string user = line.substr(pos) ; + G::Str::trimLeft( user , " \t" ) ; + std::pair rc = ProtocolMessage::verify( user ) ; + bool local = rc.first ; + if( local && rc.second.length() ) + sendVerified( rc.second ) ; + else if( local ) + sendNotVerified( rc.second ) ; + else + sendWillAccept( rc.second ) ; +} + +void GSmtp::ServerProtocol::doEhlo( const std::string & line , bool & predicate ) +{ + std::string peer_name = parsePeerName( line ) ; + if( peer_name.empty() ) + { + predicate = false ; + sendMissingParameter() ; + } + else + { + m_peer_name = peer_name ; + m_message.clear() ; + sendEhloReply( m_thishost ) ; + } +} + +void GSmtp::ServerProtocol::doHelo( const std::string & line , bool & predicate ) +{ + std::string peer_name = parsePeerName( line ) ; + if( peer_name.empty() ) + { + predicate = false ; + sendMissingParameter() ; + } + else + { + m_peer_name = peer_name ; + m_message.clear() ; + sendHeloReply( m_thishost ) ; + } +} + +void GSmtp::ServerProtocol::doMail( const std::string & line , bool & predicate ) +{ + m_message.clear() ; + std::string from = parseFrom( line ) ; + bool ok = m_message.setFrom( from ) ; + predicate = ok ; + if( ok ) + sendMailReply() ; + else + sendBadFrom( from ) ; +} + +void GSmtp::ServerProtocol::doRcpt( const std::string & line , bool & predicate ) +{ + std::string to = parseTo( line ) ; + bool ok = m_message.addTo( to ) ; + predicate = ok ; + if( ok ) + sendRcptReply() ; + else + sendBadTo( to ) ; +} + +void GSmtp::ServerProtocol::doUnknown( const std::string & line , bool & ) +{ + sendUnrecognised( line ) ; +} + +void GSmtp::ServerProtocol::doRset( const std::string & line , bool & ) +{ + m_message.clear() ; + sendRsetReply() ; +} + +void GSmtp::ServerProtocol::doNoRecipients( const std::string & line , bool & ) +{ + sendNoRecipients() ; +} + +void GSmtp::ServerProtocol::doData( const std::string & line , bool & ) +{ + m_message.addReceived( receivedLine() ) ; + sendDataReply() ; +} + +void GSmtp::ServerProtocol::sendOutOfSequence( const std::string & line ) +{ + send( "503 command out of sequence -- use RSET to resynchronise" ) ; +} + +void GSmtp::ServerProtocol::sendMissingParameter() +{ + send( "501 parameter required" ) ; +} + +bool GSmtp::ServerProtocol::isEndOfText( const std::string & line ) const +{ + return line.length() == 1U && line.at(0U) == '.' ; +} + +bool GSmtp::ServerProtocol::isEscaped( const std::string & line ) const +{ + return line.length() > 1U && line.at(0U) == '.' ; +} + +std::string GSmtp::ServerProtocol::commandString( const std::string & line ) const +{ + size_t ws_pos = line.find_first_of( " \t" ) ; + std::string command = line.substr( 0U , ws_pos ) ; + G::Str::toUpper( command ) ; + return command ; +} + +//static +GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::commandEvent( const std::string & command ) +{ + if( command == "QUIT" ) return eQuit ; + if( command == "HELO" ) return eHelo ; + if( command == "EHLO" ) return eEhlo ; + if( command == "RSET" ) return eRset ; + if( command == "DATA" ) return eData ; + if( command == "RCPT" ) return eRcpt ; + if( command == "MAIL" ) return eMail ; + if( command == "VRFY" ) return eVrfy ; + if( command == "NOOP" ) return eNoop ; + if( command == "HELP" ) return eHelp ; + return eUnknown ; +} + +void GSmtp::ServerProtocol::sendClosing() +{ + send( "221 closing connection" ) ; +} + +void GSmtp::ServerProtocol::sendVerified( const std::string & user ) +{ + send( std::string("250 ") + user ) ; +} + +void GSmtp::ServerProtocol::sendNotVerified( const std::string & user ) +{ + send( std::string("550 no such mailbox: ") + user ) ; +} + +void GSmtp::ServerProtocol::sendWillAccept( const std::string & user ) +{ + send( std::string("252 cannot verify but will accept: ") + user ) ; +} + +void GSmtp::ServerProtocol::sendUnrecognised( const std::string & line ) +{ + send( "500 command unrecognized: \"" + line + std::string("\"") ) ; +} + +void GSmtp::ServerProtocol::sendNoRecipients() +{ + send( "554 no valid recipients" ) ; +} + +void GSmtp::ServerProtocol::sendDataReply() +{ + send( "354 start mail input -- end with ." ) ; +} + +void GSmtp::ServerProtocol::sendRsetReply() +{ + send( "250 state reset" ) ; +} + +void GSmtp::ServerProtocol::sendMailReply() +{ + sendOk() ; +} + +void GSmtp::ServerProtocol::sendCompletionReply( bool ok , const std::string & reason ) +{ + if( ok ) + sendOk() ; + else + send( std::string("452 message processing failed: ") + reason ) ; +} + +void GSmtp::ServerProtocol::sendRcptReply() +{ + sendOk() ; +} + +void GSmtp::ServerProtocol::sendBadFrom( const std::string & from ) +{ + send( "553 mailbox name not allowed" ) ; +} + +void GSmtp::ServerProtocol::sendBadTo( const std::string & to ) +{ + send( std::string("550 mailbox unavailable: ") + to ) ; +} + +void GSmtp::ServerProtocol::sendEhloReply( const std::string & domain ) +{ + ss() + << "250-" << domain << " says hello" << crlf() + //<<"250-XYZEXTENSION" << crlf() + << "250 8BITMIME" + << end() ; +} + +void GSmtp::ServerProtocol::sendHeloReply( const std::string & domain ) +{ + sendOk() ; +} + +void GSmtp::ServerProtocol::sendOk() +{ + send( "250 OK" ) ; +} + +std::ostream & GSmtp::ServerProtocol::ss() +{ + delete m_ss ; + m_ss = NULL ; + m_ss = new std::stringstream ; + return *m_ss ; +} + +GSmtp::ServerProtocol::End GSmtp::ServerProtocol::end() +{ + return End(*this) ; +} + +void GSmtp::ServerProtocol::onEnd() +{ + G_ASSERT( m_ss != NULL ) ; + send( m_ss->str() ) ; +} + +//static +std::string GSmtp::ServerProtocol::crlf() +{ + return std::string( "\015\012" ) ; +} + +void GSmtp::ServerProtocol::send( std::string line ) +{ + G_LOG( "GSmtp::ServerProtocol: tx>>: \"" << line << "\"" ) ; + line.append( crlf() ) ; + m_sender.protocolSend( line ) ; +} + +GSmtp::ServerProtocol::~ServerProtocol() +{ + delete m_ss ; +} + +std::string GSmtp::ServerProtocol::parseFrom( const std::string & line ) const +{ + // eg. MAIL FROM: + return parse( line ) ; +} + +std::string GSmtp::ServerProtocol::parseTo( const std::string & line ) const +{ + // eg. RCPT TO:<@first.co.uk,@second.co.uk:you@final.co.uk> + // eg. RCPT TO: + return parse( line ) ; +} + +std::string GSmtp::ServerProtocol::parse( const std::string & line ) const +{ + size_t start = line.find( '<' ) ; + size_t end = line.find( '>' ) ; + if( start == std::string::npos || end == std::string::npos || end < start ) + return std::string() ; + + std::string s = line.substr( start + 1U , end - start - 1U ) ; + G::Str::trim( s , " \t" ) ; + + // strip source route + if( s.length() > 0U && s.at(0U) == '@' ) + { + size_t colon_pos = s.find( ':' ) ; + if( colon_pos == std::string::npos ) + return std::string() ; + s = s.substr( colon_pos + 1U ) ; + } + + return s ; +} + +std::string GSmtp::ServerProtocol::parsePeerName( const std::string & line ) const +{ + size_t pos = line.find_first_of( " \t" ) ; + if( pos == std::string::npos ) + return std::string() ; + + std::string peer_name = line.substr( pos + 1U ) ; + G::Str::trimLeft( peer_name , " \t" ) ; + return peer_name ; +} + +std::string GSmtp::ServerProtocol::receivedLine() const +{ + G::DateTime::EpochTime t = G::DateTime::now() ; + G::DateTime::BrokenDownTime tm = G::DateTime::local(t) ; + std::string zone = G::DateTime::offsetString(G::DateTime::offset(t)) ; + G::Date date( tm ) ; + G::Time time( tm ) ; + + std::stringstream ss ; + ss + << "Reveived: " + << "FROM " << m_peer_name << " " + << "([" << m_peer_address << "]) " + << "BY " << m_thishost << " " + << "WITH ESMTP " + << "; " + << date.weekdayString(true) << ", " + << date.monthday() << " " + << date.monthString(true) << " " + << date.year() << " " + << time.hhmmss(":") << " " + << zone ; + return ss.str() ; +} + +// === + +GSmtp::ServerProtocol::Sender::~Sender() +{ +} + +// === + +GSmtp::ServerProtocol::End::End( ServerProtocol & p ) : + m_p(p) +{ +} + +// === + +GSmtp::ServerProtocol::Transition::Transition( State s1 , State s2 , Action a , State s3 ) : + from(s1) , + to(s2) , + action(a) , + alt(s3) +{ +} + diff --git a/src/main/gserverprotocol.h b/src/main/gserverprotocol.h new file mode 100644 index 0000000..aa6c9f7 --- /dev/null +++ b/src/main/gserverprotocol.h @@ -0,0 +1,207 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gserverprotocol.h +// + +#ifndef G_SMTP_SERVER_PROTOCOL_H +#define G_SMTP_SERVER_PROTOCOL_H + +#include "gdef.h" +#include "gsmtp.h" +#include "gprotocolmessage.h" +#include + +namespace GSmtp +{ + class ServerProtocol ; +} ; + +// Class: GSmtp::ServerProtocol +// Description: Implements the SMTP server-side protocol. +// +// Uses the ProtocolMessage class as its down-stream interface, +// used for assembling and processing the incoming email +// messages. +// +// Uses the ServerProtocol::Sender as its "sideways" +// interface to talk back to the email-sending client. +// +// A special feature of the implementation (which is +// important to the ServerPeer class) is that once +// Sender::protocolDone() has been called (from within +// ServerProtocol::apply()) no non-static method of +// ServerProtocol is called and no non-static data-member +// is accessed. This allows the ServerProtocol object to +// be deleted from within Sender::protocolDone(), and therefore +// allows the ServerPeer implementation to contain an +// instance of ServerProtocol as a data member. +// +// See also: ProtocolMessage, RFC2821 +// +class GSmtp::ServerProtocol +{ +public: + class Sender // Used to send protocol replies. + { + public: virtual void protocolSend( const std::string & s ) = 0 ; + public: virtual void protocolDone() = 0 ; + public: virtual ~Sender() ; + private: void operator=( const Sender & ) ; + } ; + + class End // A private implementation class. + { + public: ServerProtocol & m_p ; + public: End( ServerProtocol & p ) ; + } ; + + ServerProtocol( Sender & sender , const std::string & thishost , + const std::string & peer_address ) ; + // Constructor. The Sender interface is used to + // send protocol replies back to the client. + // The 'thishost' string is used in HELO + // replies (etc.) The peer address string is + // put into the "Received:" trace lines. + + void init( const std::string & ident ) ; + // Starts the protocol. The 'ident' string is issued + // to the client. + + ~ServerProtocol() ; + // Destructor. + + bool apply( const std::string & line ) ; + // Called on receipt of a string from the client. + // The string is expected to be terminated. + // Returns true if the protocol has completed + // and Sender::protocolDone() has been called. + + void onEnd() ; + // A pseudo-private method used by the End class. + +private: + enum Event + { + eQuit , + eHelo , + eEhlo , + eRset , + eNoop , + eData , + eRcpt , + eMail , + eVrfy , + eHelp , + eUnknown + } ; + enum State + { + sStart , + sEnd , + sIdle , + sGotMail , + sGotRcpt , + sData , + s_Any , + s_Same + } ; + typedef void (ServerProtocol::*Action)( const std::string & , bool & predicate ) ; + struct Transition + { + State from ; + State to ; + Action action ; + State alt ; + Transition(State s1,State s2,Action a,State s3) ; + } ; + typedef std::multimap Map ; + +private: + ServerProtocol( const ServerProtocol & ) ; + void operator=( const ServerProtocol & ) ; + State applyEvent( Event , const std::string & ) ; + void send( std::string ) ; + static Event commandEvent( const std::string & ) ; + std::string commandString( const std::string & line ) const ; + std::ostream & ss() ; + End end() ; + static std::string crlf() ; + bool isEndOfText( const std::string & ) const ; + bool isEscaped( const std::string & ) const ; + void addTransition( Event , State old , State new_ , Action , State alt = s_Same ) ; + void doNoop( const std::string & , bool & ) ; + void doQuit( const std::string & , bool & ) ; + void doEhlo( const std::string & , bool & ) ; + void doHelo( const std::string & , bool & ) ; + void doMail( const std::string & line , bool & ) ; + void doRcpt( const std::string & line , bool & ) ; + void doUnknown( const std::string & line , bool & ) ; + void doRset( const std::string & line , bool & ) ; + void doData( const std::string & line , bool & ) ; + void doVrfy( const std::string & line , bool & ) ; + void doNoRecipients( const std::string & line , bool & ) ; + void sendBadFrom( const std::string & line ) ; + void sendBadTo( const std::string & line ) ; + void sendOutOfSequence( const std::string & line ) ; + void sendGreeting( const std::string & , const std::string & ) ; + void sendClosing() ; + void sendUnrecognised( const std::string & ) ; + void sendHeloReply( const std::string & ) ; + void sendEhloReply( const std::string & ) ; + void sendRsetReply() ; + void sendMailReply() ; + void sendRcptReply() ; + void sendDataReply() ; + void sendCompletionReply( bool ok , const std::string & ) ; + void sendNoRecipients() ; + void sendMissingParameter() ; + void sendVerified( const std::string & ) ; + void sendNotVerified( const std::string & ) ; + void sendWillAccept( const std::string & ) ; + void sendOk() ; + std::string parseFrom( const std::string & ) const ; + std::string parseTo( const std::string & ) const ; + std::string parsePeerName( const std::string & ) const ; + std::string parse( const std::string & ) const ; + std::string receivedLine() const ; + +private: + Sender & m_sender ; + State m_state ; + Map m_map ; + ProtocolMessage m_message ; + std::stringstream * m_ss ; + std::string m_thishost ; + std::string m_peer_name ; + std::string m_peer_address ; +} ; + +namespace GSmtp +{ + inline + std::ostream & operator<<( std::ostream & stream , const ServerProtocol::End & e ) + { + e.m_p.onEnd() ; + return stream ; + } +} ; + +#endif diff --git a/src/main/gsmtp.h b/src/main/gsmtp.h new file mode 100644 index 0000000..03d353d --- /dev/null +++ b/src/main/gsmtp.h @@ -0,0 +1,31 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsmtp.h +// + +#ifndef G_SMTP_H +#define G_SMTP_H + +#include "gdef.h" +#include "gnet.h" +#include "glog.h" + +#endif diff --git a/src/main/gsmtpclient.cpp b/src/main/gsmtpclient.cpp new file mode 100644 index 0000000..bfd800a --- /dev/null +++ b/src/main/gsmtpclient.cpp @@ -0,0 +1,208 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsmtpclient.cpp +// + +#include "gdef.h" +#include "gnet.h" +#include "gsmtp.h" +#include "glocal.h" +#include "gfile.h" +#include "gstr.h" +#include "gmemory.h" +#include "gsmtpclient.h" +#include "gresolve.h" +#include "glog.h" + +//static +std::string GSmtp::Client::crlf() +{ + return std::string("\015\012") ; +} + +GSmtp::Client::Client( GSmtp::MessageStore & store , bool quit_on_disconnect ) : + GNet::Client(false,quit_on_disconnect) , + m_callback(NULL) , + m_store(store) , + m_buffer(crlf()) , + m_protocol(*this,GNet::Local::fqdn()) , + m_socket(NULL) +{ +} + +GSmtp::Client::Client( GSmtp::MessageStore & store , ClientCallback & callback , bool quit_on_disconnect ) : + GNet::Client(false,quit_on_disconnect) , + m_callback(&callback) , + m_store(store) , + m_buffer(crlf()) , + m_protocol(*this,GNet::Local::fqdn()) , + m_socket(NULL) +{ +} + +std::string GSmtp::Client::init( const std::string & s ) +{ + size_t pos = s.find(':') ; + if( pos == std::string::npos ) + return "invalid address string: no colon (:)" ; + + return init( s.substr(0U,pos) , s.substr(pos+1U) ) ; +} + +std::string GSmtp::Client::init( const std::string & host , const std::string & service ) +{ + if( m_store.empty() ) + return "no messages to send" ; + + std::string error ; + bool rc = connect( host , service , &error ) ; + return rc ? std::string() : error ; +} + +bool GSmtp::Client::busy() const +{ + return connected() ; // GNet::Client::connected() +} + +bool GSmtp::Client::protocolSend( const std::string & line ) +{ + ssize_t rc = socket().write( line.data() , line.length() ) ; + if( rc < 0 ) + { + m_pending = line ; + if( socket().eWouldBlock() ) + blocked() ; + return false ; + } + else if( rc < line.length() ) + { + m_pending = line.substr(rc) ; + blocked() ; // GNet::Client::blocked() => addWriteHandler() + return false ; + } + else + { + return true ; + } +} + +void GSmtp::Client::onConnect( GNet::Socket & socket ) +{ + m_socket = &socket ; + m_iter = m_store.iterator() ; + if( !sendNext() ) + finish() ; +} + +void GSmtp::Client::finish() +{ + if( m_callback != NULL ) + { + m_callback->onCompletion(std::string()) ; + m_callback = NULL ; + } + + disconnect() ; // GNet::Client::disconnect() +} + +bool GSmtp::Client::sendNext() +{ + m_message <<= 0 ; + + { + std::auto_ptr message( m_iter.next() ) ; + if( message.get() == NULL ) + { + G_LOG( "GSmtp::Client: no more messages to send" ) ; + GNet::Socket * s = m_socket ; + m_socket = NULL ; + s->close() ; + return false ; + } + m_message = message ; + } + + m_protocol.start( m_message->from() , m_message->to() , m_message->eightBit() , + m_message->extractContentStream() , *this ) ; + return true ; +} + +void GSmtp::Client::callback( bool ok ) +{ + G_DEBUG( "GSmtp::Client::callback: " << ok ) ; + if( m_message.get() != NULL ) + { + if( ok ) + m_message->destroy() ; + else + m_message->fail("smtp protocol failure") ; + } + if( !sendNext() ) + finish() ; +} + +void GSmtp::Client::onDisconnect() +{ + if( m_callback != NULL ) + m_callback->onCompletion( "connection to server lost" ) ; + m_socket = NULL ; +} + +GNet::Socket & GSmtp::Client::socket() +{ + if( m_socket == NULL ) + throw NotConnected() ; + return * m_socket ; +} + +void GSmtp::Client::onData( const char * data , size_t size ) +{ + m_buffer.add( std::string(data,size) ) ; + while( m_buffer.more() ) + { + m_protocol.apply( m_buffer.line() ) ; + if( m_protocol.done() ) + finish() ; + } +} + +void GSmtp::Client::onError( const std::string &error ) +{ + if( m_callback != NULL ) + m_callback->onCompletion( std::string("error on connection to server: ") + error ) ; + G_WARNING( "GSmtp::Client: error: \"" << error << "\"" ) ; +} + +void GSmtp::Client::onWriteable() +{ + if( protocolSend(m_pending) ) + { + m_protocol.sendComplete() ; + } +} + +// === + +void GSmtp::Client::ClientCallback::onCompletion( std::string ) +{ + // empty +} + diff --git a/src/main/gsmtpclient.h b/src/main/gsmtpclient.h new file mode 100644 index 0000000..66866e6 --- /dev/null +++ b/src/main/gsmtpclient.h @@ -0,0 +1,114 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsmtpclient.h +// + +#ifndef G_SMTP_CLIENT_H +#define G_SMTP_CLIENT_H + +#include "gdef.h" +#include "gnet.h" +#include "gsmtp.h" +#include "glinebuffer.h" +#include "gclient.h" +#include "gclientprotocol.h" +#include "gmessagestore.h" +#include "gsocket.h" +#include "gstrings.h" +#include "gexception.h" +#include +#include + +namespace GSmtp +{ + class Client ; + class ClientProtocol ; +} ; + +// Class: GSmtp::Client +// Description: A class which acts as an SMTP client, extracting +// messages from a message store and forwarding them to +// a remote SMTP server. +// +class GSmtp::Client : private GNet::Client , + private GSmtp::ClientProtocol::Sender , private GSmtp::ClientProtocol::Callback +{ +public: + G_EXCEPTION( NotConnected , "not connected" ) ; + class ClientCallback // A callback interface used by GSmtp::Client. + { + public: virtual void onCompletion( std::string ) ; + } ; + + Client( MessageStore & store , bool quit_on_disconnect ) ; + // Constructor. The reference is kept. + + Client( MessageStore & store , ClientCallback & callback , bool quit_on_disconnect ) ; + // Constructor. The references are kept. + // + // The callback is used to signal that + // all message processing has finished + // or that the server connection has + // been lost. + + std::string init( const std::string & host , const std::string & service ) ; + // Starts the sending process. Messages + // are extracted from the message store + // (as passed in the ctor) and forwarded + // on to the specified server. + // + // Returns an error string if there are no messages + // to be sent, or if the network connection + // cannot be initiated. Returns the empty + // string on success. + + std::string init( const std::string & host_service ) ; + // An overload. + + bool busy() const ; + // Returns true if the client is still + // busy processing messages. + +private: + virtual void onConnect( GNet::Socket & socket ) ; // GNet::Client + virtual void onDisconnect() ; // GNet::Client + virtual void onData( const char * data , size_t size ) ; // GNet::Client + virtual void onWriteable() ; // GNet::Client + virtual void onError( const std::string & error ) ; // GNet::Client + virtual bool protocolSend( const std::string & ) ; // Sender + virtual void callback( bool ) ; // ClientCallback + GNet::Socket & socket() ; + static std::string crlf() ; + bool sendNext() ; + void finish() ; + +private: + MessageStore & m_store ; + std::auto_ptr m_message ; + MessageStore::Iterator m_iter ; + GNet::LineBuffer m_buffer ; + ClientProtocol m_protocol ; + GNet::Socket * m_socket ; + std::string m_pending ; + ClientCallback * m_callback ; +} ; + +#endif diff --git a/src/main/gsmtpserver.cpp b/src/main/gsmtpserver.cpp new file mode 100644 index 0000000..dd70907 --- /dev/null +++ b/src/main/gsmtpserver.cpp @@ -0,0 +1,133 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsmtpserver.cpp +// + +#include "gdef.h" +#include "gsmtp.h" +#include "gsmtpserver.h" +#include "glocal.h" +#include "glog.h" +#include "gdebug.h" +#include "gassert.h" +#include + +GSmtp::ServerPeer::ServerPeer( GNet::StreamSocket * socket , GNet::Address peer_address , + Server & server , const std::string & ident ) : + GNet::ServerPeer( socket , peer_address ) , + m_protocol( *this , thishost() , peer_address.displayString(false) ) , + m_buffer( crlf() ) , + m_server( server ) +{ + G_LOG( "GSmtp::ServerPeer: new connection from " << peer_address.displayString() ) ; + m_protocol.init( ident ) ; +} + +std::string GSmtp::ServerPeer::thishost() const +{ + return GNet::Local::fqdn() ; +} + +std::string GSmtp::ServerPeer::crlf() +{ + return std::string("\015\012") ; +} + +void GSmtp::ServerPeer::onDelete() +{ + G_WARNING( "GSmtp::ServerPeer: peer disconnected" ) ; +} + +void GSmtp::ServerPeer::onData( const char * p , size_t n ) +{ + std::string s( p , n ) ; + m_buffer.add( s ) ; + while( m_buffer.more() ) + { + bool this_deleted = processLine( m_buffer.line() ) ; + if( this_deleted ) + break ; + } +} + +bool GSmtp::ServerPeer::processLine( const std::string & line ) +{ + return m_protocol.apply( line ) ; +} + +void GSmtp::ServerPeer::protocolSend( const std::string & line ) +{ + if( line.length() == 0U ) + return ; + + ssize_t rc = socket().write( line.data() , line.length() ) ; + if( rc == -1 && ! socket().eWouldBlock() ) + { + doDelete() ; // onDelete() and "delete this" + } + else if( rc < line.length() ) + { + G_ERROR( "GSmtp::ServerPeer::protocolSend: " << + "flow-control asserted: connection blocked" ) ; + + // an SMTP server only sends short status messages + // back to the client so it is pretty wierd if the + // client/network cannot cope -- so just drop the + // connection + // + doDelete() ; + } +} + +void GSmtp::ServerPeer::protocolDone() +{ + G_LOG( "GSmtp::ServerPeer: disconnecting" ) ; + doDelete() ; // onDelete() and "delete this" +} + +// === + +GSmtp::Server::Server( unsigned int port , bool allow_remote , const std::string & ident ) : + GNet::Server( port ) , + m_ident( ident ) , + m_allow_remote( allow_remote ) +{ + G_LOG( "GSmtp::Server: listening on port " << port ) ; +} + +GNet::ServerPeer * GSmtp::Server::newPeer( GNet::StreamSocket * socket , GNet::Address peer_address ) +{ + std::auto_ptr ptr(socket) ; + + if( ! m_allow_remote && + !peer_address.sameHost(GNet::Local::canonicalAddress()) && + !peer_address.sameHost(GNet::Local::localhostAddress()) ) + { + G_WARNING( "GSmtp::Server: configured to reject non-local connection: " + << peer_address.displayString(false) << " is not one of " + << GNet::Local::canonicalAddress().displayString(false) << "," + << GNet::Local::localhostAddress().displayString(false) ) ; + return NULL ; + } + + return new ServerPeer( ptr.release() , peer_address , *this , m_ident ) ; +} + diff --git a/src/main/gsmtpserver.h b/src/main/gsmtpserver.h new file mode 100644 index 0000000..a58c530 --- /dev/null +++ b/src/main/gsmtpserver.h @@ -0,0 +1,88 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsmtpserver.h +// + +#ifndef G_SMTP_SERVER_H +#define G_SMTP_SERVER_H + +#include "gdef.h" +#include "gsmtp.h" +#include "gserver.h" +#include "glinebuffer.h" +#include "gserverprotocol.h" +#include +#include + +namespace GSmtp +{ + class Server ; + class ServerPeer ; +} ; + +// Class: GSmtp::ServerPeer +// Description: Represents a connection from an SMTP client. +// Instances are created on the heap by Server (only). +// See also: Server +// +class GSmtp::ServerPeer : public GNet::ServerPeer , private GSmtp::ServerProtocol::Sender +{ +public: + ServerPeer( GNet::StreamSocket * , GNet::Address , Server & server , const std::string & ident ) ; + // Constructor. + +private: + ServerPeer( const ServerPeer & ) ; + void operator=( const ServerPeer & ) ; + virtual void protocolSend( const std::string & line ) ; // from ServerProtocol::Sender + virtual void protocolDone() ; // from ServerProtocol::Sender + virtual void onDelete() ; // from GNet::ServerPeer + virtual void onData( const char * , size_t ) ; // from GNet::ServerPeer + bool processLine( const std::string & line ) ; + std::string thishost() const ; + +private: + GNet::LineBuffer m_buffer ; + static std::string crlf() ; + ServerProtocol m_protocol ; + Server & m_server ; +} ; + +// Class: GSmtp::Server +// Description: An SMTP server class. +// +class GSmtp::Server : private GNet::Server +{ +public: + Server( unsigned int port , bool allow_remote , const std::string & ident ) ; + // Constructor. + +private: + virtual GNet::ServerPeer * newPeer( GNet::StreamSocket * , GNet::Address ) ; + Server( const Server & ) ; + void operator=( const Server & ) ; + +private: + std::string m_ident ; + bool m_allow_remote ; +} ; + +#endif diff --git a/src/main/mailrelay.cpp b/src/main/mailrelay.cpp new file mode 100644 index 0000000..9eaa551 --- /dev/null +++ b/src/main/mailrelay.cpp @@ -0,0 +1,429 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// mailrelay.cpp +// + +#include "gdef.h" +#include "gsmtp.h" +#include "gsmtpserver.h" +#include "gsmtpclient.h" +#include "gevent.h" +#include "garg.h" +#include "gdaemon.h" +#include "gstr.h" +#include "gpath.h" +#include "gmessagestore.h" +#include "gadminserver.h" +#include "gexception.h" +#include "ggetopt.h" +#include "gdebug.h" +#include +#include + +namespace +{ + class Main + { + public: + explicit Main( G::Arg & arg ) ; + void run() ; + private: + std::string versionNumber() const ; + void version() const ; + void banner() const ; + void warranty() const ; + void usage( std::ostream & s , const std::string & exe , const G::GetOpt & opt ) const ; + void help( const std::string & exe ) const ; + void shortHelp( std::ostream & stream , const std::string & exe ) const ; + void copyright() const ; + std::string switchSpec() const ; + void runCore() ; + unsigned int ttyColumns() const ; + std::string checkOptions() const ; + const G::GetOpt & opt() const ; + unsigned int optPort() const ; + unsigned int optAdminPort() const ; + bool optCloseStderr() const ; + bool optLog() const ; + bool optSyslog() const ; + bool optDaemon() const ; + bool optDoForwarding() const ; + bool optDoServing() const ; + bool optDoAdmin() const ; + bool optAllowRemoteClients() const ; + G::Path optSpoolDir() const ; + std::string optServerAddress() const ; + void doForwarding( GSmtp::MessageStore & store ) ; + void closeFiles() ; + void closeMoreFiles() ; + std::string smtpIdent() const ; + void recordPid() ; + private: + G::Arg m_arg ; + G::GetOpt * m_opt ; + } ; +} ; + +Main::Main( G::Arg & arg ) : + m_arg(arg) +{ +} + +void Main::banner() const +{ + std::cout + << "E-MailRelay V" << versionNumber() << std::endl ; +} + +void Main::copyright() const +{ + std::cout << "Copyright (C) 2001 Graeme Walker" << std::endl ; +} + +void Main::warranty() const +{ + std::cout + << "This software is provided without warranty of any kind." << std::endl + << "You may redistribure copies of this program under the terms of the GNU " + << "General Public License." << std::endl + << "For more information refer to the file named COPYING." << std::endl ; +} + +void Main::version() const +{ + banner() ; + warranty() ; + copyright() ; +} + +std::string Main::versionNumber() const +{ + return "0.9.1" ; +} + +std::string Main::smtpIdent() const +{ + return std::string("E-MailRelay V") + versionNumber() ; +} + +unsigned int Main::ttyColumns() const +{ + const unsigned int default_ = 79U ; + try + { + const char * p = std::getenv( "COLUMNS" ) ; + return p ? G::Str::toUInt(p) : default_ ; + } + catch( std::exception & ) + { + return default_ ; + } +} + +void Main::usage( std::ostream & s , const std::string & exe , const G::GetOpt & opt ) const +{ + opt.showUsage( s , exe , "" , 30U , ttyColumns() ) ; +} + +void Main::help( const std::string & exe ) const +{ + std::cout << std::endl ; + + std::cout + << "To start a 'storage' daemon in background..." << std::endl + << " " << exe << " --as-server" << std::endl + << std::endl ; + + std::cout + << "To forward stored mail to \"mail.myisp.co.uk\"..." << std::endl + << " " << exe << " --as-client mail.myisp.co.uk:smtp" << std::endl + << std::endl ; + + std::cout + << "To start a 'store & forward' daemon..." << std::endl + << " " << exe << " --as-server --admin 10025 --forward-to mail.myisp.co.uk:smtp" << std::endl + << " (and then \"" << exe << "poke 10025\" to trigger forwarding)" << std::endl + << std::endl ; +} + +void Main::shortHelp( std::ostream & stream , const std::string & exe ) const +{ + stream + << std::string(exe.length()+2U,' ') + << "try \"" << exe << " --help\" for more information" << std::endl ; +} + +std::string Main::switchSpec() const +{ + std::stringstream ss ; + ss + << "h!help!displays help text and exits!0!|" + << "l!log!writes log information on standard error (if open) and syslog (if not disabled)!0!|" + << "v!verbose!generates more verbose logging " + << "(if compiled-in and logging enabled and stderr open)!0!|" + << "e!close-stderr!closes the standard error stream when daemonising!0!|" + << "s!spool-dir!specifies the spool directory " + << "(default is \"" << GSmtp::MessageStore::defaultDirectory() + << "\")!1!dir|" + << "q!as-client!equivalent to \"--no-syslog --no-daemon --log --dont-serve --forward --forward-to\"!" + << "1!host:port|" + << "d!as-server!equivalent to \"--close-stderr --log\"!0!|" + << "n!no-syslog!disables syslog output!0!|" + << "t!no-daemon!does not detach from the terminal!0!|" + << "x!dont-serve!stops the process acting as a server (usually used with --forward)!0!|" + << "f!forward!forwards stored mail on startup (requires --forward-to)!0!|" + << "o!forward-to!specifies the remote smtp server (required by --forward and --admin)!1!host:port|" + << "r!remote-clients!allows remote clients to connect!0!|" + << "i!pid-file!records the daemon process-id in the given file!1!pid-file|" + << "p!port!specifies the smtp listening port number!1!port|" + << "a!admin!enables the administration interface and specifies its listening port number!1!admin-port|" + << "V!version!displays version information and exits!0!" + ; + return ss.str() ; +} + +const G::GetOpt & Main::opt() const +{ + G_ASSERT( m_opt != NULL ) ; + return *m_opt ; +} + +void Main::run() +{ + m_opt = new G::GetOpt( m_arg , switchSpec() , '|' , '!' , '^' ) ; + + if( opt().contains("help") ) + { + banner() ; + std::cout << std::endl ; + usage( std::cout , m_arg.prefix() , opt() ) ; + help( m_arg.prefix() ) ; + copyright() ; + } + else if( opt().hasErrors() ) + { + opt().showErrors( std::cerr , m_arg.prefix() ) ; + shortHelp( std::cerr , m_arg.prefix() ) ; + } + else if( opt().args().c() > 1U ) + { + std::cerr << m_arg.prefix() << ": usage error: too many non-switch arguments" << std::endl ; + shortHelp( std::cerr , m_arg.prefix() ) ; + } + else if( opt().contains("version") ) + { + version() ; + } + else + { + G::LogOutput debug( optLog() , opt().contains("verbose") ) ; + if( optSyslog() ) + debug.syslog(G::LogOutput::Mail) ; + + runCore() ; + } +} + +bool Main::optLog() const +{ + return opt().contains("log") || opt().contains("as-client") || opt().contains("as-server") ; +} + +bool Main::optSyslog() const +{ + return !opt().contains("no-syslog") && !opt().contains("as-client") ; +} + +unsigned int Main::optPort() const +{ + return opt().contains("port") ? + G::Str::toUInt(opt().value("port")) : 25U ; +} + +unsigned int Main::optAdminPort() const +{ + return opt().contains("admin") ? + G::Str::toUInt(opt().value("admin")) : 10025U ; +} + +bool Main::optCloseStderr() const +{ + return opt().contains("close-stderr") || opt().contains("as-server") ; +} + +bool Main::optDaemon() const +{ + return !opt().contains("no-daemon") && !opt().contains("as-client") ; +} + +G::Path Main::optSpoolDir() const +{ + return opt().contains("spool-dir") ? + G::Path(opt().value("spool-dir")) : + GSmtp::MessageStore::defaultDirectory() ; +} + +std::string Main::optServerAddress() const +{ + const char * key = opt().contains("forward-to") ? "forward-to" : "as-client" ; + return opt().contains(key) ? opt().value(key) : std::string() ; +} + +std::string Main::checkOptions() const +{ + if( optDoAdmin() && optAdminPort() == optPort() ) + { + return "the smtp listening port and the " + "admin listening port must be different" ; + } + + if( optDaemon() && optSpoolDir().isRelative() ) + { + return "in daemon mode the spool-dir must " + "be an absolute path (starting with /)" ; + } + + if( !opt().contains("forward-to") && + (opt().contains("admin") || opt().contains("forward")) ) + { + return "usage error: the --admin and --forward " + "switches require --forward-to" ; + } + + return std::string() ; +} + +bool Main::optDoForwarding() const +{ + return opt().contains("forward") || opt().contains("as-client") ; +} + +void Main::doForwarding( GSmtp::MessageStore & store ) +{ + const bool quit_on_disconnect = true ; + GSmtp::Client client( store , quit_on_disconnect ) ; + std::string error = client.init( optServerAddress() ) ; + if( error.length() ) + throw G::Exception( error + ": " + optServerAddress() ) ; + + GNet::EventSources::instance().run() ; +} + +void Main::closeFiles() +{ + if( optDaemon() ) + { + const bool keep_stderr = true ; + G::Daemon::closeFiles( keep_stderr ) ; + } +} + +void Main::closeMoreFiles() +{ + if( optDaemon() && optCloseStderr() ) + G::Daemon::closeStderr() ; +} + +bool Main::optDoServing() const +{ + return !opt().contains("dont-serve") && !opt().contains("as-client") ; +} + +bool Main::optAllowRemoteClients() const +{ + return opt().contains("remote-clients") ; +} + +bool Main::optDoAdmin() const +{ + return opt().contains("admin") ; +} + +void Main::runCore() +{ + std::string error = checkOptions() ; + if( !error.empty() ) + throw G::Exception( error ) ; + + G::Daemon::setUmask() ; + if( optDaemon() ) + { + closeFiles() ; // before opening any sockets or message-store streams + if( opt().contains("pid-file") ) + G::Daemon::detach( G::Path(opt().value("pid-file")) ) ; + else + G::Daemon::detach() ; + } + + GSmtp::MessageStore store( optSpoolDir() ) ; + + std::auto_ptr event_loop( GNet::EventSources::create() ) ; + if( ! event_loop->init() ) + throw G::Exception( "cannot initialise network layer" ) ; + + if( optDoForwarding() ) + { + if( store.empty() ) + std::cerr << m_arg.prefix() << ": no messages to send" << std::endl ; + else + doForwarding( store ) ; + } + + if( optDoServing() ) + { + GSmtp::Server server( optPort() , optAllowRemoteClients() , smtpIdent() ) ; + + if( optDoAdmin() ) + { + GSmtp::AdminServer admin_server( optAdminPort() , + optAllowRemoteClients() , optServerAddress() ) ; + closeMoreFiles() ; + event_loop->run() ; + } + else + { + closeMoreFiles() ; + event_loop->run() ; + } + } +} + +int main( int argc , char * argv [] ) +{ + G::Arg arg( argc , argv ) ; + try + { + Main main( arg ) ; + main.run() ; + return EXIT_SUCCESS ; + } + catch( std::exception & e ) + { + std::cerr << arg.prefix() << ": exception: " << e.what() << std::endl ; + } + catch( ... ) + { + std::cerr << arg.prefix() << ": unrecognised exception" << std::endl ; + } + return EXIT_FAILURE ; +} + + diff --git a/src/main/poke.c b/src/main/poke.c new file mode 100644 index 0000000..0ddfff7 --- /dev/null +++ b/src/main/poke.c @@ -0,0 +1,81 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// poke.c +// + +// This is a small program that connects to the +// specified port on the local machine, sends +// a fixed string, and prints out the first +// bit of what it gets sent back. +// +// Its purpose is to provide a low-overhead +// mechanism for stimulating the mail-relay +// daemon to send its queued-up messages to +// the remote smtp server. +// +// usage: poke [ []] +// + +#include +#include +#include +#include +#include +#include +#include + +int main( int argc , char * argv [] ) +{ + const char * const host = "127.0.0.1" ; + unsigned short port = 10025U ; + char buffer[160U] = "FLUSH" ; + struct sockaddr_in address ; + int fd , rc ; + + if( argc > 1 ) + port = atoi(argv[1]) ; + + if( argc > 2 ) + { + buffer[0] = '\0' ; + strncat( buffer , argv[2] , sizeof(buffer)-5U ) ; + } + strcat( buffer , "\015\012" ) ; + + fd = socket( AF_INET , SOCK_STREAM , 0 ) ; + if( fd < 0 ) return EXIT_FAILURE ; + memset( &address , 0 , sizeof(address) ) ; + address.sin_family = AF_INET ; + address.sin_port = htons( port ) ; + address.sin_addr.s_addr = inet_addr( host ) ; + rc = connect( fd , (const struct sockaddr*)&address , sizeof(address) ) ; + if( rc < 0 ) return EXIT_FAILURE ; + rc = write( fd , buffer , strlen(buffer) ) ; + if( rc != strlen(buffer) ) return EXIT_FAILURE ; + rc = read( fd , buffer , sizeof(buffer)-1U) ; + if( rc <= 0 ) return EXIT_FAILURE ; + write( STDOUT_FILENO , buffer , rc ) ; + buffer[0U] = '\n' ; + buffer[1U] = '\0' ; + write( STDOUT_FILENO , buffer , strlen(buffer) ) ; + return EXIT_SUCCESS ; +} + diff --git a/src/win32/Makefile.am b/src/win32/Makefile.am new file mode 100644 index 0000000..bbb5f5e --- /dev/null +++ b/src/win32/Makefile.am @@ -0,0 +1,35 @@ +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + +EXTRA_DIST = gappinst.h \ + gappinst.cpp \ + gcracker.cpp \ + gcracker.h \ + gpump.cpp \ + gpump.h \ + gpump_nodialog.cpp \ + gsize.h \ + gwinbase.cpp \ + gwinbase.h \ + gwindow.cpp \ + gwindow.h \ + gwinhid.cpp \ + gwinhid.h + diff --git a/src/win32/Makefile.in b/src/win32/Makefile.in new file mode 100644 index 0000000..1e99461 --- /dev/null +++ b/src/win32/Makefile.in @@ -0,0 +1,192 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# +# Copyright (C) 2001 Graeme Walker +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# === + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_MAN2HTML = @HAVE_MAN2HTML@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +EXTRA_DIST = gappinst.h gappinst.cpp gcracker.cpp gcracker.h gpump.cpp gpump.h gpump_nodialog.cpp gsize.h gwinbase.cpp gwinbase.h gwindow.cpp gwindow.h gwinhid.cpp gwinhid.h + +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../config.h +CONFIG_CLEAN_FILES = +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps src/win32/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src/win32 + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: tags distdir info-am info dvi-am dvi check check-am \ +installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/win32/gappinst.cpp b/src/win32/gappinst.cpp new file mode 100644 index 0000000..e7be5e5 --- /dev/null +++ b/src/win32/gappinst.cpp @@ -0,0 +1,38 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gappinst.cpp +// + +#include "gdef.h" +#include "gappinst.h" + +HINSTANCE GGui::ApplicationInstance::m_hinstance = 0 ; + +GGui::ApplicationInstance::ApplicationInstance( HINSTANCE hinstance ) +{ + m_hinstance = hinstance ; +} + +HINSTANCE GGui::ApplicationInstance::hinstance() +{ + return m_hinstance ; +} + diff --git a/src/win32/gappinst.h b/src/win32/gappinst.h new file mode 100644 index 0000000..c1a28f0 --- /dev/null +++ b/src/win32/gappinst.h @@ -0,0 +1,72 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gappinst.h +// + +#ifndef G_APPINST_H +#define G_APPINST_H + +#include "gdef.h" + +namespace GGui +{ + class ApplicationInstance ; +} ; + +// Class: GGui::ApplicationInstance +// +// Description: A class for storing the application's +// instance handle, as obtained from WinMain(). +// +// Other low-level classes in this library use this +// class to obtain the application instance handle, +// rather than calling GApplication::instance(). +// +// Programs which need a message pump, but want to +// avoid the overhead of the full GUI application +// must use this class as an absolute minimum. +// However, they should probably use the GApplicationBase +// class, which also minimises the dependencies on +// the framework. +// +// See also: ApplicationBase +// +class GGui::ApplicationInstance +{ +public: + explicit ApplicationInstance( HINSTANCE hinstance ) ; + // Constructor. (Setting the static value + // through a constructor is a bit klunky + // but it makes it easy to retrofit this + // class to the original version of + // GApplication.) + + static HINSTANCE hinstance() ; + // Returns the instance handle that was + // passed to the constructor. Returns + // zero if no GApplicationInstance + // object has been created. + +private: + static HINSTANCE m_hinstance ; +} ; + +#endif diff --git a/src/win32/gcracker.cpp b/src/win32/gcracker.cpp new file mode 100644 index 0000000..5dcf2be --- /dev/null +++ b/src/win32/gcracker.cpp @@ -0,0 +1,471 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gcracker.cpp +// + +#include "gdef.h" +#include "gcracker.h" +#include "gstrings.h" +#include "gdebug.h" +#include "glog.h" +#include + +GGui::Cracker::Cracker( HWND hwnd ) : + WindowBase(hwnd) +{ +} + +GGui::Cracker::~Cracker() +{ +} + +GGui::Cracker & GGui::Cracker::operator=( const Cracker & other ) +{ + WindowBase::operator=( other ) ; + return *this ; +} + +LRESULT GGui::Cracker::crack( UINT message , WPARAM wparam , + LPARAM lparam , bool &defolt ) +{ + defolt = false ; + switch( message ) + { + case WM_PAINT: + { + G_DEBUG( "GCracker::onPaint" ) ; + bool done = onPaintMessage() ; + if( ! done ) + { + PAINTSTRUCT ps ; + HDC dc = ::BeginPaint( m_hwnd , &ps ) ; + onPaint( dc ) ; + ::EndPaint( m_hwnd , &ps ) ; + } + return 0 ; + } + + case WM_CLOSE: + { + G_DEBUG( "GCracker::onClose" ) ; + if( onClose() ) + ::PostMessage( m_hwnd , WM_DESTROY , 0 , 0 ) ; + return 0 ; + } + + case WM_DESTROY: + { + G_DEBUG( "GCracker::onDestroy" ) ; + onDestroy() ; + return 0 ; + } + + case WM_NCDESTROY: + { + G_DEBUG( "GCracker::onNcDestroy" ) ; + onNcDestroy() ; + return 0 ; + } + + case WM_CTLCOLORMSGBOX: + { + return (LRESULT)onControlColour( (HDC)wparam , + (HWND)lparam , CTLCOLOR_MSGBOX ) ; + } + + case WM_CTLCOLORDLG: + { + return (LRESULT)onControlColour( (HDC)wparam , + (HWND)lparam , CTLCOLOR_DLG ) ; + } + + case WM_CTLCOLOREDIT: + { + return (LRESULT)onControlColour( (HDC)wparam , + (HWND)lparam , CTLCOLOR_EDIT ) ; + } + + case WM_CTLCOLORLISTBOX: + { + return (LRESULT)onControlColour( (HDC)wparam , + (HWND)lparam , CTLCOLOR_LISTBOX ) ; + } + + case WM_CTLCOLORBTN: + { + return (LRESULT)onControlColour( (HDC)wparam , + (HWND)lparam , CTLCOLOR_BTN ) ; + } + + case WM_CTLCOLORSCROLLBAR: + { + return (LRESULT)onControlColour( (HDC)wparam , + (HWND)lparam , CTLCOLOR_SCROLLBAR ) ; + } + + case WM_CTLCOLORSTATIC: + { + return (LRESULT)onControlColour( (HDC)wparam , + (HWND)lparam , CTLCOLOR_STATIC ) ; + } + + case WM_SYSCOLORCHANGE: + { + onSysColourChange() ; + return 0 ; + } + + case WM_KILLFOCUS: + { + // wparam, not lparam -- the Win3.x help file seems + // to have a typo -- comfirmed by Broland's windowsx.h + HWND hwnd_other = reinterpret_cast( wparam ) ; // sic + onLooseFocus( hwnd_other ) ; + return 0 ; + } + + case WM_SETFOCUS: + { + HWND hwnd_other = reinterpret_cast( wparam ) ; + onGetFocus( hwnd_other ) ; + return 0 ; + } + + case WM_CHAR: + { + unsigned repeat_count = static_cast(LOWORD(lparam)) ; + WORD vkey = static_cast(wparam) ; + onChar( vkey , repeat_count ) ; + return 0 ; + } + + case WM_ERASEBKGND: + { + G_DEBUG( "GCracker::onEraseBackground" ) ; + HDC hdc = reinterpret_cast(wparam) ; + return onEraseBackground( hdc ) ; + } + + case WM_DROPFILES: + { + G_DEBUG( "GCracker::onDrop" ) ; + HDROP hdrop = reinterpret_cast(wparam) ; + int count = ::DragQueryFile( hdrop , -1 , NULL , 0 ) ; + G::Strings list ; + for( int i = 0 ; i < count ; i++ ) + { + static char buffer[512] ; + if( ::DragQueryFile( hdrop , i , buffer , sizeof(buffer) ) < sizeof(buffer) ) + { + G_DEBUG( "GCracker::onDrop: \"" << buffer << "\"" ) ; + list.push_back( std::string(buffer) ) ; + } + } + ::DragFinish( hdrop ) ; + return !onDrop( list ) ; + } + + case WM_SIZE: + { + SizeType type = restored ; + switch( wparam ) + { + case SIZE_MAXIMIZED: type = maximised ; break ; + case SIZE_MINIMIZED: type = minimised ; break ; + case SIZE_RESTORED: type = restored ; break ; + case SIZE_MAXHIDE: + case SIZE_MAXSHOW: + default: + return ::DefWindowProc( m_hwnd , message , wparam , lparam ) ; + } + onSize( type , LOWORD(lparam) , HIWORD(lparam) ) ; + return 0 ; + } + + case WM_MOVE: + { + onMove( (int)LOWORD(lparam) , (int)HIWORD(lparam) ) ; + return 0 ; + } + + case WM_COMMAND: + { + UINT type = GET_WM_COMMAND_CMD( wparam , lparam ) ; + const UINT menu = 0 ; + const UINT accelerator = 1 ; + + if( type == menu || type == accelerator ) + { + G_DEBUG( "GCracker::onMenuCommand" ) ; + onMenuCommand( wparam ) ; + } + + else + { + HWND window = GET_WM_COMMAND_HWND( wparam , lparam ) ; + UINT id = GET_WM_COMMAND_ID( wparam , lparam ) ; + UINT message = type ; + G_DEBUG( "GCracker::onControlCommand" ) ; + onControlCommand( window , message , id ) ; + } + + return 0 ; + } + + case WM_LBUTTONDBLCLK: + { + const unsigned x = LOWORD(lparam) ; + const unsigned y = HIWORD(lparam) ; + const unsigned keys = wparam ; + onDoubleClick( x , y , keys ) ; + return 0 ; + } + + case WM_LBUTTONDOWN: + { + const int x = static_cast(LOWORD(lparam)) ; + const int y = static_cast(HIWORD(lparam)) ; + const bool shift = !!( wparam & MK_SHIFT ) ; + const bool control = !!( wparam & MK_CONTROL ) ; + //const bool left = !!( wparam & MK_LBUTTON ) ; + //const bool right = !!( wparam & MK_RBUTTON ) ; + //const bool middle = !!( wparam & MK_MBUTTON ) ; + (void)onLeftMouseButtonDown( x , y , shift , control ) ; + return 0 ; + } + + case WM_LBUTTONUP: + { + const int x = static_cast(LOWORD(lparam)) ; + const int y = static_cast(HIWORD(lparam)) ; + const bool shift = !!( wparam & MK_SHIFT ) ; + const bool control = !!( wparam & MK_CONTROL ) ; + //const bool left = !!( wparam & MK_LBUTTON ) ; + //const bool right = !!( wparam & MK_RBUTTON ) ; + //const bool middle = !!( wparam & MK_MBUTTON ) ; + (void)onLeftMouseButtonUp( x , y , shift , control ) ; + return 0 ; + } + + case WM_MOUSEMOVE: + { + const unsigned x = LOWORD(lparam) ; + const unsigned y = HIWORD(lparam) ; + const bool shift = !!( wparam & MK_SHIFT ) ; + const bool control = !!( wparam & MK_CONTROL ) ; + const bool left = !!( wparam & MK_LBUTTON ) ; + const bool middle = !!( wparam & MK_MBUTTON ) ; + const bool right = !!( wparam & MK_RBUTTON ) ; + (void)onMouseMove( x , y , shift , control , + left , middle , right ) ; + return 0 ; + } + + case WM_GETMINMAXINFO: + { + MINMAXINFO far *f_p = (MINMAXINFO far *)lparam ; + G_ASSERT( f_p != NULL ) ; + int dx = f_p->ptMaxSize.x ; + int dy = f_p->ptMaxSize.y ; + onDimension( dx , dy ) ; + f_p->ptMaxTrackSize.x = f_p->ptMaxSize.x = dx ; + f_p->ptMaxTrackSize.y = f_p->ptMaxSize.y = dy ; + return 0 ; + } + + case WM_USER: + { + return onUser( wparam , lparam ) ; + } + + case WM_USER+1: // see wm_idle() + { + return onIdle() ? 1 : 0 ; + } + + case WM_TIMER: + { + onTimer( wparam ) ; + return 0 ; + } + + case WM_INITMENUPOPUP: + { + onInitMenuPopup( (HMENU)wparam , (unsigned)LOWORD(lparam) , + !!HIWORD(lparam) ) ; + return 0 ; + } + + case WM_QUERYNEWPALETTE: + { + bool realised = onPalette() ; + return realised ? 1 : 0 ; + } + + case WM_PALETTECHANGED: + { + HWND hwnd_other = reinterpret_cast(wparam) ; + if( m_hwnd != hwnd_other ) + onPaletteChange() ; + return 0 ; + } + } + + defolt = true ; // ie. call DefWindowProc() + return 0L ; // ignored +} + +//static +unsigned int GGui::Cracker::wm_idle() +{ + return WM_USER+1 ; +} + +// trivial default implementations of virtual functions... + +HBRUSH GGui::Cracker::onControlColour( HDC , HWND , WORD ) +{ + return 0 ; +} + +void GGui::Cracker::onSysColourChange() +{ +} + +bool GGui::Cracker::onCreate() +{ + return true ; +} + +bool GGui::Cracker::onPaintMessage() +{ + return false ; +} + +void GGui::Cracker::onPaint( HDC ) +{ +} + +bool GGui::Cracker::onClose() +{ + return true ; +} + +void GGui::Cracker::onDestroy() +{ +} + +void GGui::Cracker::onNcDestroy() +{ +} + +void GGui::Cracker::onMenuCommand( UINT ) +{ +} + +void GGui::Cracker::onControlCommand( HWND , UINT , UINT ) +{ +} + +bool GGui::Cracker::onDrop( const G::Strings & ) +{ + return false ; +} + +void GGui::Cracker::onSize( SizeType , unsigned , unsigned ) +{ +} + +void GGui::Cracker::onMove( int , int ) +{ +} + +void GGui::Cracker::onGetFocus( HWND ) +{ +} + +void GGui::Cracker::onLooseFocus( HWND ) +{ +} + +void GGui::Cracker::onChar( WORD , unsigned ) +{ +} + +void GGui::Cracker::onDimension( int & , int & ) +{ +} + +void GGui::Cracker::onDoubleClick( unsigned , unsigned , unsigned ) +{ +} + +void GGui::Cracker::onTimer( unsigned ) +{ +} + +void GGui::Cracker::onInitMenuPopup( HMENU , unsigned , bool ) +{ +} + +bool GGui::Cracker::onIdle() +{ + return false ; +} + +LRESULT GGui::Cracker::onUser( WPARAM , LPARAM ) +{ + return 0 ; +} + +bool GGui::Cracker::onEraseBackground( HDC hdc ) +{ + WPARAM wparam = reinterpret_cast(hdc) ; + return !! ::DefWindowProc( m_hwnd , WM_ERASEBKGND , wparam , 0L ) ; +} + +void GGui::Cracker::onMouseMove( unsigned x , unsigned y , + bool shift_key_down , bool control_key_down , + bool left_button_down , bool middle_button_down , + bool right_button_down ) +{ +} + +void GGui::Cracker::onLeftMouseButtonDown( int x , int y , + bool shift_key_down , bool control_key_down ) +{ +} + +void GGui::Cracker::onLeftMouseButtonUp( int x , int y , + bool shift_key_down , bool control_key_down ) +{ +} + +bool GGui::Cracker::onPalette() +{ + return false ; +} + +void GGui::Cracker::onPaletteChange() +{ +} + + diff --git a/src/win32/gcracker.h b/src/win32/gcracker.h new file mode 100644 index 0000000..d28e680 --- /dev/null +++ b/src/win32/gcracker.h @@ -0,0 +1,234 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gcracker.h +// + +#ifndef G_CRACKER_H +#define G_CRACKER_H + +#include "gdef.h" +#include "gwinbase.h" +#include "gstrings.h" +#include +#include + +namespace GGui +{ + class Cracker ; +} ; + +// Class: GGui::Cracker +// Description: The Cracker class encapsulates a typical +// window procedure by 'cracking' Windows messages +// into virtual functions. +// +class GGui::Cracker : public WindowBase +{ +public: + explicit Cracker( HWND hwnd ) ; + // Constructor. + + virtual ~Cracker() ; + // Virtual destructor. + + Cracker( const Cracker &other ) ; + // Copy constructor. + + Cracker &operator=( const Cracker &other ) ; + // Assignment operator. + + LRESULT crack( unsigned msg , WPARAM w , LPARAM l , bool &defolt ) ; + // Cracks the given message, calling + // virtual functions as appropriate. + // If the message is not processed + // then 'defolt' is set to true: the + // user should then normally call + // DefWindowProc(). + + static unsigned int wm_idle() ; + // Returns a message number which should be used for + // idle messages. See onIdle(). + +protected: + virtual bool onEraseBackground( HDC hdc ) ; + // Overridable. Called when the window + // receives a WM_ERASEBKGND message. The default + // implementation uses the brush from the + // window class registration. Returns true + // if the background was erased. + + virtual HBRUSH onControlColour( HDC hDC , HWND hWndControl , WORD type ) ; + // Overridable. Called when the window + // receives a WM_CTLCOLOR message. + // + // Under Win32 all the various MW_CTLCOLOR + // messages call this method. + + virtual void onSysColourChange() ; + // Overridable. Called when the window + // receives a WM_SYSCOLORCHANGE message. + + virtual bool onCreate() ; + // Overridable. Called when the window + // receives a WM_CREATE message. + // The main window should return false + // if the application should fail to start up. + + virtual bool onPaintMessage() ; + // Overridable. Called when the window + // receives a WM_PAINT message, before + // ::BeginPaint() is called. + // If the override returns true then + // the message is considered to be + // fully processed and onPaint() + // is not used. + + virtual void onPaint( HDC dc ) ; + // Overridable. Called when the window + // receives a WM_PAINT message, + // after ::BeginPaint(). + + virtual bool onClose() ; + // Overridable. Called when the window + // receives a WM_CLOSE message. The main + // window should return true if the + // application should terminate. + + virtual void onDestroy() ; + // Overridable. Called when the window + // receives a WM_DESTROY message. + + virtual void onNcDestroy() ; + // Overridable. Called when the window + // receives a WM_NCDESTROY message. + + virtual void onMenuCommand( UINT id ) ; + // Overridable. Called when the window + // receives a WM_COMMAND message resulting + // from a menu action. + + virtual void onControlCommand( HWND hwnd , UINT message , UINT id ) ; + // Overridable. Called when the window + // receives a WM_COMMAND message from a + // control. + + virtual bool onDrop( const G::Strings & files ) ; + // Overridable. Called when the window + // receives a WM_DROPFILES message. + // Returns false if the file list + // is ignored. See also: DragAcceptFiles(). + + enum SizeType { maximised , minimised , restored } ; + virtual void onSize( SizeType type , unsigned dx , unsigned dy ) ; + // Overridable. Called on receipt of + // a WM_SIZE message. + + virtual void onMove( int x , int y ) ; + // Overridable. Called on receipt of + // a WM_MOVE message. + + virtual void onLooseFocus( HWND to ) ; + // Overridable. Called on receipt of + // a WM_KILLFOCUS message. + + virtual void onGetFocus( HWND from ) ; + // Overridable. Called on receipt of + // a WM_SETFOCUS message, indicating that + // this windows has just received input focus. + // Typically this is overridden with a + // call to ::SetFocus(), passing focus on to + // some more appropriate window. + + virtual void onChar( WORD vkey , unsigned repeat_count ) ; + // Overridable. Called on receipt of + // a WM_CHAR message. + + virtual void onDimension( int &dx , int &dy ) ; + // Overridable. Called on receipt of + // a WM_MINMAXINFO message. + + virtual void onDoubleClick( unsigned x , unsigned y , unsigned keys ) ; + // Overridable. Called when the left mouse + // button is double clicked (but depending + // on the window class-style). + + virtual void onTimer( unsigned id ) ; + // Overridable. Called on receipt of a WM_TIMER + // message. + + virtual LRESULT onUser( WPARAM wparam , LPARAM lparam ) ; + // Overridable. Called on receipt of a WM_USER + // message. + + virtual void onInitMenuPopup( HMENU hmenu , unsigned position , bool system_menu ) ; + // Overridable. Called just before a popup menu + // is displayed. Overrides will normally enable + // and/or grey out menu items as appropriate. + + virtual void onMouseMove( unsigned x , unsigned y , + bool shift_key_down , bool control_key_down , + bool left_button_down , bool middle_button_down , + bool right_button_down ) ; + // Overridable. Called on receipt of a mouse-move message. + + virtual void onLeftMouseButtonDown( int x , int y , + bool shift_key_down , bool control_key_down ) ; + // Overridable. Called on receipt of a mouse left-button-down + // message. + + virtual void onLeftMouseButtonUp( int x , int y , + bool shift_key_down , bool control_key_down ) ; + // Overridable. Called on receipt of a mouse left-button-up + // message. + + virtual bool onPalette() ; + // Called when the window gets focus, allowing + // it to realise its own palette into the + // system-wide hardware palette. If the + // window has a palette it should realise + // it and return true. If it has no palette + // it should return false. + // + // See also: WM_QUERYNEWPALETTE + + virtual void onPaletteChange() ; + // Called when some other window changes + // the system-wide hardware palette. + // + // If a window has a palette, but it ignores this + // message, then the window's colors will be bogus + // while another application has input focus. + // + // See also: WM_PALETTECHANGED + + virtual bool onIdle() ; + // Called whenever the event loop becomes empty. + // If true is returned then it is called again + // (as long as the queue is still empty). +} ; + +inline +GGui::Cracker::Cracker( const Cracker & other ) : + WindowBase(other) +{ +} + +#endif diff --git a/src/win32/gpump.cpp b/src/win32/gpump.cpp new file mode 100644 index 0000000..fcf0950 --- /dev/null +++ b/src/win32/gpump.cpp @@ -0,0 +1,80 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gpump.cpp +// + +#include "gdef.h" +#include "gpump.h" +#include "gcracker.h" +#include "gdebug.h" + +void GGui::Pump::run() +{ + runCore( false , 0 , 0 ) ; +} + +void GGui::Pump::run( HWND idle_window , unsigned int idle_message ) +{ + G_LOG( "GGui::Pump::run: " << idle_window << " " << idle_message ) ; + ::PostMessage( idle_window , GGui::Cracker::wm_idle() , 0 , 0 ) ; // pump priming + runCore( true , idle_window , idle_message ) ; +} + +void GGui::Pump::runCore( bool idle , HWND hwnd_idle , unsigned int wm_idle ) +{ + MSG msg ; + while( ::GetMessage( &msg , NULL , 0 , 0 ) ) + { + if( dialogMessage( msg ) ) // (may be stubbed out as a build option) + { + ; // no-op + } + else + { + ::TranslateMessage( &msg ) ; + ::DispatchMessage( &msg ) ; + } + + while( idle && empty() && sendIdle(hwnd_idle,wm_idle) ) + { + ; // no-op + } + } +} + +bool GGui::Pump::empty() +{ + MSG msg ; + bool message = !! ::PeekMessage( &msg , NULL , 0 , 0 , PM_NOREMOVE ) ; + return ! message ; +} + +bool GGui::Pump::sendIdle( HWND hwnd_idle , unsigned int wm_idle ) +{ + G_ASSERT( hwnd_idle != 0 ) ; + return !! ::SendMessage( hwnd_idle , wm_idle , 0 , 0 ) ; +} + +void GGui::Pump::quit() +{ + ::PostQuitMessage( 0 ) ; +} + diff --git a/src/win32/gpump.h b/src/win32/gpump.h new file mode 100644 index 0000000..5f3fdbc --- /dev/null +++ b/src/win32/gpump.h @@ -0,0 +1,65 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gpump.h +// + +#ifndef G_PUMP_H +#define G_PUMP_H + +#include "gdef.h" + +namespace GGui +{ + class Pump ; +} ; + +// Class: GGui::Pump +// +// Description: A static class which implements a +// Windows message pump. Uses GDialog::dialogMessage() +// in its implementation in order to support modeless +// dialog boxes. +// +class GGui::Pump +{ +public: + static void run() ; + // GetMessage()/DispatchMessage() message pump. + // Typically called from WinMain(). + + static void run( HWND idle_window , unsigned int idle_message ) ; + // An overload which sends idle messages once + // the message queue is empty. If the idle message + // handler returns 1 then the message is sent again. + + static void quit() ; + // Causes run() to return (once the call stack + // has unwound). + +private: + static bool dialogMessage( MSG & ) ; // links gpump.cpp to gdialog.cpp (or not) + Pump() ; // not implemented + static bool empty() ; + static bool sendIdle( HWND , unsigned int ) ; + static void runCore( bool , HWND , unsigned int ) ; +} ; + +#endif diff --git a/src/win32/gpump_nodialog.cpp b/src/win32/gpump_nodialog.cpp new file mode 100644 index 0000000..d99ff4f --- /dev/null +++ b/src/win32/gpump_nodialog.cpp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gpump_nodialog.cpp +// + +#include "gdef.h" +#include "gpump.h" + +//static +bool GGui::Pump::dialogMessage( MSG & msg ) +{ + return false ; +} + + diff --git a/src/win32/gsize.h b/src/win32/gsize.h new file mode 100644 index 0000000..0f97fa0 --- /dev/null +++ b/src/win32/gsize.h @@ -0,0 +1,71 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gsize.h +// + +#ifndef G_SIZE_H +#define G_SIZE_H + +#include "gdef.h" +#include + +namespace GGui +{ + class Size ; +} ; + +// Class: GGui::Size +// Description: A structure representing the size of a rectangle (typically +// a GUI window). +// +class GGui::Size +{ +public: + unsigned long dx ; + unsigned long dy ; + Size() ; + void streamOut( std::ostream & ) const ; +} ; + +inline +GGui::Size::Size() : + dx(0) , + dy(0) +{ +} + +inline +void GGui::Size::streamOut( std::ostream & s ) const +{ + s << "(" << dx << "," << dy << ")" ; +} + +namespace GGui +{ + inline + std::ostream & operator<<( std::ostream & stream , const Size & size ) + { + size.streamOut( stream ) ; + return stream ; + } +} ; + +#endif diff --git a/src/win32/gwinbase.cpp b/src/win32/gwinbase.cpp new file mode 100644 index 0000000..82f523a --- /dev/null +++ b/src/win32/gwinbase.cpp @@ -0,0 +1,86 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gwinbase.cpp +// + +#include "gdef.h" +#include "gwinbase.h" +#include "gdebug.h" +#include + +GGui::WindowBase::WindowBase( HWND hwnd ) : + m_hwnd(hwnd) +{ +} + +GGui::WindowBase::~WindowBase() +{ +} + +HWND GGui::WindowBase::handle() const +{ + return m_hwnd ; +} + +GGui::WindowBase & GGui::WindowBase::operator=( const WindowBase & other ) +{ + m_hwnd = other.m_hwnd ; + return *this ; +} + +GGui::WindowBase::WindowBase( const WindowBase &other ) : + m_hwnd(other.m_hwnd) +{ +} + +void GGui::WindowBase::setHandle( HWND hwnd ) +{ + m_hwnd = hwnd ; +} + +GGui::Size GGui::WindowBase::internalSize() const +{ + Size size ; + RECT rect ; + if( ::GetClientRect( m_hwnd , &rect ) ) + { + G_ASSERT( rect.right >= rect.left ) ; + G_ASSERT( rect.bottom >= rect.top ) ; + size.dx = rect.right - rect.left ; + size.dy = rect.bottom - rect.top ; + } + return size ; +} + +GGui::Size GGui::WindowBase::externalSize() const +{ + GGui::Size size ; + RECT rect ; + if( ::GetWindowRect( m_hwnd , &rect ) ) + { + G_ASSERT( rect.right >= rect.left ) ; + G_ASSERT( rect.bottom >= rect.top ) ; + size.dx = rect.right - rect.left ; + size.dy = rect.bottom - rect.top ; + } + return size ; +} + diff --git a/src/win32/gwinbase.h b/src/win32/gwinbase.h new file mode 100644 index 0000000..a76fac5 --- /dev/null +++ b/src/win32/gwinbase.h @@ -0,0 +1,74 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gwinbase.h +// + +#ifndef G_WINBASE_H +#define G_WINBASE_H + +#include "gdef.h" +#include "gsize.h" + +namespace GGui +{ + class WindowBase ; +} ; + +// Class: GGui::WindowBase +// Description: A low-level window class which +// encapsulates a window handle and methods +// to retrieve basic window attributes. +// +class GGui::WindowBase +{ + +public: + explicit WindowBase( HWND hwnd ) ; + // Constructor. + + virtual ~WindowBase() ; + // Virtual destructor. + + HWND handle() const ; + // Returns the window handle. + + WindowBase &operator=( const WindowBase &other ) ; + // Assignment operator. + + WindowBase( const WindowBase &other ) ; + // Copy constructor. + + Size externalSize() const ; + // Returns the external size of the window. + + Size internalSize() const ; + // Returns the internal size of the window. + // (ie. the size of the client area) + +protected: + void setHandle( HWND hwnd ) ; + // Sets the window handle. + +protected: + HWND m_hwnd ; +} ; + +#endif diff --git a/src/win32/gwindow.cpp b/src/win32/gwindow.cpp new file mode 100644 index 0000000..e648e30 --- /dev/null +++ b/src/win32/gwindow.cpp @@ -0,0 +1,331 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gwindow.cpp +// + +#include "gdef.h" +#include "gwindow.h" +#include "gdebug.h" +#include "glog.h" + +GGui::Window::Window( HWND hwnd ) : + Cracker(hwnd) +{ + G_DEBUG( "GGui::Window::ctor: this = " << this ) ; +} + +GGui::Window::~Window() +{ +} + +extern "C" LRESULT CALLBACK gwindow_wndproc_export( HWND hwnd , UINT message , WPARAM wparam , LPARAM lparam ) +{ + return GGui::Window::wndProc( hwnd , message , wparam , lparam ) ; +} + +WNDPROC GGui::Window::windowProcedure() +{ + return gwindow_wndproc_export ; +} + +bool GGui::Window::registerWindowClass( const char *class_name , + HINSTANCE hinstance , UINT style , + HICON icon , HCURSOR cursor , + HBRUSH background , const char *menu_name ) +{ + WNDCLASS c ; + + c.style = style ; + c.lpfnWndProc = gwindow_wndproc_export ; + c.cbClsExtra = 4 ; // reserved + c.cbWndExtra = 4 ; // for pointer to Window + c.hInstance = hinstance ; + c.hIcon = icon ; + c.hCursor = cursor ; + c.hbrBackground = background ; + c.lpszMenuName = menu_name ; + c.lpszClassName = class_name ; + + return ::RegisterClass( &c ) != 0 ; +} + +bool GGui::Window::create( const char *class_name , + const char *title , DWORD style , + int x , int y , int dx , int dy , + HWND parent , HMENU menu , HINSTANCE hinstance ) +{ + G_ASSERT( m_hwnd == NULL ) ; + + if( title == NULL ) + title = "" ; + + void *vp = reinterpret_cast(this) ; + m_hwnd = ::CreateWindow( class_name , title , + style , x , y , dx , dy , + parent , menu , hinstance , vp ) ; + + G_DEBUG( "GGui::Window::create: handle " << m_hwnd ) ; + return m_hwnd != NULL ; +} + +void GGui::Window::update() +{ + G_ASSERT( m_hwnd != NULL ) ; + ::UpdateWindow( m_hwnd ) ; +} + +void GGui::Window::show( int style ) +{ + G_ASSERT( m_hwnd != NULL ) ; + ::ShowWindow( m_hwnd , style ) ; +} + +#if 0 +void GGui::Window::setGlobal() +{ + G_ASSERT( m_hwnd != NULL ) ; + G_ASSERT( sizeof(LONG) >= sizeof(HWND) ) ; + LONG cl = reinterpret_cast(m_hwnd) ; + ::SetClassLong( m_hwnd , 0 , cl ) ; +} +HWND GGui::Window::getGlobal() const +{ + G_ASSERT( m_hwnd != NULL ) ; + LONG cl = ::GetClassLong( m_hwnd , 0 ) ; + return reinterpret_cast(cl) ; +} +#endif + +void GGui::Window::invalidate( bool erase ) +{ + ::InvalidateRect( m_hwnd , NULL , erase ) ; +} + +GGui::Window * GGui::Window::instance( HWND hwnd ) +{ + G_ASSERT( hwnd != NULL ) ; + LONG wl = ::GetWindowLong( hwnd , 0 ) ; + void far * vp = reinterpret_cast(wl) ; + return reinterpret_cast(vp) ; +} + +//static +LRESULT GGui::Window::wndProc( HWND hwnd , UINT msg , WPARAM wparam , LPARAM lparam ) +{ + if( msg == WM_CREATE ) + { + G_DEBUG( "GGui::Window::wndProc: WM_CREATE: " << hwnd ) ; + CREATESTRUCT *cs = reinterpret_cast(lparam) ; + Window *window = reinterpret_cast(cs->lpCreateParams) ; + G_ASSERT( window != NULL ) ; + G_DEBUG( "GGui::Window::wndProc: WM_CREATE: this = " << window ) ; + void far * vp = reinterpret_cast(window) ; + LONG wl = reinterpret_cast(vp) ; + ::SetWindowLong( hwnd , 0 , wl ) ; + window->setHandle( hwnd ) ; + return window->onCreateCore() ? 0 : -1 ; + } + else + { + Window *window = Window::instance( hwnd ) ; + // G_ASSERT( window != NULL ) ; + if( window == NULL ) + return ::DefWindowProc(hwnd,msg,wparam,lparam) ; + + G_ASSERT( hwnd == window->handle() ) ; + return window->wndProc( msg , wparam , lparam ) ; + } +} + +LRESULT GGui::Window::sendUserString( HWND hwnd , const char *string ) +{ + G_ASSERT( string != NULL ) ; + return ::SendMessage( hwnd , WM_USER_STRING , 0 , (LPARAM)(const char far *)string ) ; +} + +LRESULT GGui::Window::onUserString( const char * ) +{ + return 0 ; +} + +LRESULT GGui::Window::wndProc( unsigned message , WPARAM wparam , LPARAM lparam ) +{ + if( message == WM_USER_STRING ) + { + G_DEBUG( "GGui::Window::onUserString" ) ; + if( lparam ) + { + std::string s( (const char far *)lparam ) ; // far to near + return onUserString( s.c_str() ) ; + } + else + { + return onUserString( "" ) ; + } + } + else + { + bool defolt ; + LRESULT rc = wndProcCore( message , wparam , lparam , defolt ) ; + if( defolt ) + return ::DefWindowProc( m_hwnd , message , wparam , lparam ) ; + else + return rc ; + } +} + +LRESULT GGui::Window::wndProcCore( unsigned msg , WPARAM wparam , LPARAM lparam , + bool & defolt ) +{ + try + { + return crack( msg , wparam , lparam , defolt ) ; + } + catch( std::exception & e ) + { + // absorb the exception + G_DEBUG( "GGui::Window::wndProcCore: exception: " << e.what() ) ; + defolt = true ; + return 0 ; // ignored + } +} + +bool GGui::Window::onCreateCore() +{ + try + { + return onCreate() ; + } + catch( std::exception & e ) + { + // absorb the exception + G_DEBUG( "GGui::Window::onCreateCore: exception: " << e.what() ) ; + return false ; + } +} + +void GGui::Window::destroy() +{ + ::DestroyWindow( handle() ) ; +} + +DWORD GGui::Window::windowStyleMain() +{ + return WS_OVERLAPPEDWINDOW ; +} + +DWORD GGui::Window::windowStylePopup() +{ + return + WS_THICKFRAME | + WS_POPUP | + WS_SYSMENU | + WS_CAPTION | + WS_VISIBLE ; +} + +DWORD GGui::Window::windowStyleChild() +{ + return WS_CHILDWINDOW ; +} + +UINT GGui::Window::classStyle( bool redraw ) +{ + return + redraw ? + (CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW) : + (CS_DBLCLKS) ; +} + +HBRUSH GGui::Window::classBrush() +{ + return (HBRUSH)( 1 + COLOR_BACKGROUND ) ; +} + +HICON GGui::Window::classIcon() +{ + return ::LoadIcon( NULL , IDI_APPLICATION ) ; +} + +HCURSOR GGui::Window::classCursor() +{ + return ::LoadCursor( NULL , IDC_ARROW ) ; +} + +GGui::Size GGui::Window::borderSize( bool has_menu ) +{ + // note that the Win3.1 and Win32 help files + // have different definitions of these metrics + // wrt. the +/- CYBORDER -- needs more testing + + // See also AdjustWindowRect, AdjustWindowRectEx and + // MSDN 4 "Ask Dr GUI #10". + + Size size ; + size.dx = ::GetSystemMetrics( SM_CXFRAME ) * 2 ; + size.dy = ::GetSystemMetrics( SM_CYFRAME ) * 2 ; + size.dy += ::GetSystemMetrics( SM_CYCAPTION ) - ::GetSystemMetrics( SM_CYBORDER ) ; + if( has_menu ) + size.dy += ::GetSystemMetrics( SM_CYMENU ) + ::GetSystemMetrics( SM_CYBORDER ) ; + return size ; +} + +GGui::Size GGui::Window::clientSize() const +{ + Size size ; + RECT rect ; + BOOL success = ::GetClientRect( handle() , &rect ) ; +// if( success ) + { + size.dx = rect.right ; + size.dy = rect.bottom ; + } + return size ; +} + +void GGui::Window::resize( Size new_size , bool repaint ) +{ + // note that GetWindowRect() returns coordinates + // relative to the top left corner of the screen -- + // MoveWindow takes coordinates relative to the + // screen for top-level windows, but relative to + // the parent window for child windows + + RECT rect ; + if( ::GetWindowRect( m_hwnd , &rect ) ) + { + HWND parent = ::GetParent( handle() ) ; + const bool child_window = parent != 0 ; + if( child_window ) + { + rect.left = 0 ; + rect.top = 0 ; + } + + ::MoveWindow( handle() , + rect.left , + rect.top , + new_size.dx , + new_size.dy , + repaint ) ; + } +} + diff --git a/src/win32/gwindow.h b/src/win32/gwindow.h new file mode 100644 index 0000000..eb3557a --- /dev/null +++ b/src/win32/gwindow.h @@ -0,0 +1,208 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gwindow.h +// + +#ifndef G_WINDOW_H +#define G_WINDOW_H + +#include "gdef.h" +#include "gcracker.h" +#include "gsize.h" + +#define WM_USER_STRING (WM_USER + 123) + +namespace GGui +{ + class Window ; +} ; + +// Class: GGui::Window +// Description: A window class. Window messages +// should be processed by overriding the on*() +// virtual functions inherited from GCracker. +// +class GGui::Window : public Cracker +{ +public: + explicit Window( HWND hwnd = 0 ) ; + // Constructor. Normally the default constructor + // is used, followed by create(). Use + // registerWindowClass() before calling + // any create() function. + + virtual ~Window() ; + // Virtual destructor. The Windows window + // is _not_ destroyed. + + static bool registerWindowClass( const char *class_name , + HINSTANCE hinstance , + UINT class_style , + HICON icon , + HCURSOR cursor , + HBRUSH background , + const char *menu_name ) ; + // Registers a window class specifically for Window objects. + // All Window windows must have their window class registered + // in this way before any window is created. + // + // Returns true on success. Fails beningly if the class is already + // registered. + // + // Default values are provided by the methods classStyle(), + // classIcon() , classCursor() and classBrush(). The 'menu_name' + // parameter may be null. + // + // See also ::RegisterClass() and struct WNDCLASS. + + bool create( const char *class_name , + const char *title , DWORD window_style , + int x , int y , int dx , int dy , + HWND parent , HMENU menu_or_child_id , HINSTANCE hinstance ) ; + // Creates the window. Returns true on success. + // + // The given window class name must be the + // name of a window class previously registered + // through registerWindowClass(). + // + // The 'title' parameter may be null. + // + // Default values for the 'window_style' parameter + // are provided by windowStyleMain(), windowStylePopup() + // and windowStyleChild(). + // + // The window size and location parameters may be specified + // using CW_USEDEFAULT. + // + // The 'parent' parameter may be null for POPUP windows + // only. + // + // The 'menu_or_child_id' is a unique child-window identifier + // if the window style is CHILD. Otherwise it is a menu handle. + // A null menu handle means that the window-class menu is used, + // as defined through registerWindowClass(). + // + // See also ::CreateWindow(). + + void update() ; + // Does ::UpdateWindow(). + + void show( int style = SW_SHOW ) ; + // Does ::ShowWindow(). + + void destroy() ; + // Does ::DestroyWindow(). + + void invalidate( bool erase = true ) ; + // Invalidates the window so that it redraws. + + static Size borderSize( bool has_menu = true ) ; + // Returns the size of the border of a _typical_ + // main window. The actual border size will + // depend on the window style and its size + // (since the menu bar changes height at + // run-time). + + Size clientSize() const ; + // Returns the size of the window's client area. + // (The client size is also available in WM_MOVE + // messages and from onSize().) + + void resize( Size new_size , bool repaint = true ) ; + // Resizes the window. The top-left corner stays put. + + static Window * instance( HWND hwnd ) ; + // Maps from a window handle to a Window + // object. The handle must be that of + // a Window window. + + static LRESULT sendUserString( HWND hwnd , const char *string ) ; + // Sends a string to a specified window. + // The other window will receive a WM_USER_STRING + // message (defined above) -- see onUserString(). + + static UINT classStyle( bool redraw = false ) ; + // Returns a general-purpose value for + // resisterWindowClass(..class_style..). + + static DWORD windowStyleMain() ; + // Returns a value for create(..window_style..) + // for a typical 'main' window. + // + // The parent window parameter should be NULL. + // The x,y,dx,dy parameters will normally + // be CW_USEDEFAULT. + + static DWORD windowStylePopup() ; + // Returns a value for create(..window_style..) + // for a typical 'popup' window ie. (in this + // case) a window which typically acts like a + // modeless dialog box -- it can be independetly + // activated, has a title bar but no minimise/ + // maximise buttons, and stays on top of its + // parent (if any). + // + // The parent window parameter may be NULL -- if + // NULL then this window will be independently + // iconised with a separate button on the toolbar. + + static DWORD windowStyleChild() ; + // Returns a value for create(..window_style..) + // for a typical 'child' window. + // + // The parent window parameter cannot be NULL. + + static HBRUSH classBrush() ; + // Returns a default value for + // registerWindowClass(..background..). + + static HICON classIcon() ; + // Returns a default for registerWindowClass(..hicon..). + + static HCURSOR classCursor() ; + // Returns a default for registerWindowClass(..hcursor..). + +protected: + virtual LRESULT onUserString( const char *string ) ; + // Overridable. Called when the window + // receives a WM_USER_STRING message. + + static WNDPROC windowProcedure() ; + // Returns the address of the exported 'C' window + // procedure. This is not for general use -- see + // WindowHidden. + +public: + static LRESULT wndProc( HWND hwnd , UINT message , WPARAM wparam , LPARAM lparam ) ; + // Called directly from the global, exported + // window procedure. The implementation locates + // the particular Window object and calls its + // non-static wndProc() method. + +private: + LRESULT wndProc( UINT message , WPARAM wparam , LPARAM lparam ) ; + LRESULT wndProcCore( UINT , WPARAM , LPARAM , bool & ) ; + bool onCreateCore() ; + Window( const Window &other ) ; // not implemented + Window &operator=( const Window &other ) ; // not implemented +} ; + +#endif diff --git a/src/win32/gwinhid.cpp b/src/win32/gwinhid.cpp new file mode 100644 index 0000000..5a26ffb --- /dev/null +++ b/src/win32/gwinhid.cpp @@ -0,0 +1,86 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gwinhid.cpp +// + +#include "gdef.h" +#include "gwinhid.h" +#include "gdebug.h" +#include + +bool GGui::WindowHidden::m_registered = false ; + +GGui::WindowHidden::WindowHidden( HINSTANCE hinstance ) : + m_destroyed(false) +{ + std::string window_class_name = windowClassName() ; + + if( !m_registered ) + { + bool success = registerWindowClass( window_class_name.c_str() , + hinstance , classStyle() , classIcon() , classCursor() , + classBrush() , /*menu-name*/ NULL ) ; + + G_ASSERT( success || !"GGui::WindowHidden: window class registration error" ) ; + + m_registered = true ; + } + + { + bool success = create( window_class_name.c_str() , + "" , // title + 0 , // window style + 0 , 0 , 10 , 10 , // x,y,dx,dy + 0 , // parent + 0 , // menu + hinstance ) ; + + G_ASSERT( success || !"GGui::WindowHidden: window creation error" ) ; + } +} + +GGui::WindowHidden::~WindowHidden() +{ + if( !m_destroyed && handle() != 0 ) + destroy() ; + + G_ASSERT( !::IsWindow(handle()) ) ; +} + +void GGui::WindowHidden::onNcDestroy() +{ + m_destroyed = true ; +} + +std::string GGui::WindowHidden::windowClassName() +{ + // a fixed class name would create problems for + // Win16 since class names are system-wide -- we + // need a class name which is unique to this + // executable or DLL and common to all processes + // created from it + + WNDPROC wndproc = windowProcedure() ; + std::stringstream ss ; + ss << "GGui::WindowHidden." << (unsigned long)wndproc ; + return ss.str() ; +} + diff --git a/src/win32/gwinhid.h b/src/win32/gwinhid.h new file mode 100644 index 0000000..1f394be --- /dev/null +++ b/src/win32/gwinhid.h @@ -0,0 +1,63 @@ +// +// Copyright (C) 2001 Graeme Walker +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later +// version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// === +// +// gwinhid.h +// + +#ifndef G_WINHID_H +#define G_WINHID_H + +#include "gdef.h" +#include "gwindow.h" +#include + +namespace GGui +{ + class WindowHidden ; +} ; + +// Class: GGui::WindowHidden +// Description: A derivation of GWindow for +// a hidden window (without a parent). +// +class GGui::WindowHidden : public Window +{ +public: + explicit WindowHidden( HINSTANCE hinstance ) ; + // Default constructor. Registers the window + // class if necessary and creates the window. + + virtual ~WindowHidden() ; + // Virtual destructor. + +protected: + void onNcDestroy() ; + +private: + WindowHidden( const WindowHidden & ) ; + void operator=( const WindowHidden & ) ; + std::string windowClassName() ; + +private: + bool m_destroyed ; + static bool m_registered ; +} ; + +#endif diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..e69de29