Add to git. master
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 25 Apr 2014 10:40:11 +0000 (11:40 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Fri, 25 Apr 2014 10:40:11 +0000 (11:40 +0100)
198 files changed:
.cvsignore [new file with mode: 0644]
COPYING.LIB [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile+ [new file with mode: 0644]
README [new file with mode: 0644]
README.FreeBSD [new file with mode: 0644]
README.Solaris [new file with mode: 0644]
README.openbsd [new file with mode: 0644]
conf/test_setcontext.c [new file with mode: 0644]
conf/test_setcontext.sh [new file with mode: 0755]
configure [new file with mode: 0755]
doc/.cvsignore [new file with mode: 0644]
doc/cgi_erase.3.html [new file with mode: 0644]
doc/cgi_escape.3.html [new file with mode: 0644]
doc/cgi_get_post_max.3.html [new file with mode: 0644]
doc/cgi_param.3.html [new file with mode: 0644]
doc/cgi_param_list.3.html [new file with mode: 0644]
doc/cgi_params.3.html [new file with mode: 0644]
doc/cgi_set_post_max.3.html [new file with mode: 0644]
doc/cgi_unescape.3.html [new file with mode: 0644]
doc/copy_cgi.3.html [new file with mode: 0644]
doc/eg_echo.c [new file with mode: 0644]
doc/ftpc_ascii.3.html [new file with mode: 0644]
doc/ftpc_binary.3.html [new file with mode: 0644]
doc/ftpc_cdup.3.html [new file with mode: 0644]
doc/ftpc_cwd.3.html [new file with mode: 0644]
doc/ftpc_delete.3.html [new file with mode: 0644]
doc/ftpc_dir.3.html [new file with mode: 0644]
doc/ftpc_get.3.html [new file with mode: 0644]
doc/ftpc_login.3.html [new file with mode: 0644]
doc/ftpc_ls.3.html [new file with mode: 0644]
doc/ftpc_mkdir.3.html [new file with mode: 0644]
doc/ftpc_put.3.html [new file with mode: 0644]
doc/ftpc_pwd.3.html [new file with mode: 0644]
doc/ftpc_quit.3.html [new file with mode: 0644]
doc/ftpc_quote.3.html [new file with mode: 0644]
doc/ftpc_rmdir.3.html [new file with mode: 0644]
doc/ftpc_set_passive_mode.3.html [new file with mode: 0644]
doc/ftpc_type.3.html [new file with mode: 0644]
doc/http_get_log_file.3.html [new file with mode: 0644]
doc/http_get_servername.3.html [new file with mode: 0644]
doc/http_request_get_header.3.html [new file with mode: 0644]
doc/http_request_get_headers.3.html [new file with mode: 0644]
doc/http_request_is_HEAD.3.html [new file with mode: 0644]
doc/http_request_method.3.html [new file with mode: 0644]
doc/http_request_method_string.3.html [new file with mode: 0644]
doc/http_request_nr_headers.3.html [new file with mode: 0644]
doc/http_request_path.3.html [new file with mode: 0644]
doc/http_request_query_string.3.html [new file with mode: 0644]
doc/http_request_time.3.html [new file with mode: 0644]
doc/http_request_url.3.html [new file with mode: 0644]
doc/http_request_version.3.html [new file with mode: 0644]
doc/http_response_end_headers.3.html [new file with mode: 0644]
doc/http_response_send_header.3.html [new file with mode: 0644]
doc/http_response_send_headers.3.html [new file with mode: 0644]
doc/http_set_log_file.3.html [new file with mode: 0644]
doc/http_set_servername.3.html [new file with mode: 0644]
doc/index.html [new file with mode: 0644]
doc/io_copy.3.html [new file with mode: 0644]
doc/io_fclose.3.html [new file with mode: 0644]
doc/io_fdopen.3.html [new file with mode: 0644]
doc/io_fflush.3.html [new file with mode: 0644]
doc/io_fgetc.3.html [new file with mode: 0644]
doc/io_fgets.3.html [new file with mode: 0644]
doc/io_fileno.3.html [new file with mode: 0644]
doc/io_fprintf.3.html [new file with mode: 0644]
doc/io_fputc.3.html [new file with mode: 0644]
doc/io_fputs.3.html [new file with mode: 0644]
doc/io_fread.3.html [new file with mode: 0644]
doc/io_fwrite.3.html [new file with mode: 0644]
doc/io_get_inbufcount.3.html [new file with mode: 0644]
doc/io_get_outbufcount.3.html [new file with mode: 0644]
doc/io_pclose.3.html [new file with mode: 0644]
doc/io_popen.3.html [new file with mode: 0644]
doc/io_setbufmode.3.html [new file with mode: 0644]
doc/io_ungetc.3.html [new file with mode: 0644]
doc/mutex_enter.3.html [new file with mode: 0644]
doc/mutex_leave.3.html [new file with mode: 0644]
doc/mutex_try_enter.3.html [new file with mode: 0644]
doc/new_cgi.3.html [new file with mode: 0644]
doc/new_ftpc.3.html [new file with mode: 0644]
doc/new_http_request.3.html [new file with mode: 0644]
doc/new_http_response.3.html [new file with mode: 0644]
doc/new_mutex.3.html [new file with mode: 0644]
doc/new_pseudothread.3.html [new file with mode: 0644]
doc/new_rwlock.3.html [new file with mode: 0644]
doc/new_wait_queue.3.html [new file with mode: 0644]
doc/pseudothread_count_threads.3.html [new file with mode: 0644]
doc/pseudothread_get_stack_size.3.html [new file with mode: 0644]
doc/pseudothread_get_threads.3.html [new file with mode: 0644]
doc/pseudothread_set_stack_size.3.html [new file with mode: 0644]
doc/pth_accept.3.html [new file with mode: 0644]
doc/pth_catch.3.html [new file with mode: 0644]
doc/pth_connect.3.html [new file with mode: 0644]
doc/pth_die.3.html [new file with mode: 0644]
doc/pth_exit.3.html [new file with mode: 0644]
doc/pth_get_PC.3.html [new file with mode: 0644]
doc/pth_get_SP.3.html [new file with mode: 0644]
doc/pth_get_data.3.html [new file with mode: 0644]
doc/pth_get_language.3.html [new file with mode: 0644]
doc/pth_get_name.3.html [new file with mode: 0644]
doc/pth_get_pool.3.html [new file with mode: 0644]
doc/pth_get_run.3.html [new file with mode: 0644]
doc/pth_get_stack.3.html [new file with mode: 0644]
doc/pth_get_stack_size.3.html [new file with mode: 0644]
doc/pth_get_thread_num.3.html [new file with mode: 0644]
doc/pth_get_tz.3.html [new file with mode: 0644]
doc/pth_millisleep.3.html [new file with mode: 0644]
doc/pth_nanosleep.3.html [new file with mode: 0644]
doc/pth_poll.3.html [new file with mode: 0644]
doc/pth_read.3.html [new file with mode: 0644]
doc/pth_recv.3.html [new file with mode: 0644]
doc/pth_recvfrom.3.html [new file with mode: 0644]
doc/pth_recvmsg.3.html [new file with mode: 0644]
doc/pth_select.3.html [new file with mode: 0644]
doc/pth_send.3.html [new file with mode: 0644]
doc/pth_sendmsg.3.html [new file with mode: 0644]
doc/pth_sendto.3.html [new file with mode: 0644]
doc/pth_set_language.3.html [new file with mode: 0644]
doc/pth_set_name.3.html [new file with mode: 0644]
doc/pth_set_tz.3.html [new file with mode: 0644]
doc/pth_sleep.3.html [new file with mode: 0644]
doc/pth_start.3.html [new file with mode: 0644]
doc/pth_timeout.3.html [new file with mode: 0644]
doc/pth_write.3.html [new file with mode: 0644]
doc/pthr_server_chroot.3.html [new file with mode: 0644]
doc/pthr_server_default_port.3.html [new file with mode: 0644]
doc/pthr_server_disable_chdir.3.html [new file with mode: 0644]
doc/pthr_server_disable_close.3.html [new file with mode: 0644]
doc/pthr_server_disable_fork.3.html [new file with mode: 0644]
doc/pthr_server_disable_syslog.3.html [new file with mode: 0644]
doc/pthr_server_main.3.html [new file with mode: 0644]
doc/pthr_server_main_loop.3.html [new file with mode: 0644]
doc/pthr_server_package_name.3.html [new file with mode: 0644]
doc/pthr_server_port_option_name.3.html [new file with mode: 0644]
doc/pthr_server_startup_fn.3.html [new file with mode: 0644]
doc/pthr_server_stderr_file.3.html [new file with mode: 0644]
doc/pthr_server_username.3.html [new file with mode: 0644]
doc/rwlock_enter_read.3.html [new file with mode: 0644]
doc/rwlock_enter_write.3.html [new file with mode: 0644]
doc/rwlock_leave.3.html [new file with mode: 0644]
doc/rwlock_readers_have_priority.3.html [new file with mode: 0644]
doc/rwlock_try_enter_read.3.html [new file with mode: 0644]
doc/rwlock_try_enter_write.3.html [new file with mode: 0644]
doc/rwlock_writers_have_priority.3.html [new file with mode: 0644]
doc/wq_nr_sleepers.3.html [new file with mode: 0644]
doc/wq_sleep_on.3.html [new file with mode: 0644]
doc/wq_wake_up.3.html [new file with mode: 0644]
doc/wq_wake_up_one.3.html [new file with mode: 0644]
examples/.cvsignore [new file with mode: 0644]
examples/README [new file with mode: 0644]
examples/pthr_eg1_echo.c [new file with mode: 0644]
examples/pthr_eg1_echo.h [new file with mode: 0644]
examples/pthr_eg1_echo_main.c [new file with mode: 0644]
examples/pthr_eg2_server.c [new file with mode: 0644]
examples/pthr_eg2_server.h [new file with mode: 0644]
examples/pthr_eg2_server_main.c [new file with mode: 0644]
src/.cvsignore [new file with mode: 0644]
src/pthr_cgi.c [new file with mode: 0644]
src/pthr_cgi.h [new file with mode: 0644]
src/pthr_context.c [new file with mode: 0644]
src/pthr_context.h [new file with mode: 0644]
src/pthr_dbi.c [new file with mode: 0644]
src/pthr_dbi.h [new file with mode: 0644]
src/pthr_ftpc.c [new file with mode: 0644]
src/pthr_ftpc.h [new file with mode: 0644]
src/pthr_http.c [new file with mode: 0644]
src/pthr_http.h [new file with mode: 0644]
src/pthr_iolib.c [new file with mode: 0644]
src/pthr_iolib.h [new file with mode: 0644]
src/pthr_listener.c [new file with mode: 0644]
src/pthr_listener.h [new file with mode: 0644]
src/pthr_mutex.c [new file with mode: 0644]
src/pthr_mutex.h [new file with mode: 0644]
src/pthr_pseudothread.c [new file with mode: 0644]
src/pthr_pseudothread.h [new file with mode: 0644]
src/pthr_reactor.c [new file with mode: 0644]
src/pthr_reactor.h [new file with mode: 0644]
src/pthr_rwlock.c [new file with mode: 0644]
src/pthr_rwlock.h [new file with mode: 0644]
src/pthr_server.c [new file with mode: 0644]
src/pthr_server.h [new file with mode: 0644]
src/pthr_stack.c [new file with mode: 0644]
src/pthr_stack.h [new file with mode: 0644]
src/pthr_version.h.in [new file with mode: 0644]
src/pthr_wait_queue.c [new file with mode: 0644]
src/pthr_wait_queue.h [new file with mode: 0644]
src/test_bigstack.c [new file with mode: 0644]
src/test_context.c [new file with mode: 0644]
src/test_dbi.c [new file with mode: 0644]
src/test_except1.c [new file with mode: 0644]
src/test_except2.c [new file with mode: 0644]
src/test_except3.c [new file with mode: 0644]
src/test_mutex.c [new file with mode: 0644]
src/test_pseudothread.c [new file with mode: 0644]
src/test_reactor.c [new file with mode: 0644]
src/test_rwlock.c [new file with mode: 0644]
src/test_select.c [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..1964f99
--- /dev/null
@@ -0,0 +1 @@
+build-*
\ No newline at end of file
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644 (file)
index 0000000..eb685a5
--- /dev/null
@@ -0,0 +1,481 @@
+                 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!
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..2f2ecca
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,32 @@
+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.
diff --git a/Makefile+ b/Makefile+
new file mode 100644 (file)
index 0000000..29b1474
--- /dev/null
+++ b/Makefile+
@@ -0,0 +1,604 @@
+# -*- 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 &amp; 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 &gt;= 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
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..34bacb1
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+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
diff --git a/README.FreeBSD b/README.FreeBSD
new file mode 100644 (file)
index 0000000..e99f7ae
--- /dev/null
@@ -0,0 +1,5 @@
+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.
diff --git a/README.Solaris b/README.Solaris
new file mode 100644 (file)
index 0000000..c03edbe
--- /dev/null
@@ -0,0 +1,16 @@
+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
diff --git a/README.openbsd b/README.openbsd
new file mode 100644 (file)
index 0000000..58a23a7
--- /dev/null
@@ -0,0 +1,29 @@
+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
diff --git a/conf/test_setcontext.c b/conf/test_setcontext.c
new file mode 100644 (file)
index 0000000..b3d0a41
--- /dev/null
@@ -0,0 +1,53 @@
+/* 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);
+}
diff --git a/conf/test_setcontext.sh b/conf/test_setcontext.sh
new file mode 100755 (executable)
index 0000000..e209772
--- /dev/null
@@ -0,0 +1,24 @@
+# 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
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..04d9db0
--- /dev/null
+++ b/configure
@@ -0,0 +1,66 @@
+#!/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
diff --git a/doc/.cvsignore b/doc/.cvsignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/doc/cgi_erase.3.html b/doc/cgi_erase.3.html
new file mode 100644 (file)
index 0000000..1063108
--- /dev/null
@@ -0,0 +1,132 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/cgi_escape.3.html b/doc/cgi_escape.3.html
new file mode 100644 (file)
index 0000000..3449bcc
--- /dev/null
@@ -0,0 +1,87 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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>&quot; &quot;</b> is
+replaced by <b>&quot;+&quot;</b>, and non-printable
+characters are replaced by <b>&quot;%hh&quot;</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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/cgi_get_post_max.3.html b/doc/cgi_get_post_max.3.html
new file mode 100644 (file)
index 0000000..2f2ee9f
--- /dev/null
@@ -0,0 +1,88 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/cgi_param.3.html b/doc/cgi_param.3.html
new file mode 100644 (file)
index 0000000..c9e2b22
--- /dev/null
@@ -0,0 +1,132 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/cgi_param_list.3.html b/doc/cgi_param_list.3.html
new file mode 100644 (file)
index 0000000..c9e2b22
--- /dev/null
@@ -0,0 +1,132 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/cgi_params.3.html b/doc/cgi_params.3.html
new file mode 100644 (file)
index 0000000..c3faebc
--- /dev/null
@@ -0,0 +1,132 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/cgi_set_post_max.3.html b/doc/cgi_set_post_max.3.html
new file mode 100644 (file)
index 0000000..db97fc0
--- /dev/null
@@ -0,0 +1,88 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/cgi_unescape.3.html b/doc/cgi_unescape.3.html
new file mode 100644 (file)
index 0000000..25bc5e4
--- /dev/null
@@ -0,0 +1,87 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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>&quot; &quot;</b> is
+replaced by <b>&quot;+&quot;</b>, and non-printable
+characters are replaced by <b>&quot;%hh&quot;</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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/copy_cgi.3.html b/doc/copy_cgi.3.html
new file mode 100644 (file)
index 0000000..c3faebc
--- /dev/null
@@ -0,0 +1,132 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/eg_echo.c b/doc/eg_echo.c
new file mode 100644 (file)
index 0000000..2df5582
--- /dev/null
@@ -0,0 +1,60 @@
+#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);
+}
diff --git a/doc/ftpc_ascii.3.html b/doc/ftpc_ascii.3.html
new file mode 100644 (file)
index 0000000..6e345c9
--- /dev/null
@@ -0,0 +1,100 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_binary.3.html b/doc/ftpc_binary.3.html
new file mode 100644 (file)
index 0000000..7ee3300
--- /dev/null
@@ -0,0 +1,100 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_cdup.3.html b/doc/ftpc_cdup.3.html
new file mode 100644 (file)
index 0000000..a65ace5
--- /dev/null
@@ -0,0 +1,93 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_cwd.3.html b/doc/ftpc_cwd.3.html
new file mode 100644 (file)
index 0000000..a65ace5
--- /dev/null
@@ -0,0 +1,93 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_delete.3.html b/doc/ftpc_delete.3.html
new file mode 100644 (file)
index 0000000..4c98b79
--- /dev/null
@@ -0,0 +1,84 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_dir.3.html b/doc/ftpc_dir.3.html
new file mode 100644 (file)
index 0000000..fdaff34
--- /dev/null
@@ -0,0 +1,103 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_get.3.html b/doc/ftpc_get.3.html
new file mode 100644 (file)
index 0000000..ce2af92
--- /dev/null
@@ -0,0 +1,94 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_login.3.html b/doc/ftpc_login.3.html
new file mode 100644 (file)
index 0000000..582a03c
--- /dev/null
@@ -0,0 +1,90 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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>&quot;ftp&quot;</b> or
+<b>&quot;anonymous&quot;</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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_ls.3.html b/doc/ftpc_ls.3.html
new file mode 100644 (file)
index 0000000..fdaff34
--- /dev/null
@@ -0,0 +1,103 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_mkdir.3.html b/doc/ftpc_mkdir.3.html
new file mode 100644 (file)
index 0000000..eeb7f5b
--- /dev/null
@@ -0,0 +1,86 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_put.3.html b/doc/ftpc_put.3.html
new file mode 100644 (file)
index 0000000..aa044ee
--- /dev/null
@@ -0,0 +1,94 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_pwd.3.html b/doc/ftpc_pwd.3.html
new file mode 100644 (file)
index 0000000..239020b
--- /dev/null
@@ -0,0 +1,84 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_quit.3.html b/doc/ftpc_quit.3.html
new file mode 100644 (file)
index 0000000..128190f
--- /dev/null
@@ -0,0 +1,86 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_quote.3.html b/doc/ftpc_quote.3.html
new file mode 100644 (file)
index 0000000..20165b2
--- /dev/null
@@ -0,0 +1,85 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_rmdir.3.html b/doc/ftpc_rmdir.3.html
new file mode 100644 (file)
index 0000000..8828550
--- /dev/null
@@ -0,0 +1,86 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_set_passive_mode.3.html b/doc/ftpc_set_passive_mode.3.html
new file mode 100644 (file)
index 0000000..a74dd07
--- /dev/null
@@ -0,0 +1,93 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/ftpc_type.3.html b/doc/ftpc_type.3.html
new file mode 100644 (file)
index 0000000..de5404c
--- /dev/null
@@ -0,0 +1,100 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_get_log_file.3.html b/doc/http_get_log_file.3.html
new file mode 100644 (file)
index 0000000..b4bd8d7
--- /dev/null
@@ -0,0 +1,137 @@
+<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 &lt;pthr_http.h&gt;
+
+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 - &quot;METHOD URL HTTP/x.y&quot;
+CODE length &quot;Referer&quot; &quot;User
+Agent&quot;</td></table>
+
+<table width="100%" 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 &quot;-&quot; is intended to store the HTTP auth
+username, when HTTP authorization is supported by the
+library. The &quot;length&quot; field is only known if the
+caller sends back a &quot;Content-Length&quot; 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_get_servername.3.html b/doc/http_get_servername.3.html
new file mode 100644 (file)
index 0000000..dd234e3
--- /dev/null
@@ -0,0 +1,92 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_get_header.3.html b/doc/http_request_get_header.3.html
new file mode 100644 (file)
index 0000000..7e3aa0e
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_get_headers.3.html b/doc/http_request_get_headers.3.html
new file mode 100644 (file)
index 0000000..7e3aa0e
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_is_HEAD.3.html b/doc/http_request_is_HEAD.3.html
new file mode 100644 (file)
index 0000000..7e3aa0e
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_method.3.html b/doc/http_request_method.3.html
new file mode 100644 (file)
index 0000000..486a8cb
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_method_string.3.html b/doc/http_request_method_string.3.html
new file mode 100644 (file)
index 0000000..486a8cb
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_nr_headers.3.html b/doc/http_request_nr_headers.3.html
new file mode 100644 (file)
index 0000000..486a8cb
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_path.3.html b/doc/http_request_path.3.html
new file mode 100644 (file)
index 0000000..486a8cb
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_query_string.3.html b/doc/http_request_query_string.3.html
new file mode 100644 (file)
index 0000000..d94f954
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_time.3.html b/doc/http_request_time.3.html
new file mode 100644 (file)
index 0000000..d94f954
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_url.3.html b/doc/http_request_url.3.html
new file mode 100644 (file)
index 0000000..d94f954
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_request_version.3.html b/doc/http_request_version.3.html
new file mode 100644 (file)
index 0000000..d94f954
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_response_end_headers.3.html b/doc/http_response_end_headers.3.html
new file mode 100644 (file)
index 0000000..d0cbae1
--- /dev/null
@@ -0,0 +1,122 @@
+<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 &lt;pthr_http.h&gt;
+
+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>&quot;: &quot;</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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_response_send_header.3.html b/doc/http_response_send_header.3.html
new file mode 100644 (file)
index 0000000..d0cbae1
--- /dev/null
@@ -0,0 +1,122 @@
+<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 &lt;pthr_http.h&gt;
+
+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>&quot;: &quot;</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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_response_send_headers.3.html b/doc/http_response_send_headers.3.html
new file mode 100644 (file)
index 0000000..d0cbae1
--- /dev/null
@@ -0,0 +1,122 @@
+<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 &lt;pthr_http.h&gt;
+
+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>&quot;: &quot;</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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_set_log_file.3.html b/doc/http_set_log_file.3.html
new file mode 100644 (file)
index 0000000..f663d0b
--- /dev/null
@@ -0,0 +1,137 @@
+<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 &lt;pthr_http.h&gt;
+
+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 - &quot;METHOD URL HTTP/x.y&quot;
+CODE length &quot;Referer&quot; &quot;User
+Agent&quot;</td></table>
+
+<table width="100%" 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 &quot;-&quot; is intended to store the HTTP auth
+username, when HTTP authorization is supported by the
+library. The &quot;length&quot; field is only known if the
+caller sends back a &quot;Content-Length&quot; 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/http_set_servername.3.html b/doc/http_set_servername.3.html
new file mode 100644 (file)
index 0000000..923720f
--- /dev/null
@@ -0,0 +1,92 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/index.html b/doc/index.html
new file mode 100644 (file)
index 0000000..a46d3cf
--- /dev/null
@@ -0,0 +1,1088 @@
+<!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 &lt;stdio.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;sys/socket.h&gt;
+
+#include &lt;pool.h&gt;
+
+#include &lt;pthr_pseudothread.h&gt;
+#include &lt;pthr_iolib.h&gt;
+#include &lt;pthr_server.h&gt;
+
+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-&gt;sock = sock;
+  p-&gt;pth = new_pseudothread (pool, run, p, "processor thread");
+
+  pth_start (p-&gt;pth);
+}
+
+static void
+run (void *vp)
+{
+  processor_thread p = (processor_thread) vp;
+  io_handle io;
+  char buffer[256];
+
+  io = io_fdopen (p-&gt;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-&gt;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-&gt;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 &lt; 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 &lt; 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&amp;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&amp;def=456
+The path component was: /path/here
+The query string was: abc=123&amp;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 (&amp;sa, 0, sizeof sa);
+  sa.sa_handler = catch_quit_signal;
+  sa.sa_flags = SA_RESTART;
+  sigaction (SIGINT, &amp;sa, 0);
+  sigaction (SIGQUIT, &amp;sa, 0);
+  sigaction (SIGTERM, &amp;sa, 0);
+
+  /* ... but ignore SIGPIPE errors. */
+  sa.sa_handler = SIG_IGN;
+  sa.sa_flags = SA_RESTART;
+  sigaction (SIGPIPE, &amp;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-&gt;io = io_fdopen (p-&gt;sock);
+
+  /* Sit in a loop reading HTTP requests. */
+  while (!close)
+    {
+      /* ----- HTTP request ----- */
+      p-&gt;http_request = new_http_request (pool, p-&gt;io);
+      if (p-&gt;http_request == 0)     /* Normal end of file. */
+        break;</span>
+
+      /* Get the path and locate the file. */
+      path = http_request_path (p-&gt;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-&gt;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-&gt;pool, statbuf-&gt;st_size);
+
+  fd = open (path, O_RDONLY);
+  if (fd &lt; 0)
+    return file_not_found_error (p);
+
+  http_response = new_http_response (pool, p-&gt;http_request, p-&gt;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-&gt;http_request)) return cl;
+
+  while ((r = read (fd, buffer, n)) &gt; 0)
+    {
+      io_fwrite (buffer, r, 1, p-&gt;io);
+    }
+
+  if (r &lt; 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-&gt;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-&gt;http_request, p-&gt;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-&gt;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-&gt;io,
+             "&lt;html&gt;&lt;head&gt;&lt;title&gt;Directory: %s&lt;/title&gt;&lt;/head&gt;" CRLF
+             "&lt;body bgcolor=\"#ffffff\"&gt;" CRLF
+             "&lt;h1&gt;Directory: %s&lt;/h1&gt;" CRLF
+             "&lt;table&gt;" CRLF
+             "&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;"
+             "&lt;td&gt;&lt;a href=\"..\"&gt;Parent directory&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;" CRLF,
+             path, path);
+
+  while ((d = readdir (dir)) != 0)
+    {
+      if (d-&gt;d_name[0] != '.')      /* Ignore hidden files. */
+       {
+         const char *filename;
+         struct stat fstatbuf;
+
+         /* Generate the full pathname to this file. */
+         filename = psprintf (p-&gt;pool, "%s/%s", path, d-&gt;d_name);
+
+         /* Stat the file to find out what it is. */
+         if (lstat (filename, &amp;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-&gt;io,
+                         "&lt;tr&gt;&lt;td&gt;[ %s ]&lt;/td&gt;&lt;td align=right&gt;%d&lt;/td&gt;"
+                         "&lt;td&gt;&lt;a href=\"%s%s\"&gt;%s&lt;/a&gt;",
+                         type, size,
+                         d-&gt;d_name,
+                         S_ISDIR (fstatbuf.st_mode) ? "/" : "",
+                         d-&gt;d_name);
+
+             if (S_ISLNK (fstatbuf.st_mode))
+               {
+                 char link[NAME_MAX+1];
+                 int r;
+
+                 r = readlink (filename, link, NAME_MAX);
+                 if (r &gt;= 0) link[r] = '\0';
+                 else strcpy (link, "unknown");
+
+                 io_fprintf (p-&gt;io, " -&amp;gt; %s", link);
+               }
+
+             io_fputs ("&lt;/td&gt;&lt;/tr&gt;" CRLF, p-&gt;io);
+           }
+       }
+    }
+
+  io_fprintf (p-&gt;io,
+             "&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;" 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>
diff --git a/doc/io_copy.3.html b/doc/io_copy.3.html
new file mode 100644 (file)
index 0000000..50fe5ee
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fclose.3.html b/doc/io_fclose.3.html
new file mode 100644 (file)
index 0000000..50fe5ee
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fdopen.3.html b/doc/io_fdopen.3.html
new file mode 100644 (file)
index 0000000..50fe5ee
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fflush.3.html b/doc/io_fflush.3.html
new file mode 100644 (file)
index 0000000..7a0a76c
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fgetc.3.html b/doc/io_fgetc.3.html
new file mode 100644 (file)
index 0000000..7a0a76c
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fgets.3.html b/doc/io_fgets.3.html
new file mode 100644 (file)
index 0000000..7a0a76c
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fileno.3.html b/doc/io_fileno.3.html
new file mode 100644 (file)
index 0000000..7a0a76c
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fprintf.3.html b/doc/io_fprintf.3.html
new file mode 100644 (file)
index 0000000..772278f
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fputc.3.html b/doc/io_fputc.3.html
new file mode 100644 (file)
index 0000000..772278f
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fputs.3.html b/doc/io_fputs.3.html
new file mode 100644 (file)
index 0000000..772278f
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fread.3.html b/doc/io_fread.3.html
new file mode 100644 (file)
index 0000000..772278f
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_fwrite.3.html b/doc/io_fwrite.3.html
new file mode 100644 (file)
index 0000000..b81547f
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_get_inbufcount.3.html b/doc/io_get_inbufcount.3.html
new file mode 100644 (file)
index 0000000..b81547f
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_get_outbufcount.3.html b/doc/io_get_outbufcount.3.html
new file mode 100644 (file)
index 0000000..b81547f
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_pclose.3.html b/doc/io_pclose.3.html
new file mode 100644 (file)
index 0000000..c43aab6
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_popen.3.html b/doc/io_popen.3.html
new file mode 100644 (file)
index 0000000..c43aab6
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_setbufmode.3.html b/doc/io_setbufmode.3.html
new file mode 100644 (file)
index 0000000..c43aab6
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/io_ungetc.3.html b/doc/io_ungetc.3.html
new file mode 100644 (file)
index 0000000..c43aab6
--- /dev/null
@@ -0,0 +1,172 @@
+<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 &lt;pthr_iolib.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/mutex_enter.3.html b/doc/mutex_enter.3.html
new file mode 100644 (file)
index 0000000..4d3e29d
--- /dev/null
@@ -0,0 +1,128 @@
+<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 &lt;pthr_mutex.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/mutex_leave.3.html b/doc/mutex_leave.3.html
new file mode 100644 (file)
index 0000000..4d3e29d
--- /dev/null
@@ -0,0 +1,128 @@
+<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 &lt;pthr_mutex.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/mutex_try_enter.3.html b/doc/mutex_try_enter.3.html
new file mode 100644 (file)
index 0000000..4d3e29d
--- /dev/null
@@ -0,0 +1,128 @@
+<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 &lt;pthr_mutex.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/new_cgi.3.html b/doc/new_cgi.3.html
new file mode 100644 (file)
index 0000000..f83bb91
--- /dev/null
@@ -0,0 +1,132 @@
+<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 &lt;pthr_cgi.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/new_ftpc.3.html b/doc/new_ftpc.3.html
new file mode 100644 (file)
index 0000000..c856941
--- /dev/null
@@ -0,0 +1,92 @@
+<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 &lt;pthr_ftpc.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/new_http_request.3.html b/doc/new_http_request.3.html
new file mode 100644 (file)
index 0000000..d661b90
--- /dev/null
@@ -0,0 +1,165 @@
+<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 &lt;pthr_http.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/new_http_response.3.html b/doc/new_http_response.3.html
new file mode 100644 (file)
index 0000000..e428de4
--- /dev/null
@@ -0,0 +1,122 @@
+<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 &lt;pthr_http.h&gt;
+
+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>&quot;: &quot;</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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/new_mutex.3.html b/doc/new_mutex.3.html
new file mode 100644 (file)
index 0000000..9bfae72
--- /dev/null
@@ -0,0 +1,128 @@
+<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 &lt;pthr_mutex.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/new_pseudothread.3.html b/doc/new_pseudothread.3.html
new file mode 100644 (file)
index 0000000..a2355a7
--- /dev/null
@@ -0,0 +1,110 @@
+<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 &quot;pseudothreads&quot; 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 &lt;pthr_pseudothread.h&gt;
+
+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 &quot;process listing&quot; 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/new_rwlock.3.html b/doc/new_rwlock.3.html
new file mode 100644 (file)
index 0000000..d3a82f0
--- /dev/null
@@ -0,0 +1,184 @@
+<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 &lt;pthr_rwlock.h&gt;
+
+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
+&lt;pthr_mutex.h&gt;.</td></table>
+
+<table width="100%" 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/new_wait_queue.3.html b/doc/new_wait_queue.3.html
new file mode 100644 (file)
index 0000000..df65c8e
--- /dev/null
@@ -0,0 +1,217 @@
+<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 &lt;pthr_wait_queue.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pseudothread_count_threads.3.html b/doc/pseudothread_count_threads.3.html
new file mode 100644 (file)
index 0000000..31b698a
--- /dev/null
@@ -0,0 +1,110 @@
+<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 &quot;pseudothreads&quot; 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 &lt;pthr_pseudothread.h&gt;
+
+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 &quot;process listing&quot; 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pseudothread_get_stack_size.3.html b/doc/pseudothread_get_stack_size.3.html
new file mode 100644 (file)
index 0000000..837823b
--- /dev/null
@@ -0,0 +1,91 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pseudothread_get_threads.3.html b/doc/pseudothread_get_threads.3.html
new file mode 100644 (file)
index 0000000..556d5ae
--- /dev/null
@@ -0,0 +1,110 @@
+<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 &quot;pseudothreads&quot; 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 &lt;pthr_pseudothread.h&gt;
+
+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 &quot;process listing&quot; 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pseudothread_set_stack_size.3.html b/doc/pseudothread_set_stack_size.3.html
new file mode 100644 (file)
index 0000000..fd031b9
--- /dev/null
@@ -0,0 +1,91 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_accept.3.html b/doc/pth_accept.3.html
new file mode 100644 (file)
index 0000000..e0c0f08
--- /dev/null
@@ -0,0 +1,112 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_catch.3.html b/doc/pth_catch.3.html
new file mode 100644 (file)
index 0000000..30801d6
--- /dev/null
@@ -0,0 +1,120 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_connect.3.html b/doc/pth_connect.3.html
new file mode 100644 (file)
index 0000000..66e25b7
--- /dev/null
@@ -0,0 +1,112 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_die.3.html b/doc/pth_die.3.html
new file mode 100644 (file)
index 0000000..61f1abd
--- /dev/null
@@ -0,0 +1,120 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_exit.3.html b/doc/pth_exit.3.html
new file mode 100644 (file)
index 0000000..61f1abd
--- /dev/null
@@ -0,0 +1,120 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_PC.3.html b/doc/pth_get_PC.3.html
new file mode 100644 (file)
index 0000000..33e8070
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_SP.3.html b/doc/pth_get_SP.3.html
new file mode 100644 (file)
index 0000000..c278b68
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_data.3.html b/doc/pth_get_data.3.html
new file mode 100644 (file)
index 0000000..c278b68
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_language.3.html b/doc/pth_get_language.3.html
new file mode 100644 (file)
index 0000000..c278b68
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_name.3.html b/doc/pth_get_name.3.html
new file mode 100644 (file)
index 0000000..c278b68
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_pool.3.html b/doc/pth_get_pool.3.html
new file mode 100644 (file)
index 0000000..2c5b6be
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_run.3.html b/doc/pth_get_run.3.html
new file mode 100644 (file)
index 0000000..2c5b6be
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_stack.3.html b/doc/pth_get_stack.3.html
new file mode 100644 (file)
index 0000000..2c5b6be
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_stack_size.3.html b/doc/pth_get_stack_size.3.html
new file mode 100644 (file)
index 0000000..2c5b6be
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_thread_num.3.html b/doc/pth_get_thread_num.3.html
new file mode 100644 (file)
index 0000000..2e86836
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_get_tz.3.html b/doc/pth_get_tz.3.html
new file mode 100644 (file)
index 0000000..2e86836
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_millisleep.3.html b/doc/pth_millisleep.3.html
new file mode 100644 (file)
index 0000000..e96429b
--- /dev/null
@@ -0,0 +1,112 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_nanosleep.3.html b/doc/pth_nanosleep.3.html
new file mode 100644 (file)
index 0000000..e96429b
--- /dev/null
@@ -0,0 +1,112 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_poll.3.html b/doc/pth_poll.3.html
new file mode 100644 (file)
index 0000000..d8ee8b4
--- /dev/null
@@ -0,0 +1,105 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_read.3.html b/doc/pth_read.3.html
new file mode 100644 (file)
index 0000000..b3d0b98
--- /dev/null
@@ -0,0 +1,112 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_recv.3.html b/doc/pth_recv.3.html
new file mode 100644 (file)
index 0000000..944ddfb
--- /dev/null
@@ -0,0 +1,91 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_recvfrom.3.html b/doc/pth_recvfrom.3.html
new file mode 100644 (file)
index 0000000..944ddfb
--- /dev/null
@@ -0,0 +1,91 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_recvmsg.3.html b/doc/pth_recvmsg.3.html
new file mode 100644 (file)
index 0000000..944ddfb
--- /dev/null
@@ -0,0 +1,91 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_select.3.html b/doc/pth_select.3.html
new file mode 100644 (file)
index 0000000..6721ae4
--- /dev/null
@@ -0,0 +1,105 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_send.3.html b/doc/pth_send.3.html
new file mode 100644 (file)
index 0000000..2163446
--- /dev/null
@@ -0,0 +1,91 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_sendmsg.3.html b/doc/pth_sendmsg.3.html
new file mode 100644 (file)
index 0000000..2163446
--- /dev/null
@@ -0,0 +1,91 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_sendto.3.html b/doc/pth_sendto.3.html
new file mode 100644 (file)
index 0000000..2163446
--- /dev/null
@@ -0,0 +1,91 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_set_language.3.html b/doc/pth_set_language.3.html
new file mode 100644 (file)
index 0000000..a1ed004
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_set_name.3.html b/doc/pth_set_name.3.html
new file mode 100644 (file)
index 0000000..a1ed004
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_set_tz.3.html b/doc/pth_set_tz.3.html
new file mode 100644 (file)
index 0000000..a1ed004
--- /dev/null
@@ -0,0 +1,188 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_sleep.3.html b/doc/pth_sleep.3.html
new file mode 100644 (file)
index 0000000..0adb59e
--- /dev/null
@@ -0,0 +1,112 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_start.3.html b/doc/pth_start.3.html
new file mode 100644 (file)
index 0000000..6ca16c4
--- /dev/null
@@ -0,0 +1,110 @@
+<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 &quot;pseudothreads&quot; 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 &lt;pthr_pseudothread.h&gt;
+
+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 &quot;process listing&quot; 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_timeout.3.html b/doc/pth_timeout.3.html
new file mode 100644 (file)
index 0000000..4042870
--- /dev/null
@@ -0,0 +1,112 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pth_write.3.html b/doc/pth_write.3.html
new file mode 100644 (file)
index 0000000..4042870
--- /dev/null
@@ -0,0 +1,112 @@
+<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 &lt;pthr_pseudothread.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_chroot.3.html b/doc/pthr_server_chroot.3.html
new file mode 100644 (file)
index 0000000..aa44d83
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_default_port.3.html b/doc/pthr_server_default_port.3.html
new file mode 100644 (file)
index 0000000..6d9c716
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_disable_chdir.3.html b/doc/pthr_server_disable_chdir.3.html
new file mode 100644 (file)
index 0000000..6d9c716
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_disable_close.3.html b/doc/pthr_server_disable_close.3.html
new file mode 100644 (file)
index 0000000..6d9c716
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_disable_fork.3.html b/doc/pthr_server_disable_fork.3.html
new file mode 100644 (file)
index 0000000..6d9c716
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_disable_syslog.3.html b/doc/pthr_server_disable_syslog.3.html
new file mode 100644 (file)
index 0000000..e5b8d8e
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_main.3.html b/doc/pthr_server_main.3.html
new file mode 100644 (file)
index 0000000..d57116c
--- /dev/null
@@ -0,0 +1,125 @@
+<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 &lt;pthr_server_main.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_main_loop.3.html b/doc/pthr_server_main_loop.3.html
new file mode 100644 (file)
index 0000000..e5b8d8e
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_package_name.3.html b/doc/pthr_server_package_name.3.html
new file mode 100644 (file)
index 0000000..e5b8d8e
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_port_option_name.3.html b/doc/pthr_server_port_option_name.3.html
new file mode 100644 (file)
index 0000000..e5b8d8e
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_startup_fn.3.html b/doc/pthr_server_startup_fn.3.html
new file mode 100644 (file)
index 0000000..86b91ca
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_stderr_file.3.html b/doc/pthr_server_stderr_file.3.html
new file mode 100644 (file)
index 0000000..86b91ca
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/pthr_server_username.3.html b/doc/pthr_server_username.3.html
new file mode 100644 (file)
index 0000000..86b91ca
--- /dev/null
@@ -0,0 +1,171 @@
+<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 &lt;pthr_server.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/rwlock_enter_read.3.html b/doc/rwlock_enter_read.3.html
new file mode 100644 (file)
index 0000000..d7bfb59
--- /dev/null
@@ -0,0 +1,184 @@
+<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 &lt;pthr_rwlock.h&gt;
+
+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
+&lt;pthr_mutex.h&gt;.</td></table>
+
+<table width="100%" 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/rwlock_enter_write.3.html b/doc/rwlock_enter_write.3.html
new file mode 100644 (file)
index 0000000..b4fe6d8
--- /dev/null
@@ -0,0 +1,184 @@
+<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 &lt;pthr_rwlock.h&gt;
+
+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
+&lt;pthr_mutex.h&gt;.</td></table>
+
+<table width="100%" 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/rwlock_leave.3.html b/doc/rwlock_leave.3.html
new file mode 100644 (file)
index 0000000..b4fe6d8
--- /dev/null
@@ -0,0 +1,184 @@
+<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 &lt;pthr_rwlock.h&gt;
+
+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
+&lt;pthr_mutex.h&gt;.</td></table>
+
+<table width="100%" 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/rwlock_readers_have_priority.3.html b/doc/rwlock_readers_have_priority.3.html
new file mode 100644 (file)
index 0000000..b4fe6d8
--- /dev/null
@@ -0,0 +1,184 @@
+<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 &lt;pthr_rwlock.h&gt;
+
+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
+&lt;pthr_mutex.h&gt;.</td></table>
+
+<table width="100%" 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/rwlock_try_enter_read.3.html b/doc/rwlock_try_enter_read.3.html
new file mode 100644 (file)
index 0000000..b4fe6d8
--- /dev/null
@@ -0,0 +1,184 @@
+<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 &lt;pthr_rwlock.h&gt;
+
+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
+&lt;pthr_mutex.h&gt;.</td></table>
+
+<table width="100%" 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/rwlock_try_enter_write.3.html b/doc/rwlock_try_enter_write.3.html
new file mode 100644 (file)
index 0000000..46b92df
--- /dev/null
@@ -0,0 +1,184 @@
+<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 &lt;pthr_rwlock.h&gt;
+
+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
+&lt;pthr_mutex.h&gt;.</td></table>
+
+<table width="100%" 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/rwlock_writers_have_priority.3.html b/doc/rwlock_writers_have_priority.3.html
new file mode 100644 (file)
index 0000000..46b92df
--- /dev/null
@@ -0,0 +1,184 @@
+<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 &lt;pthr_rwlock.h&gt;
+
+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
+&lt;pthr_mutex.h&gt;.</td></table>
+
+<table width="100%" 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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/wq_nr_sleepers.3.html b/doc/wq_nr_sleepers.3.html
new file mode 100644 (file)
index 0000000..8e1e107
--- /dev/null
@@ -0,0 +1,217 @@
+<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 &lt;pthr_wait_queue.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/wq_sleep_on.3.html b/doc/wq_sleep_on.3.html
new file mode 100644 (file)
index 0000000..8e1e107
--- /dev/null
@@ -0,0 +1,217 @@
+<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 &lt;pthr_wait_queue.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/wq_wake_up.3.html b/doc/wq_wake_up.3.html
new file mode 100644 (file)
index 0000000..e445119
--- /dev/null
@@ -0,0 +1,217 @@
+<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 &lt;pthr_wait_queue.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/doc/wq_wake_up_one.3.html b/doc/wq_wake_up_one.3.html
new file mode 100644 (file)
index 0000000..e445119
--- /dev/null
@@ -0,0 +1,217 @@
+<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 &lt;pthr_wait_queue.h&gt;
+
+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 &lt;rich@annexia.org&gt;</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>
diff --git a/examples/.cvsignore b/examples/.cvsignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/README b/examples/README
new file mode 100644 (file)
index 0000000..bfabc92
--- /dev/null
@@ -0,0 +1,53 @@
+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/
diff --git a/examples/pthr_eg1_echo.c b/examples/pthr_eg1_echo.c
new file mode 100644 (file)
index 0000000..c42528c
--- /dev/null
@@ -0,0 +1,144 @@
+/* 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 ();
+}
diff --git a/examples/pthr_eg1_echo.h b/examples/pthr_eg1_echo.h
new file mode 100644 (file)
index 0000000..778e29b
--- /dev/null
@@ -0,0 +1,33 @@
+/* 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 */
diff --git a/examples/pthr_eg1_echo_main.c b/examples/pthr_eg1_echo_main.c
new file mode 100644 (file)
index 0000000..03af897
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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);
+}
diff --git a/examples/pthr_eg2_server.c b/examples/pthr_eg2_server.c
new file mode 100644 (file)
index 0000000..71a2c6d
--- /dev/null
@@ -0,0 +1,350 @@
+/* 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, " -&gt; %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;
+}
diff --git a/examples/pthr_eg2_server.h b/examples/pthr_eg2_server.h
new file mode 100644 (file)
index 0000000..fd5a464
--- /dev/null
@@ -0,0 +1,48 @@
+/* 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 */
diff --git a/examples/pthr_eg2_server_main.c b/examples/pthr_eg2_server_main.c
new file mode 100644 (file)
index 0000000..d2808c8
--- /dev/null
@@ -0,0 +1,82 @@
+/* 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);
+}
diff --git a/src/.cvsignore b/src/.cvsignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pthr_cgi.c b/src/pthr_cgi.c
new file mode 100644 (file)
index 0000000..9d3c66f
--- /dev/null
@@ -0,0 +1,350 @@
+/* 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;
+}
diff --git a/src/pthr_cgi.h b/src/pthr_cgi.h
new file mode 100644 (file)
index 0000000..162ebde
--- /dev/null
@@ -0,0 +1,103 @@
+/* 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 */
diff --git a/src/pthr_context.c b/src/pthr_context.c
new file mode 100644 (file)
index 0000000..396bef3
--- /dev/null
@@ -0,0 +1,160 @@
+/* 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 */
diff --git a/src/pthr_context.h b/src/pthr_context.h
new file mode 100644 (file)
index 0000000..28d5b42
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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 */
diff --git a/src/pthr_dbi.c b/src/pthr_dbi.c
new file mode 100644 (file)
index 0000000..294ca8f
--- /dev/null
@@ -0,0 +1,1145 @@
+/* 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
diff --git a/src/pthr_dbi.h b/src/pthr_dbi.h
new file mode 100644 (file)
index 0000000..43812d7
--- /dev/null
@@ -0,0 +1,268 @@
+/* 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 */
diff --git a/src/pthr_ftpc.c b/src/pthr_ftpc.c
new file mode 100644 (file)
index 0000000..6d60bf1
--- /dev/null
@@ -0,0 +1,724 @@
+/* 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;
+}
diff --git a/src/pthr_ftpc.h b/src/pthr_ftpc.h
new file mode 100644 (file)
index 0000000..9a34c39
--- /dev/null
@@ -0,0 +1,213 @@
+/* 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 */
diff --git a/src/pthr_http.c b/src/pthr_http.c
new file mode 100644 (file)
index 0000000..37bd52b
--- /dev/null
@@ -0,0 +1,702 @@
+/* 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);
+}
diff --git a/src/pthr_http.h b/src/pthr_http.h
new file mode 100644 (file)
index 0000000..7be139e
--- /dev/null
@@ -0,0 +1,244 @@
+/* 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 */
diff --git a/src/pthr_iolib.c b/src/pthr_iolib.c
new file mode 100644 (file)
index 0000000..37fc368
--- /dev/null
@@ -0,0 +1,569 @@
+/* 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 ();
+}
diff --git a/src/pthr_iolib.h b/src/pthr_iolib.h
new file mode 100644 (file)
index 0000000..010356c
--- /dev/null
@@ -0,0 +1,137 @@
+/* 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 */
diff --git a/src/pthr_listener.c b/src/pthr_listener.c
new file mode 100644 (file)
index 0000000..cd76379
--- /dev/null
@@ -0,0 +1,104 @@
+/* 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);
+    }
+}
diff --git a/src/pthr_listener.h b/src/pthr_listener.h
new file mode 100644 (file)
index 0000000..beeeab6
--- /dev/null
@@ -0,0 +1,31 @@
+/* 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 */
diff --git a/src/pthr_mutex.c b/src/pthr_mutex.c
new file mode 100644 (file)
index 0000000..fd7ceb8
--- /dev/null
@@ -0,0 +1,167 @@
+/* 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);
+}
diff --git a/src/pthr_mutex.h b/src/pthr_mutex.h
new file mode 100644 (file)
index 0000000..8b78202
--- /dev/null
@@ -0,0 +1,74 @@
+/* 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 */
diff --git a/src/pthr_pseudothread.c b/src/pthr_pseudothread.c
new file mode 100644 (file)
index 0000000..b641c8c
--- /dev/null
@@ -0,0 +1,1024 @@
+/* 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 (&current_pth->thread_ctx, &current_pth->calling_ctx);
+}
+
+inline void
+_pth_switch_calling_to_thread_context (pseudothread pth)
+{
+  current_pth = pth;           /* Set current thread. */
+  mctx_switch (&current_pth->calling_ctx, &current_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;
+}
diff --git a/src/pthr_pseudothread.h b/src/pthr_pseudothread.h
new file mode 100644 (file)
index 0000000..900a247
--- /dev/null
@@ -0,0 +1,320 @@
+/* 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 */
diff --git a/src/pthr_reactor.c b/src/pthr_reactor.c
new file mode 100644 (file)
index 0000000..db8973e
--- /dev/null
@@ -0,0 +1,556 @@
+/* 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);
+       }
+    }
+}
diff --git a/src/pthr_reactor.h b/src/pthr_reactor.h
new file mode 100644 (file)
index 0000000..365fd48
--- /dev/null
@@ -0,0 +1,68 @@
+/* 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 */
diff --git a/src/pthr_rwlock.c b/src/pthr_rwlock.c
new file mode 100644 (file)
index 0000000..27816d5
--- /dev/null
@@ -0,0 +1,306 @@
+/* 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);
+    }
+}
diff --git a/src/pthr_rwlock.h b/src/pthr_rwlock.h
new file mode 100644 (file)
index 0000000..d95894d
--- /dev/null
@@ -0,0 +1,103 @@
+/* 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 */
diff --git a/src/pthr_server.c b/src/pthr_server.c
new file mode 100644 (file)
index 0000000..d8cbe5c
--- /dev/null
@@ -0,0 +1,389 @@
+/* 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;
+}
diff --git a/src/pthr_server.h b/src/pthr_server.h
new file mode 100644 (file)
index 0000000..dec17f1
--- /dev/null
@@ -0,0 +1,123 @@
+/* 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 */
diff --git a/src/pthr_stack.c b/src/pthr_stack.c
new file mode 100644 (file)
index 0000000..9755fd5
--- /dev/null
@@ -0,0 +1,119 @@
+/* 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;
+}
diff --git a/src/pthr_stack.h b/src/pthr_stack.h
new file mode 100644 (file)
index 0000000..a5ef02e
--- /dev/null
@@ -0,0 +1,26 @@
+/* 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 */
diff --git a/src/pthr_version.h.in b/src/pthr_version.h.in
new file mode 100644 (file)
index 0000000..c243a86
--- /dev/null
@@ -0,0 +1,4 @@
+/* -*- C -*- @configure_input@ */
+
+#define PTHRLIB_PACKAGE "@PACKAGE@"
+#define PTHRLIB_VERSION "@VERSION@"
diff --git a/src/pthr_wait_queue.c b/src/pthr_wait_queue.c
new file mode 100644 (file)
index 0000000..10ea097
--- /dev/null
@@ -0,0 +1,203 @@
+/* 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);
+}
diff --git a/src/pthr_wait_queue.h b/src/pthr_wait_queue.h
new file mode 100644 (file)
index 0000000..a9a4271
--- /dev/null
@@ -0,0 +1,124 @@
+/* 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 */
diff --git a/src/test_bigstack.c b/src/test_bigstack.c
new file mode 100644 (file)
index 0000000..82dcf9a
--- /dev/null
@@ -0,0 +1,62 @@
+/* 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);
+}
diff --git a/src/test_context.c b/src/test_context.c
new file mode 100644 (file)
index 0000000..8c54646
--- /dev/null
@@ -0,0 +1,99 @@
+/* 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);
+}
diff --git a/src/test_dbi.c b/src/test_dbi.c
new file mode 100644 (file)
index 0000000..c661d54
--- /dev/null
@@ -0,0 +1,316 @@
+/* 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);
+}
diff --git a/src/test_except1.c b/src/test_except1.c
new file mode 100644 (file)
index 0000000..3701fab
--- /dev/null
@@ -0,0 +1,67 @@
+/* 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);
+}
diff --git a/src/test_except2.c b/src/test_except2.c
new file mode 100644 (file)
index 0000000..de97057
--- /dev/null
@@ -0,0 +1,54 @@
+/* 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);
+}
diff --git a/src/test_except3.c b/src/test_except3.c
new file mode 100644 (file)
index 0000000..aaad60c
--- /dev/null
@@ -0,0 +1,76 @@
+/* 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);
+}
diff --git a/src/test_mutex.c b/src/test_mutex.c
new file mode 100644 (file)
index 0000000..d8cbb9d
--- /dev/null
@@ -0,0 +1,122 @@
+/* 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 ();
+}
diff --git a/src/test_pseudothread.c b/src/test_pseudothread.c
new file mode 100644 (file)
index 0000000..547074e
--- /dev/null
@@ -0,0 +1,130 @@
+/* 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);
+}
diff --git a/src/test_reactor.c b/src/test_reactor.c
new file mode 100644 (file)
index 0000000..1d7e206
--- /dev/null
@@ -0,0 +1,97 @@
+/* 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);
+}
diff --git a/src/test_rwlock.c b/src/test_rwlock.c
new file mode 100644 (file)
index 0000000..66f57fd
--- /dev/null
@@ -0,0 +1,152 @@
+/* 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 ();
+}
diff --git a/src/test_select.c b/src/test_select.c
new file mode 100644 (file)
index 0000000..ecf3cbb
--- /dev/null
@@ -0,0 +1,148 @@
+/* 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);
+}