--- /dev/null
+build-*
\ No newline at end of file
--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, 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 library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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.
+\f
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+Required packages
+-----------------
+
+Before installing pthrlib, you need to make sure you have the following
+packages installed:
+
+postgresql libpq From your distro or from http://www.postgresql.org/
+c2lib http://www.annexia.org/freeware/c2lib/
+cdoc http://www.annexia.org/freeware/cdoc/
+make+ http://www.annexia.org/freeware/makeplus/
+pcre http://www.pcre.org/
+
+Building and installing
+-----------------------
+
+./configure [--prefix=/usr --sysconfdir=/etc]
+make+
+make+ test
+make+ install
+
+See the make+ documentation for additional targets (eg. building RPMs).
+
+Problems
+--------
+
+If the build fails, please examine the following files for clues:
+
+build-*/config.h
+build-*/config.mk
+build-*/config.log
+
+If you discover a bug, please contact the package author.
--- /dev/null
+# -*- Makefile -*-
+#
+# This is a make+ file. Make+ is a set of scripts which enhance GNU
+# make and let you build RPMs, and other package types with just one
+# control file. To build this package you will need to download make+
+# from this site: http://www.annexia.org/freeware/makeplus/
+
+PACKAGE := pthrlib
+VERSION_MAJOR := 3
+VERSION_MINOR := 3.1
+VERSION := $(VERSION_MAJOR).$(VERSION_MINOR)
+
+SUMMARY := small, fast, efficient server library for C
+COPYRIGHT := GNU LGPL
+AUTHOR := Richard W.M. Jones <rich@annexia.org>
+
+define DESCRIPTION
+pthrlib is a library for writing small, fast and efficient servers in
+C. It offers a list of advanced features. This library has been used
+to write a very tiny and fast web server called rws and a closed
+source chat server. All functions are documented in manual pages, and
+example servers are included.
+endef
+
+RPM_REQUIRES := c2lib >= 1.4.0, postgresql-devel >= 7.1
+RPM_GROUP := Development/Libraries
+
+CFLAGS += -Wall -Werror -g -O2 -I$(includedir)/c2lib
+ifeq (1,$(HAVE_PG_CONFIG))
+CFLAGS += -I$(shell pg_config --includedir)
+endif
+ifneq ($(shell uname), SunOS)
+# Avoid a warning about reordering system include paths.
+CFLAGS += $(shell pcre-config --cflags)
+endif
+
+ifeq (1,$(HAVE_PG_CONFIG))
+LIBS += -L$(shell pg_config --libdir) -lpq
+endif
+LIBS += $(shell pcre-config --libs) -lm
+
+OBJS := src/pthr_cgi.o src/pthr_context.o src/pthr_dbi.o \
+ src/pthr_ftpc.o src/pthr_http.o src/pthr_iolib.o \
+ src/pthr_listener.o src/pthr_mutex.o \
+ src/pthr_pseudothread.o src/pthr_reactor.o \
+ src/pthr_rwlock.o src/pthr_server.o src/pthr_stack.o \
+ src/pthr_wait_queue.o
+LOBJS := $(OBJS:.o=.lo)
+
+HEADERS := $(srcdir)/src/pthr_cgi.h $(srcdir)/src/pthr_context.h \
+ $(srcdir)/src/pthr_dbi.h $(srcdir)/src/pthr_ftpc.h \
+ $(srcdir)/src/pthr_http.h $(srcdir)/src/pthr_iolib.h \
+ $(srcdir)/src/pthr_listener.h $(srcdir)/src/pthr_mutex.h \
+ $(srcdir)/src/pthr_pseudothread.h \
+ $(srcdir)/src/pthr_reactor.h \
+ $(srcdir)/src/pthr_rwlock.h $(srcdir)/src/pthr_server.h \
+ $(srcdir)/src/pthr_stack.h $(srcdir)/src/pthr_wait_queue.h
+
+all: build
+
+configure:
+ @pg_config --version || \
+ ( echo "PostgreSQL must be installed - make sure PATH" \
+ "contains pg bin directory"; exit 1 )
+ $(MP_CONFIGURE_START)
+ $(MP_REQUIRE_LIB) pmap c2lib
+ $(MP_CHECK_PROG) pg_config
+ $(MP_CHECK_LIB) PQconnectStart pq
+ $(MP_CHECK_HEADERS) alloca.h arpa/inet.h assert.h \
+ ctype.h dirent.h errno.h \
+ execinfo.h fcntl.h grp.h libpq-fe.h netdb.h \
+ netinet/in.h netinet/ip.h netinet/ip_icmp.h postgresql/libpq-fe.h \
+ pwd.h setjmp.h signal.h string.h syslog.h sys/mman.h sys/poll.h \
+ sys/socket.h sys/stat.h sys/syslimits.h sys/time.h sys/types.h \
+ sys/uio.h sys/wait.h \
+ time.h ucontext.h unistd.h
+ $(MP_CHECK_FUNCS) backtrace getenv gettimeofday gmtime putenv setenv \
+ socket strftime syslog time unsetenv PQescapeString
+ $(srcdir)/conf/test_setcontext.sh
+ $(MP_CONFIGURE_END)
+
+build: static dynamic examples/pthr_eg1_echo examples/pthr_eg2_server \
+ manpages syms
+
+# Build the static library.
+
+static: src/libpthrlib.a
+
+src/libpthrlib.a: $(OBJS)
+ $(MP_LINK_STATIC) $@ $^
+
+# Build the dynamic library.
+
+dynamic: src/libpthrlib.so
+
+src/libpthrlib.so: $(LOBJS)
+ $(MP_LINK_DYNAMIC) $@ $^ $(LIBS)
+
+# Build object files.
+src/%.o: src/%.c
+ $(CC) $(CFLAGS) -I../src -c $< -o $@
+
+# Build dynamic object files.
+src/%.lo: src/%.c
+ $(CC) $(CFLAGS) -fPIC -I../src -c $< -o $@
+
+# Build the example programs.
+
+examples/pthr_eg1_echo: examples/pthr_eg1_echo.o examples/pthr_eg1_echo_main.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+
+examples/pthr_eg2_server: examples/pthr_eg2_server.o \
+ examples/pthr_eg2_server_main.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+
+# Build object files.
+examples/%.o: examples/%.c
+ $(CC) $(CFLAGS) -I../src -c $< -o $@
+
+# Build the manual pages.
+
+manpages: $(srcdir)/src/*.h
+ if cdoc; then \
+ rm -f *.3; \
+ cdoc \
+ --author '$(AUTHOR)' \
+ --license '$(COPYRIGHT)' \
+ --version '$(PACKAGE)-$(VERSION)' \
+ $^; \
+ fi
+
+# Build the symbols table.
+
+syms: src/libpthrlib.syms
+
+src/libpthrlib.syms: src/libpthrlib.so
+ nm $< | sort | grep -i '^[0-9a-f]' | awk '{print $$1 " " $$3}' > $@
+
+test: src/test_context src/test_reactor src/test_pseudothread src/test_select \
+ src/test_bigstack src/test_except1 src/test_except2 src/test_except3 \
+ src/test_mutex src/test_rwlock src/test_dbi
+ LD_LIBRARY_PATH=src:$(LD_LIBRARY_PATH) $(MP_RUN_TESTS) $^
+
+src/test_context: src/test_context.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_reactor: src/test_reactor.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_pseudothread: src/test_pseudothread.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_select: src/test_select.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_bigstack: src/test_bigstack.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_except1: src/test_except1.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_except2: src/test_except2.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_except3: src/test_except3.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_mutex: src/test_mutex.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_rwlock: src/test_rwlock.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+src/test_dbi: src/test_dbi.o
+ $(CC) $(CFLAGS) $^ -o $@ -Lsrc -lpthrlib $(LIBS)
+
+install:
+ install -d $(DESTDIR)$(libdir)
+ install -d $(DESTDIR)$(includedir)
+ install -d $(DESTDIR)$(man3dir)
+ install -d $(DESTDIR)$(datadir)/rws/symtabs/
+ install -d $(DESTDIR)$(bindir)
+
+ $(MP_INSTALL_STATIC_LIB) src/libpthrlib.a
+ $(MP_INSTALL_DYNAMIC_LIB) src/libpthrlib.so
+
+ install -m 0644 $(HEADERS) $(DESTDIR)$(includedir)
+ install -m 0644 *.3 $(DESTDIR)$(man3dir)
+ install -m 0644 src/*.syms $(DESTDIR)$(datadir)/rws/symtabs/
+ install -m 0755 examples/pthr_eg1_echo examples/pthr_eg2_server \
+ $(DESTDIR)$(bindir)
+
+define WEBSITE
+<% include page_header.msp %>
+
+ <h1>$(PACKAGE) - $(SUMMARY)</h1>
+
+ <p>
+ <tt>pthrlib</tt> is a library for writing small, fast and efficient
+ servers in C. It offers a list of advanced features (see below).
+ This library has been used to write a very tiny and fast
+ <a href="../rws/">web server called rws</a> and
+ a closed source chat server. All functions are documented in
+ manual pages, and example servers are included.
+ </p>
+
+ <p>
+ It contains the following features:
+ </p>
+
+ <ul>
+ <li> <i>reactor & pseudothreads</i>: underpinning
+ the whole library is a lightweight cooperative threading
+ library written on top of a Reactor pattern. Typically
+ you can create of the order of thousands of threads
+ (the number of threads is generally limited by other
+ things like how many file descriptors your C library
+ supports -- assuming each thread is handling one client
+ over one socket). Pseudothreads support thread
+ listings and throw/catch-style exception handling.
+ <li> <i>iolib</i>: a buffered I/O library written
+ on top of the pseudothread "syscalls".
+ <li> <i>http</i>: a library for writing HTTP/1.1
+ RFC-compliant servers.
+ <li> <i>cgi</i>: a library for writing CGI scripts
+ in C which run inside the server [new in pthrlib 2.0.1].
+ <li> <i>dbi</i>: a PostgreSQL database interface,
+ modelled on Perl's DBI. Includes connection pooling
+ [new in pthrlib 3.0.8].
+ <li> <i>wait_queue</i>: synchronize between threads
+ using wait queues (several threads go to sleep on
+ the wait queue until some point later when another
+ thread wakes them all up).
+ <li> <i>mutex, rwlock</i>: simple mutual exclusion
+ locks and multiple-reader/single-writer locks.
+ <li> <i>listener</i>: a thread class which listens
+ for connections on a given port and throws off
+ threads to process them.
+ <li> <i>ftpc</i>: an FTP client library.
+ </ul>
+
+ <p>
+ <a href="doc/">There is extensive documentation and
+ a tutorial here.</a>
+ </p>
+
+ <h2>Download</h2>
+
+ <table border="1">
+ <tr>
+ <th> File </th>
+ <th> Format </th>
+ <th> Contents </th>
+ </tr>
+ <tr>
+ <td> <a href="$(PACKAGE)-$(VERSION).tar.gz">$(PACKAGE)-$(VERSION).tar.gz</a> </td>
+ <td> tar.gz </td>
+ <td> Latest source distribution </td>
+ </tr>
+ <tr>
+ <td> <a href="$(PACKAGE)-$(VERSION)-1.i686.rpm">$(PACKAGE)-$(VERSION)-1.i686.rpm</a> </td>
+ <td> i686 binary RPM </td>
+ <td> Binary development libraries, header files, man pages
+ for Red Hat Linux </td>
+ </tr>
+ <tr>
+ <td> <a href="$(PACKAGE)-$(VERSION)-1.src.rpm">$(PACKAGE)-$(VERSION)-1.src.rpm</a> </td>
+ <td> source RPM </td>
+ <td> Source files for Red Hat Linux </td>
+ </tr>
+ </table>
+
+ <p>
+ This library requires <a href="../c2lib/">c2lib</a>.
+ To rebuild the manual pages which come with the
+ package, you will also need to download the
+ <a href="../c2lib/">cdoc</a> program. Since
+ 3.2.0, pthrlib requires the <a href="../makeplus/">make+</a>
+ build system.
+ </p>
+
+ <p>
+ <a href="/cgi-bin/autopatch.pl?dir=pthrlib">Patches between versions
+ ...</a>
+ </p>
+
+ <h2>News</h2>
+
+<p>
+<b>Sat Feb 1 2003:</b>
+Updated README and INSTALL files. Updated
+<code>Makefile+</code> to work with the new
+version of make+. Ported to FreeBSD 5.0.
+Added connection pooling to pthr_dbi library.
+<strong>NB: The RPMs were built on Debian and may not work on Red Hat
+Linux.</strong> </p>
+
+<p>
+<b>Sat Feb 8 17:00:47 GMT 2003:</b>
+Ported to Solaris, OpenBSD and FreeBSD (thanks to
+<a href="http://www.azazel.net/">Jeremy Sowden</a>
+and <a href="http://www.mondaymorning.org/">Richard Baker</a>
+for help and equipment). Updated to work with gcc 3.2 (multi-line
+strings are now deprecated). Fixed for RH 7.3.
+</p>
+
+ <p>
+ <b>Sun Dec 8 13:44:32 GMT 2002:</b>
+ Major API change: <code>current_pth</code> contains
+ the current thread. A lot of functions which took
+ <code>pth</code> as a parameter now no longer need
+ these parameter. Fixed <code>io_popen</code> to
+ call <code>_exit</code> instead of <code>exit</code>
+ so that the registered <code>atexit</code> functions
+ aren't called incorrectly in the child process.
+ Fixed <code>pth_wait_writable</code>, <code>pth_wait_readable</code>.
+ Updated to use <a href="../makeplus/">make+</a>.
+ Enabled debugging and optimisations.
+ Fixes to compile on RH 7.3.
+ </p>
+
+ <p>
+ <b>Mon Nov 25 09:31:37 GMT 2002:</b>
+ Added symbols file for full symbol resolution in monolith.
+ Catch segfaults and dump a stack trace.
+ Added <code>mutex_nr_sleepers</code> function.
+ </p>
+
+ <p>
+ <b>Sun Nov 17 23:31:32 GMT 2002:</b> Debian packages. Added MSP
+ files. Added patches to enabled chunked encoding (thanks to
+ Steve Atkins). Fixes to compile on RH 7.3. Support for
+ <code>DBI_TIMESTAMP</code>, <code>DBI_INTERVAL</code>,
+ <code>DBI_INT_OR_NULL</code>, <code>DBI_CHAR</code>,
+ <code>DBI_BOOL</code> in the
+ DBI library. Better handling of NULLs in the DBI library.
+ Debugging for DBI.
+ </p>
+
+ <p>
+ <b>Thu Nov 14 15:33:29 GMT 2002:</b> Major checkpoint release
+ for Monolith.
+ </p>
+
+ <p>
+ <b>Sun Oct 20 14:46:46 BST 2002:</b> Support for cookies.
+ Get last serial number from an <code>INSERT</code>.
+ DBI now correctly handles nulls in execute statements.
+ Fixed elusive bugs in <code>pth_wait_readable</code> and
+ <code>pth_wait_writable</code> which were causing monolith
+ to crash. Optimisation to wait queues which also removes
+ a crashing bug in monolith.
+ </p>
+
+ <p>
+ <b>Tue Oct 15 23:40:42 BST 2002:</b> Multiple bug fixes.
+ </p>
+
+ <p>
+ <b>Sun Oct 13 18:47:41 BST 2002:</b> Patch to
+ disable test_dbi code if no DBI configured.
+ Added <code>pthr_server_default_address</code>
+ and <code>pthr_server_address_option</code> which
+ allows the server to listen on a single interface
+ instead of on <code>INADDR_ANY</code>.
+ (Both patches thanks to Steve Atkins - steve at blighty com).
+ </p>
+
+ <p>
+ <b>Sun Oct 13 12:55:08 BST 2002:</b> Added a
+ complete PostgreSQL database interface library,
+ similar to the Perl DBI. See <code>src/test_dbi.c</code>
+ for example usage. This requires
+ <a href="../c2lib/">c2lib >= 1.2.21</a>.
+ </p>
+
+ <p>
+ <b>Wed Oct 9 19:10:38 BST 2002:</b> Added
+ <code>http_request_set_url</code> method which
+ is used by <a href="../rws/">rws</a> to update
+ the URL during internal rewrites.
+ </p>
+
+ <p>
+ <b>Sat Sep 7 15:38:33 BST 2002:</b> Packages are now
+ available as i686 binary RPMs and source RPMs.
+ </p>
+
+ <h2>Old news and old versions</h2>
+
+ <p>
+ <b>Sat Aug 31 14:53:52 BST 2002</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-3.0.4.tar.gz">pthrlib-3.0.4.tar.gz</a>
+ released. This contains a complete tutorial and some
+ documentation fixes.
+ </p>
+
+ <p>
+ <b>Fri Aug 23 15:00:51 BST 2002</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-3.0.3.tar.gz">pthrlib-3.0.3.tar.gz</a>
+ released. This adds the <code>copy_cgi</code> and
+ <code>cgi_erase</code> functions required by
+ <a href="../monolith/">monolith</a>.
+ </p>
+
+ <p>
+ <b>Thu Aug 22 13:20:32 BST 2002</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-3.0.2.tar.gz">pthrlib-3.0.2.tar.gz</a>
+ released.
+ This includes manual pages which were accidentally omitted
+ from the previous version.
+ </p>
+
+ <p>
+ <b>Wed Aug 21 14:20:12 BST 2002</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-3.0.1.tar.gz">pthrlib-3.0.1.tar.gz</a>
+ fixes a few silly bugs which stopped the new version
+ of <a href="../rws/">rws</a> from working.
+ </p>
+
+ <p>
+ <b>Wed Aug 21 11:59:17 BST 2002</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-3.0.0.tar.gz">pthrlib-3.0.0.tar.gz</a>
+ released. This version replaces the old
+ <code>pthr_server_main</code> function with a newer, simpler
+ <code>pthr_server_main_loop</code> function. The
+ <code>pthr_listener</code> thread code has been modified
+ slightly to allow multiple listeners to run at the same time
+ (necessary for Fleet). This breaks a lot of old code, hence the
+ major version number increase. All the included examples
+ have been changed to support the new interface.
+ </p>
+
+ <p>
+ <b>Fri Nov 16 10:43:00 GMT 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-2.2.1.tar.gz">pthrlib-2.2.1.tar.gz</a> released.
+ A few bug fixes for glibc 2.2.4. You will need to use this
+ version if you are using Red Hat Linux 7.x.
+ </p>
+
+ <p>
+ <b>Mon Jul 9 07:43:07 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-2.2.0.tar.gz">pthrlib-2.2.0.tar.gz</a> released.
+ Added an FTP client library.
+ </p>
+
+ <p>
+ <b>Fri Jun 15 15:46:10 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-2.1.6.tar.gz">pthrlib-2.1.6.tar.gz</a> released.
+ Stack support improved: stack overflows cause core dumps
+ rather than random memory corruption; stacks are
+ stored in memory mapped regions so they don't take memory
+ unless you actually use them; stack size is configurable
+ at run time. HTTP library uses
+ exceptions for error messages.
+ Support for profiling. Added a second example webserver.
+ Tests for exception handling code. Added API call to
+ allow you to count the number of threads running.
+ Documentation updates.
+ <p>
+
+ <b>Wed May 30 13:01:46 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-2.1.0.tar.gz">pthrlib-2.1.0.tar.gz</a> released.
+ This has experimental support for throw/catch-style exception
+ handling. Please see the <code>pth_die(3)</code> and
+ <code>pth_catch(3)</code> manual pages for full details.
+ </p>
+
+ <p>
+ <b>Tue May 22 14:11:21 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-2.0.5.tar.gz">pthrlib-2.0.5.tar.gz</a> released.
+ Backed out previous URL-escaping patch. The fix goes better
+ into <a href="../rws/">rws</a> instead.
+ </p>
+
+ <p>
+ <b>Tue May 22 11:37:10 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-2.0.4.tar.gz">pthrlib-2.0.4.tar.gz</a> released.
+ URLs are now unescaped properly, allowing clients to correctly
+ request URLs like '/%7Euser/'.
+ </p>
+
+ <p>
+ <b>Tue Apr 10 16:04:41 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-2.0.2.tar.gz">pthrlib-2.0.2.tar.gz</a> released.
+ Added support for generating logfiles.
+ </p>
+
+ <p>
+ <b>Mon Apr 9 17:23:59 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-2.0.1.tar.gz">pthrlib-2.0.1.tar.gz</a> released.
+ The <i>http</i> library component in version 1.x has
+ been split more logically to support only the mechanics
+ of parsing HTTP connections. The CGI part of it (eg.
+ query string parsing, etc.) has been moved into a
+ new library called <i>cgi</i>. This necessarily breaks
+ older programs, hence the major version number
+ increase. There are some other minor changes and
+ bug fixes: the server name string can now be
+ changed; CGI.pm-like POST_MAX can be set; fixed
+ a really nasty bug in <i>iolib</i> -- it wasn't
+ setting the output buffering mode to the correct
+ default; fixed <code>io_popen(3)</code> so it can
+ handle the case where fds 0 and/or 1 are already
+ closed; added <code>io_copy(3)</code> function to
+ copy bytes between two file descriptors;
+ <code>pthr_server_main</code> now includes <code>sys/types.h</code>
+ correctly.
+ </p>
+
+ <p>
+ <b>Mon Mar 26 13:05:42 BST 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-1.6.6.tar.gz">pthrlib-1.6.6.tar.gz</a> released.
+ Add <code>-lm</code> when linking with c2lib libraries.
+ Documentation fixes.
+ </p>
+
+ <p>
+ <b>Mon Mar 12 12:12:49 GMT 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-1.6.5.tar.gz">pthrlib-1.6.5.tar.gz</a> released.
+ This fixes a bug in the way the library was calling
+ <code>pstrcat</code> (in <a href="../c2lib/">c2lib</a>).
+ Upgrading is highly recommended. You will also need
+ to upgrade to the latest <a href="../c2lib/">c2lib</a>.
+ </p>
+
+
+ <p>
+ <b>Fri Mar 9 17:32:13 GMT 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-1.6.4.tar.gz">pthrlib-1.6.4.tar.gz</a> released.
+ Unit test suite added which covers much of the functionality.
+ Fixed a nasty bug in <tt>io_popen</tt>. Fixed another nasty
+ bug in the pseudothread code. Fixed a false assertion in
+ the rwlock code (revealed during unit testing).
+ </p>
+
+ <p>
+ <b>Fri Feb 23 16:37:58 GMT 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-1.6.2.tar.gz">pthrlib-1.6.2.tar.gz</a> released.
+ <tt>pth_timeout</tt> function added and a nasty race condition
+ in the reactor (which affected wait queues) fixed.
+ </p>
+
+ <p>
+ <b>Fri Feb 16 18:02:46 GMT 2001</b>
+ </p>
+
+ <p>
+ <a href="pthrlib-1.6.1.tar.gz">pthrlib-1.6.1.tar.gz</a> released.
+ All functions are documented in manual pages.
+ </p>
+
+<% include page_footer.msp %>
+endef
+
+upload_website:
+ scp $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION)-1.*.rpm \
+ $(PACKAGE)-$(VERSION).bin.tar.gz \
+ 10.0.0.248:annexia.org/freeware/$(PACKAGE)/
+ scp index.html \
+ 10.0.0.248:annexia.org/freeware/$(PACKAGE)/index.msp
+
+.PHONY: build configure test upload_website
--- /dev/null
+This is pthrlib from http://www.annexia.org/freeware/pthrlib/
+
+pthrlib is a library for writing small, fast and efficient servers in C.
\ No newline at end of file
--- /dev/null
+pthrlib has been ported to FreeBSD 5.0 on 2003/02/25.
+
+Currently postgresql7 and pcre are required - install them as necessary
+from the FreeBSD ports or packages collection. We hope to remove the
+requirement to have postgresql libraries at a later date.
--- /dev/null
+You need to install c2lib, postgresql (and development libraries) and pcre.
+
+PostgreSQL is normally installed in /usr/local/pgsql, so you need to
+add the right directories to your $CFLAGS and $LIBS, eg:
+
+CFLAGS='-I/usr/local/pgsql/include' export CFLAGS
+LIBS='-L/usr/local/pgsql/lib' export LIBS
+
+If in doubt, check build-*/config.h, and make sure it contains the
+following line after the configure stage:
+
+#define HAVE_LIBPQ_FE_H 1
+
+Solaris supports the setcontext/makecontext family of functions, and
+pthrlib should be able to detect this. Check that HAVE_WORKING_SETCONTEXT
+is defined in build-*/config.h.
\ No newline at end of file
--- /dev/null
+Notes on the OpenBSD port
+-------------------------
+
+You need the gcc toolchain, GNU make, postgresql, pcre installed.
+
+OpenBSD has a serious bug in the order constructors in shared libraries
+are called. It calls them in, essentially, a random order, instead of
+in dependency-order. See the following newsgroup article:
+
+http://groups.google.com/groups?selm=b049u2%24227u%241%40FreeBSD.csie.NCTU.edu.tw&oe=UTF-8&output=gplain
+
+I have added some grotesque hacks to work around this problem.
+
+For pthr_context.c, I found the contents of jmp_buf by examination
+in gdb. jmp_buf is an array of 10 longs with the following layout:
+
+ offset contents
+ 0 PC (return address)
+ 1 ebx
+ 2 SP (stack pointer)
+ 3 ebp
+ 4 esi
+ 5 edi
+ 6 ?
+ 7 ?
+ 8 ?
+ 9 ?
+
+Rich 2003/01/26
--- /dev/null
+/* Test for a working setcontext implementation.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_setcontext.c,v 1.1 2003/02/02 18:05:30 rich Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ucontext.h>
+
+#define MAGIC 0x14151617
+
+static void fn (int);
+
+int
+main ()
+{
+ ucontext_t ucp;
+
+ if (getcontext (&ucp) == -1)
+ exit (1);
+
+ makecontext (&ucp, fn, 1, MAGIC);
+
+ setcontext (&ucp);
+ exit (1);
+}
+
+static void
+fn (int magic)
+{
+ if (magic != MAGIC)
+ exit (1);
+
+ /* Working implementation. */
+ printf ("ok\n");
+ exit (0);
+}
--- /dev/null
+# Test for a working setcontext implementation.
+#
+# Copyright (C) 2003 Richard W.M. Jones <rich@annexia.org>
+#
+# Older versions of Linux had the setcontext/makecontext functions
+# in glibc, but the functions were null. Duh! So the only way to
+# determine if these functions are implemented and actually work
+# is to try them out.
+
+result=no
+
+if $CC $CFLAGS \
+ $srcdir/conf/test_setcontext.c -o conf/test_setcontext 2>/dev/null
+then
+ if conf/test_setcontext | grep 'ok' >/dev/null 2>&1; then
+ result=yes
+ fi
+fi
+
+if [ "x$result" = "xyes" ]; then
+ echo "#define HAVE_WORKING_SETCONTEXT 1" >> config.h
+else
+ echo "/* #define HAVE_WORKING_SETCONTEXT 1 */" >> config.h
+fi
--- /dev/null
+#!/bin/sh -
+#
+# This is make+. Make+ is a set of scripts which enhance GNU make and
+# let you build RPMs, and other packages types with just one control
+# file. Read more at http://www.annexia.org/freeware/makeplus/
+#
+# The original author is Richard W.M. Jones <rich@annexia.org>.
+#
+# This software has been explicitly placed in the PUBLIC DOMAIN. You
+# do not need any sort of license or agreement to use or copy this
+# software. You may also copyright this software yourself, and/or
+# relicense it under any terms you want, at any time and at no cost.
+# This allows you (among other things) to include this software with
+# other packages so that the user does not need to download and
+# install make+ separately.
+
+mp_options=""
+
+usage ()
+{
+ cat <<EOF
+./configure [--options]
+
+Installation directory options:
+ --prefix=PREFIX Installation prefix [default: /usr/local]
+ --sysconfdir=SYSCONF Installation prefix for configuration files
+ [default: PREFIX/etc]
+ --localstatedir=STATE Installation prefix for writable files
+ [default: PREFIX/var]
+
+Help options:
+ --help Display this help and exit.
+ --print-mp-cmd Display the make+ command and exit (use as final arg)
+EOF
+ exit 1
+}
+
+while [ $# -gt 0 ]; do
+ opt=$1 ; shift
+
+ case "$opt" in
+ --help)
+ usage
+ ;;
+ --print-mp-cmd)
+ echo "rm -f build-\*/config.mk"
+ echo "make+ $mp_options configure"
+ exit 0
+ ;;
+ --*prefix|--*dir)
+ opt=`echo $opt | sed 's/^--//'`
+ arg=$1 ; shift
+ mp_options="$mp_options $opt=$arg"
+ ;;
+ --*prefix=*|--*dir=*)
+ opt=`echo $opt | sed 's/^--//'`
+ mp_options="$mp_options $opt"
+ ;;
+ *)
+ mp_options="$mp_options $opt"
+ ;;
+ esac
+done
+
+rm -f build-*/config.mk
+make+ $mp_options configure
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_cgi</title>
+</head>
+<body>
+
+<h1 align=center>new_cgi</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:13 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi</b> creates a new CGI object from an existing
+HTTP request. It reads the query string or POSTed parameters
+and parses them internally. CGI parameters are case
+sensitive, and multiple parameters may be passed with the
+same name. Parameter values are automatically unescaped by
+the library before you get to see them.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_params</b> returns a list of all the names of the
+parameters passed to the script. The list is returned as a
+<b>vector</b> of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param</b> returns the value of a single named CGI
+parameter, or <b>NULL</b> if there is no such parameter. If
+multiple parameters were given with the same name, this
+returns one of them, essentially at random.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param_list</b> returns the list of values of the
+named CGI parameter. The list is returned as a <b>vector</b>
+of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_erase</b> erases the named parameter. If a parameter
+was erased, this returns true, else this returns
+false.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>copy_cgi</b> copies <b>cgi</b> into pool
+<b>pool</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_get_post_max(3)</b>, <b>cgi_escape(3)</b>,
+<b>new_http_request(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>cgi_escape</title>
+</head>
+<body>
+
+<h1 align=center>cgi_escape</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:14 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+cgi_escape, cgi_unescape - %-escape and unescape CGI arguments.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+char *cgi_escape (pool, const char *str);
+char *cgi_unescape (pool, const char *str);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions do %-escaping and unescaping on CGI
+arguments. When %-escaping a string, <b>" "</b> is
+replaced by <b>"+"</b>, and non-printable
+characters are replaced by <b>"%hh"</b> where
+<b>hh</b> is a two-digit hex code. Unescaping a string
+reverses this process.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_request(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>cgi_get_post_max</title>
+</head>
+<body>
+
+<h1 align=center>cgi_get_post_max</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:14 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+cgi_get_post_max, cgi_set_post_max - Get and set the internal POST_MAX parameter.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+int cgi_get_post_max (void);
+int cgi_set_post_max (int new_post_max);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions get and set the internal POST_MAX parameter
+which can be used to limit the size of <b>POST</b> method
+requests which this library will handle. If set to a
+non-negative integer, then POST requests will be limited to
+the number of bytes given. The default is -1 (unlimited)
+which can leave the server open to denial of service
+attacks.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_cgi</title>
+</head>
+<body>
+
+<h1 align=center>new_cgi</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:14 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi</b> creates a new CGI object from an existing
+HTTP request. It reads the query string or POSTed parameters
+and parses them internally. CGI parameters are case
+sensitive, and multiple parameters may be passed with the
+same name. Parameter values are automatically unescaped by
+the library before you get to see them.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_params</b> returns a list of all the names of the
+parameters passed to the script. The list is returned as a
+<b>vector</b> of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param</b> returns the value of a single named CGI
+parameter, or <b>NULL</b> if there is no such parameter. If
+multiple parameters were given with the same name, this
+returns one of them, essentially at random.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param_list</b> returns the list of values of the
+named CGI parameter. The list is returned as a <b>vector</b>
+of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_erase</b> erases the named parameter. If a parameter
+was erased, this returns true, else this returns
+false.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>copy_cgi</b> copies <b>cgi</b> into pool
+<b>pool</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_get_post_max(3)</b>, <b>cgi_escape(3)</b>,
+<b>new_http_request(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_cgi</title>
+</head>
+<body>
+
+<h1 align=center>new_cgi</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:14 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi</b> creates a new CGI object from an existing
+HTTP request. It reads the query string or POSTed parameters
+and parses them internally. CGI parameters are case
+sensitive, and multiple parameters may be passed with the
+same name. Parameter values are automatically unescaped by
+the library before you get to see them.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_params</b> returns a list of all the names of the
+parameters passed to the script. The list is returned as a
+<b>vector</b> of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param</b> returns the value of a single named CGI
+parameter, or <b>NULL</b> if there is no such parameter. If
+multiple parameters were given with the same name, this
+returns one of them, essentially at random.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param_list</b> returns the list of values of the
+named CGI parameter. The list is returned as a <b>vector</b>
+of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_erase</b> erases the named parameter. If a parameter
+was erased, this returns true, else this returns
+false.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>copy_cgi</b> copies <b>cgi</b> into pool
+<b>pool</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_get_post_max(3)</b>, <b>cgi_escape(3)</b>,
+<b>new_http_request(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_cgi</title>
+</head>
+<body>
+
+<h1 align=center>new_cgi</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:15 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi</b> creates a new CGI object from an existing
+HTTP request. It reads the query string or POSTed parameters
+and parses them internally. CGI parameters are case
+sensitive, and multiple parameters may be passed with the
+same name. Parameter values are automatically unescaped by
+the library before you get to see them.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_params</b> returns a list of all the names of the
+parameters passed to the script. The list is returned as a
+<b>vector</b> of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param</b> returns the value of a single named CGI
+parameter, or <b>NULL</b> if there is no such parameter. If
+multiple parameters were given with the same name, this
+returns one of them, essentially at random.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param_list</b> returns the list of values of the
+named CGI parameter. The list is returned as a <b>vector</b>
+of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_erase</b> erases the named parameter. If a parameter
+was erased, this returns true, else this returns
+false.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>copy_cgi</b> copies <b>cgi</b> into pool
+<b>pool</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_get_post_max(3)</b>, <b>cgi_escape(3)</b>,
+<b>new_http_request(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>cgi_get_post_max</title>
+</head>
+<body>
+
+<h1 align=center>cgi_get_post_max</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:15 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+cgi_get_post_max, cgi_set_post_max - Get and set the internal POST_MAX parameter.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+int cgi_get_post_max (void);
+int cgi_set_post_max (int new_post_max);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions get and set the internal POST_MAX parameter
+which can be used to limit the size of <b>POST</b> method
+requests which this library will handle. If set to a
+non-negative integer, then POST requests will be limited to
+the number of bytes given. The default is -1 (unlimited)
+which can leave the server open to denial of service
+attacks.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>cgi_escape</title>
+</head>
+<body>
+
+<h1 align=center>cgi_escape</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:15 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+cgi_escape, cgi_unescape - %-escape and unescape CGI arguments.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+char *cgi_escape (pool, const char *str);
+char *cgi_unescape (pool, const char *str);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions do %-escaping and unescaping on CGI
+arguments. When %-escaping a string, <b>" "</b> is
+replaced by <b>"+"</b>, and non-printable
+characters are replaced by <b>"%hh"</b> where
+<b>hh</b> is a two-digit hex code. Unescaping a string
+reverses this process.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi(3)</b>, <b>new_http_request(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_cgi</title>
+</head>
+<body>
+
+<h1 align=center>new_cgi</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:15 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi</b> creates a new CGI object from an existing
+HTTP request. It reads the query string or POSTed parameters
+and parses them internally. CGI parameters are case
+sensitive, and multiple parameters may be passed with the
+same name. Parameter values are automatically unescaped by
+the library before you get to see them.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_params</b> returns a list of all the names of the
+parameters passed to the script. The list is returned as a
+<b>vector</b> of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param</b> returns the value of a single named CGI
+parameter, or <b>NULL</b> if there is no such parameter. If
+multiple parameters were given with the same name, this
+returns one of them, essentially at random.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param_list</b> returns the list of values of the
+named CGI parameter. The list is returned as a <b>vector</b>
+of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_erase</b> erases the named parameter. If a parameter
+was erased, this returns true, else this returns
+false.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>copy_cgi</b> copies <b>cgi</b> into pool
+<b>pool</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_get_post_max(3)</b>, <b>cgi_escape(3)</b>,
+<b>new_http_request(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_iolib.h>
+#include <pthr_server.h>
+
+static void start_processor (int sock, void *data);
+static void run (void *);
+
+typedef struct processor_thread
+{
+ pseudothread pth; /* Pseudothread handle. */
+ int sock; /* Socket. */
+} *processor_thread;
+
+int
+main (int argc, char *argv[])
+{
+ /* Start up the server. */
+ pthr_server_main_loop (argc, argv, start_processor);
+
+ exit (0);
+}
+
+static void
+start_processor (int sock, void *data)
+{
+ pool pool;
+ processor_thread p;
+
+ pool = new_pool ();
+ p = pmalloc (pool, sizeof *p);
+
+ p->sock = sock;
+ p->pth = new_pseudothread (pool, run, p, "processor thread");
+
+ pth_start (p->pth);
+}
+
+static void
+run (void *vp)
+{
+ processor_thread p = (processor_thread) vp;
+ io_handle io;
+ char buffer[256];
+
+ io = io_fdopen (p->pth, p->sock);
+
+ /* Sit in a loop reading strings and echoing them back. */
+ while (io_fgets (buffer, sizeof buffer, io, 1))
+ io_fputs (buffer, io);
+
+ io_fclose (io);
+
+ pth_exit (p->pth);
+}
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_type</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_type</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:15 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_type, ftpc_ascii, ftpc_binary - Set connection type.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_type (ftpc ftpc, char type);
+int ftpc_ascii (ftpc ftpc);
+int ftpc_binary (ftpc ftpc);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_type</b> sets the connection type. Most FTP servers
+only support type 'a' (ASCII) or type 'i' (bInary), although
+esoteric FTP servers might support 'e'
+(EBCDIC).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_ascii</b> sets the type to ASCII.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_binary</b> sets the type to bInary.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_type</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_type</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:16 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_type, ftpc_ascii, ftpc_binary - Set connection type.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_type (ftpc ftpc, char type);
+int ftpc_ascii (ftpc ftpc);
+int ftpc_binary (ftpc ftpc);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_type</b> sets the connection type. Most FTP servers
+only support type 'a' (ASCII) or type 'i' (bInary), although
+esoteric FTP servers might support 'e'
+(EBCDIC).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_ascii</b> sets the type to ASCII.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_binary</b> sets the type to bInary.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_cwd</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_cwd</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:16 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_cwd, ftpc_cdup - Change directory on the server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_cwd (ftpc ftpc, const char *pathname);
+int ftpc_cdup (ftpc ftpc);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_cwd</b> changes the directory to
+<b>pathname</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_cdup</b> moves to the parent directory. On most
+Unix-like FTP servers this is equivalent to doing <b>CWD
+..</b></td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_cwd</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_cwd</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:16 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_cwd, ftpc_cdup - Change directory on the server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_cwd (ftpc ftpc, const char *pathname);
+int ftpc_cdup (ftpc ftpc);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_cwd</b> changes the directory to
+<b>pathname</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_cdup</b> moves to the parent directory. On most
+Unix-like FTP servers this is equivalent to doing <b>CWD
+..</b></td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_delete</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_delete</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:16 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_delete - Delete a file on the server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_delete (ftpc ftpc, const char *pathname);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_delete</b> deletes a file called <b>pathname</b> on
+the server.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_ls</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_ls</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:17 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_ls, ftpc_dir - List the contents of a directory on the server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+vector ftpc_ls (ftpc ftpc, pool, const char *pathname);
+vector ftpc_dir (ftpc ftpc, pool, const char *pathname);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_ls</b> and <b>ftpc_dir</b> list the contents of
+either the current directory (if <b>pathname</b> is
+<b>NULL</b>) or else another directory
+<b>pathname</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_ls</b> issues the command <b>NLST -a</b>, returning
+a vector of strings giving the name of each
+file.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_dir</b> issues the command <b>LIST -a</b>, returning
+a human-readable list of filenames, similar to issuing the
+<b>ls -al</b> command in Unix.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_get</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_get</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:17 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_get, ftpc_put - Download or upload a file.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_get (ftpc ftpc, const char *remote_file, const char *local_file);
+int ftpc_put (ftpc ftpc, const char *local_file, const char *remote_file);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_get</b> attempts to download <b>remote_file</b> from
+the server and store it in a local file called
+<b>local_file</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_put</b> attempts to upload a file called
+<b>local_file</b> to the server and store it in a file on
+the server called <b>remote_file</b>.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_login</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_login</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:17 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_login - Log onto the FTP server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_login (ftpc ftpc, const char *username, const char *password);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Attempt to log onto the FTP server as user <b>username</b>
+with password <b>password</b>. If <b>username</b> is
+<b>NULL</b>, <b>"ftp"</b> or
+<b>"anonymous"</b>, then log in anonymously. For
+anonymous logins, the <b>password</b> may be <b>NULL</b>, in
+which case the environment variable <b>LOGNAME</b> followed
+by a single <b>@</b> character is used as the
+password.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_ls</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_ls</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:17 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_ls, ftpc_dir - List the contents of a directory on the server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+vector ftpc_ls (ftpc ftpc, pool, const char *pathname);
+vector ftpc_dir (ftpc ftpc, pool, const char *pathname);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_ls</b> and <b>ftpc_dir</b> list the contents of
+either the current directory (if <b>pathname</b> is
+<b>NULL</b>) or else another directory
+<b>pathname</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_ls</b> issues the command <b>NLST -a</b>, returning
+a vector of strings giving the name of each
+file.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_dir</b> issues the command <b>LIST -a</b>, returning
+a human-readable list of filenames, similar to issuing the
+<b>ls -al</b> command in Unix.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_mkdir</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_mkdir</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:17 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_mkdir, ftpc_rmdir - Create or remove directories on the server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_mkdir (ftpc ftpc, const char *pathname);
+int ftpc_rmdir (ftpc ftpc, const char *pathname);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_mkdir</b> creates a directory called <b>pathname</b>
+on the server. <b>ftpc_rmdir</b> removes a directory called
+<b>pathname</b> on the server.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_get</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_get</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:18 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_get, ftpc_put - Download or upload a file.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_get (ftpc ftpc, const char *remote_file, const char *local_file);
+int ftpc_put (ftpc ftpc, const char *local_file, const char *remote_file);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_get</b> attempts to download <b>remote_file</b> from
+the server and store it in a local file called
+<b>local_file</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_put</b> attempts to upload a file called
+<b>local_file</b> to the server and store it in a file on
+the server called <b>remote_file</b>.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_pwd</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_pwd</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:18 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_pwd - Return current directory on the server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+char *ftpc_pwd (ftpc ftpc);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_pwd</b> returns the current directory on the
+server.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The current directory, as a string allocated in the pool, or
+NULL if the command fails. If a fatal error occurs with the
+connection, an exception is thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_quit</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_quit</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:18 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_quit - Nicely disconnect from the FTP server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_quit (ftpc ftpc);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_quit</b> sends a <b>QUIT</b> command to the FTP
+server and then drops the network connection. After using
+this function, the <b>ftpc</b> object associated with the
+connection is invalid and should not be used.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_quote</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_quote</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:18 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_quote - Issue a command to the FTP server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_quote (ftpc ftpc, const char *cmd);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_quote</b> issues a command directly to the FTP
+server. This function may be used for issuing <b>SITE</b>
+commands for example.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_mkdir</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_mkdir</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:19 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_mkdir, ftpc_rmdir - Create or remove directories on the server.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_mkdir (ftpc ftpc, const char *pathname);
+int ftpc_rmdir (ftpc ftpc, const char *pathname);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_mkdir</b> creates a directory called <b>pathname</b>
+on the server. <b>ftpc_rmdir</b> removes a directory called
+<b>pathname</b> on the server.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_set_passive_mode</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_set_passive_mode</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:19 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_set_passive_mode - Change to/from active or passive mode.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_set_passive_mode (ftpc ftpc, int flag);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If <b>flag</b> is true, all future connections on this
+<b>ftpc</b> object will be in passive mode. If <b>flag</b>
+is false, all future connections will be in active
+mode.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Passive mode is required by a few servers and some
+firewalls.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>ftpc_type</title>
+</head>
+<body>
+
+<h1 align=center>ftpc_type</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#RETURNS">RETURNS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:19 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+ftpc_type, ftpc_ascii, ftpc_binary - Set connection type.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+int ftpc_type (ftpc ftpc, char type);
+int ftpc_ascii (ftpc ftpc);
+int ftpc_binary (ftpc ftpc);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_type</b> sets the connection type. Most FTP servers
+only support type 'a' (ASCII) or type 'i' (bInary), although
+esoteric FTP servers might support 'e'
+(EBCDIC).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_ascii</b> sets the type to ASCII.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_binary</b> sets the type to bInary.</td></table>
+<a name="RETURNS"></a>
+<h2>RETURNS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+0 if successful, -1 if the attempt failed. If a fatal error
+occurs with the connection, an exception is
+thrown.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>http_set_log_file</title>
+</head>
+<body>
+
+<h1 align=center>http_set_log_file</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:19 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+http_set_log_file, http_get_log_file - enable HTTP logs on file pointer</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+FILE *http_set_log_file (FILE *fp);
+FILE *http_get_log_file (void);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>FILE *fp</b> argument to <b>http_set_log_file</b>
+sets the file pointer on which HTTP logs are generated. To
+disable logging, set <b>fp</b> to <b>NULL</b>. The function
+returns <b>fp</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_get_log_file</b> returns the current file pointer or
+<b>NULL</b> if logging is disabled.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The default is that logging is disabled.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Currently log messages are generated at the end of the HTTP
+response headers and have the following fixed
+format:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+YYYY/MM/DD HH:MM ip:port - "METHOD URL HTTP/x.y"
+CODE length "Referer" "User
+Agent"</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The first "-" is intended to store the HTTP auth
+username, when HTTP authorization is supported by the
+library. The "length" field is only known if the
+caller sends back a "Content-Length" header.
+Otherwise 0 is printed in that position.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Log format should be customizable. It should be possible
+(optionally, of course) to look up the IP address and print
+a hostname.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>http_get_servername</title>
+</head>
+<body>
+
+<h1 align=center>http_get_servername</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:20 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+http_get_servername, http_set_servername - get and set the server name string</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+const char *http_get_servername (void);
+const char *http_set_servername (const char *new_server_name);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Get and set the server name (which is sent in the
+<b>Server</b>) header by the server when it responds to
+requests.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The default string is
+<b>pthrlib-httpd/version</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request(3)</b>, <b>new_http_response(3)</b>,
+<b>new_cgi(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:20 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:20 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:20 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:21 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:21 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:21 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:21 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:22 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:22 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:22 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:22 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_response</title>
+</head>
+<body>
+
+<h1 align=center>new_http_response</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:23 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_response, http_response_send_header, http_response_send_headers, http_response_end_headers - functions for sending HTTP responses</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_response new_http_response (pseudothread, http_request, io_handle, int code, const char *msg);
+void http_response_send_header (http_response, const char *key, const char *value);
+void http_response_send_headers (http_response, ...);
+int http_response_end_headers (http_response h);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently generate outgoing
+HTTP responses.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response</b> generates a new HTTP response
+object and returns it. <b>code</b> is the HTTP response code
+(see RFC 2616 for a list of codes), and <b>msg</b> is the
+HTTP response message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_send_header</b> sends a single HTTP header
+back to the client. The header is constructed by
+concatenating <b>key</b>, <b>": "</b>,
+<b>value</b> and <b>CR LF</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_send_headers</b> sends back several headers
+in a single call. The arguments to this function are a list
+of <b>key</b>, <b>value</b> pairs followed by a single
+<b>NULL</b> argument which terminates the list.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_end_headers</b> ends the header list. It
+causes the code to emit any missing-but-required headers and
+then send the final <b>CR LF</b> characters.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_response</title>
+</head>
+<body>
+
+<h1 align=center>new_http_response</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:23 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_response, http_response_send_header, http_response_send_headers, http_response_end_headers - functions for sending HTTP responses</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_response new_http_response (pseudothread, http_request, io_handle, int code, const char *msg);
+void http_response_send_header (http_response, const char *key, const char *value);
+void http_response_send_headers (http_response, ...);
+int http_response_end_headers (http_response h);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently generate outgoing
+HTTP responses.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response</b> generates a new HTTP response
+object and returns it. <b>code</b> is the HTTP response code
+(see RFC 2616 for a list of codes), and <b>msg</b> is the
+HTTP response message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_send_header</b> sends a single HTTP header
+back to the client. The header is constructed by
+concatenating <b>key</b>, <b>": "</b>,
+<b>value</b> and <b>CR LF</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_send_headers</b> sends back several headers
+in a single call. The arguments to this function are a list
+of <b>key</b>, <b>value</b> pairs followed by a single
+<b>NULL</b> argument which terminates the list.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_end_headers</b> ends the header list. It
+causes the code to emit any missing-but-required headers and
+then send the final <b>CR LF</b> characters.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_response</title>
+</head>
+<body>
+
+<h1 align=center>new_http_response</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:23 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_response, http_response_send_header, http_response_send_headers, http_response_end_headers - functions for sending HTTP responses</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_response new_http_response (pseudothread, http_request, io_handle, int code, const char *msg);
+void http_response_send_header (http_response, const char *key, const char *value);
+void http_response_send_headers (http_response, ...);
+int http_response_end_headers (http_response h);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently generate outgoing
+HTTP responses.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response</b> generates a new HTTP response
+object and returns it. <b>code</b> is the HTTP response code
+(see RFC 2616 for a list of codes), and <b>msg</b> is the
+HTTP response message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_send_header</b> sends a single HTTP header
+back to the client. The header is constructed by
+concatenating <b>key</b>, <b>": "</b>,
+<b>value</b> and <b>CR LF</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_send_headers</b> sends back several headers
+in a single call. The arguments to this function are a list
+of <b>key</b>, <b>value</b> pairs followed by a single
+<b>NULL</b> argument which terminates the list.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_end_headers</b> ends the header list. It
+causes the code to emit any missing-but-required headers and
+then send the final <b>CR LF</b> characters.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>http_set_log_file</title>
+</head>
+<body>
+
+<h1 align=center>http_set_log_file</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:23 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+http_set_log_file, http_get_log_file - enable HTTP logs on file pointer</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+FILE *http_set_log_file (FILE *fp);
+FILE *http_get_log_file (void);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>FILE *fp</b> argument to <b>http_set_log_file</b>
+sets the file pointer on which HTTP logs are generated. To
+disable logging, set <b>fp</b> to <b>NULL</b>. The function
+returns <b>fp</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_get_log_file</b> returns the current file pointer or
+<b>NULL</b> if logging is disabled.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The default is that logging is disabled.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Currently log messages are generated at the end of the HTTP
+response headers and have the following fixed
+format:</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+YYYY/MM/DD HH:MM ip:port - "METHOD URL HTTP/x.y"
+CODE length "Referer" "User
+Agent"</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The first "-" is intended to store the HTTP auth
+username, when HTTP authorization is supported by the
+library. The "length" field is only known if the
+caller sends back a "Content-Length" header.
+Otherwise 0 is printed in that position.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Log format should be customizable. It should be possible
+(optionally, of course) to look up the IP address and print
+a hostname.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>http_get_servername</title>
+</head>
+<body>
+
+<h1 align=center>http_get_servername</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:24 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+http_get_servername, http_set_servername - get and set the server name string</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+const char *http_get_servername (void);
+const char *http_set_servername (const char *new_server_name);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Get and set the server name (which is sent in the
+<b>Server</b>) header by the server when it responds to
+requests.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The default string is
+<b>pthrlib-httpd/version</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request(3)</b>, <b>new_http_response(3)</b>,
+<b>new_cgi(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>pthrlib documentation index</title>
+ <style type="text/css"><!--
+ h1 {
+ text-align: center;
+ }
+ pre {
+ background-color: #eeeeff;
+ }
+ code {
+ color: green;
+ font-weight: bold;
+ }
+ --></style>
+ </head>
+
+ <body bgcolor="#ffffff">
+ <h1>pthrlib documentation index</h1>
+
+ <p>
+ <code>pthrlib</code> is a library for writing small, fast
+ and efficient servers in C. It offers a list of advanced
+ features. This library has been used to write a
+ <a href="http://www.annexia.org/freeware/rws/">very
+ tiny and fast web server called rws</a> and a closed
+ source chat server.
+ </p>
+
+ <p>
+ The primary aims of <code>pthrlib</code> are:
+ </p>
+
+ <ul>
+ <li> Be very simple to use.
+ <li> Provide rich functionality for C programmers out of the box.
+ <li> Meticulous attention paid to the number of syscalls
+ issued and the efficiency of those syscalls, so typically
+ a <code>pthrlib</code> server will outperform any other
+ server architecture or language (on non-SMP).
+ <li> Tiny memory footprint for running servers.
+ <li> Cooperative threading reduces the danger and agony
+ of thread programming.
+ <li> Use of <code>c2lib</code> removes many risks of
+ buffer overflows.
+ </ul>
+
+ <h2>Tutorial and programming examples</h2>
+
+ <p>
+ At the heart of <code>pthrlib</code> is a threading
+ library called <q><b>pseudothreads</b></q>. This library
+ is a typical lightweight threading library, written
+ from scratch to be as small and fast as possible (it
+ therefore lacks many of the unnecessary features
+ which complicate other lightweight threading libraries,
+ such as the ability to suspend threads).
+ </p>
+
+ <p>
+ A small <code>pthrlib</code> server will start off
+ with just a single listener thread, listening for
+ new connections on a socket. When connections come
+ in, a new thread is spun off to handle it:
+ </p>
+
+ <table border="1">
+ <tr> <td> listener thread </td> </tr>
+ <tr> <td> processing thread, connected to client #1 </td> </tr>
+ <tr> <td> processing thread, connected to client #2 </td> </tr>
+ <tr> <td> processing thread, connected to client #3 </td> </tr>
+ <tr> <td align="center"> ... </td> </tr>
+ </table>
+
+ <p>
+ More complex <code>pthrlib</code> servers may contain
+ several core threads: for example our closed-source
+ chat server has one extra thread called <code>autoannounce</code>
+ which periodically sends out announcement messages to
+ all clients. They may also use more than one thread
+ per client. Since threads are very lightweight, you
+ should be able to create as many threads as necessary
+ for your application.
+ </p>
+
+ <h3>Simple <q>echo</q> server</h3>
+
+ <p>
+ To help you create a server with a listener thread
+ spinning off threads for each incoming connection,
+ there is a helper function called <code>pthr_server_main_loop(3)</code>.
+ Almost all programs will want to use it, such as the
+ following simple <q>echo</q> program (I have split
+ the program into chunks for readability).
+ </p>
+
+ <p>
+ Standard includes for socket programs, and predeclare
+ static functions:
+ </p>
+
+<pre>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+#include <pool.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_iolib.h>
+#include <pthr_server.h>
+
+static void start_processor (int sock, void *data);
+static void run (void *);
+</pre>
+
+ <p>
+ Recall from the diagram above that we will start one
+ processing thread for each client. The following structure
+ is used to store the per-thread information about that
+ processing thread:
+ </p>
+
+<pre>
+typedef struct processor_thread
+{
+ pseudothread pth; /* Pseudothread handle. */
+ int sock; /* Socket. */
+} *processor_thread;
+</pre>
+
+ <p>
+ <code>main</code> is very simple, since
+ <code>pthr_server_main_loop</code> does all the hard work
+ of opening up a listening socket, forking into the
+ background, parsing command line arguments and so on.
+ Note that we pass a pointer to our
+ <code>start_processor</code> function.
+ </p>
+
+<pre>
+int
+main (int argc, char *argv[])
+{
+ /* Start up the server. */
+ pthr_server_main_loop (argc, argv, start_processor);
+
+ exit (0);
+}
+</pre>
+
+ <p>
+ Whenever a client makes a new connection to our server,
+ the listener thread is going to call <code>start_processor</code>.
+ This creates allocates the per-thread data structure
+ and starts the new thread. The <code>run</code>
+ function is the actual new processing thread running.
+ </p>
+
+<pre>
+static void
+start_processor (int sock, void *data)
+{
+ pool pool;
+ processor_thread p;
+
+ pool = new_pool ();
+ p = pmalloc (pool, sizeof *p);
+
+ p->sock = sock;
+ p->pth = new_pseudothread (pool, run, p, "processor thread");
+
+ pth_start (p->pth);
+}
+
+static void
+run (void *vp)
+{
+ processor_thread p = (processor_thread) vp;
+ io_handle io;
+ char buffer[256];
+
+ io = io_fdopen (p->sock);
+
+ /* Sit in a loop reading strings and echoing them back. */
+ while (io_fgets (buffer, sizeof buffer, io, 1))
+ io_fputs (buffer, io);
+
+ io_fclose (io);
+
+ pth_exit ();
+}
+</pre>
+
+ <p>
+ Here is a typical run with this program (what I
+ typed is shown in <b>bold text</b>):
+ </p>
+
+<pre>
+$ <b>./eg_echo -p 9000</b>
+$ <b>telnet localhost 9000</b>
+Trying 127.0.0.1...
+Connected to localhost.localnet (127.0.0.1).
+Escape character is '^]'.
+<b>hello</b>
+hello
+<b>goodbye</b>
+goodbye
+<b>^]</b>
+
+telnet> <b>quit</b>
+Connection closed.
+</pre>
+
+ <h3>Simple HTTP server</h3>
+
+ <p>
+ <b>Note:</b> Although it is possible to write complete
+ mini webservers using just <code>pthrlib</code>, it is
+ often more flexible and just as fast to use
+ <a href="http://www.annexia.org/freeware/rws/">rws's</a>
+ shared object scripts. <code>rws</code> provides you
+ with the complete web serving framework. If you don't
+ use <code>rws</code> and you need to, say, serve an
+ image or a static page at some point in your application,
+ then you will need to either link to another web server
+ like Apache, or else write your own static file service
+ code (it can be done -- we did it for the chat server --
+ but it's unnecessary).
+ </p>
+
+ <p>
+ The following code comes from example 1 supplied with
+ <code>pthrlib</code>. You can find the working code
+ in the <code>examples/</code> directory. I have omitted
+ some parts of the code in order to concentrate on the
+ interesting and relevant bits.
+ </p>
+
+ <p>
+ First of all, the <code>main</code> function:
+ </p>
+
+<pre>
+static void start_processor (int sock, void *);
+
+int
+main (int argc, char *argv[])
+{
+ /* Start up the server. */
+ pthr_server_main_loop (argc, argv, start_processor);
+
+ exit (0);
+}
+
+static void
+start_processor (int sock, void *data)
+{
+ (void) new_eg1_echo_processor (sock);
+}
+</pre>
+
+ <p>
+ Again, we are using <code>pthr_server_main_loop</code> to
+ do the hard work. <code>start_processor</code> starts the
+ processor thread. The processor thread's <code>run</code>
+ function has the following outline:
+ </p>
+
+<pre>
+static void
+run (void *vp)
+{
+ eg1_echo_processor p = (eg1_echo_processor) vp;
+ int close = 0;
+ io_handle io;
+
+ io = io_fdopen (p->sock);
+
+ /* Sit in a loop reading HTTP requests. */
+ while (!close)
+ {
+ /* Parse the HTTP request. */
+ : : :
+ : : :
+
+ /* Form the HTTP response. */
+ : : :
+ : : :
+ }
+
+ io_fclose (io);
+
+ pth_exit ();
+}
+</pre>
+
+ <p>
+ The purpose of this loop is to deal with HTTP keepalives,
+ where a client (or perhaps many different clients through a
+ proxy) makes a series of requests over the same TCP connection.
+ For each request, we'll make an iteration of the <code>while</code>
+ loop. Each request is independent of the previous one.
+ </p>
+
+ <p>
+ At the beginning of the thread, the listening thread hands us
+ a socket file descriptor in <code>sock</code>. Doing I/O directly
+ on a file descriptor is inconvenient, and it can't be
+ wrapped up directly in a <code>stdio</code> <code>FILE *</code>
+ because these block, hanging the entire process (and all other
+ threads). <code>iolib</code> is a replacement for <code>stdio</code>
+ which works with pools and doesn't block. <code>io_fdopen</code>
+ wraps up a file descriptor in a full buffered <code>io_handle</code>.
+ </p>
+
+ <p>
+ Now lets look at the step which parses the HTTP request:
+ </p>
+
+<pre>
+ http_request http_request;
+ cgi cgi;
+ pool pool = pth_get_pool (p->pth);
+ : : :
+ : : :
+
+ /* ----- HTTP request ----- */
+ http_request = new_http_request (pool, io);
+ if (http_request == 0) /* Normal end of file. */
+ break;
+
+ cgi = new_cgi (pool, http_request, io);
+ if (cgi == 0) /* XXX Should send an error here. */
+ break;
+</pre>
+
+ <p>
+ The <code>new_http_request</code> function parses the
+ HTTP headers. It does pretty much the equivalent of what
+ Apache does just before it hands off to a normal CGI script.
+ You can think of <code>new_cgi</code> as being somewhat
+ equivalent to Perl's <code>CGI.pm</code>.
+ </p>
+
+ <p>
+ Here's the code which generates the HTTP response:
+ </p>
+
+<pre>
+ http_response http_response;
+ : : :
+ : : :
+
+ http_response = new_http_response (pool, http_request,
+ io,
+ 200, "OK");
+ http_response_send_header (http_response,
+ "Content-Type", "text/plain");
+ close = http_response_end_headers (http_response);
+
+ if (!http_request_is_HEAD (http_request))
+ {
+ io_fprintf (io, "Hello. This is your server.\r\n\r\n");
+ io_fprintf (io, "Your browser sent the following headers:\r\n");
+
+ headers = http_request_get_headers (http_request);
+ for (i = 0; i < vector_size (headers); ++i)
+ {
+ vector_get (headers, i, header);
+ io_fprintf (io, "\t%s: %s\r\n", header.key, header.value);
+ }
+
+ io_fprintf (io, "----- end of headers -----\r\n");
+
+ io_fprintf (io, "The URL was: %s\r\n",
+ http_request_get_url (http_request));
+ io_fprintf (io, "The path component was: %s\r\n",
+ http_request_path (http_request));
+ io_fprintf (io, "The query string was: %s\r\n",
+ http_request_query_string (http_request));
+ io_fprintf (io, "The query arguments were:\r\n");
+
+ params = cgi_params (cgi);
+ for (i = 0; i < vector_size (params); ++i)
+ {
+ vector_get (params, i, name);
+ value = cgi_param (cgi, name);
+ io_fprintf (io, "\t%s=%s\r\n", name, value);
+ }
+
+ io_fprintf (io, "----- end of parameters -----\r\n");
+ }
+</pre>
+
+ <p>
+ <code>new_http_response</code>,
+ <code>http_response_send_header</code> and
+ <code>http_response_end_headers</code> generates the
+ HTTP headers for the response back to the client. We'll
+ see those headers in a minute.
+ Notice that we send back an explicit
+ <code>Content-Type: text/plain</code>
+ header.
+ </p>
+
+ <p>
+ The rest of the code actually generates the page. The
+ simplest way to describe it is to show an actual interaction
+ with the server. What I typed is shown in <b>bold text</b>.
+ </p>
+
+<pre>
+$ <b>./pthr_eg1_echo -p 9000</b>
+$ <b>telnet localhost 9000</b>
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+<b>GET /path/here?abc=123&def=456 HTTP/1.0</b>
+<b>Host: localhost:9000</b>
+
+HTTP/1.1 200 OK
+Content-Type: text/plain
+Server: pthrlib-httpd/3.0.3
+Date: Fri, 30 Aug 2002 17:04:03 GMT
+Connection: close
+
+Hello. This is your server.
+
+Your browser sent the following headers:
+ host: localhost:9000
+----- end of headers -----
+The URL was: /path/here?abc=123&def=456
+The path component was: /path/here
+The query string was: abc=123&def=456
+The query arguments were:
+ abc=123
+ def=456
+----- end of parameters -----
+Connection closed by foreign host.
+</pre>
+
+ <h3>Static file webserver</h3>
+
+ <p>
+ This following code is from example 2. You can find
+ the complete working program in the <code>examples/</code>
+ directory. It's a very minimal webserver which can
+ only serve static files from a single directory. If
+ you start the server up as <code>root</code>, then
+ the server will <code>chroot(2)</code> itself into
+ a configurable directory, and change its user ID to
+ <code>nobody.nobody</code>
+ </p>
+
+ <p>
+ Again the <code>main</code> function uses
+ <code>pthr_server_main_loop</code> for simplicity.
+ However one thing which <code>pthr_server_main_loop</code>
+ can't do (yet) is set up signal handlers, so we have
+ to do those by hand first:
+ </p>
+
+<pre>
+int
+main (int argc, char *argv[])
+{
+ struct sigaction sa;
+
+ /* Intercept signals. */
+ memset (&sa, 0, sizeof sa);
+ sa.sa_handler = catch_quit_signal;
+ sa.sa_flags = SA_RESTART;
+ sigaction (SIGINT, &sa, 0);
+ sigaction (SIGQUIT, &sa, 0);
+ sigaction (SIGTERM, &sa, 0);
+
+ /* ... but ignore SIGPIPE errors. */
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_RESTART;
+ sigaction (SIGPIPE, &sa, 0);
+
+ /* Start up the server. */
+ pthr_server_chroot (root);
+ pthr_server_username (user);
+ pthr_server_main_loop (argc, argv, start_processor);
+
+ exit (0);
+}
+
+static void
+start_processor (int sock, void *data)
+{
+ (void) new_eg2_server_processor (sock);
+}
+
+static void
+catch_quit_signal (int sig)
+{
+ exit (0);
+}
+</pre>
+
+ <p>
+ Notice that just before we actually call
+ <code>pthr_server_main_loop</code>, we configure
+ the main loop code first by telling it the
+ root directory (where we want to <code>chroot(2)</code>
+ to) and the username (<code>nobody</code>).
+ </p>
+
+ <p>
+ The <code>eg2_server_processor</code> thread
+ structure contains a little more data this time. It
+ contains most of the information about the current
+ request:
+ </p>
+
+<pre>
+struct eg2_server_processor
+{
+ /* Pseudothread handle. */
+ pseudothread pth;
+
+ /* Socket. */
+ int sock;
+
+ /* Pool for memory allocations. */
+ struct pool *pool;
+
+ /* HTTP request. */
+ http_request http_request;
+
+ /* IO handle. */
+ io_handle io;
+};
+</pre>
+
+ <p>
+ The <code>run</code> function has the same basic outline,
+ ie. a <code>while</code> loop to process each request on
+ the same keep-alive connection, and a call to
+ <code>new_http_request</code> to parse the HTTP headers. The
+ outline code is shown in <span style="color: red">red text</span>
+ below. The code to handle the response is shown in
+ black.
+ </p>
+
+<pre>
+<span style="color: red">static void
+run (void *vp)
+{
+ eg2_server_processor p = (eg2_server_processor) vp;
+ int close = 0;
+ const char *path;
+ struct stat statbuf;
+
+ p->io = io_fdopen (p->sock);
+
+ /* Sit in a loop reading HTTP requests. */
+ while (!close)
+ {
+ /* ----- HTTP request ----- */
+ p->http_request = new_http_request (pool, p->io);
+ if (p->http_request == 0) /* Normal end of file. */
+ break;</span>
+
+ /* Get the path and locate the file. */
+ path = http_request_path (p->http_request);
+ if (stat (path, &statbuf) == -1)
+ {
+ close = file_not_found_error (p);
+ continue;
+ }
+
+ /* File or directory? */
+ if (S_ISDIR (statbuf.st_mode))
+ {
+ close = serve_directory (p, path, &statbuf);
+ continue;
+ }
+ else if (S_ISREG (statbuf.st_mode))
+ {
+ close = serve_file (p, path, &statbuf);
+ continue;
+ }
+ else
+ {
+ close = file_not_found_error (p);
+ continue;
+ }
+ <span style="color: red">}
+
+ io_fclose (p->io);
+
+ pth_exit ();
+}</span>
+</pre>
+
+ <p>
+ This is a very simple webserver, so all it does is take the
+ <code>path</code> component of the request, and uses it directly
+ as a filename (note that it relies completely on the
+ <code>chroot(2)</code> environment for security).
+ </p>
+
+ <p>
+ Firstly it calls <code>stat</code> to find out if the filename
+ is a directory or a regular file. If it is neither, or if the
+ file doesn't exist, it calls <code>file_not_found_error</code>
+ which sends back a 404 FILE NOT FOUND error.
+ </p>
+
+ <p>
+ If the file is a regular file, we call <code>serve_file</code>,
+ which is a simple piece of code:
+ </p>
+
+<pre>
+static int
+serve_file (eg2_server_processor p, const char *path,
+ const struct stat *statbuf)
+{
+ http_response http_response;
+ const int n = 4096;
+ char *buffer = alloca (n);
+ int cl, fd, r;
+ char *content_length = pitoa (p->pool, statbuf->st_size);
+
+ fd = open (path, O_RDONLY);
+ if (fd < 0)
+ return file_not_found_error (p);
+
+ http_response = new_http_response (pool, p->http_request, p->io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/plain",
+ "Content-Length", content_length,
+ /* End of headers. */
+ NULL);
+ cl = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return cl;
+
+ while ((r = read (fd, buffer, n)) > 0)
+ {
+ io_fwrite (buffer, r, 1, p->io);
+ }
+
+ if (r < 0)
+ perror ("read");
+
+ close (fd);
+
+ return cl;
+}
+</pre>
+
+ <p>
+ Firstly we work out the size of the file, using the
+ <code>statbuf.st_size</code> field. The
+ <a href="http://www.annexia.org/freeware/c2lib/">c2lib</a>
+ function <code>pitoa</code> turns this into a string (all
+ headers must be passed as strings). Next we open the
+ file. If this fails, then the file is inaccessible or
+ has just gone, so we return a 404 instead.
+ </p>
+
+ <p>
+ Next we generate our headers:
+ </p>
+
+<pre>
+Content-Type: text/plain
+Content-Length: <i>(size of the file in octets)</i>
+</pre>
+
+ <p>
+ <code>pthrlib</code> will generate other standard
+ headers as well.
+ </p>
+
+ <p>
+ If the request was a <code>HEAD</code> request, then
+ the client only wants to see the headers, so we stop
+ right there. Otherwise we copy the file back to our
+ user.
+ </p>
+
+ <p>
+ Party question: Why is it OK to use <code>read(2)</code>
+ when reading the file, but not OK to use <code>write(2)</code>
+ when writing to the socket? Why will this <i>not</i> cause
+ the whole server process to block (on Linux at least)?
+ </p>
+
+ <p>
+ Serving a directory is more complicated, so we'll take it in
+ steps. Recall that to serve a directory, we actually need
+ to create an HTML page which lists the files, with information
+ about those files and links to the files themselves.
+ </p>
+
+ <p>
+ Firstly if the user requested the directory as:
+ </p>
+
+<pre>
+http://your.hostname/path/to/directory
+</pre>
+
+ <p>
+ then we need to redirect them to:
+ </p>
+
+<pre>
+http://your.hostname/path/to/directory<b>/</b>
+</pre>
+
+ <p>
+ (note the trailing slash). The reason for this is that
+ relative links within our page won't work otherwise. The
+ browser will request <code>/path/to/file</code> instead of
+ <code>/path/to/directory/file</code>. This is actually a
+ bit of webserver arcana which is often forgotten. If you
+ don't believe me, Apache does this too: go look at the source!
+ </p>
+
+<pre>
+static int
+serve_directory (eg2_server_processor p, const char *path,
+ const struct stat *statbuf)
+{
+ http_response http_response;
+ int close;
+ DIR *dir;
+ struct dirent *d;
+
+ /* If the path doesn't end with a "/", then we need to send
+ * a redirect back to the client so it refetches the page
+ * with "/" appended.
+ */
+ if (path[strlen (path)-1] != '/')
+ {
+ char *location = psprintf (p->pool, "%s/", path);
+ return moved_permanently (p, location);
+ }
+</pre>
+
+ <p>
+ <code>moved_permanently</code> sends back a 301 MOVED PERMANENTLY
+ page causing the browser to re-request the new location.
+ </p>
+
+ <p>
+ The next piece of code should be familiar boilerplate. We open
+ the directory, and send back headers. If the request is
+ <code>HEAD</code> we then drop out.
+ </p>
+
+<pre>
+ dir = opendir (path);
+ if (dir == 0)
+ return file_not_found_error (p);
+
+ http_response = new_http_response (pool, p->http_request, p->io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/html",
+ NO_CACHE_HEADERS,
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return close;
+</pre>
+
+ <p>
+ The next piece of code is the complicated bit which generates
+ the HTML page listing the files:
+ </p>
+
+<pre>
+ io_fprintf (p->io,
+ "<html><head><title>Directory: %s</title></head>" CRLF
+ "<body bgcolor=\"#ffffff\">" CRLF
+ "<h1>Directory: %s</h1>" CRLF
+ "<table>" CRLF
+ "<tr><td></td><td></td>"
+ "<td><a href=\"..\">Parent directory</a></td></tr>" CRLF,
+ path, path);
+
+ while ((d = readdir (dir)) != 0)
+ {
+ if (d->d_name[0] != '.') /* Ignore hidden files. */
+ {
+ const char *filename;
+ struct stat fstatbuf;
+
+ /* Generate the full pathname to this file. */
+ filename = psprintf (p->pool, "%s/%s", path, d->d_name);
+
+ /* Stat the file to find out what it is. */
+ if (lstat (filename, &fstatbuf) == 0)
+ {
+ const char *type;
+ int size;
+
+ if (S_ISDIR (fstatbuf.st_mode))
+ type = "dir";
+ else if (S_ISREG (fstatbuf.st_mode))
+ type = "file";
+ else if (S_ISLNK (fstatbuf.st_mode))
+ type = "link";
+ else
+ type = "special";
+
+ size = fstatbuf.st_size;
+
+ /* Print the details. */
+ io_fprintf (p->io,
+ "<tr><td>[ %s ]</td><td align=right>%d</td>"
+ "<td><a href=\"%s%s\">%s</a>",
+ type, size,
+ d->d_name,
+ S_ISDIR (fstatbuf.st_mode) ? "/" : "",
+ d->d_name);
+
+ if (S_ISLNK (fstatbuf.st_mode))
+ {
+ char link[NAME_MAX+1];
+ int r;
+
+ r = readlink (filename, link, NAME_MAX);
+ if (r >= 0) link[r] = '\0';
+ else strcpy (link, "unknown");
+
+ io_fprintf (p->io, " -&gt; %s", link);
+ }
+
+ io_fputs ("</td></tr>" CRLF, p->io);
+ }
+ }
+ }
+
+ io_fprintf (p->io,
+ "</table></body></html>" CRLF);
+
+ return close;
+</pre>
+
+ <p>
+ We first send the top of the HTML page, and the beginning
+ of the table (the whole page is one large table, of course).
+ </p>
+
+ <p>
+ Next we loop over the directory entries using <code>readdir(3)</code>
+ to read each one. Ignoring files which start with a dot (.) we
+ <code>lstat(2)</code> each file to find out if it's a directory,
+ file or symbolic link, or some type of special device node.
+ </p>
+
+ <p>
+ Depending on the file type, we generate a different bit
+ of HTML containing a relative link to the file or
+ directory (if it's a directory we need to remember to
+ append a trailing slash to the name to avoid that extra
+ 301 redirect).
+ </p>
+
+ <p>
+ Finally after we reach the end of the directory we finish
+ of the table and the page and return.
+ </p>
+
+ <h2>Further examples</h2>
+
+ <p>
+ That's the end of this <code>pthrlib</code> tutorial, I
+ hope you enjoyed it.
+ </p>
+
+ <p>
+ <code>pthrlib</code> isn't just about writing web servers.
+ You can use it to write all sorts of types of servers,
+ or even clients (it has an FTP client library which I
+ used to load-test <code>Net::FTPServer</code>).
+ </p>
+
+ <p>
+ If, however, you feel like using <code>pthrlib</code> to
+ write a web server, I strongly urge you to use
+ <a href="http://www.annexia.org/freeware/rws/">rws</a>
+ and shared object scripts. These are described in
+ the <a href="http://www.annexia.org/freeware/rws/doc/">rws
+ documentation</a>. (rws uses <code>pthrlib</code>).
+ </p>
+
+ <h2>Links to manual pages</h2>
+
+ <p>
+ (These manual pages are not always up to date. For the
+ latest documentation, always consult the manual pages
+ supplied with the latest <code>pthrlib</code> package!)
+ </p>
+
+ <h3>Pseudothreads</h3>
+
+ <ul>
+ <li> <a href="new_pseudothread.3.html"><code>new_pseudothread(3)</code></a> </li>
+ <li> <a href="pseudothread_count_threads.3.html"><code>pseudothread_count_threads(3)</code></a> </li>
+ <li> <a href="pseudothread_get_stack_size.3.html"><code>pseudothread_get_stack_size(3)</code></a> </li>
+ <li> <a href="pseudothread_get_threads.3.html"><code>pseudothread_get_threads(3)</code></a> </li>
+ <li> <a href="pseudothread_set_stack_size.3.html"><code>pseudothread_set_stack_size(3)</code></a> </li>
+ <li> <a href="pth_accept.3.html"><code>pth_accept(3)</code></a> </li>
+ <li> <a href="pth_catch.3.html"><code>pth_catch(3)</code></a> </li>
+ <li> <a href="pth_connect.3.html"><code>pth_connect(3)</code></a> </li>
+ <li> <a href="pth_die.3.html"><code>pth_die(3)</code></a> </li>
+ <li> <a href="pth_exit.3.html"><code>pth_exit(3)</code></a> </li>
+ <li> <a href="pth_get_data.3.html"><code>pth_get_data(3)</code></a> </li>
+ <li> <a href="pth_get_language.3.html"><code>pth_get_language(3)</code></a> </li>
+ <li> <a href="pth_get_name.3.html"><code>pth_get_name(3)</code></a> </li>
+ <li> <a href="pth_get_PC.3.html"><code>pth_get_PC(3)</code></a> </li>
+ <li> <a href="pth_get_pool.3.html"><code>pth_get_pool(3)</code></a> </li>
+ <li> <a href="pth_get_run.3.html"><code>pth_get_run(3)</code></a> </li>
+ <li> <a href="pth_get_SP.3.html"><code>pth_get_SP(3)</code></a> </li>
+ <li> <a href="pth_get_stack.3.html"><code>pth_get_stack(3)</code></a> </li>
+ <li> <a href="pth_get_stack_size.3.html"><code>pth_get_stack_size(3)</code></a> </li>
+ <li> <a href="pth_get_thread_num.3.html"><code>pth_get_thread_num(3)</code></a> </li>
+ <li> <a href="pth_get_tz.3.html"><code>pth_get_tz(3)</code></a> </li>
+ <li> <a href="pth_millisleep.3.html"><code>pth_millisleep(3)</code></a> </li>
+ <li> <a href="pth_nanosleep.3.html"><code>pth_nanosleep(3)</code></a> </li>
+ <li> <a href="pth_poll.3.html"><code>pth_poll(3)</code></a> </li>
+ <li> <a href="pth_read.3.html"><code>pth_read(3)</code></a> </li>
+ <li> <a href="pth_recv.3.html"><code>pth_recv(3)</code></a> </li>
+ <li> <a href="pth_recvfrom.3.html"><code>pth_recvfrom(3)</code></a> </li>
+ <li> <a href="pth_recvmsg.3.html"><code>pth_recvmsg(3)</code></a> </li>
+ <li> <a href="pth_select.3.html"><code>pth_select(3)</code></a> </li>
+ <li> <a href="pth_send.3.html"><code>pth_send(3)</code></a> </li>
+ <li> <a href="pth_sendmsg.3.html"><code>pth_sendmsg(3)</code></a> </li>
+ <li> <a href="pth_sendto.3.html"><code>pth_sendto(3)</code></a> </li>
+ <li> <a href="pth_set_language.3.html"><code>pth_set_language(3)</code></a> </li>
+ <li> <a href="pth_set_name.3.html"><code>pth_set_name(3)</code></a> </li>
+ <li> <a href="pth_set_tz.3.html"><code>pth_set_tz(3)</code></a> </li>
+ <li> <a href="pth_sleep.3.html"><code>pth_sleep(3)</code></a> </li>
+ <li> <a href="pth_start.3.html"><code>pth_start(3)</code></a> </li>
+ <li> <a href="pth_timeout.3.html"><code>pth_timeout(3)</code></a> </li>
+ <li> <a href="pth_write.3.html"><code>pth_write(3)</code></a> </li>
+ </ul>
+
+ <h3>Server main loop</h3>
+
+ <ul>
+ <li> <a href="pthr_server_main_loop.3.html"><code>pthr_server_main_loop(3)</code></a> </li>
+ <li> <a href="pthr_server_default_port.3.html"><code>pthr_server_default_port(3)</code></a> </li>
+ <li> <a href="pthr_server_port_option_name.3.html"><code>pthr_server_port_option_name(3)</code></a> </li>
+ <li> <a href="pthr_server_disable_syslog.3.html"><code>pthr_server_disable_syslog(3)</code></a> </li>
+ <li> <a href="pthr_server_package_name.3.html"><code>pthr_server_package_name(3)</code></a> </li>
+ <li> <a href="pthr_server_disable_fork.3.html"><code>pthr_server_disable_fork(3)</code></a> </li>
+ <li> <a href="pthr_server_disable_chdir.3.html"><code>pthr_server_disable_chdir(3)</code></a> </li>
+ <li> <a href="pthr_server_disable_close.3.html"><code>pthr_server_disable_close(3)</code></a> </li>
+ <li> <a href="pthr_server_chroot.3.html"><code>pthr_server_chroot(3)</code></a> </li>
+ <li> <a href="pthr_server_username.3.html"><code>pthr_server_username(3)</code></a> </li>
+ <li> <a href="pthr_server_stderr_file.3.html"><code>pthr_server_stderr_file(3)</code></a> </li>
+ <li> <a href="pthr_server_startup_fn.3.html"><code>pthr_server_startup_fn(3)</code></a> </li>
+ </ul>
+
+ <h3>Buffered I/O library</h3>
+
+ <ul>
+ <li> <a href="io_copy.3.html"><code>io_copy(3)</code></a> </li>
+ <li> <a href="io_fclose.3.html"><code>io_fclose(3)</code></a> </li>
+ <li> <a href="io_fdopen.3.html"><code>io_fdopen(3)</code></a> </li>
+ <li> <a href="io_fflush.3.html"><code>io_fflush(3)</code></a> </li>
+ <li> <a href="io_fgetc.3.html"><code>io_fgetc(3)</code></a> </li>
+ <li> <a href="io_fgets.3.html"><code>io_fgets(3)</code></a> </li>
+ <li> <a href="io_fileno.3.html"><code>io_fileno(3)</code></a> </li>
+ <li> <a href="io_fprintf.3.html"><code>io_fprintf(3)</code></a> </li>
+ <li> <a href="io_fputc.3.html"><code>io_fputc(3)</code></a> </li>
+ <li> <a href="io_fputs.3.html"><code>io_fputs(3)</code></a> </li>
+ <li> <a href="io_fread.3.html"><code>io_fread(3)</code></a> </li>
+ <li> <a href="io_fwrite.3.html"><code>io_fwrite(3)</code></a> </li>
+ <li> <a href="io_get_inbufcount.3.html"><code>io_get_inbufcount(3)</code></a> </li>
+ <li> <a href="io_get_outbufcount.3.html"><code>io_get_outbufcount(3)</code></a> </li>
+ <li> <a href="io_pclose.3.html"><code>io_pclose(3)</code></a> </li>
+ <li> <a href="io_popen.3.html"><code>io_popen(3)</code></a> </li>
+ <li> <a href="io_setbufmode.3.html"><code>io_setbufmode(3)</code></a> </li>
+ <li> <a href="io_ungetc.3.html"><code>io_ungetc(3)</code></a> </li>
+ </ul>
+
+ <h3>HTTP server library</h3>
+
+ <ul>
+ <li> <a href="http_get_log_file.3.html"><code>http_get_log_file(3)</code></a> </li>
+ <li> <a href="http_get_servername.3.html"><code>http_get_servername(3)</code></a> </li>
+ <li> <a href="http_request_get_header.3.html"><code>http_request_get_header(3)</code></a> </li>
+ <li> <a href="http_request_get_headers.3.html"><code>http_request_get_headers(3)</code></a> </li>
+ <li> <a href="http_request_is_HEAD.3.html"><code>http_request_is_HEAD(3)</code></a> </li>
+ <li> <a href="http_request_method.3.html"><code>http_request_method(3)</code></a> </li>
+ <li> <a href="http_request_method_string.3.html"><code>http_request_method_string(3)</code></a> </li>
+ <li> <a href="http_request_nr_headers.3.html"><code>http_request_nr_headers(3)</code></a> </li>
+ <li> <a href="http_request_path.3.html"><code>http_request_path(3)</code></a> </li>
+ <li> <a href="http_request_query_string.3.html"><code>http_request_query_string(3)</code></a> </li>
+ <li> <a href="http_request_time.3.html"><code>http_request_time(3)</code></a> </li>
+ <li> <a href="http_request_url.3.html"><code>http_request_url(3)</code></a> </li>
+ <li> <a href="http_request_version.3.html"><code>http_request_version(3)</code></a> </li>
+ <li> <a href="http_response_end_headers.3.html"><code>http_response_end_headers(3)</code></a> </li>
+ <li> <a href="http_response_send_header.3.html"><code>http_response_send_header(3)</code></a> </li>
+ <li> <a href="http_response_send_headers.3.html"><code>http_response_send_headers(3)</code></a> </li>
+ <li> <a href="http_set_log_file.3.html"><code>http_set_log_file(3)</code></a> </li>
+ <li> <a href="http_set_servername.3.html"><code>http_set_servername(3)</code></a> </li>
+ <li> <a href="new_http_request.3.html"><code>new_http_request(3)</code></a> </li>
+ <li> <a href="new_http_response.3.html"><code>new_http_response(3)</code></a> </li>
+ </ul>
+
+ <h3>CGI library</h3>
+
+ <ul>
+ <li> <a href="cgi_erase.3.html"><code>cgi_erase(3)</code></a> </li>
+ <li> <a href="cgi_escape.3.html"><code>cgi_escape(3)</code></a> </li>
+ <li> <a href="cgi_get_post_max.3.html"><code>cgi_get_post_max(3)</code></a> </li>
+ <li> <a href="cgi_param.3.html"><code>cgi_param(3)</code></a> </li>
+ <li> <a href="cgi_param_list.3.html"><code>cgi_param_list(3)</code></a> </li>
+ <li> <a href="cgi_params.3.html"><code>cgi_params(3)</code></a> </li>
+ <li> <a href="cgi_set_post_max.3.html"><code>cgi_set_post_max(3)</code></a> </li>
+ <li> <a href="cgi_unescape.3.html"><code>cgi_unescape(3)</code></a> </li>
+ <li> <a href="copy_cgi.3.html"><code>copy_cgi(3)</code></a> </li>
+ <li> <a href="new_cgi.3.html"><code>new_cgi(3)</code></a> </li>
+ </ul>
+
+ <h3>Thread synchronisation (mutexes, R/W-locks, wait queues)</h3>
+
+ <ul>
+ <li> <a href="mutex_enter.3.html"><code>mutex_enter(3)</code></a> </li>
+ <li> <a href="mutex_leave.3.html"><code>mutex_leave(3)</code></a> </li>
+ <li> <a href="mutex_try_enter.3.html"><code>mutex_try_enter(3)</code></a> </li>
+ <li> <a href="new_mutex.3.html"><code>new_mutex(3)</code></a> </li>
+ <li> <a href="new_rwlock.3.html"><code>new_rwlock(3)</code></a> </li>
+ <li> <a href="new_wait_queue.3.html"><code>new_wait_queue(3)</code></a> </li>
+ <li> <a href="rwlock_enter_read.3.html"><code>rwlock_enter_read(3)</code></a> </li>
+ <li> <a href="rwlock_enter_write.3.html"><code>rwlock_enter_write(3)</code></a> </li>
+ <li> <a href="rwlock_leave.3.html"><code>rwlock_leave(3)</code></a> </li>
+ <li> <a href="rwlock_readers_have_priority.3.html"><code>rwlock_readers_have_priority(3)</code></a> </li>
+ <li> <a href="rwlock_try_enter_read.3.html"><code>rwlock_try_enter_read(3)</code></a> </li>
+ <li> <a href="rwlock_try_enter_write.3.html"><code>rwlock_try_enter_write(3)</code></a> </li>
+ <li> <a href="rwlock_writers_have_priority.3.html"><code>rwlock_writers_have_priority(3)</code></a> </li>
+ <li> <a href="wq_nr_sleepers.3.html"><code>wq_nr_sleepers(3)</code></a> </li>
+ <li> <a href="wq_sleep_on.3.html"><code>wq_sleep_on(3)</code></a> </li>
+ <li> <a href="wq_wake_up.3.html"><code>wq_wake_up(3)</code></a> </li>
+ <li> <a href="wq_wake_up_one.3.html"><code>wq_wake_up_one(3)</code></a> </li>
+ </ul>
+
+ <h3>FTP client library</h3>
+
+ <ul>
+ <li> <a href="ftpc_ascii.3.html"><code>ftpc_ascii(3)</code></a> </li>
+ <li> <a href="ftpc_binary.3.html"><code>ftpc_binary(3)</code></a> </li>
+ <li> <a href="ftpc_cdup.3.html"><code>ftpc_cdup(3)</code></a> </li>
+ <li> <a href="ftpc_cwd.3.html"><code>ftpc_cwd(3)</code></a> </li>
+ <li> <a href="ftpc_delete.3.html"><code>ftpc_delete(3)</code></a> </li>
+ <li> <a href="ftpc_dir.3.html"><code>ftpc_dir(3)</code></a> </li>
+ <li> <a href="ftpc_get.3.html"><code>ftpc_get(3)</code></a> </li>
+ <li> <a href="ftpc_login.3.html"><code>ftpc_login(3)</code></a> </li>
+ <li> <a href="ftpc_ls.3.html"><code>ftpc_ls(3)</code></a> </li>
+ <li> <a href="ftpc_mkdir.3.html"><code>ftpc_mkdir(3)</code></a> </li>
+ <li> <a href="ftpc_put.3.html"><code>ftpc_put(3)</code></a> </li>
+ <li> <a href="ftpc_pwd.3.html"><code>ftpc_pwd(3)</code></a> </li>
+ <li> <a href="ftpc_quit.3.html"><code>ftpc_quit(3)</code></a> </li>
+ <li> <a href="ftpc_quote.3.html"><code>ftpc_quote(3)</code></a> </li>
+ <li> <a href="ftpc_rmdir.3.html"><code>ftpc_rmdir(3)</code></a> </li>
+ <li> <a href="ftpc_set_passive_mode.3.html"><code>ftpc_set_passive_mode(3)</code></a> </li>
+ <li> <a href="ftpc_type.3.html"><code>ftpc_type(3)</code></a> </li>
+ <li> <a href="new_ftpc.3.html"><code>new_ftpc(3)</code></a> </li>
+ </ul>
+
+ <hr>
+ <address><a href="mailto:rich@annexia.org">Richard Jones</a></address>
+<!-- Created: Wed May 1 19:36:16 BST 2002 -->
+<!-- hhmts start -->
+Last modified: Sun Dec 1 14:44:00 GMT 2002
+<!-- hhmts end -->
+ </body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:24 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:24 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:24 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:25 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:25 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:25 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:25 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:26 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:26 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:26 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:26 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:27 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:27 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:27 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:28 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:28 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:28 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>io_fdopen</title>
+</head>
+<body>
+
+<h1 align=center>io_fdopen</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:28 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+io_fdopen, io_fclose, io_fgetc, io_fgets, io_ungetc, io_fread, io_fputc, io_fputs, io_fprintf, io_fwrite, io_fflush, io_fileno, io_popen, io_pclose, io_copy, io_setbufmode, io_get_inbufcount, io_get_outbufcount - A buffered I/O library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_iolib.h>
+
+io_handle io_fdopen (pseudothread pth, int sock);
+void io_fclose (io_handle);
+int io_fgetc (io_handle);
+char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+int io_ungetc (int c, io_handle);
+size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fputc (int c, io_handle);
+int io_fputs (const char *s, io_handle);
+int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+int io_fflush (io_handle);
+int io_fileno (io_handle);
+io_handle io_popen (pseudothread pth, const char *command, const char *mode);
+void io_pclose (io_handle);
+int io_copy (io_handle from_io, io_handle to_io, int len);
+void io_setbufmode (io_handle, int mode);
+int io_get_inbufcount (io_handle);
+int io_get_outbufcount (io_handle);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>io_*</b> functions replace the normal blocking C
+library <b>f*</b> functions with equivalents which work on
+non-blocking pseudothread file descriptors.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+All of the functions in the synopsis above work identically
+to the C library equivalents, except where documented
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fdopen</b> associates a socket <b>sock</b> with a I/O
+handle. The association cannot be broken later (so use
+<b>dup(2)</b> if you wish to later take back control of the
+underlying socket). If either the current thread exits or
+<b>io_fclose</b> is called, the underlying socket is closed
+(with <b>close(2)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fclose</b> flushes all unwritten data out of the
+socket and closes it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_fgets</b> operates similarly to the ordinary C library
+function <b>fgets(3)</b>, except that it contains a useful
+fourth argument, <b>store_eol</b>. If this fourth argument
+is false, then the end of line characters (<b>CR</b>, <b>CR
+LF</b> or <b>LF</b>) are stripped from the string before it
+is stored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_copy</b> copies <b>len</b> bytes from <b>from_io</b>
+to <b>to_io</b>. If <b>len</b> equals -1 then bytes are
+copied from <b>from_io</b> until end of file is reached. If
+<b>len</b> equals 0, then no bytes are copied. The number of
+bytes actually copied is returned.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_setbufmode</b> sets the output buffer mode, and works
+completely differently to the ordinary C library function
+<b>setbufmode(3)</b>. The three mode arguments possible are:
+<b>IO_MODE_LINE_BUFFERED</b>, <b>IO_MODE_UNBUFFERED</b> and
+<b>IO_MODE_FULLY_BUFFERED</b>, and these correspond to line
+buffering, no buffering and full (block)
+buffering.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>io_get_inbufcount</b> and <b>io_get_outbufcount</b>
+return the number of characters read and written on the
+socket since the socket was associated with the I/O
+object.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fgetc(3)</b>, <b>fgets(3)</b>, <b>ungetc(3)</b>,
+<b>fread(3)</b>, <b>fputc(3)</b>, <b>fputs(3)</b>,
+<b>fprintf(3)</b>, <b>fwrite(3)</b>, <b>fflush(3)</b>,
+<b>fileno(3)</b>, <b>popen(3)</b>, <b>pclose(3)</b>,
+<b>pth_exit(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_mutex</title>
+</head>
+<body>
+
+<h1 align=center>new_mutex</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:29 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_mutex, mutex_enter, mutex_leave, mutex_try_enter - mutual exclusion (mutex) locks</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_mutex.h>
+
+mutex new_mutex (pool);
+void mutex_enter (mutex, pseudothread);
+void mutex_leave (mutex, pseudothread);
+int mutex_try_enter (mutex, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Mutex locks are simple: at most one pseudothread may enter
+the critical area protected by the lock at once. If instead
+you wish multiple reader / single writer semantics, then
+please see <b>new_rwlock(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Mutex locks are automatically released if they are being
+held when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_mutex</b> creates a new mutex object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>mutex_enter</b> and <b>mutex_leave</b> enter and leave
+the critical section. Only one thread can run at a time
+inside the critical section.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>mutex_try_enter</b> is identical to <b>mutex_enter</b>
+except that it does not block if the lock is held by another
+thread. The function returns true if the lock was
+successfully acquired, or false if another thread is
+currently holding it.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>mutex_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_mutex</title>
+</head>
+<body>
+
+<h1 align=center>new_mutex</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:29 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_mutex, mutex_enter, mutex_leave, mutex_try_enter - mutual exclusion (mutex) locks</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_mutex.h>
+
+mutex new_mutex (pool);
+void mutex_enter (mutex, pseudothread);
+void mutex_leave (mutex, pseudothread);
+int mutex_try_enter (mutex, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Mutex locks are simple: at most one pseudothread may enter
+the critical area protected by the lock at once. If instead
+you wish multiple reader / single writer semantics, then
+please see <b>new_rwlock(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Mutex locks are automatically released if they are being
+held when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_mutex</b> creates a new mutex object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>mutex_enter</b> and <b>mutex_leave</b> enter and leave
+the critical section. Only one thread can run at a time
+inside the critical section.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>mutex_try_enter</b> is identical to <b>mutex_enter</b>
+except that it does not block if the lock is held by another
+thread. The function returns true if the lock was
+successfully acquired, or false if another thread is
+currently holding it.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>mutex_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_mutex</title>
+</head>
+<body>
+
+<h1 align=center>new_mutex</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:29 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_mutex, mutex_enter, mutex_leave, mutex_try_enter - mutual exclusion (mutex) locks</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_mutex.h>
+
+mutex new_mutex (pool);
+void mutex_enter (mutex, pseudothread);
+void mutex_leave (mutex, pseudothread);
+int mutex_try_enter (mutex, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Mutex locks are simple: at most one pseudothread may enter
+the critical area protected by the lock at once. If instead
+you wish multiple reader / single writer semantics, then
+please see <b>new_rwlock(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Mutex locks are automatically released if they are being
+held when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_mutex</b> creates a new mutex object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>mutex_enter</b> and <b>mutex_leave</b> enter and leave
+the critical section. Only one thread can run at a time
+inside the critical section.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>mutex_try_enter</b> is identical to <b>mutex_enter</b>
+except that it does not block if the lock is held by another
+thread. The function returns true if the lock was
+successfully acquired, or false if another thread is
+currently holding it.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>mutex_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_cgi</title>
+</head>
+<body>
+
+<h1 align=center>new_cgi</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:29 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_cgi, cgi_params, cgi_param, cgi_param_list, cgi_erase, copy_cgi - Library for parsing CGI query strings.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_cgi.h>
+
+cgi new_cgi (pool, http_request, io_handle);
+vector cgi_params (cgi);
+const char *cgi_param (cgi, const char *name);
+const vector cgi_param_list (cgi, const char *name);
+int cgi_erase (cgi, const char *name);
+cgi copy_cgi (pool, cgi);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_cgi</b> creates a new CGI object from an existing
+HTTP request. It reads the query string or POSTed parameters
+and parses them internally. CGI parameters are case
+sensitive, and multiple parameters may be passed with the
+same name. Parameter values are automatically unescaped by
+the library before you get to see them.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_params</b> returns a list of all the names of the
+parameters passed to the script. The list is returned as a
+<b>vector</b> of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param</b> returns the value of a single named CGI
+parameter, or <b>NULL</b> if there is no such parameter. If
+multiple parameters were given with the same name, this
+returns one of them, essentially at random.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_param_list</b> returns the list of values of the
+named CGI parameter. The list is returned as a <b>vector</b>
+of <b>char *</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_erase</b> erases the named parameter. If a parameter
+was erased, this returns true, else this returns
+false.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>copy_cgi</b> copies <b>cgi</b> into pool
+<b>pool</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>cgi_get_post_max(3)</b>, <b>cgi_escape(3)</b>,
+<b>new_http_request(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_ftpc</title>
+</head>
+<body>
+
+<h1 align=center>new_ftpc</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:29 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_ftpc - Create a new FTP client object.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_ftpc.h>
+
+ftpc new_ftpc (pool, pseudothread pth, const char *server);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Create a new FTP client object, connected to the FTP server
+called <b>server</b>. The <b>server</b> may be an IP address
+or a hostname. If the <b>server</b> name ends with
+<b>:port</b> then <b>port</b> is the port number to connect
+to.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The default mode for new connections is active mode. Call
+<b>ftpc_set_mode(3)</b> to change the mode.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>ftpc_login(3)</b>, <b>ftpc_set_mode(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_request</title>
+</head>
+<body>
+
+<h1 align=center>new_http_request</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:30 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_request, http_request_time, http_request_url, http_request_path, http_request_query_string, http_request_method, http_request_method_string, http_request_is_HEAD, http_request_version, http_request_nr_headers, http_request_get_headers, http_request_get_header - functions for parsing HTTP requests</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_request new_http_request (pseudothread, io_handle);
+time_t http_request_time (http_request);
+const char *http_request_url (http_request);
+const char *http_request_path (http_request);
+const char *http_request_query_string (http_request);
+int http_request_method (http_request);
+const char *http_request_method_string (http_request);
+int http_request_is_HEAD (http_request);
+void http_request_version (http_request, int *major, int *minor);
+int http_request_nr_headers (http_request);
+vector http_request_get_headers (http_request);
+const char *http_request_get_header (http_request h, const char *key);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently parse incoming HTTP
+requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+clients. The request parser understands GET, HEAD and POST
+requests and conforms as far as possible to RFC
+2616.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request</b> creates a new request object,
+parsing the incoming request on the given <b>io_handle</b>.
+If the stream closes at the beginning of the request the
+function returns <b>NULL</b>. If the request is faulty, then
+the library prints a message to syslog and throws an
+exception by calling <b>pth_die(3)</b>. Otherwise it
+initializes a complete <b>http_request</b> object and
+returns it.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_time</b> returns the timestamp of the
+incoming request.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_url</b> returns the complete URL of the
+request. <b>http_request_path</b> returns just the path
+component of the URL (ie. without the query string if there
+was one). <b>http_request_query_string</b> returns just the
+query string (for GET requests only). Do not do your own
+parsing of query strings: there is a CGI library built into
+pthrlib (see: <b>new_cgi(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_method</b> returns the method, one of
+<b>HTTP_METHOD_GET</b>, <b>HTTP_METHOD_HEAD</b> or
+<b>HTTP_METHOD_POST</b>. <b>http_request_is_HEAD</b> is just
+a quick way of testing if the method is a HEAD request.
+<b>http_request_method_string</b> returns the method as a
+string rather than a coded number.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_version</b> returns the major and minor
+numbers of the HTTP request (eg. major = 1, minor = 0 for a
+HTTP/1.0 request).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_request_nr_headers</b>,
+<b>http_request_get_headers</b> and
+<b>http_request_get_header</b> return the number of HTTP
+headers, the list of HTTP headers and a particular HTTP
+header (if it exists). <b>http_request_get_headers</b>
+returns a <b>vector</b> or <b>struct http_header</b>. This
+structure contains at least two fields called <b>key</b> and
+<b>value</b>. HTTP header keys are case insensitive when
+searching, and you will find that the list of keys returned
+by <b>http_request_get_headers</b> has been converted to
+lowercase.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_http_response</title>
+</head>
+<body>
+
+<h1 align=center>new_http_response</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:30 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_http_response, http_response_send_header, http_response_send_headers, http_response_end_headers - functions for sending HTTP responses</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_http.h>
+
+http_response new_http_response (pseudothread, http_request, io_handle, int code, const char *msg);
+void http_response_send_header (http_response, const char *key, const char *value);
+void http_response_send_headers (http_response, ...);
+int http_response_end_headers (http_response h);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+These functions allow you to efficiently generate outgoing
+HTTP responses.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_response</b> generates a new HTTP response
+object and returns it. <b>code</b> is the HTTP response code
+(see RFC 2616 for a list of codes), and <b>msg</b> is the
+HTTP response message.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_send_header</b> sends a single HTTP header
+back to the client. The header is constructed by
+concatenating <b>key</b>, <b>": "</b>,
+<b>value</b> and <b>CR LF</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_send_headers</b> sends back several headers
+in a single call. The arguments to this function are a list
+of <b>key</b>, <b>value</b> pairs followed by a single
+<b>NULL</b> argument which terminates the list.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>http_response_end_headers</b> ends the header list. It
+causes the code to emit any missing-but-required headers and
+then send the final <b>CR LF</b> characters.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_http_request(3)</b>, <b>new_cgi(3)</b>,
+<b>new_pseudothread(3)</b>, <b>io_fdopen(3)</b>, RFC
+2616.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_mutex</title>
+</head>
+<body>
+
+<h1 align=center>new_mutex</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:30 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_mutex, mutex_enter, mutex_leave, mutex_try_enter - mutual exclusion (mutex) locks</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_mutex.h>
+
+mutex new_mutex (pool);
+void mutex_enter (mutex, pseudothread);
+void mutex_leave (mutex, pseudothread);
+int mutex_try_enter (mutex, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Mutex locks are simple: at most one pseudothread may enter
+the critical area protected by the lock at once. If instead
+you wish multiple reader / single writer semantics, then
+please see <b>new_rwlock(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Mutex locks are automatically released if they are being
+held when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_mutex</b> creates a new mutex object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>mutex_enter</b> and <b>mutex_leave</b> enter and leave
+the critical section. Only one thread can run at a time
+inside the critical section.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>mutex_try_enter</b> is identical to <b>mutex_enter</b>
+except that it does not block if the lock is held by another
+thread. The function returns true if the lock was
+successfully acquired, or false if another thread is
+currently holding it.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>mutex_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_pseudothread</title>
+</head>
+<body>
+
+<h1 align=center>new_pseudothread</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:30 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_pseudothread, pth_start, pseudothread_get_threads, pseudothread_count_threads - lightweight "pseudothreads" library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name);
+void pth_start (pseudothread pth);
+vector pseudothread_get_threads (pool);
+int pseudothread_count_threads (void);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Pseudothreads are lightweight, cooperatively scheduled
+threads.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_pseudothread</b> creates a new pseudothread. The
+thread only starts running when you call <b>pth_start</b>.
+The <b>pool</b> argument passed is used for all allocations
+within the thread. This pool is automatically deleted when
+the thread exits. The <b>name</b> argument is the name of
+the thread (used in thread listings -- see
+<b>pseudothread_get_threads(3)</b>). You may change the name
+later. The <b>run</b> and <b>data</b> arguments are the
+entry point into the thread. The entry point is called as
+<b>run (data)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_get_threads</b> returns a list of all the
+currently running pseudothreads. This allows you to
+implement a "process listing" for a program. The
+returned value is a vector of pseudothread structures (not
+pointers). These structures are opaque to you, but you can
+call the pth_get_* functions on the address of each
+one.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_count_threads</b> counts the number of
+currently running threads.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_rwlock</title>
+</head>
+<body>
+
+<h1 align=center>new_rwlock</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:31 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Multiple reader / single writer locks (rwlocks) do what they
+say. They allow either many readers to access a critical
+section or a single writer (but not both). If instead you
+require simple mutex semantics, then please see
+<pthr_mutex.h>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are automatically released if they are being held
+when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are not ``upgradable''. The implementation is too
+complex to justify that.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_rwlock</b> creates a new rwlock object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_writers_have_priority</b> changes the nature of
+the lock so that writers have priority over readers. If this
+is the case then new readers will not be able to enter a
+critical section if there are writers waiting to enter. [NB:
+This is the default.]</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_readers_have_priority</b> changes the nature of
+the lock so that readers have priority over writers. Note
+that if this is the case then writers are likely to be
+starved if the lock is frequently read.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_read</b> enters the critical section as a
+reader. Any number of readers are allowed to enter a
+critical section at the same time. This function may
+block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_write</b> enters the critical section as a
+writer. Only a single writer is allowed to enter a critical
+section, and then only if there are no readers. This
+function may block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_read</b> is identical to
+<b>rwlock_enter_read</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_write</b> is identical to
+<b>rwlock_enter_write</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_leave</b> leaves the critical
+section.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>rwlock_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_wait_queue</title>
+</head>
+<body>
+
+<h1 align=center>new_wait_queue</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#HISTORY">HISTORY</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:31 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_wait_queue</b> creates a wait queue
+object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up</b> wakes up all the threads which are
+currently sleeping on the wait queue. Note that this
+function does not block, and because pseudothreads are
+non-preemptive, none of the sleeping threads will actually
+begin running until at least the current thread blocks
+somewhere else.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up_one</b> wakes up just the first thread at the
+head of the wait queue (the one which has been waiting the
+longest).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_sleep_on</b> sends the current thread to sleep on the
+wait queue. This call blocks (obviously).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_nr_sleepers</b> returns the number of threads which
+are currently asleep on the wait queue.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Please read the HISTORY section below for some background
+into how wait queues are implemented. This may help if you
+find there are tricky race conditions in your
+code.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="HISTORY"></a>
+<h2>HISTORY</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Originally, wait queues were implemented using underlying
+Unix pipes. This worked (to some extent) but the overhead of
+requiring one pipe (ie. one inode, two file descriptors) per
+wait queue made this implementation unacceptably
+heavyweight.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are now implemented using a simple hack in the
+reactor which will be described below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are subtle. Consider this example: Threads 1, 2
+and 3 are sleeping on a wait queue. Now thread 4 wakes up
+the queue. You would expect (probably) threads 1, 2 and 3 to
+each be woken up and allowed to start running. However,
+since this is a cooperatively multitasking environment, it
+may happen that thread 1 wakes up first, does some work and
+then goes back to sleep on the wait queue, all before
+threads 2 and 3 have woken up. With a naive implementation
+of wait queues, thread 4 might end up waking up thread 1
+*again* (and even again after that), never waking up threads
+2 and 3 and ultimately starving those threads.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To avoid this situation, we might consider two possible
+alternatives: either when thread 1 goes back to sleep, it
+goes to sleep on a 'different' queue, or else thread 4 might
+take a copy of the wait queue and delete the queue before it
+wakes any of the threads up.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Another nasty situation which might arise in real life is
+this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes
+them up. Thread 1, while processing its work, happens also
+to wake up the same wait queue. What should happen to this
+second wake-up event? Should it be ignored? Should it wake
+up threads 2 and 3? Should it wake up any other threads
+which happen to have gone to sleep on the queue after 1, 2
+and 3? Or perhaps some combination of these?</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The solution that we have come up with is as follows. A wait
+queue consists of a simple list of threads which are
+sleeping on it. When a thread wishes to sleep on the wait
+queue, it is added to this list, and it switches back into
+the reactor context. When a thread wishes to wake up all
+sleepers, it: (a) copies the list of sleeping pseudothreads
+into its own private space (b) clears the list of sleeping
+pseudothreads (c) registers a prepoll handler to run which
+will wake up (ie. switch into the context of) each of these
+threads in turn (d) continues to run to completion. A thread
+which wishes to wake just one pseudothread works similarly
+except that it only copies (and removes) a single item off
+the list.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note various invariant conditions: A thread cannot be
+entered on the wait queue sleeping list more than once
+(because it cannot call sleep_on when it is already
+sleeping). For similar reasons, a thread cannot be entered
+on any of the multiple lists at the same time. This implies
+that a thread cannot be woken up multiple
+times.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The reader should satisfy themselves that this algorithm is
+free of races, and solves all the problems outlined above.
+In addition, it has the desirable property that wake_up*
+never sleeps.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_pseudothread</title>
+</head>
+<body>
+
+<h1 align=center>new_pseudothread</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:31 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_pseudothread, pth_start, pseudothread_get_threads, pseudothread_count_threads - lightweight "pseudothreads" library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name);
+void pth_start (pseudothread pth);
+vector pseudothread_get_threads (pool);
+int pseudothread_count_threads (void);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Pseudothreads are lightweight, cooperatively scheduled
+threads.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_pseudothread</b> creates a new pseudothread. The
+thread only starts running when you call <b>pth_start</b>.
+The <b>pool</b> argument passed is used for all allocations
+within the thread. This pool is automatically deleted when
+the thread exits. The <b>name</b> argument is the name of
+the thread (used in thread listings -- see
+<b>pseudothread_get_threads(3)</b>). You may change the name
+later. The <b>run</b> and <b>data</b> arguments are the
+entry point into the thread. The entry point is called as
+<b>run (data)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_get_threads</b> returns a list of all the
+currently running pseudothreads. This allows you to
+implement a "process listing" for a program. The
+returned value is a vector of pseudothread structures (not
+pointers). These structures are opaque to you, but you can
+call the pth_get_* functions on the address of each
+one.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_count_threads</b> counts the number of
+currently running threads.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pseudothread_set_stack_size</title>
+</head>
+<body>
+
+<h1 align=center>pseudothread_set_stack_size</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:31 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pseudothread_set_stack_size, pseudothread_get_stack_size - set and get default stack size</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pseudothread_set_stack_size (int size);
+int pseudothread_get_stack_size (void);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_set_stack_size</b> sets the stack size for
+newly created threads. The default stack size is 64
+KBytes.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_get_stack_size</b> returns the current stack
+size setting.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_pseudothread(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_pseudothread</title>
+</head>
+<body>
+
+<h1 align=center>new_pseudothread</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:32 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_pseudothread, pth_start, pseudothread_get_threads, pseudothread_count_threads - lightweight "pseudothreads" library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name);
+void pth_start (pseudothread pth);
+vector pseudothread_get_threads (pool);
+int pseudothread_count_threads (void);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Pseudothreads are lightweight, cooperatively scheduled
+threads.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_pseudothread</b> creates a new pseudothread. The
+thread only starts running when you call <b>pth_start</b>.
+The <b>pool</b> argument passed is used for all allocations
+within the thread. This pool is automatically deleted when
+the thread exits. The <b>name</b> argument is the name of
+the thread (used in thread listings -- see
+<b>pseudothread_get_threads(3)</b>). You may change the name
+later. The <b>run</b> and <b>data</b> arguments are the
+entry point into the thread. The entry point is called as
+<b>run (data)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_get_threads</b> returns a list of all the
+currently running pseudothreads. This allows you to
+implement a "process listing" for a program. The
+returned value is a vector of pseudothread structures (not
+pointers). These structures are opaque to you, but you can
+call the pth_get_* functions on the address of each
+one.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_count_threads</b> counts the number of
+currently running threads.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pseudothread_set_stack_size</title>
+</head>
+<body>
+
+<h1 align=center>pseudothread_set_stack_size</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:32 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pseudothread_set_stack_size, pseudothread_get_stack_size - set and get default stack size</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pseudothread_set_stack_size (int size);
+int pseudothread_get_stack_size (void);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_set_stack_size</b> sets the stack size for
+newly created threads. The default stack size is 64
+KBytes.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_get_stack_size</b> returns the current stack
+size setting.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_pseudothread(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_accept</title>
+</head>
+<body>
+
+<h1 align=center>pth_accept</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:32 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_accept</b>, <b>pth_connect</b>, <b>pth_read</b>,
+<b>pth_write</b>, <b>pth_sleep</b> and <b>pth_nanosleep</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block. For general I/O
+you will probably wish to wrap up your sockets in I/O handle
+objects, which give you a higher-level buffered interface to
+sockets (see <b>io_fdopen(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_millisleep</b> sleeps for a given number of
+milliseconds.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout</b> is similar to the <b>alarm(2)</b> system
+call: it registers a timeout (in seconds). The thread will
+exit automatically (even in the middle of a system call) if
+the timeout is reached. To reset the timeout, call
+<b>pth_timeout</b> with a timeout of 0.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_select(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_exit</title>
+</head>
+<body>
+
+<h1 align=center>pth_exit</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:32 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_exit, pth_die, pth_catch - exit a pseudothread and exception handling</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+void pth_exit (pseudothread) __attribute__((noreturn));
+#define pth_die(pth,msg) _pth_die ((pth), (msg), __FILE__, __LINE__)
+const char *pth_catch (pseudothread pth, void (*fn) (void *), void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_exit</b> causes the current thread to exit
+immediately. Note: you cannot force another thread to exit
+by calling this function. If you try to do that, strange and
+undefined things are likely to happen. Only the currently
+running thread may call <b>pth_exit</b> on
+itself.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_die</b> is similar in concept to <b>pth_exit</b>,
+except that it throws an exception which may be caught by
+using the <b>pth_catch</b> function. The distinction between
+<b>pth_die</b> and <b>pth_exit</b> is the same as the
+distinction between the Perl functions <b>die</b> and
+<b>exit</b>, in as much as <b>exit</b> in Perl always exits
+the process immediately, and <b>die</b> in Perl generally
+exits the process immediately unless the programmer catches
+the exception with <b>eval</b> and handles it
+appropriately.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_catch</b> is used to catch exceptions thrown by
+<b>pth_die</b>. You give <b>fn</b> (function) and
+<b>data</b> arguments, and the function calls <b>fn
+(data)</b>. If, during this call, the code calls
+<b>pth_die</b>, then the exception message is immediately
+returned from <b>pth_catch</b>. If the code runs
+successfully to completion, then <b>pth_catch</b> will
+return <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Exceptions may be nested.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that <b>pth_catch</b> will not catch calls to
+<b>pth_exit</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_accept</title>
+</head>
+<body>
+
+<h1 align=center>pth_accept</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:33 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_accept</b>, <b>pth_connect</b>, <b>pth_read</b>,
+<b>pth_write</b>, <b>pth_sleep</b> and <b>pth_nanosleep</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block. For general I/O
+you will probably wish to wrap up your sockets in I/O handle
+objects, which give you a higher-level buffered interface to
+sockets (see <b>io_fdopen(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_millisleep</b> sleeps for a given number of
+milliseconds.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout</b> is similar to the <b>alarm(2)</b> system
+call: it registers a timeout (in seconds). The thread will
+exit automatically (even in the middle of a system call) if
+the timeout is reached. To reset the timeout, call
+<b>pth_timeout</b> with a timeout of 0.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_select(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_exit</title>
+</head>
+<body>
+
+<h1 align=center>pth_exit</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:33 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_exit, pth_die, pth_catch - exit a pseudothread and exception handling</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+void pth_exit (pseudothread) __attribute__((noreturn));
+#define pth_die(pth,msg) _pth_die ((pth), (msg), __FILE__, __LINE__)
+const char *pth_catch (pseudothread pth, void (*fn) (void *), void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_exit</b> causes the current thread to exit
+immediately. Note: you cannot force another thread to exit
+by calling this function. If you try to do that, strange and
+undefined things are likely to happen. Only the currently
+running thread may call <b>pth_exit</b> on
+itself.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_die</b> is similar in concept to <b>pth_exit</b>,
+except that it throws an exception which may be caught by
+using the <b>pth_catch</b> function. The distinction between
+<b>pth_die</b> and <b>pth_exit</b> is the same as the
+distinction between the Perl functions <b>die</b> and
+<b>exit</b>, in as much as <b>exit</b> in Perl always exits
+the process immediately, and <b>die</b> in Perl generally
+exits the process immediately unless the programmer catches
+the exception with <b>eval</b> and handles it
+appropriately.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_catch</b> is used to catch exceptions thrown by
+<b>pth_die</b>. You give <b>fn</b> (function) and
+<b>data</b> arguments, and the function calls <b>fn
+(data)</b>. If, during this call, the code calls
+<b>pth_die</b>, then the exception message is immediately
+returned from <b>pth_catch</b>. If the code runs
+successfully to completion, then <b>pth_catch</b> will
+return <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Exceptions may be nested.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that <b>pth_catch</b> will not catch calls to
+<b>pth_exit</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_exit</title>
+</head>
+<body>
+
+<h1 align=center>pth_exit</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:33 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_exit, pth_die, pth_catch - exit a pseudothread and exception handling</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+void pth_exit (pseudothread) __attribute__((noreturn));
+#define pth_die(pth,msg) _pth_die ((pth), (msg), __FILE__, __LINE__)
+const char *pth_catch (pseudothread pth, void (*fn) (void *), void *data);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_exit</b> causes the current thread to exit
+immediately. Note: you cannot force another thread to exit
+by calling this function. If you try to do that, strange and
+undefined things are likely to happen. Only the currently
+running thread may call <b>pth_exit</b> on
+itself.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_die</b> is similar in concept to <b>pth_exit</b>,
+except that it throws an exception which may be caught by
+using the <b>pth_catch</b> function. The distinction between
+<b>pth_die</b> and <b>pth_exit</b> is the same as the
+distinction between the Perl functions <b>die</b> and
+<b>exit</b>, in as much as <b>exit</b> in Perl always exits
+the process immediately, and <b>die</b> in Perl generally
+exits the process immediately unless the programmer catches
+the exception with <b>eval</b> and handles it
+appropriately.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_catch</b> is used to catch exceptions thrown by
+<b>pth_die</b>. You give <b>fn</b> (function) and
+<b>data</b> arguments, and the function calls <b>fn
+(data)</b>. If, during this call, the code calls
+<b>pth_die</b>, then the exception message is immediately
+returned from <b>pth_catch</b>. If the code runs
+successfully to completion, then <b>pth_catch</b> will
+return <b>NULL</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Exceptions may be nested.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that <b>pth_catch</b> will not catch calls to
+<b>pth_exit</b>.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:33 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:34 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:34 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:34 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:34 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:35 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:35 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:35 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:35 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:36 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:36 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_accept</title>
+</head>
+<body>
+
+<h1 align=center>pth_accept</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:36 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_accept</b>, <b>pth_connect</b>, <b>pth_read</b>,
+<b>pth_write</b>, <b>pth_sleep</b> and <b>pth_nanosleep</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block. For general I/O
+you will probably wish to wrap up your sockets in I/O handle
+objects, which give you a higher-level buffered interface to
+sockets (see <b>io_fdopen(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_millisleep</b> sleeps for a given number of
+milliseconds.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout</b> is similar to the <b>alarm(2)</b> system
+call: it registers a timeout (in seconds). The thread will
+exit automatically (even in the middle of a system call) if
+the timeout is reached. To reset the timeout, call
+<b>pth_timeout</b> with a timeout of 0.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_select(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_accept</title>
+</head>
+<body>
+
+<h1 align=center>pth_accept</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:36 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_accept</b>, <b>pth_connect</b>, <b>pth_read</b>,
+<b>pth_write</b>, <b>pth_sleep</b> and <b>pth_nanosleep</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block. For general I/O
+you will probably wish to wrap up your sockets in I/O handle
+objects, which give you a higher-level buffered interface to
+sockets (see <b>io_fdopen(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_millisleep</b> sleeps for a given number of
+milliseconds.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout</b> is similar to the <b>alarm(2)</b> system
+call: it registers a timeout (in seconds). The thread will
+exit automatically (even in the middle of a system call) if
+the timeout is reached. To reset the timeout, call
+<b>pth_timeout</b> with a timeout of 0.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_select(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_poll</title>
+</head>
+<body>
+
+<h1 align=center>pth_poll</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:37 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_poll, pth_select - pseudothread poll and select system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_poll (pseudothread, struct pollfd *fds, unsigned int n, int timeout);
+int pth_select (pseudothread, int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll</b> behaves like the <b>poll(2)</b> system call.
+It specifies an array <b>n</b> of <b>fds</b> file descriptor
+structures each of which is checked for an I/O event. If
+<b>timeout</b> is greater than or equal to zero, then the
+call will return after this many milliseconds if no I/O
+events are detected. If <b>timeout</b> is negative, then the
+timeout is infinite.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_select</b> behaves like the <b>select(2)</b> system
+call.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that <b>pth_select</b> is implemented as a library on
+top of <b>pth_poll</b>, and is considerably less efficient
+than <b>pth_poll</b>. It is recommended that you rewrite any
+code which uses <b>pth_select</b>/<b>select</b> to use
+<b>pth_poll</b>/<b>poll</b> instead.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_accept</title>
+</head>
+<body>
+
+<h1 align=center>pth_accept</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:37 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_accept</b>, <b>pth_connect</b>, <b>pth_read</b>,
+<b>pth_write</b>, <b>pth_sleep</b> and <b>pth_nanosleep</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block. For general I/O
+you will probably wish to wrap up your sockets in I/O handle
+objects, which give you a higher-level buffered interface to
+sockets (see <b>io_fdopen(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_millisleep</b> sleeps for a given number of
+milliseconds.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout</b> is similar to the <b>alarm(2)</b> system
+call: it registers a timeout (in seconds). The thread will
+exit automatically (even in the middle of a system call) if
+the timeout is reached. To reset the timeout, call
+<b>pth_timeout</b> with a timeout of 0.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_select(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_send</title>
+</head>
+<body>
+
+<h1 align=center>pth_send</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:37 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_send</b>, <b>pth_sendto</b>, <b>pth_sendmsg</b>,
+<b>pth_recv</b>, <b>pth_recvfrom</b> and <b>pth_recvmsg</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_read(3)</b>,
+<b>pth_write(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_send</title>
+</head>
+<body>
+
+<h1 align=center>pth_send</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:37 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_send</b>, <b>pth_sendto</b>, <b>pth_sendmsg</b>,
+<b>pth_recv</b>, <b>pth_recvfrom</b> and <b>pth_recvmsg</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_read(3)</b>,
+<b>pth_write(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_send</title>
+</head>
+<body>
+
+<h1 align=center>pth_send</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:37 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_send</b>, <b>pth_sendto</b>, <b>pth_sendmsg</b>,
+<b>pth_recv</b>, <b>pth_recvfrom</b> and <b>pth_recvmsg</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_read(3)</b>,
+<b>pth_write(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_poll</title>
+</head>
+<body>
+
+<h1 align=center>pth_poll</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:38 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_poll, pth_select - pseudothread poll and select system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_poll (pseudothread, struct pollfd *fds, unsigned int n, int timeout);
+int pth_select (pseudothread, int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll</b> behaves like the <b>poll(2)</b> system call.
+It specifies an array <b>n</b> of <b>fds</b> file descriptor
+structures each of which is checked for an I/O event. If
+<b>timeout</b> is greater than or equal to zero, then the
+call will return after this many milliseconds if no I/O
+events are detected. If <b>timeout</b> is negative, then the
+timeout is infinite.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_select</b> behaves like the <b>select(2)</b> system
+call.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that <b>pth_select</b> is implemented as a library on
+top of <b>pth_poll</b>, and is considerably less efficient
+than <b>pth_poll</b>. It is recommended that you rewrite any
+code which uses <b>pth_select</b>/<b>select</b> to use
+<b>pth_poll</b>/<b>poll</b> instead.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_send</title>
+</head>
+<body>
+
+<h1 align=center>pth_send</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:38 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_send</b>, <b>pth_sendto</b>, <b>pth_sendmsg</b>,
+<b>pth_recv</b>, <b>pth_recvfrom</b> and <b>pth_recvmsg</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_read(3)</b>,
+<b>pth_write(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_send</title>
+</head>
+<body>
+
+<h1 align=center>pth_send</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:38 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_send</b>, <b>pth_sendto</b>, <b>pth_sendmsg</b>,
+<b>pth_recv</b>, <b>pth_recvfrom</b> and <b>pth_recvmsg</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_read(3)</b>,
+<b>pth_write(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_send</title>
+</head>
+<body>
+
+<h1 align=center>pth_send</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:38 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_send, pth_sendto, pth_sendmsg, pth_recv, pth_recvfrom, pth_recvmsg - pseudothread network system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_send (pseudothread pth, int s, const void *msg, int len, unsigned int flags);
+int pth_sendto (pseudothread pth, int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+int pth_sendmsg (pseudothread pth, int s, const struct msghdr *msg, unsigned int flags);
+int pth_recv (pseudothread pth, int s, void *buf, int len, unsigned int flags);
+int pth_recvfrom (pseudothread pth, int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+int pth_recvmsg (pseudothread pth, int s, struct msghdr *msg, unsigned int flags);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_send</b>, <b>pth_sendto</b>, <b>pth_sendmsg</b>,
+<b>pth_recv</b>, <b>pth_recvfrom</b> and <b>pth_recvmsg</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_read(3)</b>,
+<b>pth_write(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:39 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:39 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_get_pool</title>
+</head>
+<body>
+
+<h1 align=center>pth_get_pool</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:39 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_get_pool, pth_get_name, pth_get_thread_num, pth_get_run, pth_get_data, pth_get_language, pth_get_tz, pth_get_stack, pth_get_stack_size, pth_get_PC, pth_get_SP, pth_set_name, pth_set_language, pth_set_tz - get and set status of pseudothreads</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pool pth_get_pool (pseudothread pth);
+const char *pth_get_name (pseudothread pth);
+int pth_get_thread_num (pseudothread pth);
+void (*pth_get_run (pseudothread pth)) (void *);
+void *pth_get_data (pseudothread pth);
+const char *pth_get_language (pseudothread pth);
+const char *pth_get_tz (pseudothread pth);
+void *pth_get_stack (pseudothread pth);
+int pth_get_stack_size (pseudothread pth);
+unsigned long pth_get_PC (pseudothread pth);
+unsigned long pth_get_SP (pseudothread pth);
+void pth_set_name (pseudothread pth, const char *name);
+void pth_set_language (pseudothread, const char *lang);
+void pth_set_tz (pseudothread, const char *tz);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_pool</b> returns the pool associated with this
+thread. The thread should use this pool for all allocations,
+ensuring that they are cleaned up correctly when the thread
+is deleted. See <b>new_pool(3)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_name</b> returns the name of the thread. Note
+that this string is normally stored in thread's local pool,
+and therefore the string memory may be unexpected
+deallocated if the thread exits. Callers should take a copy
+of the string in their own pool if they are likely to need
+the string for a long period of time.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_thread_num</b> returns the thread number (roughly
+the equivalent of a process ID).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_run</b> and <b>pth_get_data</b> return the
+original entry point information of the thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the
+<b>LANGUAGE</b> environment variable for this thread. See
+the discussion of <b>pth_set_language</b>
+below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_language</b> returns the value of the <b>TZ</b>
+environment variable for this thread. See the discussion of
+<b>pth_set_tz</b> below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack</b> returns the top of the stack for this
+thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_stack_size</b> returns the maximum size of the
+stack for this thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_PC</b> returns the current program counter (PC)
+for this thread. Obviously it only makes sense to call this
+from another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_get_SP</b> returns the current stack pointer (SP) for
+this thread. Obviously it only makes sense to call this from
+another thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_name</b> changes the name of the thread. The
+string <b>name</b> argument passed must be either statically
+allocated, or allocated in the thread's own pool (the normal
+case), or else allocated in another pool with a longer life
+than the current thread.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_language</b> changes the per-thread
+<b>LANGUAGE</b> environment variable. This is useful when
+serving clients from multiple locales, and using GNU gettext
+for translation.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_set_tz</b> changes the per-thread <b>TZ</b>
+environment variable. This is useful when serving clients
+from multiple timezones, and using <b>localtime</b> and
+related functions.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_accept</title>
+</head>
+<body>
+
+<h1 align=center>pth_accept</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:39 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_accept</b>, <b>pth_connect</b>, <b>pth_read</b>,
+<b>pth_write</b>, <b>pth_sleep</b> and <b>pth_nanosleep</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block. For general I/O
+you will probably wish to wrap up your sockets in I/O handle
+objects, which give you a higher-level buffered interface to
+sockets (see <b>io_fdopen(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_millisleep</b> sleeps for a given number of
+milliseconds.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout</b> is similar to the <b>alarm(2)</b> system
+call: it registers a timeout (in seconds). The thread will
+exit automatically (even in the middle of a system call) if
+the timeout is reached. To reset the timeout, call
+<b>pth_timeout</b> with a timeout of 0.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_select(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_pseudothread</title>
+</head>
+<body>
+
+<h1 align=center>new_pseudothread</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:40 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_pseudothread, pth_start, pseudothread_get_threads, pseudothread_count_threads - lightweight "pseudothreads" library</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name);
+void pth_start (pseudothread pth);
+vector pseudothread_get_threads (pool);
+int pseudothread_count_threads (void);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Pseudothreads are lightweight, cooperatively scheduled
+threads.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_pseudothread</b> creates a new pseudothread. The
+thread only starts running when you call <b>pth_start</b>.
+The <b>pool</b> argument passed is used for all allocations
+within the thread. This pool is automatically deleted when
+the thread exits. The <b>name</b> argument is the name of
+the thread (used in thread listings -- see
+<b>pseudothread_get_threads(3)</b>). You may change the name
+later. The <b>run</b> and <b>data</b> arguments are the
+entry point into the thread. The entry point is called as
+<b>run (data)</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_get_threads</b> returns a list of all the
+currently running pseudothreads. This allows you to
+implement a "process listing" for a program. The
+returned value is a vector of pseudothread structures (not
+pointers). These structures are opaque to you, but you can
+call the pth_get_* functions on the address of each
+one.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pseudothread_count_threads</b> counts the number of
+currently running threads.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_accept</title>
+</head>
+<body>
+
+<h1 align=center>pth_accept</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:40 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_accept</b>, <b>pth_connect</b>, <b>pth_read</b>,
+<b>pth_write</b>, <b>pth_sleep</b> and <b>pth_nanosleep</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block. For general I/O
+you will probably wish to wrap up your sockets in I/O handle
+objects, which give you a higher-level buffered interface to
+sockets (see <b>io_fdopen(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_millisleep</b> sleeps for a given number of
+milliseconds.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout</b> is similar to the <b>alarm(2)</b> system
+call: it registers a timeout (in seconds). The thread will
+exit automatically (even in the middle of a system call) if
+the timeout is reached. To reset the timeout, call
+<b>pth_timeout</b> with a timeout of 0.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_select(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pth_accept</title>
+</head>
+<body>
+
+<h1 align=center>pth_accept</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#SEE ALSO">SEE ALSO</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:40 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pth_accept, pth_connect, pth_read, pth_write, pth_sleep, pth_millisleep, pth_nanosleep, pth_timeout - pseudothread system calls</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_pseudothread.h>
+
+int pth_accept (pseudothread, int, struct sockaddr *, int *);
+int pth_connect (pseudothread, int, struct sockaddr *, int);
+ssize_t pth_read (pseudothread, int, void *, size_t);
+ssize_t pth_write (pseudothread, int, const void *, size_t);
+int pth_sleep (pseudothread, int seconds);
+int pth_millisleep (pseudothread, int millis);
+int pth_nanosleep (pseudothread, const struct timespec *req, struct timespec *rem);
+void pth_timeout (pseudothread, int seconds);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_accept</b>, <b>pth_connect</b>, <b>pth_read</b>,
+<b>pth_write</b>, <b>pth_sleep</b> and <b>pth_nanosleep</b>
+behave just like the corresponding system calls. However
+these calls handle non-blocking sockets and cause the thread
+to sleep on the reactor if it would block. For general I/O
+you will probably wish to wrap up your sockets in I/O handle
+objects, which give you a higher-level buffered interface to
+sockets (see <b>io_fdopen(3)</b>).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_millisleep</b> sleeps for a given number of
+milliseconds.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_timeout</b> is similar to the <b>alarm(2)</b> system
+call: it registers a timeout (in seconds). The thread will
+exit automatically (even in the middle of a system call) if
+the timeout is reached. To reset the timeout, call
+<b>pth_timeout</b> with a timeout of 0.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="SEE ALSO"></a>
+<h2>SEE ALSO</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pth_poll(3)</b>, <b>pth_select(3)</b>.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:40 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:41 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:41 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:41 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:41 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:42 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri May 3 16:10:41 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main - server framework</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server_main.h>
+
+void pthr_server_main (const char *name, void (*processor_fn) (int sock, struct sockaddr_in addr), void (*global_fn) (void), int port, int fork_into_background, int close_io, int chdir_root) __attribute__((noreturn));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>pthr_server_main</b> is a convenient framework function
+for writing servers quickly using pthrlib. It creates a
+listener thread which listens on the given port number and
+throws off new processor threads to handle each incoming
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>name</b> is the name of the server.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>processor_fn</b> is a function which should create a new
+processor thread for each incoming connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>global_fn</b> is called after forking, but before
+starting to accept new connections. It should perform any
+necessary initialization.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>port</b> is the port number to listen on.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>fork_into_background</b> should be set if the process
+should fork into the background (this is normally desirable
+for server processes).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>close_io</b> should be set if the process should close
+file descriptors 0, 1 and 2 before starting up.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>chdir_root</b> should be set if the process should change
+to the root directory before starting up.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-2.2.2</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:42 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:42 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:42 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:43 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:43 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>pthr_server_main_loop</title>
+</head>
+<body>
+
+<h1 align=center>pthr_server_main_loop</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:43 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthr_server_main_loop, pthr_server_default_port, pthr_server_port_option_name, pthr_server_disable_syslog, pthr_server_package_name, pthr_server_disable_fork, pthr_server_disable_chdir, pthr_server_disable_close, pthr_server_chroot, pthr_server_username, pthr_server_stderr_file, pthr_server_startup_fn - Enter server main loop.</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_server.h>
+
+void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+void pthr_server_default_port (int default_port);
+void pthr_server_port_option_name (char port_option_name);
+void pthr_server_disable_syslog (void);
+void pthr_server_package_name (const char *package_name);
+void pthr_server_disable_fork (void);
+void pthr_server_disable_chdir (void);
+void pthr_server_disable_close (void);
+void pthr_server_chroot (const char *root);
+void pthr_server_username (const char *username);
+void pthr_server_stderr_file (const char *pathname);
+void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The function <b>pthr_server_main_loop</b> is a helper
+function which allows you to write very simple servers
+quickly using <b>pthrlib</b>. Normally your <b>main</b>
+should just contain a call to <b>pthr_server_main_loop
+(argc, argv, processor)</b> and you would include a function
+called <b>processor</b> which is called on every
+connection.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The other functions allow you to customise the behaviour of
+<b>pthr_server_main_loop</b>. You should call any of these
+once in your <b>main</b> before calling
+<b>pthr_server_main_loop</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server listens on port 80 and allows you to
+override this on the command line using the <b>-p</b>
+option. To change this, call <b>pthr_server_default_port</b>
+and/or <b>pthr_server_port_option_name</b>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server writes package and version information
+to syslog (although it will not be able to correctly
+determine the package). Use
+<b>pthr_server_disable_syslog</b> to disable syslogging
+entirely, or <b>pthr_server_package_name</b> to change the
+name displayed in the logs.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+By default the server forks into the background, changes to
+the root directory and disconnects entirely from the
+terminal. Use <b>pthr_server_disable_fork</b>,
+<b>pthr_server_disable_chdir</b> and
+<b>pthr_server_disable_close</b> to change these
+respectively.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server chroot after start up, then
+call <b>pthr_server_chroot</b>. This is only possible if the
+server is run as root, otherwise the chroot is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have the server change user and group after
+start up, then call <b>pthr_server_username</b> (the group
+is taken from the password file and <b>initgroups(3)</b> is
+also called). This is only possible if the server is run as
+root, otherwise the change user is silently
+ignored.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+If you wish to have <b>stderr</b> connected to a file (this
+is done after changing user, so make sure the file is
+accessible or owned by the user, not by root), then call
+<b>pthr_server_stderr_file</b>. Note that unless you call
+this function or <b>pthr_server_disable_close</b> then
+<b>stderr</b> is sent to <b>/dev/null</b>!</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The <b>startup_fn</b> is called after all of the above
+operations have completed, but before the listener thread is
+created. You do miscellaneous start-of-day functions from
+here, in particular starting up other global threads. The
+command line arguments are also passed to this
+function.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_rwlock</title>
+</head>
+<body>
+
+<h1 align=center>new_rwlock</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:43 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Multiple reader / single writer locks (rwlocks) do what they
+say. They allow either many readers to access a critical
+section or a single writer (but not both). If instead you
+require simple mutex semantics, then please see
+<pthr_mutex.h>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are automatically released if they are being held
+when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are not ``upgradable''. The implementation is too
+complex to justify that.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_rwlock</b> creates a new rwlock object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_writers_have_priority</b> changes the nature of
+the lock so that writers have priority over readers. If this
+is the case then new readers will not be able to enter a
+critical section if there are writers waiting to enter. [NB:
+This is the default.]</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_readers_have_priority</b> changes the nature of
+the lock so that readers have priority over writers. Note
+that if this is the case then writers are likely to be
+starved if the lock is frequently read.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_read</b> enters the critical section as a
+reader. Any number of readers are allowed to enter a
+critical section at the same time. This function may
+block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_write</b> enters the critical section as a
+writer. Only a single writer is allowed to enter a critical
+section, and then only if there are no readers. This
+function may block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_read</b> is identical to
+<b>rwlock_enter_read</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_write</b> is identical to
+<b>rwlock_enter_write</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_leave</b> leaves the critical
+section.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>rwlock_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_rwlock</title>
+</head>
+<body>
+
+<h1 align=center>new_rwlock</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:44 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Multiple reader / single writer locks (rwlocks) do what they
+say. They allow either many readers to access a critical
+section or a single writer (but not both). If instead you
+require simple mutex semantics, then please see
+<pthr_mutex.h>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are automatically released if they are being held
+when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are not ``upgradable''. The implementation is too
+complex to justify that.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_rwlock</b> creates a new rwlock object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_writers_have_priority</b> changes the nature of
+the lock so that writers have priority over readers. If this
+is the case then new readers will not be able to enter a
+critical section if there are writers waiting to enter. [NB:
+This is the default.]</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_readers_have_priority</b> changes the nature of
+the lock so that readers have priority over writers. Note
+that if this is the case then writers are likely to be
+starved if the lock is frequently read.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_read</b> enters the critical section as a
+reader. Any number of readers are allowed to enter a
+critical section at the same time. This function may
+block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_write</b> enters the critical section as a
+writer. Only a single writer is allowed to enter a critical
+section, and then only if there are no readers. This
+function may block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_read</b> is identical to
+<b>rwlock_enter_read</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_write</b> is identical to
+<b>rwlock_enter_write</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_leave</b> leaves the critical
+section.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>rwlock_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_rwlock</title>
+</head>
+<body>
+
+<h1 align=center>new_rwlock</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:44 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Multiple reader / single writer locks (rwlocks) do what they
+say. They allow either many readers to access a critical
+section or a single writer (but not both). If instead you
+require simple mutex semantics, then please see
+<pthr_mutex.h>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are automatically released if they are being held
+when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are not ``upgradable''. The implementation is too
+complex to justify that.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_rwlock</b> creates a new rwlock object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_writers_have_priority</b> changes the nature of
+the lock so that writers have priority over readers. If this
+is the case then new readers will not be able to enter a
+critical section if there are writers waiting to enter. [NB:
+This is the default.]</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_readers_have_priority</b> changes the nature of
+the lock so that readers have priority over writers. Note
+that if this is the case then writers are likely to be
+starved if the lock is frequently read.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_read</b> enters the critical section as a
+reader. Any number of readers are allowed to enter a
+critical section at the same time. This function may
+block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_write</b> enters the critical section as a
+writer. Only a single writer is allowed to enter a critical
+section, and then only if there are no readers. This
+function may block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_read</b> is identical to
+<b>rwlock_enter_read</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_write</b> is identical to
+<b>rwlock_enter_write</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_leave</b> leaves the critical
+section.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>rwlock_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_rwlock</title>
+</head>
+<body>
+
+<h1 align=center>new_rwlock</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:44 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Multiple reader / single writer locks (rwlocks) do what they
+say. They allow either many readers to access a critical
+section or a single writer (but not both). If instead you
+require simple mutex semantics, then please see
+<pthr_mutex.h>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are automatically released if they are being held
+when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are not ``upgradable''. The implementation is too
+complex to justify that.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_rwlock</b> creates a new rwlock object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_writers_have_priority</b> changes the nature of
+the lock so that writers have priority over readers. If this
+is the case then new readers will not be able to enter a
+critical section if there are writers waiting to enter. [NB:
+This is the default.]</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_readers_have_priority</b> changes the nature of
+the lock so that readers have priority over writers. Note
+that if this is the case then writers are likely to be
+starved if the lock is frequently read.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_read</b> enters the critical section as a
+reader. Any number of readers are allowed to enter a
+critical section at the same time. This function may
+block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_write</b> enters the critical section as a
+writer. Only a single writer is allowed to enter a critical
+section, and then only if there are no readers. This
+function may block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_read</b> is identical to
+<b>rwlock_enter_read</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_write</b> is identical to
+<b>rwlock_enter_write</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_leave</b> leaves the critical
+section.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>rwlock_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_rwlock</title>
+</head>
+<body>
+
+<h1 align=center>new_rwlock</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:44 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Multiple reader / single writer locks (rwlocks) do what they
+say. They allow either many readers to access a critical
+section or a single writer (but not both). If instead you
+require simple mutex semantics, then please see
+<pthr_mutex.h>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are automatically released if they are being held
+when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are not ``upgradable''. The implementation is too
+complex to justify that.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_rwlock</b> creates a new rwlock object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_writers_have_priority</b> changes the nature of
+the lock so that writers have priority over readers. If this
+is the case then new readers will not be able to enter a
+critical section if there are writers waiting to enter. [NB:
+This is the default.]</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_readers_have_priority</b> changes the nature of
+the lock so that readers have priority over writers. Note
+that if this is the case then writers are likely to be
+starved if the lock is frequently read.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_read</b> enters the critical section as a
+reader. Any number of readers are allowed to enter a
+critical section at the same time. This function may
+block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_write</b> enters the critical section as a
+writer. Only a single writer is allowed to enter a critical
+section, and then only if there are no readers. This
+function may block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_read</b> is identical to
+<b>rwlock_enter_read</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_write</b> is identical to
+<b>rwlock_enter_write</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_leave</b> leaves the critical
+section.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>rwlock_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_rwlock</title>
+</head>
+<body>
+
+<h1 align=center>new_rwlock</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:45 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Multiple reader / single writer locks (rwlocks) do what they
+say. They allow either many readers to access a critical
+section or a single writer (but not both). If instead you
+require simple mutex semantics, then please see
+<pthr_mutex.h>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are automatically released if they are being held
+when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are not ``upgradable''. The implementation is too
+complex to justify that.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_rwlock</b> creates a new rwlock object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_writers_have_priority</b> changes the nature of
+the lock so that writers have priority over readers. If this
+is the case then new readers will not be able to enter a
+critical section if there are writers waiting to enter. [NB:
+This is the default.]</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_readers_have_priority</b> changes the nature of
+the lock so that readers have priority over writers. Note
+that if this is the case then writers are likely to be
+starved if the lock is frequently read.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_read</b> enters the critical section as a
+reader. Any number of readers are allowed to enter a
+critical section at the same time. This function may
+block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_write</b> enters the critical section as a
+writer. Only a single writer is allowed to enter a critical
+section, and then only if there are no readers. This
+function may block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_read</b> is identical to
+<b>rwlock_enter_read</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_write</b> is identical to
+<b>rwlock_enter_write</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_leave</b> leaves the critical
+section.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>rwlock_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_rwlock</title>
+</head>
+<body>
+
+<h1 align=center>new_rwlock</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#BUGS">BUGS</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:45 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_rwlock, rwlock_writers_have_priority, rwlock_readers_have_priority, rwlock_enter_read, rwlock_enter_write, rwlock_try_enter_read, rwlock_try_enter_write, rwlock_leave - multiple reader / single writer locks (rwlocks)</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_rwlock.h>
+
+rwlock new_rwlock (pool);
+void rwlock_writers_have_priority (rwlock);
+void rwlock_readers_have_priority (rwlock);
+void rwlock_enter_read (rwlock, pseudothread);
+void rwlock_enter_write (rwlock, pseudothread);
+int rwlock_try_enter_read (rwlock, pseudothread);
+int rwlock_try_enter_write (rwlock, pseudothread);
+void rwlock_leave (rwlock, pseudothread);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Multiple reader / single writer locks (rwlocks) do what they
+say. They allow either many readers to access a critical
+section or a single writer (but not both). If instead you
+require simple mutex semantics, then please see
+<pthr_mutex.h>.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are automatically released if they are being held
+when the thread exits.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+RWlocks are not ``upgradable''. The implementation is too
+complex to justify that.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note that there are possible deadlocks when using locks. To
+avoid deadlocks, always ensure every thread acquires locks
+in the same order.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_rwlock</b> creates a new rwlock object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_writers_have_priority</b> changes the nature of
+the lock so that writers have priority over readers. If this
+is the case then new readers will not be able to enter a
+critical section if there are writers waiting to enter. [NB:
+This is the default.]</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_readers_have_priority</b> changes the nature of
+the lock so that readers have priority over writers. Note
+that if this is the case then writers are likely to be
+starved if the lock is frequently read.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_read</b> enters the critical section as a
+reader. Any number of readers are allowed to enter a
+critical section at the same time. This function may
+block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_enter_write</b> enters the critical section as a
+writer. Only a single writer is allowed to enter a critical
+section, and then only if there are no readers. This
+function may block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_read</b> is identical to
+<b>rwlock_enter_read</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_try_enter_write</b> is identical to
+<b>rwlock_enter_write</b>, but it does not block. It returns
+true if the lock was successfully acquired, or false if the
+operation would block.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>rwlock_leave</b> leaves the critical
+section.</td></table>
+<a name="BUGS"></a>
+<h2>BUGS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+A common mistake is to accidentally call <b>rwlock_leave</b>
+when you are not holding the lock. This generally causes the
+library to crash.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_wait_queue</title>
+</head>
+<body>
+
+<h1 align=center>new_wait_queue</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#HISTORY">HISTORY</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:45 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_wait_queue</b> creates a wait queue
+object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up</b> wakes up all the threads which are
+currently sleeping on the wait queue. Note that this
+function does not block, and because pseudothreads are
+non-preemptive, none of the sleeping threads will actually
+begin running until at least the current thread blocks
+somewhere else.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up_one</b> wakes up just the first thread at the
+head of the wait queue (the one which has been waiting the
+longest).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_sleep_on</b> sends the current thread to sleep on the
+wait queue. This call blocks (obviously).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_nr_sleepers</b> returns the number of threads which
+are currently asleep on the wait queue.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Please read the HISTORY section below for some background
+into how wait queues are implemented. This may help if you
+find there are tricky race conditions in your
+code.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="HISTORY"></a>
+<h2>HISTORY</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Originally, wait queues were implemented using underlying
+Unix pipes. This worked (to some extent) but the overhead of
+requiring one pipe (ie. one inode, two file descriptors) per
+wait queue made this implementation unacceptably
+heavyweight.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are now implemented using a simple hack in the
+reactor which will be described below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are subtle. Consider this example: Threads 1, 2
+and 3 are sleeping on a wait queue. Now thread 4 wakes up
+the queue. You would expect (probably) threads 1, 2 and 3 to
+each be woken up and allowed to start running. However,
+since this is a cooperatively multitasking environment, it
+may happen that thread 1 wakes up first, does some work and
+then goes back to sleep on the wait queue, all before
+threads 2 and 3 have woken up. With a naive implementation
+of wait queues, thread 4 might end up waking up thread 1
+*again* (and even again after that), never waking up threads
+2 and 3 and ultimately starving those threads.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To avoid this situation, we might consider two possible
+alternatives: either when thread 1 goes back to sleep, it
+goes to sleep on a 'different' queue, or else thread 4 might
+take a copy of the wait queue and delete the queue before it
+wakes any of the threads up.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Another nasty situation which might arise in real life is
+this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes
+them up. Thread 1, while processing its work, happens also
+to wake up the same wait queue. What should happen to this
+second wake-up event? Should it be ignored? Should it wake
+up threads 2 and 3? Should it wake up any other threads
+which happen to have gone to sleep on the queue after 1, 2
+and 3? Or perhaps some combination of these?</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The solution that we have come up with is as follows. A wait
+queue consists of a simple list of threads which are
+sleeping on it. When a thread wishes to sleep on the wait
+queue, it is added to this list, and it switches back into
+the reactor context. When a thread wishes to wake up all
+sleepers, it: (a) copies the list of sleeping pseudothreads
+into its own private space (b) clears the list of sleeping
+pseudothreads (c) registers a prepoll handler to run which
+will wake up (ie. switch into the context of) each of these
+threads in turn (d) continues to run to completion. A thread
+which wishes to wake just one pseudothread works similarly
+except that it only copies (and removes) a single item off
+the list.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note various invariant conditions: A thread cannot be
+entered on the wait queue sleeping list more than once
+(because it cannot call sleep_on when it is already
+sleeping). For similar reasons, a thread cannot be entered
+on any of the multiple lists at the same time. This implies
+that a thread cannot be woken up multiple
+times.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The reader should satisfy themselves that this algorithm is
+free of races, and solves all the problems outlined above.
+In addition, it has the desirable property that wake_up*
+never sleeps.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_wait_queue</title>
+</head>
+<body>
+
+<h1 align=center>new_wait_queue</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#HISTORY">HISTORY</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:45 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_wait_queue</b> creates a wait queue
+object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up</b> wakes up all the threads which are
+currently sleeping on the wait queue. Note that this
+function does not block, and because pseudothreads are
+non-preemptive, none of the sleeping threads will actually
+begin running until at least the current thread blocks
+somewhere else.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up_one</b> wakes up just the first thread at the
+head of the wait queue (the one which has been waiting the
+longest).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_sleep_on</b> sends the current thread to sleep on the
+wait queue. This call blocks (obviously).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_nr_sleepers</b> returns the number of threads which
+are currently asleep on the wait queue.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Please read the HISTORY section below for some background
+into how wait queues are implemented. This may help if you
+find there are tricky race conditions in your
+code.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="HISTORY"></a>
+<h2>HISTORY</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Originally, wait queues were implemented using underlying
+Unix pipes. This worked (to some extent) but the overhead of
+requiring one pipe (ie. one inode, two file descriptors) per
+wait queue made this implementation unacceptably
+heavyweight.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are now implemented using a simple hack in the
+reactor which will be described below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are subtle. Consider this example: Threads 1, 2
+and 3 are sleeping on a wait queue. Now thread 4 wakes up
+the queue. You would expect (probably) threads 1, 2 and 3 to
+each be woken up and allowed to start running. However,
+since this is a cooperatively multitasking environment, it
+may happen that thread 1 wakes up first, does some work and
+then goes back to sleep on the wait queue, all before
+threads 2 and 3 have woken up. With a naive implementation
+of wait queues, thread 4 might end up waking up thread 1
+*again* (and even again after that), never waking up threads
+2 and 3 and ultimately starving those threads.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To avoid this situation, we might consider two possible
+alternatives: either when thread 1 goes back to sleep, it
+goes to sleep on a 'different' queue, or else thread 4 might
+take a copy of the wait queue and delete the queue before it
+wakes any of the threads up.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Another nasty situation which might arise in real life is
+this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes
+them up. Thread 1, while processing its work, happens also
+to wake up the same wait queue. What should happen to this
+second wake-up event? Should it be ignored? Should it wake
+up threads 2 and 3? Should it wake up any other threads
+which happen to have gone to sleep on the queue after 1, 2
+and 3? Or perhaps some combination of these?</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The solution that we have come up with is as follows. A wait
+queue consists of a simple list of threads which are
+sleeping on it. When a thread wishes to sleep on the wait
+queue, it is added to this list, and it switches back into
+the reactor context. When a thread wishes to wake up all
+sleepers, it: (a) copies the list of sleeping pseudothreads
+into its own private space (b) clears the list of sleeping
+pseudothreads (c) registers a prepoll handler to run which
+will wake up (ie. switch into the context of) each of these
+threads in turn (d) continues to run to completion. A thread
+which wishes to wake just one pseudothread works similarly
+except that it only copies (and removes) a single item off
+the list.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note various invariant conditions: A thread cannot be
+entered on the wait queue sleeping list more than once
+(because it cannot call sleep_on when it is already
+sleeping). For similar reasons, a thread cannot be entered
+on any of the multiple lists at the same time. This implies
+that a thread cannot be woken up multiple
+times.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The reader should satisfy themselves that this algorithm is
+free of races, and solves all the problems outlined above.
+In addition, it has the desirable property that wake_up*
+never sleeps.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_wait_queue</title>
+</head>
+<body>
+
+<h1 align=center>new_wait_queue</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#HISTORY">HISTORY</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:46 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_wait_queue</b> creates a wait queue
+object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up</b> wakes up all the threads which are
+currently sleeping on the wait queue. Note that this
+function does not block, and because pseudothreads are
+non-preemptive, none of the sleeping threads will actually
+begin running until at least the current thread blocks
+somewhere else.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up_one</b> wakes up just the first thread at the
+head of the wait queue (the one which has been waiting the
+longest).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_sleep_on</b> sends the current thread to sleep on the
+wait queue. This call blocks (obviously).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_nr_sleepers</b> returns the number of threads which
+are currently asleep on the wait queue.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Please read the HISTORY section below for some background
+into how wait queues are implemented. This may help if you
+find there are tricky race conditions in your
+code.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="HISTORY"></a>
+<h2>HISTORY</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Originally, wait queues were implemented using underlying
+Unix pipes. This worked (to some extent) but the overhead of
+requiring one pipe (ie. one inode, two file descriptors) per
+wait queue made this implementation unacceptably
+heavyweight.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are now implemented using a simple hack in the
+reactor which will be described below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are subtle. Consider this example: Threads 1, 2
+and 3 are sleeping on a wait queue. Now thread 4 wakes up
+the queue. You would expect (probably) threads 1, 2 and 3 to
+each be woken up and allowed to start running. However,
+since this is a cooperatively multitasking environment, it
+may happen that thread 1 wakes up first, does some work and
+then goes back to sleep on the wait queue, all before
+threads 2 and 3 have woken up. With a naive implementation
+of wait queues, thread 4 might end up waking up thread 1
+*again* (and even again after that), never waking up threads
+2 and 3 and ultimately starving those threads.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To avoid this situation, we might consider two possible
+alternatives: either when thread 1 goes back to sleep, it
+goes to sleep on a 'different' queue, or else thread 4 might
+take a copy of the wait queue and delete the queue before it
+wakes any of the threads up.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Another nasty situation which might arise in real life is
+this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes
+them up. Thread 1, while processing its work, happens also
+to wake up the same wait queue. What should happen to this
+second wake-up event? Should it be ignored? Should it wake
+up threads 2 and 3? Should it wake up any other threads
+which happen to have gone to sleep on the queue after 1, 2
+and 3? Or perhaps some combination of these?</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The solution that we have come up with is as follows. A wait
+queue consists of a simple list of threads which are
+sleeping on it. When a thread wishes to sleep on the wait
+queue, it is added to this list, and it switches back into
+the reactor context. When a thread wishes to wake up all
+sleepers, it: (a) copies the list of sleeping pseudothreads
+into its own private space (b) clears the list of sleeping
+pseudothreads (c) registers a prepoll handler to run which
+will wake up (ie. switch into the context of) each of these
+threads in turn (d) continues to run to completion. A thread
+which wishes to wake just one pseudothread works similarly
+except that it only copies (and removes) a single item off
+the list.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note various invariant conditions: A thread cannot be
+entered on the wait queue sleeping list more than once
+(because it cannot call sleep_on when it is already
+sleeping). For similar reasons, a thread cannot be entered
+on any of the multiple lists at the same time. This implies
+that a thread cannot be woken up multiple
+times.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The reader should satisfy themselves that this algorithm is
+free of races, and solves all the problems outlined above.
+In addition, it has the desirable property that wake_up*
+never sleeps.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+<html>
+<head>
+<meta name="generator" content="groff -Thtml, see www.gnu.org">
+<meta name="Content-Style" content="text/css">
+<title>new_wait_queue</title>
+</head>
+<body>
+
+<h1 align=center>new_wait_queue</h1>
+<a href="#NAME">NAME</a><br>
+<a href="#SYNOPSIS">SYNOPSIS</a><br>
+<a href="#DESCRIPTION">DESCRIPTION</a><br>
+<a href="#AUTHOR">AUTHOR</a><br>
+<a href="#LICENSE">LICENSE</a><br>
+<a href="#VERSION">VERSION</a><br>
+<a href="#HISTORY">HISTORY</a><br>
+
+<hr>
+<!-- Creator : groff version 1.17.2 -->
+<!-- CreationDate: Fri Aug 30 16:16:46 2002 -->
+<a name="NAME"></a>
+<h2>NAME</h2>
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+new_wait_queue, wq_wake_up, wq_wake_up_one, wq_sleep_on, wq_nr_sleepers - wait queues</td></table>
+<a name="SYNOPSIS"></a>
+<h2>SYNOPSIS</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<pre><b>#include <pthr_wait_queue.h>
+
+wait_queue new_wait_queue (pool);
+void wq_wake_up (wait_queue);
+void wq_wake_up_one (wait_queue);
+void wq_sleep_on (wait_queue, pseudothread);
+int wq_nr_sleepers (wait_queue);
+</b></pre></td></table>
+<a name="DESCRIPTION"></a>
+<h2>DESCRIPTION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>new_wait_queue</b> creates a wait queue
+object.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up</b> wakes up all the threads which are
+currently sleeping on the wait queue. Note that this
+function does not block, and because pseudothreads are
+non-preemptive, none of the sleeping threads will actually
+begin running until at least the current thread blocks
+somewhere else.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_wake_up_one</b> wakes up just the first thread at the
+head of the wait queue (the one which has been waiting the
+longest).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_sleep_on</b> sends the current thread to sleep on the
+wait queue. This call blocks (obviously).</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+<b>wq_nr_sleepers</b> returns the number of threads which
+are currently asleep on the wait queue.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Please read the HISTORY section below for some background
+into how wait queues are implemented. This may help if you
+find there are tricky race conditions in your
+code.</td></table>
+<a name="AUTHOR"></a>
+<h2>AUTHOR</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Richard Jones <rich@annexia.org></td></table>
+<a name="LICENSE"></a>
+<h2>LICENSE</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+GNU LGPL (see http://www.gnu.org/)</td></table>
+<a name="VERSION"></a>
+<h2>VERSION</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+pthrlib-3.0.3</td></table>
+<a name="HISTORY"></a>
+<h2>HISTORY</h2>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Originally, wait queues were implemented using underlying
+Unix pipes. This worked (to some extent) but the overhead of
+requiring one pipe (ie. one inode, two file descriptors) per
+wait queue made this implementation unacceptably
+heavyweight.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are now implemented using a simple hack in the
+reactor which will be described below.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Wait queues are subtle. Consider this example: Threads 1, 2
+and 3 are sleeping on a wait queue. Now thread 4 wakes up
+the queue. You would expect (probably) threads 1, 2 and 3 to
+each be woken up and allowed to start running. However,
+since this is a cooperatively multitasking environment, it
+may happen that thread 1 wakes up first, does some work and
+then goes back to sleep on the wait queue, all before
+threads 2 and 3 have woken up. With a naive implementation
+of wait queues, thread 4 might end up waking up thread 1
+*again* (and even again after that), never waking up threads
+2 and 3 and ultimately starving those threads.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+To avoid this situation, we might consider two possible
+alternatives: either when thread 1 goes back to sleep, it
+goes to sleep on a 'different' queue, or else thread 4 might
+take a copy of the wait queue and delete the queue before it
+wakes any of the threads up.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Another nasty situation which might arise in real life is
+this: Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes
+them up. Thread 1, while processing its work, happens also
+to wake up the same wait queue. What should happen to this
+second wake-up event? Should it be ignored? Should it wake
+up threads 2 and 3? Should it wake up any other threads
+which happen to have gone to sleep on the queue after 1, 2
+and 3? Or perhaps some combination of these?</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The solution that we have come up with is as follows. A wait
+queue consists of a simple list of threads which are
+sleeping on it. When a thread wishes to sleep on the wait
+queue, it is added to this list, and it switches back into
+the reactor context. When a thread wishes to wake up all
+sleepers, it: (a) copies the list of sleeping pseudothreads
+into its own private space (b) clears the list of sleeping
+pseudothreads (c) registers a prepoll handler to run which
+will wake up (ie. switch into the context of) each of these
+threads in turn (d) continues to run to completion. A thread
+which wishes to wake just one pseudothread works similarly
+except that it only copies (and removes) a single item off
+the list.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+Note various invariant conditions: A thread cannot be
+entered on the wait queue sleeping list more than once
+(because it cannot call sleep_on when it is already
+sleeping). For similar reasons, a thread cannot be entered
+on any of the multiple lists at the same time. This implies
+that a thread cannot be woken up multiple
+times.</td></table>
+
+<table width="100%" border=0 rules="none" frame="void"
+ cols="2" cellspacing="0" cellpadding="0">
+<tr valign="top" align="left">
+<td width="10%"></td><td width="90%">
+The reader should satisfy themselves that this algorithm is
+free of races, and solves all the problems outlined above.
+In addition, it has the desirable property that wake_up*
+never sleeps.</td></table>
+<hr>
+</body>
+</html>
--- /dev/null
+Examples
+--------
+
+pthr_eg1_*
+
+ This is a very basic example webserver which simply
+ prints out the query string and a few other details
+ like request headers when you connect to it.
+
+ Run it as:
+
+ ./pthr_eg1_echo -p 8002
+
+ and then try connecting to it (eg. with lynx, netscape, etc.):
+
+ http://localhost:8002/something
+
+pthr_eg2_*
+
+ This is a slightly more advanced example. This webserver
+ will serve flat files (only). It can be used for benchmarking,
+ profiling/optimizing the library, or even as a very simple
+ production webserver.
+
+ It needs to be started as ``root'' because it chroots and
+ changes its user ID for security reasons.
+
+ All configuration is by way of command line arguments. Try
+ typing ``./pthr_eg2_server --help'' for a complete list.
+
+ This webserver is capable of exploiting the full power
+ of SMP machines.
+
+ To start it (as root):
+
+ ./pthr_eg2_server -p 8003 -r /home/httpd/html -u nobody
+
+ If you have more than one processor, then you need to
+ use the ``-n'' argument to tell it how many processors
+ you have.
+
+ Tip for benchmarking: use a lot of client machines
+ connected through a full-duplex ethernet switch to
+ the server. On each client machine, run a recursive
+ ``wget'' to download a large number of files, eg:
+
+ wget -r http://server:8003/
+
+rws
+
+ RWS is a full-featured webserver built on top of pthrlib.
+ It is distributed separately. You can get it from
+ http://www.annexia.org/freeware/rws/
--- /dev/null
+/* Pseudothread echo example.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_eg1_echo.c,v 1.6 2002/12/01 14:57:36 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include <pool.h>
+
+#include "src/pthr_pseudothread.h"
+#include "src/pthr_iolib.h"
+#include "src/pthr_http.h"
+#include "src/pthr_cgi.h"
+#include "pthr_eg1_echo.h"
+
+struct eg1_echo_processor
+{
+ /* Pseudothread handle. */
+ pseudothread pth;
+
+ /* Socket. */
+ int sock;
+};
+
+static void run (void *vp);
+
+eg1_echo_processor
+new_eg1_echo_processor (int sock)
+{
+ pool pool;
+ eg1_echo_processor p;
+
+ pool = new_pool ();
+ p = pmalloc (pool, sizeof *p);
+
+ p->sock = sock;
+ p->pth = new_pseudothread (pool, run, p, "eg1_echo_processor");
+
+ pth_start (p->pth);
+
+ return p;
+}
+
+static void
+run (void *vp)
+{
+ eg1_echo_processor p = (eg1_echo_processor) vp;
+ int close = 0;
+ http_request http_request;
+ cgi cgi;
+ http_response http_response;
+ pool pool = pth_get_pool (p->pth);
+ io_handle io;
+ int i;
+
+ io = io_fdopen (p->sock);
+
+ /* Sit in a loop reading HTTP requests. */
+ while (!close)
+ {
+ vector headers, params;
+ struct http_header header;
+ const char *name, *value;
+
+ /* ----- HTTP request ----- */
+ http_request = new_http_request (pool, io);
+ if (http_request == 0) /* Normal end of file. */
+ break;
+
+ cgi = new_cgi (pool, http_request, io);
+ if (cgi == 0) /* XXX Should send an error here. */
+ break;
+
+ /* ----- HTTP response ----- */
+ http_response = new_http_response (pool, http_request,
+ io,
+ 200, "OK");
+ http_response_send_header (http_response,
+ "Content-Type", "text/plain");
+ close = http_response_end_headers (http_response);
+
+ if (!http_request_is_HEAD (http_request))
+ {
+ io_fprintf (io, "Hello. This is your server.\r\n\r\n");
+ io_fprintf (io, "Your browser sent the following headers:\r\n");
+
+ headers = http_request_get_headers (http_request);
+ for (i = 0; i < vector_size (headers); ++i)
+ {
+ vector_get (headers, i, header);
+ io_fprintf (io, "\t%s: %s\r\n", header.key, header.value);
+ }
+
+ io_fprintf (io, "----- end of headers -----\r\n");
+
+ io_fprintf (io, "The URL was: %s\r\n",
+ http_request_get_url (http_request));
+ io_fprintf (io, "The path component was: %s\r\n",
+ http_request_path (http_request));
+ io_fprintf (io, "The query string was: %s\r\n",
+ http_request_query_string (http_request));
+ io_fprintf (io, "The query arguments were:\r\n");
+
+ params = cgi_params (cgi);
+ for (i = 0; i < vector_size (params); ++i)
+ {
+ vector_get (params, i, name);
+ value = cgi_param (cgi, name);
+ io_fprintf (io, "\t%s=%s\r\n", name, value);
+ }
+
+ io_fprintf (io, "----- end of parameters -----\r\n");
+ }
+ }
+
+ io_fclose (io);
+
+ pth_exit ();
+}
--- /dev/null
+/* Pseudothread echo example.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_eg1_echo.h,v 1.2 2002/08/21 10:42:15 rich Exp $
+ */
+
+#ifndef PTHR_EG1_ECHO_H
+#define PTHR_EG1_ECHO_H
+
+#include <pool.h>
+
+#include "src/pthr_pseudothread.h"
+
+struct eg1_echo_processor;
+typedef struct eg1_echo_processor *eg1_echo_processor;
+
+extern eg1_echo_processor new_eg1_echo_processor (int sock);
+
+#endif /* PTHR_EG1_ECHO_H */
--- /dev/null
+/* Pseudothread echo example.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_eg1_echo_main.c,v 1.2 2002/08/21 10:42:15 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "src/pthr_server.h"
+#include "pthr_eg1_echo.h"
+
+static void start_processor (int sock, void *);
+
+int
+main (int argc, char *argv[])
+{
+ /* Start up the server. */
+ pthr_server_main_loop (argc, argv, start_processor);
+
+ exit (0);
+}
+
+static void
+start_processor (int sock, void *data)
+{
+ (void) new_eg1_echo_processor (sock);
+}
--- /dev/null
+/* Pseudothread server example.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_eg2_server.c,v 1.7 2003/02/05 22:13:32 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_SYSLIMITS_H
+#include <sys/syslimits.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include "src/pthr_pseudothread.h"
+#include "src/pthr_iolib.h"
+#include "src/pthr_http.h"
+#include "src/pthr_cgi.h"
+#include "pthr_eg2_server.h"
+
+struct eg2_server_processor
+{
+ /* Pseudothread handle. */
+ pseudothread pth;
+
+ /* Socket. */
+ int sock;
+
+ /* Pool for memory allocations. */
+ struct pool *pool;
+
+ /* HTTP request. */
+ http_request http_request;
+
+ /* IO handle. */
+ io_handle io;
+};
+
+static int file_not_found_error (eg2_server_processor p);
+static int moved_permanently (eg2_server_processor p, const char *);
+static int serve_directory (eg2_server_processor p, const char *, const struct stat *);
+static int serve_file (eg2_server_processor p, const char *, const struct stat *);
+static void run (void *vp);
+
+eg2_server_processor
+new_eg2_server_processor (int sock)
+{
+ pool pool;
+ eg2_server_processor p;
+
+ pool = new_pool ();
+ p = pmalloc (pool, sizeof *p);
+
+ p->pool = pool;
+ p->sock = sock;
+ p->pth = new_pseudothread (pool, run, p, "eg2_server_processor");
+
+ pth_start (p->pth);
+
+ return p;
+}
+
+static void
+run (void *vp)
+{
+ eg2_server_processor p = (eg2_server_processor) vp;
+ int close = 0;
+ const char *path;
+ struct stat statbuf;
+
+ p->io = io_fdopen (p->sock);
+
+ /* Sit in a loop reading HTTP requests. */
+ while (!close)
+ {
+ /* ----- HTTP request ----- */
+ p->http_request = new_http_request (p->pool, p->io);
+ if (p->http_request == 0) /* Normal end of file. */
+ break;
+
+ /* Get the path and locate the file. */
+ path = http_request_path (p->http_request);
+ if (stat (path, &statbuf) == -1)
+ {
+ close = file_not_found_error (p);
+ continue;
+ }
+
+ /* File or directory? */
+ if (S_ISDIR (statbuf.st_mode))
+ {
+ close = serve_directory (p, path, &statbuf);
+ continue;
+ }
+ else if (S_ISREG (statbuf.st_mode))
+ {
+ close = serve_file (p, path, &statbuf);
+ continue;
+ }
+ else
+ {
+ close = file_not_found_error (p);
+ continue;
+ }
+ }
+
+ io_fclose (p->io);
+
+ pth_exit ();
+}
+
+static int
+file_not_found_error (eg2_server_processor p)
+{
+ http_response http_response;
+ int close;
+
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 404, "File or directory not found");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/html",
+ NO_CACHE_HEADERS,
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return close;
+
+ io_fprintf (p->io,
+ "<html><head><title>File or directory not found</title></head>" CRLF
+ "<body bgcolor=\"#ffffff\">" CRLF
+ "<h1>404 File or directory not found</h1>" CRLF
+ "The file you requested was not found on this server." CRLF
+ "</body></html>" CRLF);
+
+ return close;
+}
+
+int
+moved_permanently (eg2_server_processor p, const char *location)
+{
+ http_response http_response;
+ int close;
+
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 301, "Moved permanently");
+ http_response_send_headers (http_response,
+ /* Content length. */
+ "Content-Length", "0",
+ /* Location. */
+ "Location", location,
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return close;
+
+ return close;
+}
+
+static int
+serve_directory (eg2_server_processor p, const char *path,
+ const struct stat *statbuf)
+{
+ http_response http_response;
+ int close;
+ DIR *dir;
+ struct dirent *d;
+
+#ifndef NAME_MAX
+ /* Solaris defines NAME_MAX on a per-filesystem basis.
+ * See: http://lists.spine.cx/archives/everybuddy/2002-May/001419.html
+ */
+ long NAME_MAX = pathconf (path, _PC_NAME_MAX);
+#endif
+
+ /* If the path doesn't end with a "/", then we need to send
+ * a redirect back to the client so it refetches the page
+ * with "/" appended.
+ */
+ if (path[strlen (path)-1] != '/')
+ {
+ char *location = psprintf (p->pool, "%s/", path);
+ return moved_permanently (p, location);
+ }
+
+ dir = opendir (path);
+ if (dir == 0)
+ return file_not_found_error (p);
+
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/html",
+ NO_CACHE_HEADERS,
+ /* End of headers. */
+ NULL);
+ close = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return close;
+
+ io_fprintf (p->io,
+ "<html><head><title>Directory: %s</title></head>" CRLF
+ "<body bgcolor=\"#ffffff\">" CRLF
+ "<h1>Directory: %s</h1>" CRLF
+ "<table>" CRLF
+ "<tr><td></td><td></td>"
+ "<td><a href=\"..\">Parent directory</a></td></tr>" CRLF,
+ path, path);
+
+ while ((d = readdir (dir)) != 0)
+ {
+ if (d->d_name[0] != '.') /* Ignore hidden files. */
+ {
+ const char *filename;
+ struct stat fstatbuf;
+
+ /* Generate the full pathname to this file. */
+ filename = psprintf (p->pool, "%s/%s", path, d->d_name);
+
+ /* Stat the file to find out what it is. */
+ if (lstat (filename, &fstatbuf) == 0)
+ {
+ const char *type;
+ int size;
+
+ if (S_ISDIR (fstatbuf.st_mode))
+ type = "dir";
+ else if (S_ISREG (fstatbuf.st_mode))
+ type = "file";
+ else if (S_ISLNK (fstatbuf.st_mode))
+ type = "link";
+ else
+ type = "special";
+
+ size = fstatbuf.st_size;
+
+ /* Print the details. */
+ io_fprintf (p->io,
+ "<tr><td>[ %s ]</td><td align=right>%d</td>"
+ "<td><a href=\"%s%s\">%s</a>",
+ type, size,
+ d->d_name,
+ S_ISDIR (fstatbuf.st_mode) ? "/" : "",
+ d->d_name);
+
+ if (S_ISLNK (fstatbuf.st_mode))
+ {
+ char link[NAME_MAX+1];
+ int r;
+
+ r = readlink (filename, link, NAME_MAX);
+ if (r >= 0) link[r] = '\0';
+ else strcpy (link, "unknown");
+
+ io_fprintf (p->io, " -> %s", link);
+ }
+
+ io_fputs ("</td></tr>" CRLF, p->io);
+ }
+ }
+ }
+
+ io_fprintf (p->io,
+ "</table></body></html>" CRLF);
+
+ return close;
+}
+
+static int
+serve_file (eg2_server_processor p, const char *path,
+ const struct stat *statbuf)
+{
+ http_response http_response;
+ const int n = 4096;
+ char *buffer = alloca (n);
+ int cl, fd, r;
+ char *content_length = pitoa (p->pool, statbuf->st_size);
+
+ fd = open (path, O_RDONLY);
+ if (fd < 0)
+ return file_not_found_error (p);
+
+ http_response = new_http_response (p->pool, p->http_request, p->io,
+ 200, "OK");
+ http_response_send_headers (http_response,
+ /* Content type. */
+ "Content-Type", "text/plain",
+ "Content-Length", content_length,
+ /* End of headers. */
+ NULL);
+ cl = http_response_end_headers (http_response);
+
+ if (http_request_is_HEAD (p->http_request)) return cl;
+
+ while ((r = read (fd, buffer, n)) > 0)
+ {
+ io_fwrite (buffer, r, 1, p->io);
+ }
+
+ if (r < 0)
+ perror ("read");
+
+ close (fd);
+
+ return cl;
+}
--- /dev/null
+/* Pseudothread server example.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_eg2_server.h,v 1.2 2002/08/21 10:42:16 rich Exp $
+ */
+
+#ifndef PTHR_EG2_SERVER_H
+#define PTHR_EG2_SERVER_H
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include <pool.h>
+
+#include "src/pthr_pseudothread.h"
+#include "src/pthr_http.h"
+#include "src/pthr_iolib.h"
+
+/* Define some RFC-compliant dates to represent past and future. */
+#define DISTANT_PAST "Thu, 01 Dec 1994 16:00:00 GMT"
+#define DISTANT_FUTURE "Sun, 01 Dec 2030 16:00:00 GMT"
+
+/* Headers which are sent to defeat caches. */
+#define NO_CACHE_HEADERS "Cache-Control", "must-revalidate", "Expires", DISTANT_PAST, "Pragma", "no-cache"
+
+#define CRLF "\r\n"
+
+struct eg2_server_processor;
+typedef struct eg2_server_processor *eg2_server_processor;
+
+extern eg2_server_processor new_eg2_server_processor (int sock);
+
+#endif /* PTHR_EG2_SERVER_H */
--- /dev/null
+/* Pseudothread server example.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_eg2_server_main.c,v 1.4 2002/08/21 10:42:16 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "src/pthr_server.h"
+#include "pthr_eg2_server.h"
+
+static void start_processor (int sock, void *data);
+static void catch_quit_signal (int sig);
+
+const char *root = "/tmp", *user = "nobody";
+
+int
+main (int argc, char *argv[])
+{
+ struct sigaction sa;
+
+ /* Intercept signals. */
+ memset (&sa, 0, sizeof sa);
+ sa.sa_handler = catch_quit_signal;
+ sa.sa_flags = SA_RESTART;
+ sigaction (SIGINT, &sa, 0);
+ sigaction (SIGQUIT, &sa, 0);
+ sigaction (SIGTERM, &sa, 0);
+
+ /* ... but ignore SIGPIPE errors. */
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_RESTART;
+ sigaction (SIGPIPE, &sa, 0);
+
+ /* Start up the server. */
+ pthr_server_chroot (root);
+ pthr_server_username (user);
+ pthr_server_main_loop (argc, argv, start_processor);
+
+ exit (0);
+}
+
+static void
+start_processor (int sock, void *data)
+{
+ (void) new_eg2_server_processor (sock);
+}
+
+static void
+catch_quit_signal (int sig)
+{
+ exit (0);
+}
--- /dev/null
+/* CGI library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_cgi.c,v 1.6 2003/02/02 18:05:30 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <hash.h>
+#include <pstring.h>
+
+#include "pthr_http.h"
+#include "pthr_cgi.h"
+
+static int post_max = -1;
+
+static void parse_qs (cgi, const char *qs);
+static void insert_param (cgi, char *name, char *value);
+
+struct cgi
+{
+ pool pool; /* Pool for allocations. */
+ shash params; /* Parameters (hash of string -> vector). */
+};
+
+int
+cgi_get_post_max (void)
+{
+ return post_max;
+}
+
+int
+cgi_set_post_max (int new_post_max)
+{
+ return post_max = new_post_max;
+}
+
+cgi
+new_cgi (struct pool *pool, http_request h, io_handle io)
+{
+ cgi c = pmalloc (pool, sizeof *c);
+
+ c->pool = pool;
+ c->params = new_shash (pool, vector);
+
+ if (http_request_method (h) != HTTP_METHOD_POST)
+ {
+ const char *qs = http_request_query_string (h);
+
+ parse_qs (c, qs);
+ }
+ else /* Method POST. */
+ {
+ const char *content_length_s =
+ http_request_get_header (h, "Content-Length");
+ const char *content_type = http_request_get_header (h, "Content-Type");
+ int content_length = -1;
+ const char std_type[] = "application/x-www-form-urlencoded";
+ struct pool *tmp = new_subpool (pool);
+ char *content;
+
+ if (content_length_s &&
+ sscanf (content_length_s, "%d", &content_length) != 1)
+ return 0; /* Error in field format. */
+
+ /* Content length too long? */
+ if (post_max >= 0 && content_length >= 0 && content_length > post_max)
+ return 0; /* Content too long. */
+
+ /* Check content type. If missing, assume it defaults to the
+ * standard application/x-www-form-urlencoded.
+ */
+ if (content_type &&
+ strncasecmp (content_type, std_type, strlen (std_type)) != 0)
+ return 0; /* Unexpected/unknown content type. */
+
+ /* Read the content, either to the end of input of else to the
+ * expected length. Note that Netscape 4 sends an extra CRLF
+ * after the POST data with is explicitly forbidden (see:
+ * RFC 2616, section 4.1). We ignore these next time around when
+ * we are reading the next request (see code in new_http_request).
+ */
+ if (content_length >= 0)
+ {
+ content = pmalloc (tmp, content_length + 1);
+ if (io_fread (content, 1, content_length, io) < content_length)
+ return 0;
+
+ content[content_length] = '\0';
+ }
+ else
+ {
+ char t[1024];
+ int r, n = 0;
+
+ content = pstrdup (tmp, "");
+
+ while ((r = io_fread (t, 1, 1024, io)) > 0)
+ {
+ n += r;
+ if (post_max >= 0 && n > post_max)
+ return 0; /* Content too long. */
+
+ t[r] = '\0';
+ content = pstrcat (tmp, content, t);
+ }
+ }
+
+ parse_qs (c, content);
+
+ delete_pool (tmp);
+ }
+
+ return c;
+}
+
+static void
+parse_qs (cgi c, const char *qs)
+{
+ vector v;
+ int i;
+ static char *one = "1";
+
+ if (qs && qs[0] != '\0')
+ {
+ /* Parse the query string. */
+ v = pstrcsplit (c->pool, qs, '&');
+
+ for (i = 0; i < vector_size (v); ++i)
+ {
+ char *param, *t;
+
+ vector_get (v, i, param);
+ t = strchr (param, '=');
+
+ /* No '=' char found? Assume it's a parameter of the form name=1. */
+ if (!t) { insert_param (c, param, one); continue; }
+
+ /* Split the string on the '=' character and set name and value. */
+ *t = '\0';
+ insert_param (c, param, t+1);
+ }
+ }
+}
+
+static void
+insert_param (cgi c, char *name, char *value)
+{
+ vector v;
+ char *s;
+
+ if (!shash_get (c->params, name, v))
+ v = new_vector (c->pool, char *);
+
+ s = cgi_unescape (c->pool, value);
+ vector_push_back (v, s);
+ shash_insert (c->params, name, v);
+}
+
+vector
+cgi_params (cgi c)
+{
+ return shash_keys (c->params);
+}
+
+const char *
+cgi_param (cgi c, const char *name)
+{
+ vector v;
+ char *s;
+
+ if (!shash_get (c->params, name, v))
+ return 0;
+
+ vector_get (v, 0, s);
+ return s;
+}
+
+const vector
+cgi_param_list (cgi c, const char *name)
+{
+ vector v;
+
+ if (!shash_get (c->params, name, v))
+ return 0;
+
+ return v;
+}
+
+int
+cgi_erase (cgi c, const char *name)
+{
+ return shash_erase (c->params, name);
+}
+
+cgi
+copy_cgi (pool npool, cgi c)
+{
+ const char *key, *value;
+ int i, j;
+ vector keys, values, v;
+
+ cgi nc = pmalloc (npool, sizeof *nc);
+
+ nc->pool = npool;
+ nc->params = new_shash (npool, vector);
+
+ keys = shash_keys_in_pool (c->params, npool);
+
+ for (i = 0; i < vector_size (keys); ++i)
+ {
+ vector_get (keys, i, key);
+ values = cgi_param_list (c, key);
+
+ for (j = 0; j < vector_size (values); ++j)
+ {
+ vector_get (values, j, value);
+ value = pstrdup (npool, value);
+
+ if (!shash_get (nc->params, key, v))
+ v = new_vector (npool, char *);
+
+ vector_push_back (v, value);
+ shash_insert (nc->params, key, v);
+ }
+ }
+
+ return nc;
+}
+
+char *
+cgi_escape (pool pool, const char *str)
+{
+ int i, j;
+ int len = strlen (str);
+ int new_len = 0;
+ char *new_str;
+ static const char hexdigits[] = "0123456789abcdef";
+
+ /* Work out how long the escaped string will be. Escaped strings
+ * get bigger.
+ */
+ for (i = 0; i < len; ++i)
+ {
+ if (isalnum ((int) str[i]) ||
+ str[i] == ',' || str[i] == '-' || str[i] == ' ')
+ new_len++;
+ else
+ new_len += 3;
+ }
+
+ new_str = pmalloc (pool, new_len + 1);
+
+ /* Escape the string. */
+ for (i = 0, j = 0; i < len; ++i)
+ {
+ if (isalnum ((int) str[i]) ||
+ str[i] == ',' || str[i] == '-')
+ new_str[j++] = str[i];
+ else if (str[i] == ' ')
+ new_str[j++] = '+';
+ else
+ {
+ new_str[j++] = '%';
+ new_str[j++] = hexdigits [(str[i] >> 4) & 0xf];
+ new_str[j++] = hexdigits [str[i] & 0xf];
+ }
+ }
+
+ new_str[j++] = '\0';
+
+ return new_str;
+}
+
+static inline int
+hex_to_int (char c)
+{
+ if (c >= '0' && c <= '9') return c - '0';
+ else if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+ else return -1;
+}
+
+char *
+cgi_unescape (pool pool, const char *str)
+{
+ int i, j;
+ int len = strlen (str);
+ char *new_str;
+
+ /* Unescaped strings always get smaller. */
+ new_str = pmalloc (pool, len + 1);
+
+ for (i = 0, j = 0; i < len; ++i)
+ {
+ if (str[i] == '%')
+ {
+ if (i+2 < len)
+ {
+ int a = hex_to_int (str[i+1]);
+ int b = hex_to_int (str[i+2]);
+
+ if (a >= 0 && b >= 0)
+ {
+ new_str[j++] = a << 4 | b;
+ i += 2;
+ }
+ else
+ new_str[j++] = str[i];
+ }
+ else
+ new_str[j++] = str[i];
+ }
+ else if (str[i] == '+')
+ new_str[j++] = ' ';
+ else
+ new_str[j++] = str[i];
+ }
+
+ new_str[j++] = '\0';
+
+ return new_str;
+}
--- /dev/null
+/* CGI library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_cgi.h,v 1.3 2002/08/23 13:53:02 rich Exp $
+ */
+
+#ifndef PTHR_CGI_H
+#define PTHR_CGI_H
+
+#include <pool.h>
+
+#include "pthr_http.h"
+#include "pthr_iolib.h"
+
+struct cgi;
+typedef struct cgi *cgi;
+
+/* Function: cgi_get_post_max - Get and set the internal POST_MAX parameter.
+ * Function: cgi_set_post_max
+ *
+ * These functions get and set the internal POST_MAX parameter which
+ * can be used to limit the size of @code{POST} method requests which
+ * this library will handle. If set to a non-negative integer, then
+ * POST requests will be limited to the number of bytes given. The
+ * default is -1 (unlimited) which can leave the server open to denial
+ * of service attacks.
+ *
+ * See also: @ref{new_cgi(3)}.
+ */
+extern int cgi_get_post_max (void);
+extern int cgi_set_post_max (int new_post_max);
+
+/* Function: new_cgi - Library for parsing CGI query strings.
+ * Function: cgi_params
+ * Function: cgi_param
+ * Function: cgi_param_list
+ * Function: cgi_erase
+ * Function: copy_cgi
+ *
+ * @code{new_cgi} creates a new CGI object from an existing HTTP
+ * request. It reads the query string or POSTed parameters and
+ * parses them internally. CGI parameters are case sensitive, and
+ * multiple parameters may be passed with the same name. Parameter
+ * values are automatically unescaped by the library before you
+ * get to see them.
+ *
+ * @code{cgi_params} returns a list of all the names of the parameters passed
+ * to the script. The list is returned as a @code{vector} of
+ * @code{char *}.
+ *
+ * @code{cgi_param} returns the value of a single named CGI parameter,
+ * or @code{NULL} if there is no such parameter. If multiple parameters
+ * were given with the same name, this returns one of them, essentially
+ * at random.
+ *
+ * @code{cgi_param_list} returns the list of values of the named
+ * CGI parameter. The list is returned as a @code{vector} of
+ * @code{char *}.
+ *
+ * @code{cgi_erase} erases the named parameter. If a parameter
+ * was erased, this returns true, else this returns false.
+ *
+ * @code{copy_cgi} copies @code{cgi} into pool @code{pool}.
+ *
+ * See also: @ref{cgi_get_post_max(3)}, @ref{cgi_escape(3)},
+ * @ref{new_http_request(3)}.
+ */
+extern cgi new_cgi (pool, http_request, io_handle);
+extern vector cgi_params (cgi);
+extern const char *cgi_param (cgi, const char *name);
+extern const vector cgi_param_list (cgi, const char *name);
+extern int cgi_erase (cgi, const char *name);
+extern cgi copy_cgi (pool, cgi);
+
+/* Function: cgi_escape - %-escape and unescape CGI arguments.
+ * Function: cgi_unescape
+ *
+ * These functions do %-escaping and unescaping on CGI arguments.
+ * When %-escaping a string, @code{" "} is replaced by @code{"+"},
+ * and non-printable
+ * characters are replaced by @code{"%hh"} where @code{hh} is a
+ * two-digit hex code. Unescaping a string reverses this process.
+ *
+ * See also: @ref{new_cgi(3)}, @ref{new_http_request(3)}.
+ */
+extern char *cgi_escape (pool, const char *str);
+extern char *cgi_unescape (pool, const char *str);
+
+#endif /* PTHR_CGI_H */
--- /dev/null
+/* Very Linux-specific context switching.
+ * Written by RWMJ with lots of help from GNU pth library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_context.c,v 1.7 2003/02/25 17:07:23 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pthr_context.h"
+
+#ifdef HAVE_WORKING_SETCONTEXT
+
+void
+mctx_set (mctx_t *mctx,
+ void (*sf_addr) (void *), void *sf_arg,
+ void *sk_addr, int sk_size)
+{
+ /* Fetch current context. */
+ getcontext (&mctx->uc);
+
+ /* Update to point to new stack. */
+ mctx->uc.uc_link = 0;
+ /* XXX I cheated and used code from pth. We should do proper
+ * automatic detection here instead.
+ */
+#if defined(__i386__)
+ mctx->uc.uc_stack.ss_sp = sk_addr + sk_size - 4;
+ /* mctx->uc.uc_stack.ss_size = sk_size; */
+#elif defined(__sparc__)
+ mctx->uc.uc_stack.ss_sp = sk_addr + sk_size - 8;
+ mctx->uc.uc_stack.ss_size = sk_size - 8;
+#else
+#error "Unsupported architecture"
+#endif
+ mctx->uc.uc_stack.ss_flags = 0;
+
+ /* Make new context. */
+ makecontext (&mctx->uc, sf_addr, 1, sf_arg);
+}
+
+unsigned long
+mctx_get_PC (mctx_t *mctx)
+{
+#if defined(__i386__) || defined(__i386)
+ return (unsigned long) mctx->uc.uc_mcontext.eip;
+#elif defined(__sparc)
+ return (unsigned long) mctx->uc.uc_mcontext.gregs[REG_PC];
+#else
+ return 0; /* This is a non-essential function. */
+#endif
+}
+
+unsigned long
+mctx_get_SP (mctx_t *mctx)
+{
+ return (unsigned long) mctx->uc.uc_stack.ss_sp;
+}
+
+#else /* setjmp implementation */
+
+#ifdef linux
+
+# if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined(JB_PC) && defined(JB_SP)
+# define SET_PC(mctx, v) mctx->jb[0].__jmpbuf[JB_PC] = (int) v
+# define SET_SP(mctx, v) mctx->jb[0].__jmpbuf[JB_SP] = (int) v
+# define GET_PC(mctx) mctx->jb[0].__jmpbuf[JB_PC]
+# define GET_SP(mctx) mctx->jb[0].__jmpbuf[JB_SP]
+# elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined(__mc68000__)
+# define SET_PC(mctx, v) mctx->jb[0].__jmpbuf[0].__aregs[0] = (long int) v
+# define SET_SP(mctx, v) mctx->jb[0].__jmpbuf[0].__sp = (int *) v
+# define GET_PC(mctx) mctx->jb[0].__jmpbuf[0].__aregs[0]
+# define GET_SP(mctx) mctx->jb[0].__jmpbuf[0].__sp
+# elif defined(__GNU_LIBRARY__) && defined(__i386__)
+# define SET_PC(mctx, v) mctx->jb[0].__jmpbuf[0].__pc = (char *) v
+# define SET_SP(mctx, v) mctx->jb[0].__jmpbuf[0].__sp = (void *) v
+# define GET_PC(mctx) mctx->jb[0].__jmpbuf[0].__pc
+# define GET_SP(mctx) mctx->jb[0].__jmpbuf[0].__sp
+# else
+# error "Unsupported Linux (g)libc version and/or platform"
+# endif
+
+#elif defined(__OpenBSD__) && defined(__i386__)
+
+# define GET_PC(mctx) mctx->jb[0]
+# define GET_SP(mctx) mctx->jb[2]
+# define SET_PC(mctx,v) GET_PC(mctx) = (long) v
+# define SET_SP(mctx,v) GET_SP(mctx) = (long) v
+
+#elif defined(__FreeBSD__) && defined(__i386__)
+
+# define GET_PC(mctx) mctx->jb[0]._jb[0]
+# define GET_SP(mctx) mctx->jb[0]._jb[2]
+# define SET_PC(mctx,v) GET_PC(mctx) = (int) v
+# define SET_SP(mctx,v) GET_SP(mctx) = (int) v
+
+#else
+#error "setjmp implementation has not been ported to your OS or architecture - please contact the maintainer"
+#endif
+
+void
+mctx_set (mctx_t *mctx,
+ void (*sf_addr) (void *), void *sf_arg,
+ void *sk_addr, int sk_size)
+{
+ int *stack_ptr = (int *) (sk_addr + sk_size);
+
+ /* Prevent gcc from allocating these variables in registers, to avoid
+ * a warning about variables being clobbered by setjmp/longjmp.
+ */
+ (void) &sk_addr;
+ (void) &stack_ptr;
+
+#ifdef __i386__
+ /* Push function argument onto the stack. */
+ *--stack_ptr = (int) sf_arg;
+ /* Push return address onto the stack. */
+ *--stack_ptr = 0xfafafafa;
+#if defined(__OpenBSD__) || defined(__FreeBSD__)
+ /* OpenBSD needs an extra word here for what? Frame pointer? */
+ *--stack_ptr = 0;
+#endif
+#else
+#error "unsupported machine architecture"
+#endif
+
+ mctx_save (mctx);
+ SET_PC (mctx, sf_addr);
+ SET_SP (mctx, stack_ptr);
+}
+
+unsigned long
+mctx_get_PC (mctx_t *mctx)
+{
+ return (unsigned) GET_PC (mctx);
+}
+
+unsigned long
+mctx_get_SP (mctx_t *mctx)
+{
+ return (unsigned long) GET_SP (mctx);
+}
+
+#endif /* ! USE_UCONTEXT */
--- /dev/null
+/* Very Linux-specific context switching.
+ * Written by RWMJ with lots of help from GNU pth library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_context.h,v 1.7 2003/02/05 22:13:32 rich Exp $
+ */
+
+#ifndef PTHR_CONTEXT_H
+#define PTHR_CONTEXT_H
+
+#include <setjmp.h>
+
+#ifdef HAVE_WORKING_SETCONTEXT
+
+#include <ucontext.h>
+
+typedef struct mctx_st {
+ ucontext_t uc;
+} mctx_t;
+
+/* Save machine context. */
+#define mctx_save(mctx) (void) getcontext (&(mctx)->uc)
+
+/* Restore machine context. */
+#define mctx_restore(mctx) (void) setcontext (&(mctx)->uc)
+
+/* Switch machine context. */
+#define mctx_switch(mctx_old, mctx_new) \
+(void) swapcontext (&((mctx_old)->uc), &((mctx_new)->uc))
+
+#else /* setjmp implementation */
+
+#include <setjmp.h>
+
+typedef struct mctx_st {
+ jmp_buf jb;
+} mctx_t;
+
+/* Save machine context. */
+#define mctx_save(mctx) (void) setjmp ((mctx)->jb)
+
+/* Restore machine context. */
+#define mctx_restore(mctx) longjmp ((mctx)->jb, 1)
+
+/* Switch machine context. */
+#define mctx_switch(mctx_old, mctx_new) \
+do { if (setjmp ((mctx_old)->jb) == 0) longjmp ((mctx_new)->jb, 1); } while(0)
+
+#endif /* ! USE_UCONTEXT */
+
+/* Create machine context. */
+extern void mctx_set (mctx_t *mctx,
+ void (*sf_addr) (void *), void *sf_arg,
+ void *sk_addr, int sk_size);
+
+/* Return PC. */
+extern unsigned long mctx_get_PC (mctx_t *mctx);
+
+/* Return SP. */
+extern unsigned long mctx_get_SP (mctx_t *mctx);
+
+#endif /* PTHR_CONTEXT_H */
--- /dev/null
+/* Database interface library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_dbi.c,v 1.18 2003/06/17 18:10:30 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_POSTGRESQL_LIBPQ_FE_H
+#include <postgresql/libpq-fe.h>
+#endif
+
+#ifdef HAVE_LIBPQ_FE_H
+#include <libpq-fe.h>
+#endif
+
+#ifndef HAVE_PQESCAPESTRING
+size_t PQescapeString(char *to, const char *from, size_t length);
+#endif
+
+#include <pool.h>
+#include <hash.h>
+#include <vector.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include "pthr_pseudothread.h"
+#include "pthr_dbi.h"
+
+#define DBI_FORCE_DEBUG 0
+#define DBI_POOLS_DEBUG 0
+
+#define DEBUG(dbh,sth,fs,args...) do { if (DBI_FORCE_DEBUG || ((dbh)->flags & DBI_DEBUG)) { if ((sth) == 0) fprintf (stderr, "dbi: dbh %p: " fs, (dbh) , ## args); else fprintf (stderr, "dbi: dbh %p sth %p: " fs, (dbh), (void *) (sth) , ## args); fputc ('\n', stderr); } } while (0)
+
+/* Database handle. */
+struct db_handle
+{
+ pool pool; /* Pool for allocations. */
+ const char *conninfo; /* Connection string. */
+ int flags; /* Flags. */
+ int in_transaction; /* Are we in a transaction yet? */
+ PGconn *conn; /* The database connection object. */
+};
+
+/* Statement handle. */
+struct st_handle
+{
+ pool pool; /* Subpool used for allocations. */
+ db_handle dbh; /* Parent database connection. */
+ const char *orig_query; /* Original query string. */
+ vector query; /* Query, split into string, '?' and '@'. */
+ vector intypes; /* Placeholder types (vector of int). */
+ PGresult *result; /* Last result. */
+ int fetch_allowed; /* True if there are tuples to fetch. */
+ int next_tuple; /* Next row to fetch. */
+ vector outtypes; /* Output types (vector of struct otype). */
+};
+
+struct otype
+{
+ int type; /* Type of this element. */
+ void *varptr; /* Pointer to variable for result. */
+};
+
+static void init_dbi (void) __attribute__((constructor));
+static void free_dbi (void) __attribute__((destructor));
+static void disconnect (void *vdbh);
+static void parse_timestamp (st_handle sth, const char *str, struct dbi_timestamp *ts);
+static void parse_interval (st_handle sth, const char *str, struct dbi_interval *inv);
+
+/* Global variables. */
+static pool dbi_pool;
+static const pcre *re_qs, *re_timestamp, *re_interval;
+
+/* For connection pools. */
+static shash connpools; /* Hash conninfo string -> vector of dbh. */
+static hash inuse; /* Hash dbh -> subpool, of handles in use. */
+
+/* Initialise the library. */
+static void
+init_dbi ()
+{
+#ifndef __OpenBSD__
+ dbi_pool = new_subpool (global_pool);
+#else
+ dbi_pool = new_pool ();
+#endif
+ re_qs = precomp (dbi_pool, "\\?|@", 0);
+ re_timestamp = precomp (dbi_pool,
+" (?:(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)) # date (YYYY-MM-DD)\n"
+" \\s* # space between date and time\n"
+" (?:(\\d\\d):(\\d\\d) # HH:MM\n"
+" (?::(\\d\\d))? # optional :SS\n"
+" (?:\\.(\\d+))? # optional .microseconds\n"
+" (?:([+-])(\\d\\d))? # optional +/-OO offset from UTC\n"
+" )?", PCRE_EXTENDED);
+ re_interval = precomp (dbi_pool,
+" (?:(\\d+)\\syears?)? # years\n"
+" \\s* # \n"
+" (?:(\\d+)\\smons?)? # months\n"
+" \\s* # \n"
+" (?:(\\d+)\\sdays?)? # days\n"
+" \\s* # \n"
+" (?:(\\d\\d):(\\d\\d) # HH:MM\n"
+" (?::(\\d\\d))? # optional :SS\n"
+" )?", PCRE_EXTENDED);
+
+ /* Set up connection pools. */
+ connpools = new_shash (dbi_pool, vector);
+ inuse = new_hash (dbi_pool, db_handle, pool);
+}
+
+/* Free up global memory used by the library. */
+static void
+free_dbi ()
+{
+ delete_pool (dbi_pool);
+}
+
+db_handle
+new_db_handle (pool pool, const char *conninfo, int flags)
+{
+ db_handle dbh = pmalloc (pool, sizeof *dbh);
+ int status, fd;
+
+ dbh->pool = pool;
+ dbh->conninfo = pstrdup (pool, conninfo);
+ dbh->flags = flags;
+ dbh->in_transaction = 0;
+
+ /* Begin the database connection. */
+ dbh->conn = PQconnectStart (conninfo);
+ if (dbh->conn == 0) /* Failed. */
+ return 0;
+
+ /* See the PostgreSQL documentation for the libpq connect functions
+ * for details about how the following loop works.
+ */
+ status = PQstatus (dbh->conn);
+ if (status == CONNECTION_BAD)
+ {
+ PQfinish (dbh->conn);
+ return 0; /* Connection failed immediately. */
+ }
+
+ if (PQsetnonblocking (dbh->conn, 1) == -1) abort ();
+ fd = PQsocket (dbh->conn);
+
+ status = PGRES_POLLING_WRITING;
+
+ while (status != PGRES_POLLING_OK &&
+ status != PGRES_POLLING_FAILED)
+ {
+ switch (status)
+ {
+ case PGRES_POLLING_WRITING:
+ pth_wait_writable (fd);
+ break;
+ case PGRES_POLLING_READING:
+ pth_wait_readable (fd);
+ break;
+ }
+ status = PQconnectPoll (dbh->conn);
+ }
+
+ if (status == PGRES_POLLING_FAILED)
+ {
+ PQfinish (dbh->conn);
+ return 0; /* Connection failed. */
+ }
+
+ /*- Connected! -*/
+
+ DEBUG (dbh, 0, "connected");
+
+ /* Remember to clean up this connection when the pool gets deleted. */
+ pool_register_cleanup_fn (dbh->pool, disconnect, dbh);
+
+ return dbh;
+}
+
+static void
+disconnect (void *vdbh)
+{
+ db_handle dbh = (db_handle) vdbh;
+
+ PQfinish (dbh->conn);
+
+ DEBUG (dbh, 0, "disconnected");
+}
+
+void
+db_set_debug (db_handle dbh, int d)
+{
+ if (d)
+ {
+ dbh->flags |= DBI_DEBUG;
+ DEBUG (dbh, 0, "debugging enabled");
+ }
+ else
+ {
+ DEBUG (dbh, 0, "debugging disabled");
+ dbh->flags &= ~DBI_DEBUG;
+ }
+}
+
+int
+db_get_debug (db_handle dbh)
+{
+ return dbh->flags & DBI_DEBUG;
+}
+
+void
+db_commit (db_handle dbh)
+{
+ st_handle sth;
+
+ sth = st_prepare_cached (dbh, "commit work");
+ st_execute (sth);
+
+ dbh->in_transaction = 0;
+}
+
+void
+db_rollback (db_handle dbh)
+{
+ st_handle sth;
+
+ sth = st_prepare_cached (dbh, "rollback work");
+ st_execute (sth);
+
+ dbh->in_transaction = 0;
+}
+
+int
+db_in_transaction (db_handle dbh)
+{
+ return dbh->in_transaction;
+}
+
+static void return_dbh (void *vdbh);
+
+db_handle
+get_db_handle (const char *conninfo, int flags)
+{
+ pool pool;
+ vector free_dbhs;
+ db_handle dbh = 0;
+
+ /* Allocate a subpool of the current thread's pool. If the thread dies
+ * or if the thread calls put_db_handle, this subpool will be deleted,
+ * resulting in the return_dbh function being called.
+ */
+ pool = new_subpool (pth_get_pool (current_pth));
+
+ /* Get a free handle. */
+ if (shash_get (connpools, conninfo, free_dbhs) &&
+ vector_size (free_dbhs) > 0)
+ {
+ /* Push back, pop front to ensure handles are circulated and used
+ * equally.
+ */
+ vector_pop_front (free_dbhs, dbh);
+ dbh->flags = flags;
+
+ assert (strcmp (dbh->conninfo, conninfo) == 0);
+ }
+
+ if (!dbh) /* Need to create a new handle. */
+ dbh = new_db_handle (dbi_pool, conninfo, flags);
+
+ /* Remember to return this handle when the subpool is deleted. */
+ pool_register_cleanup_fn (pool, return_dbh, dbh);
+
+ /* Remember this handle is in use. */
+ hash_insert (inuse, dbh, pool);
+
+#if DBI_POOLS_DEBUG
+ fprintf (stderr, "get_db_handle: conninfo \"%s\" -> dbh %p\n",
+ conninfo, dbh);
+#endif
+
+ return dbh;
+}
+
+void
+put_db_handle (db_handle dbh)
+{
+ pool pool;
+
+#if DBI_POOLS_DEBUG
+ fprintf (stderr, "put_db_handle: dbh %p (conninfo \"%s\")\n",
+ dbh, dbh->conninfo);
+#endif
+
+ /* Find the corresponding subpool and delete it. */
+ if (!hash_get (inuse, dbh, pool)) abort ();
+ delete_pool (pool);
+}
+
+/* This function is called when the thread exits or calls put_db_handle. We
+ * need to return this handle to the correct connection pool.
+ */
+static void
+return_dbh (void *vdbh)
+{
+ db_handle dbh = (db_handle) vdbh;
+ vector free_dbhs;
+
+#if DBI_POOLS_DEBUG
+ fprintf (stderr, "return_dbh: dbh %p (conninfo \"%s\")\n",
+ dbh, dbh->conninfo);
+#endif
+
+ if (dbh->in_transaction) db_rollback (dbh);
+
+ /* Remove from the in-use list. */
+ if (!hash_erase (inuse, dbh)) abort ();
+
+ if (shash_get (connpools, dbh->conninfo, free_dbhs))
+ vector_push_back (free_dbhs, dbh);
+ else
+ {
+ /* Need to create the free dbhs vector. */
+ free_dbhs = new_vector (dbi_pool, db_handle);
+ vector_push_back (free_dbhs, dbh);
+ shash_insert (connpools, dbh->conninfo, free_dbhs);
+ }
+}
+
+int
+st_serial (st_handle sth, const char *seq_name)
+{
+ db_handle dbh = sth->dbh;
+ st_handle sth2;
+ int serial;
+
+ /* In PostgreSQL, to fetch the serial number we need to issue another
+ * command to the database.
+ */
+ sth2 = st_prepare_cached (dbh, "select currval (?)", DBI_STRING);
+ st_execute (sth2, seq_name);
+
+ st_bind (sth2, 0, serial, DBI_INT);
+
+ if (!st_fetch (sth2))
+ {
+ if ((dbh->flags & DBI_THROW_ERRORS))
+ pth_die ("dbi: st_serial: failed to fetch sequence value");
+ else
+ return -1;
+ }
+
+ return serial;
+}
+
+static void finish_handle (void *vsth);
+
+st_handle
+new_st_handle (db_handle dbh, const char *query, int flags, ...)
+{
+ st_handle sth;
+ pool pool;
+ int i;
+ va_list args;
+
+ /* XXX Ignore the caching flag at the moment. Statement handles cannot
+ * be trivially cached, because the same handle might then be used
+ * concurrently in two different threads, which would cause serious
+ * problems. Also since PostgreSQL doesn't support PREPARE yet, we
+ * don't get the particular performance boost by caching query plans
+ * on the server side anyway.
+ *
+ * Actually the first statement isn't strictly true. We should never
+ * be sharing database handles across threads, so as long as we only
+ * cache statements in the database handle, we ought to be OK.
+ *
+ * (And the second statement isn't true anymore either - since 7.3,
+ * PostgreSQL supports PREPAREd statements).
+ */
+
+ /* Allocating in a subpool isn't strictly necessary at the moment. However
+ * in the future it will allow us to safely free up the memory associated
+ * with the statement handle when the handle is 'finished'.
+ */
+ pool = new_subpool (dbh->pool);
+ sth = pmalloc (pool, sizeof *sth);
+
+ sth->pool = pool;
+ sth->dbh = dbh;
+ sth->orig_query = query;
+ sth->result = 0;
+ sth->fetch_allowed = 0;
+ sth->outtypes = 0;
+
+ /* Examine the query string looking for ? and @ placeholders which
+ * don't occur inside strings.
+ * XXX Haven't implemented the test for placeholders inside strings
+ * yet, so avoid using ? and @ as anything but placeholders for the
+ * moment XXX
+ */
+ sth->query = pstrresplit2 (pool, query, re_qs);
+
+ /* Set up the array of input types. */
+ sth->intypes = new_vector (pool, int);
+ va_start (args, flags);
+
+ for (i = 0; i < vector_size (sth->query); ++i)
+ {
+ char *q;
+
+ vector_get (sth->query, i, q);
+
+ if (strcmp (q, "?") == 0 || strcmp (q, "@") == 0)
+ {
+ int type = va_arg (args, int);
+
+ assert (DBI_MIN_TYPE <= type && type <= DBI_MAX_TYPE);
+ vector_push_back (sth->intypes, type);
+ }
+ }
+
+ va_end (args);
+
+ /* Remember to clean up this handle when the pool gets deleted. */
+ pool_register_cleanup_fn (pool, finish_handle, sth);
+
+ DEBUG (dbh, sth, "handle created for query: %s", sth->orig_query);
+
+ return sth;
+}
+
+static void
+finish_handle (void *vsth)
+{
+ st_handle sth = (st_handle) vsth;
+
+ if (sth->result)
+ PQclear (sth->result);
+ sth->result = 0;
+
+ DEBUG (sth->dbh, sth, "finished (implicit)");
+}
+
+static int exec_error (st_handle sth, PGresult *result);
+static char *escape_string (pool, const char *);
+
+int
+st_execute (st_handle sth, ...)
+{
+ pool pool = sth->pool;
+ int i, typeidx;
+ va_list args;
+ char *query;
+ PGconn *conn;
+ PGresult *result;
+ int fd;
+ ExecStatusType status;
+
+ /* Formulate a query with the types substituted as appropriate. */
+ query = pstrdup (pool, "");
+ va_start (args, sth);
+
+ for (i = 0, typeidx = 0; i < vector_size (sth->query); ++i)
+ {
+ char *q;
+ int type;
+
+ vector_get (sth->query, i, q);
+
+ if (strcmp (q, "?") == 0) /* Simple placeholder. */
+ {
+ vector_get (sth->intypes, typeidx, type); typeidx++;
+
+ switch (type)
+ {
+ case DBI_INT:
+ query = pstrcat (pool, query, pitoa (pool, va_arg (args, int)));
+ break;
+
+ case DBI_INT_OR_NULL:
+ {
+ int r = va_arg (args, int);
+
+ if (r != 0)
+ query = pstrcat (pool, query, pitoa (pool, r));
+ else
+ query = pstrcat (pool, query, "null");
+ }
+ break;
+
+ case DBI_STRING:
+ {
+ const char *str = va_arg (args, const char *);
+
+ if (str)
+ {
+ query = pstrcat (pool, query, "'");
+ query = pstrcat (pool, query, escape_string (pool, str));
+ query = pstrcat (pool, query, "'");
+ }
+ else
+ query = pstrcat (pool, query, "null");
+ }
+ break;
+
+ case DBI_BOOL:
+ query =
+ pstrcat (pool, query, va_arg (args, int) ? "'t'" : "'f'");
+ break;
+
+ case DBI_CHAR:
+ {
+ char str[2] = { va_arg (args, int), '\0' }; /* sic */
+
+ query = pstrcat (pool, query, "'");
+ query = pstrcat (pool, query, escape_string (pool, str));
+ query = pstrcat (pool, query, "'");
+ }
+ break;
+
+ case DBI_TIMESTAMP:
+ case DBI_INTERVAL:
+ abort (); /* Not implemented yet! */
+
+ default:
+ abort ();
+ }
+ }
+ else if (strcmp (q, "@") == 0) /* List placeholder. */
+ {
+ vector v;
+
+ vector_get (sth->intypes, typeidx, type); typeidx++;
+
+ /* We don't know yet if v is a vector of int or char *. */
+ v = va_arg (args, vector);
+
+ /* But we _do_ know that if the vector is empty, PG will fail.
+ * Stupid bug in PostgreSQL.
+ */
+ assert (vector_size (v) > 0);
+
+ switch (type)
+ {
+ case DBI_INT:
+ v = pvitostr (pool, v);
+ query = pstrcat (pool, query, pjoin (pool, v, ","));
+ break;
+
+ case DBI_STRING:
+ /* XXX Does not handle nulls correctly. */
+ query = pstrcat (pool, query, "'");
+ query = pstrcat (pool, query,
+ pjoin (pool,
+ pmap (pool, v, escape_string), "','"));
+ query = pstrcat (pool, query, "'");
+ break;
+
+ case DBI_BOOL:
+ case DBI_CHAR:
+ case DBI_INT_OR_NULL:
+ case DBI_TIMESTAMP:
+ case DBI_INTERVAL:
+ abort (); /* Not implemented yet! */
+
+ default:
+ abort ();
+ }
+ }
+ else /* String. */
+ query = pstrcat (pool, query, q);
+ }
+
+ va_end (args);
+
+ /* In transaction? If not, we need to issue a BEGIN WORK command. */
+ if (!sth->dbh->in_transaction)
+ {
+ st_handle sth_bw;
+
+ /* So we don't go into infinite recursion here ... */
+ sth->dbh->in_transaction = 1;
+
+ sth_bw = st_prepare_cached (sth->dbh, "begin work");
+ if (st_execute (sth_bw) == -1)
+ {
+ sth->dbh->in_transaction = 0;
+ return exec_error (sth, 0);
+ }
+ }
+
+ DEBUG (sth->dbh, sth, "execute: %s", query);
+
+ /* Get the connection. */
+ conn = sth->dbh->conn;
+ assert (PQisnonblocking (conn));
+ fd = PQsocket (conn);
+
+ /* Run it. */
+ if (PQsendQuery (conn, query) != 1)
+ return exec_error (sth, 0);
+
+ /* Get all the command results. Ignore all but the last one. */
+ do
+ {
+ /* Wait for the result. */
+ while (PQisBusy (conn))
+ {
+ /* Blocks ..? */
+ if (PQflush (conn) == EOF)
+ return exec_error (sth, 0);
+
+ pth_wait_readable (fd);
+
+ if (PQconsumeInput (conn) != 1)
+ return exec_error (sth, 0);
+ }
+
+ result = PQgetResult (conn);
+ if (result)
+ {
+ if (sth->result) PQclear (sth->result);
+ sth->result = result;
+ }
+ }
+ while (result);
+
+ /* Get the result status. */
+ status = PQresultStatus (sth->result);
+
+ if (status == PGRES_COMMAND_OK) /* INSERT, UPDATE, DELETE, etc. */
+ {
+ char *s = PQcmdTuples (sth->result);
+ int rv;
+
+ sth->fetch_allowed = 0;
+
+ if (s && strlen (s) > 0 && sscanf (s, "%d", &rv) == 1)
+ return rv; /* Return rows affected. */
+ else
+ return 0; /* Command OK, unknown # rows affected. */
+ }
+ else if (status == PGRES_TUPLES_OK)
+ {
+ sth->fetch_allowed = 1;
+ sth->next_tuple = 0;
+
+ /* SELECT OK, return number of rows in the result. */
+ return PQntuples (sth->result);
+ }
+ else
+ /* Some other error. */
+ return exec_error (sth, sth->result);
+}
+
+static char *
+escape_string (pool pool, const char *s)
+{
+ int len = strlen (s);
+ char *r = pmalloc (pool, len * 2 + 1);
+
+ PQescapeString (r, s, len);
+ return r;
+}
+
+static int
+exec_error (st_handle sth, PGresult *result)
+{
+ if (!result) /* Some sort of connection-related error. */
+ {
+ perror ("dbi: st_execute: database connection error");
+ if ((sth->dbh->flags & DBI_THROW_ERRORS))
+ pth_die ("dbi: st_execute: database execution error");
+ else
+ return -1;
+ }
+ else /* Execution error. */
+ {
+ char *error = psprintf (sth->pool,
+ "dbi: st_execute: %s",
+ PQresultErrorMessage (result));
+
+ fprintf (stderr, "%s\n", error);
+ if ((sth->dbh->flags & DBI_THROW_ERRORS))
+ pth_die (error);
+ else
+ return -1;
+ }
+}
+
+void
+_st_bind (st_handle sth, int colidx, void *varptr, int type)
+{
+ struct otype zero = { 0, 0 };
+ struct otype ot;
+ int extend_by;
+
+ if (sth->outtypes == 0)
+ sth->outtypes = new_vector (sth->pool, struct otype);
+
+ /* Is the vector large enough? If not, extend it. */
+ extend_by = colidx - vector_size (sth->outtypes) + 1;
+ if (extend_by > 0)
+ vector_fill (sth->outtypes, zero, extend_by);
+
+ ot.type = type;
+ ot.varptr = varptr;
+ vector_replace (sth->outtypes, colidx, ot);
+}
+
+int
+st_fetch (st_handle sth)
+{
+ int nr_rows, i;
+ struct otype ot;
+
+ if (!sth->result || !sth->fetch_allowed)
+ {
+ const char *error =
+ "dbi: st_fetch: fetch without execute, or on a non-SELECT statement";
+
+ fprintf (stderr, "%s\n", error);
+ if ((sth->dbh->flags & DBI_THROW_ERRORS))
+ pth_die (error);
+ else
+ return -1;
+ }
+
+ /* Get number of rows in the result. */
+ nr_rows = PQntuples (sth->result);
+
+ if (sth->next_tuple >= nr_rows)
+ {
+ DEBUG (sth->dbh, sth, "fetch: no more rows in query");
+ return 0; /* Finished. */
+ }
+
+ DEBUG (sth->dbh, sth, "fetch: starting row fetch");
+
+ /* Fetch it. */
+ if (sth->outtypes)
+ for (i = 0; i < vector_size (sth->outtypes); ++i)
+ {
+ vector_get (sth->outtypes, i, ot);
+
+ if (ot.type != 0 && ot.varptr != 0)
+ {
+ int is_null = PQgetisnull (sth->result, sth->next_tuple, i);
+ char *r = !is_null ? PQgetvalue (sth->result, sth->next_tuple, i)
+ : 0;
+
+ DEBUG (sth->dbh, sth, "fetch: col %d: %s", i, r);
+
+ switch (ot.type)
+ {
+ case DBI_STRING:
+ * (char **) ot.varptr = r;
+ break;
+
+ case DBI_INT:
+ case DBI_INT_OR_NULL:
+ if (is_null)
+ * (int *) ot.varptr = 0; /* Best we can do in C ! */
+ else
+ sscanf (r, "%d", (int *) ot.varptr);
+ break;
+
+ case DBI_BOOL:
+ if (is_null)
+ * (int *) ot.varptr = 0; /* Best we can do! */
+ else
+ * (int *) ot.varptr = strcmp (r, "t") == 0;
+ break;
+
+ case DBI_CHAR:
+ if (is_null)
+ * (char *) ot.varptr = 0; /* Best we can do! */
+ else
+ * (char *) ot.varptr = r[0];
+ break;
+
+ case DBI_TIMESTAMP:
+ {
+ struct dbi_timestamp *ts =
+ (struct dbi_timestamp *) ot.varptr;
+
+ memset (ts, 0, sizeof *ts);
+ if (is_null)
+ ts->is_null = 1;
+ else
+ parse_timestamp (sth, r, ts);
+ }
+ break;
+
+ case DBI_INTERVAL:
+ {
+ struct dbi_interval *inv =
+ (struct dbi_interval *) ot.varptr;
+
+ memset (inv, 0, sizeof *inv);
+ if (is_null)
+ inv->is_null = 1;
+ else
+ parse_interval (sth, r, inv);
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ }
+
+ DEBUG (sth->dbh, sth, "fetch: ended row fetch");
+
+ sth->next_tuple++;
+
+ return 1; /* Row returned. */
+}
+
+static inline int
+parse_fixed_width_int (const char *str, int width)
+{
+ int r = 0, i;
+
+ for (i = 0; i < width; ++i)
+ {
+ r *= 10;
+ r += str[i] - '0';
+ }
+
+ return r;
+}
+
+/* Parse a timestamp field from a PostgreSQL database, and return it
+ * broken out into the dbi_timestamp structure. This can also parse
+ * dates and times.
+ */
+static void
+parse_timestamp (st_handle sth, const char *str, struct dbi_timestamp *ts)
+{
+ vector v;
+ const char *s, *sign;
+
+ /* Parse the timestamp. */
+ v = prematch (sth->pool, str, re_timestamp, 0);
+
+ if (!v)
+ pth_die (psprintf (sth->pool,
+ "dbi: parse_timestamp: invalid timestamp: %s",
+ str));
+
+ if (vector_size (v) <= 1) return;
+ vector_get (v, 1, s);
+ if (s) ts->year = parse_fixed_width_int (s, 4);
+
+ if (vector_size (v) <= 2) return;
+ vector_get (v, 2, s);
+ if (s) ts->month = parse_fixed_width_int (s, 2);
+
+ if (vector_size (v) <= 3) return;
+ vector_get (v, 3, s);
+ if (s) ts->day = parse_fixed_width_int (s, 2);
+
+ if (vector_size (v) <= 4) return;
+ vector_get (v, 4, s);
+ if (s) ts->hour = parse_fixed_width_int (s, 2);
+
+ if (vector_size (v) <= 5) return;
+ vector_get (v, 5, s);
+ if (s) ts->min = parse_fixed_width_int (s, 2);
+
+ if (vector_size (v) <= 6) return;
+ vector_get (v, 6, s);
+ if (s) ts->sec = parse_fixed_width_int (s, 2);
+
+ if (vector_size (v) <= 7) return;
+ vector_get (v, 7, s);
+ if (s) sscanf (s, "%d", &ts->microsecs);
+
+ if (vector_size (v) <= 9) return;
+ vector_get (v, 8, sign);
+ vector_get (v, 9, s);
+ if (sign && s)
+ {
+ ts->utc_offset = parse_fixed_width_int (s, 2);
+ if (sign[0] == '-') ts->utc_offset = -ts->utc_offset;
+ }
+}
+
+/* Parse an interval field from a PostgreSQL database, and return it
+ * broken out into the dbi_interval structure.
+ */
+static void
+parse_interval (st_handle sth, const char *str, struct dbi_interval *inv)
+{
+ vector v;
+ const char *s;
+
+ /* Parse the interval. */
+ v = prematch (sth->pool, str, re_interval, 0);
+
+ if (!v)
+ pth_die (psprintf (sth->pool,
+ "dbi: parse_interval: invalid interval: %s",
+ str));
+
+ if (vector_size (v) <= 1) return;
+ vector_get (v, 1, s);
+ if (s) sscanf (s, "%d", &inv->years);
+
+ if (vector_size (v) <= 2) return;
+ vector_get (v, 2, s);
+ if (s) sscanf (s, "%d", &inv->months);
+
+ if (vector_size (v) <= 3) return;
+ vector_get (v, 3, s);
+ if (s) sscanf (s, "%d", &inv->days);
+
+ if (vector_size (v) <= 4) return;
+ vector_get (v, 4, s);
+ if (s) inv->hours = parse_fixed_width_int (s, 2);
+
+ if (vector_size (v) <= 5) return;
+ vector_get (v, 5, s);
+ if (s) inv->mins = parse_fixed_width_int (s, 2);
+
+ if (vector_size (v) <= 6) return;
+ vector_get (v, 6, s);
+ if (s) inv->secs = parse_fixed_width_int (s, 2);
+}
+
+vector
+st_fetch_all_rows (st_handle sth)
+{
+ vector result;
+ int row, nr_rows;
+ int col, nr_cols;
+
+ if (!sth->result || !sth->fetch_allowed)
+ {
+ const char *error =
+ "dbi: st_fetch_all_rows: fetch without execute, or on a non-SELECT statement";
+
+ fprintf (stderr, "%s\n", error);
+ if ((sth->dbh->flags & DBI_THROW_ERRORS))
+ pth_die (error);
+ else
+ return 0;
+ }
+
+ DEBUG (sth->dbh, sth, "fetch_all_rows");
+
+ result = new_vector (sth->pool, vector);
+
+ /* Get number of rows, columns in the result. */
+ nr_rows = PQntuples (sth->result);
+ nr_cols = PQnfields (sth->result);
+
+ /* Fetch it. */
+ for (row = 0; row < nr_rows; ++row)
+ {
+ vector v = new_vector (sth->pool, char *);
+
+ for (col = 0; col < nr_cols; ++col)
+ {
+ char *s = PQgetisnull (sth->result, row, col)
+ ? 0 : PQgetvalue (sth->result, row, col);
+
+ vector_push_back (v, s);
+ }
+
+ vector_push_back (result, v);
+ }
+
+ return result;
+}
+
+void
+st_finish (st_handle sth)
+{
+ if (sth->result)
+ PQclear (sth->result);
+ sth->result = 0;
+
+ DEBUG (sth->dbh, sth, "finished (explicit)");
+}
+
+#ifndef HAVE_PQESCAPESTRING
+/* This is taken from the PostgreSQL source code. */
+
+/* ---------------
+ * Escaping arbitrary strings to get valid SQL strings/identifiers.
+ *
+ * Replaces "\\" with "\\\\" and "'" with "''".
+ * length is the length of the buffer pointed to by
+ * from. The buffer at to must be at least 2*length + 1 characters
+ * long. A terminating NUL character is written.
+ * ---------------
+ */
+
+size_t
+PQescapeString(char *to, const char *from, size_t length)
+{
+ const char *source = from;
+ char *target = to;
+ unsigned int remaining = length;
+
+ while (remaining > 0)
+ {
+ switch (*source)
+ {
+ case '\\':
+ *target = '\\';
+ target++;
+ *target = '\\';
+ /* target and remaining are updated below. */
+ break;
+
+ case '\'':
+ *target = '\'';
+ target++;
+ *target = '\'';
+ /* target and remaining are updated below. */
+ break;
+
+ default:
+ *target = *source;
+ /* target and remaining are updated below. */
+ }
+ source++;
+ target++;
+ remaining--;
+ }
+
+ /* Write the terminating NUL character. */
+ *target = '\0';
+
+ return target - to;
+}
+#endif
+
+#if 0
+/*
+ * PQescapeBytea - converts from binary string to the
+ * minimal encoding necessary to include the string in an SQL
+ * INSERT statement with a bytea type column as the target.
+ *
+ * The following transformations are applied
+ * '\0' == ASCII 0 == \\000
+ * '\'' == ASCII 39 == \'
+ * '\\' == ASCII 92 == \\\\
+ * anything >= 0x80 ---> \\ooo (where ooo is an octal expression)
+ */
+unsigned char *
+PQescapeBytea(unsigned char *bintext, size_t binlen, size_t *bytealen)
+{
+ unsigned char *vp;
+ unsigned char *rp;
+ unsigned char *result;
+ size_t i;
+ size_t len;
+
+ /*
+ * empty string has 1 char ('\0')
+ */
+ len = 1;
+
+ vp = bintext;
+ for (i = binlen; i > 0; i--, vp++)
+ {
+ if (*vp == 0 || *vp >= 0x80)
+ len += 5; /* '5' is for '\\ooo' */
+ else if (*vp == '\'')
+ len += 2;
+ else if (*vp == '\\')
+ len += 4;
+ else
+ len++;
+ }
+
+ rp = result = (unsigned char *) malloc(len);
+ if (rp == NULL)
+ return NULL;
+
+ vp = bintext;
+ *bytealen = len;
+
+ for (i = binlen; i > 0; i--, vp++)
+ {
+ if (*vp == 0 || *vp >= 0x80)
+ {
+ (void) sprintf(rp, "\\\\%03o", *vp);
+ rp += 5;
+ }
+ else if (*vp == '\'')
+ {
+ rp[0] = '\\';
+ rp[1] = '\'';
+ rp += 2;
+ }
+ else if (*vp == '\\')
+ {
+ rp[0] = '\\';
+ rp[1] = '\\';
+ rp[2] = '\\';
+ rp[3] = '\\';
+ rp += 4;
+ }
+ else
+ *rp++ = *vp;
+ }
+ *rp = '\0';
+
+ return result;
+}
+#endif
--- /dev/null
+/* Database interface library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_dbi.h,v 1.9 2003/02/22 14:23:06 rich Exp $
+ */
+
+#ifndef PTHR_DBI_H
+#define PTHR_DBI_H
+
+struct db_handle;
+typedef struct db_handle *db_handle;
+
+struct st_handle;
+typedef struct st_handle *st_handle;
+
+#include <pool.h>
+#include <vector.h>
+
+/* Function: new_db_handle - database interface library
+ * Function: db_commit
+ * Function: db_rollback
+ * Function: db_in_transaction
+ * Function: get_db_handle
+ * Function: put_db_handle
+ * Function: new_st_handle
+ * Function: st_prepare
+ * Function: st_prepare_cached
+ * Function: st_execute
+ * Function: st_serial
+ * Function: _st_bind
+ * Function: st_bind
+ * Function: st_fetch
+ * Function: st_fetch_all_rows
+ * Function: st_finish
+ * Function: db_set_debug
+ * Function: db_get_debug
+ *
+ * @code{pthr_dbi} is a library for interfacing pthrlib programs
+ * with the PostgreSQL database (see @code{http://www.postgresql.org/}).
+ *
+ * @code{new_db_handle} creates a new database handle, and connects
+ * to the database. The connection structure is allocated in
+ * @code{pool}. The connection is automatically closed and the memory
+ * freed when the pool is deleted. The @code{conninfo} string is
+ * the connection string passed to @code{libpq}. This string is
+ * fully documented in the PostgreSQL Programmer's Guide, Section I
+ * (Client Interfaces), libpq, 1.2 Database Connection Functions.
+ * Commonly the string will contain:
+ *
+ * @code{"host=HOSTNAME dbname=DBNAME"}
+ *
+ * or:
+ *
+ * @code{"host=HOSTNAME dbname=DBNAME user=USER password=PASSWORD"}
+ *
+ * The @code{flags} parameter contains zero or more of the following flags:
+ *
+ * @code{DBI_THROW_ERRORS}: If set causes database errors to
+ * call @code{pth_die} (this is the recommended behaviour).
+ *
+ * Normally this function returns a database handle. If the database
+ * connection fails, this function returns @code{NULL}.
+ *
+ * @code{db_commit} commits the current database transaction and
+ * begins a new one.
+ *
+ * @code{db_rollback} rolls back the current database transaction and
+ * begins a new one.
+ *
+ * If a database connection is closed without issuing either a commit
+ * or rollback (eg. the pool is deleted or the program exits), then
+ * the database will rollback the transaction. Some of this functionality
+ * relies on the database to do the right thing.
+ *
+ * @code{db_in_transaction} returns a flag indicating whether the
+ * handle is in a transaction. By this we mean that some commands have
+ * been executed on the handle, but neither @code{db_commit} nor
+ * @code{db_rollback} have been called.
+ *
+ * @code{get_db_handle} and @code{put_db_handle} are used to implement
+ * connection pooling.
+ *
+ * The @code{get_db_handle} function "borrows" a database handle from
+ * a process-wide pool of compatible handles. The thread has exclusive
+ * use of this handle until it either calls @code{put_db_handle} to
+ * "give it back" to the pool, or until the thread exits, at which
+ * point the handle is automatically returned to the pool. In either
+ * case, if the thread wishes to commit changes it has made, it must
+ * call @code{db_commit} on the handle before it is returned to the
+ * pool. If a handle is returned to the pool in an uncommitted state,
+ * then the connection is rolled back.
+ *
+ * Calling @code{get_db_handle} multiple times returns multiple
+ * different handles.
+ *
+ * Connection pooling is far more efficient than opening and closing
+ * connections using @code{new_db_handle}. However certain things are
+ * not possible with connection pooling: eg. creating a temporary
+ * table which persists across several threads, or executing a single
+ * transaction across multiple HTTP requests. In these (rare) cases,
+ * the program should do its own connection management.
+ *
+ * Note that there are separate pools for each @code{conninfo} string.
+ *
+ * @code{new_st_handle}, and the synonyms @code{st_prepare} and
+ * @code{st_prepare_cached} create a new statement and return the
+ * statement handle. The @code{query} parameter is the SQL query.
+ * '?' and '@' characters in the query may be used as placeholders
+ * (when they appear outside strings) for scalar and vector values
+ * respectively. The final parameter(s) are a list of the types
+ * of these placeholders, and must correspond exactly to the types
+ * passed in the @code{st_execute} call.
+ *
+ * If the @code{st_prepare_cached} form of statement creation is
+ * used, then the statement is cached in the database handle. At
+ * the moment, this doesn't make a lot of difference to performance,
+ * but when a future version of PostgreSQL properly supports prepared
+ * statements, this will make a big difference in performance by
+ * allowing query plans to be cached on the server. In practice it
+ * is almost always best to use @code{st_prepare_cached}. The only
+ * possible exception is when using statements which refer to
+ * temporary tables.
+ *
+ * @code{st_execute} executes the query with the given parameter
+ * list. The parameters are substituted for the '?' and '@' placeholders
+ * in the query, in order. The tyes of the parameters must correspond
+ * exactly to the types passed in the prepare call.
+ *
+ * @code{st_execute} may be called multiple times on the same
+ * statement handle. You do not need to (and should not, if possible)
+ * prepare the statement each time.
+ *
+ * @code{st_execute} returns the number of rows affected, for
+ * @code{INSERT} and @code{UPDATE} statements.
+ *
+ * If the command was an @CODE{INSERT} statement, then you can use
+ * @code{st_serial} as a convenience function to return the serial
+ * number assigned to the new row. The argument passed is the
+ * sequence name (usually @code{tablename_columnname_seq}).
+ *
+ * @code{st_bind} binds a local variable to a column in the
+ * result. The arguments are the column number (starting at 0),
+ * the local variable name, and the type of the variable.
+ *
+ * Unlike in Perl DBI, you may call @code{st_bind} at any point
+ * after preparing the statement, and bindings are persistent
+ * across executes.
+ *
+ * Possible types for the prepare, @code{st_execute} and
+ * @code{st_bind} calls: @code{DBI_INT}, @code{DBI_INT_OR_NULL},
+ * @code{DBI_STRING}, @code{DBI_BOOL}, @code{DBI_CHAR},
+ * @code{DBI_TIMESTAMP}, @code{DBI_INTERVAL}, @code{DBI_VECTOR_INT},
+ * @code{DBI_VECTOR_INT_OR_NULL}, @code{DBI_VECTOR_STRING},
+ * @code{DBI_VECTOR_BOOL}, @code{DBI_VECTOR_CHAR},
+ * @code{DBI_VECTOR_TIMESTAMP}, @code{DBI_VECTOR_INTERVAL}.
+ *
+ * @code{DBI_INT_OR_NULL} differs from an ordinary @code{DBI_INT}
+ * in that the integer value of @code{0} is treated as a @code{null}
+ * (useful when passed as a parameter to @code{st_execute}, not very
+ * useful otherwise).
+ *
+ * The @code{DBI_TIMESTAMP}, @code{DBI_INTERVAL}, @code{DBI_VECTOR_TIMESTAMP}
+ * and @code{DBI_VECTOR_INTERVAL} types refer respectively to the
+ * PostgreSQL database types @code{timestamp} and @code{interval}
+ * and the relevant structures @code{struct dbi_timestamp} and
+ * @code{struct dbi_interval} defined in @code{<pthr_dbi.h>}.
+ *
+ * @code{st_fetch} fetches the next result row from the query. It
+ * returns true if the result row was fetched, or false if there
+ * are no more rows. @code{st_fetch} returns the actual results
+ * in the variables bound to each column by @code{st_bind}. Any
+ * unbound columns are ignored.
+ *
+ * @code{st_fetch_all_rows} fetches all of the result rows
+ * in one go, returning a @code{vector} of @code{vector} of @code{char *}.
+ *
+ * @code{st_finish} is an optional step which you may use once you
+ * have finished with a statement handle. It frees up the memory
+ * used by the results held in the statement handle. (This memory
+ * would otherwise not be freed up until another @code{st_execute}
+ * or the pool containing the statement handle is deleted).
+ *
+ * The @code{db_(set|get)_debug} functions are used to update the
+ * state of the debug flag on a database handle. When this handle
+ * is set to true, then database statements which are executed are
+ * also printed out to @code{stderr}. The default is no debugging.
+ *
+ * It is not likely that we will support other databases in future
+ * unless something dramatic happens to PostgreSQL. Install and learn
+ * PostgreSQL and I promise that your life will be happier.
+ */
+extern db_handle new_db_handle (pool, const char *conninfo, int flags);
+extern void db_commit (db_handle);
+extern void db_rollback (db_handle);
+extern int db_in_transaction (db_handle);
+extern db_handle get_db_handle (const char *conninfo, int flags);
+extern void put_db_handle (db_handle dbh);
+extern st_handle new_st_handle (db_handle, const char *query, int flags, ...);
+#define st_prepare(db,query,types...) new_st_handle ((db), (query), 0 , ## types)
+#define st_prepare_cached(db,query,types...) new_st_handle ((db), (query), DBI_ST_CACHE , ## types)
+extern int st_execute (st_handle, ...);
+extern int st_serial (st_handle, const char *seq_name);
+extern void _st_bind (st_handle, int colidx, void *varptr, int type);
+#define st_bind(sth,colidx,var,type) _st_bind ((sth), (colidx), &(var), (type))
+extern int st_fetch (st_handle);
+extern vector st_fetch_all_rows (st_handle);
+extern void st_finish (st_handle);
+extern void db_set_debug (db_handle, int);
+extern int db_get_debug (db_handle);
+
+/* Flags for new_db_handle. */
+#define DBI_THROW_ERRORS 0x0001
+#define DBI_DEBUG 0x0002
+
+/* Flags for new_st_handle. */
+#define DBI_ST_CACHE 0x0001
+
+/* Database types. */
+/* NB. 0 must not be a valid type! */
+#define DBI_MIN_TYPE 1001
+#define DBI_INT 1001
+#define DBI_STRING 1002
+#define DBI_BOOL 1003
+#define DBI_CHAR 1004
+#define DBI_TIMESTAMP 1005
+#define DBI_INTERVAL 1006
+#define DBI_INT_OR_NULL 1007
+#define DBI_MAX_TYPE 1007
+#define DBI_VECTOR_INT DBI_INT
+#define DBI_VECTOR_STRING DBI_STRING
+#define DBI_VECTOR_BOOL DBI_BOOL
+#define DBI_VECTOR_CHAR DBI_CHAR
+#define DBI_VECTOR_TIMESTAMP DBI_TIMESTAMP
+#define DBI_VECTOR_INTERVAL DBI_INTERVAL
+#define DBI_VECTOR_INT_OR_NULL DBI_INT_OR_NULL
+
+/* For the timestamp and interval types, these structures are used. */
+struct dbi_timestamp
+{
+ int is_null; /* NULL if true (other fields will be zero). */
+ int year, month, day;
+ int hour, min, sec;
+ int microsecs;
+ int utc_offset;
+};
+
+struct dbi_interval
+{
+ int is_null; /* NULL if true (other fields will be zero). */
+ int secs, mins, hours;
+ int days, months, years;
+};
+
+#endif /* PTHR_DBI_H */
--- /dev/null
+/* FTP client library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_ftpc.c,v 1.7 2002/12/01 14:29:27 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <pstring.h>
+
+#include "pthr_pseudothread.h"
+#include "pthr_iolib.h"
+#include "pthr_ftpc.h"
+
+#define IS_1xx(c) ((c) >= 100 && (c) <= 199)
+#define IS_2xx(c) ((c) >= 200 && (c) <= 299)
+#define IS_3xx(c) ((c) >= 300 && (c) <= 399)
+#define IS_4xx(c) ((c) >= 400 && (c) <= 499)
+#define IS_5xx(c) ((c) >= 500 && (c) <= 599)
+#define IS_NOT_1xx(c) (!(IS_1xx(c)))
+#define IS_NOT_2xx(c) (!(IS_2xx(c)))
+#define IS_NOT_3xx(c) (!(IS_3xx(c)))
+#define IS_NOT_4xx(c) (!(IS_4xx(c)))
+#define IS_NOT_5xx(c) (!(IS_5xx(c)))
+
+#define REPLY_BUFFER_SIZE 2048
+
+struct ftpc
+{
+ /* General information about the connection. */
+ pool pool;
+ io_handle io;
+
+ int port;
+ int passive_mode;
+ int verbose;
+
+ struct sockaddr_in addr;
+ int pasv_data_port;
+
+ /* Reply buffer - stores the last line read from the server. */
+ char *reply;
+
+ /* These are kept for information only. */
+ char *server;
+ const char *username;
+ char *server_greeting;
+};
+
+static int eat_reply (ftpc);
+static int do_command (ftpc, const char *cmd, const char *arg);
+static io_handle open_data (ftpc, int data_sock);
+static int issue_port_or_pasv (ftpc f);
+
+ftpc
+new_ftpc (pool pool, const char *server)
+{
+ ftpc f;
+ char *t;
+ struct hostent *h;
+ int sock, code;
+
+ f = pcalloc (pool, 1, sizeof *f);
+ f->pool = pool;
+ f->server = pstrdup (pool, server);
+ f->reply = pmalloc (pool, REPLY_BUFFER_SIZE);
+
+#if 0
+ /* Just during testing, enable verbose mode always. */
+ f->verbose = 1;
+#endif
+
+ /* Name contains a port number? If so, extract it out first. */
+ t = strrchr (f->server, ':');
+ if (t)
+ {
+ *t = 0;
+ if (sscanf (t+1, "%d", &f->port) != 1)
+ {
+ fprintf (stderr, "bad port number: %s\n", t+1);
+ return 0;
+ }
+ }
+ else
+ {
+ struct servent *se;
+
+ /* Try to determine the default port for FTP. */
+ se = getservbyname ("ftp", "tcp");
+ if (se)
+ f->port = ntohs (se->s_port);
+ else
+ f->port = 21; /* Default FTP control port. */
+ }
+
+ /* Resolve the name of the server, if necessary. */
+ h = gethostbyname (f->server);
+ if (!h)
+ {
+ herror (f->server);
+ return 0;
+ }
+
+ f->addr.sin_family = AF_INET;
+ memcpy (&f->addr.sin_addr, h->h_addr, sizeof f->addr.sin_addr);
+ f->addr.sin_port = htons (f->port);
+
+ /* Create a socket. */
+ sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == -1)
+ {
+ perror ("socket");
+ return 0;
+ }
+
+ /* Set non-blocking. */
+ if (fcntl (sock, F_SETFL, O_NONBLOCK) == -1)
+ {
+ perror ("fcntl: O_NONBLOCK");
+ return 0;
+ }
+
+ /* Attempt to connect to the server. */
+ if (pth_connect (sock, (struct sockaddr *) &f->addr, sizeof f->addr)
+ == -1)
+ {
+ perror (f->server);
+ return 0;
+ }
+
+ /* Wrap up the socket in an IO handle. */
+ f->io = io_fdopen (sock);
+ if (!f->io) return 0;
+
+ /* Expect a response string back immediately from the server. */
+ code = eat_reply (f);
+
+ /* Save the server greeting. */
+ f->server_greeting = pstrdup (f->pool, f->reply+4);
+
+ if (IS_NOT_2xx (code))
+ {
+ fprintf (stderr, "bad response from server %d\n", code);
+ return 0;
+ }
+
+ return f;
+}
+
+int
+ftpc_set_passive_mode (ftpc f, int flag)
+{
+ return f->passive_mode = flag;
+}
+
+int
+ftpc_set_verbose (ftpc f, int flag)
+{
+ return f->verbose = flag;
+}
+
+void
+ftpc_perror (ftpc f, const char *msg)
+{
+ fprintf (stderr, "%s: %s\n", msg, f->reply);
+}
+
+int
+ftpc_login (ftpc f, const char *username, const char *password)
+{
+ int is_anonymous, code;
+
+ is_anonymous = !username || strcmp (username, "ftp") == 0 ||
+ strcmp (username, "anonymous") == 0;
+
+ if (is_anonymous)
+ {
+ if (!username)
+ username = "ftp";
+ if (!password)
+ {
+ char *logname = getenv ("LOGNAME");
+
+ if (!logname) logname = "nobody";
+ password = psprintf (f->pool, "%s@", logname);
+ }
+ }
+ else
+ {
+ if (!password) password = "";
+ }
+
+ f->username = username;
+
+ /* Send the USER command. */
+ code = do_command (f, "USER", username);
+ if (IS_NOT_3xx (code))
+ return -1;
+
+ /* Send the PASS command. */
+ code = do_command (f, "PASS", password);
+ if (IS_NOT_2xx (code))
+ return -1;
+
+ return 0;
+}
+
+int
+ftpc_type (ftpc f, char type)
+{
+ int code;
+ char t[2] = { type, 0 };
+
+ code = do_command (f, "TYPE", t);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+int
+ftpc_ascii (ftpc f)
+{
+ return ftpc_type (f, 'a');
+}
+
+int
+ftpc_binary (ftpc f)
+{
+ return ftpc_type (f, 'i');
+}
+
+int
+ftpc_cwd (ftpc f, const char *pathname)
+{
+ int code = do_command (f, "CWD", pathname);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+int
+ftpc_cdup (ftpc f)
+{
+ int code = do_command (f, "CDUP", 0);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+char *
+ftpc_pwd (ftpc f)
+{
+ int code, len;
+ char *path;
+
+ code = do_command (f, "PWD", 0);
+ if (IS_NOT_2xx (code)) return 0;
+
+ path = pstrdup (f->pool, &f->reply[4]);
+
+ /* Strip quotes around the pathname, if there are any. */
+ len = strlen (path);
+ if (len >= 2 && path[0] == '"' && path[len-1] == '"')
+ {
+ path[len-1] = '\0';
+ path++;
+ }
+
+ return path;
+}
+
+int
+ftpc_mkdir (ftpc f, const char *pathname)
+{
+ int code = do_command (f, "MKD", pathname);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+int
+ftpc_rmdir (ftpc f, const char *pathname)
+{
+ int code = do_command (f, "RMD", pathname);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+int
+ftpc_delete (ftpc f, const char *pathname)
+{
+ int code = do_command (f, "DELE", pathname);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+vector
+ftpc_ls (ftpc f, pool pool, const char *pathname)
+{
+ int code, data_sock;
+ io_handle io;
+ vector v;
+
+ /* Issue PORT or PASV command and get a socket. */
+ if ((data_sock = issue_port_or_pasv (f)) == -1)
+ return 0;
+
+ if (!pool) pool = f->pool;
+
+ v = new_vector (pool, const char *);
+
+ /* Issue the NLST command. */
+ code = do_command (f, "NLST -a", pathname);
+ if (IS_NOT_1xx (code)) { close (data_sock); return 0; }
+
+ /* Open data connection to server. */
+ io = open_data (f, data_sock);
+ if (!io) return 0;
+
+ /* Read the data, line at a time. */
+ while (io_fgets (f->reply, REPLY_BUFFER_SIZE, io, 0))
+ {
+ char *s = pstrdup (pool, f->reply);
+ vector_push_back (v, s);
+ }
+
+ /* Close the data connection. */
+ io_fclose (io);
+
+ /* Check return code. */
+ code = eat_reply (f);
+ return IS_2xx (code) ? v : 0;
+}
+
+vector
+ftpc_dir (ftpc f, pool pool, const char *pathname)
+{
+ int code, data_sock;
+ io_handle io;
+ vector v;
+
+ /* Issue PORT or PASV command and get a socket. */
+ if ((data_sock = issue_port_or_pasv (f)) == -1)
+ return 0;
+
+ if (!pool) pool = f->pool;
+
+ v = new_vector (pool, const char *);
+
+ /* Issue the LIST command. */
+ code = do_command (f, "LIST -a", pathname);
+ if (IS_NOT_1xx (code)) { close (data_sock); return 0; }
+
+ /* Open data connection to server. */
+ io = open_data (f, data_sock);
+ if (!io) return 0;
+
+ /* Read the data, line at a time. */
+ while (io_fgets (f->reply, REPLY_BUFFER_SIZE, io, 0))
+ {
+ char *s = pstrdup (pool, f->reply);
+ vector_push_back (v, s);
+ }
+
+ /* Close the data connection. */
+ io_fclose (io);
+
+ /* Check return code. */
+ code = eat_reply (f);
+ return IS_2xx (code) ? v : 0;
+}
+
+/* XXX Only works for binary transfers. */
+int
+ftpc_get (ftpc f, const char *remote_file, const char *local_file)
+{
+ int code, data_sock, n;
+ FILE *fp;
+ io_handle io;
+ char buf[1024];
+
+ /* Issue PORT or PASV command and get a socket. */
+ if ((data_sock = issue_port_or_pasv (f)) == -1)
+ return -1;
+
+ /* Issue the RETR command. */
+ code = do_command (f, "RETR", remote_file);
+ if (IS_NOT_1xx (code)) { close (data_sock); return -1; }
+
+ /* Open the local file. */
+ fp = fopen (local_file, "w");
+ if (fp == 0) { perror (local_file); return -1; }
+
+ /* Open data connection to server. */
+ io = open_data (f, data_sock);
+ if (!io) return -1;
+
+ /* Read the data, block at a time. */
+ while ((n = io_fread (buf, 1, sizeof buf, io)) > 0)
+ {
+ if (fwrite (buf, 1, n, fp) != n)
+ {
+ perror (local_file);
+ fclose (fp);
+ io_fclose (io);
+ return -1;
+ }
+ }
+
+ /* Close everything. */
+ fclose (fp);
+ io_fclose (io);
+
+ /* Check return code. */
+ code = eat_reply (f);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+/* XXX Only works for binary transfers. */
+int
+ftpc_put (ftpc f, const char *local_file, const char *remote_file)
+{
+ int code, data_sock, n;
+ FILE *fp;
+ io_handle io;
+ char buf[1024];
+
+ /* Issue PORT or PASV command and get a socket. */
+ if ((data_sock = issue_port_or_pasv (f)) == -1)
+ return -1;
+
+ /* Issue the STOR command. */
+ code = do_command (f, "STOR", remote_file);
+ if (IS_NOT_1xx (code)) { close (data_sock); return -1; }
+
+ /* Open the local file. */
+ fp = fopen (local_file, "r");
+ if (fp == 0) { perror (local_file); return -1; }
+
+ /* Open data connection to server. */
+ io = open_data (f, data_sock);
+ if (!io) return -1;
+
+ /* Read the data, block at a time. */
+ while ((n = fread (buf, 1, sizeof buf, fp)) > 0)
+ {
+ if (io_fwrite (buf, 1, n, io) != n)
+ {
+ perror (remote_file);
+ fclose (fp);
+ io_fclose (io);
+ return -1;
+ }
+ }
+
+ /* Close everything. */
+ fclose (fp);
+ io_fclose (io);
+
+ /* Check return code. */
+ code = eat_reply (f);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+int
+ftpc_quote (ftpc f, const char *cmd)
+{
+ int code;
+
+ code = do_command (f, cmd, 0);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+int
+ftpc_quit (ftpc f)
+{
+ int code;
+
+ code = do_command (f, "QUIT", 0);
+ io_fclose (f->io);
+ return IS_2xx (code) ? 0 : -1;
+}
+
+/* Execute a command, get the reply and return the code. */
+static int
+do_command (ftpc f, const char *cmd, const char *arg)
+{
+ if (f->verbose)
+ {
+ if (f->username)
+ fprintf (stderr, "%s@%s: ", f->username, f->server);
+ else
+ fprintf (stderr, "%s: ", f->server);
+
+ if (arg == 0)
+ fprintf (stderr, "%s\r\n", cmd);
+ else
+ fprintf (stderr, "%s %s\r\n", cmd, arg);
+ }
+
+ if (arg == 0)
+ io_fprintf (f->io, "%s\r\n", cmd);
+ else
+ io_fprintf (f->io, "%s %s\r\n", cmd, arg);
+
+ return eat_reply (f);
+}
+
+/* Eat the reply from the server, and throw it all away, except for
+ * the code.
+ */
+static int
+eat_reply (ftpc f)
+{
+ int code;
+
+ while (io_fgets (f->reply, REPLY_BUFFER_SIZE, f->io, 0))
+ {
+ if (f->verbose)
+ {
+ if (f->username)
+ fprintf (stderr, "%s@%s: %s\n", f->username, f->server, f->reply);
+ else
+ fprintf (stderr, "%s: %s\n", f->server, f->reply);
+ }
+
+ if (strlen (f->reply) < 4 ||
+ f->reply[0] < '1' || f->reply[0] > '5' ||
+ f->reply[1] < '0' || f->reply[1] > '9' ||
+ f->reply[2] < '0' || f->reply[2] > '9' ||
+ (f->reply[3] != ' ' && f->reply[3] != '-'))
+ pth_die ("badly formatted reply from server");
+
+ /* Is this the last line? */
+ if (f->reply[3] == ' ')
+ {
+ /* Yes: extract the code. */
+ code = 100 * (f->reply[0] - '0') +
+ 10 * (f->reply[1] - '0') + (f->reply[2] - '0');
+
+ return code;
+ }
+ }
+
+ pth_die ("server closed the connection unexpectedly");
+}
+
+/* Issue either PORT or PASV command to the server and get a socket. */
+static int
+issue_port_or_pasv (ftpc f)
+{
+ int code, a1, a2, a3, a4, p1, p2;
+ char *t;
+ struct sockaddr_in addr;
+ int addr_len, data_sock;
+
+ /* Create data socket. */
+ data_sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (data_sock == -1)
+ {
+ perror ("socket");
+ return -1;
+ }
+
+ /* Set non-blocking. */
+ if (fcntl (data_sock, F_SETFL, O_NONBLOCK) == -1) abort ();
+
+ if (f->passive_mode)
+ {
+ /* Issue PASV command to get a port number. */
+ code = do_command (f, "PASV", 0);
+ if (IS_NOT_2xx (code)) return -1;
+
+ /* The reply should say something like:
+ * 227 Entering Passive Mode (A,A,A,A,P,P)
+ * We ignore the address fields and concentrate on just
+ * the port number.
+ */
+ t = strchr (&f->reply[4], '(');
+ if (!t ||
+ sscanf (t, "( %d , %d , %d , %d , %d , %d )",
+ &a1, &a2, &a3, &a4, &p1, &p2) != 6)
+ {
+ close (data_sock);
+ fprintf (stderr, "cannot parse reply from PASV command\n");
+ return -1;
+ }
+
+ f->pasv_data_port = p1 * 256 + p2;
+ }
+ else
+ {
+ /* Bind the socket. */
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ if (bind (data_sock, (struct sockaddr *) &addr, sizeof addr) == -1)
+ {
+ close (data_sock);
+ perror ("bind");
+ return -1;
+ }
+
+ /* Listen on socket. */
+ if (listen (data_sock, 1) == -1)
+ {
+ close (data_sock);
+ perror ("listen");
+ return -1;
+ }
+
+ addr_len = sizeof addr;
+ if (getsockname (data_sock, (struct sockaddr *) &addr, &addr_len)
+ == -1)
+ {
+ close (data_sock);
+ perror ("getsockname");
+ return -1;
+ }
+
+ /* Issue a PORT command to tell the server our port number. */
+ a1 = (ntohl (f->addr.sin_addr.s_addr) >> 24) & 0xff;
+ a2 = (ntohl (f->addr.sin_addr.s_addr) >> 16) & 0xff;
+ a3 = (ntohl (f->addr.sin_addr.s_addr) >> 8) & 0xff;
+ a4 = ntohl (f->addr.sin_addr.s_addr) & 0xff;
+ p1 = ntohs (addr.sin_port) / 256;
+ p2 = ntohs (addr.sin_port) % 256;
+ t = psprintf (f->pool, "%d,%d,%d,%d,%d,%d", a1, a2, a3, a4, p1, p2);
+ code = do_command (f, "PORT", t);
+ if (IS_NOT_2xx (code)) return -1;
+ }
+
+ return data_sock;
+}
+
+/* Open a data connection to the server (which is expecting it). */
+static io_handle
+open_data (ftpc f, int data_sock)
+{
+ io_handle io;
+ struct sockaddr_in addr;
+ int addr_len, sock;
+
+ if (f->passive_mode)
+ {
+ /* Connect to server. */
+ f->addr.sin_port = htons (f->pasv_data_port);
+
+ if (pth_connect (data_sock, (struct sockaddr *) &f->addr,
+ sizeof f->addr) == -1)
+ {
+ close (data_sock);
+ perror (f->server);
+ return 0;
+ }
+
+ io = io_fdopen (data_sock);
+ if (!io) return 0;
+ }
+ else
+ {
+ addr_len = sizeof addr;
+
+ /* Accept connection from server. */
+ if ((sock = pth_accept (data_sock,
+ (struct sockaddr *) &addr, &addr_len)) == -1)
+ {
+ close (data_sock);
+ perror ("accept");
+ return 0;
+ }
+
+ close (data_sock);
+
+ /* Verify this connection comes from the server. */
+ if (addr.sin_addr.s_addr != f->addr.sin_addr.s_addr)
+ {
+ fprintf (stderr, "connection accepted, but not from FTP server");
+ return 0;
+ }
+
+ io = io_fdopen (sock);
+ if (!io) return 0;
+ }
+
+ return io;
+}
--- /dev/null
+/* FTP client library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_ftpc.h,v 1.5 2002/12/01 14:29:27 rich Exp $
+ */
+
+#ifndef PTHR_FTPC_H
+#define PTHR_FTPC_H
+
+#include <pool.h>
+#include <vector.h>
+
+#include <pthr_pseudothread.h>
+#include <pthr_iolib.h>
+
+struct ftpc;
+typedef struct ftpc *ftpc;
+
+/* Function: new_ftpc - Create a new FTP client object.
+ *
+ * Create a new FTP client object, connected to the FTP server
+ * called @code{server}. The @code{server} may be an IP address or a hostname.
+ * If the @code{server} name ends with @code{:port} then @code{port}
+ * is the port number to connect to.
+ *
+ * The default mode for new connections is active mode. Call
+ * @ref{ftpc_set_mode(3)} to change the mode.
+ *
+ * See also: @ref{ftpc_login(3)}, @ref{ftpc_set_mode(3)}.
+ */
+extern ftpc new_ftpc (pool, const char *server);
+
+/* Function: ftpc_set_passive_mode - Change to/from active or passive mode.
+ *
+ * If @code{flag} is true, all future connections on this @code{ftpc}
+ * object will be in passive mode. If @code{flag} is false, all
+ * future connections will be in active mode.
+ *
+ * Passive mode is required by a few servers and some firewalls.
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern int ftpc_set_passive_mode (ftpc ftpc, int flag);
+
+extern int ftpc_set_verbose (ftpc ftpc, int flag);
+
+extern void ftpc_perror (ftpc f, const char *msg);
+
+/* Function: ftpc_login - Log onto the FTP server.
+ *
+ * Attempt to log onto the FTP server as user @code{username} with
+ * password @code{password}. If @code{username} is @code{NULL},
+ * @code{"ftp"} or @code{"anonymous"}, then log in anonymously.
+ * For anonymous logins, the @code{password} may be @code{NULL},
+ * in which case the environment variable @code{LOGNAME} followed
+ * by a single @code{@} character is used as the password.
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern int ftpc_login (ftpc ftpc, const char *username, const char *password);
+
+/* Function: ftpc_type - Set connection type.
+ * Function: ftpc_ascii
+ * Function: ftpc_binary
+ *
+ * @code{ftpc_type} sets the connection type. Most FTP servers only
+ * support type 'a' (ASCII) or type 'i' (bInary), although esoteric
+ * FTP servers might support 'e' (EBCDIC).
+ *
+ * @code{ftpc_ascii} sets the type to ASCII.
+ *
+ * @code{ftpc_binary} sets the type to bInary.
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern int ftpc_type (ftpc ftpc, char type);
+extern int ftpc_ascii (ftpc ftpc);
+extern int ftpc_binary (ftpc ftpc);
+
+/* Function: ftpc_cwd - Change directory on the server.
+ * Function: ftpc_cdup
+ *
+ * @code{ftpc_cwd} changes the directory to @code{pathname}.
+ *
+ * @code{ftpc_cdup} moves to the parent directory. On most Unix-like
+ * FTP servers this is equivalent to doing @code{CWD ..}
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern int ftpc_cwd (ftpc ftpc, const char *pathname);
+extern int ftpc_cdup (ftpc ftpc);
+
+/* Function: ftpc_pwd - Return current directory on the server.
+ *
+ * @code{ftpc_pwd} returns the current directory on the server.
+ *
+ * Returns: The current directory, as a string allocated in the
+ * pool, or NULL if the command fails. If a fatal error occurs
+ * with the connection, an exception is thrown.
+ */
+extern char *ftpc_pwd (ftpc ftpc);
+
+/* Function: ftpc_mkdir - Create or remove directories on the server.
+ * Function: ftpc_rmdir
+ *
+ * @code{ftpc_mkdir} creates a directory called @code{pathname}
+ * on the server. @code{ftpc_rmdir} removes a directory called
+ * @code{pathname} on the server.
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern int ftpc_mkdir (ftpc ftpc, const char *pathname);
+extern int ftpc_rmdir (ftpc ftpc, const char *pathname);
+
+/* Function: ftpc_delete - Delete a file on the server.
+ *
+ * @code{ftpc_delete} deletes a file called @code{pathname}
+ * on the server.
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern int ftpc_delete (ftpc ftpc, const char *pathname);
+
+/* Function: ftpc_ls - List the contents of a directory on the server.
+ * Function: ftpc_dir
+ *
+ * @code{ftpc_ls} and @code{ftpc_dir} list the contents of either
+ * the current directory (if @code{pathname} is @code{NULL}) or
+ * else another directory @code{pathname}.
+ *
+ * @code{ftpc_ls} issues the command @code{NLST -a}, returning a
+ * vector of strings giving the name of each file.
+ *
+ * @code{ftpc_dir} issues the command @code{LIST -a}, returning
+ * a human-readable list of filenames, similar to issuing the
+ * @code{ls -al} command in Unix.
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern vector ftpc_ls (ftpc ftpc, pool, const char *pathname);
+extern vector ftpc_dir (ftpc ftpc, pool, const char *pathname);
+
+/* Function: ftpc_get - Download or upload a file.
+ * Function: ftpc_put
+ *
+ * @code{ftpc_get} attempts to download @code{remote_file} from the
+ * server and store it in a local file called @code{local_file}.
+ *
+ * @code{ftpc_put} attempts to upload a file called @code{local_file}
+ * to the server and store it in a file on the server called
+ * @code{remote_file}.
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern int ftpc_get (ftpc ftpc, const char *remote_file, const char *local_file);
+extern int ftpc_put (ftpc ftpc, const char *local_file, const char *remote_file);
+
+/* Function: ftpc_quote - Issue a command to the FTP server.
+ *
+ * @code{ftpc_quote} issues a command directly to the FTP server.
+ * This function may be used for issuing @code{SITE} commands for
+ * example.
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern int ftpc_quote (ftpc ftpc, const char *cmd);
+
+/* Function: ftpc_quit - Nicely disconnect from the FTP server.
+ *
+ * @code{ftpc_quit} sends a @code{QUIT} command to the FTP server
+ * and then drops the network connection. After using this function,
+ * the @code{ftpc} object associated with the connection is
+ * invalid and should not be used.
+ *
+ * Returns: 0 if successful, -1 if the attempt failed.
+ * If a fatal error occurs with the connection, an exception
+ * is thrown.
+ */
+extern int ftpc_quit (ftpc ftpc);
+
+#endif /* PTHR_FTPC_H */
--- /dev/null
+/* HTTP library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_http.c,v 1.16 2003/02/02 18:05:31 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include <pool.h>
+#include <hash.h>
+#include <vector.h>
+#include <pstring.h>
+#include <pre.h>
+
+#include "pthr_reactor.h"
+#include "pthr_iolib.h"
+#include "pthr_cgi.h"
+#include "pthr_http.h"
+
+#define MAX_LINE_LENGTH 4096
+#define CRLF "\r\n"
+
+static const char *servername = PACKAGE "-httpd/" VERSION;
+static FILE *log_fp = 0;
+
+struct http_request
+{
+ pool pool; /* Pool for memory allocations. */
+ time_t t; /* Request time. */
+ int method; /* Method. */
+ const char *original_url; /* Original request URL (used for logging). */
+ const char *url; /* The URL. */
+ const char *path; /* Path only. */
+ const char *query_string; /* Query string only. */
+ int is_http09; /* Is it an HTTP/0.9 request? */
+ int major, minor; /* Major/minor version numbers. */
+ sash headers; /* Headers. */
+};
+
+struct http_response
+{
+ pool pool; /* Pool. */
+ http_request request; /* The original request. */
+ int code; /* Response code. */
+ io_handle io; /* IO handle. */
+ unsigned extra_headers; /* Bitmap of extra headers to send. */
+#define _HTTP_XH_SERVER 1
+#define _HTTP_XH_DATE 2
+#define _HTTP_XH_CONTENT_TYPE 4
+#define _HTTP_XH_CONNECTION 8
+#define _HTTP_XH_CONTENT_LENGTH 16
+#define _HTTP_XH_TRANSFER_ENCODING_CHUNKED 32
+ int content_length; /* Content length (if seen). */
+};
+
+#define _HTTP_XH_LENGTH_DEFINED (_HTTP_XH_CONTENT_LENGTH|_HTTP_XH_TRANSFER_ENCODING_CHUNKED)
+
+static void parse_url (http_request h);
+static void do_logging (http_response h);
+
+const char *
+http_get_servername (void)
+{
+ return servername;
+}
+
+const char *
+http_set_servername (const char *new_server_name)
+{
+ return servername = new_server_name;
+}
+
+http_request
+new_http_request (pool pool, io_handle io)
+{
+ char line[MAX_LINE_LENGTH];
+ char *start_url, *end_url, *end_key;
+ char *key, *value;
+
+ http_request h = pmalloc (pool, sizeof *h);
+
+ memset (h, 0, sizeof *h);
+ h->pool = pool;
+ h->headers = new_sash (h->pool);
+ h->t = reactor_time / 1000;
+
+ /* Read the first line of the request. As a sop to Netscape 4, ignore
+ * blank lines (see note below about Netscape generating extra CRLFs
+ * after POST requests).
+ */
+ again:
+ if (!io_fgets (line, sizeof line, io, 0))
+ return 0;
+ if (line[0] == '\0') goto again;
+
+ /* Previous versions of the server supported only GET requests. We
+ * now support GET and HEAD (as required by RFC 2616 section 5.1.1)
+ * and POST (see: http://www.w3.org/MarkUp/html-spec/html-spec_8.html).
+ * See RFC 2616 section 14.7 for a description of the Allow header.
+ */
+ if (strncmp (line, "GET ", 4) == 0)
+ {
+ h->method = HTTP_METHOD_GET;
+ start_url = line + 4;
+ }
+ else if (strncmp (line, "HEAD ", 5) == 0)
+ {
+ h->method = HTTP_METHOD_HEAD;
+ start_url = line + 5;
+ }
+ else if (strncmp (line, "POST ", 5) == 0)
+ {
+ h->method = HTTP_METHOD_POST;
+ start_url = line + 5;
+ }
+ else
+ {
+ const char *msg = "bad method (not a GET, HEAD or POST request)";
+
+ syslog (LOG_INFO, msg);
+ io_fputs ("HTTP/1.1 405 Method not allowed" CRLF
+ "Allow: GET, HEAD, POST" CRLF
+ CRLF, io);
+ pth_die (msg);
+ }
+
+ /* Parse out the URL. According to RFC 2616 section 5.1.2 we ought
+ * to be able to support absolute URIs in requests. At the moment
+ * we can't. Luckily no HTTP/1.1 clients should generate them.
+ */
+ end_url = strchr (start_url, ' ');
+ if (end_url == 0)
+ {
+ /* It's an HTTP/0.9 request! */
+ h->original_url = h->url = pstrdup (h->pool, start_url);
+ parse_url (h);
+ h->is_http09 = 1;
+ h->major = 0;
+ h->minor = 9;
+
+ return h;
+ }
+
+ /* It's an HTTP > 0.9 request, so there must be headers following. */
+ *end_url = '\0';
+ h->original_url = h->url = pstrdup (h->pool, start_url);
+ parse_url (h);
+
+ /* Check HTTP version number. */
+ if (strncmp (end_url+1, "HTTP/", 5) != 0 ||
+ !isdigit ((int) *(end_url+6)) ||
+ *(end_url+7) != '.' ||
+ !isdigit ((int) *(end_url+8)))
+ {
+ const char *msg = "badly formed request -- no HTTP/x.y";
+
+ syslog (LOG_INFO, msg);
+ io_fputs ("HTTP/1.1 400 Badly formed request" CRLF, io);
+ pth_die (msg);
+ }
+
+ h->is_http09 = 0;
+ h->major = *(end_url+6) - '0';
+ h->minor = *(end_url+8) - '0';
+
+ /* Read the headers. */
+ for (;;)
+ {
+ if (!io_fgets (line, sizeof line, io, 0))
+ {
+ const char *msg = "unexpected EOF reading headers";
+
+ syslog (LOG_INFO, msg);
+ io_fputs ("HTTP/1.1 400 Unexpected EOF in request" CRLF, io);
+ pth_die (msg);
+ }
+
+ /* End of headers? */
+ if (line[0] == '\0')
+ break;
+
+ /* Check that the header has the form Key: Value. */
+ end_key = strchr (line, ':');
+ if (end_key == 0)
+ {
+ const char *msg = "badly formed header in request";
+
+ syslog (LOG_INFO, msg);
+ io_fputs ("HTTP/1.1 400 Badly formed header" CRLF, io);
+ pth_die (msg);
+ }
+
+ /* Split up the key and value and store them. */
+ *end_key = '\0';
+
+ /* Find the beginning of the value field.
+ * RFC822 / RFC2616 does not require a space after the colon
+ * and effectively says to ignore linear white space after the
+ * colon. So remove that here.
+ */
+ end_key++;
+ ptrim (end_key);
+
+ key = pstrdup (h->pool, line);
+ value = pstrdup (h->pool, end_key);
+
+ /* Canonicalize the key (HTTP header keys are case insensitive). */
+ pstrlwr (key);
+
+ sash_insert (h->headers, key, value);
+ }
+
+ return h;
+}
+
+/* This function is called just after h->url has been set. It
+ * parses out the path and query string parameters from the URL
+ * and stores them separately.
+ */
+static void
+parse_url (http_request h)
+{
+ if (h->method == HTTP_METHOD_POST)
+ {
+ h->path = h->url;
+ h->query_string = 0;
+ }
+ else /* GET or HEAD requests. */
+ {
+ char *p, *t;
+
+ p = pstrdup (h->pool, h->url);
+ t = strchr (p, '?');
+
+ if (t == 0) /* No query string. */
+ {
+ h->path = p;
+ h->query_string = 0;
+ return;
+ }
+
+ *t = '\0';
+ h->path = p; /* Path is everything before '?' char. */
+ h->query_string = t+1; /* Query string is everything after it. */
+ }
+}
+
+time_t
+http_request_time (http_request h)
+{
+ return h->t;
+}
+
+const char *
+http_request_get_url (http_request h)
+{
+ return h->url;
+}
+
+void
+http_request_set_url (http_request h, const char *url)
+{
+ h->url = url;
+ parse_url (h);
+}
+
+const char *
+http_request_path (http_request h)
+{
+ return h->path;
+}
+
+const char *
+http_request_query_string (http_request h)
+{
+ return h->query_string;
+}
+
+int
+http_request_method (http_request h)
+{
+ return h->method;
+}
+
+const char *
+http_request_method_string (http_request h)
+{
+ switch (h->method)
+ {
+ case HTTP_METHOD_GET: return "GET";
+ case HTTP_METHOD_HEAD: return "HEAD";
+ case HTTP_METHOD_POST: return "POST";
+ }
+ abort ();
+}
+
+int
+http_request_is_HEAD (http_request h)
+{
+ return h->method == HTTP_METHOD_HEAD;
+}
+
+void
+http_request_version (http_request h, int *major, int *minor)
+{
+ *major = h->major;
+ *minor = h->minor;
+}
+
+int
+http_request_nr_headers (http_request h)
+{
+ return sash_size (h->headers);
+}
+
+vector
+http_request_get_headers (http_request h)
+{
+ vector keys = sash_keys (h->headers);
+ vector r = new_vector (h->pool, struct http_header);
+ int i;
+
+ for (i = 0; i < vector_size (keys); ++i)
+ {
+ struct http_header header;
+ char *key;
+
+ vector_get (keys, i, key);
+ header.key = key;
+ sash_get (h->headers, key, header.value);
+
+ vector_push_back (r, header);
+ }
+
+ return r;
+}
+
+const char *
+http_request_get_header (http_request h, const char *key)
+{
+ char *k = alloca (strlen (key) + 1);
+ const char *v;
+
+ /* Canonicalize the key. */
+ strcpy (k, key);
+ pstrlwr (k);
+
+ sash_get (h->headers, k, v);
+
+ return v;
+}
+
+const char *
+http_request_get_cookie (http_request h, const char *key)
+{
+ const char *cookie_hdr;
+ static pcre *re = 0;
+ vector v;
+ int keylen = strlen (key);
+ int i;
+
+ cookie_hdr = http_request_get_header (h, "Cookie");
+ if (!cookie_hdr) return 0;
+
+ /* Split it into pieces at whitespace, commas or semi-colons. */
+ if (!re) re = precomp (global_pool, "[ \t\n,;]+", 0);
+ v = pstrresplit (h->pool, cookie_hdr, re);
+
+ for (i = 0; i < vector_size (v); ++i)
+ {
+ char *str;
+
+ vector_get (v, i, str);
+
+ /* It'll either be NAME=VALUE or $Path="?VALUE"?. We actually
+ * don't care too much about the Path or Domain, so ignore them.
+ */
+ if (str[0] != '$')
+ {
+ /* Matching name? */
+ if (strncasecmp (str, key, keylen) == 0
+ && str[keylen] == '=')
+ {
+ return cgi_unescape (h->pool, &str[keylen+1]);
+ }
+ }
+ }
+
+ return 0;
+}
+
+http_response
+new_http_response (pool pool, http_request request,
+ io_handle io,
+ int code, const char *msg)
+{
+ http_response h = pmalloc (pool, sizeof *h);
+
+ memset (h, 0, sizeof *h);
+
+ h->pool = pool;
+ h->request = request;
+ h->code = code;
+ h->io = io;
+ h->extra_headers = ~0;
+ h->content_length = 0;
+
+ /* Set the output mode to fully buffered for efficiency. */
+ io_setbufmode (h->io, IO_MODE_FULLY_BUFFERED);
+
+ /* HTTP/0.9? No response line or headers. */
+ if (request->is_http09) return h;
+
+ /* Write the response line. */
+ io_fprintf (io, "HTTP/1.1 %d %s" CRLF, code, msg);
+
+ return h;
+}
+
+void
+http_response_send_header (http_response h,
+ const char *key, const char *value)
+{
+ /* HTTP/0.9? No response line or headers. */
+ if (h->request->is_http09) return;
+
+ io_fputs (key, h->io);
+ io_fputs (": ", h->io);
+ io_fputs (value, h->io);
+ io_fputs (CRLF, h->io);
+
+ /* Check for caller sending known header key and remove that
+ * from the bitmap so we don't overwrite caller's header
+ * in http_response_end_headers function.
+ */
+ if (strcasecmp (key, "Server") == 0)
+ h->extra_headers &= ~_HTTP_XH_SERVER;
+ if (strcasecmp (key, "Date") == 0)
+ h->extra_headers &= ~_HTTP_XH_DATE;
+ if (strcasecmp (key, "Content-Type") == 0)
+ h->extra_headers &= ~_HTTP_XH_CONTENT_TYPE;
+ if (strcasecmp (key, "Connection") == 0)
+ h->extra_headers &= ~_HTTP_XH_CONNECTION;
+ if (strcasecmp (key, "Content-Length") == 0)
+ {
+ h->extra_headers &= ~_HTTP_XH_CONTENT_LENGTH;
+ sscanf (value, "%d", &h->content_length);
+ }
+ if (strcasecmp (key, "Transfer-Encoding") == 0 &&
+ strcasecmp (value, "chunked") == 0)
+ h->extra_headers &= ~_HTTP_XH_TRANSFER_ENCODING_CHUNKED;
+}
+
+void
+http_response_send_headers (http_response h, ...)
+{
+ va_list args;
+ const char *k, *v;
+
+ /* HTTP/0.9? No response line or headers. */
+ if (h->request->is_http09) return;
+
+ va_start (args, h);
+ while ((k = va_arg (args, const char *)) != 0)
+ {
+ v = va_arg (args, const char *);
+ http_response_send_header (h, k, v);
+ }
+ va_end (args);
+}
+
+int
+http_response_end_headers (http_response h)
+{
+ int close = 1;
+
+ /* HTTP/0.9? No response line or headers. */
+ if (h->request->is_http09)
+ goto out;
+
+ /* Send any remaining headers. */
+ if (h->extra_headers & _HTTP_XH_SERVER)
+ http_response_send_header (h, "Server", servername);
+
+#if HAVE_STRFTIME && HAVE_GMTIME
+ /* See RFC 2616 section 3.3.1. */
+ if (h->extra_headers & _HTTP_XH_DATE)
+ {
+ time_t t;
+ char s[64];
+
+ t = http_request_time (h->request);
+ strftime (s, sizeof s, "%a, %d %b %Y %H:%M:%S GMT", gmtime (&t));
+ http_response_send_header (h, "Date", s);
+ }
+#endif
+
+ /* This is not correct: see RFC 2616 section 3.4.1 for more details. */
+ if (h->extra_headers & _HTTP_XH_CONTENT_TYPE)
+ http_response_send_header (h, "Content-Type", "text/plain");
+
+ if (h->extra_headers & _HTTP_XH_CONNECTION)
+ {
+ /* Deal with persistent connections (see RFC 2616 section 8.1). */
+
+ /* Server app must have sent a Content-Length header, otherwise
+ * the connection must close anyway. (RFC 2616 sections 4.4, 8.1.2.1)
+ */
+ if ((h->extra_headers & _HTTP_XH_LENGTH_DEFINED) ==
+ _HTTP_XH_LENGTH_DEFINED)
+ {
+ close = 1;
+ http_response_send_header (h, "Connection", "close");
+ }
+ else
+ {
+ /* Otherwise look for the Connection: header in the request. */
+ const char *conn_hdr
+ = http_request_get_header (h->request, "Connection");
+
+ if (conn_hdr)
+ {
+ if (strcasecmp (conn_hdr, "close") == 0)
+ {
+ close = 1;
+ http_response_send_header (h, "Connection", "close");
+ }
+ else if (strcasecmp (conn_hdr, "keep-alive") == 0)
+ {
+ close = 0;
+ http_response_send_header (h, "Connection", "keep-alive");
+ }
+ else
+ {
+ close = 1;
+ http_response_send_header (h, "Connection", "close");
+ }
+ }
+ else
+ {
+ /* Assume close for HTTP/1.0 clients, keep-alive for HTTP/1.1
+ * clients.
+ */
+ if (h->request->major == 1 && h->request->minor >= 1)
+ {
+ close = 0;
+ http_response_send_header (h, "Connection", "keep-alive");
+ }
+ else
+ {
+ close = 1;
+ http_response_send_header (h, "Connection", "close");
+ }
+ }
+ }
+ }
+
+ io_fputs (CRLF, h->io);
+
+ out:
+ if (log_fp) do_logging (h);
+
+ return close;
+}
+
+void
+http_response_write_chunk (http_response h, const char *data, int length)
+{
+ io_fprintf (h->io, "%X" CRLF, length);
+ io_fwrite (data, 1, length, h->io);
+ io_fprintf (h->io, CRLF);
+}
+
+void
+http_response_write_chunk_string (http_response h, const char *string)
+{
+ io_fprintf (h->io, "%X" CRLF "%s" CRLF, strlen (string), string);
+}
+
+void
+http_response_write_chunk_end (http_response h)
+{
+ io_fputs ("0" CRLF, h->io);
+}
+
+FILE *
+http_set_log_file (FILE *fp)
+{
+ return log_fp = fp;
+}
+
+FILE *
+http_get_log_file (void)
+{
+ return log_fp;
+}
+
+static void
+do_logging (http_response h)
+{
+#if HAVE_STRFTIME && HAVE_GMTIME
+ char time_str[64];
+ struct tm *tm;
+ time_t t;
+#else
+ const char *time_str;
+#endif
+ const char *referer;
+ const char *method;
+ const char *url;
+ const char *user_agent;
+ int major, minor;
+ struct sockaddr_in addr;
+ socklen_t addrlen;
+ const char *addr_str;
+ int port;
+
+ /* Get the request time. */
+#if HAVE_STRFTIME && HAVE_GMTIME
+ t = http_request_time (h->request);
+ tm = gmtime (&t);
+ strftime (time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", tm);
+#else
+ time_str = "- -";
+#endif
+
+ /* Get the referer (sic) header. */
+ if ((referer = http_request_get_header (h->request, "Referer")) == 0)
+ referer = "-";
+
+ /* Get the user agent header. */
+ if ((user_agent = http_request_get_header (h->request, "User-Agent")) == 0)
+ user_agent = "-";
+
+ /* Get the URL. */
+ method = http_request_method_string (h->request);
+ url = h->request->original_url;
+ http_request_version (h->request, &major, &minor);
+
+ /* Get the address and port number of the peer (client). */
+ addrlen = sizeof addr;
+ getpeername (io_fileno (h->io), (struct sockaddr *) &addr, &addrlen);
+ addr_str = inet_ntoa (addr.sin_addr);
+ port = ntohs (addr.sin_port);
+
+ fprintf (log_fp,
+ "%s %s:%d \"%s %s HTTP/%d.%d\" %d %d \"%s\" \"%s\"\n",
+ time_str, addr_str, port, method, url, major, minor,
+ h->code, h->content_length, referer, user_agent);
+ fflush (log_fp);
+}
--- /dev/null
+/* HTTP library.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_http.h,v 1.15 2002/12/08 13:41:07 rich Exp $
+ */
+
+#ifndef PTHR_HTTP_H
+#define PTHR_HTTP_H
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <setjmp.h>
+#include <time.h>
+
+#include <pool.h>
+#include <vector.h>
+#include <hash.h>
+
+#include "pthr_pseudothread.h"
+#include "pthr_iolib.h"
+
+struct http_request;
+typedef struct http_request *http_request;
+
+struct http_response;
+typedef struct http_response *http_response;
+
+/* A vector of struct http_header is returned from the
+ * HTTP_REQUEST_GET_HEADERS call.
+ */
+struct http_header { const char *key, *value; };
+
+/* Supported methods. */
+#define HTTP_METHOD_GET 1
+#define HTTP_METHOD_HEAD 2
+#define HTTP_METHOD_POST 3
+
+/* Function: http_get_servername - get and set the server name string
+ * Function: http_set_servername
+ *
+ * Get and set the server name (which is sent in the @code{Server})
+ * header by the server when it responds to requests.
+ *
+ * The default string is @code{pthrlib-httpd/version}.
+ *
+ * See also: @ref{new_http_request(3)}, @ref{new_http_response(3)},
+ * @ref{new_cgi(3)}.
+ */
+const char *http_get_servername (void);
+const char *http_set_servername (const char *new_server_name);
+
+/* Function: new_http_request - functions for parsing HTTP requests
+ * Function: http_request_time
+ * Function: http_request_get_url
+ * Function: http_request_set_url
+ * Function: http_request_path
+ * Function: http_request_query_string
+ * Function: http_request_method
+ * Function: http_request_method_string
+ * Function: http_request_is_HEAD
+ * Function: http_request_version
+ * Function: http_request_nr_headers
+ * Function: http_request_get_headers
+ * Function: http_request_get_header
+ * Function: http_request_get_cookie
+ *
+ * These functions allow you to efficiently parse incoming
+ * HTTP requests from conforming HTTP/0.9, HTTP/1.0 and HTTP/1.1
+ * clients. The request parser understands GET, HEAD and POST
+ * requests and conforms as far as possible to RFC 2616.
+ *
+ * @code{new_http_request} creates a new request object, parsing
+ * the incoming request on the given @code{io_handle}. If the
+ * stream closes at the beginning of the request the function
+ * returns @code{NULL}. If the request is faulty, then the
+ * library prints a message to syslog and throws an exception
+ * by calling @ref{pth_die(3)}. Otherwise it initializes a complete
+ * @code{http_request} object and returns it.
+ *
+ * @code{http_request_time} returns the timestamp of the incoming
+ * request.
+ *
+ * @code{http_request_get_url} returns the complete URL of the request.
+ *
+ * @code{http_request_path} returns just the path component of
+ * the URL (ie. without the query string if there was one).
+ * @code{http_request_query_string} returns just the query string
+ * (for GET requests only). Do not do your own parsing of query
+ * strings: there is a CGI library built into pthrlib (see:
+ * @ref{new_cgi(3)}).
+ *
+ * @code{http_request_set_url} updates the URL (and hence path
+ * and query string). It is used by servers which support internal
+ * redirects, such as @code{rws}.
+ *
+ * @code{http_request_method} returns the method, one of
+ * @code{HTTP_METHOD_GET}, @code{HTTP_METHOD_HEAD} or
+ * @code{HTTP_METHOD_POST}. @code{http_request_is_HEAD} is
+ * just a quick way of testing if the method is a HEAD
+ * request. @code{http_request_method_string} returns the
+ * method as a string rather than a coded number.
+ *
+ * @code{http_request_version} returns the major and minor
+ * numbers of the HTTP request (eg. major = 1, minor = 0 for
+ * a HTTP/1.0 request).
+ *
+ * @code{http_request_nr_headers}, @code{http_request_get_headers}
+ * and @code{http_request_get_header} return the number of
+ * HTTP headers, the list of HTTP headers and a particular
+ * HTTP header (if it exists). @code{http_request_get_headers}
+ * returns a @code{vector} or @code{struct http_header}. This
+ * structure contains at least two fields called @code{key} and
+ * @code{value}. HTTP header keys are case insensitive when
+ * searching, and you will find that the list of keys returned
+ * by @code{http_request_get_headers} has been converted to
+ * lowercase.
+ *
+ * @code{http_request_get_cookie} gets a named browser cookie
+ * sent in the request. It returns the value of the cookie
+ * or @code{NULL} if no such named cookie was sent by the browser.
+ *
+ * To send a cookie to the browser, you should generate and send
+ * a @code{Set-Cookie} header with the appropriate content. Note
+ * that not all browsers support cookies.
+ *
+ * See also: @ref{new_http_response(3)}, @ref{new_cgi(3)},
+ * @ref{new_pseudothread(3)}, @ref{io_fdopen(3)}, RFC 2616.
+ */
+extern http_request new_http_request (pool, io_handle);
+extern time_t http_request_time (http_request);
+extern const char *http_request_get_url (http_request);
+extern void http_request_set_url (http_request, const char *new_url);
+extern const char *http_request_path (http_request);
+extern const char *http_request_query_string (http_request);
+extern int http_request_method (http_request);
+extern const char *http_request_method_string (http_request);
+extern int http_request_is_HEAD (http_request);
+extern void http_request_version (http_request, int *major, int *minor);
+extern int http_request_nr_headers (http_request);
+extern vector http_request_get_headers (http_request);
+extern const char *http_request_get_header (http_request h, const char *key);
+extern const char *http_request_get_cookie (http_request h, const char *key);
+
+/* Function: new_http_response - functions for sending HTTP responses
+ * Function: http_response_send_header
+ * Function: http_response_send_headers
+ * Function: http_response_end_headers
+ * Function: http_response_write_chunk
+ * Function: http_response_write_chunk_string
+ * Function: http_response_write_chunk_end
+ *
+ * These functions allow you to efficiently generate outgoing HTTP
+ * responses.
+ *
+ * @code{new_http_response} generates a new HTTP response object and
+ * returns it. @code{code} is the HTTP response code (see RFC 2616
+ * for a list of codes), and @code{msg} is the HTTP response message.
+ *
+ * @code{http_response_send_header} sends a single HTTP header back
+ * to the client. The header is constructed by concatenating
+ * @code{key}, @code{": "}, @code{value} and @code{CR LF}.
+ *
+ * @code{http_response_send_headers} sends back several headers in
+ * a single call. The arguments to this function are a list of
+ * @code{key}, @code{value} pairs followed by a single @code{NULL}
+ * argument which terminates the list.
+ *
+ * @code{http_response_end_headers} ends the header list. It causes
+ * the code to emit any missing-but-required headers and then send
+ * the final @code{CR LF} characters.
+ *
+ * @code{http_response_write_chunk}, @code{http_response_write_chunk_string}
+ * and @code{http_response_write_chunk_end}
+ * allow the caller to use chunked encoding, which is an
+ * alternative to sending the @code{Content-Length} header. To enable
+ * chunked encoding, the application should first call
+ * @code{http_response_send_header (h, "Transfer-Encoding", "chunked");}.
+ * Then, instead of writing directly to @code{io} to send data,
+ * the application should call @code{http_response_write_chunk}
+ * or @code{http_response_write_chunk_string} to write each block
+ * (or string) of data. At the end of the data, the application
+ * should call @code{http_response_write_chunk_end}.
+ * (Steve Atkins implemented chunked encoding).
+ *
+ * See also: @ref{new_http_request(3)}, @ref{new_cgi(3)},
+ * @ref{new_pseudothread(3)}, @ref{io_fdopen(3)}, RFC 2616.
+ */
+extern http_response new_http_response (pool, http_request, io_handle, int code, const char *msg);
+extern void http_response_send_header (http_response, const char *key, const char *value);
+extern void http_response_send_headers (http_response, ...);
+extern int http_response_end_headers (http_response h);
+extern void http_response_write_chunk (http_response, const char *data, int length);
+extern void http_response_write_chunk_string (http_response, const char *string);
+extern void http_response_write_chunk_end (http_response);
+
+/* Function: http_set_log_file - enable HTTP logs on file pointer
+ * Function: http_get_log_file
+ *
+ * The @code{FILE *fp} argument to @code{http_set_log_file} sets
+ * the file pointer on which HTTP logs are generated. To disable
+ * logging, set @code{fp} to @code{NULL}. The function returns
+ * @code{fp}.
+ *
+ * @code{http_get_log_file} returns the current file pointer
+ * or @code{NULL} if logging is disabled.
+ *
+ * The default is that logging is disabled.
+ *
+ * Currently log messages are generated at the end of the
+ * HTTP response headers and have the following fixed format:
+ *
+ * YYYY/MM/DD HH:MM ip:port - "METHOD URL HTTP/x.y" CODE length "Referer" "User Agent"
+ *
+ * The first "-" is intended to store the HTTP auth username, when
+ * HTTP authorization is supported by the library. The "length"
+ * field is only known if the caller sends back a "Content-Length"
+ * header. Otherwise 0 is printed in that position.
+ *
+ * Bugs: Log format should be customizable. It should be possible
+ * (optionally, of course) to look up the IP address and print
+ * a hostname.
+ *
+ * See also: @ref{new_http_request(3)}, @ref{new_cgi(3)},
+ * @ref{new_pseudothread(3)}, @ref{io_fdopen(3)}, RFC 2616.
+ */
+extern FILE *http_set_log_file (FILE *fp);
+extern FILE *http_get_log_file (void);
+
+#endif /* PTHR_HTTP_H */
--- /dev/null
+/* A small buffered I/O library.
+ * - by Richard W.M. Jones <rich@annexia.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_iolib.c,v 1.9 2003/02/02 18:05:31 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_TYPE_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include <pool.h>
+
+#include "pthr_iolib.h"
+#include "pthr_pseudothread.h"
+
+struct io_handle
+{
+ /* Pool for memory allocation, etc. */
+ pool pool;
+
+ /* The underlying socket. */
+ int sock;
+
+ /* The incoming buffer. */
+ char *inbuf; /* The actual buffer. */
+ char *inbufpos; /* Current position within buffer. */
+ int inbuflen; /* Number of bytes left to read. */
+
+ int inbufcount; /* Total number of bytes read. */
+
+ /* The outgoing buffer. */
+ char *outbuf; /* The actual buffer. */
+ char *outbufpos; /* Current position within buffer. */
+ int outbuffree; /* Number of bytes free left in buffer. */
+
+ int outbufcount; /* Total number of bytes written. */
+
+ int outbufmode; /* Output buffer mode. */
+};
+
+static void _flush (io_handle io, int ignore_errors);
+static void _err (io_handle io, const char *msg) __attribute__((noreturn));
+static void _do_close (io_handle io);
+
+io_handle
+io_fdopen (int sock)
+{
+ io_handle io;
+ pool pool = new_subpool (pth_get_pool (current_pth));
+
+ io = pmalloc (pool, sizeof *io);
+
+ io->inbuf = pmalloc (pool, IOLIB_INPUT_BUFFER_SIZE * sizeof (char));
+
+ io->outbuf = pmalloc (pool, IOLIB_OUTPUT_BUFFER_SIZE * sizeof (char));
+
+ io->pool = pool;
+ io->sock = sock;
+
+ io->inbufpos = &io->inbuf[IOLIB_INPUT_BUFFER_SIZE]; /* Let ungetc work. */
+ io->inbuflen = 0;
+ io->inbufcount = 0;
+ io->outbufpos = io->outbuf;
+ io->outbuffree = IOLIB_OUTPUT_BUFFER_SIZE;
+ io->outbufcount = 0;
+ io->outbufmode = IO_MODE_LINE_BUFFERED;
+
+ /* Register to automagically close this I/O handle when we
+ * exit this thread.
+ */
+ pool_register_cleanup_fn (pool, (void (*)(void *)) _do_close, io);
+
+ return io;
+}
+
+inline int
+io_fflush (io_handle io)
+{
+ if (io->outbufpos > io->outbuf)
+ _flush (io, 0);
+
+ return 0;
+}
+
+void
+_do_close (io_handle io)
+{
+ /* Flush the buffer, but don't worry too much about errors. */
+ if (io->outbufpos > io->outbuf)
+ _flush (io, 1);
+
+ /* Close underlying socket. */
+ if (close (io->sock) < 0) _err (io, "close");
+}
+
+void
+io_fclose (io_handle io)
+{
+ /* Deleting the subpool containing io causes the _do_close callback
+ * handler to run which actually closes the socket. The memory
+ * containing io is also freed.
+ */
+ delete_pool (io->pool);
+}
+
+int
+io_fgetc (io_handle io)
+{
+ int r;
+
+ io_fflush (io);
+
+ next:
+ /* Satify this from the input buffer? */
+ if (io->inbuflen > 0)
+ {
+ int c = *io->inbufpos;
+ io->inbufpos++;
+ io->inbuflen--;
+ return c;
+ }
+
+ /* Refill the input buffer from the socket. */
+ r = pth_read (io->sock, io->inbuf, IOLIB_INPUT_BUFFER_SIZE * sizeof (char));
+ if (r < 0) _err (io, "read");
+
+ io->inbufpos = io->inbuf;
+ io->inbuflen = r;
+ io->inbufcount += r;
+
+ /* End of buffer? */
+ if (r == 0) return -1;
+
+ /* Return the next character. */
+ goto next;
+}
+
+char *
+io_fgets (char *s, int max_size, io_handle io, int store_eol)
+{
+ int n = 0;
+
+ io_fflush (io);
+
+ while (n < max_size - 1)
+ {
+ int c = io_fgetc (io);
+
+ /* End of file? */
+ if (c == -1)
+ {
+ s[n] = '\0';
+ return n > 0 ? s : 0;
+ }
+
+ s[n++] = c;
+
+ /* End of line? */
+ if (c == '\n')
+ break;
+ }
+
+ /* Remove the trailing CR LF? */
+ if (!store_eol)
+ {
+ while (n > 0 && (s[n-1] == '\n' || s[n-1] == '\r'))
+ n--;
+ }
+
+ /* Terminate the line. */
+ s[n] = '\0';
+ return s;
+}
+
+int
+io_ungetc (int c, io_handle io)
+{
+ if (io->inbufpos > io->inbuf)
+ {
+ io->inbufpos--;
+ *io->inbufpos = c;
+ io->inbuflen++;
+ return c;
+ }
+
+ return -1; /* No room left in input buffer. */
+}
+
+size_t
+io_fread (void *ptr, size_t size, size_t nmemb, io_handle io)
+{
+ size_t n = size * nmemb, c = 0;
+ int i;
+ char *cptr = (char *) ptr;
+
+ io_fflush (io);
+
+ /* Satisfy as much as possible from the input buffer. */
+ i = n > io->inbuflen ? io->inbuflen : n;
+ memcpy (cptr, io->inbufpos, i * sizeof (char));
+ n -= i;
+ c += i;
+ cptr += i;
+ io->inbuflen -= i;
+ io->inbufpos += i;
+
+ /* Read the rest directly from the socket until we have either
+ * satisfied the request or there is an EOF.
+ */
+ while (n > 0)
+ {
+ int r = pth_read (io->sock, cptr, n * sizeof (char));
+ if (r < 0) _err (io, "read");
+
+ if (r == 0) /* End of file. */
+ return c;
+
+ n -= r;
+ c += r;
+ cptr += r;
+ io->inbufcount += r;
+ }
+
+ return c;
+}
+
+inline int
+io_fputc (int c, io_handle io)
+{
+ again:
+ if (io->outbuffree > 0)
+ {
+ *io->outbufpos = c;
+ io->outbufpos++;
+ io->outbuffree--;
+
+ /* Flush the buffer after each character or line. */
+ if (io->outbufmode == IO_MODE_UNBUFFERED ||
+ (io->outbufmode == IO_MODE_LINE_BUFFERED && c == '\n'))
+ _flush (io, 0);
+
+ return c;
+ }
+
+ /* We need to flush the output buffer and try again. */
+ _flush (io, 0);
+ goto again;
+}
+
+int
+io_fputs (const char *s, io_handle io)
+{
+ while (*s)
+ {
+ io_fputc (*s, io);
+ s++;
+ }
+
+ /* According to the manual page, fputs returns a non-negative number
+ * on success or EOF on error.
+ */
+ return 1;
+}
+
+int
+io_fprintf (io_handle io, const char *fs, ...)
+{
+ va_list args;
+ int r, n = 4096;
+ char *s = alloca (n);
+
+ if (s == 0) abort ();
+
+ va_start (args, fs);
+ r = vsnprintf (s, n, fs, args);
+ va_end (args);
+
+ /* Did the string get truncated? If so, we can allocate the
+ * correct sized buffer directly from the pool.
+ *
+ * Note: according to the manual page, a return value of -1 indicates
+ * that the string was truncated. We have found that this is not
+ * actually true however. In fact, the library seems to return the
+ * number of characters which would have been written into the string
+ * (ie. r > n).
+ */
+ if (r > n)
+ {
+ n = r + 1;
+ s = pmalloc (io->pool, n);
+
+ va_start (args, fs);
+ r = vsnprintf (s, n, fs, args);
+ va_end (args);
+ }
+
+ io_fputs (s, io);
+
+ return r;
+}
+
+size_t
+io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle io)
+{
+ size_t n = size * nmemb, c = 0;
+ char *cptr = (char *) ptr;
+
+ /* Flush out any existing data. */
+ io_fflush (io);
+
+ /* Write the data directly to the socket. */
+ while (n > 0)
+ {
+ int r = pth_write (io->sock, cptr, n * sizeof (char));
+ if (r < 0) _err (io, "write");
+
+ n -= r;
+ cptr += r;
+ c += r;
+ io->outbufcount += r;
+ }
+
+ return c;
+}
+
+io_handle
+io_popen (const char *command, const char *type)
+{
+ int fd[2], s;
+ int pid;
+ int mode = 0; /* 0 for read, 1 for write. */
+ io_handle io;
+
+ if (strcmp (type, "r") == 0)
+ mode = 0;
+ else if (strcmp (type, "w") == 0)
+ mode = 1;
+ else
+ abort ();
+
+ /* Create a pipe between parent and child. */
+ if (pipe (fd) < 0) { perror ("pipe"); return 0; }
+
+ /* Fork and connect up the pipe appropriately. */
+ pid = fork ();
+ if (pid < 0) { close (fd[0]); close (fd[1]); perror ("fork"); return 0; }
+ else if (pid > 0) /* Parent process. */
+ {
+ if (mode == 0)
+ {
+ close (fd[1]);
+ s = fd[0];
+ }
+ else
+ {
+ close (fd[0]);
+ s = fd[1];
+ }
+
+ /* Set the file descriptor to non-blocking. */
+ if (fcntl (s, F_SETFL, O_NONBLOCK) < 0) abort ();
+
+ io = io_fdopen (s);
+ if (io == 0)
+ {
+ close (s);
+ return 0;
+ }
+
+ return io;
+ }
+ else /* Child process. */
+ {
+ if (mode == 0)
+ {
+ close (fd[0]);
+ if (fd[1] != 1)
+ {
+ dup2 (fd[1], 1);
+ close (fd[1]);
+ }
+ }
+ else
+ {
+ close (fd[1]);
+ if (fd[0] != 0)
+ {
+ dup2 (fd[0], 0);
+ close (fd[0]);
+ }
+ }
+
+ /* Run the child command. */
+ _exit (system (command));
+
+ /*NOTREACHED*/
+ return 0;
+ }
+}
+
+void
+io_pclose (io_handle io)
+{
+ /* Close connection. */
+ io_fclose (io);
+
+ /* Wait for child to exit. */
+ wait (NULL);
+}
+
+int
+io_copy (io_handle from_io, io_handle to_io, int len)
+{
+ int written_len = 0;
+
+ /* Synchronize the input handle. */
+ io_fflush (from_io);
+
+ while (len != 0)
+ {
+ int n;
+
+ /* Read bytes directly from the input buffer. */
+ if (from_io->inbuflen == 0)
+ {
+ int c;
+
+ /* Need to refill the input buffer. We do this by reading a single
+ * character and the "ungetting" it back into the buffer. The
+ * io_ungetc call is guaranteed to work in this case.
+ */
+ c = io_fgetc (from_io);
+ if (c == -1) return written_len;
+ io_ungetc (c, from_io);
+ }
+
+ /* Decide how many bytes to read. */
+ if (len == -1)
+ n = from_io->inbuflen;
+ else
+ {
+ if (len >= from_io->inbuflen)
+ n = from_io->inbuflen;
+ else
+ n = len;
+ }
+
+ /* Write it. */
+ io_fwrite (from_io->inbufpos, 1, n, to_io);
+ written_len += n;
+ if (len > 0) len -= n;
+ from_io->inbufpos += n;
+ from_io->inbuflen -= n;
+ }
+
+ return written_len;
+}
+
+void
+io_setbufmode (io_handle io, int mode)
+{
+ io->outbufmode = mode;
+}
+
+static void
+_flush (io_handle io, int ignore_errors)
+{
+ size_t n = io->outbufpos - io->outbuf;
+ char *cptr = io->outbuf;
+
+ /* Write the data to the socket. */
+ while (n > 0)
+ {
+ int r = pth_write (io->sock, cptr, n * sizeof (char));
+ if (r < 0)
+ {
+ if (!ignore_errors)
+ _err (io, "write");
+ else
+ break;
+ }
+
+ n -= r;
+ cptr += r;
+ io->outbufcount += r;
+ }
+
+ /* Reset the output buffer. */
+ io->outbufpos = io->outbuf;
+ io->outbuffree = IOLIB_OUTPUT_BUFFER_SIZE;
+}
+
+int
+io_fileno (io_handle io)
+{
+ return io->sock;
+}
+
+int
+io_get_inbufcount (io_handle io)
+{
+ return io->inbufcount;
+}
+
+int
+io_get_outbufcount (io_handle io)
+{
+ return io->outbufcount;
+}
+
+static void
+_err (io_handle io, const char *msg)
+{
+ /* I commented out the following line because, strangely, it seems to
+ * cause some sort of memory corruption bug. If there is a bug, it's
+ * probably related to either stack overflow or the stack swapping /
+ * context switching stuff. Whatever. Removed it for safety.
+ * -- RWMJ 2000/12/05.
+ */
+#if 0
+ perror (msg);
+#endif
+
+ /* Should errors be catchable? ie. Should we call pth_die here? And
+ * if so, what are the consequences of doing it?
+ * -- RWMJ 2001/05/30.
+ */
+ pth_exit ();
+}
--- /dev/null
+/* A small buffered I/O library.
+ * - by Richard W.M. Jones <rich@annexia.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_iolib.h,v 1.5 2002/12/01 14:29:28 rich Exp $
+ */
+
+#ifndef PTHR_IOLIB_H
+#define PTHR_IOLIB_H
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <setjmp.h>
+
+#include <pool.h>
+
+#include "pthr_pseudothread.h"
+
+#define IOLIB_INPUT_BUFFER_SIZE 1024
+#define IOLIB_OUTPUT_BUFFER_SIZE 1024
+
+struct io_handle;
+typedef struct io_handle *io_handle;
+
+/* Buffer modes. */
+#define IO_MODE_LINE_BUFFERED 0 /* Default. */
+#define IO_MODE_UNBUFFERED 1
+#define IO_MODE_FULLY_BUFFERED 2
+
+/* Function: io_fdopen - A buffered I/O library
+ * Function: io_fclose
+ * Function: io_fgetc
+ * Function: io_fgets
+ * Function: io_ungetc
+ * Function: io_fread
+ * Function: io_fputc
+ * Function: io_fputs
+ * Function: io_fprintf
+ * Function: io_fwrite
+ * Function: io_fflush
+ * Function: io_fileno
+ * Function: io_popen
+ * Function: io_pclose
+ * Function: io_copy
+ * Function: io_setbufmode
+ * Function: io_get_inbufcount
+ * Function: io_get_outbufcount
+ *
+ * The @code{io_*} functions replace the normal blocking C library
+ * @code{f*} functions with equivalents which work on non-blocking
+ * pseudothread file descriptors.
+ *
+ * All of the functions in the synopsis above work identically
+ * to the C library equivalents, except where documented below.
+ *
+ * @code{io_fdopen} associates a socket @code{sock} with a
+ * I/O handle. The association cannot be broken later (so use
+ * @ref{dup(2)} if you wish to later take back control of the
+ * underlying socket). If either the current thread exits
+ * or @code{io_fclose} is called, the underlying socket is
+ * closed (with @ref{close(2)}).
+ *
+ * @code{io_fclose} flushes all unwritten data out of the socket
+ * and closes it.
+ *
+ * @code{io_fgets} operates similarly to the ordinary C library
+ * function @ref{fgets(3)}, except that it contains a useful
+ * fourth argument, @code{store_eol}. If this fourth argument is
+ * false, then the end of line characters (@code{CR}, @code{CR LF}
+ * or @code{LF}) are stripped from the string before it is stored.
+ *
+ * @code{io_copy} copies @code{len} bytes from @code{from_io}
+ * to @code{to_io}. If @code{len} equals -1 then bytes are
+ * copied from @code{from_io} until end of file is reached.
+ * If @code{len} equals 0, then no bytes are copied. The
+ * number of bytes actually copied is returned.
+ *
+ * @code{io_setbufmode} sets the output buffer mode, and works
+ * completely differently to the ordinary C library function
+ * @ref{setbufmode(3)}. The three mode arguments possible are:
+ * @code{IO_MODE_LINE_BUFFERED}, @code{IO_MODE_UNBUFFERED} and
+ * @code{IO_MODE_FULLY_BUFFERED}, and these correspond to line
+ * buffering, no buffering and full (block) buffering.
+ *
+ * @code{io_get_inbufcount} and @code{io_get_outbufcount} return
+ * the number of characters read and written on the socket since
+ * the socket was associated with the I/O object.
+ *
+ * See also:
+ * @ref{fgetc(3)},
+ * @ref{fgets(3)},
+ * @ref{ungetc(3)},
+ * @ref{fread(3)},
+ * @ref{fputc(3)},
+ * @ref{fputs(3)},
+ * @ref{fprintf(3)},
+ * @ref{fwrite(3)},
+ * @ref{fflush(3)},
+ * @ref{fileno(3)},
+ * @ref{popen(3)},
+ * @ref{pclose(3)},
+ * @ref{pth_exit(3)}.
+ */
+extern io_handle io_fdopen (int sock);
+extern void io_fclose (io_handle);
+extern int io_fgetc (io_handle);
+extern char *io_fgets (char *s, int max_size, io_handle, int store_eol);
+extern int io_ungetc (int c, io_handle);
+extern size_t io_fread (void *ptr, size_t size, size_t nmemb, io_handle);
+extern int io_fputc (int c, io_handle);
+extern int io_fputs (const char *s, io_handle);
+extern int io_fprintf (io_handle, const char *fs, ...) __attribute__ ((format (printf, 2, 3)));
+extern size_t io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle);
+extern int io_fflush (io_handle);
+extern int io_fileno (io_handle);
+extern io_handle io_popen (const char *command, const char *mode);
+extern void io_pclose (io_handle);
+extern int io_copy (io_handle from_io, io_handle to_io, int len);
+extern void io_setbufmode (io_handle, int mode);
+extern int io_get_inbufcount (io_handle);
+extern int io_get_outbufcount (io_handle);
+
+#endif /* PTHR_IOLIB_H */
--- /dev/null
+/* Listener thread.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_listener.c,v 1.5 2002/12/08 13:41:07 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include "pthr_pseudothread.h"
+#include "pthr_listener.h"
+
+struct listener
+{
+ pseudothread pth;
+ int sock;
+ void (*processor_fn) (int sock, void *data);
+ void *data;
+};
+
+static void run (void *vp);
+
+listener
+new_listener (int sock,
+ void (*processor_fn) (int sock, void *data), void *data)
+{
+ pool pool;
+ listener p;
+
+ pool = new_pool ();
+ p = pmalloc (pool, sizeof *p);
+
+ p->sock = sock;
+ p->processor_fn = processor_fn;
+ p->data = data;
+ p->pth = new_pseudothread (pool, run, p, "listener");
+
+ pth_start (p->pth);
+
+ return p;
+}
+
+static void
+run (void *vp)
+{
+ listener p = (listener) vp;
+
+ for (;;)
+ {
+ struct sockaddr_in addr;
+ int sz, ns;
+
+ /* Wait for the new connection. */
+ sz = sizeof addr;
+ ns = pth_accept (p->sock, (struct sockaddr *) &addr, &sz);
+
+ if (ns < 0) { perror ("accept"); continue; }
+
+ /* Set the new socket to non-blocking. */
+ if (fcntl (ns, F_SETFL, O_NONBLOCK) < 0) abort ();
+
+ /* Create a new processor thread to handle this connection. */
+ p->processor_fn (ns, p->data);
+ }
+}
--- /dev/null
+/* Listener thread.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_listener.h,v 1.3 2002/08/21 10:42:19 rich Exp $
+ */
+
+#ifndef PTHR_LISTENER_H
+#define PTHR_LISTENER_H
+
+#include "pthr_reactor.h"
+#include "pthr_pseudothread.h"
+
+struct listener;
+typedef struct listener *listener;
+
+extern listener new_listener (int sock, void (*processor_fn) (int sock, void *data), void *data);
+
+#endif /* PTHR_LISTENER_H */
--- /dev/null
+/* Mutex locks.
+ * by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_mutex.c,v 1.4 2003/02/02 18:05:31 rich Exp $
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifndef __sun__
+
+#include <pool.h>
+
+#include "pthr_pseudothread.h"
+#include "pthr_wait_queue.h"
+
+#else
+/* SunOS annoyingly defines a 'struct mutex' type, and even worse defines
+ * it as soon as you include <stdlib.h>.
+ */
+#define POOL_H
+struct pool;
+typedef struct pool *pool;
+extern pool new_subpool (pool);
+extern void delete_pool (pool);
+extern void *pmalloc (pool, unsigned n);
+extern void pool_register_cleanup_fn (pool, void (*fn) (void *), void *);
+
+#define PTHR_PSEUDOTHREAD_H
+struct pseudothread;
+typedef struct pseudothread *pseudothread;
+extern pseudothread current_pth;
+extern pool pth_get_pool (pseudothread pth);
+
+#define PTHR_WAIT_QUEUE_H
+struct wait_queue;
+typedef struct wait_queue *wait_queue;
+extern wait_queue new_wait_queue (pool);
+extern void wq_wake_up (wait_queue);
+extern void wq_wake_up_one (wait_queue);
+extern void wq_sleep_on (wait_queue);
+extern int wq_nr_sleepers (wait_queue);
+#endif
+
+#include "pthr_mutex.h"
+
+static void _do_release (void *);
+static void _delete_mutex (void *);
+
+struct mutex
+{
+ pseudothread pth; /* Pseudothread which is holding the lock, or null. */
+ wait_queue wq; /* Queue of threads waiting to enter. */
+ pool pool; /* Subpool of pth pool which holds the lock. If the
+ * thread exits without releasing the lock, then
+ * the subpool is deleted, which causes our callback
+ * to run, releasing the lock.
+ */
+};
+
+mutex
+new_mutex (pool p)
+{
+ mutex m = pmalloc (p, sizeof *m);
+
+ m->pth = 0;
+ m->pool = 0;
+ m->wq = new_wait_queue (p);
+
+ /* The purpose of this cleanup is just to check that the mutex
+ * isn't released with threads in the critical section.
+ */
+ pool_register_cleanup_fn (p, _delete_mutex, m);
+
+ return m;
+}
+
+static void
+_delete_mutex (void *vm)
+{
+ mutex m = (mutex) vm;
+ assert (m->pth == 0);
+}
+
+/* This function is identical to MUTEX_ENTER, except that it does not
+ * block if the lock is held by another thread. The function
+ * returns TRUE if the lock was successfully acquired, or FALSE
+ * if another thread is holding it.
+ */
+inline int
+mutex_try_enter (mutex m)
+{
+ if (m->pth == 0)
+ {
+ /* Create a subpool. If the thread exits early, then this subpool
+ * with be deleted implicitly. If, on the other hand, we release
+ * the lock in RWLOCK_LEAVE, then we will delete this pool
+ * explicitly. Either way, _DO_RELEASE will be called.
+ */
+ pool pool = new_subpool (pth_get_pool (current_pth));
+
+ /* Register _DO_RELEASE to run when the subpool is deleted. */
+ pool_register_cleanup_fn (pool, _do_release, m);
+
+ m->pth = current_pth;
+ m->pool = pool;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Enter a critical section. This function blocks until the lock is
+ * acquired.
+ */
+void
+mutex_enter (mutex m)
+{
+ while (mutex_try_enter (m) == 0)
+ wq_sleep_on (m->wq);
+}
+
+/* Leave a critical section.
+ */
+void
+mutex_leave (mutex m)
+{
+ assert (m->pth == current_pth);
+
+ /* Force _DO_RELEASE to run. */
+ delete_pool (m->pool);
+}
+
+static void
+_do_release (void *vm)
+{
+ mutex m = (mutex) vm;
+
+ m->pth = 0;
+ m->pool = 0;
+
+ /* Anyone waiting to enter? */
+ if (wq_nr_sleepers (m->wq) > 0) wq_wake_up_one (m->wq);
+}
+
+int
+mutex_nr_sleepers (mutex m)
+{
+ return wq_nr_sleepers (m->wq);
+}
--- /dev/null
+/* Simple mutex locks for pthrlib.
+ * by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_mutex.h,v 1.4 2002/12/01 14:29:28 rich Exp $
+ */
+
+#ifndef PTHR_MUTEX_H
+#define PTHR_MUTEX_H
+
+#include <pool.h>
+
+#include "pthr_pseudothread.h"
+#include "pthr_wait_queue.h"
+
+struct mutex;
+typedef struct mutex *mutex;
+
+/* Function: new_mutex - mutual exclusion (mutex) locks
+ * Function: mutex_enter
+ * Function: mutex_leave
+ * Function: mutex_try_enter
+ * Function: mutex_nr_sleepers
+ *
+ * Mutex locks are simple: at most one pseudothread may enter the
+ * critical area protected by the lock at once. If instead you
+ * wish multiple reader / single writer semantics, then please
+ * see @ref{new_rwlock(3)}.
+ *
+ * Mutex locks are automatically released if they are being held when
+ * the thread exits.
+ *
+ * Note that there are possible deadlocks when using locks. To
+ * avoid deadlocks, always ensure every thread acquires locks
+ * in the same order.
+ *
+ * @code{new_mutex} creates a new mutex object.
+ *
+ * @code{mutex_enter} and @code{mutex_leave} enter and leave
+ * the critical section. Only one thread can run at a time
+ * inside the critical section.
+ *
+ * @code{mutex_try_enter} is identical to @code{mutex_enter}
+ * except that it does not block if the lock is held by another
+ * thread. The function returns true if the lock was successfully
+ * acquired, or false if another thread is currently holding it.
+ *
+ * @code{mutex_nr_sleepers} returns the number of threads which
+ * are queued up waiting to enter the critical section.
+ *
+ * Bugs: A common mistake is to accidentally call @code{mutex_leave}
+ * when you are not holding the lock. This generally causes the
+ * library to crash.
+ */
+extern mutex new_mutex (pool);
+extern void mutex_enter (mutex);
+extern void mutex_leave (mutex);
+extern int mutex_try_enter (mutex);
+extern int mutex_nr_sleepers (mutex);
+
+#endif /* PTHR_MUTEX_H */
--- /dev/null
+/* Pseudothread handler.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_pseudothread.c,v 1.22 2003/02/05 22:13:32 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+#include <pstring.h>
+
+#include "pthr_reactor.h"
+#include "pthr_context.h"
+#include "pthr_stack.h"
+#include "pthr_pseudothread.h"
+
+struct pseudothread
+{
+ /* Thread context and calling (reactor) context. */
+ mctx_t thread_ctx, calling_ctx;
+
+ /* Thread number. */
+ int n;
+
+ /* Pointer to thread stack and size. */
+ void *stack;
+ int stack_size;
+
+ /* Alarm handling. */
+ int alarm_received;
+ reactor_timer alarm_timer;
+
+ /* Pool for memory allocations in this thread. */
+ pool pool;
+
+ /* Name of the thread. */
+ const char *name;
+
+ /* Used to implement pth_exit. */
+ jmp_buf exit_jmp;
+
+ /* Used to implement pth_die. */
+ vector exception_jmp_vec;
+ const char *exception_msg;
+
+ /* Used to implement pth_poll, pth_select. */
+ int poll_timeout;
+
+ /* Start point and data for thread. */
+ void (*run) (void *);
+ void *data;
+
+ /* LANGUAGE environment variable for this thread. If null, then
+ * the variable is unset in the thread.
+ */
+ const char *lang;
+
+ /* TZ environment variable for this thread. If null, then the
+ * variable is unset in the thread.
+ */
+ const char *tz;
+};
+
+/* Currently running pseudothread. */
+pseudothread current_pth = 0;
+
+/* Global list of threads. */
+static vector threads = 0;
+
+/* Default stack size, in bytes. */
+static int default_stack_size = 65536;
+
+static void block (int sock, int ops);
+static void return_from_block (int sock, int events, void *);
+static void _sleep (int timeout);
+static void return_from_sleep (void *);
+static void _poll (struct pollfd *fds, unsigned int n, int timeout);
+static void return_from_poll (int sock, int events, void *);
+static void return_from_poll_timeout (void *);
+static void return_from_alarm (void *);
+
+static void thread_trampoline (void *vpth);
+
+static void pseudothread_init (void) __attribute__ ((constructor));
+
+static void
+pseudothread_init ()
+{
+#ifdef __OpenBSD__
+ /* OpenBSD doesn't call constructors in the correct order. */
+ pool global_pool = new_pool ();
+#endif
+ threads = new_vector (global_pool, struct pseudothread *);
+}
+
+int
+pseudothread_set_stack_size (int size)
+{
+ return default_stack_size = size;
+}
+
+int
+pseudothread_get_stack_size (void)
+{
+ return default_stack_size;
+}
+
+pseudothread
+new_pseudothread (pool pool,
+ void (*run) (void *), void *data,
+ const char *name)
+{
+ pseudothread pth;
+ void *stack_addr;
+ int i;
+
+ /* Allocate space for the pseudothread. */
+ pth = pcalloc (pool, 1, sizeof *pth);
+
+ pth->run = run;
+ pth->data = data;
+ pth->pool = pool;
+ pth->name = name;
+
+ /* Create a stack for this thread. */
+ stack_addr = _pth_get_stack (default_stack_size);
+ if (stack_addr == 0) abort ();
+ pth->stack = stack_addr;
+ pth->stack_size = default_stack_size;
+
+ /* Create a new thread context. */
+ mctx_set (&pth->thread_ctx,
+ thread_trampoline, pth,
+ stack_addr, default_stack_size);
+
+ /* Allocate space in the global threads list for this thread. */
+ for (i = 0; i < vector_size (threads); ++i)
+ {
+ pseudothread p;
+
+ vector_get (threads, i, p);
+ if (p == 0)
+ {
+ pth->n = i;
+ vector_replace (threads, i, pth);
+ goto done;
+ }
+ }
+
+ pth->n = i;
+ vector_push_back (threads, pth);
+
+ done:
+ return pth;
+}
+
+static void
+thread_trampoline (void *vpth)
+{
+ pseudothread pth = (pseudothread) vpth;
+ mctx_t calling_ctx;
+ void *stack;
+ int stack_size;
+ const struct pseudothread *null_thread = 0;
+
+ /* Set up the current_pth before running user code. */
+ current_pth = pth;
+
+ if (setjmp (pth->exit_jmp) == 0)
+ pth->run (pth->data);
+
+ /* We return here either when "run" finishes normally or after
+ * a longjmp caused by the pseudothread calling pth_exit.
+ */
+
+ calling_ctx = pth->calling_ctx;
+
+ /* Remove the thread from the list of threads. */
+ vector_replace (threads, pth->n, null_thread);
+
+ /* Delete the pool and the stack. */
+ stack = pth->stack;
+ stack_size = pth->stack_size;
+ delete_pool (pth->pool);
+ _pth_return_stack (stack, stack_size);
+
+ /* Restore calling context (this never returns ...). */
+ mctx_restore (&calling_ctx);
+}
+
+void
+pth_start (pseudothread pth)
+{
+ pseudothread old_pth = current_pth;
+
+ /* Swap into the new context -- this actually calls thread_trampoline. */
+ mctx_switch (&pth->calling_ctx, &pth->thread_ctx);
+
+ /* Restore current_pth before returning into user code. */
+ current_pth = old_pth;
+}
+
+void
+pth_set_name (const char *name)
+{
+ current_pth->name = name;
+}
+
+const char *
+pth_get_name (pseudothread pth)
+{
+ return pth->name;
+}
+
+int
+pth_get_thread_num (pseudothread pth)
+{
+ return pth->n;
+}
+
+void
+(*pth_get_run (pseudothread pth)) (void *)
+{
+ return pth->run;
+}
+
+void *
+pth_get_data (pseudothread pth)
+{
+ return pth->data;
+}
+
+const char *
+pth_get_language (pseudothread pth)
+{
+ return pth->lang;
+}
+
+const char *
+pth_get_tz (pseudothread pth)
+{
+ return pth->tz;
+}
+
+void *
+pth_get_stack (pseudothread pth)
+{
+ return pth->stack;
+}
+
+int
+pth_get_stack_size (pseudothread pth)
+{
+ return pth->stack_size;
+}
+
+unsigned long
+pth_get_PC (pseudothread pth)
+{
+ return mctx_get_PC (&pth->thread_ctx);
+}
+
+unsigned long
+pth_get_SP (pseudothread pth)
+{
+ return mctx_get_SP (&pth->thread_ctx);
+}
+
+void
+pth_exit ()
+{
+ longjmp (current_pth->exit_jmp, 1);
+}
+
+const char *
+pth_catch (void (*fn) (void *), void *data)
+{
+ jmp_buf jb;
+
+ if (setjmp (jb) == 0)
+ {
+ /* Register the exception handler. */
+ if (current_pth->exception_jmp_vec == 0)
+ current_pth->exception_jmp_vec
+ = new_vector (current_pth->pool, jmp_buf);
+ vector_push_back (current_pth->exception_jmp_vec, jb);
+
+ /* Run the function. */
+ fn (data);
+
+ /* No errors: pop the exception handler. */
+ vector_pop_back (current_pth->exception_jmp_vec, jb);
+
+ return 0;
+ }
+
+ /* Exception was fired off. Return the message. */
+ return current_pth->exception_msg;
+}
+
+void
+_pth_die (const char *msg, const char *filename, int lineno)
+{
+ /* If there is a surrounding exception handler registered, then
+ * jump directly to it.
+ */
+ if (current_pth->exception_jmp_vec &&
+ vector_size (current_pth->exception_jmp_vec) > 0)
+ {
+ jmp_buf jb;
+
+ current_pth->exception_msg = msg;
+ vector_pop_back (current_pth->exception_jmp_vec, jb);
+ longjmp (jb, 1);
+ /*NOTREACHED*/
+ }
+
+ /* Otherwise: print the message and exit the pseudothread immediately. */
+ fprintf (stderr, "%s:%d: %s\n", filename, lineno, msg);
+ pth_exit ();
+}
+
+pool
+pth_get_pool (pseudothread pth)
+{
+ return pth->pool;
+}
+
+int
+pth_accept (int s, struct sockaddr *addr, int *size)
+{
+ int r;
+
+ do
+ {
+ block (s, REACTOR_READ);
+
+ /* Accept the connection. */
+ r = accept (s, addr, size);
+ }
+ while (r == -1 && errno == EWOULDBLOCK);
+
+ return r;
+}
+
+int
+pth_connect (int s, struct sockaddr *addr, int size)
+{
+ int r, sz;
+
+ /* Connect (NB. The socket had better have been set to non-blocking) */
+ r = connect (s, addr, size);
+
+ if (r == 0) return 0; /* Connected immediately. */
+ else
+ {
+ if (errno == EINPROGRESS)
+ {
+ /* Wait for the socket to connect. */
+ block (s, REACTOR_WRITE);
+
+ /* Read the error code (see connect(2) man page for details). */
+ sz = sizeof r;
+ if (getsockopt (s, SOL_SOCKET, SO_ERROR, &r, &sz) < 0)
+ return -1;
+
+ /* What is the error code? */
+ errno = r;
+ return r == 0 ? 0 : -1;
+ }
+ else
+ {
+ /* Some other type of error before blocking. */
+ return -1;
+ }
+ }
+}
+
+ssize_t
+pth_read (int s, void *buf, size_t count)
+{
+ int r;
+
+ again:
+ r = read (s, buf, count);
+ if (r == -1 && errno == EWOULDBLOCK)
+ {
+ block (s, REACTOR_READ);
+ goto again;
+ }
+
+ return r;
+}
+
+ssize_t
+pth_write (int s, const void *buf, size_t count)
+{
+ int r;
+
+ again:
+ r = write (s, buf, count);
+ if (r == -1 && errno == EWOULDBLOCK)
+ {
+ block (s, REACTOR_WRITE);
+ goto again;
+ }
+
+ return r;
+}
+
+int
+pth_sleep (int seconds)
+{
+ _sleep (seconds * 1000);
+ return seconds;
+}
+
+int
+pth_nanosleep (const struct timespec *req,
+ struct timespec *rem)
+{
+ int timeout = req->tv_sec * 1000 + req->tv_nsec / 1000000;
+
+ _sleep (timeout);
+ return 0;
+}
+
+int
+pth_millisleep (int millis)
+{
+ _sleep (millis);
+ return 0;
+}
+
+void
+pth_timeout (int seconds)
+{
+ /* Unregister any previously registered alarm. */
+ if (current_pth->alarm_timer)
+ reactor_unset_timer_early (current_pth->alarm_timer);
+ current_pth->alarm_timer = 0;
+
+ if (seconds != 0)
+ current_pth->alarm_timer =
+ reactor_set_timer (current_pth->pool, seconds * 1000,
+ return_from_alarm, current_pth);
+}
+
+int
+pth_send (int s, const void *msg, int len, unsigned int flags)
+{
+ int r;
+
+ again:
+ r = send (s, msg, len, flags);
+ if (r == -1 && errno == EWOULDBLOCK)
+ {
+ block (s, REACTOR_WRITE);
+ goto again;
+ }
+
+ return r;
+}
+
+int
+pth_sendto (int s, const void *msg, int len, unsigned int flags,
+ const struct sockaddr *to, int tolen)
+{
+ int r;
+
+ again:
+ r = sendto (s, msg, len, flags, to, tolen);
+ if (r == -1 && errno == EWOULDBLOCK)
+ {
+ block (s, REACTOR_WRITE);
+ goto again;
+ }
+
+ return r;
+}
+
+int
+pth_sendmsg (int s, const struct msghdr *msg, unsigned int flags)
+{
+ int r;
+
+ again:
+ r = sendmsg (s, msg, flags);
+ if (r == -1 && errno == EWOULDBLOCK)
+ {
+ block (s, REACTOR_WRITE);
+ goto again;
+ }
+
+ return r;
+}
+
+int
+pth_recv (int s, void *buf, int len, unsigned int flags)
+{
+ int r;
+
+ again:
+ r = recv (s, buf, len, flags);
+ if (r == -1 && errno == EWOULDBLOCK)
+ {
+ block (s, REACTOR_READ);
+ goto again;
+ }
+
+ return r;
+}
+
+int
+pth_recvfrom (int s, void *buf, int len, unsigned int flags,
+ struct sockaddr *from, int *fromlen)
+{
+ int r;
+
+ again:
+ r = recvfrom (s, buf, len, flags, from, fromlen);
+ if (r == -1 && errno == EWOULDBLOCK)
+ {
+ block (s, REACTOR_READ);
+ goto again;
+ }
+
+ return r;
+}
+
+int
+pth_recvmsg (int s, struct msghdr *msg, unsigned int flags)
+{
+ int r;
+
+ again:
+ r = recvmsg (s, msg, flags);
+ if (r == -1 && errno == EWOULDBLOCK)
+ {
+ block (s, REACTOR_READ);
+ goto again;
+ }
+
+ return r;
+}
+
+/* NB. Although it may appear that this version of poll is
+ * inefficient because it makes extra real poll(2) system calls,
+ * in the case where there are many fds being polled, and they
+ * are frequently ready (ie. high load cases ...), this will
+ * be much more efficient than alternatives.
+ */
+int
+pth_poll (struct pollfd *fds, unsigned int n, int timeout)
+{
+ int r;
+
+ again:
+ r = poll (fds, n, 0);
+ if (r == 0)
+ {
+ _poll (fds, n, timeout);
+ if (current_pth->poll_timeout) return 0;
+ goto again;
+ }
+
+ return r;
+}
+
+/* Select is inefficiently implemented just as a library on top of
+ * the pth_poll call. Rewrite your code so it doesn't use pth_select.
+ */
+int
+pth_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ struct timeval *timeout)
+{
+ pool p = new_subpool (current_pth->pool);
+ vector v = new_vector (p, struct pollfd);
+ int i, to, r;
+ struct pollfd pfd, *fds;
+
+ /* Convert the sets into an array of poll descriptors. */
+ for (i = 0; i < n; ++i)
+ {
+ if (readfds && FD_ISSET (i, readfds))
+ {
+ pfd.fd = i;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ vector_push_back (v, pfd);
+ }
+ if (writefds && FD_ISSET (i, writefds))
+ {
+ pfd.fd = i;
+ pfd.events = POLLOUT;
+ pfd.revents = 0;
+ vector_push_back (v, pfd);
+ }
+ if (exceptfds && FD_ISSET (i, exceptfds))
+ {
+ pfd.fd = i;
+ pfd.events = POLLERR;
+ pfd.revents = 0;
+ vector_push_back (v, pfd);
+ }
+ }
+
+ /* Call the underlying poll function. */
+ to = timeout ? timeout->tv_sec * 1000 + timeout->tv_usec / 1000 : -1;
+ vector_get_ptr (v, 0, fds);
+ n = vector_size (v);
+
+ r = pth_poll (fds, n, to);
+
+ /* Error. */
+ if (r == -1)
+ {
+ delete_pool (p);
+ return -1;
+ }
+
+ if (readfds) FD_ZERO (readfds);
+ if (writefds) FD_ZERO (writefds);
+ if (exceptfds) FD_ZERO (exceptfds);
+
+ /* Timeout. */
+ if (r == 0)
+ {
+ delete_pool (p);
+ return 0;
+ }
+
+ /* Convert the returned events into sets. */
+ for (i = 0; i < n; ++i)
+ {
+ if (fds[i].revents & POLLIN)
+ FD_SET (fds[i].fd, readfds);
+ if (fds[i].revents & POLLOUT)
+ FD_SET (fds[i].fd, writefds);
+ if (fds[i].revents & POLLERR)
+ FD_SET (fds[i].fd, exceptfds);
+ }
+
+ delete_pool (p);
+ return r;
+}
+
+int
+pth_wait_readable (int fd)
+{
+ int r;
+ struct pollfd fds[1];
+
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0; /* Don't care. */
+
+ again:
+ r = poll (fds, 1, -1);
+ if (r == 0)
+ {
+ block (fd, REACTOR_READ);
+ goto again;
+ }
+
+ return r >= 0 ? 1 : -1;
+}
+
+int
+pth_wait_writable (int fd)
+{
+ int r;
+ struct pollfd fds[1];
+
+ fds[0].fd = fd;
+ fds[0].events = POLLOUT;
+ fds[0].revents = 0; /* Don't care. */
+
+ again:
+ r = poll (fds, 1, -1);
+ if (r == 0)
+ {
+ block (fd, REACTOR_WRITE);
+ goto again;
+ }
+
+ return r >= 0 ? 1 : -1;
+}
+
+#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV)
+#define do_setenv(name,value) setenv ((name), (value), 1)
+#define do_unsetenv(name) unsetenv ((name))
+#elif defined(HAVE_PUTENV)
+/* This implementation is ugly, but then putenv is an ugly function. */
+
+/* This array maps environment variable names to the 'putenv'-ed env
+ * string. First time round we add a new env string to this array and
+ * putenv it into the environment. Subsequently we modify the env
+ * string.
+ */
+static struct putenv_map {
+ struct putenv_map *next;
+ const char *name;
+ char env[256];
+} *putenv_map = 0;
+
+static inline void
+do_setenv (const char *name, const char *value)
+{
+ struct putenv_map *m;
+
+ for (m = putenv_map; m; m = m->next)
+ if (strcmp (m->name, name) == 0)
+ {
+ /* Modify the env in-place. */
+ finish:
+ snprintf (m->env, sizeof m->env, "%s=%s", name, value);
+ putenv (m->env);
+ return;
+ }
+
+ /* Create a new entry. */
+ m = pmalloc (global_pool, sizeof *m);
+ m->next = putenv_map;
+ m->name = name;
+ goto finish;
+}
+
+#define do_unsetenv(name) putenv ((name))
+
+#else
+#error "no setenv/unsetenv or putenv in your libc"
+#endif
+
+static inline void
+_restore_lang ()
+{
+ if (current_pth->lang == 0)
+ do_unsetenv ("LANGUAGE");
+ else
+ do_setenv ("LANGUAGE", current_pth->lang);
+
+#ifdef __GLIBC__
+ {
+ /* Please see gettext info file, node ``Being a `gettext' grok'', section
+ * ``Changing the language at runtime''.
+ */
+ extern int _nl_msg_cat_cntr;
+ _nl_msg_cat_cntr++;
+ }
+#endif
+}
+
+static inline void
+_restore_tz ()
+{
+ if (current_pth->tz == 0)
+ do_unsetenv ("TZ");
+ else
+ do_setenv ("TZ", current_pth->tz);
+}
+
+void
+pth_set_language (const char *lang)
+{
+ current_pth->lang = pstrdup (current_pth->pool, lang);
+ _restore_lang ();
+}
+
+void
+pth_set_tz (const char *tz)
+{
+ current_pth->tz = pstrdup (current_pth->pool, tz);
+ _restore_tz ();
+}
+
+inline void
+_pth_switch_thread_to_calling_context ()
+{
+ mctx_switch (¤t_pth->thread_ctx, ¤t_pth->calling_ctx);
+}
+
+inline void
+_pth_switch_calling_to_thread_context (pseudothread pth)
+{
+ current_pth = pth; /* Set current thread. */
+ mctx_switch (¤t_pth->calling_ctx, ¤t_pth->thread_ctx);
+}
+
+inline int
+_pth_alarm_received ()
+{
+ return current_pth->alarm_received;
+}
+
+static void
+block (int sock, int operations)
+{
+ /* Register a read event in the reactor. */
+ reactor_handle handle
+ = reactor_register (sock, operations, return_from_block, current_pth);
+
+ /* Swap context back to the calling context. */
+ _pth_switch_thread_to_calling_context ();
+
+ /* Unregister the handle. */
+ reactor_unregister (handle);
+
+ /* Received alarm signal? - Exit. */
+ if (_pth_alarm_received ())
+ pth_exit ();
+
+ /* Restore environment. */
+ _restore_lang ();
+ _restore_tz ();
+}
+
+static void
+return_from_block (int sock, int events, void *vpth)
+{
+ /* Swap back to the thread context. */
+ _pth_switch_calling_to_thread_context ((pseudothread) vpth);
+}
+
+static void
+_sleep (int timeout)
+{
+ /* Register a timer in the reactor. */
+ reactor_timer timer
+ = reactor_set_timer (current_pth->pool, timeout,
+ return_from_sleep, current_pth);
+
+ /* Swap context back to the calling context. */
+ _pth_switch_thread_to_calling_context ();
+
+ /* Received alarm signal? - Exit. */
+ if (_pth_alarm_received ())
+ {
+ reactor_unset_timer_early (timer);
+ pth_exit ();
+ }
+
+ /* Note: no need to unregister the timer, since it has fired. */
+
+ /* Restore environment. */
+ _restore_lang ();
+ _restore_tz ();
+}
+
+static void
+return_from_sleep (void *vpth)
+{
+ /* Swap back to the thread context. */
+ _pth_switch_calling_to_thread_context ((pseudothread) vpth);
+}
+
+static void
+_poll (struct pollfd *fds, unsigned int n, int timeout)
+{
+ reactor_timer timer = 0;
+ reactor_handle handle[n];
+ int i;
+
+ (void) &timer; /* Tell gcc not to put it in a register. */
+ current_pth->poll_timeout = 0;
+
+ /* Register all events in the reactor. */
+ for (i = 0; i < n; ++i)
+ /* NB: Poll operations == reactor operations. */
+ handle[i] = reactor_register (fds[i].fd, fds[i].events,
+ return_from_poll, current_pth);
+
+ /* Timeout? */
+ if (timeout >= 0)
+ timer = reactor_set_timer (current_pth->pool, timeout,
+ return_from_poll_timeout, current_pth);
+
+ /* Swap context back to calling context. */
+ _pth_switch_thread_to_calling_context ();
+
+ /* Unregister all the handles. */
+ for (i = 0; i < n; ++i)
+ reactor_unregister (handle[i]);
+
+ /* Delete the timer, if it exists but hasn't fired. */
+ if (timer && !current_pth->poll_timeout)
+ reactor_unset_timer_early (timer);
+
+ /* Received alarm signal? Exit. */
+ if (_pth_alarm_received ())
+ pth_exit ();
+
+ /* Restore environment. */
+ _restore_lang ();
+ _restore_tz ();
+}
+
+static void
+return_from_poll (int sock, int events, void *vpth)
+{
+ /* Swap back to the thread context. */
+ _pth_switch_calling_to_thread_context ((pseudothread) vpth);
+}
+
+static void
+return_from_poll_timeout (void *vpth)
+{
+ pseudothread pth = (pseudothread) vpth;
+
+ pth->poll_timeout = 1;
+
+ /* Swap back to the thread context. */
+ _pth_switch_calling_to_thread_context (pth);
+}
+
+static void
+return_from_alarm (void *vpth)
+{
+ pseudothread pth = (pseudothread) vpth;
+
+ pth->alarm_received = 1;
+ pth->alarm_timer = 0;
+
+ /* Swap back to the thread context. */
+ _pth_switch_calling_to_thread_context (pth);
+}
+
+vector
+pseudothread_get_threads (pool pool)
+{
+ vector v = new_vector (pool, struct pseudothread);
+ int i;
+
+ for (i = 0; i < vector_size (threads); ++i)
+ {
+ pseudothread pth;
+ struct pseudothread pth_copy;
+
+ vector_get (threads, i, pth);
+
+ if (pth)
+ {
+ /* Perform a deep copy of the structure. */
+ memcpy (&pth_copy, pth, sizeof pth_copy);
+ if (pth_copy.name)
+ pth_copy.name = pstrdup (pool, pth_copy.name);
+ if (pth_copy.lang)
+ pth_copy.lang = pstrdup (pool, pth_copy.lang);
+ if (pth_copy.tz)
+ pth_copy.tz = pstrdup (pool, pth_copy.tz);
+
+ vector_push_back (v, pth_copy);
+ }
+ }
+
+ return v;
+}
+
+int
+pseudothread_count_threads (void)
+{
+ int count = 0;
+ int i;
+
+ for (i = 0; i < vector_size (threads); ++i)
+ {
+ pseudothread pth;
+
+ vector_get (threads, i, pth);
+
+ if (pth) count++;
+ }
+
+ return count;
+}
--- /dev/null
+/* Pseudothread handler.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_pseudothread.h,v 1.13 2002/12/01 14:29:30 rich Exp $
+ */
+
+#ifndef PTHR_PSEUDOTHREAD_H
+#define PTHR_PSEUDOTHREAD_H
+
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <setjmp.h>
+#include <time.h>
+
+#include <pool.h>
+#include <vector.h>
+
+#include "pthr_reactor.h"
+#include "pthr_context.h"
+
+struct pseudothread;
+typedef struct pseudothread *pseudothread;
+
+/* Function: current_pth - current pseudothread
+ *
+ * @code{current_pth} is a global variable containing the currently
+ * running thread.
+ */
+extern pseudothread current_pth;
+
+/* Function: pseudothread_set_stack_size - set and get default stack size
+ * Function: pseudothread_get_stack_size
+ *
+ * @code{pseudothread_set_stack_size} sets the stack
+ * size for newly created threads. The default stack size
+ * is 64 KBytes.
+ *
+ * @code{pseudothread_get_stack_size} returns the current
+ * stack size setting.
+ *
+ * See also: @ref{new_pseudothread(3)}.
+ */
+extern int pseudothread_set_stack_size (int size);
+extern int pseudothread_get_stack_size (void);
+
+/* Function: new_pseudothread - lightweight "pseudothreads" library
+ * Function: pth_start
+ * Function: pseudothread_get_threads
+ * Function: pseudothread_count_threads
+ *
+ * Pseudothreads are lightweight, cooperatively scheduled threads.
+ *
+ * @code{new_pseudothread} creates a new pseudothread. The thread
+ * only starts running when you call @code{pth_start}. The @code{pool}
+ * argument passed is used for all allocations within the thread.
+ * This pool is automatically deleted when the thread exits.
+ * The @code{name} argument is the name of the thread (used in
+ * thread listings -- see @ref{pseudothread_get_threads(3)}). You
+ * may change the name later. The @code{run} and @code{data}
+ * arguments are the entry point into the thread. The entry
+ * point is called as @code{run (data)}.
+ *
+ * @code{pseudothread_get_threads} returns a list of all the
+ * currently running pseudothreads. This allows you to implement
+ * a "process listing" for a program. The returned value
+ * is a vector of pseudothread structures (not pointers). These
+ * structures are opaque to you, but you can call the pth_get_*
+ * functions on the address of each one.
+ *
+ * @code{pseudothread_count_threads} counts the number of currently
+ * running threads.
+ */
+extern pseudothread new_pseudothread (pool, void (*run) (void *), void *data, const char *name);
+extern void pth_start (pseudothread pth);
+extern vector pseudothread_get_threads (pool);
+extern int pseudothread_count_threads (void);
+
+/* Function: pth_accept - pseudothread system calls
+ * Function: pth_connect
+ * Function: pth_read
+ * Function: pth_write
+ * Function: pth_sleep
+ * Function: pth_millisleep
+ * Function: pth_nanosleep
+ * Function: pth_timeout
+ *
+ * @code{pth_accept}, @code{pth_connect}, @code{pth_read}, @code{pth_write},
+ * @code{pth_sleep} and @code{pth_nanosleep} behave just like the
+ * corresponding system calls. However these calls handle non-blocking
+ * sockets and cause the thread to sleep on the reactor if it would
+ * block. For general I/O you will probably wish to wrap up your
+ * sockets in I/O handle objects, which give you a higher-level
+ * buffered interface to sockets (see @ref{io_fdopen(3)}).
+ *
+ * @code{pth_millisleep} sleeps for a given number of milliseconds.
+ *
+ * @code{pth_timeout} is similar to the @ref{alarm(2)} system call: it
+ * registers a timeout (in seconds). The thread will exit automatically
+ * (even in the middle of a system call) if the timeout is reached.
+ * To reset the timeout, call @code{pth_timeout} with a timeout of 0.
+ *
+ * See also: @ref{pth_poll(3)}, @ref{pth_select(3)}.
+ */
+extern int pth_accept (int, struct sockaddr *, int *);
+extern int pth_connect (int, struct sockaddr *, int);
+extern ssize_t pth_read (int, void *, size_t);
+extern ssize_t pth_write (int, const void *, size_t);
+extern int pth_sleep (int seconds);
+extern int pth_millisleep (int millis);
+extern int pth_nanosleep (const struct timespec *req, struct timespec *rem);
+extern void pth_timeout (int seconds);
+
+/* Function: pth_send - pseudothread network system calls
+ * Function: pth_sendto
+ * Function: pth_sendmsg
+ * Function: pth_recv
+ * Function: pth_recvfrom
+ * Function: pth_recvmsg
+ *
+ * @code{pth_send}, @code{pth_sendto}, @code{pth_sendmsg},
+ * @code{pth_recv}, @code{pth_recvfrom} and @code{pth_recvmsg}
+ * behave just like the corresponding system calls. However
+ * these calls handle non-blocking sockets and cause the
+ * thread to sleep on the reactor if it would block.
+ *
+ * See also: @ref{pth_poll(3)}, @ref{pth_read(3)}, @ref{pth_write(3)}.
+ */
+extern int pth_send (int s, const void *msg, int len, unsigned int flags);
+extern int pth_sendto (int s, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
+extern int pth_sendmsg (int s, const struct msghdr *msg, unsigned int flags);
+extern int pth_recv (int s, void *buf, int len, unsigned int flags);
+extern int pth_recvfrom (int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
+extern int pth_recvmsg (int s, struct msghdr *msg, unsigned int flags);
+
+/* Function: pth_poll - pseudothread poll and select system calls
+ * Function: pth_select
+ *
+ * @code{pth_poll} behaves like the @ref{poll(2)} system call. It
+ * specifies an array @code{n} of @code{fds} file descriptor structures
+ * each of which is checked for an I/O event. If @code{timeout} is
+ * greater than or equal to zero, then the call will return after
+ * this many milliseconds if no I/O events are detected. If @code{timeout}
+ * is negative, then the timeout is infinite.
+ *
+ * @code{pth_select} behaves like the @ref{select(2)} system call.
+ *
+ * Note that @code{pth_select} is implemented as a library on top
+ * of @code{pth_poll}, and is considerably less efficient than
+ * @code{pth_poll}. It is recommended that you rewrite any code
+ * which uses @code{pth_select}/@code{select} to use
+ * @code{pth_poll}/@code{poll} instead.
+ *
+ * See also: @ref{pth_timeout(3)}, @ref{pth_wait_readable(3)}.
+ */
+extern int pth_poll (struct pollfd *fds, unsigned int n, int timeout);
+extern int pth_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
+
+/* Function: pth_wait_readable - wait for a file descriptor to become readable or writable
+ * Function: pth_wait_writable
+ *
+ * @code{pth_wait_readable} waits until a given file descriptor (or
+ * socket) has data to read.
+ *
+ * @code{pth_wait_writable} waits until a given file descriptor (or
+ * socket) can be written without blocking.
+ *
+ * The @code{fd} must be set to non-blocking.
+ *
+ * Returns: Both functions return 0 if OK, or -1 if there was an error.
+ *
+ * See also: @ref{pth_poll(3)}.
+ */
+extern int pth_wait_readable (int fd);
+extern int pth_wait_writable (int fd);
+
+/* Function: pth_exit - exit a pseudothread and exception handling
+ * Function: pth_die
+ * Function: pth_catch
+ *
+ * @code{pth_exit} causes the current thread to exit immediately.
+ *
+ * @code{pth_die} is similar in concept to @code{pth_exit}, except
+ * that it throws an exception which may be caught by using the
+ * @code{pth_catch} function. The distinction between @code{pth_die}
+ * and @code{pth_exit} is the same as the distinction between
+ * the Perl functions @code{die} and @code{exit}, in as much as
+ * @code{exit} in Perl always exits the process immediately, and
+ * @code{die} in Perl generally exits the process immediately
+ * unless the programmer catches the exception with @code{eval}
+ * and handles it appropriately.
+ *
+ * @code{pth_catch} is used to catch exceptions thrown by
+ * @code{pth_die}. You give @code{fn} (function) and @code{data}
+ * arguments, and the function calls @code{fn (data)}. If, during
+ * this call, the code calls @code{pth_die}, then the exception
+ * message is immediately returned from @code{pth_catch}. If
+ * the code runs successfully to completion, then @code{pth_catch}
+ * will return @code{NULL}.
+ *
+ * Exceptions may be nested.
+ *
+ * Note that @code{pth_catch} will not catch calls to @code{pth_exit}.
+ */
+extern void pth_exit (void) __attribute__((noreturn));
+#define pth_die(msg) _pth_die ((msg), __FILE__, __LINE__)
+extern const char *pth_catch (void (*fn) (void *), void *data);
+
+extern void _pth_die (const char *msg,
+ const char *filename, int lineno)
+ __attribute__((noreturn));
+
+/* Function: pth_get_pool - get and set status of pseudothreads
+ * Function: pth_get_name
+ * Function: pth_get_thread_num
+ * Function: pth_get_run
+ * Function: pth_get_data
+ * Function: pth_get_language
+ * Function: pth_get_tz
+ * Function: pth_get_stack
+ * Function: pth_get_stack_size
+ * Function: pth_get_PC
+ * Function: pth_get_SP
+ * Function: pth_set_name
+ * Function: pth_set_language
+ * Function: pth_set_tz
+ *
+ * @code{pth_get_pool} returns the pool associated with this thread.
+ * The thread should use this pool for all allocations, ensuring that
+ * they are cleaned up correctly when the thread is deleted. See
+ * @ref{new_pool(3)}.
+ *
+ * @code{pth_get_name} returns the name of the thread. Note that
+ * this string is normally stored in thread's local pool, and therefore
+ * the string memory may be unexpected deallocated if the thread
+ * exits. Callers should take a copy of the string in their own pool
+ * if they are likely to need the string for a long period of time.
+ *
+ * @code{pth_get_thread_num} returns the thread number (roughly
+ * the equivalent of a process ID).
+ *
+ * @code{pth_get_run} and @code{pth_get_data} return the original
+ * entry point information of the thread.
+ *
+ * @code{pth_get_language} returns the value of the @code{LANGUAGE}
+ * environment variable for this thread. See the discussion
+ * of @code{pth_set_language} below.
+ *
+ * @code{pth_get_language} returns the value of the @code{TZ}
+ * environment variable for this thread. See the discussion
+ * of @code{pth_set_tz} below.
+ *
+ * @code{pth_get_stack} returns the top of the stack for this
+ * thread.
+ *
+ * @code{pth_get_stack_size} returns the maximum size of the stack for
+ * this thread.
+ *
+ * @code{pth_get_PC} returns the current program counter (PC) for
+ * this thread. Obviously it only makes sense to call this from
+ * another thread.
+ *
+ * @code{pth_get_SP} returns the current stack pointer (SP) for
+ * this thread. Obviously it only makes sense to call this from
+ * another thread.
+ *
+ * @code{pth_set_name} changes the name of the thread. The string
+ * @code{name} argument passed must be either statically allocated,
+ * or allocated in the thread's own pool (the normal case), or else
+ * allocated in another pool with a longer life than the current
+ * thread.
+ *
+ * @code{pth_set_language} changes the per-thread @code{LANGUAGE}
+ * environment variable. This is useful when serving clients from
+ * multiple locales, and using GNU gettext for translation.
+ *
+ * @code{pth_set_tz} changes the per-thread @code{TZ}
+ * environment variable. This is useful when serving clients from
+ * multiple timezones, and using @code{localtime} and related
+ * functions.
+ */
+extern pool pth_get_pool (pseudothread pth);
+extern const char *pth_get_name (pseudothread pth);
+extern int pth_get_thread_num (pseudothread pth);
+extern void (*pth_get_run (pseudothread pth)) (void *);
+extern void *pth_get_data (pseudothread pth);
+extern const char *pth_get_language (pseudothread pth);
+extern const char *pth_get_tz (pseudothread pth);
+extern void *pth_get_stack (pseudothread pth);
+extern int pth_get_stack_size (pseudothread pth);
+extern unsigned long pth_get_PC (pseudothread pth);
+extern unsigned long pth_get_SP (pseudothread pth);
+extern void pth_set_name (const char *name);
+extern void pth_set_language (const char *lang);
+extern void pth_set_tz (const char *tz);
+
+/* These low-level functions are used by other parts of the pthrlib library.
+ * Do not use them from user programs. They switch thread context with the
+ * calling context and v.v.
+ */
+extern void _pth_switch_thread_to_calling_context (void);
+extern void _pth_switch_calling_to_thread_context (pseudothread new_pth);
+extern int _pth_alarm_received (void);
+
+#endif /* PTHR_PSEUDOTHREAD_H */
--- /dev/null
+/* A specialized Reactor.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_reactor.c,v 1.6 2002/08/21 10:42:19 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "pthr_reactor.h"
+
+#define REACTOR_DEBUG 0
+
+struct reactor_handle
+{
+ int offset; /* Points into internal poll fds array. */
+ void (*fn) (int, int, void *);
+ void *data;
+};
+
+struct reactor_timer
+{
+ pool pool;
+ struct reactor_timer *prev, *next;
+ unsigned long long delta;
+ void (*fn) (void *);
+ void *data;
+};
+
+struct reactor_prepoll
+{
+ pool pool;
+ struct reactor_prepoll *next;
+ void (*fn) (void *);
+ void *data;
+ int fired;
+};
+
+/* This is how HANDLES and POLL_ARRAY work:
+ *
+ * HANDLES is a straightforward list of reactor handle objects. When
+ * a user registers an event handler in the reactor, they get back
+ * an integer which is in fact an offset into the HANDLES array.
+ *
+ * POLL_ARRAY is the array which actually gets passed to the poll(2)
+ * system call.
+ *
+ * HANDLES and POLL_ARRAY are related through the OFFSET field in
+ * a reactor handle. Thus:
+ *
+ * HANDLES: +------+------+------+------+------+
+ * offset: | 2 | 1 | 3 | 3 | 0 |
+ * | | | | | |
+ * | | | | | \ | | | | |
+ * | | | | | \ | | | | |
+ * +-|----+-|----+----\-+-|----+-|----+
+ * \_____|_____ \ | |
+ * POLL_ARRAY: +------+-|----+\-----\-|----+ |
+ * | | | | \ |\ | |
+ * | | | | | |
+ * | _______________________/
+ * | | | | |
+ * +------+------+------+------+
+ *
+ * Note that you may have two handles pointing to the same offset
+ * in POLL_ARRAY: this happens when we share pollfds because they
+ * both contain the same fd and event information.
+ *
+ * If the OFFSET field of a handle contains -1, then the handle
+ * is unused. Notice that we never decrease the size of the HANDLES
+ * array.
+ *
+ * Adding (registering) a handle is easy enough: firstly we look for
+ * an unused (offset == -1) handle, and if not found we reallocate
+ * the HANDLES array to make it larger. Then we search through the
+ * POLL_ARRAY to see if it's possible to share. If not, we extend
+ * the POLL_ARRAY by 1 and set the offset accordingly.
+ *
+ * Deleting (unregistering) a handle is not so easy: deleting the
+ * handle is OK -- just set the offset to -1. But we must also
+ * check the POLL_ARRAY entry, and if it is not shared, delete it,
+ * shrink the POLL_ARRAY and update all handles which point to
+ * an offset larger than the deleted element in POLL_ARRAY.
+ */
+
+
+/* The list of reactor handles registered. */
+static struct reactor_handle *handles = 0;
+static struct pollfd *poll_array = 0;
+static int nr_handles_allocated = 0;
+static int nr_array_allocated = 0;
+static int nr_array_used = 0;
+
+/* The list of timers, stored in time order (in a delta queue). */
+static struct reactor_timer *head_timer = 0;
+
+/* The list of prepoll handlers in no particular order. */
+static struct reactor_prepoll *head_prepoll = 0;
+
+/* The current time, or near as dammit, in milliseconds from Unix epoch. */
+unsigned long long reactor_time;
+
+/* Function prototypes. */
+static void remove_timer (void *timerp);
+static void remove_prepoll (void *timerp);
+
+/* Cause reactor_init / reactor_stop to be called automatically. */
+static void reactor_init (void) __attribute__ ((constructor));
+static void reactor_stop (void) __attribute__ ((destructor));
+
+static void
+reactor_init ()
+{
+ struct timeval tv;
+ struct sigaction sa;
+
+ /* Catch EPIPE errors rather than sending a signal. */
+ memset (&sa, 0, sizeof sa);
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_RESTART;
+ sigaction (SIGPIPE, &sa, 0);
+
+ /* Update the reactor time. */
+ gettimeofday (&tv, 0);
+ reactor_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
+}
+
+static void
+reactor_stop ()
+{
+ int i;
+ reactor_timer p, p_next;
+ reactor_prepoll prepoll, prepoll_next;
+
+ /* There should be no prepoll handlers registered. Check it and free them.*/
+ for (prepoll = head_prepoll; prepoll; prepoll = prepoll_next)
+ {
+ syslog (LOG_WARNING, "prepoll handler left registered in reactor: fn=%p, data=%p",
+ prepoll->fn, prepoll->data);
+ prepoll_next = prepoll;
+ delete_pool (prepoll->pool);
+ }
+
+ /* There should be no timers registered. Check it and free them up. */
+ for (p = head_timer; p; p = p_next)
+ {
+ syslog (LOG_WARNING, "timer left registered in reactor: fn=%p, data=%p",
+ p->fn, p->data);
+ p_next = p->next;
+ delete_pool (p->pool);
+ }
+
+ /* There should be no handles registered. Check for this. */
+ for (i = 0; i < nr_handles_allocated; ++i)
+ if (handles[i].offset >= 0)
+ syslog (LOG_WARNING, "handle left registered in reactor: fn=%p, data=%p",
+ handles[i].fn, handles[i].data);
+
+ /* Free up memory used by handles. */
+ if (handles) free (handles);
+ if (poll_array) free (poll_array);
+}
+
+reactor_handle
+reactor_register (int socket, int operations,
+ void (*fn) (int socket, int events, void *), void *data)
+{
+ int i, h, a;
+
+ /* Find an unused handle. */
+ for (i = 0; i < nr_handles_allocated; ++i)
+ if (handles[i].offset == -1)
+ {
+ h = i;
+ goto found_handle;
+ }
+
+ /* No free handles: allocate a new handle. */
+ h = nr_handles_allocated;
+ nr_handles_allocated += 8;
+ handles = realloc (handles,
+ nr_handles_allocated * sizeof (struct reactor_handle));
+ for (i = h; i < nr_handles_allocated; ++i)
+ handles[i].offset = -1;
+
+ found_handle:
+ /* Examine the poll descriptors to see if we can share with an
+ * existing descriptor.
+ */
+ for (a = 0; a < nr_array_used; ++a)
+ if (poll_array[a].fd == socket &&
+ poll_array[a].events == operations)
+ goto found_poll_descriptor;
+
+ /* Allocate space in the array of poll descriptors. */
+ if (nr_array_used >= nr_array_allocated)
+ {
+ nr_array_allocated += 8;
+ poll_array = realloc (poll_array,
+ nr_array_allocated * sizeof (struct pollfd));
+ }
+ a = nr_array_used++;
+
+ /* Create the poll descriptor. */
+ poll_array[a].fd = socket;
+ poll_array[a].events = operations;
+
+ found_poll_descriptor:
+ /* Create the handle. */
+ handles[h].offset = a;
+ handles[h].fn = fn;
+ handles[h].data = data;
+
+#if REACTOR_DEBUG
+ fprintf (stderr,
+ "reactor_register (fd=%d, ops=0x%x, fn=%p, data=%p) = %p\n",
+ socket, operations, fn, data, &handles[h]);
+#endif
+
+ /* Return the handle. */
+ return h;
+}
+
+void
+reactor_unregister (reactor_handle handle)
+{
+ int i, a = handles[handle].offset;
+
+#if REACTOR_DEBUG
+ fprintf (stderr,
+ "reactor_unregister (handle=%d [fd=%d, ops=0x%x])\n",
+ handle, poll_array[a].fd, poll_array[a].events);
+#endif
+
+ handles[handle].offset = -1;
+
+ /* Does any other handle share this element? If so, leave POLL_ARRAY alone.
+ */
+ for (i = 0; i < nr_handles_allocated; ++i)
+ if (handles[i].offset == a)
+ return;
+
+ /* Not shared. Remove this element from poll_array. */
+ memcpy (&poll_array[a], &poll_array[a+1],
+ (nr_array_used - a - 1) * sizeof (struct pollfd));
+ nr_array_used --;
+
+ /* Any handles in used which use offset > a should be updated. */
+ for (i = 0; i < nr_handles_allocated; ++i)
+ if (handles[i].offset > a)
+ handles[i].offset --;
+}
+
+reactor_timer
+reactor_set_timer (pool pp,
+ int timeout,
+ void (*fn) (void *data),
+ void *data)
+{
+ pool sp;
+ reactor_timer timer, p, last_p;
+ unsigned long long trigger_time, this_time;
+
+ sp = new_subpool (pp);
+
+ timer = pmalloc (sp, sizeof *timer);
+
+ timer->pool = sp;
+ timer->fn = fn;
+ timer->data = data;
+
+ /* Register a function to clean up this timer when the subpool is deleted.*/
+ pool_register_cleanup_fn (sp, remove_timer, timer);
+
+ /* Calculate the trigger time. */
+ trigger_time = reactor_time + timeout;
+
+ if (head_timer == 0) /* List is empty. */
+ {
+ timer->prev = timer->next = 0;
+ timer->delta = trigger_time;
+ return head_timer = timer;
+ }
+
+ /* Find out where to insert this handle in the delta queue. */
+ this_time = 0;
+ last_p = 0;
+ for (p = head_timer; p; last_p = p, p = p->next)
+ {
+ this_time += p->delta;
+
+ if (this_time >= trigger_time) /* Insert before element p. */
+ {
+ timer->prev = p->prev;
+ timer->next = p;
+ if (p->prev) /* Not the first element. */
+ p->prev->next = timer;
+ else /* First element in list. */
+ head_timer = timer;
+ p->prev = timer;
+ timer->delta = trigger_time - (this_time - p->delta);
+ p->delta = this_time - trigger_time;
+ return timer;
+ }
+ }
+
+ /* Insert at the end of the list. */
+ last_p->next = timer;
+ timer->prev = last_p;
+ timer->next = 0;
+ timer->delta = trigger_time - this_time;
+ return timer;
+}
+
+static void
+remove_timer (void *timerp)
+{
+ struct reactor_timer *timer = (struct reactor_timer *) timerp;
+
+ /* Remove this timer from the list. */
+ if (timer->prev != 0)
+ timer->prev->next = timer->next;
+ else
+ head_timer = timer->next;
+
+ if (timer->next != 0)
+ {
+ timer->next->prev = timer->prev;
+ timer->next->delta += timer->delta;
+ }
+}
+
+void
+reactor_unset_timer_early (reactor_timer timer)
+{
+ delete_pool (timer->pool);
+}
+
+reactor_prepoll
+reactor_register_prepoll (pool pp,
+ void (*fn) (void *data),
+ void *data)
+{
+ pool sp;
+ reactor_prepoll p;
+
+ sp = new_subpool (pp);
+
+ p = pmalloc (sp, sizeof *p);
+
+ p->pool = sp;
+ p->fn = fn;
+ p->data = data;
+
+ pool_register_cleanup_fn (sp, remove_prepoll, p);
+
+ p->next = head_prepoll;
+ head_prepoll = p;
+
+ return p;
+}
+
+static void
+remove_prepoll (void *handlep)
+{
+ reactor_prepoll handle = (reactor_prepoll) handlep, prev = 0, this;
+
+ /* Find this handle in the list. */
+ for (this = head_prepoll;
+ this && this != handle;
+ prev = this, this = this->next)
+ ;
+
+ assert (this == handle);
+
+ if (prev == 0) { /* Remove head handler. */
+ head_prepoll = head_prepoll->next;
+ } else { /* Remove inner handler. */
+ prev->next = this->next;
+ }
+}
+
+void
+reactor_unregister_prepoll (reactor_prepoll handle)
+{
+ delete_pool (handle->pool);
+}
+
+void
+reactor_invoke ()
+{
+ int i, r, a;
+ reactor_prepoll prepoll;
+ struct timeval tv;
+
+#if 0
+ /* Update the reactor time. */
+ gettimeofday (&tv, 0);
+ reactor_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
+#endif
+
+ /* Fire any timers which are ready. */
+ while (head_timer != 0 && head_timer->delta <= reactor_time)
+ {
+ reactor_timer timer;
+ void (*fn) (void *);
+ void *data;
+
+ /* Calling fn might change head_timer. */
+ timer = head_timer;
+ fn = timer->fn;
+ data = timer->data;
+
+ /* Remove the timer from the queue now (this avoids a rare race
+ * condition exposed if code calls pth_sleep followed immediately
+ * by pth_exit).
+ */
+ reactor_unset_timer_early (timer);
+
+ fn (data);
+ }
+
+ /* Run the prepoll handlers. This is tricky -- we have to check
+ * (a) that we run every prepoll handler, even if new ones are
+ * added while we are running them, and (b) that we don't accidentally
+ * hit a prepoll handler which has been removed. The only thing we
+ * can be sure of is that HEAD_PREPOLL is always valid. Anything it
+ * points to can change any time we call a handler. Therefore this
+ * is how we do it: (1) go through the list, marked all of the handlers
+ * as not fired (ie. clearing the FIRED flag); (2) go through the list
+ * looking for the first non-fired handle, mark it as fired and run
+ * it; (3) repeat step (2) until there are no more non-fired handles.
+ */
+ for (prepoll = head_prepoll; prepoll; prepoll = prepoll->next)
+ prepoll->fired = 0;
+
+ prepoll_again:
+ for (prepoll = head_prepoll;
+ prepoll && prepoll->fired;
+ prepoll = prepoll->next)
+ ;
+
+ if (prepoll)
+ {
+ prepoll->fired = 1;
+ prepoll->fn (prepoll->data);
+ goto prepoll_again;
+ }
+
+ /* Poll file descriptors. */
+ if (nr_array_used >= 0)
+ {
+#if REACTOR_DEBUG
+ fprintf (stderr, "reactor_invoke: poll [");
+ for (i = 0; i < nr_array_used; ++i)
+ fprintf (stderr, "(fd=%d, ops=0x%x)",
+ poll_array[i].fd, poll_array[i].events);
+ fprintf (stderr, "]\n");
+#endif
+
+ r = poll (poll_array, nr_array_used,
+ head_timer ? head_timer->delta - reactor_time : -1);
+
+ /* Update the reactor time. */
+ gettimeofday (&tv, 0);
+ reactor_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
+
+#if REACTOR_DEBUG
+ fprintf (stderr, "reactor_invoke: poll returned %d [", r);
+ for (i = 0; i < nr_array_used; ++i)
+ if (poll_array[i].revents != 0)
+ fprintf (stderr, "(fd=%d, ops=0x%x)",
+ poll_array[i].fd, poll_array[i].revents);
+ fprintf (stderr, "]\n");
+#endif
+
+ if (r > 0) /* Some descriptors are ready. */
+ {
+ /* Check for events happening in the array, but go via the
+ * handles because there is no back pointer back to the
+ * handles from the array. Surprisingly enough, this code
+ * appears to be free of race conditions (note that calling
+ * fn can register/unregister handles).
+ */
+ for (i = 0; i < nr_handles_allocated; ++i)
+ {
+ a = handles[i].offset;
+
+ if (a >= 0 && poll_array[a].revents != 0)
+ {
+ handles[i].fn (poll_array[a].fd, poll_array[a].revents,
+ handles[i].data);
+ }
+ }
+ }
+ else if (r == 0) /* The head timer has fired. */
+ {
+ reactor_timer timer;
+ void (*fn) (void *);
+ void *data;
+
+ /* Calling fn might change head_timer. */
+ timer = head_timer;
+ fn = timer->fn;
+ data = timer->data;
+
+ /* Remove the timer from the queue now (this avoids a rare race
+ * condition exposed if code calls pth_sleep followed immediately
+ * by pth_exit).
+ */
+ reactor_unset_timer_early (timer);
+
+ fn (data);
+ }
+ }
+}
--- /dev/null
+/* A specialized Reactor.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_reactor.h,v 1.3 2002/08/21 10:42:20 rich Exp $
+ */
+
+#ifndef PTHR_REACTOR_H
+#define PTHR_REACTOR_H
+
+#include <sys/poll.h>
+
+#include <pool.h>
+
+/* A reactor handle. */
+struct reactor_handle;
+typedef int reactor_handle; /* These are offsets into a private array
+ * of struct reactor_handle.
+ */
+
+/* A timer. */
+struct reactor_timer;
+typedef struct reactor_timer *reactor_timer;
+
+/* Pre-poll handlers. */
+struct reactor_prepoll;
+typedef struct reactor_prepoll *reactor_prepoll;
+
+/* Reactor operations. */
+#define REACTOR_READ POLLIN
+#define REACTOR_WRITE POLLOUT
+
+/* Reactor time types. */
+typedef unsigned long long reactor_time_t;
+typedef signed long long reactor_timediff_t;
+
+/* Reactor time in milliseconds from Unix epoch. */
+extern reactor_time_t reactor_time;
+
+/* Reactor functions. */
+extern reactor_handle reactor_register (int socket, int operations,
+ void (*fn) (int socket, int events,
+ void *data),
+ void *data);
+extern void reactor_unregister (reactor_handle handle);
+extern reactor_timer reactor_set_timer (pool, int timeout,
+ void (*fn) (void *data),
+ void *data);
+extern void reactor_unset_timer_early (reactor_timer timer);
+extern reactor_prepoll reactor_register_prepoll (pool, void (*fn) (void *data),
+ void *data);
+extern void reactor_unregister_prepoll (reactor_prepoll handle);
+extern void reactor_invoke (void);
+
+#endif /* PTHR_REACTOR_H */
--- /dev/null
+/* Multiple reader / single writer locks for pthrlib.
+ * by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_rwlock.c,v 1.3 2002/12/01 14:29:30 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <pool.h>
+#include <hash.h>
+
+#include "pthr_pseudothread.h"
+#include "pthr_wait_queue.h"
+#include "pthr_rwlock.h"
+
+/* I added this while I was trying to pin down a possible memory corruption
+ * problem. It can be disabled in normal operations.
+ */
+/* #define RWLOCK_MEM_DEBUG 1 */
+#define RWLOCK_MEM_DEBUG 0
+
+#if RWLOCK_MEM_DEBUG
+#define RWLOCK_MEM_MAGIC 0x99775533
+#endif
+
+struct rwlock
+{
+#if RWLOCK_MEM_DEBUG
+ unsigned magic;
+#endif
+ int n; /* If N == 0, lock is free.
+ * If N > 0, lock is held by N readers.
+ * If N == -1, lock is held by 1 writer.
+ */
+ wait_queue writers_wq; /* Writers wait on this queue. */
+ wait_queue readers_wq; /* Readers wait on this queue. */
+
+ /* A hash from pth pointer -> subpool. The keys of this hash are
+ * pseudothreads which are currently in the critical section. The
+ * values are subpools of the appropriate pth pool. If a thread
+ * exits without releasing the lock, then the subpool is deleted,
+ * which causes our callback to run, releasing the lock.
+ */
+ hash pools;
+
+ unsigned writers_have_priority:1;
+};
+
+static void _do_enter (rwlock);
+static void _do_release (void *);
+static void _delete_rwlock (void *);
+
+rwlock
+new_rwlock (pool p)
+{
+ rwlock rw = pmalloc (p, sizeof *rw);
+
+#if RWLOCK_MEM_DEBUG
+ rw->magic = RWLOCK_MEM_MAGIC;
+#endif
+
+ rw->n = 0;
+ rw->readers_wq = new_wait_queue (p);
+ rw->writers_wq = new_wait_queue (p);
+ rw->writers_have_priority = 1;
+ rw->pools = new_hash (p, pseudothread, pool);
+
+ /* The purpose of this cleanup is just to check that the rwlock
+ * isn't released with threads in the critical section.
+ */
+ pool_register_cleanup_fn (p, _delete_rwlock, rw);
+
+ return rw;
+}
+
+static void
+_delete_rwlock (void *vrw)
+{
+ rwlock rw = (rwlock) vrw;
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+ assert (rw->n == 0);
+}
+
+/* Calling this function changes the nature of the lock so that
+ * writers have priority over readers. If this is the case then
+ * new readers will not be able to enter a critical section if
+ * there are writers waiting to enter.
+ * [NB: This is the default.]
+ */
+void
+rwlock_writers_have_priority (rwlock rw)
+{
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+ rw->writers_have_priority = 1;
+}
+
+/* Calling this function changes the nature of the lock so that
+ * readers have priority over writers. Note that if this is the case
+ * then writers are likely to be starved if the lock is frequently
+ * read.
+ */
+void
+rwlock_readers_have_priority (rwlock rw)
+{
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+ rw->writers_have_priority = 0;
+}
+
+/* This function is identical to RWLOCK_ENTER_READ, but it
+ * does not block. It returns TRUE if the lock was successfully
+ * acquired, or FALSE if the operation would block.
+ */
+inline int
+rwlock_try_enter_read (rwlock rw)
+{
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+
+ if (rw->n >= 0 &&
+ (!rw->writers_have_priority ||
+ wq_nr_sleepers (rw->writers_wq) == 0))
+ {
+ _do_enter (rw);
+ rw->n++;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* This function is identical to RWLOCK_ENTER_WRITE, but it
+ * does not block. It returns TRUE if the lock was successfully
+ * acquired, or FALSE if the operation would block.
+ */
+inline int
+rwlock_try_enter_write (rwlock rw)
+{
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+
+ if (rw->n == 0)
+ {
+ _do_enter (rw);
+ rw->n = -1;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Enter a critical section as a reader. Any number of readers
+ * are allowed to enter a critical section at the same time. This
+ * function may block.
+ */
+void
+rwlock_enter_read (rwlock rw)
+{
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+
+ while (rwlock_try_enter_read (rw) == 0)
+ wq_sleep_on (rw->readers_wq);
+}
+
+/* Enter a critical section as a writer. Only a single writer
+ * is allowed to enter a critical section, and then only if
+ * there are no readers. This function may block.
+ */
+void
+rwlock_enter_write (rwlock rw)
+{
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+
+ while (rwlock_try_enter_write (rw) == 0)
+ wq_sleep_on (rw->writers_wq);
+}
+
+/* Leave a critical section. */
+void
+rwlock_leave (rwlock rw)
+{
+ pool pool;
+
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+
+ /* If this core dumps, it's probably because the pth didn't actually
+ * hold a lock.
+ */
+ if (!hash_get (rw->pools, current_pth, pool)) abort ();
+
+ /* Force _DO_RELEASE to run. */
+ delete_pool (pool);
+}
+
+struct cleanup_data
+{
+ pseudothread pth;
+ rwlock rw;
+};
+
+/* This function registers a clean-up function which deals with the
+ * case when a thread exits early without releasing the lock.
+ */
+static void
+_do_enter (rwlock rw)
+{
+ struct cleanup_data *data;
+ pool pool;
+
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+
+ /* Create a subpool. If the thread exits early, then this subpool
+ * with be deleted implicitly. If, on the other hand, we release
+ * the lock in RWLOCK_LEAVE, then we will delete this pool
+ * explicitly. Either way, _DO_RELEASE will be called.
+ */
+ pool = new_subpool (pth_get_pool (current_pth));
+
+ /* Save it in the hash. */
+ hash_insert (rw->pools, current_pth, pool);
+
+ /* Register a clean-up function in the subpool to call _DO_RELEASE. */
+ data = pmalloc (pool, sizeof (struct cleanup_data));
+ data->pth = current_pth;
+ data->rw = rw;
+ pool_register_cleanup_fn (pool, _do_release, data);
+}
+
+/* This function is called to do the actual work of releasing a lock. */
+static void
+_do_release (void *vdata)
+{
+ struct cleanup_data *data = (struct cleanup_data *)vdata;
+ pseudothread pth = data->pth;
+ rwlock rw = data->rw;
+
+#if RWLOCK_MEM_DEBUG
+ assert (rw->magic == RWLOCK_MEM_MAGIC);
+#endif
+
+ assert (rw->n != 0);
+
+ /* Remove this pseudothread from rw->pools. */
+ if (!hash_erase (rw->pools, pth)) abort ();
+
+ if (rw->n > 0) /* Reader leaving critical section? */
+ {
+ rw->n --;
+ if (rw->n == 0)
+ {
+ /* Any writers waiting? */
+ if (wq_nr_sleepers (rw->writers_wq) > 0)
+ wq_wake_up_one (rw->writers_wq);
+
+ /* This can't happen (probably). */
+ /* XXX It does happen -- but I believe it's not a mistake. */
+ /*assert (wq_nr_sleepers (rw->readers_wq) == 0);*/
+ }
+ }
+ else /* Writer leaving critical section? */
+ {
+ rw->n = 0;
+
+ /* Any writers waiting? */
+ if (wq_nr_sleepers (rw->writers_wq) > 0)
+ wq_wake_up_one (rw->writers_wq);
+ /* Any readers waiting? */
+ else if (wq_nr_sleepers (rw->readers_wq) > 0)
+ wq_wake_up_one (rw->readers_wq);
+ }
+}
--- /dev/null
+/* Multiple reader / single writer locks for pthrlib.
+ * by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_rwlock.h,v 1.3 2002/12/01 14:29:30 rich Exp $
+ */
+
+#ifndef PTHR_RWLOCK_H
+#define PTHR_RWLOCK_H
+
+#include <pool.h>
+#include <hash.h>
+
+#include "pthr_pseudothread.h"
+#include "pthr_wait_queue.h"
+
+struct rwlock;
+typedef struct rwlock *rwlock;
+
+/* Function: new_rwlock - multiple reader / single writer locks (rwlocks)
+ * Function: rwlock_writers_have_priority
+ * Function: rwlock_readers_have_priority
+ * Function: rwlock_enter_read
+ * Function: rwlock_enter_write
+ * Function: rwlock_try_enter_read
+ * Function: rwlock_try_enter_write
+ * Function: rwlock_leave
+ *
+ * Multiple reader / single writer locks (rwlocks) do what they
+ * say. They allow either many readers to access a critical section
+ * or a single writer (but not both). If instead you require simple
+ * mutex semantics, then please see <pthr_mutex.h>.
+ *
+ * RWlocks are automatically released if they are being held when
+ * the thread exits.
+ *
+ * RWlocks are not ``upgradable''. The implementation is too
+ * complex to justify that.
+ *
+ * Note that there are possible deadlocks when using locks. To
+ * avoid deadlocks, always ensure every thread acquires locks
+ * in the same order.
+ *
+ * @code{new_rwlock} creates a new rwlock object.
+ *
+ * @code{rwlock_writers_have_priority} changes the nature of the lock so that
+ * writers have priority over readers. If this is the case then
+ * new readers will not be able to enter a critical section if
+ * there are writers waiting to enter.
+ * [NB: This is the default.]
+ *
+ * @code{rwlock_readers_have_priority} changes the nature of the lock so that
+ * readers have priority over writers. Note that if this is the case
+ * then writers are likely to be starved if the lock is frequently
+ * read.
+ *
+ * @code{rwlock_enter_read} enters the critical section as a reader.
+ * Any number of readers are allowed to enter a critical section at
+ * the same time. This function may block.
+ *
+ * @code{rwlock_enter_write} enters the critical section as a writer.
+ * Only a single writer is allowed to enter a critical section, and
+ * then only if there are no readers. This function may block.
+ *
+ * @code{rwlock_try_enter_read} is identical to
+ * @code{rwlock_enter_read}, but it
+ * does not block. It returns true if the lock was successfully
+ * acquired, or false if the operation would block.
+ *
+ * @code{rwlock_try_enter_write} is identical to
+ * @code{rwlock_enter_write}, but it
+ * does not block. It returns true if the lock was successfully
+ * acquired, or false if the operation would block.
+ *
+ * @code{rwlock_leave} leaves the critical section.
+ *
+ * Bugs: A common mistake is to accidentally call @code{rwlock_leave}
+ * when you are not holding the lock. This generally causes the
+ * library to crash.
+ */
+extern rwlock new_rwlock (pool);
+extern void rwlock_writers_have_priority (rwlock);
+extern void rwlock_readers_have_priority (rwlock);
+extern void rwlock_enter_read (rwlock);
+extern void rwlock_enter_write (rwlock);
+extern int rwlock_try_enter_read (rwlock);
+extern int rwlock_try_enter_write (rwlock);
+extern void rwlock_leave (rwlock);
+
+#endif /* PTHR_RWLOCK_H */
--- /dev/null
+/* Generic server process.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_server.c,v 1.10 2003/02/05 22:13:33 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include "pthr_listener.h"
+#include "pthr_server.h"
+
+static int default_port = 80;
+static char port_option_name = 'p';
+static in_addr_t default_address = INADDR_ANY;
+static char address_option_name = 'a';
+static int disable_syslog = 0;
+static const char *package_name = PACKAGE " " VERSION;
+static int disable_fork = 0;
+static int disable_chdir = 0;
+static int disable_close = 0;
+static const char *root = 0;
+static const char *username = 0;
+static const char *stderr_file = 0;
+static void (*startup_fn)(int argc, char *argv[]) = 0;
+static int enable_stack_trace_on_segv = 0;
+
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
+#define CAN_CATCH_SIGSEGV
+#endif
+
+#ifdef CAN_CATCH_SIGSEGV
+static void catch_sigsegv (int);
+#endif
+
+extern char *optarg;
+extern int optind;
+extern int opterr;
+
+#ifdef __OpenBSD__
+extern int optreset;
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((in_addr_t) 0xffffffff)
+#endif
+
+void
+pthr_server_main_loop (int argc, char *argv[],
+ void (*processor_fn) (int sock, void *))
+{
+ struct sockaddr_in addr;
+ int port = default_port;
+ in_addr_t address = default_address;
+ int sock, one = 1;
+ int c;
+ char getopt_scr[10];
+
+ /* Reset the getopt library. */
+ optind = 1;
+ opterr = 0;
+
+#ifdef __OpenBSD__
+ optreset = 1;
+#endif
+
+ /* Handle command-line arguments. Get the correct port and address to
+ * listen on.
+ */
+ snprintf (getopt_scr, sizeof getopt_scr,
+ "%c:%c:", port_option_name, address_option_name);
+
+ while ((c = getopt (argc, argv, getopt_scr)) != -1)
+ {
+ if (port_option_name == c)
+ {
+ if (sscanf (optarg, "%d", &port) != 1)
+ {
+ fprintf (stderr, "invalid port option: %s\n", optarg);
+ exit (1);
+ }
+ }
+ else if (address_option_name == c)
+ {
+ /* Should really use inet_aton() (or even inet_pton())
+ * but inet_addr() is more widely supported.
+ */
+ address = inet_addr (optarg);
+ if (INADDR_NONE == address)
+ {
+ fprintf (stderr, "invalid address: %s\n", optarg);
+ exit(1);
+ }
+ }
+ }
+
+ /* Bind a socket to the appropriate port. */
+ sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0) abort ();
+
+ setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = address;
+ addr.sin_port = htons (port);
+ if (bind (sock, (struct sockaddr *) &addr, sizeof addr) < 0)
+ {
+ /* Generally this means that the port is already bound. */
+ perror ("bind");
+ exit (1);
+ }
+
+ /* Put the socket into listen mode. */
+ if (listen (sock, 10) < 0) abort ();
+
+ /* Set the new socket to non-blocking. */
+ if (fcntl (sock, F_SETFL, O_NONBLOCK) < 0) abort ();
+
+ /* If running as root, and asked to chroot, then chroot. */
+ if (root && geteuid () == 0)
+ {
+ if (chroot (root) == -1)
+ {
+ perror (root);
+ exit (1);
+ }
+ }
+
+ /* If running as root, and asked to change user, do so now. */
+ if (username && geteuid () == 0)
+ {
+ struct passwd *pw = getpwnam (username);
+
+ if (pw == 0)
+ {
+ fprintf (stderr, "username not found: %s", username);
+ exit (1);
+ }
+
+ if (initgroups (username, pw->pw_gid) == -1 ||
+ setgid (pw->pw_gid) == -1 ||
+ setuid (pw->pw_uid) == -1)
+ {
+ perror ("setuid");
+ exit (1);
+ }
+ }
+
+ if (!disable_chdir)
+ chdir ("/");
+
+ if (!disable_close)
+ {
+ /* Close connections to the terminal. */
+ close (0);
+ close (1);
+ close (2);
+ open ("/dev/null", O_RDWR);
+ dup2 (0, 1);
+ dup2 (0, 2);
+ setsid ();
+ }
+
+ if (stderr_file)
+ {
+ /* Reopen stderr on a log file. */
+ close (2);
+ if (open (stderr_file, O_WRONLY|O_CREAT|O_APPEND, 0644) == -1)
+ {
+ /* Hard to output an error message at this point ... */
+ abort ();
+ }
+ }
+
+ if (!disable_fork)
+ {
+ /* Fork into background. */
+ int pid = fork ();
+
+ if (pid < 0) { perror ("fork"); exit (1); }
+ else if (pid > 0)
+ {
+ /* Parent process: exit normally. */
+ exit (0);
+ }
+ }
+
+ if (!disable_syslog)
+ {
+ /* Open connection to syslog. */
+ openlog (package_name, LOG_PID|LOG_NDELAY, LOG_USER);
+ syslog (LOG_INFO,
+ "%s starting up on port %d", package_name, port);
+ }
+
+ if (enable_stack_trace_on_segv)
+ {
+#ifdef CAN_CATCH_SIGSEGV
+ struct sigaction sa;
+
+ /* Print a stack trace to stderr if we get SIGSEGV. */
+ memset (&sa, 0, sizeof sa);
+ sa.sa_handler = catch_sigsegv;
+ sa.sa_flags = 0;
+ sigaction (SIGSEGV, &sa, 0);
+#endif
+ }
+
+ /* Run the startup function, if any. */
+ if (startup_fn)
+ startup_fn (argc, argv);
+
+ /* Start the listener thread. */
+ (void) new_listener (sock, processor_fn, 0);
+
+ /* Run the reactor. */
+ while (pseudothread_count_threads () > 0)
+ reactor_invoke ();
+}
+
+#ifdef CAN_CATCH_SIGSEGV
+static void
+catch_sigsegv (int sig)
+{
+#define MAX_ADDRS 50
+ const char msg[] = "** Segmentation fault **\n\nStack trace:\n\n";
+ void *addr[MAX_ADDRS];
+ int n;
+
+ write (2, msg, sizeof (msg) - 1);
+
+ /* Write the stack trace to stderr. */
+ n = backtrace (addr, MAX_ADDRS);
+ backtrace_symbols_fd (addr, n, 2);
+
+ /* Really abort. */
+ abort ();
+#undef MAX_ADDRS
+}
+#endif
+
+void
+pthr_server_default_port (int _default_port)
+{
+ default_port = _default_port;
+}
+
+void
+pthr_server_port_option_name (char _port_option_name)
+{
+ port_option_name = _port_option_name;
+}
+
+void
+pthr_server_default_address (in_addr_t _default_address)
+{
+ default_address = _default_address;
+}
+
+void
+pthr_server_address_option_name (char _address_option_name)
+{
+ address_option_name = _address_option_name;
+}
+
+void
+pthr_server_disable_syslog (void)
+{
+ disable_syslog = 1;
+}
+
+void
+pthr_server_package_name (const char *_package_name)
+{
+ package_name = _package_name;
+}
+
+void
+pthr_server_disable_fork (void)
+{
+ disable_fork = 1;
+}
+
+void
+pthr_server_disable_chdir (void)
+{
+ disable_chdir = 1;
+}
+
+void
+pthr_server_disable_close (void)
+{
+ disable_close = 1;
+}
+
+void
+pthr_server_chroot (const char *_root)
+{
+ root = _root;
+}
+
+void
+pthr_server_username (const char *_username)
+{
+ username = _username;
+}
+
+void
+pthr_server_stderr_file (const char *_pathname)
+{
+ stderr_file = _pathname;
+}
+
+void
+pthr_server_startup_fn (void (*_startup_fn) (int argc, char *argv[]))
+{
+ startup_fn = _startup_fn;
+}
+
+void
+pthr_server_enable_stack_trace_on_segv (void)
+{
+ enable_stack_trace_on_segv = 1;
+}
--- /dev/null
+/* Generic server process.
+ * - by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_server.h,v 1.4 2002/11/20 14:22:20 rich Exp $
+ */
+
+#ifndef PTHR_SERVER_H
+#define PTHR_SERVER_H
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+/* Function: pthr_server_main_loop - Enter server main loop.
+ * Function: pthr_server_default_port
+ * Function: pthr_server_port_option_name
+ * Function: pthr_server_default_address
+ * Function: pthr_server_address_option_name
+ * Function: pthr_server_disable_syslog
+ * Function: pthr_server_package_name
+ * Function: pthr_server_disable_fork
+ * Function: pthr_server_disable_chdir
+ * Function: pthr_server_disable_close
+ * Function: pthr_server_chroot
+ * Function: pthr_server_username
+ * Function: pthr_server_stderr_file
+ * Function: pthr_server_startup_fn
+ * Function: pthr_server_enable_stack_trace_on_segv
+ *
+ * The function @code{pthr_server_main_loop} is a helper function which
+ * allows you to write very simple servers quickly using @code{pthrlib}.
+ * Normally your @code{main} should just contain a call to
+ * @code{pthr_server_main_loop (argc, argv, processor)}
+ * and you would include a function called @code{processor} which is
+ * called on every connection.
+ *
+ * The other functions allow you to customise the behaviour of
+ * @code{pthr_server_main_loop}. You should call any of these once
+ * in your @code{main} before calling @code{pthr_server_main_loop}.
+ *
+ * By default the server listens on port 80 and allows you to
+ * override this on the command line using the @code{-p} option.
+ * To change this, call @code{pthr_server_default_port} and/or
+ * @code{pthr_server_port_option_name}.
+ *
+ * By default the server listens on address INADDR_ANY (all local
+ * addresses) and allows you to override this on the command line
+ * using the @code{-a} option. To change this, call
+ * @code{pthr_server_default_address} and/or
+ * @code{pthr_server_address_option_name}.
+ *
+ * By default the server writes package and version information to
+ * syslog (although it will not be able to correctly determine the
+ * package). Use @code{pthr_server_disable_syslog} to disable syslogging
+ * entirely, or @code{pthr_server_package_name} to change the name displayed
+ * in the logs.
+ *
+ * By default the server forks into the background, changes to the
+ * root directory and disconnects entirely from the terminal. Use
+ * @code{pthr_server_disable_fork}, @code{pthr_server_disable_chdir} and
+ * @code{pthr_server_disable_close} to change these respectively.
+ *
+ * If you wish to have the server chroot after start up, then call
+ * @code{pthr_server_chroot}. This is only possible if the server
+ * is run as root, otherwise the chroot is silently ignored.
+ *
+ * If you wish to have the server change user and group after start up,
+ * then call @code{pthr_server_username} (the group is taken from
+ * the password file and @code{initgroups(3)} is also called). This
+ * is only possible if the server is run as root, otherwise the
+ * change user is silently ignored.
+ *
+ * If you wish to have @code{stderr} connected to a file (this is done after
+ * changing user, so make sure the file is accessible or owned by the
+ * user, not by root), then call @code{pthr_server_stderr_file}. Note
+ * that unless you call this function or @code{pthr_server_disable_close}
+ * then @code{stderr} is sent to @code{/dev/null}!
+ *
+ * The @code{startup_fn} is called after all of the above operations
+ * have completed, but before the listener thread is created. You
+ * do miscellaneous start-of-day functions from here, in particular
+ * starting up other global threads. The command line arguments are also
+ * passed to this function.
+ *
+ * If @code{pthr_server_enable_stack_trace_on_segv} is called, then
+ * the server will attempt to dump a full stack trace to @code{stderr}
+ * if it receives a @code{SIGSEGV} (segmentation fault). This is useful
+ * for diagnosis of errors in production systems, but relies on some
+ * esoteric GLIBC functions (if these functions don't exist, then this
+ * setting does nothing). If the executable is linked with @code{-rdynamic}
+ * then symbolic names will be given in the stack trace, if available.
+ */
+extern void pthr_server_main_loop (int argc, char *argv[], void (*processor_fn) (int sock, void *));
+extern void pthr_server_default_port (int default_port);
+extern void pthr_server_port_option_name (char port_option_name);
+extern void pthr_server_default_address (in_addr_t default_address);
+extern void pthr_server_address_option_name (char address_option_name);
+extern void pthr_server_disable_syslog (void);
+extern void pthr_server_package_name (const char *package_name);
+extern void pthr_server_disable_fork (void);
+extern void pthr_server_disable_chdir (void);
+extern void pthr_server_disable_close (void);
+extern void pthr_server_chroot (const char *root);
+extern void pthr_server_username (const char *username);
+extern void pthr_server_stderr_file (const char *pathname);
+extern void pthr_server_startup_fn (void (*startup_fn) (int argc, char *argv[]));
+extern void pthr_server_enable_stack_trace_on_segv (void);
+
+#endif /* PTHR_SERVER_H */
--- /dev/null
+/* Pseudothread stacks.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_stack.c,v 1.4 2003/02/05 22:13:33 rich Exp $
+ */
+
+/* The _PTH_GET_STACK and _PTH_RETURN_STACK functions are responsible
+ * for allocating new stacks and returning them when they are finished.
+ *
+ * We are careful to avoid freeing up a stack while we are actually
+ * using it. This means in particular that the _PTH_RETURN_STACK
+ * function doesn't directly free up the current stack. It will
+ * be freed up instead next time this function is called.
+ *
+ * Linux has the ability to allocate stacks from anonymous memory.
+ * We use this ability if available. However, Linux also has the
+ * ability to allocate "grows down" stack segments using the
+ * non-portable MAP_GROWSDOWN flag. We *don't* use this feature
+ * because (a) LinuxThreads itself doesn't use it any more and
+ * (b) it can cause intermittent failures if something else
+ * happens to allocate memory in the middle of our (as yet
+ * unallocated) stack.
+ *
+ * On Linux we also allocate a guard page to protect against
+ * stack overflow.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#include "pthr_stack.h"
+
+#define GUARD_PAGE_SIZE 8192
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+static inline void *
+alloc_stack (int size)
+{
+ void *base;
+
+ /* Allocate the actual stack memory. */
+ base = mmap (0, size, PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ if (base == MAP_FAILED) return 0;
+
+ /* Allocate a guard page right at the bottom of the stack. */
+ if (mprotect (base, GUARD_PAGE_SIZE, 0) == -1) abort ();
+
+ return base;
+}
+
+static inline void
+free_stack (void *base, int size)
+{
+ munmap (base, size);
+}
+
+/* Stack pending deallocation (see notes above). */
+static void *pending_stack_base = 0;
+static int pending_stack_size = 0;
+
+void *
+_pth_get_stack (int size)
+{
+ void *base;
+
+ /* Is there a stack waiting to be freed up? If so, free it now. */
+ if (pending_stack_base)
+ {
+ free_stack (pending_stack_base, pending_stack_size);
+ pending_stack_base = 0;
+ }
+
+ /* Allocate a stack of the appropriate size, if available. */
+ base = alloc_stack (size);
+ if (!base) return 0;
+
+ return base;
+}
+
+void
+_pth_return_stack (void *base, int size)
+{
+ /* Is there a stack waiting to be freed up? If so, free it now. */
+ if (pending_stack_base)
+ {
+ free_stack (pending_stack_base, pending_stack_size);
+ pending_stack_base = 0;
+ }
+
+ /* Don't actually free the stack right now. We're still using it. */
+ pending_stack_base = base;
+ pending_stack_size = size;
+}
--- /dev/null
+/* Pseudothread stacks.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_stack.h,v 1.2 2001/06/06 12:34:38 rich Exp $
+ */
+
+#ifndef PTHR_STACK_H
+#define PTHR_STACK_H
+
+extern void *_pth_get_stack (int size);
+extern void _pth_return_stack (void *, int size);
+
+#endif /* PTHR_STACK_H */
--- /dev/null
+/* -*- C -*- @configure_input@ */
+
+#define PTHRLIB_PACKAGE "@PACKAGE@"
+#define PTHRLIB_VERSION "@VERSION@"
--- /dev/null
+/* Wait queues.
+ * by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_wait_queue.c,v 1.5 2002/12/01 14:29:30 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#include <pool.h>
+#include <vector.h>
+
+#include "pthr_pseudothread.h"
+#include "pthr_wait_queue.h"
+
+/* See implementation notes in <pthr_wait_queue.h>. */
+struct wait_queue
+{
+ /* List of threads currently sleeping on the queue. */
+ vector sleepers;
+};
+
+wait_queue
+new_wait_queue (pool pool)
+{
+ wait_queue wq = pmalloc (pool, sizeof *wq);
+
+ wq->sleepers = new_vector (pool, pseudothread);
+ return wq;
+}
+
+int
+wq_nr_sleepers (wait_queue wq)
+{
+ return vector_size (wq->sleepers);
+}
+
+/* To sleep on the wait queue, we register ourselves, then we swap back
+ * into the reactor context.
+ */
+void
+wq_sleep_on (wait_queue wq)
+{
+ vector_push_back (wq->sleepers, current_pth);
+
+ /* Swap context back to the calling context. */
+ _pth_switch_thread_to_calling_context ();
+
+ /* When we get here, we have been woken up ... */
+
+ /* Have we been signalled? */
+ if (_pth_alarm_received ())
+ {
+ int i;
+ pseudothread p;
+
+ /* Remove self from sleepers list. */
+ for (i = 0; i < vector_size (wq->sleepers); ++i)
+ {
+ vector_get (wq->sleepers, i, p);
+ if (p == current_pth)
+ {
+ vector_erase (wq->sleepers, i);
+ goto found;
+ }
+ }
+
+ /* Oops - not found on sleepers list. */
+ abort ();
+
+ found:
+ /* Exit. */
+ pth_exit ();
+ }
+}
+
+/* This is the prepoll handler which actually wakes up the threads. */
+struct wake_up_info
+{
+ pool pool;
+ vector sleepers; /* Pseudothreads to wake up. */
+ reactor_prepoll handler; /* Handler (must be unregistered at end). */
+};
+
+static void
+do_wake_up (void *infop)
+{
+ struct wake_up_info *info = (struct wake_up_info *) infop;
+ int i;
+
+ for (i = 0; i < vector_size (info->sleepers); ++i)
+ {
+ pseudothread pth;
+
+ vector_get (info->sleepers, i, pth);
+
+ /* Swap into the thread context. */
+ _pth_switch_calling_to_thread_context (pth);
+ }
+
+ reactor_unregister_prepoll (info->handler);
+ delete_pool (info->pool);
+}
+
+/* To wake up we take a private copy of the wait queue, clear the
+ * sleepers list, then register a prepoll handler which will eventually
+ * run and wake up each sleeper in turn.
+ */
+static inline void
+wake_up (wait_queue wq, int n)
+{
+ pool pool;
+ vector v;
+ reactor_prepoll handler;
+ struct wake_up_info *wake_up_info;
+
+ /* Added this experimentally to get around a bug when rws running monolith
+ * apps which have database connections open is killed. It seems to be
+ * something to do with having prepoll handlers registered when the
+ * reactor exits. Avoid this entirely here - there is no need, as far as
+ * I can see, to do anything in this function if no one is actually sleeping
+ * on the queue.
+ * - RWMJ 2002/10/15
+ */
+ if (vector_size (wq->sleepers) == 0) return;
+
+ /* This will be freed up by the prepoll handler. */
+ pool = new_subpool (global_pool);
+
+ /* Take a private copy, either of the whole queue, or just part of it,
+ * and also clear the list.
+ */
+ if (n == -1)
+ {
+ v = copy_vector (pool, wq->sleepers);
+ vector_clear (wq->sleepers);
+ }
+ else
+ {
+ v = new_vector (pool, pseudothread);
+
+ while (n > 0)
+ {
+ pseudothread pth;
+
+ vector_pop_front (wq->sleepers, pth);
+ vector_push_back (v, pth);
+ n--;
+ }
+ }
+
+ /* Register a prepoll handler to wake up these sleepin' bewts. */
+ wake_up_info = pmalloc (pool, sizeof *wake_up_info);
+ wake_up_info->pool = pool;
+ wake_up_info->sleepers = v;
+ handler = reactor_register_prepoll (pool, do_wake_up, wake_up_info);
+ wake_up_info->handler = handler;
+}
+
+void
+wq_wake_up (wait_queue wq)
+{
+ wake_up (wq, -1);
+}
+
+void
+wq_wake_up_one (wait_queue wq)
+{
+ /* If there is nothing on the wait queue, but we were instructed to
+ * wake one, then there is probably a bug in the code.
+ */
+ if (vector_size (wq->sleepers) < 1) abort ();
+
+ wake_up (wq, 1);
+}
--- /dev/null
+/* Wait queues.
+ * by Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: pthr_wait_queue.h,v 1.3 2002/12/01 14:29:31 rich Exp $
+ */
+
+#ifndef PTHR_WAIT_QUEUE_H
+#define PTHR_WAIT_QUEUE_H
+
+#include <pool.h>
+
+#include "pthr_pseudothread.h"
+
+struct wait_queue;
+typedef struct wait_queue *wait_queue;
+
+/* Function: new_wait_queue - wait queues
+ * Function: wq_wake_up
+ * Function: wq_wake_up_one
+ * Function: wq_sleep_on
+ * Function: wq_nr_sleepers
+ *
+ * @code{new_wait_queue} creates a wait queue object.
+ *
+ * @code{wq_wake_up} wakes up all the threads which are currently
+ * sleeping on the wait queue. Note that this function does not block,
+ * and because pseudothreads are non-preemptive, none of the sleeping threads
+ * will actually begin running until at least the current thread
+ * blocks somewhere else.
+ *
+ * @code{wq_wake_up_one} wakes up just the first thread at the head
+ * of the wait queue (the one which has been waiting the longest).
+ *
+ * @code{wq_sleep_on} sends the current thread to sleep on the wait
+ * queue. This call blocks (obviously).
+ *
+ * @code{wq_nr_sleepers} returns the number of threads which are
+ * currently asleep on the wait queue.
+ *
+ * Please read the HISTORY section below for some background into
+ * how wait queues are implemented. This may help if you find there
+ * are tricky race conditions in your code.
+ *
+ * History:
+ *
+ * Originally, wait queues were implemented using underlying Unix
+ * pipes. This worked (to some extent) but the overhead of requiring
+ * one pipe (ie. one inode, two file descriptors) per wait queue
+ * made this implementation unacceptably heavyweight.
+ *
+ * Wait queues are now implemented using a simple hack in the reactor
+ * which will be described below.
+ *
+ * Wait queues are subtle. Consider this example: Threads 1, 2 and 3 are
+ * sleeping on a wait queue. Now thread 4 wakes up the queue. You would
+ * expect (probably) threads 1, 2 and 3 to each be woken up and allowed
+ * to start running. However, since this is a cooperatively multitasking
+ * environment, it may happen that thread 1 wakes up first, does some
+ * work and then goes back to sleep on the wait queue, all before threads
+ * 2 and 3 have woken up. With a naive implementation of wait queues,
+ * thread 4 might end up waking up thread 1 *again* (and even again after
+ * that), never waking up threads 2 and 3 and ultimately starving those
+ * threads.
+ *
+ * To avoid this situation, we might consider two possible alternatives:
+ * either when thread 1 goes back to sleep, it goes to sleep on a
+ * 'different' queue, or else thread 4 might take a copy of the wait
+ * queue and delete the queue before it wakes any of the threads up.
+ *
+ * Another nasty situation which might arise in real life is this:
+ * Again, threads 1, 2 and 3 are sleeping. Thread 4 wakes them up.
+ * Thread 1, while processing its work, happens also to wake up the
+ * same wait queue. What should happen to this second wake-up event?
+ * Should it be ignored? Should it wake up threads 2 and 3? Should
+ * it wake up any other threads which happen to have gone to sleep
+ * on the queue after 1, 2 and 3? Or perhaps some combination of
+ * these?
+ *
+ * The solution that we have come up with is as follows. A wait queue
+ * consists of a simple list of threads which are sleeping on it. When
+ * a thread wishes to sleep on the wait queue, it is added to this list,
+ * and it switches back into the reactor context. When a thread wishes
+ * to wake up all sleepers, it:
+ * (a) copies the list of sleeping pseudothreads into its own private
+ * space
+ * (b) clears the list of sleeping pseudothreads
+ * (c) registers a prepoll handler to run which will wake up (ie. switch
+ * into the context of) each of these threads in turn
+ * (d) continues to run to completion.
+ * A thread which wishes to wake just one pseudothread works similarly
+ * except that it only copies (and removes) a single item off the list.
+ *
+ * Note various invariant conditions: A thread cannot be entered on the
+ * wait queue sleeping list more than once (because it cannot call
+ * sleep_on when it is already sleeping). For similar reasons, a thread
+ * cannot be entered on any of the multiple lists at the same time.
+ * This implies that a thread cannot be woken up multiple times.
+ *
+ * The reader should satisfy themselves that this algorithm is free
+ * of races, and solves all the problems outlined above. In addition, it
+ * has the desirable property that wake_up* never sleeps.
+ *
+ */
+extern wait_queue new_wait_queue (pool);
+extern void wq_wake_up (wait_queue);
+extern void wq_wake_up_one (wait_queue);
+extern void wq_sleep_on (wait_queue);
+extern int wq_nr_sleepers (wait_queue);
+
+#endif /* PTHR_WAIT_QUEUE_H */
--- /dev/null
+/* Test big stack usage.
+ * Copyright (C) 2001 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_bigstack.c,v 1.3 2002/08/21 10:42:20 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pthr_reactor.h"
+#include "pthr_pseudothread.h"
+
+static pool test_pool;
+static pseudothread test_pth;
+
+/* XXX I suspect that gcc will just turn this into tail recursion,
+ * proving nothing.
+ */
+static void
+recurse (int n)
+{
+ char s[1024];
+ s[1023] = 'a';
+ if (n > 0) recurse (n-1);
+}
+
+static void
+do_test (void *data)
+{
+ recurse (100);
+}
+
+int
+main ()
+{
+ pseudothread_set_stack_size (512 * 1024);
+
+ test_pool = new_pool ();
+ test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread");
+ pth_start (test_pth);
+
+ while (pseudothread_count_threads () > 0)
+ reactor_invoke ();
+
+ exit (0);
+}
--- /dev/null
+/* Test context switching.
+ * Copyright (C) 2001-2003 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_context.c,v 1.4 2003/02/05 22:13:33 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_SETJMP_H
+#include <setjmp.h>
+#endif
+
+#include "pthr_context.h"
+
+#define STACK_SIZE 4096
+#define MARKER_SIZE 16
+
+static unsigned char stack [MARKER_SIZE+STACK_SIZE+MARKER_SIZE];
+static mctx_t ctx, calling_ctx;
+static int called = 0;
+static void fn (void *);
+static void setjmp_test (void *);
+static jmp_buf jb;
+
+#define DATA ((void *) 0x12546731)
+
+int
+main ()
+{
+ int i;
+
+ /* Place magic numbers into the marker areas at each end of the stack, so
+ * we can detect stack overrun.
+ */
+ memset (stack, 0xeb, MARKER_SIZE);
+ memset (stack + MARKER_SIZE+STACK_SIZE, 0xeb, MARKER_SIZE);
+ mctx_set (&ctx, fn, DATA, stack + MARKER_SIZE, STACK_SIZE);
+ mctx_switch (&calling_ctx, &ctx);
+ assert (called == 1);
+
+ /* Verify the ends of the stack haven't been overrun. */
+ for (i = 0; i < MARKER_SIZE; ++i)
+ assert (stack[i] == 0xeb && stack[i + MARKER_SIZE+STACK_SIZE] == 0xeb);
+
+ /* Check setjmp/longjmp work on the alternate stack. This was found to be
+ * a problem on Solaris.
+ */
+ mctx_set (&ctx, setjmp_test, DATA, stack + MARKER_SIZE, STACK_SIZE);
+ mctx_switch (&calling_ctx, &ctx);
+
+ exit (0);
+}
+
+static void
+fn (void *data)
+{
+ assert (data == DATA);
+ called = 1;
+ mctx_restore (&calling_ctx);
+ abort ();
+}
+
+static void call_longjmp (void);
+
+static void
+setjmp_test (void *data)
+{
+ if (setjmp (jb) == 0)
+ call_longjmp ();
+
+ mctx_restore (&calling_ctx);
+}
+
+static void
+call_longjmp ()
+{
+ longjmp (jb, 1);
+}
--- /dev/null
+/* Test the database interface.
+ * Copyright (C) 2002 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_dbi.c,v 1.6 2002/12/09 10:43:27 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+
+#include "pthr_pseudothread.h"
+#include "pthr_dbi.h"
+
+static pool test_pool;
+static pseudothread test_pth;
+
+static void
+do_test (void *data)
+{
+ db_handle dbh;
+ st_handle sth;
+ int userid, rownum;
+ char *alias, *username;
+ struct dbi_timestamp ts;
+ struct dbi_interval inv;
+
+ /* Open a connection to the database. */
+ dbh = new_db_handle (test_pool, "", DBI_THROW_ERRORS);
+ if (!dbh)
+ pth_die ("failed to connect to the database, check PGHOST, etc.");
+
+ /* Create some tables and some data. */
+ sth = st_prepare_cached
+ (dbh,
+ "create temporary table tdbi_users "
+ " (userid int4, "
+ " username text not null, "
+ " age int2 not null, "
+ " last_login date, "
+ " unique (userid), "
+ " unique (username))");
+ st_execute (sth);
+
+ sth = st_prepare_cached
+ (dbh,
+ "create temporary table tdbi_aliases "
+ " (userid int4 references tdbi_users (userid), "
+ " alias text not null)");
+ st_execute (sth);
+
+ sth = st_prepare_cached
+ (dbh,
+ "insert into tdbi_users (userid, username, age) values (?, ?, ?)",
+ DBI_INT, DBI_STRING, DBI_INT);
+ st_execute (sth, 1, "rich", 30);
+ st_execute (sth, 2, "anna", 45);
+ st_execute (sth, 3, "bob", 55);
+ st_execute (sth, 4, "dan", 24);
+
+ sth = st_prepare_cached
+ (dbh,
+ "insert into tdbi_aliases (userid, alias) values (?, ?)",
+ DBI_INT, DBI_STRING);
+ st_execute (sth, 1, "richard");
+ st_execute (sth, 1, "richie");
+ st_execute (sth, 1, "richy");
+ st_execute (sth, 2, "ann");
+ st_execute (sth, 2, "annie");
+ st_execute (sth, 3, "robert");
+ st_execute (sth, 3, "bobbie");
+ st_execute (sth, 3, "bobby");
+
+ /* Select out some results. */
+ sth = st_prepare_cached
+ (dbh,
+ "select u.userid, u.username, a.alias "
+ "from tdbi_users u, tdbi_aliases a "
+ "where u.userid = a.userid "
+ "order by 3");
+ st_execute (sth);
+
+ st_bind (sth, 0, userid, DBI_INT);
+ st_bind (sth, 1, username, DBI_STRING);
+ st_bind (sth, 2, alias, DBI_STRING);
+
+ rownum = 0;
+ while (st_fetch (sth))
+ {
+ switch (rownum)
+ {
+ case 0:
+ assert (userid == 2 &&
+ strcmp (username, "anna") == 0 &&
+ strcmp (alias, "ann") == 0);
+ break;
+ case 1:
+ assert (userid == 2 &&
+ strcmp (username, "anna") == 0 &&
+ strcmp (alias, "annie") == 0);
+ break;
+ case 2:
+ assert (userid == 3 &&
+ strcmp (username, "bob") == 0 &&
+ strcmp (alias, "bobbie") == 0);
+ break;
+ case 3:
+ assert (userid == 3 &&
+ strcmp (username, "bob") == 0 &&
+ strcmp (alias, "bobby") == 0);
+ break;
+ case 4:
+ assert (userid == 1 &&
+ strcmp (username, "rich") == 0 &&
+ strcmp (alias, "richard") == 0);
+ break;
+ case 5:
+ assert (userid == 1 &&
+ strcmp (username, "rich") == 0 &&
+ strcmp (alias, "richie") == 0);
+ break;
+ case 6:
+ assert (userid == 1 &&
+ strcmp (username, "rich") == 0 &&
+ strcmp (alias, "richy") == 0);
+ break;
+ case 7:
+ assert (userid == 3 &&
+ strcmp (username, "bob") == 0 &&
+ strcmp (alias, "robert") == 0);
+ break;
+ default:
+ abort ();
+ }
+
+ rownum++;
+ }
+
+ sth = st_prepare_cached
+ (dbh,
+ "select username from tdbi_users where age > 40 order by 1");
+ st_execute (sth);
+
+ st_bind (sth, 0, username, DBI_STRING);
+
+ rownum = 0;
+ while (st_fetch (sth))
+ {
+ switch (rownum)
+ {
+ case 0:
+ assert (strcmp (username, "anna") == 0);
+ break;
+ case 1:
+ assert (strcmp (username, "bob") == 0);
+ break;
+ default:
+ abort ();
+ }
+
+ rownum++;
+ }
+
+ /* Select out one row, no rows. */
+ sth = st_prepare_cached
+ (dbh,
+ "select userid from tdbi_users where username = ?", DBI_STRING);
+ st_execute (sth, "rich");
+
+ st_bind (sth, 0, userid, DBI_INT);
+
+ assert (st_fetch (sth) != 0);
+ assert (userid == 1);
+ assert (st_fetch (sth) == 0);
+ assert (userid == 1); /* Hasn't splatted userid. */
+
+ st_execute (sth, "fred");
+
+ assert (st_fetch (sth) == 0);
+
+ /* Check the st_finish function does nothing bad. */
+ st_finish (sth);
+
+ /* Drop the tables. */
+ sth = st_prepare_cached
+ (dbh,
+ "drop table tdbi_aliases; drop table tdbi_users");
+ st_execute (sth);
+
+ /* Test timestamps and intervals.
+ * XXX Retrieval only tested/supported at present.
+ */
+ sth = st_prepare_cached
+ (dbh,
+ "create temporary table tdbi_times "
+ " (ord int2 not null, ts timestamp, inv interval)");
+ st_execute (sth);
+
+ sth = st_prepare_cached
+ (dbh,
+ "insert into tdbi_times (ord, ts, inv) values (?, ?, ?)",
+ DBI_INT, DBI_STRING, DBI_STRING);
+ st_execute (sth, 0, "2002/11/09 01:02", 0);
+ st_execute (sth, 1, "2002/10/07 03:04:05", "1 year 1 day");
+ st_execute (sth, 2, "2002/09/04 06:07:08.999", "01:00");
+ st_execute (sth, 3, 0, "30 mins");
+ st_execute (sth, 4, 0, "1 year 2 months 6 days 8 hours 9 mins");
+
+ sth = st_prepare_cached
+ (dbh, "select ord, ts, inv from tdbi_times order by 1");
+ st_execute (sth);
+
+ st_bind (sth, 1, ts, DBI_TIMESTAMP);
+ st_bind (sth, 2, inv, DBI_INTERVAL);
+
+ assert (st_fetch (sth));
+ assert (!ts.is_null);
+ assert (ts.year == 2002 && ts.month == 11 && ts.day == 9 &&
+ ts.hour == 1 && ts.min == 2 && ts.sec == 0 &&
+ ts.microsecs == 0);
+ assert (inv.is_null);
+
+ assert (st_fetch (sth));
+ assert (!ts.is_null);
+ assert (ts.year == 2002 && ts.month == 10 && ts.day == 7 &&
+ ts.hour == 3 && ts.min == 4 && ts.sec == 5 &&
+ ts.microsecs == 0);
+ assert (!inv.is_null);
+ assert (inv.years == 1 && inv.months == 0 &&
+ inv.days == 1 && inv.hours == 0 && inv.mins == 0 &&
+ inv.secs == 0);
+
+ assert (st_fetch (sth));
+ assert (!ts.is_null);
+ assert (ts.year == 2002 && ts.month == 9 && ts.day == 4 &&
+ ts.hour == 6 && ts.min == 7 && ts.sec == 8 &&
+ ts.microsecs == 999);
+ assert (!inv.is_null);
+ assert (inv.years == 0 && inv.months == 0 &&
+ inv.days == 0 && inv.hours == 1 && inv.mins == 0 &&
+ inv.secs == 0);
+
+ assert (st_fetch (sth));
+ assert (ts.is_null);
+ assert (!inv.is_null);
+ assert (inv.years == 0 && inv.months == 0 &&
+ inv.days == 0 && inv.hours == 0 && inv.mins == 30 &&
+ inv.secs == 0);
+
+ assert (st_fetch (sth));
+ assert (ts.is_null);
+ assert (!inv.is_null);
+ assert (inv.years == 1 && inv.months == 2 &&
+ inv.days == 6 && inv.hours == 8 && inv.mins == 9 &&
+ inv.secs == 0);
+
+ /* Drop the table. */
+ sth = st_prepare_cached
+ (dbh,
+ "drop table tdbi_times");
+ st_execute (sth);
+
+ /* Try rolling back the database. */
+ db_rollback (dbh);
+}
+
+int
+main ()
+{
+ char *env = getenv ("TEST_DBI");
+
+ /* Skip the test unless the 'TEST_DBI' environment variable is set. */
+ if (!env || strcmp (env, "1") != 0)
+ {
+ fprintf (stderr,
+"WARNING: DBI test skipped. If you want to run the DBI test, then you must\n"
+"have:\n"
+" (a) A working PostgreSQL >= 7.1 database.\n"
+" (b) postgresql-devel packages installed (ie. libpq, header files).\n"
+" (c) PGHOST, etc., set up to provide access to a database where I can\n"
+" create temporary tables.\n"
+"Set the TEST_DBI environment variable to 1 and run this test again.\n");
+
+ exit (0);
+ }
+
+ test_pool = new_pool ();
+ test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread");
+ pth_start (test_pth);
+
+ while (pseudothread_count_threads () > 0)
+ reactor_invoke ();
+
+ exit (0);
+}
--- /dev/null
+/* Test the exception handling.
+ * Copyright (C) 2001 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_except1.c,v 1.4 2002/12/01 14:29:31 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+
+#include "pthr_pseudothread.h"
+
+static pool test_pool;
+static pseudothread test_pth;
+
+static void
+do_die (void *data)
+{
+ pth_die ("this is the message");
+}
+
+static void
+do_test (void *data)
+{
+ const char *msg;
+
+ /* Check that catch can correctly catch a die. */
+ msg = pth_catch (do_die, 0);
+
+ if (!msg || strcmp (msg, "this is the message") != 0)
+ abort ();
+}
+
+int
+main ()
+{
+ test_pool = new_pool ();
+ test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread");
+ pth_start (test_pth);
+
+ while (pseudothread_count_threads () > 0)
+ reactor_invoke ();
+
+ exit (0);
+}
--- /dev/null
+/* Test the exception handling.
+ * Copyright (C) 2001 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_except2.c,v 1.3 2002/12/01 14:29:31 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <pool.h>
+#include <vector.h>
+
+#include "pthr_pseudothread.h"
+
+static pool test_pool;
+static pseudothread test_pth;
+
+static void
+do_test (void *data)
+{
+ /* Just die, no catch. */
+ pth_die ("you can just ignore this message");
+ abort ();
+}
+
+int
+main ()
+{
+ test_pool = new_pool ();
+ test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread");
+ pth_start (test_pth);
+
+ while (pseudothread_count_threads () > 0)
+ reactor_invoke ();
+
+ exit (0);
+}
--- /dev/null
+/* Test the exception handling.
+ * Copyright (C) 2001 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_except3.c,v 1.4 2002/12/01 14:29:31 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <pool.h>
+
+#include "pthr_pseudothread.h"
+
+static pool test_pool;
+static pseudothread test_pth;
+
+static void
+do_die (void *data)
+{
+ pth_die ("this is do_die");
+}
+
+static void
+do_nested (void *data)
+{
+ const char *msg;
+
+ msg = pth_catch (do_die, 0);
+
+ if (!msg || strcmp (msg, "this is do_die") != 0)
+ abort ();
+}
+
+static void
+do_test (void *data)
+{
+ const char *msg;
+
+ msg = pth_catch (do_nested, 0);
+
+ if (msg) abort ();
+}
+
+int
+main ()
+{
+ test_pool = new_pool ();
+ test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread");
+ pth_start (test_pth);
+
+ while (pseudothread_count_threads () > 0)
+ reactor_invoke ();
+
+ exit (0);
+}
--- /dev/null
+/* Test mutexes.
+ * Copyright (C) 2001 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_mutex.c,v 1.3 2002/12/01 14:29:31 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include "pthr_mutex.h"
+
+#define NR_THREADS 50
+#define NR_INCREMENTS 50
+
+static int var = 0; /* The contended variable. */
+static mutex lock; /* The lock. */
+static int nr_threads = NR_THREADS;
+
+static void
+start_monitor_thread (void *data)
+{
+ int p = 0, v;
+
+ printf ("[ ]\r[");
+
+ while (nr_threads > 0)
+ {
+ /* Get value of contended variable and draw a scale. */
+ v = 72 * var / (NR_THREADS * NR_INCREMENTS);
+ while (v > p)
+ {
+ printf (".");
+ fflush (stdout);
+ p++;
+ }
+
+ pth_millisleep (100);
+ }
+
+ printf ("\n");
+
+ /* Check v is correct at the end. */
+ assert (var == NR_THREADS * NR_INCREMENTS);
+ exit (0);
+}
+
+static void
+start_thread (void *data)
+{
+ int i;
+
+ for (i = 0; i < NR_INCREMENTS; ++i)
+ {
+ int v;
+
+ mutex_enter (lock);
+ v = var; /* Do a slow R/M/W. */
+ pth_millisleep (1);
+ var = v + 1;
+ mutex_leave (lock);
+
+ pth_millisleep (1);
+ }
+
+ nr_threads--;
+}
+
+int
+main ()
+{
+ pseudothread pth[NR_THREADS];
+ pseudothread monitor_pth;
+ pool p;
+ int i;
+
+ /* Create the lock. */
+ lock = new_mutex (global_pool);
+
+ /* Create the monitoring thread. */
+ p = new_subpool (global_pool);
+ monitor_pth = new_pseudothread (p, start_monitor_thread, 0,
+ "monitor");
+ pth_start (monitor_pth);
+
+ /* Create the threads. */
+ for (i = 0; i < NR_THREADS; ++i)
+ {
+ p = new_subpool (global_pool);
+ pth[i] = new_pseudothread (p, start_thread, 0,
+ psprintf (p, "thread %d", i));
+ }
+
+ /* Start all the threads running. */
+ for (i = 0; i < NR_THREADS; ++i)
+ pth_start (pth[i]);
+
+ for (;;) reactor_invoke ();
+}
--- /dev/null
+/* Test the pseudothreads.
+ * Copyright (C) 2001 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_pseudothread.c,v 1.5 2002/12/01 14:29:31 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+
+#include "pthr_pseudothread.h"
+
+static void set_flag (void *data) { int *flag = (int *) data; *flag = 1; }
+
+static pool test_pool;
+static pseudothread test_pth;
+
+static pool pool1;
+static pseudothread pth1;
+static int pool_gone = 0;
+static int thread_has_run = 0;
+
+static void
+test_exiting (void *data)
+{
+ assert (current_pth == pth1);
+ thread_has_run = 1;
+ pth_exit ();
+ abort ();
+}
+
+static void
+test_timeout (void *data)
+{
+ assert (current_pth == pth1);
+ thread_has_run = 1;
+ pth_timeout (1);
+ pth_sleep (1000);
+}
+
+static void
+do_test (void *data)
+{
+ /* Check current_pth set correctly on thread start. */
+ assert (current_pth == test_pth);
+
+ /* Launch a thread and check that the thread runs and the pool is
+ * deleted at the end.
+ */
+ pool1 = new_pool ();
+ pool_register_cleanup_fn (pool1, set_flag, &pool_gone);
+ pth1 = new_pseudothread (pool1, set_flag, &thread_has_run, "pth1");
+ assert (pool_gone == 0);
+ assert (thread_has_run == 0);
+ pth_start (pth1); /* The thread actually runs and exits here. */
+ assert (pool_gone == 1);
+ assert (thread_has_run == 1);
+ assert (current_pth == test_pth);
+
+ /* Check pth_get_* functions. */
+ assert (pth_get_pool (test_pth) == test_pool);
+ assert (strcmp (pth_get_name (test_pth), "testing thread") == 0);
+ assert (pth_get_thread_num (test_pth) == 0);
+ assert (pth_get_run (test_pth) == do_test);
+ assert (pth_get_data (test_pth) == 0);
+ assert (pth_get_language (test_pth) == 0);
+
+ /* Check pth_exit function. */
+ pool1 = new_pool ();
+ pth1 = new_pseudothread (pool1, test_exiting, 0, "exiting thread");
+ thread_has_run = 0;
+ pth_start (pth1);
+ assert (thread_has_run == 1);
+ assert (current_pth == test_pth);
+
+ /* Check timeout for system calls. */
+ pool1 = new_pool ();
+ pool_register_cleanup_fn (pool1, set_flag, &pool_gone);
+ pth1 = new_pseudothread (pool1, test_timeout, 0, "timeout thread");
+ thread_has_run = 0;
+ pool_gone = 0;
+ pth_start (pth1);
+ assert (thread_has_run == 1);
+ assert (current_pth == test_pth);
+ while (!pool_gone) { pth_millisleep (100); }
+}
+
+int
+main ()
+{
+ test_pool = new_pool ();
+ test_pth = new_pseudothread (test_pool, do_test, 0, "testing thread");
+
+ /* Check that pth_start restores current_pth correctly. */
+ current_pth = (pseudothread) 0x1234;
+ pth_start (test_pth);
+
+ assert (current_pth == (pseudothread) 0x1234);
+
+ while (pseudothread_count_threads () > 0)
+ reactor_invoke ();
+
+ exit (0);
+}
--- /dev/null
+/* Test the reactor.
+ * Copyright (C) 2001 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_reactor.c,v 1.2 2002/08/21 10:42:21 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <pool.h>
+
+#include "pthr_reactor.h"
+
+static void set_flag (void *data) { int *flag = (int *) data; *flag = 1; }
+static void set_flag_h (int s, int e, void *data) { int *flag = (int *) data; *flag = 1; }
+
+int
+main ()
+{
+ int p1[2], p2[2];
+ reactor_handle h1, h2;
+ int flag1 = 0, flag2 = 0, flag3 = 0;
+ char c = '\0';
+ reactor_timer t1;
+ reactor_prepoll pre1;
+
+ /* Create some pipes. */
+ if (pipe (p1) < 0) { perror ("pipe"); exit (1); }
+ if (pipe (p2) < 0) { perror ("pipe"); exit (1); }
+
+ if (fcntl (p1[0], F_SETFL, O_NONBLOCK) < 0) { perror ("fcntl"); exit (1); }
+ if (fcntl (p1[1], F_SETFL, O_NONBLOCK) < 0) { perror ("fcntl"); exit (1); }
+ if (fcntl (p2[0], F_SETFL, O_NONBLOCK) < 0) { perror ("fcntl"); exit (1); }
+ if (fcntl (p2[1], F_SETFL, O_NONBLOCK) < 0) { perror ("fcntl"); exit (1); }
+
+ /* Register read handlers. */
+ h1 = reactor_register (p1[0], REACTOR_READ, set_flag_h, &flag1);
+ h2 = reactor_register (p2[0], REACTOR_READ, set_flag_h, &flag2);
+
+ /* Register a prepoll handler. */
+ pre1 = reactor_register_prepoll (global_pool, set_flag, &flag3);
+
+ /* Write something and check the flags. */
+ write (p1[1], &c, 1);
+ reactor_invoke ();
+ assert (flag1 == 1);
+ assert (flag2 == 0);
+ assert (flag3 == 1);
+ flag1 = flag3 = 0;
+ read (p1[0], &c, 1);
+ write (p2[1], &c, 1);
+ reactor_invoke ();
+ assert (flag1 == 0);
+ assert (flag2 == 1);
+ assert (flag3 == 1);
+ flag2 = flag3 = 0;
+ read (p1[0], &c, 1);
+
+ reactor_unregister (h1);
+ reactor_unregister (h2);
+ reactor_unregister_prepoll (pre1);
+
+ /* Register a timer function. */
+ t1 = reactor_set_timer (global_pool, 1000, set_flag, &flag1);
+ sleep (2);
+ reactor_invoke ();
+ assert (flag1 == 1);
+ assert (flag3 == 0);
+ flag1 = 0;
+
+ exit (0);
+}
--- /dev/null
+/* Test rwlocks.
+ * Copyright (C) 2001 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_rwlock.c,v 1.3 2002/12/01 14:29:31 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <pool.h>
+#include <pstring.h>
+
+#include "pthr_rwlock.h"
+
+#define NR_WRITER_THREADS 1
+#define NR_READER_THREADS 500
+#define NR_INCREMENTS 1000
+
+static int var = 0; /* The contended variable. */
+static rwlock lock; /* The lock. */
+static int nr_writer_threads = NR_WRITER_THREADS;
+static int nr_reader_threads = NR_READER_THREADS;
+
+static void
+start_monitor_thread (void *data)
+{
+ int p = 0, v;
+
+ printf ("[ ]\r[");
+
+ while (nr_writer_threads > 0 && nr_reader_threads > 0)
+ {
+ /* Get value of contended variable and draw a scale. */
+ v = 72 * var / (NR_WRITER_THREADS * NR_INCREMENTS);
+ while (v > p)
+ {
+ printf (".");
+ fflush (stdout);
+ p++;
+ }
+
+ pth_millisleep (100);
+ }
+
+ printf ("\n");
+
+ /* Check v is correct at the end. */
+ assert (var == NR_WRITER_THREADS * NR_INCREMENTS);
+ exit (0);
+}
+
+static void
+start_writer_thread (void *data)
+{
+ int i;
+
+ for (i = 0; i < NR_INCREMENTS; ++i)
+ {
+ int v;
+
+ rwlock_enter_write (lock);
+ v = var; /* Do a slow R/M/W. */
+ pth_millisleep (1);
+ var = v + 1;
+ rwlock_leave (lock);
+
+ pth_millisleep (1);
+ }
+
+ nr_writer_threads--;
+}
+
+static void
+start_reader_thread (void *data)
+{
+ while (nr_writer_threads > 0)
+ {
+ rwlock_enter_read (lock);
+ pth_millisleep (1);
+ rwlock_leave (lock);
+
+ pth_millisleep (1);
+ }
+
+ nr_reader_threads--;
+}
+
+int
+main ()
+{
+ pseudothread writer_pth[NR_WRITER_THREADS];
+ pseudothread reader_pth[NR_READER_THREADS];
+ pseudothread monitor_pth;
+ pool p;
+ int i;
+
+ /* Create the lock. */
+ lock = new_rwlock (global_pool);
+
+ /* Create the monitoring thread. */
+ p = new_subpool (global_pool);
+ monitor_pth = new_pseudothread (p, start_monitor_thread, 0,
+ "monitor");
+ pth_start (monitor_pth);
+
+ /* Create the writer threads. */
+ for (i = 0; i < NR_WRITER_THREADS; ++i)
+ {
+ p = new_subpool (global_pool);
+ writer_pth[i] = new_pseudothread (p,
+ start_writer_thread, 0,
+ psprintf (p, "writer thread %d", i));
+ }
+
+ /* Create the reader threads. */
+ for (i = 0; i < NR_READER_THREADS; ++i)
+ {
+ p = new_subpool (global_pool);
+ reader_pth[i] = new_pseudothread (p,
+ start_reader_thread, 0,
+ psprintf (p, "reader thread %d", i));
+ }
+
+ /* Start all the threads running. */
+ for (i = 0; i < NR_WRITER_THREADS; ++i)
+ pth_start (writer_pth[i]);
+ for (i = 0; i < NR_READER_THREADS; ++i)
+ pth_start (reader_pth[i]);
+
+ for (;;) reactor_invoke ();
+}
--- /dev/null
+/* Test pth_select call (this also tests pth_poll, implicitly).
+ * Copyright (C) 2001 Richard W.M. Jones <rich@annexia.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: test_select.c,v 1.4 2003/02/05 22:13:33 rich Exp $
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "pthr_reactor.h"
+#include "pthr_pseudothread.h"
+
+#define NR_WRITERS 4
+#define NR_CHARS 100
+
+static pool reader_pool;
+static pseudothread reader_pth;
+static pool writer_pool[NR_WRITERS];
+static pseudothread writer_pth[NR_WRITERS];
+
+static int fds[NR_WRITERS][2];
+
+static void
+writer (void *vp)
+{
+ int id = *(int *)vp;
+ int i, fd = fds[id][1];
+ char c[1] = { '0' + id };
+
+ for (i = 0; i < NR_CHARS; ++i)
+ {
+ pth_write (fd, c, 1);
+ pth_millisleep (3);
+ }
+
+ c[0] = '\xff';
+ pth_write (fd, c, 1);
+ close (fd);
+}
+
+static void
+reader (void *vp)
+{
+ int i, running = NR_WRITERS, r, max_fd = -1;
+ fd_set readfds, returnfds;
+ struct timeval tv;
+
+ FD_ZERO (&readfds);
+
+ for (i = 0; i < NR_WRITERS; ++i)
+ {
+ int fd = fds[i][0];
+
+ if (fd > max_fd) max_fd = fd;
+ FD_SET (fd, &readfds);
+ }
+
+ while (running)
+ {
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000;
+ returnfds = readfds;
+ r = pth_select (max_fd+1, &returnfds, 0, 0, &tv);
+
+ if (r == -1) abort ();
+ if (r > 0)
+ {
+ for (i = 0; i <= max_fd; ++i)
+ {
+ if (FD_ISSET (i, &returnfds))
+ {
+ char c[1];
+
+ pth_read (i, c, 1);
+ if (c[0] == '\xff')
+ {
+ running--;
+ close (i);
+ }
+ else
+ {
+ putchar (c[0]); putchar ('\r'); fflush (stdout);
+ }
+ }
+ }
+ }
+ }
+}
+
+int
+main ()
+{
+ int i;
+
+ for (i = 0; i < NR_WRITERS; ++i)
+ {
+ if (pipe (fds[i]) == -1) abort ();
+
+ if (fcntl (fds[i][0], F_SETFL, O_NONBLOCK) == -1) abort ();
+ if (fcntl (fds[i][1], F_SETFL, O_NONBLOCK) == -1) abort ();
+
+ writer_pool[i] = new_subpool (global_pool);
+ writer_pth[i] = new_pseudothread (writer_pool[i], writer, &i,
+ "writer");
+ pth_start (writer_pth[i]);
+ }
+
+ reader_pool = new_subpool (global_pool);
+ reader_pth = new_pseudothread (reader_pool, reader, 0, "reader");
+ pth_start (reader_pth);
+
+ while (pseudothread_count_threads () > 0)
+ reactor_invoke ();
+
+ exit (0);
+}