From ead43a77394f6c0f539c6ad10574d30b9de0fb4f Mon Sep 17 00:00:00 2001 From: Graeme Walker Date: Sat, 8 Sep 2001 12:00:00 +0000 Subject: [PATCH] v0.9.1 --- AUTHORS | 4 + COPYING | 340 +++++ ChangeLog | 9 + INSTALL | 189 +++ Makefile.am | 2 + Makefile.in | 352 +++++ NEWS | 10 + README | 67 + aclocal.m4 | 127 ++ bin/Makefile.am | 10 + bin/Makefile.in | 252 ++++ bin/emailrelay-deliver.sh | 74 + bin/emailrelay-filter.sh | 194 +++ bin/emailrelay-notify.sh | 94 ++ bin/emailrelay-test.sh | 148 ++ bin/emailrelay.sh | 71 + bin/txt2html.sh | 235 ++++ config.h.in | 41 + configure | 2222 ++++++++++++++++++++++++++++++ configure.in | 46 + doc/Makefile.am | 39 + doc/Makefile.in | 239 ++++ doc/developer.txt | 161 +++ doc/emailrelay-poke.1 | 35 + doc/emailrelay.1 | 156 +++ doc/index.html | 15 + doc/reference.txt | 190 +++ doc/userguide.txt | 188 +++ install-sh | 251 ++++ lib/Makefile.am | 1 + lib/Makefile.in | 275 ++++ lib/gcc2.95/Makefile.am | 22 + lib/gcc2.95/Makefile.in | 191 +++ lib/gcc2.95/exception | 37 + lib/gcc2.95/iostream | 36 + lib/gcc2.95/limits | 33 + lib/gcc2.95/sstream | 67 + lib/gcc2.95/xlocale | 30 + lib/msvc6.0/Makefile.am | 21 + lib/msvc6.0/Makefile.in | 191 +++ lib/msvc6.0/cstdio | 35 + lib/msvc6.0/cstdlib | 35 + lib/msvc6.0/cstring | 43 + lib/msvc6.0/ctime | 40 + missing | 190 +++ mkinstalldirs | 40 + src/Makefile.am | 1 + src/Makefile.in | 275 ++++ src/glib/Makefile.am | 73 + src/glib/Makefile.in | 292 ++++ src/glib/garg.cpp | 140 ++ src/glib/garg.h | 114 ++ src/glib/garg_unix.cpp | 32 + src/glib/garg_win32.cpp | 37 + src/glib/gassert.h | 37 + src/glib/gconvert.h | 48 + src/glib/gdaemon.h | 79 ++ src/glib/gdaemon_unix.cpp | 107 ++ src/glib/gdaemon_win32.cpp | 48 + src/glib/gdate.cpp | 266 ++++ src/glib/gdate.h | 134 ++ src/glib/gdatetime.cpp | 103 ++ src/glib/gdatetime.h | 85 ++ src/glib/gdatetime_unix.cpp | 41 + src/glib/gdatetime_win32.cpp | 43 + src/glib/gdebug.h | 35 + src/glib/gdef.h | 190 +++ src/glib/gdirectory.cpp | 75 + src/glib/gdirectory.h | 137 ++ src/glib/gdirectory_unix.cpp | 241 ++++ src/glib/gdirectory_win32.cpp | 302 ++++ src/glib/gexception.cpp | 82 ++ src/glib/gexception.h | 89 ++ src/glib/gfile.cpp | 85 ++ src/glib/gfile.h | 76 + src/glib/gfile_unix.cpp | 32 + src/glib/gfile_win32.cpp | 33 + src/glib/gfs.h | 52 + src/glib/gfs_unix.cpp | 61 + src/glib/gfs_win32.cpp | 60 + src/glib/ggetopt.cpp | 500 +++++++ src/glib/ggetopt.h | 175 +++ src/glib/glog.cpp | 124 ++ src/glib/glog.h | 130 ++ src/glib/glogoutput.cpp | 178 +++ src/glib/glogoutput.h | 113 ++ src/glib/glogoutput_unix.cpp | 64 + src/glib/glogoutput_win32.cpp | 60 + src/glib/gmemory.h | 60 + src/glib/gnumber.cpp | 545 ++++++++ src/glib/gnumber.h | 213 +++ src/glib/gpath.cpp | 424 ++++++ src/glib/gpath.h | 179 +++ src/glib/gpid.h | 77 ++ src/glib/gpid_unix.cpp | 81 ++ src/glib/gpid_win32.cpp | 79 ++ src/glib/gstr.cpp | 467 +++++++ src/glib/gstr.h | 202 +++ src/glib/gstrings.h | 42 + src/glib/gtime.cpp | 98 ++ src/glib/gtime.h | 83 ++ src/gnet/Makefile.am | 60 + src/gnet/Makefile.in | 292 ++++ src/gnet/gaddress.h | 172 +++ src/gnet/gaddress_ipv4.cpp | 437 ++++++ src/gnet/gclient.cpp | 465 +++++++ src/gnet/gclient.h | 117 ++ src/gnet/gclient_unix.cpp | 32 + src/gnet/gclient_win32.cpp | 32 + src/gnet/gdescriptor.h | 38 + src/gnet/gdescriptor_unix.cpp | 37 + src/gnet/gdescriptor_win32.cpp | 37 + src/gnet/gevent.cpp | 206 +++ src/gnet/gevent.h | 237 ++++ src/gnet/gevent_unix.cpp | 33 + src/gnet/gevent_win32.cpp | 33 + src/gnet/geventserver.cpp | 74 + src/gnet/geventserver.h | 65 + src/gnet/glinebuffer.cpp | 99 ++ src/gnet/glinebuffer.h | 81 ++ src/gnet/glocal.h | 69 + src/gnet/glocal_unix.cpp | 73 + src/gnet/glocal_win32.cpp | 91 ++ src/gnet/gnet.h | 49 + src/gnet/grequest.cpp | 218 +++ src/gnet/grequest.h | 116 ++ src/gnet/gresolve.cpp | 102 ++ src/gnet/gresolve.h | 122 ++ src/gnet/gresolve_ipv4.cpp | 38 + src/gnet/gresolve_unix.cpp | 224 +++ src/gnet/gresolve_win32.cpp | 257 ++++ src/gnet/gselect.cpp | 203 +++ src/gnet/gselect.h | 90 ++ src/gnet/gserver.cpp | 154 +++ src/gnet/gserver.h | 160 +++ src/gnet/gsocket.cpp | 419 ++++++ src/gnet/gsocket.h | 368 +++++ src/gnet/gsocket_unix.cpp | 95 ++ src/gnet/gsocket_win32.cpp | 93 ++ src/gnet/gwinsock.cpp | 260 ++++ src/gnet/gwinsock.h | 126 ++ src/main/Makefile.am | 51 + src/main/Makefile.in | 381 +++++ src/main/doxygen.cfg | 170 +++ src/main/emailrelay.dsp | 486 +++++++ src/main/empty_file | 0 src/main/gadminserver.cpp | 154 +++ src/main/gadminserver.h | 103 ++ src/main/gclientprotocol.cpp | 361 +++++ src/main/gclientprotocol.h | 180 +++ src/main/gmessagestore.cpp | 755 ++++++++++ src/main/gmessagestore.h | 189 +++ src/main/gmessagestore_unix.cpp | 34 + src/main/gmessagestore_win32.cpp | 41 + src/main/gprotocolmessage.cpp | 170 +++ src/main/gprotocolmessage.h | 110 ++ src/main/gserverprotocol.cpp | 508 +++++++ src/main/gserverprotocol.h | 207 +++ src/main/gsmtp.h | 31 + src/main/gsmtpclient.cpp | 208 +++ src/main/gsmtpclient.h | 114 ++ src/main/gsmtpserver.cpp | 133 ++ src/main/gsmtpserver.h | 88 ++ src/main/mailrelay.cpp | 429 ++++++ src/main/poke.c | 81 ++ src/win32/Makefile.am | 35 + src/win32/Makefile.in | 192 +++ src/win32/gappinst.cpp | 38 + src/win32/gappinst.h | 72 + src/win32/gcracker.cpp | 471 +++++++ src/win32/gcracker.h | 234 ++++ src/win32/gpump.cpp | 80 ++ src/win32/gpump.h | 65 + src/win32/gpump_nodialog.cpp | 33 + src/win32/gsize.h | 71 + src/win32/gwinbase.cpp | 86 ++ src/win32/gwinbase.h | 74 + src/win32/gwindow.cpp | 331 +++++ src/win32/gwindow.h | 208 +++ src/win32/gwinhid.cpp | 86 ++ src/win32/gwinhid.h | 63 + stamp-h.in | 0 182 files changed, 27444 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 aclocal.m4 create mode 100644 bin/Makefile.am create mode 100644 bin/Makefile.in create mode 100755 bin/emailrelay-deliver.sh create mode 100755 bin/emailrelay-filter.sh create mode 100755 bin/emailrelay-notify.sh create mode 100755 bin/emailrelay-test.sh create mode 100755 bin/emailrelay.sh create mode 100755 bin/txt2html.sh create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.in create mode 100644 doc/Makefile.am create mode 100644 doc/Makefile.in create mode 100644 doc/developer.txt create mode 100644 doc/emailrelay-poke.1 create mode 100644 doc/emailrelay.1 create mode 100644 doc/index.html create mode 100644 doc/reference.txt create mode 100644 doc/userguide.txt create mode 100755 install-sh create mode 100644 lib/Makefile.am create mode 100644 lib/Makefile.in create mode 100644 lib/gcc2.95/Makefile.am create mode 100644 lib/gcc2.95/Makefile.in create mode 100644 lib/gcc2.95/exception create mode 100644 lib/gcc2.95/iostream create mode 100644 lib/gcc2.95/limits create mode 100644 lib/gcc2.95/sstream create mode 100644 lib/gcc2.95/xlocale create mode 100644 lib/msvc6.0/Makefile.am create mode 100644 lib/msvc6.0/Makefile.in create mode 100644 lib/msvc6.0/cstdio create mode 100644 lib/msvc6.0/cstdlib create mode 100644 lib/msvc6.0/cstring create mode 100644 lib/msvc6.0/ctime create mode 100755 missing create mode 100755 mkinstalldirs create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/glib/Makefile.am create mode 100644 src/glib/Makefile.in create mode 100644 src/glib/garg.cpp create mode 100644 src/glib/garg.h create mode 100644 src/glib/garg_unix.cpp create mode 100644 src/glib/garg_win32.cpp create mode 100644 src/glib/gassert.h create mode 100644 src/glib/gconvert.h create mode 100644 src/glib/gdaemon.h create mode 100644 src/glib/gdaemon_unix.cpp create mode 100644 src/glib/gdaemon_win32.cpp create mode 100644 src/glib/gdate.cpp create mode 100644 src/glib/gdate.h create mode 100644 src/glib/gdatetime.cpp create mode 100644 src/glib/gdatetime.h create mode 100644 src/glib/gdatetime_unix.cpp create mode 100644 src/glib/gdatetime_win32.cpp create mode 100644 src/glib/gdebug.h create mode 100644 src/glib/gdef.h create mode 100644 src/glib/gdirectory.cpp create mode 100644 src/glib/gdirectory.h create mode 100644 src/glib/gdirectory_unix.cpp create mode 100644 src/glib/gdirectory_win32.cpp create mode 100644 src/glib/gexception.cpp create mode 100644 src/glib/gexception.h create mode 100644 src/glib/gfile.cpp create mode 100644 src/glib/gfile.h create mode 100644 src/glib/gfile_unix.cpp create mode 100644 src/glib/gfile_win32.cpp create mode 100644 src/glib/gfs.h create mode 100644 src/glib/gfs_unix.cpp create mode 100644 src/glib/gfs_win32.cpp create mode 100644 src/glib/ggetopt.cpp create mode 100644 src/glib/ggetopt.h create mode 100644 src/glib/glog.cpp create mode 100644 src/glib/glog.h create mode 100644 src/glib/glogoutput.cpp create mode 100644 src/glib/glogoutput.h create mode 100644 src/glib/glogoutput_unix.cpp create mode 100644 src/glib/glogoutput_win32.cpp create mode 100644 src/glib/gmemory.h create mode 100644 src/glib/gnumber.cpp create mode 100644 src/glib/gnumber.h create mode 100644 src/glib/gpath.cpp create mode 100644 src/glib/gpath.h create mode 100644 src/glib/gpid.h create mode 100644 src/glib/gpid_unix.cpp create mode 100644 src/glib/gpid_win32.cpp create mode 100644 src/glib/gstr.cpp create mode 100644 src/glib/gstr.h create mode 100644 src/glib/gstrings.h create mode 100644 src/glib/gtime.cpp create mode 100644 src/glib/gtime.h create mode 100644 src/gnet/Makefile.am create mode 100644 src/gnet/Makefile.in create mode 100644 src/gnet/gaddress.h create mode 100644 src/gnet/gaddress_ipv4.cpp create mode 100644 src/gnet/gclient.cpp create mode 100644 src/gnet/gclient.h create mode 100644 src/gnet/gclient_unix.cpp create mode 100644 src/gnet/gclient_win32.cpp create mode 100644 src/gnet/gdescriptor.h create mode 100644 src/gnet/gdescriptor_unix.cpp create mode 100644 src/gnet/gdescriptor_win32.cpp create mode 100644 src/gnet/gevent.cpp create mode 100644 src/gnet/gevent.h create mode 100644 src/gnet/gevent_unix.cpp create mode 100644 src/gnet/gevent_win32.cpp create mode 100644 src/gnet/geventserver.cpp create mode 100644 src/gnet/geventserver.h create mode 100644 src/gnet/glinebuffer.cpp create mode 100644 src/gnet/glinebuffer.h create mode 100644 src/gnet/glocal.h create mode 100644 src/gnet/glocal_unix.cpp create mode 100644 src/gnet/glocal_win32.cpp create mode 100644 src/gnet/gnet.h create mode 100644 src/gnet/grequest.cpp create mode 100644 src/gnet/grequest.h create mode 100644 src/gnet/gresolve.cpp create mode 100644 src/gnet/gresolve.h create mode 100644 src/gnet/gresolve_ipv4.cpp create mode 100644 src/gnet/gresolve_unix.cpp create mode 100644 src/gnet/gresolve_win32.cpp create mode 100644 src/gnet/gselect.cpp create mode 100644 src/gnet/gselect.h create mode 100644 src/gnet/gserver.cpp create mode 100644 src/gnet/gserver.h create mode 100644 src/gnet/gsocket.cpp create mode 100644 src/gnet/gsocket.h create mode 100644 src/gnet/gsocket_unix.cpp create mode 100644 src/gnet/gsocket_win32.cpp create mode 100644 src/gnet/gwinsock.cpp create mode 100644 src/gnet/gwinsock.h create mode 100644 src/main/Makefile.am create mode 100644 src/main/Makefile.in create mode 100644 src/main/doxygen.cfg create mode 100644 src/main/emailrelay.dsp create mode 100644 src/main/empty_file create mode 100644 src/main/gadminserver.cpp create mode 100644 src/main/gadminserver.h create mode 100644 src/main/gclientprotocol.cpp create mode 100644 src/main/gclientprotocol.h create mode 100644 src/main/gmessagestore.cpp create mode 100644 src/main/gmessagestore.h create mode 100644 src/main/gmessagestore_unix.cpp create mode 100644 src/main/gmessagestore_win32.cpp create mode 100644 src/main/gprotocolmessage.cpp create mode 100644 src/main/gprotocolmessage.h create mode 100644 src/main/gserverprotocol.cpp create mode 100644 src/main/gserverprotocol.h create mode 100644 src/main/gsmtp.h create mode 100644 src/main/gsmtpclient.cpp create mode 100644 src/main/gsmtpclient.h create mode 100644 src/main/gsmtpserver.cpp create mode 100644 src/main/gsmtpserver.h create mode 100644 src/main/mailrelay.cpp create mode 100644 src/main/poke.c create mode 100644 src/win32/Makefile.am create mode 100644 src/win32/Makefile.in create mode 100644 src/win32/gappinst.cpp create mode 100644 src/win32/gappinst.h create mode 100644 src/win32/gcracker.cpp create mode 100644 src/win32/gcracker.h create mode 100644 src/win32/gpump.cpp create mode 100644 src/win32/gpump.h create mode 100644 src/win32/gpump_nodialog.cpp create mode 100644 src/win32/gsize.h create mode 100644 src/win32/gwinbase.cpp create mode 100644 src/win32/gwinbase.h create mode 100644 src/win32/gwindow.cpp create mode 100644 src/win32/gwindow.h create mode 100644 src/win32/gwinhid.cpp create mode 100644 src/win32/gwinhid.h create mode 100644 stamp-h.in 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